| // 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" |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/buffer" |
| "github.com/google/netstack/tcpip/header" |
| "github.com/google/netstack/tcpip/link/channel" |
| "github.com/google/netstack/tcpip/network/ipv6" |
| "github.com/google/netstack/tcpip/stack" |
| ) |
| |
| 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, vv buffer.VectorisedView) { |
| // 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 := vv.First() |
| vv.TrimFront(fakeNetHeaderLen) |
| |
| // Handle control packets. |
| if b[2] == uint8(fakeControlProtocol) { |
| nb := vv.First() |
| if len(nb) < fakeNetHeaderLen { |
| return |
| } |
| |
| vv.TrimFront(fakeNetHeaderLen) |
| f.dispatcher.DeliverTransportControlPacket(tcpip.Address(nb[1:2]), tcpip.Address(nb[0:1]), fakeNetNumber, tcpip.TransportProtocolNumber(nb[2]), stack.ControlPortUnreachable, 0, vv) |
| return |
| } |
| |
| // Dispatch the packet to the transport protocol. |
| f.dispatcher.DeliverTransportPacket(r, tcpip.TransportProtocolNumber(b[2]), buffer.View([]byte{}), vv) |
| } |
| |
| 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, hdr buffer.Prependable, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) *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 := hdr.Prepend(fakeNetHeaderLen) |
| b[0] = r.RemoteAddress[0] |
| b[1] = f.id.LocalAddress[0] |
| b[2] = byte(params.Protocol) |
| |
| if loop&stack.PacketLoop != 0 { |
| views := make([]buffer.View, 1, 1+len(payload.Views())) |
| views[0] = hdr.View() |
| views = append(views, payload.Views()...) |
| vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views) |
| f.HandlePacket(r, vv) |
| } |
| if loop&stack.PacketOut == 0 { |
| return nil |
| } |
| |
| return f.ep.WritePacket(r, gso, hdr, payload, fakeNetNumber) |
| } |
| |
| // WritePackets implements stack.LinkEndpoint.WritePackets. |
| func (f *fakeNetworkEndpoint) WritePackets(r *stack.Route, gso *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) (int, *tcpip.Error) { |
| panic("not implemented") |
| } |
| |
| func (*fakeNetworkEndpoint) WriteHeaderIncludedPacket(r *stack.Route, payload buffer.VectorisedView, loop stack.PacketLooping) *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.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 |
| } |
| } |
| |
| func fakeNetFactory() stack.NetworkProtocol { |
| return &fakeNetworkProtocol{} |
| } |
| |
| 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.Inject(fakeNetNumber, 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.Inject(fakeNetNumber, 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.Inject(fakeNetNumber, 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.Inject(fakeNetNumber-1, 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.Inject(fakeNetNumber, 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 */, hdr, payload.ToVectorisedView(), stack.NetworkHeaderParams{Protocol: fakeTransNumber, TTL: 123, TOS: stack.DefaultTOS}) |
| } |
| |
| 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.Inject(fakeNetNumber, 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) |
| } |
| } |
| |
| 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 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.Inject(fakeNetNumber, 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) { |
| // Create a stack with the fake network protocol, two NICs, each with |
| // an address. |
| s := stack.New(stack.Options{ |
| NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()}, |
| }) |
| s.SetForwarding(true) |
| |
| ep1 := channel.New(10, defaultMTU, "") |
| if err := s.CreateNIC(1, ep1); err != nil { |
| t.Fatal("CreateNIC #1 failed:", err) |
| } |
| if err := s.AddAddress(1, fakeNetNumber, "\x01"); err != nil { |
| t.Fatal("AddAddress #1 failed:", err) |
| } |
| |
| ep2 := channel.New(10, defaultMTU, "") |
| if err := s.CreateNIC(2, ep2); err != nil { |
| t.Fatal("CreateNIC #2 failed:", err) |
| } |
| if err := s.AddAddress(2, fakeNetNumber, "\x02"); err != nil { |
| t.Fatal("AddAddress #2 failed:", err) |
| } |
| |
| // Route all packets to address 3 to NIC 2. |
| { |
| subnet, err := tcpip.NewSubnet("\x03", "\xff") |
| if err != nil { |
| t.Fatal(err) |
| } |
| s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 2}}) |
| } |
| |
| // Send a packet to address 3. |
| buf := buffer.NewView(30) |
| buf[0] = 3 |
| ep1.Inject(fakeNetNumber, buf.ToVectorisedView()) |
| |
| select { |
| case <-ep2.C: |
| default: |
| t.Fatal("Packet not forwarded") |
| } |
| |
| // Test that forwarding increments Tx stats correctly. |
| if got, want := s.NICInfo()[2].Stats.Tx.Packets.Value(), uint64(1); got != want { |
| t.Errorf("got Tx.Packets.Value() = %d, want = %d", got, want) |
| } |
| |
| if got, want := s.NICInfo()[2].Stats.Tx.Bytes.Value(), uint64(len(buf)); got != want { |
| t.Errorf("got Tx.Bytes.Value() = %d, want = %d", got, want) |
| } |
| } |
| |
| // TestNICAutoGenAddr tests the auto-generation of IPv6 link-local addresses |
| // (or lack there-of if disabled (default)). Note, DAD will be disabled in |
| // these tests. |
| func TestNICAutoGenAddr(t *testing.T) { |
| tests := []struct { |
| name string |
| autoGen bool |
| linkAddr tcpip.LinkAddress |
| shouldGen bool |
| }{ |
| { |
| "Disabled", |
| false, |
| linkAddr1, |
| false, |
| }, |
| { |
| "Enabled", |
| true, |
| linkAddr1, |
| true, |
| }, |
| { |
| "Nil MAC", |
| true, |
| tcpip.LinkAddress([]byte(nil)), |
| false, |
| }, |
| { |
| "Empty MAC", |
| true, |
| tcpip.LinkAddress(""), |
| false, |
| }, |
| { |
| "Invalid MAC", |
| true, |
| tcpip.LinkAddress("\x01\x02\x03"), |
| false, |
| }, |
| { |
| "Multicast MAC", |
| true, |
| tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), |
| false, |
| }, |
| { |
| "Unspecified MAC", |
| true, |
| tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00"), |
| false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| opts := stack.Options{ |
| NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()}, |
| } |
| |
| if test.autoGen { |
| // Only set opts.AutoGenIPv6LinkLocal when |
| // test.autoGen is true because |
| // opts.AutoGenIPv6LinkLocal should be false by |
| // default. |
| opts.AutoGenIPv6LinkLocal = true |
| } |
| |
| e := channel.New(10, 1280, test.linkAddr) |
| s := stack.New(opts) |
| if err := s.CreateNIC(1, e); err != nil { |
| t.Fatalf("CreateNIC(_) = %s", err) |
| } |
| |
| addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) |
| if err != nil { |
| t.Fatalf("stack.GetMainNICAddress(_, _) err = %s", err) |
| } |
| |
| if test.shouldGen { |
| // Should have auto-generated an address and |
| // resolved immediately (DAD is disabled). |
| if want := (tcpip.AddressWithPrefix{Address: header.LinkLocalAddr(test.linkAddr), PrefixLen: header.IPv6LinkLocalPrefix.PrefixLen}); addr != want { |
| t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", addr, want) |
| } |
| } else { |
| // Should not have auto-generated an address. |
| if want := (tcpip.AddressWithPrefix{}); addr != want { |
| t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", 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) { |
| 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(10, 1280, linkAddr1) |
| s := stack.New(opts) |
| if err := s.CreateNIC(1, e); err != nil { |
| t.Fatalf("CreateNIC(_) = %s", err) |
| } |
| |
| // Address should not be considered bound to the |
| // NIC yet (DAD ongoing). |
| addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber) |
| if err != nil { |
| t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err) |
| } |
| if want := (tcpip.AddressWithPrefix{}); addr != want { |
| t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", 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 e.err != nil { |
| t.Fatal("got DAD error: ", e.err) |
| } |
| if e.nicid != 1 { |
| t.Fatalf("got DAD event w/ nicid = %d, want = 1", e.nicid) |
| } |
| if e.addr != linkLocalAddr { |
| t.Fatalf("got DAD event w/ addr = %s, want = %s", addr, linkLocalAddr) |
| } |
| if !e.resolved { |
| t.Fatal("got DAD event w/ resolved = false, want = true") |
| } |
| } |
| addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber) |
| if err != nil { |
| t.Fatalf("stack.GetMainNICAddress(_, _) err = %s", err) |
| } |
| if want := (tcpip.AddressWithPrefix{Address: linkLocalAddr, PrefixLen: header.IPv6LinkLocalPrefix.PrefixLen}); addr != want { |
| t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", 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) |
| } |
| } |
| }) |
| } |
| } |
| } |