blob: 3f8a2a095f865a781e8c048b46459f1484943016 [file] [log] [blame]
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package stack_test contains tests for the stack. It is in its own package so
// that the tests can also validate that all definitions needed to implement
// transport and network protocols are properly exported by the stack package.
package stack_test
import (
"bytes"
"fmt"
"math"
"sort"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/rand"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
const (
fakeNetNumber tcpip.NetworkProtocolNumber = math.MaxUint32
fakeNetHeaderLen = 12
fakeDefaultPrefixLen = 8
// fakeControlProtocol is used for control packets that represent
// destination port unreachable.
fakeControlProtocol tcpip.TransportProtocolNumber = 2
// defaultMTU is the MTU, in bytes, used throughout the tests, except
// where another value is explicitly used. It is chosen to match the MTU
// of loopback interfaces on linux systems.
defaultMTU = 65536
)
// fakeNetworkEndpoint is a network-layer protocol endpoint. It counts sent and
// received packets; the counts of all endpoints are aggregated in the protocol
// descriptor.
//
// Headers of this protocol are fakeNetHeaderLen bytes, but we currently only
// use the first three: destination address, source address, and transport
// protocol. They're all one byte fields to simplify parsing.
type fakeNetworkEndpoint struct {
nicID tcpip.NICID
id stack.NetworkEndpointID
prefixLen int
proto *fakeNetworkProtocol
dispatcher stack.TransportDispatcher
ep stack.LinkEndpoint
}
func (f *fakeNetworkEndpoint) MTU() uint32 {
return f.ep.MTU() - uint32(f.MaxHeaderLength())
}
func (f *fakeNetworkEndpoint) NICID() tcpip.NICID {
return f.nicID
}
func (f *fakeNetworkEndpoint) PrefixLen() int {
return f.prefixLen
}
func (*fakeNetworkEndpoint) DefaultTTL() uint8 {
return 123
}
func (f *fakeNetworkEndpoint) ID() *stack.NetworkEndpointID {
return &f.id
}
func (f *fakeNetworkEndpoint) HandlePacket(r *stack.Route, pkt stack.PacketBuffer) {
// Increment the received packet count in the protocol descriptor.
f.proto.packetCount[int(f.id.LocalAddress[0])%len(f.proto.packetCount)]++
// Consume the network header.
b := pkt.Data.First()
pkt.Data.TrimFront(fakeNetHeaderLen)
// Handle control packets.
if b[2] == uint8(fakeControlProtocol) {
nb := pkt.Data.First()
if len(nb) < fakeNetHeaderLen {
return
}
pkt.Data.TrimFront(fakeNetHeaderLen)
f.dispatcher.DeliverTransportControlPacket(tcpip.Address(nb[1:2]), tcpip.Address(nb[0:1]), fakeNetNumber, tcpip.TransportProtocolNumber(nb[2]), stack.ControlPortUnreachable, 0, pkt)
return
}
// Dispatch the packet to the transport protocol.
f.dispatcher.DeliverTransportPacket(r, tcpip.TransportProtocolNumber(b[2]), pkt)
}
func (f *fakeNetworkEndpoint) MaxHeaderLength() uint16 {
return f.ep.MaxHeaderLength() + fakeNetHeaderLen
}
func (f *fakeNetworkEndpoint) PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, dstAddr tcpip.Address) uint16 {
return 0
}
func (f *fakeNetworkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return f.ep.Capabilities()
}
func (f *fakeNetworkEndpoint) WritePacket(r *stack.Route, gso *stack.GSO, params stack.NetworkHeaderParams, pkt stack.PacketBuffer) *tcpip.Error {
// Increment the sent packet count in the protocol descriptor.
f.proto.sendPacketCount[int(r.RemoteAddress[0])%len(f.proto.sendPacketCount)]++
// Add the protocol's header to the packet and send it to the link
// endpoint.
b := pkt.Header.Prepend(fakeNetHeaderLen)
b[0] = r.RemoteAddress[0]
b[1] = f.id.LocalAddress[0]
b[2] = byte(params.Protocol)
if r.Loop&stack.PacketLoop != 0 {
views := make([]buffer.View, 1, 1+len(pkt.Data.Views()))
views[0] = pkt.Header.View()
views = append(views, pkt.Data.Views()...)
f.HandlePacket(r, stack.PacketBuffer{
Data: buffer.NewVectorisedView(len(views[0])+pkt.Data.Size(), views),
})
}
if r.Loop&stack.PacketOut == 0 {
return nil
}
return f.ep.WritePacket(r, gso, fakeNetNumber, pkt)
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (f *fakeNetworkEndpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, params stack.NetworkHeaderParams) (int, *tcpip.Error) {
panic("not implemented")
}
func (*fakeNetworkEndpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt stack.PacketBuffer) *tcpip.Error {
return tcpip.ErrNotSupported
}
func (*fakeNetworkEndpoint) Close() {}
type fakeNetGoodOption bool
type fakeNetBadOption bool
type fakeNetInvalidValueOption int
type fakeNetOptions struct {
good bool
}
// fakeNetworkProtocol is a network-layer protocol descriptor. It aggregates the
// number of packets sent and received via endpoints of this protocol. The index
// where packets are added is given by the packet's destination address MOD 10.
type fakeNetworkProtocol struct {
packetCount [10]int
sendPacketCount [10]int
opts fakeNetOptions
}
func (f *fakeNetworkProtocol) Number() tcpip.NetworkProtocolNumber {
return fakeNetNumber
}
func (f *fakeNetworkProtocol) MinimumPacketSize() int {
return fakeNetHeaderLen
}
func (f *fakeNetworkProtocol) DefaultPrefixLen() int {
return fakeDefaultPrefixLen
}
func (f *fakeNetworkProtocol) PacketCount(intfAddr byte) int {
return f.packetCount[int(intfAddr)%len(f.packetCount)]
}
func (*fakeNetworkProtocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
return tcpip.Address(v[1:2]), tcpip.Address(v[0:1])
}
func (f *fakeNetworkProtocol) NewEndpoint(nicID tcpip.NICID, addrWithPrefix tcpip.AddressWithPrefix, linkAddrCache stack.LinkAddressCache, dispatcher stack.TransportDispatcher, ep stack.LinkEndpoint, _ *stack.Stack) (stack.NetworkEndpoint, *tcpip.Error) {
return &fakeNetworkEndpoint{
nicID: nicID,
id: stack.NetworkEndpointID{LocalAddress: addrWithPrefix.Address},
prefixLen: addrWithPrefix.PrefixLen,
proto: f,
dispatcher: dispatcher,
ep: ep,
}, nil
}
func (f *fakeNetworkProtocol) SetOption(option interface{}) *tcpip.Error {
switch v := option.(type) {
case fakeNetGoodOption:
f.opts.good = bool(v)
return nil
case fakeNetInvalidValueOption:
return tcpip.ErrInvalidOptionValue
default:
return tcpip.ErrUnknownProtocolOption
}
}
func (f *fakeNetworkProtocol) Option(option interface{}) *tcpip.Error {
switch v := option.(type) {
case *fakeNetGoodOption:
*v = fakeNetGoodOption(f.opts.good)
return nil
default:
return tcpip.ErrUnknownProtocolOption
}
}
// Close implements TransportProtocol.Close.
func (*fakeNetworkProtocol) Close() {}
// Wait implements TransportProtocol.Wait.
func (*fakeNetworkProtocol) Wait() {}
func fakeNetFactory() stack.NetworkProtocol {
return &fakeNetworkProtocol{}
}
// linkEPWithMockedAttach is a stack.LinkEndpoint that tests can use to verify
// that LinkEndpoint.Attach was called.
type linkEPWithMockedAttach struct {
stack.LinkEndpoint
attached bool
}
// Attach implements stack.LinkEndpoint.Attach.
func (l *linkEPWithMockedAttach) Attach(d stack.NetworkDispatcher) {
l.LinkEndpoint.Attach(d)
l.attached = d != nil
}
func (l *linkEPWithMockedAttach) isAttached() bool {
return l.attached
}
func TestNetworkReceive(t *testing.T) {
// Create a stack with the fake network protocol, one nic, and two
// addresses attached to it: 1 & 2.
ep := channel.New(10, defaultMTU, "")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil {
t.Fatal("AddAddress failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x02"); err != nil {
t.Fatal("AddAddress failed:", err)
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
// Make sure packet with wrong address is not delivered.
buf[0] = 3
ep.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if fakeNet.packetCount[1] != 0 {
t.Errorf("packetCount[1] = %d, want %d", fakeNet.packetCount[1], 0)
}
if fakeNet.packetCount[2] != 0 {
t.Errorf("packetCount[2] = %d, want %d", fakeNet.packetCount[2], 0)
}
// Make sure packet is delivered to first endpoint.
buf[0] = 1
ep.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if fakeNet.packetCount[1] != 1 {
t.Errorf("packetCount[1] = %d, want %d", fakeNet.packetCount[1], 1)
}
if fakeNet.packetCount[2] != 0 {
t.Errorf("packetCount[2] = %d, want %d", fakeNet.packetCount[2], 0)
}
// Make sure packet is delivered to second endpoint.
buf[0] = 2
ep.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if fakeNet.packetCount[1] != 1 {
t.Errorf("packetCount[1] = %d, want %d", fakeNet.packetCount[1], 1)
}
if fakeNet.packetCount[2] != 1 {
t.Errorf("packetCount[2] = %d, want %d", fakeNet.packetCount[2], 1)
}
// Make sure packet is not delivered if protocol number is wrong.
ep.InjectInbound(fakeNetNumber-1, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if fakeNet.packetCount[1] != 1 {
t.Errorf("packetCount[1] = %d, want %d", fakeNet.packetCount[1], 1)
}
if fakeNet.packetCount[2] != 1 {
t.Errorf("packetCount[2] = %d, want %d", fakeNet.packetCount[2], 1)
}
// Make sure packet that is too small is dropped.
buf.CapLength(2)
ep.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if fakeNet.packetCount[1] != 1 {
t.Errorf("packetCount[1] = %d, want %d", fakeNet.packetCount[1], 1)
}
if fakeNet.packetCount[2] != 1 {
t.Errorf("packetCount[2] = %d, want %d", fakeNet.packetCount[2], 1)
}
}
func sendTo(s *stack.Stack, addr tcpip.Address, payload buffer.View) *tcpip.Error {
r, err := s.FindRoute(0, "", addr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
return err
}
defer r.Release()
return send(r, payload)
}
func send(r stack.Route, payload buffer.View) *tcpip.Error {
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()))
return r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: fakeTransNumber, TTL: 123, TOS: stack.DefaultTOS}, stack.PacketBuffer{
Header: hdr,
Data: payload.ToVectorisedView(),
})
}
func testSendTo(t *testing.T, s *stack.Stack, addr tcpip.Address, ep *channel.Endpoint, payload buffer.View) {
t.Helper()
ep.Drain()
if err := sendTo(s, addr, payload); err != nil {
t.Error("sendTo failed:", err)
}
if got, want := ep.Drain(), 1; got != want {
t.Errorf("sendTo packet count: got = %d, want %d", got, want)
}
}
func testSend(t *testing.T, r stack.Route, ep *channel.Endpoint, payload buffer.View) {
t.Helper()
ep.Drain()
if err := send(r, payload); err != nil {
t.Error("send failed:", err)
}
if got, want := ep.Drain(), 1; got != want {
t.Errorf("send packet count: got = %d, want %d", got, want)
}
}
func testFailingSend(t *testing.T, r stack.Route, ep *channel.Endpoint, payload buffer.View, wantErr *tcpip.Error) {
t.Helper()
if gotErr := send(r, payload); gotErr != wantErr {
t.Errorf("send failed: got = %s, want = %s ", gotErr, wantErr)
}
}
func testFailingSendTo(t *testing.T, s *stack.Stack, addr tcpip.Address, ep *channel.Endpoint, payload buffer.View, wantErr *tcpip.Error) {
t.Helper()
if gotErr := sendTo(s, addr, payload); gotErr != wantErr {
t.Errorf("sendto failed: got = %s, want = %s ", gotErr, wantErr)
}
}
func testRecv(t *testing.T, fakeNet *fakeNetworkProtocol, localAddrByte byte, ep *channel.Endpoint, buf buffer.View) {
t.Helper()
// testRecvInternal injects one packet, and we expect to receive it.
want := fakeNet.PacketCount(localAddrByte) + 1
testRecvInternal(t, fakeNet, localAddrByte, ep, buf, want)
}
func testFailingRecv(t *testing.T, fakeNet *fakeNetworkProtocol, localAddrByte byte, ep *channel.Endpoint, buf buffer.View) {
t.Helper()
// testRecvInternal injects one packet, and we do NOT expect to receive it.
want := fakeNet.PacketCount(localAddrByte)
testRecvInternal(t, fakeNet, localAddrByte, ep, buf, want)
}
func testRecvInternal(t *testing.T, fakeNet *fakeNetworkProtocol, localAddrByte byte, ep *channel.Endpoint, buf buffer.View, want int) {
t.Helper()
ep.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if got := fakeNet.PacketCount(localAddrByte); got != want {
t.Errorf("receive packet count: got = %d, want %d", got, want)
}
}
func TestNetworkSend(t *testing.T) {
// Create a stack with the fake network protocol, one nic, and one
// address: 1. The route table sends all packets through the only
// existing nic.
ep := channel.New(10, defaultMTU, "")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("NewNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil {
t.Fatal("AddAddress failed:", err)
}
// Make sure that the link-layer endpoint received the outbound packet.
testSendTo(t, s, "\x03", ep, nil)
}
func TestNetworkSendMultiRoute(t *testing.T) {
// Create a stack with the fake network protocol, two nics, and two
// addresses per nic, the first nic has odd address, the second one has
// even addresses.
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep1 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep1); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil {
t.Fatal("AddAddress failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x03"); err != nil {
t.Fatal("AddAddress failed:", err)
}
ep2 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(2, ep2); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(2, fakeNetNumber, "\x02"); err != nil {
t.Fatal("AddAddress failed:", err)
}
if err := s.AddAddress(2, fakeNetNumber, "\x04"); err != nil {
t.Fatal("AddAddress failed:", err)
}
// Set a route table that sends all packets with odd destination
// addresses through the first NIC, and all even destination address
// through the second one.
{
subnet0, err := tcpip.NewSubnet("\x00", "\x01")
if err != nil {
t.Fatal(err)
}
subnet1, err := tcpip.NewSubnet("\x01", "\x01")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{
{Destination: subnet1, Gateway: "\x00", NIC: 1},
{Destination: subnet0, Gateway: "\x00", NIC: 2},
})
}
// Send a packet to an odd destination.
testSendTo(t, s, "\x05", ep1, nil)
// Send a packet to an even destination.
testSendTo(t, s, "\x06", ep2, nil)
}
func testRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr, expectedSrcAddr tcpip.Address) {
r, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
defer r.Release()
if r.LocalAddress != expectedSrcAddr {
t.Fatalf("Bad source address: expected %v, got %v", expectedSrcAddr, r.LocalAddress)
}
if r.RemoteAddress != dstAddr {
t.Fatalf("Bad destination address: expected %v, got %v", dstAddr, r.RemoteAddress)
}
}
func testNoRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr tcpip.Address) {
_, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != tcpip.ErrNoRoute {
t.Fatalf("FindRoute returned unexpected error, got = %v, want = %s", err, tcpip.ErrNoRoute)
}
}
// TestAttachToLinkEndpointImmediately tests that a LinkEndpoint is attached to
// a NetworkDispatcher when the NIC is created.
func TestAttachToLinkEndpointImmediately(t *testing.T) {
const nicID = 1
tests := []struct {
name string
nicOpts stack.NICOptions
}{
{
name: "Create enabled NIC",
nicOpts: stack.NICOptions{Disabled: false},
},
{
name: "Create disabled NIC",
nicOpts: stack.NICOptions{Disabled: true},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
e := linkEPWithMockedAttach{
LinkEndpoint: loopback.New(),
}
if err := s.CreateNICWithOptions(nicID, &e, test.nicOpts); err != nil {
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, test.nicOpts, err)
}
if !e.isAttached() {
t.Fatal("link endpoint not attached to a network dispatcher")
}
})
}
}
func TestDisableUnknownNIC(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
if err := s.DisableNIC(1); err != tcpip.ErrUnknownNICID {
t.Fatalf("got s.DisableNIC(1) = %v, want = %s", err, tcpip.ErrUnknownNICID)
}
}
func TestDisabledNICsNICInfoAndCheckNIC(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
e := loopback.New()
nicOpts := stack.NICOptions{Disabled: true}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, nicOpts, err)
}
checkNIC := func(enabled bool) {
t.Helper()
allNICInfo := s.NICInfo()
nicInfo, ok := allNICInfo[nicID]
if !ok {
t.Errorf("entry for %d missing from allNICInfo = %+v", nicID, allNICInfo)
} else if nicInfo.Flags.Running != enabled {
t.Errorf("got nicInfo.Flags.Running = %t, want = %t", nicInfo.Flags.Running, enabled)
}
if got := s.CheckNIC(nicID); got != enabled {
t.Errorf("got s.CheckNIC(%d) = %t, want = %t", nicID, got, enabled)
}
}
// NIC should initially report itself as disabled.
checkNIC(false)
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
checkNIC(true)
// If the NIC is not reporting a correct enabled status, we cannot trust the
// next check so end the test here.
if t.Failed() {
t.FailNow()
}
if err := s.DisableNIC(nicID); err != nil {
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
}
checkNIC(false)
}
func TestRemoveUnknownNIC(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
if err := s.RemoveNIC(1); err != tcpip.ErrUnknownNICID {
t.Fatalf("got s.RemoveNIC(1) = %v, want = %s", err, tcpip.ErrUnknownNICID)
}
}
func TestRemoveNIC(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
e := linkEPWithMockedAttach{
LinkEndpoint: loopback.New(),
}
if err := s.CreateNIC(nicID, &e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
}
// NIC should be present in NICInfo and attached to a NetworkDispatcher.
allNICInfo := s.NICInfo()
if _, ok := allNICInfo[nicID]; !ok {
t.Errorf("entry for %d missing from allNICInfo = %+v", nicID, allNICInfo)
}
if !e.isAttached() {
t.Fatal("link endpoint not attached to a network dispatcher")
}
// Removing a NIC should remove it from NICInfo and e should be detached from
// the NetworkDispatcher.
if err := s.RemoveNIC(nicID); err != nil {
t.Fatalf("s.RemoveNIC(%d): %s", nicID, err)
}
if nicInfo, ok := s.NICInfo()[nicID]; ok {
t.Errorf("got unexpected NICInfo entry for deleted NIC %d = %+v", nicID, nicInfo)
}
if e.isAttached() {
t.Error("link endpoint for removed NIC still attached to a network dispatcher")
}
}
func TestRouteWithDownNIC(t *testing.T) {
tests := []struct {
name string
downFn func(s *stack.Stack, nicID tcpip.NICID) *tcpip.Error
upFn func(s *stack.Stack, nicID tcpip.NICID) *tcpip.Error
}{
{
name: "Disabled NIC",
downFn: (*stack.Stack).DisableNIC,
upFn: (*stack.Stack).EnableNIC,
},
// Once a NIC is removed, it cannot be brought up.
{
name: "Removed NIC",
downFn: (*stack.Stack).RemoveNIC,
},
}
const unspecifiedNIC = 0
const nicID1 = 1
const nicID2 = 2
const addr1 = tcpip.Address("\x01")
const addr2 = tcpip.Address("\x02")
const nic1Dst = tcpip.Address("\x05")
const nic2Dst = tcpip.Address("\x06")
setup := func(t *testing.T) (*stack.Stack, *channel.Endpoint, *channel.Endpoint) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep1 := channel.New(1, defaultMTU, "")
if err := s.CreateNIC(nicID1, ep1); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID1, err)
}
if err := s.AddAddress(nicID1, fakeNetNumber, addr1); err != nil {
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID1, fakeNetNumber, addr1, err)
}
ep2 := channel.New(1, defaultMTU, "")
if err := s.CreateNIC(nicID2, ep2); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID2, err)
}
if err := s.AddAddress(nicID2, fakeNetNumber, addr2); err != nil {
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID2, fakeNetNumber, addr2, err)
}
// Set a route table that sends all packets with odd destination
// addresses through the first NIC, and all even destination address
// through the second one.
{
subnet0, err := tcpip.NewSubnet("\x00", "\x01")
if err != nil {
t.Fatal(err)
}
subnet1, err := tcpip.NewSubnet("\x01", "\x01")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{
{Destination: subnet1, Gateway: "\x00", NIC: nicID1},
{Destination: subnet0, Gateway: "\x00", NIC: nicID2},
})
}
return s, ep1, ep2
}
// Tests that routes through a down NIC are not used when looking up a route
// for a destination.
t.Run("Find", func(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s, _, _ := setup(t)
// Test routes to odd address.
testRoute(t, s, unspecifiedNIC, "", "\x05", addr1)
testRoute(t, s, unspecifiedNIC, addr1, "\x05", addr1)
testRoute(t, s, nicID1, addr1, "\x05", addr1)
// Test routes to even address.
testRoute(t, s, unspecifiedNIC, "", "\x06", addr2)
testRoute(t, s, unspecifiedNIC, addr2, "\x06", addr2)
testRoute(t, s, nicID2, addr2, "\x06", addr2)
// Bringing NIC1 down should result in no routes to odd addresses. Routes to
// even addresses should continue to be available as NIC2 is still up.
if err := test.downFn(s, nicID1); err != nil {
t.Fatalf("test.downFn(_, %d): %s", nicID1, err)
}
testNoRoute(t, s, unspecifiedNIC, "", nic1Dst)
testNoRoute(t, s, unspecifiedNIC, addr1, nic1Dst)
testNoRoute(t, s, nicID1, addr1, nic1Dst)
testRoute(t, s, unspecifiedNIC, "", nic2Dst, addr2)
testRoute(t, s, unspecifiedNIC, addr2, nic2Dst, addr2)
testRoute(t, s, nicID2, addr2, nic2Dst, addr2)
// Bringing NIC2 down should result in no routes to even addresses. No
// route should be available to any address as routes to odd addresses
// were made unavailable by bringing NIC1 down above.
if err := test.downFn(s, nicID2); err != nil {
t.Fatalf("test.downFn(_, %d): %s", nicID2, err)
}
testNoRoute(t, s, unspecifiedNIC, "", nic1Dst)
testNoRoute(t, s, unspecifiedNIC, addr1, nic1Dst)
testNoRoute(t, s, nicID1, addr1, nic1Dst)
testNoRoute(t, s, unspecifiedNIC, "", nic2Dst)
testNoRoute(t, s, unspecifiedNIC, addr2, nic2Dst)
testNoRoute(t, s, nicID2, addr2, nic2Dst)
if upFn := test.upFn; upFn != nil {
// Bringing NIC1 up should make routes to odd addresses available
// again. Routes to even addresses should continue to be unavailable
// as NIC2 is still down.
if err := upFn(s, nicID1); err != nil {
t.Fatalf("test.upFn(_, %d): %s", nicID1, err)
}
testRoute(t, s, unspecifiedNIC, "", nic1Dst, addr1)
testRoute(t, s, unspecifiedNIC, addr1, nic1Dst, addr1)
testRoute(t, s, nicID1, addr1, nic1Dst, addr1)
testNoRoute(t, s, unspecifiedNIC, "", nic2Dst)
testNoRoute(t, s, unspecifiedNIC, addr2, nic2Dst)
testNoRoute(t, s, nicID2, addr2, nic2Dst)
}
})
}
})
// Tests that writing a packet using a Route through a down NIC fails.
t.Run("WritePacket", func(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s, ep1, ep2 := setup(t)
r1, err := s.FindRoute(nicID1, addr1, nic1Dst, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Errorf("FindRoute(%d, %s, %s, %d, false): %s", nicID1, addr1, nic1Dst, fakeNetNumber, err)
}
defer r1.Release()
r2, err := s.FindRoute(nicID2, addr2, nic2Dst, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Errorf("FindRoute(%d, %s, %s, %d, false): %s", nicID2, addr2, nic2Dst, fakeNetNumber, err)
}
defer r2.Release()
// If we failed to get routes r1 or r2, we cannot proceed with the test.
if t.Failed() {
t.FailNow()
}
buf := buffer.View([]byte{1})
testSend(t, r1, ep1, buf)
testSend(t, r2, ep2, buf)
// Writes with Routes that use NIC1 after being brought down should fail.
if err := test.downFn(s, nicID1); err != nil {
t.Fatalf("test.downFn(_, %d): %s", nicID1, err)
}
testFailingSend(t, r1, ep1, buf, tcpip.ErrInvalidEndpointState)
testSend(t, r2, ep2, buf)
// Writes with Routes that use NIC2 after being brought down should fail.
if err := test.downFn(s, nicID2); err != nil {
t.Fatalf("test.downFn(_, %d): %s", nicID2, err)
}
testFailingSend(t, r1, ep1, buf, tcpip.ErrInvalidEndpointState)
testFailingSend(t, r2, ep2, buf, tcpip.ErrInvalidEndpointState)
if upFn := test.upFn; upFn != nil {
// Writes with Routes that use NIC1 after being brought up should
// succeed.
//
// TODO(b/147015577): Should we instead completely invalidate all
// Routes that were bound to a NIC that was brought down at some
// point?
if err := upFn(s, nicID1); err != nil {
t.Fatalf("test.upFn(_, %d): %s", nicID1, err)
}
testSend(t, r1, ep1, buf)
testFailingSend(t, r2, ep2, buf, tcpip.ErrInvalidEndpointState)
}
})
}
})
}
func TestRoutes(t *testing.T) {
// Create a stack with the fake network protocol, two nics, and two
// addresses per nic, the first nic has odd address, the second one has
// even addresses.
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep1 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep1); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil {
t.Fatal("AddAddress failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x03"); err != nil {
t.Fatal("AddAddress failed:", err)
}
ep2 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(2, ep2); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(2, fakeNetNumber, "\x02"); err != nil {
t.Fatal("AddAddress failed:", err)
}
if err := s.AddAddress(2, fakeNetNumber, "\x04"); err != nil {
t.Fatal("AddAddress failed:", err)
}
// Set a route table that sends all packets with odd destination
// addresses through the first NIC, and all even destination address
// through the second one.
{
subnet0, err := tcpip.NewSubnet("\x00", "\x01")
if err != nil {
t.Fatal(err)
}
subnet1, err := tcpip.NewSubnet("\x01", "\x01")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{
{Destination: subnet1, Gateway: "\x00", NIC: 1},
{Destination: subnet0, Gateway: "\x00", NIC: 2},
})
}
// Test routes to odd address.
testRoute(t, s, 0, "", "\x05", "\x01")
testRoute(t, s, 0, "\x01", "\x05", "\x01")
testRoute(t, s, 1, "\x01", "\x05", "\x01")
testRoute(t, s, 0, "\x03", "\x05", "\x03")
testRoute(t, s, 1, "\x03", "\x05", "\x03")
// Test routes to even address.
testRoute(t, s, 0, "", "\x06", "\x02")
testRoute(t, s, 0, "\x02", "\x06", "\x02")
testRoute(t, s, 2, "\x02", "\x06", "\x02")
testRoute(t, s, 0, "\x04", "\x06", "\x04")
testRoute(t, s, 2, "\x04", "\x06", "\x04")
// Try to send to odd numbered address from even numbered ones, then
// vice-versa.
testNoRoute(t, s, 0, "\x02", "\x05")
testNoRoute(t, s, 2, "\x02", "\x05")
testNoRoute(t, s, 0, "\x04", "\x05")
testNoRoute(t, s, 2, "\x04", "\x05")
testNoRoute(t, s, 0, "\x01", "\x06")
testNoRoute(t, s, 1, "\x01", "\x06")
testNoRoute(t, s, 0, "\x03", "\x06")
testNoRoute(t, s, 1, "\x03", "\x06")
}
func TestAddressRemoval(t *testing.T) {
const localAddrByte byte = 0x01
localAddr := tcpip.Address([]byte{localAddrByte})
remoteAddr := tcpip.Address("\x02")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
// Send and receive packets, and verify they are received.
buf[0] = localAddrByte
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
// Remove the address, then check that send/receive doesn't work anymore.
if err := s.RemoveAddress(1, localAddr); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
// Check that removing the same address fails.
if err := s.RemoveAddress(1, localAddr); err != tcpip.ErrBadLocalAddress {
t.Fatalf("RemoveAddress returned unexpected error, got = %v, want = %s", err, tcpip.ErrBadLocalAddress)
}
}
func TestAddressRemovalWithRouteHeld(t *testing.T) {
const localAddrByte byte = 0x01
localAddr := tcpip.Address([]byte{localAddrByte})
remoteAddr := tcpip.Address("\x02")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatalf("CreateNIC failed: %v", err)
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
if err := s.AddAddress(1, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
r, err := s.FindRoute(0, "", remoteAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
// Send and receive packets, and verify they are received.
buf[0] = localAddrByte
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSend(t, r, ep, nil)
testSendTo(t, s, remoteAddr, ep, nil)
// Remove the address, then check that send/receive doesn't work anymore.
if err := s.RemoveAddress(1, localAddr); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
testFailingSend(t, r, ep, nil, tcpip.ErrInvalidEndpointState)
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
// Check that removing the same address fails.
if err := s.RemoveAddress(1, localAddr); err != tcpip.ErrBadLocalAddress {
t.Fatalf("RemoveAddress returned unexpected error, got = %v, want = %s", err, tcpip.ErrBadLocalAddress)
}
}
func verifyAddress(t *testing.T, s *stack.Stack, nicID tcpip.NICID, addr tcpip.Address) {
t.Helper()
info, ok := s.NICInfo()[nicID]
if !ok {
t.Fatalf("NICInfo() failed to find nicID=%d", nicID)
}
if len(addr) == 0 {
// No address given, verify that there is no address assigned to the NIC.
for _, a := range info.ProtocolAddresses {
if a.Protocol == fakeNetNumber && a.AddressWithPrefix != (tcpip.AddressWithPrefix{}) {
t.Errorf("verify no-address: got = %s, want = %s", a.AddressWithPrefix, (tcpip.AddressWithPrefix{}))
}
}
return
}
// Address given, verify the address is assigned to the NIC and no other
// address is.
found := false
for _, a := range info.ProtocolAddresses {
if a.Protocol == fakeNetNumber {
if a.AddressWithPrefix.Address == addr {
found = true
} else {
t.Errorf("verify address: got = %s, want = %s", a.AddressWithPrefix.Address, addr)
}
}
}
if !found {
t.Errorf("verify address: couldn't find %s on the NIC", addr)
}
}
func TestEndpointExpiration(t *testing.T) {
const (
localAddrByte byte = 0x01
remoteAddr tcpip.Address = "\x03"
noAddr tcpip.Address = ""
nicID tcpip.NICID = 1
)
localAddr := tcpip.Address([]byte{localAddrByte})
for _, promiscuous := range []bool{true, false} {
for _, spoofing := range []bool{true, false} {
t.Run(fmt.Sprintf("promiscuous=%t spoofing=%t", promiscuous, spoofing), func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
buf[0] = localAddrByte
if promiscuous {
if err := s.SetPromiscuousMode(nicID, true); err != nil {
t.Fatal("SetPromiscuousMode failed:", err)
}
}
if spoofing {
if err := s.SetSpoofing(nicID, true); err != nil {
t.Fatal("SetSpoofing failed:", err)
}
}
// 1. No Address yet, send should only work for spoofing, receive for
// promiscuous mode.
//-----------------------
verifyAddress(t, s, nicID, noAddr)
if promiscuous {
testRecv(t, fakeNet, localAddrByte, ep, buf)
} else {
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
if spoofing {
// FIXME(b/139841518):Spoofing doesn't work if there is no primary address.
// testSendTo(t, s, remoteAddr, ep, nil)
} else {
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
}
// 2. Add Address, everything should work.
//-----------------------
if err := s.AddAddress(nicID, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
verifyAddress(t, s, nicID, localAddr)
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
// 3. Remove the address, send should only work for spoofing, receive
// for promiscuous mode.
//-----------------------
if err := s.RemoveAddress(nicID, localAddr); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
verifyAddress(t, s, nicID, noAddr)
if promiscuous {
testRecv(t, fakeNet, localAddrByte, ep, buf)
} else {
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
if spoofing {
// FIXME(b/139841518):Spoofing doesn't work if there is no primary address.
// testSendTo(t, s, remoteAddr, ep, nil)
} else {
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
}
// 4. Add Address back, everything should work again.
//-----------------------
if err := s.AddAddress(nicID, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
verifyAddress(t, s, nicID, localAddr)
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
// 5. Take a reference to the endpoint by getting a route. Verify that
// we can still send/receive, including sending using the route.
//-----------------------
r, err := s.FindRoute(0, "", remoteAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
testSend(t, r, ep, nil)
// 6. Remove the address. Send should only work for spoofing, receive
// for promiscuous mode.
//-----------------------
if err := s.RemoveAddress(nicID, localAddr); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
verifyAddress(t, s, nicID, noAddr)
if promiscuous {
testRecv(t, fakeNet, localAddrByte, ep, buf)
} else {
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
if spoofing {
testSend(t, r, ep, nil)
testSendTo(t, s, remoteAddr, ep, nil)
} else {
testFailingSend(t, r, ep, nil, tcpip.ErrInvalidEndpointState)
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
}
// 7. Add Address back, everything should work again.
//-----------------------
if err := s.AddAddress(nicID, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
verifyAddress(t, s, nicID, localAddr)
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
testSend(t, r, ep, nil)
// 8. Remove the route, sendTo/recv should still work.
//-----------------------
r.Release()
verifyAddress(t, s, nicID, localAddr)
testRecv(t, fakeNet, localAddrByte, ep, buf)
testSendTo(t, s, remoteAddr, ep, nil)
// 9. Remove the address. Send should only work for spoofing, receive
// for promiscuous mode.
//-----------------------
if err := s.RemoveAddress(nicID, localAddr); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
verifyAddress(t, s, nicID, noAddr)
if promiscuous {
testRecv(t, fakeNet, localAddrByte, ep, buf)
} else {
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
if spoofing {
// FIXME(b/139841518):Spoofing doesn't work if there is no primary address.
// testSendTo(t, s, remoteAddr, ep, nil)
} else {
testFailingSendTo(t, s, remoteAddr, ep, nil, tcpip.ErrNoRoute)
}
})
}
}
}
func TestPromiscuousMode(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
// Write a packet, and check that it doesn't get delivered as we don't
// have a matching endpoint.
const localAddrByte byte = 0x01
buf[0] = localAddrByte
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
// Set promiscuous mode, then check that packet is delivered.
if err := s.SetPromiscuousMode(1, true); err != nil {
t.Fatal("SetPromiscuousMode failed:", err)
}
testRecv(t, fakeNet, localAddrByte, ep, buf)
// Check that we can't get a route as there is no local address.
_, err := s.FindRoute(0, "", "\x02", fakeNetNumber, false /* multicastLoop */)
if err != tcpip.ErrNoRoute {
t.Fatalf("FindRoute returned unexpected error: got = %v, want = %s", err, tcpip.ErrNoRoute)
}
// Set promiscuous mode to false, then check that packet can't be
// delivered anymore.
if err := s.SetPromiscuousMode(1, false); err != nil {
t.Fatal("SetPromiscuousMode failed:", err)
}
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
func TestSpoofingWithAddress(t *testing.T) {
localAddr := tcpip.Address("\x01")
nonExistentLocalAddr := tcpip.Address("\x02")
dstAddr := tcpip.Address("\x03")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
if err := s.AddAddress(1, fakeNetNumber, localAddr); err != nil {
t.Fatal("AddAddress failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
// With address spoofing disabled, FindRoute does not permit an address
// that was not added to the NIC to be used as the source.
r, err := s.FindRoute(0, nonExistentLocalAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err == nil {
t.Errorf("FindRoute succeeded with route %+v when it should have failed", r)
}
// With address spoofing enabled, FindRoute permits any address to be used
// as the source.
if err := s.SetSpoofing(1, true); err != nil {
t.Fatal("SetSpoofing failed:", err)
}
r, err = s.FindRoute(0, nonExistentLocalAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
if r.LocalAddress != nonExistentLocalAddr {
t.Errorf("got Route.LocalAddress = %s, want = %s", r.LocalAddress, nonExistentLocalAddr)
}
if r.RemoteAddress != dstAddr {
t.Errorf("got Route.RemoteAddress = %s, want = %s", r.RemoteAddress, dstAddr)
}
// Sending a packet works.
testSendTo(t, s, dstAddr, ep, nil)
testSend(t, r, ep, nil)
// FindRoute should also work with a local address that exists on the NIC.
r, err = s.FindRoute(0, localAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
if r.LocalAddress != localAddr {
t.Errorf("got Route.LocalAddress = %s, want = %s", r.LocalAddress, nonExistentLocalAddr)
}
if r.RemoteAddress != dstAddr {
t.Errorf("got Route.RemoteAddress = %s, want = %s", r.RemoteAddress, dstAddr)
}
// Sending a packet using the route works.
testSend(t, r, ep, nil)
}
func TestSpoofingNoAddress(t *testing.T) {
nonExistentLocalAddr := tcpip.Address("\x01")
dstAddr := tcpip.Address("\x02")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
// With address spoofing disabled, FindRoute does not permit an address
// that was not added to the NIC to be used as the source.
r, err := s.FindRoute(0, nonExistentLocalAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err == nil {
t.Errorf("FindRoute succeeded with route %+v when it should have failed", r)
}
// Sending a packet fails.
testFailingSendTo(t, s, dstAddr, ep, nil, tcpip.ErrNoRoute)
// With address spoofing enabled, FindRoute permits any address to be used
// as the source.
if err := s.SetSpoofing(1, true); err != nil {
t.Fatal("SetSpoofing failed:", err)
}
r, err = s.FindRoute(0, nonExistentLocalAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
if r.LocalAddress != nonExistentLocalAddr {
t.Errorf("got Route.LocalAddress = %s, want = %s", r.LocalAddress, nonExistentLocalAddr)
}
if r.RemoteAddress != dstAddr {
t.Errorf("got Route.RemoteAddress = %s, want = %s", r.RemoteAddress, dstAddr)
}
// Sending a packet works.
// FIXME(b/139841518):Spoofing doesn't work if there is no primary address.
// testSendTo(t, s, remoteAddr, ep, nil)
}
func verifyRoute(gotRoute, wantRoute stack.Route) error {
if gotRoute.LocalAddress != wantRoute.LocalAddress {
return fmt.Errorf("bad local address: got %s, want = %s", gotRoute.LocalAddress, wantRoute.LocalAddress)
}
if gotRoute.RemoteAddress != wantRoute.RemoteAddress {
return fmt.Errorf("bad remote address: got %s, want = %s", gotRoute.RemoteAddress, wantRoute.RemoteAddress)
}
if gotRoute.RemoteLinkAddress != wantRoute.RemoteLinkAddress {
return fmt.Errorf("bad remote link address: got %s, want = %s", gotRoute.RemoteLinkAddress, wantRoute.RemoteLinkAddress)
}
if gotRoute.NextHop != wantRoute.NextHop {
return fmt.Errorf("bad next-hop address: got %s, want = %s", gotRoute.NextHop, wantRoute.NextHop)
}
return nil
}
func TestOutgoingBroadcastWithEmptyRouteTable(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
s.SetRouteTable([]tcpip.Route{})
// If there is no endpoint, it won't work.
if _, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */); err != tcpip.ErrNetworkUnreachable {
t.Fatalf("got FindRoute(1, %s, %s, %d) = %s, want = %s", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err, tcpip.ErrNetworkUnreachable)
}
protoAddr := tcpip.ProtocolAddress{Protocol: fakeNetNumber, AddressWithPrefix: tcpip.AddressWithPrefix{header.IPv4Any, 0}}
if err := s.AddProtocolAddress(1, protoAddr); err != nil {
t.Fatalf("AddProtocolAddress(1, %s) failed: %s", protoAddr, err)
}
r, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(1, %s, %s, %d) failed: %s", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err)
}
if err := verifyRoute(r, stack.Route{LocalAddress: header.IPv4Any, RemoteAddress: header.IPv4Broadcast}); err != nil {
t.Errorf("FindRoute(1, %s, %s, %d) returned unexpected Route: %s)", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err)
}
// If the NIC doesn't exist, it won't work.
if _, err := s.FindRoute(2, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */); err != tcpip.ErrNetworkUnreachable {
t.Fatalf("got FindRoute(2, %s, %s, %d) = %s want = %s", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err, tcpip.ErrNetworkUnreachable)
}
}
func TestOutgoingBroadcastWithRouteTable(t *testing.T) {
defaultAddr := tcpip.AddressWithPrefix{header.IPv4Any, 0}
// Local subnet on NIC1: 192.168.1.58/24, gateway 192.168.1.1.
nic1Addr := tcpip.AddressWithPrefix{"\xc0\xa8\x01\x3a", 24}
nic1Gateway := tcpip.Address("\xc0\xa8\x01\x01")
// Local subnet on NIC2: 10.10.10.5/24, gateway 10.10.10.1.
nic2Addr := tcpip.AddressWithPrefix{"\x0a\x0a\x0a\x05", 24}
nic2Gateway := tcpip.Address("\x0a\x0a\x0a\x01")
// Create a new stack with two NICs.
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatalf("CreateNIC failed: %s", err)
}
if err := s.CreateNIC(2, ep); err != nil {
t.Fatalf("CreateNIC failed: %s", err)
}
nic1ProtoAddr := tcpip.ProtocolAddress{fakeNetNumber, nic1Addr}
if err := s.AddProtocolAddress(1, nic1ProtoAddr); err != nil {
t.Fatalf("AddProtocolAddress(1, %s) failed: %s", nic1ProtoAddr, err)
}
nic2ProtoAddr := tcpip.ProtocolAddress{fakeNetNumber, nic2Addr}
if err := s.AddProtocolAddress(2, nic2ProtoAddr); err != nil {
t.Fatalf("AddAddress(2, %s) failed: %s", nic2ProtoAddr, err)
}
// Set the initial route table.
rt := []tcpip.Route{
{Destination: nic1Addr.Subnet(), NIC: 1},
{Destination: nic2Addr.Subnet(), NIC: 2},
{Destination: defaultAddr.Subnet(), Gateway: nic2Gateway, NIC: 2},
{Destination: defaultAddr.Subnet(), Gateway: nic1Gateway, NIC: 1},
}
s.SetRouteTable(rt)
// When an interface is given, the route for a broadcast goes through it.
r, err := s.FindRoute(1, nic1Addr.Address, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(1, %s, %s, %d) failed: %s", nic1Addr.Address, header.IPv4Broadcast, fakeNetNumber, err)
}
if err := verifyRoute(r, stack.Route{LocalAddress: nic1Addr.Address, RemoteAddress: header.IPv4Broadcast}); err != nil {
t.Errorf("FindRoute(1, %s, %s, %d) returned unexpected Route: %s)", nic1Addr.Address, header.IPv4Broadcast, fakeNetNumber, err)
}
// When an interface is not given, it consults the route table.
// 1. Case: Using the default route.
r, err = s.FindRoute(0, "", header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(0, \"\", %s, %d) failed: %s", header.IPv4Broadcast, fakeNetNumber, err)
}
if err := verifyRoute(r, stack.Route{LocalAddress: nic2Addr.Address, RemoteAddress: header.IPv4Broadcast}); err != nil {
t.Errorf("FindRoute(0, \"\", %s, %d) returned unexpected Route: %s)", header.IPv4Broadcast, fakeNetNumber, err)
}
// 2. Case: Having an explicit route for broadcast will select that one.
rt = append(
[]tcpip.Route{
{Destination: tcpip.AddressWithPrefix{header.IPv4Broadcast, 8 * header.IPv4AddressSize}.Subnet(), NIC: 1},
},
rt...,
)
s.SetRouteTable(rt)
r, err = s.FindRoute(0, "", header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(0, \"\", %s, %d) failed: %s", header.IPv4Broadcast, fakeNetNumber, err)
}
if err := verifyRoute(r, stack.Route{LocalAddress: nic1Addr.Address, RemoteAddress: header.IPv4Broadcast}); err != nil {
t.Errorf("FindRoute(0, \"\", %s, %d) returned unexpected Route: %s)", header.IPv4Broadcast, fakeNetNumber, err)
}
}
func TestMulticastOrIPv6LinkLocalNeedsNoRoute(t *testing.T) {
for _, tc := range []struct {
name string
routeNeeded bool
address tcpip.Address
}{
// IPv4 multicast address range: 224.0.0.0 - 239.255.255.255
// <=> 0xe0.0x00.0x00.0x00 - 0xef.0xff.0xff.0xff
{"IPv4 Multicast 1", false, "\xe0\x00\x00\x00"},
{"IPv4 Multicast 2", false, "\xef\xff\xff\xff"},
{"IPv4 Unicast 1", true, "\xdf\xff\xff\xff"},
{"IPv4 Unicast 2", true, "\xf0\x00\x00\x00"},
{"IPv4 Unicast 3", true, "\x00\x00\x00\x00"},
// IPv6 multicast address is 0xff[8] + flags[4] + scope[4] + groupId[112]
{"IPv6 Multicast 1", false, "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Multicast 2", false, "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Multicast 3", false, "\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"},
// IPv6 link-local address starts with fe80::/10.
{"IPv6 Unicast Link-Local 1", false, "\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Link-Local 2", false, "\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"},
{"IPv6 Unicast Link-Local 3", false, "\xfe\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff"},
{"IPv6 Unicast Link-Local 4", false, "\xfe\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Link-Local 5", false, "\xfe\xbf\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"},
// IPv6 addresses that are neither multicast nor link-local.
{"IPv6 Unicast Not Link-Local 1", true, "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Not Link-Local 2", true, "\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"},
{"IPv6 Unicast Not Link-local 3", true, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Not Link-Local 4", true, "\xfe\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Not Link-Local 5", true, "\xfe\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Not Link-Local 6", true, "\xfd\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
{"IPv6 Unicast Not Link-Local 7", true, "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
} {
t.Run(tc.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
s.SetRouteTable([]tcpip.Route{})
var anyAddr tcpip.Address
if len(tc.address) == header.IPv4AddressSize {
anyAddr = header.IPv4Any
} else {
anyAddr = header.IPv6Any
}
want := tcpip.ErrNetworkUnreachable
if tc.routeNeeded {
want = tcpip.ErrNoRoute
}
// If there is no endpoint, it won't work.
if _, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); err != want {
t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, want)
}
if err := s.AddAddress(1, fakeNetNumber, anyAddr); err != nil {
t.Fatalf("AddAddress(%v, %v) failed: %v", fakeNetNumber, anyAddr, err)
}
if r, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); tc.routeNeeded {
// Route table is empty but we need a route, this should cause an error.
if err != tcpip.ErrNoRoute {
t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, tcpip.ErrNoRoute)
}
} else {
if err != nil {
t.Fatalf("FindRoute(1, %v, %v, %v) failed: %v", anyAddr, tc.address, fakeNetNumber, err)
}
if r.LocalAddress != anyAddr {
t.Errorf("Bad local address: got %v, want = %v", r.LocalAddress, anyAddr)
}
if r.RemoteAddress != tc.address {
t.Errorf("Bad remote address: got %v, want = %v", r.RemoteAddress, tc.address)
}
}
// If the NIC doesn't exist, it won't work.
if _, err := s.FindRoute(2, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); err != want {
t.Fatalf("got FindRoute(2, %v, %v, %v) = %v want = %v", anyAddr, tc.address, fakeNetNumber, err, want)
}
})
}
}
// Add a range of addresses, then check that a packet is delivered.
func TestAddressRangeAcceptsMatchingPacket(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
const localAddrByte byte = 0x01
buf[0] = localAddrByte
subnet, err := tcpip.NewSubnet(tcpip.Address("\x00"), tcpip.AddressMask("\xF0"))
if err != nil {
t.Fatal("NewSubnet failed:", err)
}
if err := s.AddAddressRange(1, fakeNetNumber, subnet); err != nil {
t.Fatal("AddAddressRange failed:", err)
}
testRecv(t, fakeNet, localAddrByte, ep, buf)
}
func testNicForAddressRange(t *testing.T, nicID tcpip.NICID, s *stack.Stack, subnet tcpip.Subnet, rangeExists bool) {
t.Helper()
// Loop over all addresses and check them.
numOfAddresses := 1 << uint(8-subnet.Prefix())
if numOfAddresses < 1 || numOfAddresses > 255 {
t.Fatalf("got numOfAddresses = %d, want = [1 .. 255] (subnet=%s)", numOfAddresses, subnet)
}
addrBytes := []byte(subnet.ID())
for i := 0; i < numOfAddresses; i++ {
addr := tcpip.Address(addrBytes)
wantNicID := nicID
// The subnet and broadcast addresses are skipped.
if !rangeExists || addr == subnet.ID() || addr == subnet.Broadcast() {
wantNicID = 0
}
if gotNicID := s.CheckLocalAddress(0, fakeNetNumber, addr); gotNicID != wantNicID {
t.Errorf("got CheckLocalAddress(0, %d, %s) = %d, want = %d", fakeNetNumber, addr, gotNicID, wantNicID)
}
addrBytes[0]++
}
// Trying the next address should always fail since it is outside the range.
if gotNicID := s.CheckLocalAddress(0, fakeNetNumber, tcpip.Address(addrBytes)); gotNicID != 0 {
t.Errorf("got CheckLocalAddress(0, %d, %s) = %d, want = %d", fakeNetNumber, tcpip.Address(addrBytes), gotNicID, 0)
}
}
// Set a range of addresses, then remove it again, and check at each step that
// CheckLocalAddress returns the correct NIC for each address or zero if not
// existent.
func TestCheckLocalAddressForSubnet(t *testing.T) {
const nicID tcpip.NICID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: nicID}})
}
subnet, err := tcpip.NewSubnet(tcpip.Address("\xa0"), tcpip.AddressMask("\xf0"))
if err != nil {
t.Fatal("NewSubnet failed:", err)
}
testNicForAddressRange(t, nicID, s, subnet, false /* rangeExists */)
if err := s.AddAddressRange(nicID, fakeNetNumber, subnet); err != nil {
t.Fatal("AddAddressRange failed:", err)
}
testNicForAddressRange(t, nicID, s, subnet, true /* rangeExists */)
if err := s.RemoveAddressRange(nicID, subnet); err != nil {
t.Fatal("RemoveAddressRange failed:", err)
}
testNicForAddressRange(t, nicID, s, subnet, false /* rangeExists */)
}
// Set a range of addresses, then send a packet to a destination outside the
// range and then check it doesn't get delivered.
func TestAddressRangeRejectsNonmatchingPacket(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
fakeNet := s.NetworkProtocolInstance(fakeNetNumber).(*fakeNetworkProtocol)
buf := buffer.NewView(30)
const localAddrByte byte = 0x01
buf[0] = localAddrByte
subnet, err := tcpip.NewSubnet(tcpip.Address("\x10"), tcpip.AddressMask("\xF0"))
if err != nil {
t.Fatal("NewSubnet failed:", err)
}
if err := s.AddAddressRange(1, fakeNetNumber, subnet); err != nil {
t.Fatal("AddAddressRange failed:", err)
}
testFailingRecv(t, fakeNet, localAddrByte, ep, buf)
}
func TestNetworkOptions(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
TransportProtocols: []stack.TransportProtocol{},
})
// Try an unsupported network protocol.
if err := s.SetNetworkProtocolOption(tcpip.NetworkProtocolNumber(99999), fakeNetGoodOption(false)); err != tcpip.ErrUnknownProtocol {
t.Fatalf("SetNetworkProtocolOption(fakeNet2, blah, false) = %v, want = tcpip.ErrUnknownProtocol", err)
}
testCases := []struct {
option interface{}
wantErr *tcpip.Error
verifier func(t *testing.T, p stack.NetworkProtocol)
}{
{fakeNetGoodOption(true), nil, func(t *testing.T, p stack.NetworkProtocol) {
t.Helper()
fakeNet := p.(*fakeNetworkProtocol)
if fakeNet.opts.good != true {
t.Fatalf("fakeNet.opts.good = false, want = true")
}
var v fakeNetGoodOption
if err := s.NetworkProtocolOption(fakeNetNumber, &v); err != nil {
t.Fatalf("s.NetworkProtocolOption(fakeNetNumber, &v) = %v, want = nil, where v is option %T", v, err)
}
if v != true {
t.Fatalf("s.NetworkProtocolOption(fakeNetNumber, &v) returned v = %v, want = true", v)
}
}},
{fakeNetBadOption(true), tcpip.ErrUnknownProtocolOption, nil},
{fakeNetInvalidValueOption(1), tcpip.ErrInvalidOptionValue, nil},
}
for _, tc := range testCases {
if got := s.SetNetworkProtocolOption(fakeNetNumber, tc.option); got != tc.wantErr {
t.Errorf("s.SetNetworkProtocolOption(fakeNet, %v) = %v, want = %v", tc.option, got, tc.wantErr)
}
if tc.verifier != nil {
tc.verifier(t, s.NetworkProtocolInstance(fakeNetNumber))
}
}
}
func stackContainsAddressRange(s *stack.Stack, id tcpip.NICID, addrRange tcpip.Subnet) bool {
ranges, ok := s.NICAddressRanges()[id]
if !ok {
return false
}
for _, r := range ranges {
if r == addrRange {
return true
}
}
return false
}
func TestAddresRangeAddRemove(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
addr := tcpip.Address("\x01\x01\x01\x01")
mask := tcpip.AddressMask(strings.Repeat("\xff", len(addr)))
addrRange, err := tcpip.NewSubnet(addr, mask)
if err != nil {
t.Fatal("NewSubnet failed:", err)
}
if got, want := stackContainsAddressRange(s, 1, addrRange), false; got != want {
t.Fatalf("got stackContainsAddressRange(...) = %t, want = %t", got, want)
}
if err := s.AddAddressRange(1, fakeNetNumber, addrRange); err != nil {
t.Fatal("AddAddressRange failed:", err)
}
if got, want := stackContainsAddressRange(s, 1, addrRange), true; got != want {
t.Fatalf("got stackContainsAddressRange(...) = %t, want = %t", got, want)
}
if err := s.RemoveAddressRange(1, addrRange); err != nil {
t.Fatal("RemoveAddressRange failed:", err)
}
if got, want := stackContainsAddressRange(s, 1, addrRange), false; got != want {
t.Fatalf("got stackContainsAddressRange(...) = %t, want = %t", got, want)
}
}
func TestGetMainNICAddressAddPrimaryNonPrimary(t *testing.T) {
for _, addrLen := range []int{4, 16} {
t.Run(fmt.Sprintf("addrLen=%d", addrLen), func(t *testing.T) {
for canBe := 0; canBe < 3; canBe++ {
t.Run(fmt.Sprintf("canBe=%d", canBe), func(t *testing.T) {
for never := 0; never < 3; never++ {
t.Run(fmt.Sprintf("never=%d", never), func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
// Insert <canBe> primary and <never> never-primary addresses.
// Each one will add a network endpoint to the NIC.
primaryAddrAdded := make(map[tcpip.AddressWithPrefix]struct{})
for i := 0; i < canBe+never; i++ {
var behavior stack.PrimaryEndpointBehavior
if i < canBe {
behavior = stack.CanBePrimaryEndpoint
} else {
behavior = stack.NeverPrimaryEndpoint
}
// Add an address and in case of a primary one include a
// prefixLen.
address := tcpip.Address(bytes.Repeat([]byte{byte(i)}, addrLen))
if behavior == stack.CanBePrimaryEndpoint {
protocolAddress := tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: address,
PrefixLen: addrLen * 8,
},
}
if err := s.AddProtocolAddressWithOptions(1, protocolAddress, behavior); err != nil {
t.Fatal("AddProtocolAddressWithOptions failed:", err)
}
// Remember the address/prefix.
primaryAddrAdded[protocolAddress.AddressWithPrefix] = struct{}{}
} else {
if err := s.AddAddressWithOptions(1, fakeNetNumber, address, behavior); err != nil {
t.Fatal("AddAddressWithOptions failed:", err)
}
}
}
// Check that GetMainNICAddress returns an address if at least
// one primary address was added. In that case make sure the
// address/prefixLen matches what we added.
gotAddr, err := s.GetMainNICAddress(1, fakeNetNumber)
if err != nil {
t.Fatal("GetMainNICAddress failed:", err)
}
if len(primaryAddrAdded) == 0 {
// No primary addresses present.
if wantAddr := (tcpip.AddressWithPrefix{}); gotAddr != wantAddr {
t.Fatalf("GetMainNICAddress: got addr = %s, want = %s", gotAddr, wantAddr)
}
} else {
// At least one primary address was added, verify the returned
// address is in the list of primary addresses we added.
if _, ok := primaryAddrAdded[gotAddr]; !ok {
t.Fatalf("GetMainNICAddress: got = %s, want any in {%v}", gotAddr, primaryAddrAdded)
}
}
})
}
})
}
})
}
}
func TestGetMainNICAddressAddRemove(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
for _, tc := range []struct {
name string
address tcpip.Address
prefixLen int
}{
{"IPv4", "\x01\x01\x01\x01", 24},
{"IPv6", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 116},
} {
t.Run(tc.name, func(t *testing.T) {
protocolAddress := tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tc.address,
PrefixLen: tc.prefixLen,
},
}
if err := s.AddProtocolAddress(1, protocolAddress); err != nil {
t.Fatal("AddProtocolAddress failed:", err)
}
// Check that we get the right initial address and prefix length.
gotAddr, err := s.GetMainNICAddress(1, fakeNetNumber)
if err != nil {
t.Fatal("GetMainNICAddress failed:", err)
}
if wantAddr := protocolAddress.AddressWithPrefix; gotAddr != wantAddr {
t.Fatalf("got s.GetMainNICAddress(...) = %s, want = %s", gotAddr, wantAddr)
}
if err := s.RemoveAddress(1, protocolAddress.AddressWithPrefix.Address); err != nil {
t.Fatal("RemoveAddress failed:", err)
}
// Check that we get no address after removal.
gotAddr, err = s.GetMainNICAddress(1, fakeNetNumber)
if err != nil {
t.Fatal("GetMainNICAddress failed:", err)
}
if wantAddr := (tcpip.AddressWithPrefix{}); gotAddr != wantAddr {
t.Fatalf("got GetMainNICAddress(...) = %s, want = %s", gotAddr, wantAddr)
}
})
}
}
// Simple network address generator. Good for 255 addresses.
type addressGenerator struct{ cnt byte }
func (g *addressGenerator) next(addrLen int) tcpip.Address {
g.cnt++
return tcpip.Address(bytes.Repeat([]byte{g.cnt}, addrLen))
}
func verifyAddresses(t *testing.T, expectedAddresses, gotAddresses []tcpip.ProtocolAddress) {
t.Helper()
if len(gotAddresses) != len(expectedAddresses) {
t.Fatalf("got len(addresses) = %d, want = %d", len(gotAddresses), len(expectedAddresses))
}
sort.Slice(gotAddresses, func(i, j int) bool {
return gotAddresses[i].AddressWithPrefix.Address < gotAddresses[j].AddressWithPrefix.Address
})
sort.Slice(expectedAddresses, func(i, j int) bool {
return expectedAddresses[i].AddressWithPrefix.Address < expectedAddresses[j].AddressWithPrefix.Address
})
for i, gotAddr := range gotAddresses {
expectedAddr := expectedAddresses[i]
if gotAddr != expectedAddr {
t.Errorf("got address = %+v, wanted = %+v", gotAddr, expectedAddr)
}
}
}
func TestAddAddress(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
var addrGen addressGenerator
expectedAddresses := make([]tcpip.ProtocolAddress, 0, 2)
for _, addrLen := range []int{4, 16} {
address := addrGen.next(addrLen)
if err := s.AddAddress(nicID, fakeNetNumber, address); err != nil {
t.Fatalf("AddAddress(address=%s) failed: %s", address, err)
}
expectedAddresses = append(expectedAddresses, tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{address, fakeDefaultPrefixLen},
})
}
gotAddresses := s.AllAddresses()[nicID]
verifyAddresses(t, expectedAddresses, gotAddresses)
}
func TestAddProtocolAddress(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
var addrGen addressGenerator
addrLenRange := []int{4, 16}
prefixLenRange := []int{8, 13, 20, 32}
expectedAddresses := make([]tcpip.ProtocolAddress, 0, len(addrLenRange)*len(prefixLenRange))
for _, addrLen := range addrLenRange {
for _, prefixLen := range prefixLenRange {
protocolAddress := tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: addrGen.next(addrLen),
PrefixLen: prefixLen,
},
}
if err := s.AddProtocolAddress(nicID, protocolAddress); err != nil {
t.Errorf("AddProtocolAddress(%+v) failed: %s", protocolAddress, err)
}
expectedAddresses = append(expectedAddresses, protocolAddress)
}
}
gotAddresses := s.AllAddresses()[nicID]
verifyAddresses(t, expectedAddresses, gotAddresses)
}
func TestAddAddressWithOptions(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
addrLenRange := []int{4, 16}
behaviorRange := []stack.PrimaryEndpointBehavior{stack.CanBePrimaryEndpoint, stack.FirstPrimaryEndpoint, stack.NeverPrimaryEndpoint}
expectedAddresses := make([]tcpip.ProtocolAddress, 0, len(addrLenRange)*len(behaviorRange))
var addrGen addressGenerator
for _, addrLen := range addrLenRange {
for _, behavior := range behaviorRange {
address := addrGen.next(addrLen)
if err := s.AddAddressWithOptions(nicID, fakeNetNumber, address, behavior); err != nil {
t.Fatalf("AddAddressWithOptions(address=%s, behavior=%d) failed: %s", address, behavior, err)
}
expectedAddresses = append(expectedAddresses, tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{address, fakeDefaultPrefixLen},
})
}
}
gotAddresses := s.AllAddresses()[nicID]
verifyAddresses(t, expectedAddresses, gotAddresses)
}
func TestAddProtocolAddressWithOptions(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID, ep); err != nil {
t.Fatal("CreateNIC failed:", err)
}
addrLenRange := []int{4, 16}
prefixLenRange := []int{8, 13, 20, 32}
behaviorRange := []stack.PrimaryEndpointBehavior{stack.CanBePrimaryEndpoint, stack.FirstPrimaryEndpoint, stack.NeverPrimaryEndpoint}
expectedAddresses := make([]tcpip.ProtocolAddress, 0, len(addrLenRange)*len(prefixLenRange)*len(behaviorRange))
var addrGen addressGenerator
for _, addrLen := range addrLenRange {
for _, prefixLen := range prefixLenRange {
for _, behavior := range behaviorRange {
protocolAddress := tcpip.ProtocolAddress{
Protocol: fakeNetNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: addrGen.next(addrLen),
PrefixLen: prefixLen,
},
}
if err := s.AddProtocolAddressWithOptions(nicID, protocolAddress, behavior); err != nil {
t.Fatalf("AddProtocolAddressWithOptions(%+v, %d) failed: %s", protocolAddress, behavior, err)
}
expectedAddresses = append(expectedAddresses, protocolAddress)
}
}
}
gotAddresses := s.AllAddresses()[nicID]
verifyAddresses(t, expectedAddresses, gotAddresses)
}
func TestCreateNICWithOptions(t *testing.T) {
type callArgsAndExpect struct {
nicID tcpip.NICID
opts stack.NICOptions
err *tcpip.Error
}
tests := []struct {
desc string
calls []callArgsAndExpect
}{
{
desc: "DuplicateNICID",
calls: []callArgsAndExpect{
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{Name: "eth1"},
err: nil,
},
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{Name: "eth2"},
err: tcpip.ErrDuplicateNICID,
},
},
},
{
desc: "DuplicateName",
calls: []callArgsAndExpect{
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{Name: "lo"},
err: nil,
},
{
nicID: tcpip.NICID(2),
opts: stack.NICOptions{Name: "lo"},
err: tcpip.ErrDuplicateNICID,
},
},
},
{
desc: "Unnamed",
calls: []callArgsAndExpect{
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{},
err: nil,
},
{
nicID: tcpip.NICID(2),
opts: stack.NICOptions{},
err: nil,
},
},
},
{
desc: "UnnamedDuplicateNICID",
calls: []callArgsAndExpect{
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{},
err: nil,
},
{
nicID: tcpip.NICID(1),
opts: stack.NICOptions{},
err: tcpip.ErrDuplicateNICID,
},
},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
s := stack.New(stack.Options{})
ep := channel.New(0, 0, tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00"))
for _, call := range test.calls {
if got, want := s.CreateNICWithOptions(call.nicID, ep, call.opts), call.err; got != want {
t.Fatalf("CreateNICWithOptions(%v, _, %+v) = %v, want %v", call.nicID, call.opts, got, want)
}
}
})
}
}
func TestNICStats(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep1 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep1); err != nil {
t.Fatal("CreateNIC failed: ", err)
}
if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil {
t.Fatal("AddAddress failed:", err)
}
// Route all packets for address \x01 to NIC 1.
{
subnet, err := tcpip.NewSubnet("\x01", "\xff")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
// Send a packet to address 1.
buf := buffer.NewView(30)
ep1.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
if got, want := s.NICInfo()[1].Stats.Rx.Packets.Value(), uint64(1); got != want {
t.Errorf("got Rx.Packets.Value() = %d, want = %d", got, want)
}
if got, want := s.NICInfo()[1].Stats.Rx.Bytes.Value(), uint64(len(buf)); got != want {
t.Errorf("got Rx.Bytes.Value() = %d, want = %d", got, want)
}
payload := buffer.NewView(10)
// Write a packet out via the address for NIC 1
if err := sendTo(s, "\x01", payload); err != nil {
t.Fatal("sendTo failed: ", err)
}
want := uint64(ep1.Drain())
if got := s.NICInfo()[1].Stats.Tx.Packets.Value(); got != want {
t.Errorf("got Tx.Packets.Value() = %d, ep1.Drain() = %d", got, want)
}
if got, want := s.NICInfo()[1].Stats.Tx.Bytes.Value(), uint64(len(payload)); got != want {
t.Errorf("got Tx.Bytes.Value() = %d, want = %d", got, want)
}
}
func TestNICForwarding(t *testing.T) {
const nicID1 = 1
const nicID2 = 2
const dstAddr = tcpip.Address("\x03")
tests := []struct {
name string
headerLen uint16
}{
{
name: "Zero header length",
},
{
name: "Non-zero header length",
headerLen: 16,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
s.SetForwarding(true)
ep1 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(nicID1, ep1); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID1, err)
}
if err := s.AddAddress(nicID1, fakeNetNumber, "\x01"); err != nil {
t.Fatalf("AddAddress(%d, %d, 0x01): %s", nicID1, fakeNetNumber, err)
}
ep2 := channelLinkWithHeaderLength{
Endpoint: channel.New(10, defaultMTU, ""),
headerLength: test.headerLen,
}
if err := s.CreateNIC(nicID2, &ep2); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID2, err)
}
if err := s.AddAddress(nicID2, fakeNetNumber, "\x02"); err != nil {
t.Fatalf("AddAddress(%d, %d, 0x02): %s", nicID2, fakeNetNumber, err)
}
// Route all packets to dstAddr to NIC 2.
{
subnet, err := tcpip.NewSubnet(dstAddr, "\xff")
if err != nil {
t.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: nicID2}})
}
// Send a packet to dstAddr.
buf := buffer.NewView(30)
buf[0] = dstAddr[0]
ep1.InjectInbound(fakeNetNumber, stack.PacketBuffer{
Data: buf.ToVectorisedView(),
})
pkt, ok := ep2.Read()
if !ok {
t.Fatal("packet not forwarded")
}
// Test that the link's MaxHeaderLength is honoured.
if capacity, want := pkt.Pkt.Header.AvailableLength(), int(test.headerLen); capacity != want {
t.Errorf("got Header.AvailableLength() = %d, want = %d", capacity, want)
}
// Test that forwarding increments Tx stats correctly.
if got, want := s.NICInfo()[nicID2].Stats.Tx.Packets.Value(), uint64(1); got != want {
t.Errorf("got Tx.Packets.Value() = %d, want = %d", got, want)
}
if got, want := s.NICInfo()[nicID2].Stats.Tx.Bytes.Value(), uint64(len(buf)); got != want {
t.Errorf("got Tx.Bytes.Value() = %d, want = %d", got, want)
}
})
}
}
// TestNICContextPreservation tests that you can read out via stack.NICInfo the
// Context data you pass via NICContext.Context in stack.CreateNICWithOptions.
func TestNICContextPreservation(t *testing.T) {
var ctx *int
tests := []struct {
name string
opts stack.NICOptions
want stack.NICContext
}{
{
"context_set",
stack.NICOptions{Context: ctx},
ctx,
},
{
"context_not_set",
stack.NICOptions{},
nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{})
id := tcpip.NICID(1)
ep := channel.New(0, 0, tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00"))
if err := s.CreateNICWithOptions(id, ep, test.opts); err != nil {
t.Fatalf("got stack.CreateNICWithOptions(%d, %+v, %+v) = %s, want nil", id, ep, test.opts, err)
}
nicinfos := s.NICInfo()
nicinfo, ok := nicinfos[id]
if !ok {
t.Fatalf("got nicinfos[%d] = _, %t, want _, true; nicinfos = %+v", id, ok, nicinfos)
}
if got, want := nicinfo.Context == test.want, true; got != want {
t.Fatal("got nicinfo.Context == ctx = %t, want %t; nicinfo.Context = %p, ctx = %p", got, want, nicinfo.Context, test.want)
}
})
}
}
// TestNICAutoGenLinkLocalAddr tests the auto-generation of IPv6 link-local
// addresses.
func TestNICAutoGenLinkLocalAddr(t *testing.T) {
const nicID = 1
var secretKey [header.OpaqueIIDSecretKeyMinBytes]byte
n, err := rand.Read(secretKey[:])
if err != nil {
t.Fatalf("rand.Read(_): %s", err)
}
if n != header.OpaqueIIDSecretKeyMinBytes {
t.Fatalf("expected rand.Read to read %d bytes, read %d bytes", header.OpaqueIIDSecretKeyMinBytes, n)
}
nicNameFunc := func(_ tcpip.NICID, name string) string {
return name
}
tests := []struct {
name string
nicName string
autoGen bool
linkAddr tcpip.LinkAddress
iidOpts stack.OpaqueInterfaceIdentifierOptions
shouldGen bool
expectedAddr tcpip.Address
}{
{
name: "Disabled",
nicName: "nic1",
autoGen: false,
linkAddr: linkAddr1,
shouldGen: false,
},
{
name: "Disabled without OIID options",
nicName: "nic1",
autoGen: false,
linkAddr: linkAddr1,
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
SecretKey: secretKey[:],
},
shouldGen: false,
},
// Tests for EUI64 based addresses.
{
name: "EUI64 Enabled",
autoGen: true,
linkAddr: linkAddr1,
shouldGen: true,
expectedAddr: header.LinkLocalAddr(linkAddr1),
},
{
name: "EUI64 Empty MAC",
autoGen: true,
shouldGen: false,
},
{
name: "EUI64 Invalid MAC",
autoGen: true,
linkAddr: "\x01\x02\x03",
shouldGen: false,
},
{
name: "EUI64 Multicast MAC",
autoGen: true,
linkAddr: "\x01\x02\x03\x04\x05\x06",
shouldGen: false,
},
{
name: "EUI64 Unspecified MAC",
autoGen: true,
linkAddr: "\x00\x00\x00\x00\x00\x00",
shouldGen: false,
},
// Tests for Opaque IID based addresses.
{
name: "OIID Enabled",
nicName: "nic1",
autoGen: true,
linkAddr: linkAddr1,
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
SecretKey: secretKey[:],
},
shouldGen: true,
expectedAddr: header.LinkLocalAddrWithOpaqueIID("nic1", 0, secretKey[:]),
},
// These are all cases where we would not have generated a
// link-local address if opaque IIDs were disabled.
{
name: "OIID Empty MAC and empty nicName",
autoGen: true,
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
SecretKey: secretKey[:1],
},
shouldGen: true,
expectedAddr: header.LinkLocalAddrWithOpaqueIID("", 0, secretKey[:1]),
},
{
name: "OIID Invalid MAC",
nicName: "test",
autoGen: true,
linkAddr: "\x01\x02\x03",
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
SecretKey: secretKey[:2],
},
shouldGen: true,
expectedAddr: header.LinkLocalAddrWithOpaqueIID("test", 0, secretKey[:2]),
},
{
name: "OIID Multicast MAC",
nicName: "test2",
autoGen: true,
linkAddr: "\x01\x02\x03\x04\x05\x06",
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
SecretKey: secretKey[:3],
},
shouldGen: true,
expectedAddr: header.LinkLocalAddrWithOpaqueIID("test2", 0, secretKey[:3]),
},
{
name: "OIID Unspecified MAC and nil SecretKey",
nicName: "test3",
autoGen: true,
linkAddr: "\x00\x00\x00\x00\x00\x00",
iidOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: nicNameFunc,
},
shouldGen: true,
expectedAddr: header.LinkLocalAddrWithOpaqueIID("test3", 0, nil),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ndpDisp := ndpDispatcher{
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
AutoGenIPv6LinkLocal: test.autoGen,
NDPDisp: &ndpDisp,
OpaqueIIDOpts: test.iidOpts,
}
e := channel.New(0, 1280, test.linkAddr)
s := stack.New(opts)
nicOpts := stack.NICOptions{Name: test.nicName, Disabled: true}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, opts, err)
}
// A new disabled NIC should not have any address, even if auto generation
// was enabled.
allStackAddrs := s.AllAddresses()
allNICAddrs, ok := allStackAddrs[nicID]
if !ok {
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
}
if l := len(allNICAddrs); l != 0 {
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
}
// Enabling the NIC should attempt auto-generation of a link-local
// address.
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
var expectedMainAddr tcpip.AddressWithPrefix
if test.shouldGen {
expectedMainAddr = tcpip.AddressWithPrefix{
Address: test.expectedAddr,
PrefixLen: header.IPv6LinkLocalPrefix.PrefixLen,
}
// Should have auto-generated an address and resolved immediately (DAD
// is disabled).
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, expectedMainAddr, newAddr); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
default:
t.Fatal("expected addr auto gen event")
}
} else {
// Should not have auto-generated an address.
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly auto-generated an address")
default:
}
}
gotMainAddr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("stack.GetMainNICAddress(_, _) err = %s", err)
}
if gotMainAddr != expectedMainAddr {
t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", gotMainAddr, expectedMainAddr)
}
})
}
}
// TestNoLinkLocalAutoGenForLoopbackNIC tests that IPv6 link-local addresses are
// not auto-generated for loopback NICs.
func TestNoLinkLocalAutoGenForLoopbackNIC(t *testing.T) {
const nicID = 1
const nicName = "nicName"
tests := []struct {
name string
opaqueIIDOpts stack.OpaqueInterfaceIdentifierOptions
}{
{
name: "IID From MAC",
opaqueIIDOpts: stack.OpaqueInterfaceIdentifierOptions{},
},
{
name: "Opaque IID",
opaqueIIDOpts: stack.OpaqueInterfaceIdentifierOptions{
NICNameFromID: func(_ tcpip.NICID, nicName string) string {
return nicName
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
AutoGenIPv6LinkLocal: true,
OpaqueIIDOpts: test.opaqueIIDOpts,
}
e := loopback.New()
s := stack.New(opts)
nicOpts := stack.NICOptions{Name: nicName}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, nicOpts, err)
}
addr, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("stack.GetMainNICAddress(%d, _) err = %s", nicID, err)
}
if want := (tcpip.AddressWithPrefix{}); addr != want {
t.Errorf("got stack.GetMainNICAddress(%d, _) = %s, want = %s", nicID, addr, want)
}
})
}
}
// TestNICAutoGenAddrDoesDAD tests that the successful auto-generation of IPv6
// link-local addresses will only be assigned after the DAD process resolves.
func TestNICAutoGenAddrDoesDAD(t *testing.T) {
const nicID = 1
ndpDisp := ndpDispatcher{
dadC: make(chan ndpDADEvent),
}
ndpConfigs := stack.DefaultNDPConfigurations()
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
NDPConfigs: ndpConfigs,
AutoGenIPv6LinkLocal: true,
NDPDisp: &ndpDisp,
}
e := channel.New(int(ndpConfigs.DupAddrDetectTransmits), 1280, linkAddr1)
s := stack.New(opts)
if err := s.CreateNIC(nicID, e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
}
// Address should not be considered bound to the
// NIC yet (DAD ongoing).
addr, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if want := (tcpip.AddressWithPrefix{}); addr != want {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, addr, want)
}
linkLocalAddr := header.LinkLocalAddr(linkAddr1)
// Wait for DAD to resolve.
select {
case <-time.After(time.Duration(ndpConfigs.DupAddrDetectTransmits)*ndpConfigs.RetransmitTimer + time.Second):
// We should get a resolution event after 1s (default time to
// resolve as per default NDP configurations). Waiting for that
// resolution time + an extra 1s without a resolution event
// means something is wrong.
t.Fatal("timed out waiting for DAD resolution")
case e := <-ndpDisp.dadC:
if diff := checkDADEvent(e, nicID, linkLocalAddr, true, nil); diff != "" {
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
}
}
addr, err = s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if want := (tcpip.AddressWithPrefix{Address: linkLocalAddr, PrefixLen: header.IPv6LinkLocalPrefix.PrefixLen}); addr != want {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, addr, want)
}
}
// TestNewPEB tests that a new PrimaryEndpointBehavior value (peb) is respected
// when an address's kind gets "promoted" to permanent from permanentExpired.
func TestNewPEBOnPromotionToPermanent(t *testing.T) {
pebs := []stack.PrimaryEndpointBehavior{
stack.NeverPrimaryEndpoint,
stack.CanBePrimaryEndpoint,
stack.FirstPrimaryEndpoint,
}
for _, pi := range pebs {
for _, ps := range pebs {
t.Run(fmt.Sprintf("%d-to-%d", pi, ps), func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
})
ep1 := channel.New(10, defaultMTU, "")
if err := s.CreateNIC(1, ep1); err != nil {
t.Fatal("CreateNIC failed:", err)
}
// Add a permanent address with initial
// PrimaryEndpointBehavior (peb), pi. If pi is
// NeverPrimaryEndpoint, the address should not
// be returned by a call to GetMainNICAddress;
// else, it should.
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x01", pi); err != nil {
t.Fatal("AddAddressWithOptions failed:", err)
}
addr, err := s.GetMainNICAddress(1, fakeNetNumber)
if err != nil {
t.Fatal("s.GetMainNICAddress failed:", err)
}
if pi == stack.NeverPrimaryEndpoint {
if want := (tcpip.AddressWithPrefix{}); addr != want {
t.Fatalf("got GetMainNICAddress = %s, want = %s", addr, want)
}
} else if addr.Address != "\x01" {
t.Fatalf("got GetMainNICAddress = %s, want = 1", addr.Address)
}
{
subnet, err := tcpip.NewSubnet("\x00", "\x00")
if err != nil {
t.Fatalf("NewSubnet failed:", err)
}
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
}
// Take a route through the address so its ref
// count gets incremented and does not actually
// get deleted when RemoveAddress is called
// below. This is because we want to test that a
// new peb is respected when an address gets
// "promoted" to permanent from a
// permanentExpired kind.
r, err := s.FindRoute(1, "\x01", "\x02", fakeNetNumber, false)
if err != nil {
t.Fatal("FindRoute failed:", err)
}
defer r.Release()
if err := s.RemoveAddress(1, "\x01"); err != nil {
t.Fatalf("RemoveAddress failed:", err)
}
//
// At this point, the address should still be
// known by the NIC, but have its
// kind = permanentExpired.
//
// Add some other address with peb set to
// FirstPrimaryEndpoint.
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x03", stack.FirstPrimaryEndpoint); err != nil {
t.Fatal("AddAddressWithOptions failed:", err)
}
// Add back the address we removed earlier and
// make sure the new peb was respected.
// (The address should just be promoted now).
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x01", ps); err != nil {
t.Fatal("AddAddressWithOptions failed:", err)
}
var primaryAddrs []tcpip.Address
for _, pa := range s.NICInfo()[1].ProtocolAddresses {
primaryAddrs = append(primaryAddrs, pa.AddressWithPrefix.Address)
}
var expectedList []tcpip.Address
switch ps {
case stack.FirstPrimaryEndpoint:
expectedList = []tcpip.Address{
"\x01",
"\x03",
}
case stack.CanBePrimaryEndpoint:
expectedList = []tcpip.Address{
"\x03",
"\x01",
}
case stack.NeverPrimaryEndpoint:
expectedList = []tcpip.Address{
"\x03",
}
}
if !cmp.Equal(primaryAddrs, expectedList) {
t.Fatalf("got NIC's primary addresses = %v, want = %v", primaryAddrs, expectedList)
}
// Once we remove the other address, if the new
// peb, ps, was NeverPrimaryEndpoint, no address
// should be returned by a call to
// GetMainNICAddress; else, our original address
// should be returned.
if err := s.RemoveAddress(1, "\x03"); err != nil {
t.Fatalf("RemoveAddress failed:", err)
}
addr, err = s.GetMainNICAddress(1, fakeNetNumber)
if err != nil {
t.Fatal("s.GetMainNICAddress failed:", err)
}
if ps == stack.NeverPrimaryEndpoint {
if want := (tcpip.AddressWithPrefix{}); addr != want {
t.Fatalf("got GetMainNICAddress = %s, want = %s", addr, want)
}
} else {
if addr.Address != "\x01" {
t.Fatalf("got GetMainNICAddress = %s, want = 1", addr.Address)
}
}
})
}
}
}
func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
const (
linkLocalAddr1 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
linkLocalAddr2 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
linkLocalMulticastAddr = tcpip.Address("\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
uniqueLocalAddr1 = tcpip.Address("\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
uniqueLocalAddr2 = tcpip.Address("\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
globalAddr1 = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
globalAddr2 = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
nicID = 1
)
// Rule 3 is not tested here, and is instead tested by NDP's AutoGenAddr test.
tests := []struct {
name string
nicAddrs []tcpip.Address
connectAddr tcpip.Address
expectedLocalAddr tcpip.Address
}{
// Test Rule 1 of RFC 6724 section 5.
{
name: "Same Global most preferred (last address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: globalAddr1,
expectedLocalAddr: globalAddr1,
},
{
name: "Same Global most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: globalAddr1,
expectedLocalAddr: globalAddr1,
},
{
name: "Same Link Local most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalAddr1,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Same Link Local most preferred (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalAddr1,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Same Unique Local most preferred (last address)",
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
connectAddr: uniqueLocalAddr1,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Same Unique Local most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: uniqueLocalAddr1,
expectedLocalAddr: uniqueLocalAddr1,
},
// Test Rule 2 of RFC 6724 section 5.
{
name: "Global most preferred (last address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: globalAddr2,
expectedLocalAddr: globalAddr1,
},
{
name: "Global most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: globalAddr2,
expectedLocalAddr: globalAddr1,
},
{
name: "Link Local most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred for link local multicast (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalMulticastAddr,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred for link local multicast (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalMulticastAddr,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Unique Local most preferred (last address)",
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
connectAddr: uniqueLocalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Unique Local most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: uniqueLocalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
// Test returning the endpoint that is closest to the front when
// candidate addresses are "equal" from the perspective of RFC 6724
// section 5.
{
name: "Unique Local for Global",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, uniqueLocalAddr2},
connectAddr: globalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Link Local for Global",
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
connectAddr: globalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local for Unique Local",
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
connectAddr: uniqueLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
e := channel.New(0, 1280, linkAddr1)
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
TransportProtocols: []stack.TransportProtocol{udp.NewProtocol()},
})
if err := s.CreateNIC(nicID, e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
}
s.SetRouteTable([]tcpip.Route{{
Destination: header.IPv6EmptySubnet,
Gateway: llAddr3,
NIC: nicID,
}})
s.AddLinkAddress(nicID, llAddr3, linkAddr3)
for _, a := range test.nicAddrs {
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, a); err != nil {
t.Errorf("s.AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, a, err)
}
}
if t.Failed() {
t.FailNow()
}
if got := addrForNewConnectionTo(t, s, tcpip.FullAddress{Addr: test.connectAddr, NIC: nicID, Port: 1234}); got != test.expectedLocalAddr {
t.Errorf("got local address = %s, want = %s", got, test.expectedLocalAddr)
}
})
}
}
func TestAddRemoveIPv4BroadcastAddressOnNICEnableDisable(t *testing.T) {
const nicID = 1
e := loopback.New()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()},
})
nicOpts := stack.NICOptions{Disabled: true}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNIC(%d, _, %+v) = %s", nicID, nicOpts, err)
}
allStackAddrs := s.AllAddresses()
allNICAddrs, ok := allStackAddrs[nicID]
if !ok {
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
}
if l := len(allNICAddrs); l != 0 {
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
}
// Enabling the NIC should add the IPv4 broadcast address.
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
allStackAddrs = s.AllAddresses()
allNICAddrs, ok = allStackAddrs[nicID]
if !ok {
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
}
if l := len(allNICAddrs); l != 1 {
t.Fatalf("got len(allNICAddrs) = %d, want = 1", l)
}
want := tcpip.ProtocolAddress{
Protocol: header.IPv4ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: header.IPv4Broadcast,
PrefixLen: 32,
},
}
if allNICAddrs[0] != want {
t.Fatalf("got allNICAddrs[0] = %+v, want = %+v", allNICAddrs[0], want)
}
// Disabling the NIC should remove the IPv4 broadcast address.
if err := s.DisableNIC(nicID); err != nil {
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
}
allStackAddrs = s.AllAddresses()
allNICAddrs, ok = allStackAddrs[nicID]
if !ok {
t.Fatalf("entry for %d missing from allStackAddrs = %+v", nicID, allStackAddrs)
}
if l := len(allNICAddrs); l != 0 {
t.Fatalf("got len(allNICAddrs) = %d, want = 0", l)
}
}
// TestLeaveIPv6SolicitedNodeAddrBeforeAddrRemoval tests that removing an IPv6
// address after leaving its solicited node multicast address does not result in
// an error.
func TestLeaveIPv6SolicitedNodeAddrBeforeAddrRemoval(t *testing.T) {
const nicID = 1
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
})
e := channel.New(10, 1280, linkAddr1)
if err := s.CreateNIC(1, e); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
}
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, addr1); err != nil {
t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, addr1, err)
}
// The NIC should have joined addr1's solicited node multicast address.
snmc := header.SolicitedNodeAddr(addr1)
in, err := s.IsInGroup(nicID, snmc)
if err != nil {
t.Fatalf("IsInGroup(%d, %s): %s", nicID, snmc, err)
}
if !in {
t.Fatalf("got IsInGroup(%d, %s) = false, want = true", nicID, snmc)
}
if err := s.LeaveGroup(ipv6.ProtocolNumber, nicID, snmc); err != nil {
t.Fatalf("LeaveGroup(%d, %d, %s): %s", ipv6.ProtocolNumber, nicID, snmc, err)
}
in, err = s.IsInGroup(nicID, snmc)
if err != nil {
t.Fatalf("IsInGroup(%d, %s): %s", nicID, snmc, err)
}
if in {
t.Fatalf("got IsInGroup(%d, %s) = true, want = false", nicID, snmc)
}
if err := s.RemoveAddress(nicID, addr1); err != nil {
t.Fatalf("RemoveAddress(%d, %s) = %s", nicID, addr1, err)
}
}
func TestJoinLeaveAllNodesMulticastOnNICEnableDisable(t *testing.T) {
const nicID = 1
e := loopback.New()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
})
nicOpts := stack.NICOptions{Disabled: true}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNIC(%d, _, %+v) = %s", nicID, nicOpts, err)
}
// Should not be in the IPv6 all-nodes multicast group yet because the NIC has
// not been enabled yet.
isInGroup, err := s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
if err != nil {
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
}
if isInGroup {
t.Fatalf("got IsInGroup(%d, %s) = true, want = false", nicID, header.IPv6AllNodesMulticastAddress)
}
// The all-nodes multicast group should be joined when the NIC is enabled.
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
isInGroup, err = s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
if err != nil {
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
}
if !isInGroup {
t.Fatalf("got IsInGroup(%d, %s) = false, want = true", nicID, header.IPv6AllNodesMulticastAddress)
}
// The all-nodes multicast group should be left when the NIC is disabled.
if err := s.DisableNIC(nicID); err != nil {
t.Fatalf("s.DisableNIC(%d): %s", nicID, err)
}
isInGroup, err = s.IsInGroup(nicID, header.IPv6AllNodesMulticastAddress)
if err != nil {
t.Fatalf("IsInGroup(%d, %s): %s", nicID, header.IPv6AllNodesMulticastAddress, err)
}
if isInGroup {
t.Fatalf("got IsInGroup(%d, %s) = true, want = false", nicID, header.IPv6AllNodesMulticastAddress)
}
}
// TestDoDADWhenNICEnabled tests that IPv6 endpoints that were added while a NIC
// was disabled have DAD performed on them when the NIC is enabled.
func TestDoDADWhenNICEnabled(t *testing.T) {
const dadTransmits = 1
const retransmitTimer = time.Second
const nicID = 1
ndpDisp := ndpDispatcher{
dadC: make(chan ndpDADEvent),
}
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
NDPConfigs: stack.NDPConfigurations{
DupAddrDetectTransmits: dadTransmits,
RetransmitTimer: retransmitTimer,
},
NDPDisp: &ndpDisp,
}
e := channel.New(dadTransmits, 1280, linkAddr1)
s := stack.New(opts)
nicOpts := stack.NICOptions{Disabled: true}
if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil {
t.Fatalf("CreateNIC(%d, _, %+v) = %s", nicID, nicOpts, err)
}
addr := tcpip.ProtocolAddress{
Protocol: header.IPv6ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: llAddr1,
PrefixLen: 128,
},
}
if err := s.AddProtocolAddress(nicID, addr); err != nil {
t.Fatalf("AddProtocolAddress(%d, %+v): %s", nicID, addr, err)
}
// Address should be in the list of all addresses.
if addrs := s.AllAddresses()[nicID]; !containsV6Addr(addrs, addr.AddressWithPrefix) {
t.Fatalf("got s.AllAddresses()[%d] = %+v, want = %+v", nicID, addrs, addr)
}
// Address should be tentative so it should not be a main address.
got, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if want := (tcpip.AddressWithPrefix{}); got != want {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, got, want)
}
// Enabling the NIC should start DAD for the address.
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
if addrs := s.AllAddresses()[nicID]; !containsV6Addr(addrs, addr.AddressWithPrefix) {
t.Fatalf("got s.AllAddresses()[%d] = %+v, want = %+v", nicID, addrs, addr)
}
// Address should not be considered bound to the NIC yet (DAD ongoing).
got, err = s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if want := (tcpip.AddressWithPrefix{}); got != want {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, got, want)
}
// Wait for DAD to resolve.
select {
case <-time.After(dadTransmits*retransmitTimer + defaultAsyncEventTimeout):
t.Fatal("timed out waiting for DAD resolution")
case e := <-ndpDisp.dadC:
if diff := checkDADEvent(e, nicID, addr.AddressWithPrefix.Address, true, nil); diff != "" {
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
}
}
if addrs := s.AllAddresses()[nicID]; !containsV6Addr(addrs, addr.AddressWithPrefix) {
t.Fatalf("got s.AllAddresses()[%d] = %+v, want = %+v", nicID, addrs, addr)
}
got, err = s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if got != addr.AddressWithPrefix {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = %s, want = %s", nicID, header.IPv6ProtocolNumber, got, addr.AddressWithPrefix)
}
// Enabling the NIC again should be a no-op.
if err := s.EnableNIC(nicID); err != nil {
t.Fatalf("s.EnableNIC(%d): %s", nicID, err)
}
if addrs := s.AllAddresses()[nicID]; !containsV6Addr(addrs, addr.AddressWithPrefix) {
t.Fatalf("got s.AllAddresses()[%d] = %+v, want = %+v", nicID, addrs, addr)
}
got, err = s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (_, %v), want = (_, nil)", nicID, header.IPv6ProtocolNumber, err)
}
if got != addr.AddressWithPrefix {
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, got, addr.AddressWithPrefix)
}
}