| // Copyright 2020 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 |
| |
| import ( |
| "encoding/binary" |
| "math" |
| "testing" |
| "time" |
| |
| "gvisor.dev/gvisor/pkg/sync" |
| "gvisor.dev/gvisor/pkg/tcpip" |
| "gvisor.dev/gvisor/pkg/tcpip/buffer" |
| "gvisor.dev/gvisor/pkg/tcpip/header" |
| ) |
| |
| const ( |
| fwdTestNetNumber tcpip.NetworkProtocolNumber = math.MaxUint32 |
| fwdTestNetHeaderLen = 12 |
| fwdTestNetDefaultPrefixLen = 8 |
| |
| // fwdTestNetDefaultMTU 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. |
| fwdTestNetDefaultMTU = 65536 |
| |
| dstAddrOffset = 0 |
| srcAddrOffset = 1 |
| protocolNumberOffset = 2 |
| ) |
| |
| var _ LinkAddressResolver = (*fwdTestNetworkEndpoint)(nil) |
| var _ NetworkEndpoint = (*fwdTestNetworkEndpoint)(nil) |
| |
| // fwdTestNetworkEndpoint is a network-layer protocol endpoint. |
| // Headers of this protocol are fwdTestNetHeaderLen 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 fwdTestNetworkEndpoint struct { |
| AddressableEndpointState |
| |
| nic NetworkInterface |
| proto *fwdTestNetworkProtocol |
| dispatcher TransportDispatcher |
| } |
| |
| func (*fwdTestNetworkEndpoint) Enable() tcpip.Error { |
| return nil |
| } |
| |
| func (*fwdTestNetworkEndpoint) Enabled() bool { |
| return true |
| } |
| |
| func (*fwdTestNetworkEndpoint) Disable() {} |
| |
| func (f *fwdTestNetworkEndpoint) MTU() uint32 { |
| return f.nic.MTU() - uint32(f.MaxHeaderLength()) |
| } |
| |
| func (*fwdTestNetworkEndpoint) DefaultTTL() uint8 { |
| return 123 |
| } |
| |
| func (f *fwdTestNetworkEndpoint) HandlePacket(pkt *PacketBuffer) { |
| if _, _, ok := f.proto.Parse(pkt); !ok { |
| return |
| } |
| |
| netHdr := pkt.NetworkHeader().View() |
| _, dst := f.proto.ParseAddresses(netHdr) |
| |
| addressEndpoint := f.AcquireAssignedAddress(dst, f.nic.Promiscuous(), CanBePrimaryEndpoint) |
| if addressEndpoint != nil { |
| addressEndpoint.DecRef() |
| // Dispatch the packet to the transport protocol. |
| f.dispatcher.DeliverTransportPacket(tcpip.TransportProtocolNumber(netHdr[protocolNumberOffset]), pkt) |
| return |
| } |
| |
| r, err := f.proto.stack.FindRoute(0, "", dst, fwdTestNetNumber, false /* multicastLoop */) |
| if err != nil { |
| return |
| } |
| defer r.Release() |
| |
| vv := buffer.NewVectorisedView(pkt.Size(), pkt.Views()) |
| pkt = NewPacketBuffer(PacketBufferOptions{ |
| ReserveHeaderBytes: int(r.MaxHeaderLength()), |
| Data: vv.ToView().ToVectorisedView(), |
| }) |
| // TODO(b/143425874) Decrease the TTL field in forwarded packets. |
| _ = r.WriteHeaderIncludedPacket(pkt) |
| } |
| |
| func (f *fwdTestNetworkEndpoint) MaxHeaderLength() uint16 { |
| return f.nic.MaxHeaderLength() + fwdTestNetHeaderLen |
| } |
| |
| func (*fwdTestNetworkEndpoint) PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, dstAddr tcpip.Address) uint16 { |
| return 0 |
| } |
| |
| func (f *fwdTestNetworkEndpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber { |
| return f.proto.Number() |
| } |
| |
| func (f *fwdTestNetworkEndpoint) WritePacket(r *Route, gso *GSO, params NetworkHeaderParams, pkt *PacketBuffer) tcpip.Error { |
| // Add the protocol's header to the packet and send it to the link |
| // endpoint. |
| b := pkt.NetworkHeader().Push(fwdTestNetHeaderLen) |
| b[dstAddrOffset] = r.RemoteAddress[0] |
| b[srcAddrOffset] = r.LocalAddress[0] |
| b[protocolNumberOffset] = byte(params.Protocol) |
| |
| return f.nic.WritePacket(r, gso, fwdTestNetNumber, pkt) |
| } |
| |
| // WritePackets implements LinkEndpoint.WritePackets. |
| func (*fwdTestNetworkEndpoint) WritePackets(r *Route, gso *GSO, pkts PacketBufferList, params NetworkHeaderParams) (int, tcpip.Error) { |
| panic("not implemented") |
| } |
| |
| func (f *fwdTestNetworkEndpoint) WriteHeaderIncludedPacket(r *Route, pkt *PacketBuffer) tcpip.Error { |
| // The network header should not already be populated. |
| if _, ok := pkt.NetworkHeader().Consume(fwdTestNetHeaderLen); !ok { |
| return &tcpip.ErrMalformedHeader{} |
| } |
| |
| return f.nic.WritePacket(r, nil /* gso */, fwdTestNetNumber, pkt) |
| } |
| |
| func (f *fwdTestNetworkEndpoint) Close() { |
| f.AddressableEndpointState.Cleanup() |
| } |
| |
| // Stats implements stack.NetworkEndpoint. |
| func (*fwdTestNetworkEndpoint) Stats() NetworkEndpointStats { |
| return &fwdTestNetworkEndpointStats{} |
| } |
| |
| var _ NetworkEndpointStats = (*fwdTestNetworkEndpointStats)(nil) |
| |
| type fwdTestNetworkEndpointStats struct{} |
| |
| // IsNetworkEndpointStats implements stack.NetworkEndpointStats. |
| func (*fwdTestNetworkEndpointStats) IsNetworkEndpointStats() {} |
| |
| var _ NetworkProtocol = (*fwdTestNetworkProtocol)(nil) |
| |
| // fwdTestNetworkProtocol is a network-layer protocol that implements Address |
| // resolution. |
| type fwdTestNetworkProtocol struct { |
| stack *Stack |
| |
| neigh *neighborCache |
| addrResolveDelay time.Duration |
| onLinkAddressResolved func(*neighborCache, tcpip.Address, tcpip.LinkAddress) |
| onResolveStaticAddress func(tcpip.Address) (tcpip.LinkAddress, bool) |
| |
| mu struct { |
| sync.RWMutex |
| forwarding bool |
| } |
| } |
| |
| func (*fwdTestNetworkProtocol) Number() tcpip.NetworkProtocolNumber { |
| return fwdTestNetNumber |
| } |
| |
| func (*fwdTestNetworkProtocol) MinimumPacketSize() int { |
| return fwdTestNetHeaderLen |
| } |
| |
| func (*fwdTestNetworkProtocol) DefaultPrefixLen() int { |
| return fwdTestNetDefaultPrefixLen |
| } |
| |
| func (*fwdTestNetworkProtocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) { |
| return tcpip.Address(v[srcAddrOffset : srcAddrOffset+1]), tcpip.Address(v[dstAddrOffset : dstAddrOffset+1]) |
| } |
| |
| func (*fwdTestNetworkProtocol) Parse(pkt *PacketBuffer) (tcpip.TransportProtocolNumber, bool, bool) { |
| netHeader, ok := pkt.NetworkHeader().Consume(fwdTestNetHeaderLen) |
| if !ok { |
| return 0, false, false |
| } |
| return tcpip.TransportProtocolNumber(netHeader[protocolNumberOffset]), true, true |
| } |
| |
| func (f *fwdTestNetworkProtocol) NewEndpoint(nic NetworkInterface, dispatcher TransportDispatcher) NetworkEndpoint { |
| e := &fwdTestNetworkEndpoint{ |
| nic: nic, |
| proto: f, |
| dispatcher: dispatcher, |
| } |
| e.AddressableEndpointState.Init(e) |
| return e |
| } |
| |
| func (*fwdTestNetworkProtocol) SetOption(tcpip.SettableNetworkProtocolOption) tcpip.Error { |
| return &tcpip.ErrUnknownProtocolOption{} |
| } |
| |
| func (*fwdTestNetworkProtocol) Option(tcpip.GettableNetworkProtocolOption) tcpip.Error { |
| return &tcpip.ErrUnknownProtocolOption{} |
| } |
| |
| func (*fwdTestNetworkProtocol) Close() {} |
| |
| func (*fwdTestNetworkProtocol) Wait() {} |
| |
| func (f *fwdTestNetworkEndpoint) LinkAddressRequest(addr, _ tcpip.Address, remoteLinkAddr tcpip.LinkAddress) tcpip.Error { |
| if fn := f.proto.onLinkAddressResolved; fn != nil { |
| time.AfterFunc(f.proto.addrResolveDelay, func() { |
| fn(f.proto.neigh, addr, remoteLinkAddr) |
| }) |
| } |
| return nil |
| } |
| |
| func (f *fwdTestNetworkEndpoint) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) { |
| if fn := f.proto.onResolveStaticAddress; fn != nil { |
| return fn(addr) |
| } |
| return "", false |
| } |
| |
| func (*fwdTestNetworkEndpoint) LinkAddressProtocol() tcpip.NetworkProtocolNumber { |
| return fwdTestNetNumber |
| } |
| |
| // Forwarding implements stack.ForwardingNetworkProtocol. |
| func (f *fwdTestNetworkProtocol) Forwarding() bool { |
| f.mu.RLock() |
| defer f.mu.RUnlock() |
| return f.mu.forwarding |
| |
| } |
| |
| // SetForwarding implements stack.ForwardingNetworkProtocol. |
| func (f *fwdTestNetworkProtocol) SetForwarding(v bool) { |
| f.mu.Lock() |
| defer f.mu.Unlock() |
| f.mu.forwarding = v |
| } |
| |
| // fwdTestPacketInfo holds all the information about an outbound packet. |
| type fwdTestPacketInfo struct { |
| RemoteLinkAddress tcpip.LinkAddress |
| LocalLinkAddress tcpip.LinkAddress |
| Pkt *PacketBuffer |
| } |
| |
| type fwdTestLinkEndpoint struct { |
| dispatcher NetworkDispatcher |
| mtu uint32 |
| linkAddr tcpip.LinkAddress |
| |
| // C is where outbound packets are queued. |
| C chan fwdTestPacketInfo |
| } |
| |
| // InjectInbound injects an inbound packet. |
| func (e *fwdTestLinkEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) { |
| e.InjectLinkAddr(protocol, "", pkt) |
| } |
| |
| // InjectLinkAddr injects an inbound packet with a remote link address. |
| func (e *fwdTestLinkEndpoint) InjectLinkAddr(protocol tcpip.NetworkProtocolNumber, remote tcpip.LinkAddress, pkt *PacketBuffer) { |
| e.dispatcher.DeliverNetworkPacket(remote, "" /* local */, protocol, pkt) |
| } |
| |
| // Attach saves the stack network-layer dispatcher for use later when packets |
| // are injected. |
| func (e *fwdTestLinkEndpoint) Attach(dispatcher NetworkDispatcher) { |
| e.dispatcher = dispatcher |
| } |
| |
| // IsAttached implements stack.LinkEndpoint.IsAttached. |
| func (e *fwdTestLinkEndpoint) IsAttached() bool { |
| return e.dispatcher != nil |
| } |
| |
| // MTU implements stack.LinkEndpoint.MTU. It returns the value initialized |
| // during construction. |
| func (e *fwdTestLinkEndpoint) MTU() uint32 { |
| return e.mtu |
| } |
| |
| // Capabilities implements stack.LinkEndpoint.Capabilities. |
| func (e fwdTestLinkEndpoint) Capabilities() LinkEndpointCapabilities { |
| caps := LinkEndpointCapabilities(0) |
| return caps | CapabilityResolutionRequired |
| } |
| |
| // GSOMaxSize returns the maximum GSO packet size. |
| func (*fwdTestLinkEndpoint) GSOMaxSize() uint32 { |
| return 1 << 15 |
| } |
| |
| // MaxHeaderLength returns the maximum size of the link layer header. Given it |
| // doesn't have a header, it just returns 0. |
| func (*fwdTestLinkEndpoint) MaxHeaderLength() uint16 { |
| return 0 |
| } |
| |
| // LinkAddress returns the link address of this endpoint. |
| func (e *fwdTestLinkEndpoint) LinkAddress() tcpip.LinkAddress { |
| return e.linkAddr |
| } |
| |
| func (e fwdTestLinkEndpoint) WritePacket(r RouteInfo, gso *GSO, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) tcpip.Error { |
| p := fwdTestPacketInfo{ |
| RemoteLinkAddress: r.RemoteLinkAddress, |
| LocalLinkAddress: r.LocalLinkAddress, |
| Pkt: pkt, |
| } |
| |
| select { |
| case e.C <- p: |
| default: |
| } |
| |
| return nil |
| } |
| |
| // WritePackets stores outbound packets into the channel. |
| func (e *fwdTestLinkEndpoint) WritePackets(r RouteInfo, gso *GSO, pkts PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) { |
| n := 0 |
| for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { |
| e.WritePacket(r, gso, protocol, pkt) |
| n++ |
| } |
| |
| return n, nil |
| } |
| |
| // Wait implements stack.LinkEndpoint.Wait. |
| func (*fwdTestLinkEndpoint) Wait() {} |
| |
| // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. |
| func (*fwdTestLinkEndpoint) ARPHardwareType() header.ARPHardwareType { |
| panic("not implemented") |
| } |
| |
| // AddHeader implements stack.LinkEndpoint.AddHeader. |
| func (e *fwdTestLinkEndpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) { |
| panic("not implemented") |
| } |
| |
| func fwdTestNetFactory(t *testing.T, proto *fwdTestNetworkProtocol) (ep1, ep2 *fwdTestLinkEndpoint) { |
| // Create a stack with the network protocol and two NICs. |
| s := New(Options{ |
| NetworkProtocols: []NetworkProtocolFactory{func(s *Stack) NetworkProtocol { |
| proto.stack = s |
| return proto |
| }}, |
| }) |
| |
| // Enable forwarding. |
| s.SetForwarding(proto.Number(), true) |
| |
| // NIC 1 has the link address "a", and added the network address 1. |
| ep1 = &fwdTestLinkEndpoint{ |
| C: make(chan fwdTestPacketInfo, 300), |
| mtu: fwdTestNetDefaultMTU, |
| linkAddr: "a", |
| } |
| if err := s.CreateNIC(1, ep1); err != nil { |
| t.Fatal("CreateNIC #1 failed:", err) |
| } |
| if err := s.AddAddress(1, fwdTestNetNumber, "\x01"); err != nil { |
| t.Fatal("AddAddress #1 failed:", err) |
| } |
| |
| // NIC 2 has the link address "b", and added the network address 2. |
| ep2 = &fwdTestLinkEndpoint{ |
| C: make(chan fwdTestPacketInfo, 300), |
| mtu: fwdTestNetDefaultMTU, |
| linkAddr: "b", |
| } |
| if err := s.CreateNIC(2, ep2); err != nil { |
| t.Fatal("CreateNIC #2 failed:", err) |
| } |
| if err := s.AddAddress(2, fwdTestNetNumber, "\x02"); err != nil { |
| t.Fatal("AddAddress #2 failed:", err) |
| } |
| |
| nic, ok := s.nics[2] |
| if !ok { |
| t.Fatal("NIC 2 does not exist") |
| } |
| |
| if l, ok := nic.linkAddrResolvers[fwdTestNetNumber]; ok { |
| proto.neigh = &l.neigh |
| } |
| |
| // Route all packets to NIC 2. |
| { |
| subnet, err := tcpip.NewSubnet("\x00", "\x00") |
| if err != nil { |
| t.Fatal(err) |
| } |
| s.SetRouteTable([]tcpip.Route{{Destination: subnet, NIC: 2}}) |
| } |
| |
| return ep1, ep2 |
| } |
| |
| func TestForwardingWithStaticResolver(t *testing.T) { |
| // Create a network protocol with a static resolver. |
| proto := &fwdTestNetworkProtocol{ |
| onResolveStaticAddress: |
| // The network address 3 is resolved to the link address "c". |
| func(addr tcpip.Address) (tcpip.LinkAddress, bool) { |
| if addr == "\x03" { |
| return "c", true |
| } |
| return "", false |
| }, |
| } |
| |
| ep1, ep2 := fwdTestNetFactory(t, proto) |
| |
| // Inject an inbound packet to address 3 on NIC 1, and see if it is |
| // forwarded to NIC 2. |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| default: |
| t.Fatal("packet not forwarded") |
| } |
| |
| // Test that the static address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| |
| func TestForwardingWithFakeResolver(t *testing.T) { |
| proto := fwdTestNetworkProtocol{ |
| addrResolveDelay: 500 * time.Millisecond, |
| onLinkAddressResolved: func(neigh *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| t.Helper() |
| if len(linkAddr) != 0 { |
| t.Fatalf("got linkAddr=%q, want unspecified", linkAddr) |
| } |
| // Any address will be resolved to the link address "c". |
| neigh.handleConfirmation(addr, "c", ReachabilityConfirmationFlags{ |
| Solicited: true, |
| Override: false, |
| IsRouter: false, |
| }) |
| }, |
| } |
| ep1, ep2 := fwdTestNetFactory(t, &proto) |
| |
| // Inject an inbound packet to address 3 on NIC 1, and see if it is |
| // forwarded to NIC 2. |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| case <-time.After(time.Second): |
| t.Fatal("packet not forwarded") |
| } |
| |
| // Test that the address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| |
| func TestForwardingWithNoResolver(t *testing.T) { |
| // Create a network protocol without a resolver. |
| proto := &fwdTestNetworkProtocol{} |
| |
| // Whether or not we use the neighbor cache here does not matter since |
| // neither linkAddrCache nor neighborCache will be used. |
| ep1, ep2 := fwdTestNetFactory(t, proto) |
| |
| // inject an inbound packet to address 3 on NIC 1, and see if it is |
| // forwarded to NIC 2. |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| |
| select { |
| case <-ep2.C: |
| t.Fatal("Packet should not be forwarded") |
| case <-time.After(time.Second): |
| } |
| } |
| |
| func TestForwardingResolutionFailsForQueuedPackets(t *testing.T) { |
| proto := &fwdTestNetworkProtocol{ |
| addrResolveDelay: 50 * time.Millisecond, |
| onLinkAddressResolved: func(*neighborCache, tcpip.Address, tcpip.LinkAddress) { |
| // Don't resolve the link address. |
| }, |
| } |
| |
| ep1, ep2 := fwdTestNetFactory(t, proto) |
| |
| const numPackets int = 5 |
| // These packets will all be enqueued in the packet queue to wait for link |
| // address resolution. |
| for i := 0; i < numPackets; i++ { |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| } |
| |
| // All packets should fail resolution. |
| // TODO(gvisor.dev/issue/5141): Use a fake clock. |
| for i := 0; i < numPackets; i++ { |
| select { |
| case got := <-ep2.C: |
| t.Fatalf("got %#v; packets should have failed resolution and not been forwarded", got) |
| case <-time.After(100 * time.Millisecond): |
| } |
| } |
| } |
| |
| func TestForwardingWithFakeResolverPartialTimeout(t *testing.T) { |
| proto := fwdTestNetworkProtocol{ |
| addrResolveDelay: 500 * time.Millisecond, |
| onLinkAddressResolved: func(neigh *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| t.Helper() |
| if len(linkAddr) != 0 { |
| t.Fatalf("got linkAddr=%q, want unspecified", linkAddr) |
| } |
| // Only packets to address 3 will be resolved to the |
| // link address "c". |
| if addr == "\x03" { |
| neigh.handleConfirmation(addr, "c", ReachabilityConfirmationFlags{ |
| Solicited: true, |
| Override: false, |
| IsRouter: false, |
| }) |
| } |
| }, |
| } |
| ep1, ep2 := fwdTestNetFactory(t, &proto) |
| |
| // Inject an inbound packet to address 4 on NIC 1. This packet should |
| // not be forwarded. |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 4 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| |
| // Inject an inbound packet to address 3 on NIC 1, and see if it is |
| // forwarded to NIC 2. |
| buf = buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| case <-time.After(time.Second): |
| t.Fatal("packet not forwarded") |
| } |
| |
| if nh := PayloadSince(p.Pkt.NetworkHeader()); nh[dstAddrOffset] != 3 { |
| t.Fatalf("got p.Pkt.NetworkHeader[dstAddrOffset] = %d, want = 3", nh[dstAddrOffset]) |
| } |
| |
| // Test that the address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| |
| func TestForwardingWithFakeResolverTwoPackets(t *testing.T) { |
| proto := fwdTestNetworkProtocol{ |
| addrResolveDelay: 500 * time.Millisecond, |
| onLinkAddressResolved: func(neigh *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| t.Helper() |
| if len(linkAddr) != 0 { |
| t.Fatalf("got linkAddr=%q, want unspecified", linkAddr) |
| } |
| // Any packets will be resolved to the link address "c". |
| neigh.handleConfirmation(addr, "c", ReachabilityConfirmationFlags{ |
| Solicited: true, |
| Override: false, |
| IsRouter: false, |
| }) |
| }, |
| } |
| ep1, ep2 := fwdTestNetFactory(t, &proto) |
| |
| // Inject two inbound packets to address 3 on NIC 1. |
| for i := 0; i < 2; i++ { |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| } |
| |
| for i := 0; i < 2; i++ { |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| case <-time.After(time.Second): |
| t.Fatal("packet not forwarded") |
| } |
| |
| if nh := PayloadSince(p.Pkt.NetworkHeader()); nh[dstAddrOffset] != 3 { |
| t.Fatalf("got p.Pkt.NetworkHeader[dstAddrOffset] = %d, want = 3", nh[dstAddrOffset]) |
| } |
| |
| // Test that the address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| } |
| |
| func TestForwardingWithFakeResolverManyPackets(t *testing.T) { |
| proto := fwdTestNetworkProtocol{ |
| addrResolveDelay: 500 * time.Millisecond, |
| onLinkAddressResolved: func(neigh *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| t.Helper() |
| if len(linkAddr) != 0 { |
| t.Fatalf("got linkAddr=%q, want unspecified", linkAddr) |
| } |
| // Any packets will be resolved to the link address "c". |
| neigh.handleConfirmation(addr, "c", ReachabilityConfirmationFlags{ |
| Solicited: true, |
| Override: false, |
| IsRouter: false, |
| }) |
| }, |
| } |
| ep1, ep2 := fwdTestNetFactory(t, &proto) |
| |
| for i := 0; i < maxPendingPacketsPerResolution+5; i++ { |
| // Inject inbound 'maxPendingPacketsPerResolution + 5' packets on NIC 1. |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = 3 |
| // Set the packet sequence number. |
| binary.BigEndian.PutUint16(buf[fwdTestNetHeaderLen:], uint16(i)) |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| } |
| |
| for i := 0; i < maxPendingPacketsPerResolution; i++ { |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| case <-time.After(time.Second): |
| t.Fatal("packet not forwarded") |
| } |
| |
| b := PayloadSince(p.Pkt.NetworkHeader()) |
| if b[dstAddrOffset] != 3 { |
| t.Fatalf("got b[dstAddrOffset] = %d, want = 3", b[dstAddrOffset]) |
| } |
| if len(b) < fwdTestNetHeaderLen+2 { |
| t.Fatalf("packet is too short to hold a sequence number: len(b) = %d", b) |
| } |
| seqNumBuf := b[fwdTestNetHeaderLen:] |
| |
| // The first 5 packets should not be forwarded so the sequence number should |
| // start with 5. |
| want := uint16(i + 5) |
| if n := binary.BigEndian.Uint16(seqNumBuf); n != want { |
| t.Fatalf("got the packet #%d, want = #%d", n, want) |
| } |
| |
| // Test that the address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| } |
| |
| func TestForwardingWithFakeResolverManyResolutions(t *testing.T) { |
| proto := fwdTestNetworkProtocol{ |
| addrResolveDelay: 500 * time.Millisecond, |
| onLinkAddressResolved: func(neigh *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress) { |
| t.Helper() |
| if len(linkAddr) != 0 { |
| t.Fatalf("got linkAddr=%q, want unspecified", linkAddr) |
| } |
| // Any packets will be resolved to the link address "c". |
| neigh.handleConfirmation(addr, "c", ReachabilityConfirmationFlags{ |
| Solicited: true, |
| Override: false, |
| IsRouter: false, |
| }) |
| }, |
| } |
| ep1, ep2 := fwdTestNetFactory(t, &proto) |
| |
| for i := 0; i < maxPendingResolutions+5; i++ { |
| // Inject inbound 'maxPendingResolutions + 5' packets on NIC 1. |
| // Each packet has a different destination address (3 to |
| // maxPendingResolutions + 7). |
| buf := buffer.NewView(30) |
| buf[dstAddrOffset] = byte(3 + i) |
| ep1.InjectInbound(fwdTestNetNumber, NewPacketBuffer(PacketBufferOptions{ |
| Data: buf.ToVectorisedView(), |
| })) |
| } |
| |
| for i := 0; i < maxPendingResolutions; i++ { |
| var p fwdTestPacketInfo |
| |
| select { |
| case p = <-ep2.C: |
| case <-time.After(time.Second): |
| t.Fatal("packet not forwarded") |
| } |
| |
| // The first 5 packets (address 3 to 7) should not be forwarded |
| // because their address resolutions are interrupted. |
| if nh := PayloadSince(p.Pkt.NetworkHeader()); nh[dstAddrOffset] < 8 { |
| t.Fatalf("got p.Pkt.NetworkHeader[dstAddrOffset] = %d, want p.Pkt.NetworkHeader[dstAddrOffset] >= 8", nh[dstAddrOffset]) |
| } |
| |
| // Test that the address resolution happened correctly. |
| if p.RemoteLinkAddress != "c" { |
| t.Fatalf("got p.RemoteLinkAddress = %s, want = c", p.RemoteLinkAddress) |
| } |
| if p.LocalLinkAddress != "b" { |
| t.Fatalf("got p.LocalLinkAddress = %s, want = b", p.LocalLinkAddress) |
| } |
| } |
| } |