mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Remove linkAddrCache
It was replaced by NUD/neighborCache. Fixes #4658. PiperOrigin-RevId: 356085221
This commit is contained in:
committed by
gVisor bot
parent
554c405e87
commit
3853a94f10
@@ -17,7 +17,6 @@ package arp_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -155,7 +154,7 @@ type testContext struct {
|
||||
nudDisp *arpDispatcher
|
||||
}
|
||||
|
||||
func newTestContext(t *testing.T, useNeighborCache bool) *testContext {
|
||||
func newTestContext(t *testing.T) *testContext {
|
||||
c := stack.DefaultNUDConfigurations()
|
||||
// Transition from Reachable to Stale almost immediately to test if receiving
|
||||
// probes refreshes positive reachability.
|
||||
@@ -173,7 +172,6 @@ func newTestContext(t *testing.T, useNeighborCache bool) *testContext {
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4},
|
||||
NUDConfigs: c,
|
||||
NUDDisp: &d,
|
||||
UseNeighborCache: useNeighborCache,
|
||||
})
|
||||
|
||||
ep := channel.New(defaultChannelSize, defaultMTU, stackLinkAddr)
|
||||
@@ -191,15 +189,6 @@ func newTestContext(t *testing.T, useNeighborCache bool) *testContext {
|
||||
if err := s.AddAddress(nicID, ipv4.ProtocolNumber, stackAddr); err != nil {
|
||||
t.Fatalf("AddAddress for ipv4 failed: %v", err)
|
||||
}
|
||||
if !useNeighborCache {
|
||||
// The remote address needs to be assigned to the NIC so we can receive and
|
||||
// verify outgoing ARP packets. The neighbor cache isn't concerned with
|
||||
// this; the tests that use linkAddrCache expect the ARP responses to be
|
||||
// received by the same NIC.
|
||||
if err := s.AddAddress(nicID, ipv4.ProtocolNumber, remoteAddr); err != nil {
|
||||
t.Fatalf("AddAddress for ipv4 failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.SetRouteTable([]tcpip.Route{{
|
||||
Destination: header.IPv4EmptySubnet,
|
||||
@@ -217,86 +206,8 @@ func (c *testContext) cleanup() {
|
||||
c.linkEP.Close()
|
||||
}
|
||||
|
||||
func TestDirectRequest(t *testing.T) {
|
||||
c := newTestContext(t, false /* useNeighborCache */)
|
||||
defer c.cleanup()
|
||||
|
||||
const senderMAC = "\x01\x02\x03\x04\x05\x06"
|
||||
const senderIPv4 = "\x0a\x00\x00\x02"
|
||||
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
h := header.ARP(v)
|
||||
h.SetIPv4OverEthernet()
|
||||
h.SetOp(header.ARPRequest)
|
||||
copy(h.HardwareAddressSender(), senderMAC)
|
||||
copy(h.ProtocolAddressSender(), senderIPv4)
|
||||
|
||||
inject := func(addr tcpip.Address) {
|
||||
copy(h.ProtocolAddressTarget(), addr)
|
||||
c.linkEP.InjectInbound(arp.ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: v.ToVectorisedView(),
|
||||
}))
|
||||
}
|
||||
|
||||
for i, address := range []tcpip.Address{stackAddr, remoteAddr} {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
expectedPacketsReceived := c.s.Stats().ARP.PacketsReceived.Value() + 1
|
||||
expectedRequestsReceived := c.s.Stats().ARP.RequestsReceived.Value() + 1
|
||||
expectedRepliesSent := c.s.Stats().ARP.OutgoingRepliesSent.Value() + 1
|
||||
|
||||
inject(address)
|
||||
pi, _ := c.linkEP.ReadContext(context.Background())
|
||||
if pi.Proto != arp.ProtocolNumber {
|
||||
t.Fatalf("expected ARP response, got network protocol number %d", pi.Proto)
|
||||
}
|
||||
rep := header.ARP(pi.Pkt.NetworkHeader().View())
|
||||
if !rep.IsValid() {
|
||||
t.Fatalf("invalid ARP response: len = %d; response = %x", len(rep), rep)
|
||||
}
|
||||
if got := rep.Op(); got != header.ARPReply {
|
||||
t.Fatalf("got Op = %d, want = %d", got, header.ARPReply)
|
||||
}
|
||||
if got, want := tcpip.LinkAddress(rep.HardwareAddressSender()), stackLinkAddr; got != want {
|
||||
t.Errorf("got HardwareAddressSender = %s, want = %s", got, want)
|
||||
}
|
||||
if got, want := tcpip.Address(rep.ProtocolAddressSender()), tcpip.Address(h.ProtocolAddressTarget()); got != want {
|
||||
t.Errorf("got ProtocolAddressSender = %s, want = %s", got, want)
|
||||
}
|
||||
if got, want := tcpip.LinkAddress(rep.HardwareAddressTarget()), tcpip.LinkAddress(h.HardwareAddressSender()); got != want {
|
||||
t.Errorf("got HardwareAddressTarget = %s, want = %s", got, want)
|
||||
}
|
||||
if got, want := tcpip.Address(rep.ProtocolAddressTarget()), tcpip.Address(h.ProtocolAddressSender()); got != want {
|
||||
t.Errorf("got ProtocolAddressTarget = %s, want = %s", got, want)
|
||||
}
|
||||
|
||||
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != expectedPacketsReceived {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, expectedPacketsReceived)
|
||||
}
|
||||
if got := c.s.Stats().ARP.RequestsReceived.Value(); got != expectedRequestsReceived {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, expectedRequestsReceived)
|
||||
}
|
||||
if got := c.s.Stats().ARP.OutgoingRepliesSent.Value(); got != expectedRepliesSent {
|
||||
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, expectedRepliesSent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inject(unknownAddr)
|
||||
// Sleep tests are gross, but this will only potentially flake
|
||||
// if there's a bug. If there is no bug this will reliably
|
||||
// succeed.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
if pkt, ok := c.linkEP.ReadContext(ctx); ok {
|
||||
t.Errorf("stackAddrBad: unexpected packet sent, Proto=%v", pkt.Proto)
|
||||
}
|
||||
if got := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.RequestsReceivedUnKnownTargetAddress.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMalformedPacket(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
c := newTestContext(t)
|
||||
defer c.cleanup()
|
||||
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
@@ -315,7 +226,7 @@ func TestMalformedPacket(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDisabledEndpoint(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
c := newTestContext(t)
|
||||
defer c.cleanup()
|
||||
|
||||
ep, err := c.s.GetNetworkEndpoint(nicID, header.ARPProtocolNumber)
|
||||
@@ -340,7 +251,7 @@ func TestDisabledEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectReply(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
c := newTestContext(t)
|
||||
defer c.cleanup()
|
||||
|
||||
const senderMAC = "\x01\x02\x03\x04\x05\x06"
|
||||
@@ -370,8 +281,8 @@ func TestDirectReply(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectRequestWithNeighborCache(t *testing.T) {
|
||||
c := newTestContext(t, true /* useNeighborCache */)
|
||||
func TestDirectRequest(t *testing.T) {
|
||||
c := newTestContext(t)
|
||||
defer c.cleanup()
|
||||
|
||||
tests := []struct {
|
||||
|
||||
+109
-288
@@ -175,168 +175,9 @@ func handleICMPInIPv6(ep stack.NetworkEndpoint, src, dst tcpip.Address, icmp hea
|
||||
}
|
||||
|
||||
func TestICMPCounts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
useNeighborCache bool
|
||||
}{
|
||||
{
|
||||
name: "linkAddrCache",
|
||||
useNeighborCache: false,
|
||||
},
|
||||
{
|
||||
name: "neighborCache",
|
||||
useNeighborCache: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
|
||||
UseNeighborCache: test.useNeighborCache,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
|
||||
t.Fatalf("CreateNIC(_, _) = %s", err)
|
||||
}
|
||||
{
|
||||
subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr1))))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.SetRouteTable(
|
||||
[]tcpip.Route{{
|
||||
Destination: subnet,
|
||||
NIC: nicID,
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
netProto := s.NetworkProtocolInstance(ProtocolNumber)
|
||||
if netProto == nil {
|
||||
t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
|
||||
}
|
||||
ep := netProto.NewEndpoint(&testInterface{}, &stubDispatcher{})
|
||||
defer ep.Close()
|
||||
|
||||
if err := ep.Enable(); err != nil {
|
||||
t.Fatalf("ep.Enable(): %s", err)
|
||||
}
|
||||
|
||||
addressableEndpoint, ok := ep.(stack.AddressableEndpoint)
|
||||
if !ok {
|
||||
t.Fatalf("expected network endpoint to implement stack.AddressableEndpoint")
|
||||
}
|
||||
addr := lladdr0.WithPrefix()
|
||||
if ep, err := addressableEndpoint.AddAndAcquirePermanentAddress(addr, stack.CanBePrimaryEndpoint, stack.AddressConfigStatic, false /* deprecated */); err != nil {
|
||||
t.Fatalf("addressableEndpoint.AddAndAcquirePermanentAddress(%s, CanBePrimaryEndpoint, AddressConfigStatic, false): %s", addr, err)
|
||||
} else {
|
||||
ep.DecRef()
|
||||
}
|
||||
|
||||
var tllData [header.NDPLinkLayerAddressSize]byte
|
||||
header.NDPOptions(tllData[:]).Serialize(header.NDPOptionsSerializer{
|
||||
header.NDPTargetLinkLayerAddressOption(linkAddr1),
|
||||
})
|
||||
|
||||
types := []struct {
|
||||
typ header.ICMPv6Type
|
||||
size int
|
||||
extraData []byte
|
||||
}{
|
||||
{
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
size: header.ICMPv6DstUnreachableMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6PacketTooBig,
|
||||
size: header.ICMPv6PacketTooBigMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6TimeExceeded,
|
||||
size: header.ICMPv6MinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6ParamProblem,
|
||||
size: header.ICMPv6MinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6EchoRequest,
|
||||
size: header.ICMPv6EchoMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6EchoReply,
|
||||
size: header.ICMPv6EchoMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6RouterSolicit,
|
||||
size: header.ICMPv6MinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6RouterAdvert,
|
||||
size: header.ICMPv6HeaderSize + header.NDPRAMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6NeighborSolicit,
|
||||
size: header.ICMPv6NeighborSolicitMinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6NeighborAdvert,
|
||||
size: header.ICMPv6NeighborAdvertMinimumSize,
|
||||
extraData: tllData[:],
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6RedirectMsg,
|
||||
size: header.ICMPv6MinimumSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6MulticastListenerQuery,
|
||||
size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6MulticastListenerReport,
|
||||
size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
|
||||
},
|
||||
{
|
||||
typ: header.ICMPv6MulticastListenerDone,
|
||||
size: header.MLDMinimumSize + header.ICMPv6HeaderSize,
|
||||
},
|
||||
{
|
||||
typ: 255, /* Unrecognized */
|
||||
size: 50,
|
||||
},
|
||||
}
|
||||
|
||||
for _, typ := range types {
|
||||
icmp := header.ICMPv6(buffer.NewView(typ.size + len(typ.extraData)))
|
||||
copy(icmp[typ.size:], typ.extraData)
|
||||
icmp.SetType(typ.typ)
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(icmp[:typ.size], lladdr0, lladdr1, buffer.View(typ.extraData).ToVectorisedView()))
|
||||
handleICMPInIPv6(ep, lladdr1, lladdr0, icmp)
|
||||
}
|
||||
|
||||
// Construct an empty ICMP packet so that
|
||||
// Stats().ICMP.ICMPv6ReceivedPacketStats.Invalid is incremented.
|
||||
handleICMPInIPv6(ep, lladdr1, lladdr0, header.ICMPv6(buffer.NewView(header.IPv6MinimumSize)))
|
||||
|
||||
icmpv6Stats := s.Stats().ICMP.V6.PacketsReceived
|
||||
visitStats(reflect.ValueOf(&icmpv6Stats).Elem(), func(name string, s *tcpip.StatCounter) {
|
||||
if got, want := s.Value(), uint64(1); got != want {
|
||||
t.Errorf("got %s = %d, want = %d", name, got, want)
|
||||
}
|
||||
})
|
||||
if t.Failed() {
|
||||
t.Logf("stats:\n%+v", s.Stats())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestICMPCountsWithNeighborCache(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
|
||||
t.Fatalf("CreateNIC(_, _) = %s", err)
|
||||
@@ -770,135 +611,116 @@ func TestICMPChecksumValidationSimple(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
useNeighborCache bool
|
||||
}{
|
||||
{
|
||||
name: "linkAddrCache",
|
||||
useNeighborCache: false,
|
||||
},
|
||||
{
|
||||
name: "neighborCache",
|
||||
useNeighborCache: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, typ := range types {
|
||||
for _, isRouter := range []bool{false, true} {
|
||||
name := typ.name
|
||||
if isRouter {
|
||||
name += " (Router)"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
e := channel.New(0, 1280, linkAddr0)
|
||||
|
||||
// Indicate that resolution for link layer addresses is required to
|
||||
// send packets over this link. This is needed so the NIC knows to
|
||||
// allocate a neighbor table.
|
||||
e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
UseNeighborCache: test.useNeighborCache,
|
||||
})
|
||||
if isRouter {
|
||||
// Enabling forwarding makes the stack act as a router.
|
||||
s.SetForwarding(ProtocolNumber, true)
|
||||
}
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(_, _) = %s", err)
|
||||
}
|
||||
|
||||
if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
|
||||
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, lladdr0, err)
|
||||
}
|
||||
{
|
||||
subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr1))))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.SetRouteTable(
|
||||
[]tcpip.Route{{
|
||||
Destination: subnet,
|
||||
NIC: nicID,
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
handleIPv6Payload := func(checksum bool) {
|
||||
icmp := header.ICMPv6(buffer.NewView(typ.size + len(typ.extraData)))
|
||||
copy(icmp[typ.size:], typ.extraData)
|
||||
icmp.SetType(typ.typ)
|
||||
if checksum {
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(icmp, lladdr1, lladdr0, buffer.View{}.ToVectorisedView()))
|
||||
}
|
||||
ip := header.IPv6(buffer.NewView(header.IPv6MinimumSize))
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(len(icmp)),
|
||||
TransportProtocol: header.ICMPv6ProtocolNumber,
|
||||
HopLimit: header.NDPHopLimit,
|
||||
SrcAddr: lladdr1,
|
||||
DstAddr: lladdr0,
|
||||
})
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: buffer.NewVectorisedView(len(ip)+len(icmp), []buffer.View{buffer.View(ip), buffer.View(icmp)}),
|
||||
})
|
||||
e.InjectInbound(ProtocolNumber, pkt)
|
||||
}
|
||||
|
||||
stats := s.Stats().ICMP.V6.PacketsReceived
|
||||
invalid := stats.Invalid
|
||||
routerOnly := stats.RouterOnlyPacketsDroppedByHost
|
||||
typStat := typ.statCounter(stats)
|
||||
|
||||
// Initial stat counts should be 0.
|
||||
if got := invalid.Value(); got != 0 {
|
||||
t.Fatalf("got invalid = %d, want = 0", got)
|
||||
}
|
||||
if got := routerOnly.Value(); got != 0 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 0", got)
|
||||
}
|
||||
if got := typStat.Value(); got != 0 {
|
||||
t.Fatalf("got %s = %d, want = 0", typ.name, got)
|
||||
}
|
||||
|
||||
// Without setting checksum, the incoming packet should
|
||||
// be invalid.
|
||||
handleIPv6Payload(false)
|
||||
if got := invalid.Value(); got != 1 {
|
||||
t.Fatalf("got invalid = %d, want = 1", got)
|
||||
}
|
||||
// Router only count should not have increased.
|
||||
if got := routerOnly.Value(); got != 0 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 0", got)
|
||||
}
|
||||
// Rx count of type typ.typ should not have increased.
|
||||
if got := typStat.Value(); got != 0 {
|
||||
t.Fatalf("got %s = %d, want = 0", typ.name, got)
|
||||
}
|
||||
|
||||
// When checksum is set, it should be received.
|
||||
handleIPv6Payload(true)
|
||||
if got := typStat.Value(); got != 1 {
|
||||
t.Fatalf("got %s = %d, want = 1", typ.name, got)
|
||||
}
|
||||
// Invalid count should not have increased again.
|
||||
if got := invalid.Value(); got != 1 {
|
||||
t.Fatalf("got invalid = %d, want = 1", got)
|
||||
}
|
||||
if !isRouter && typ.routerOnly && test.useNeighborCache {
|
||||
// Router only count should have increased.
|
||||
if got := routerOnly.Value(); got != 1 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, typ := range types {
|
||||
for _, isRouter := range []bool{false, true} {
|
||||
name := typ.name
|
||||
if isRouter {
|
||||
name += " (Router)"
|
||||
}
|
||||
})
|
||||
t.Run(name, func(t *testing.T) {
|
||||
e := channel.New(0, 1280, linkAddr0)
|
||||
|
||||
// Indicate that resolution for link layer addresses is required to
|
||||
// send packets over this link. This is needed so the NIC knows to
|
||||
// allocate a neighbor table.
|
||||
e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
})
|
||||
if isRouter {
|
||||
// Enabling forwarding makes the stack act as a router.
|
||||
s.SetForwarding(ProtocolNumber, true)
|
||||
}
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(_, _) = %s", err)
|
||||
}
|
||||
|
||||
if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
|
||||
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, lladdr0, err)
|
||||
}
|
||||
{
|
||||
subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr1))))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.SetRouteTable(
|
||||
[]tcpip.Route{{
|
||||
Destination: subnet,
|
||||
NIC: nicID,
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
handleIPv6Payload := func(checksum bool) {
|
||||
icmp := header.ICMPv6(buffer.NewView(typ.size + len(typ.extraData)))
|
||||
copy(icmp[typ.size:], typ.extraData)
|
||||
icmp.SetType(typ.typ)
|
||||
if checksum {
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(icmp, lladdr1, lladdr0, buffer.View{}.ToVectorisedView()))
|
||||
}
|
||||
ip := header.IPv6(buffer.NewView(header.IPv6MinimumSize))
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(len(icmp)),
|
||||
TransportProtocol: header.ICMPv6ProtocolNumber,
|
||||
HopLimit: header.NDPHopLimit,
|
||||
SrcAddr: lladdr1,
|
||||
DstAddr: lladdr0,
|
||||
})
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: buffer.NewVectorisedView(len(ip)+len(icmp), []buffer.View{buffer.View(ip), buffer.View(icmp)}),
|
||||
})
|
||||
e.InjectInbound(ProtocolNumber, pkt)
|
||||
}
|
||||
|
||||
stats := s.Stats().ICMP.V6.PacketsReceived
|
||||
invalid := stats.Invalid
|
||||
routerOnly := stats.RouterOnlyPacketsDroppedByHost
|
||||
typStat := typ.statCounter(stats)
|
||||
|
||||
// Initial stat counts should be 0.
|
||||
if got := invalid.Value(); got != 0 {
|
||||
t.Fatalf("got invalid = %d, want = 0", got)
|
||||
}
|
||||
if got := routerOnly.Value(); got != 0 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 0", got)
|
||||
}
|
||||
if got := typStat.Value(); got != 0 {
|
||||
t.Fatalf("got %s = %d, want = 0", typ.name, got)
|
||||
}
|
||||
|
||||
// Without setting checksum, the incoming packet should
|
||||
// be invalid.
|
||||
handleIPv6Payload(false)
|
||||
if got := invalid.Value(); got != 1 {
|
||||
t.Fatalf("got invalid = %d, want = 1", got)
|
||||
}
|
||||
// Router only count should not have increased.
|
||||
if got := routerOnly.Value(); got != 0 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 0", got)
|
||||
}
|
||||
// Rx count of type typ.typ should not have increased.
|
||||
if got := typStat.Value(); got != 0 {
|
||||
t.Fatalf("got %s = %d, want = 0", typ.name, got)
|
||||
}
|
||||
|
||||
// When checksum is set, it should be received.
|
||||
handleIPv6Payload(true)
|
||||
if got := typStat.Value(); got != 1 {
|
||||
t.Fatalf("got %s = %d, want = 1", typ.name, got)
|
||||
}
|
||||
// Invalid count should not have increased again.
|
||||
if got := invalid.Value(); got != 1 {
|
||||
t.Fatalf("got invalid = %d, want = 1", got)
|
||||
}
|
||||
if !isRouter && typ.routerOnly {
|
||||
// Router only count should have increased.
|
||||
if got := routerOnly.Value(); got != 1 {
|
||||
t.Fatalf("got RouterOnlyPacketsReceivedByHost = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1762,7 +1584,6 @@ func TestCallsToNeighborCache(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
{
|
||||
if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
|
||||
|
||||
+368
-637
File diff suppressed because it is too large
Load Diff
@@ -3,18 +3,6 @@ load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_template_instance(
|
||||
name = "linkaddrentry_list",
|
||||
out = "linkaddrentry_list.go",
|
||||
package = "stack",
|
||||
prefix = "linkAddrEntry",
|
||||
template = "//pkg/ilist:generic_list",
|
||||
types = {
|
||||
"Element": "*linkAddrEntry",
|
||||
"Linker": "*linkAddrEntry",
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "neighbor_entry_list",
|
||||
out = "neighbor_entry_list.go",
|
||||
@@ -62,8 +50,6 @@ go_library(
|
||||
"iptables_state.go",
|
||||
"iptables_targets.go",
|
||||
"iptables_types.go",
|
||||
"linkaddrcache.go",
|
||||
"linkaddrentry_list.go",
|
||||
"neighbor_cache.go",
|
||||
"neighbor_entry.go",
|
||||
"neighbor_entry_list.go",
|
||||
@@ -141,7 +127,6 @@ go_test(
|
||||
size = "small",
|
||||
srcs = [
|
||||
"forwarding_test.go",
|
||||
"linkaddrcache_test.go",
|
||||
"neighbor_cache_test.go",
|
||||
"neighbor_entry_test.go",
|
||||
"nic_test.go",
|
||||
|
||||
+250
-360
File diff suppressed because it is too large
Load Diff
@@ -1,359 +0,0 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
const linkAddrCacheSize = 512 // max cache entries
|
||||
|
||||
// linkAddrCache is a fixed-sized cache mapping IP addresses to link addresses.
|
||||
//
|
||||
// The entries are stored in a ring buffer, oldest entry replaced first.
|
||||
//
|
||||
// This struct is safe for concurrent use.
|
||||
type linkAddrCache struct {
|
||||
nic *nic
|
||||
|
||||
linkRes LinkAddressResolver
|
||||
|
||||
// ageLimit is how long a cache entry is valid for.
|
||||
ageLimit time.Duration
|
||||
|
||||
// resolutionTimeout is the amount of time to wait for a link request to
|
||||
// resolve an address.
|
||||
resolutionTimeout time.Duration
|
||||
|
||||
// resolutionAttempts is the number of times an address is attempted to be
|
||||
// resolved before failing.
|
||||
resolutionAttempts int
|
||||
|
||||
mu struct {
|
||||
sync.Mutex
|
||||
table map[tcpip.Address]*linkAddrEntry
|
||||
lru linkAddrEntryList
|
||||
}
|
||||
}
|
||||
|
||||
// entryState controls the state of a single entry in the cache.
|
||||
type entryState int
|
||||
|
||||
const (
|
||||
// incomplete means that there is an outstanding request to resolve the
|
||||
// address. This is the initial state.
|
||||
incomplete entryState = iota
|
||||
// ready means that the address has been resolved and can be used.
|
||||
ready
|
||||
)
|
||||
|
||||
// String implements Stringer.
|
||||
func (s entryState) String() string {
|
||||
switch s {
|
||||
case incomplete:
|
||||
return "incomplete"
|
||||
case ready:
|
||||
return "ready"
|
||||
default:
|
||||
return fmt.Sprintf("unknown(%d)", s)
|
||||
}
|
||||
}
|
||||
|
||||
// A linkAddrEntry is an entry in the linkAddrCache.
|
||||
// This struct is thread-compatible.
|
||||
type linkAddrEntry struct {
|
||||
// linkAddrEntryEntry access is synchronized by the linkAddrCache lock.
|
||||
linkAddrEntryEntry
|
||||
|
||||
cache *linkAddrCache
|
||||
|
||||
mu struct {
|
||||
sync.RWMutex
|
||||
|
||||
addr tcpip.Address
|
||||
linkAddr tcpip.LinkAddress
|
||||
expiration time.Time
|
||||
s entryState
|
||||
|
||||
// done is closed when address resolution is complete. It is nil iff s is
|
||||
// incomplete and resolution is not yet in progress.
|
||||
done chan struct{}
|
||||
|
||||
// onResolve is called with the result of address resolution.
|
||||
onResolve []func(LinkResolutionResult)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *linkAddrEntry) notifyCompletionLocked(linkAddr tcpip.LinkAddress) {
|
||||
res := LinkResolutionResult{LinkAddress: linkAddr, Success: len(linkAddr) != 0}
|
||||
for _, callback := range e.mu.onResolve {
|
||||
callback(res)
|
||||
}
|
||||
e.mu.onResolve = nil
|
||||
if ch := e.mu.done; ch != nil {
|
||||
close(ch)
|
||||
e.mu.done = nil
|
||||
// Dequeue the pending packets in a new goroutine to not hold up the current
|
||||
// goroutine as writing packets may be a costly operation.
|
||||
//
|
||||
// At the time of writing, when writing packets, a neighbor's link address
|
||||
// is resolved (which ends up obtaining the entry's lock) while holding the
|
||||
// link resolution queue's lock. Dequeuing packets in a new goroutine avoids
|
||||
// a lock ordering violation.
|
||||
go e.cache.nic.linkResQueue.dequeue(ch, linkAddr, len(linkAddr) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
// changeStateLocked sets the entry's state to ns.
|
||||
//
|
||||
// The entry's expiration is bumped up to the greater of itself and the passed
|
||||
// expiration; the zero value indicates immediate expiration, and is set
|
||||
// unconditionally - this is an implementation detail that allows for entries
|
||||
// to be reused.
|
||||
//
|
||||
// Precondition: e.mu must be locked
|
||||
func (e *linkAddrEntry) changeStateLocked(ns entryState, expiration time.Time) {
|
||||
if e.mu.s == incomplete && ns == ready {
|
||||
e.notifyCompletionLocked(e.mu.linkAddr)
|
||||
}
|
||||
|
||||
if expiration.IsZero() || expiration.After(e.mu.expiration) {
|
||||
e.mu.expiration = expiration
|
||||
}
|
||||
e.mu.s = ns
|
||||
}
|
||||
|
||||
// add adds a k -> v mapping to the cache.
|
||||
func (c *linkAddrCache) add(k tcpip.Address, v tcpip.LinkAddress) {
|
||||
// Calculate expiration time before acquiring the lock, since expiration is
|
||||
// relative to the time when information was learned, rather than when it
|
||||
// happened to be inserted into the cache.
|
||||
expiration := time.Now().Add(c.ageLimit)
|
||||
|
||||
c.mu.Lock()
|
||||
entry := c.getOrCreateEntryLocked(k)
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
c.mu.Unlock()
|
||||
|
||||
entry.mu.linkAddr = v
|
||||
entry.changeStateLocked(ready, expiration)
|
||||
}
|
||||
|
||||
// getOrCreateEntryLocked retrieves a cache entry associated with k. The
|
||||
// returned entry is always refreshed in the cache (it is reachable via the
|
||||
// map, and its place is bumped in LRU).
|
||||
//
|
||||
// If a matching entry exists in the cache, it is returned. If no matching
|
||||
// entry exists and the cache is full, an existing entry is evicted via LRU,
|
||||
// reset to state incomplete, and returned. If no matching entry exists and the
|
||||
// cache is not full, a new entry with state incomplete is allocated and
|
||||
// returned.
|
||||
func (c *linkAddrCache) getOrCreateEntryLocked(k tcpip.Address) *linkAddrEntry {
|
||||
if entry, ok := c.mu.table[k]; ok {
|
||||
c.mu.lru.Remove(entry)
|
||||
c.mu.lru.PushFront(entry)
|
||||
return entry
|
||||
}
|
||||
var entry *linkAddrEntry
|
||||
if len(c.mu.table) == linkAddrCacheSize {
|
||||
entry = c.mu.lru.Back()
|
||||
entry.mu.Lock()
|
||||
|
||||
delete(c.mu.table, entry.mu.addr)
|
||||
c.mu.lru.Remove(entry)
|
||||
|
||||
// Wake waiters and mark the soon-to-be-reused entry as expired.
|
||||
entry.notifyCompletionLocked("" /* linkAddr */)
|
||||
entry.mu.Unlock()
|
||||
} else {
|
||||
entry = new(linkAddrEntry)
|
||||
}
|
||||
|
||||
*entry = linkAddrEntry{
|
||||
cache: c,
|
||||
}
|
||||
entry.mu.Lock()
|
||||
entry.mu.addr = k
|
||||
entry.mu.s = incomplete
|
||||
entry.mu.Unlock()
|
||||
c.mu.table[k] = entry
|
||||
c.mu.lru.PushFront(entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
// get reports any known link address for addr.
|
||||
func (c *linkAddrCache) get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
|
||||
c.mu.Lock()
|
||||
entry := c.getOrCreateEntryLocked(addr)
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
c.mu.Unlock()
|
||||
|
||||
switch s := entry.mu.s; s {
|
||||
case ready:
|
||||
if !time.Now().After(entry.mu.expiration) {
|
||||
// Not expired.
|
||||
if onResolve != nil {
|
||||
onResolve(LinkResolutionResult{LinkAddress: entry.mu.linkAddr, Success: true})
|
||||
}
|
||||
return entry.mu.linkAddr, nil, nil
|
||||
}
|
||||
|
||||
entry.changeStateLocked(incomplete, time.Time{})
|
||||
fallthrough
|
||||
case incomplete:
|
||||
if onResolve != nil {
|
||||
entry.mu.onResolve = append(entry.mu.onResolve, onResolve)
|
||||
}
|
||||
if entry.mu.done == nil {
|
||||
entry.mu.done = make(chan struct{})
|
||||
go c.startAddressResolution(addr, localAddr, entry.mu.done) // S/R-SAFE: link non-savable; wakers dropped synchronously.
|
||||
}
|
||||
return entry.mu.linkAddr, entry.mu.done, &tcpip.ErrWouldBlock{}
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid cache entry state: %s", s))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) startAddressResolution(k tcpip.Address, localAddr tcpip.Address, done <-chan struct{}) {
|
||||
for i := 0; ; i++ {
|
||||
// Send link request, then wait for the timeout limit and check
|
||||
// whether the request succeeded.
|
||||
c.linkRes.LinkAddressRequest(k, localAddr, "" /* linkAddr */)
|
||||
|
||||
select {
|
||||
case now := <-time.After(c.resolutionTimeout):
|
||||
if stop := c.checkLinkRequest(now, k, i); stop {
|
||||
return
|
||||
}
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkLinkRequest checks whether previous attempt to resolve address has
|
||||
// succeeded and mark the entry accordingly. Returns true if request can stop,
|
||||
// false if another request should be sent.
|
||||
func (c *linkAddrCache) checkLinkRequest(now time.Time, k tcpip.Address, attempt int) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
entry, ok := c.mu.table[k]
|
||||
if !ok {
|
||||
// Entry was evicted from the cache.
|
||||
return true
|
||||
}
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
|
||||
switch s := entry.mu.s; s {
|
||||
case ready:
|
||||
// Entry was made ready by resolver.
|
||||
case incomplete:
|
||||
if attempt+1 < c.resolutionAttempts {
|
||||
// No response yet, need to send another ARP request.
|
||||
return false
|
||||
}
|
||||
// Max number of retries reached, delete entry.
|
||||
entry.notifyCompletionLocked("" /* linkAddr */)
|
||||
delete(c.mu.table, k)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid cache entry state: %s", s))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) init(nic *nic, ageLimit, resolutionTimeout time.Duration, resolutionAttempts int, linkRes LinkAddressResolver) {
|
||||
*c = linkAddrCache{
|
||||
nic: nic,
|
||||
linkRes: linkRes,
|
||||
ageLimit: ageLimit,
|
||||
resolutionTimeout: resolutionTimeout,
|
||||
resolutionAttempts: resolutionAttempts,
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.mu.table = make(map[tcpip.Address]*linkAddrEntry, linkAddrCacheSize)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
var _ neighborTable = (*linkAddrCache)(nil)
|
||||
|
||||
func (*linkAddrCache) neighbors() ([]NeighborEntry, tcpip.Error) {
|
||||
return nil, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) addStaticEntry(addr tcpip.Address, linkAddr tcpip.LinkAddress) {
|
||||
c.add(addr, linkAddr)
|
||||
}
|
||||
|
||||
func (*linkAddrCache) remove(addr tcpip.Address) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (*linkAddrCache) removeAll() tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) handleProbe(addr tcpip.Address, linkAddr tcpip.LinkAddress) {
|
||||
if len(linkAddr) != 0 {
|
||||
// NUD allows probes without a link address but linkAddrCache
|
||||
// is a simple neighbor table which does not implement NUD.
|
||||
//
|
||||
// As per RFC 4861 section 4.3,
|
||||
//
|
||||
// Source link-layer address
|
||||
// The link-layer address for the sender. MUST NOT be
|
||||
// included when the source IP address is the
|
||||
// unspecified address. Otherwise, on link layers
|
||||
// that have addresses this option MUST be included in
|
||||
// multicast solicitations and SHOULD be included in
|
||||
// unicast solicitations.
|
||||
c.add(addr, linkAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) handleConfirmation(addr tcpip.Address, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) {
|
||||
if len(linkAddr) != 0 {
|
||||
// NUD allows confirmations without a link address but linkAddrCache
|
||||
// is a simple neighbor table which does not implement NUD.
|
||||
//
|
||||
// As per RFC 4861 section 4.4,
|
||||
//
|
||||
// Target link-layer address
|
||||
// The link-layer address for the target, i.e., the
|
||||
// sender of the advertisement. This option MUST be
|
||||
// included on link layers that have addresses when
|
||||
// responding to multicast solicitations. When
|
||||
// responding to a unicast Neighbor Solicitation this
|
||||
// option SHOULD be included.
|
||||
c.add(addr, linkAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *linkAddrCache) handleUpperLevelConfirmation(tcpip.Address) {}
|
||||
|
||||
func (*linkAddrCache) nudConfig() (NUDConfigurations, tcpip.Error) {
|
||||
return NUDConfigurations{}, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (*linkAddrCache) setNUDConfig(NUDConfigurations) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
type testaddr struct {
|
||||
addr tcpip.Address
|
||||
linkAddr tcpip.LinkAddress
|
||||
}
|
||||
|
||||
var testAddrs = func() []testaddr {
|
||||
var addrs []testaddr
|
||||
for i := 0; i < 4*linkAddrCacheSize; i++ {
|
||||
addr := fmt.Sprintf("Addr%06d", i)
|
||||
addrs = append(addrs, testaddr{
|
||||
addr: tcpip.Address(addr),
|
||||
linkAddr: tcpip.LinkAddress("Link" + addr),
|
||||
})
|
||||
}
|
||||
return addrs
|
||||
}()
|
||||
|
||||
type testLinkAddressResolver struct {
|
||||
cache *linkAddrCache
|
||||
delay time.Duration
|
||||
onLinkAddressRequest func()
|
||||
}
|
||||
|
||||
func (r *testLinkAddressResolver) LinkAddressRequest(targetAddr, _ tcpip.Address, _ tcpip.LinkAddress) tcpip.Error {
|
||||
// TODO(gvisor.dev/issue/5141): Use a fake clock.
|
||||
time.AfterFunc(r.delay, func() { r.fakeRequest(targetAddr) })
|
||||
if f := r.onLinkAddressRequest; f != nil {
|
||||
f()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *testLinkAddressResolver) fakeRequest(addr tcpip.Address) {
|
||||
for _, ta := range testAddrs {
|
||||
if ta.addr == addr {
|
||||
r.cache.add(ta.addr, ta.linkAddr)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (*testLinkAddressResolver) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) {
|
||||
if addr == "broadcast" {
|
||||
return "mac_broadcast", true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (*testLinkAddressResolver) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return 1
|
||||
}
|
||||
|
||||
func getBlocking(c *linkAddrCache, addr tcpip.Address) (tcpip.LinkAddress, tcpip.Error) {
|
||||
var attemptedResolution bool
|
||||
for {
|
||||
got, ch, err := c.get(addr, "", nil)
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
if attemptedResolution {
|
||||
return got, &tcpip.ErrTimeout{}
|
||||
}
|
||||
attemptedResolution = true
|
||||
<-ch
|
||||
continue
|
||||
}
|
||||
return got, err
|
||||
}
|
||||
}
|
||||
|
||||
func newEmptyNIC() *nic {
|
||||
n := &nic{}
|
||||
n.linkResQueue.init(n)
|
||||
return n
|
||||
}
|
||||
|
||||
func TestCacheOverflow(t *testing.T) {
|
||||
var c linkAddrCache
|
||||
c.init(newEmptyNIC(), 1<<63-1, 1*time.Second, 3, nil)
|
||||
for i := len(testAddrs) - 1; i >= 0; i-- {
|
||||
e := testAddrs[i]
|
||||
c.add(e.addr, e.linkAddr)
|
||||
got, _, err := c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("insert %d, c.get(%s, '', nil): %s", i, e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("insert %d, got c.get(%s, '', nil) = %s, want = %s", i, e.addr, got, e.linkAddr)
|
||||
}
|
||||
}
|
||||
// Expect to find at least half of the most recent entries.
|
||||
for i := 0; i < linkAddrCacheSize/2; i++ {
|
||||
e := testAddrs[i]
|
||||
got, _, err := c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("check %d, c.get(%s, '', nil): %s", i, e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("check %d, got c.get(%s, '', nil) = %s, want = %s", i, e.addr, got, e.linkAddr)
|
||||
}
|
||||
}
|
||||
// The earliest entries should no longer be in the cache.
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
for i := len(testAddrs) - 1; i >= len(testAddrs)-linkAddrCacheSize; i-- {
|
||||
e := testAddrs[i]
|
||||
if entry, ok := c.mu.table[e.addr]; ok {
|
||||
t.Errorf("unexpected entry at c.mu.table[%s]: %#v", e.addr, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheConcurrent(t *testing.T) {
|
||||
var c linkAddrCache
|
||||
linkRes := &testLinkAddressResolver{cache: &c}
|
||||
c.init(newEmptyNIC(), 1<<63-1, 1*time.Second, 3, linkRes)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for r := 0; r < 16; r++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for _, e := range testAddrs {
|
||||
c.add(e.addr, e.linkAddr)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// All goroutines add in the same order and add more values than
|
||||
// can fit in the cache, so our eviction strategy requires that
|
||||
// the last entry be present and the first be missing.
|
||||
e := testAddrs[len(testAddrs)-1]
|
||||
got, _, err := c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("c.get(%s, '', nil): %s", e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("got c.get(%s, '', nil) = %s, want = %s", e.addr, got, e.linkAddr)
|
||||
}
|
||||
|
||||
e = testAddrs[0]
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if entry, ok := c.mu.table[e.addr]; ok {
|
||||
t.Errorf("unexpected entry at c.mu.table[%s]: %#v", e.addr, entry)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheAgeLimit(t *testing.T) {
|
||||
var c linkAddrCache
|
||||
linkRes := &testLinkAddressResolver{cache: &c}
|
||||
c.init(newEmptyNIC(), 1*time.Millisecond, 1*time.Second, 3, linkRes)
|
||||
|
||||
e := testAddrs[0]
|
||||
c.add(e.addr, e.linkAddr)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
_, _, err := c.get(e.addr, "", nil)
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||
t.Errorf("got c.get(%s, '', nil) = %s, want = ErrWouldBlock", e.addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheReplace(t *testing.T) {
|
||||
var c linkAddrCache
|
||||
c.init(newEmptyNIC(), 1<<63-1, 1*time.Second, 3, nil)
|
||||
e := testAddrs[0]
|
||||
l2 := e.linkAddr + "2"
|
||||
c.add(e.addr, e.linkAddr)
|
||||
got, _, err := c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("c.get(%s, '', nil): %s", e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("got c.get(%s, '', nil) = %s, want = %s", e.addr, got, e.linkAddr)
|
||||
}
|
||||
|
||||
c.add(e.addr, l2)
|
||||
got, _, err = c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("c.get(%s, '', nil): %s", e.addr, err)
|
||||
}
|
||||
if got != l2 {
|
||||
t.Errorf("got c.get(%s, '', nil) = %s, want = %s", e.addr, got, l2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheResolution(t *testing.T) {
|
||||
// There is a race condition causing this test to fail when the executor
|
||||
// takes longer than the resolution timeout to call linkAddrCache.get. This
|
||||
// is especially common when this test is run with gotsan.
|
||||
//
|
||||
// Using a large resolution timeout decreases the probability of experiencing
|
||||
// this race condition and does not affect how long this test takes to run.
|
||||
var c linkAddrCache
|
||||
linkRes := &testLinkAddressResolver{cache: &c}
|
||||
c.init(newEmptyNIC(), 1<<63-1, math.MaxInt64, 1, linkRes)
|
||||
for i, ta := range testAddrs {
|
||||
got, err := getBlocking(&c, ta.addr)
|
||||
if err != nil {
|
||||
t.Errorf("check %d, getBlocking(_, %s): %s", i, ta.addr, err)
|
||||
}
|
||||
if got != ta.linkAddr {
|
||||
t.Errorf("check %d, got getBlocking(_, %s) = %s, want = %s", i, ta.addr, got, ta.linkAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that after resolved, address stays in the cache and never returns WouldBlock.
|
||||
for i := 0; i < 10; i++ {
|
||||
e := testAddrs[len(testAddrs)-1]
|
||||
got, _, err := c.get(e.addr, "", nil)
|
||||
if err != nil {
|
||||
t.Errorf("c.get(%s, '', nil): %s", e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("got c.get(%s, '', nil) = %s, want = %s", e.addr, got, e.linkAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheResolutionFailed(t *testing.T) {
|
||||
var c linkAddrCache
|
||||
linkRes := &testLinkAddressResolver{cache: &c}
|
||||
c.init(newEmptyNIC(), 1<<63-1, 10*time.Millisecond, 5, linkRes)
|
||||
|
||||
var requestCount uint32
|
||||
linkRes.onLinkAddressRequest = func() {
|
||||
atomic.AddUint32(&requestCount, 1)
|
||||
}
|
||||
|
||||
// First, sanity check that resolution is working...
|
||||
e := testAddrs[0]
|
||||
got, err := getBlocking(&c, e.addr)
|
||||
if err != nil {
|
||||
t.Errorf("getBlocking(_, %s): %s", e.addr, err)
|
||||
}
|
||||
if got != e.linkAddr {
|
||||
t.Errorf("got getBlocking(_, %s) = %s, want = %s", e.addr, got, e.linkAddr)
|
||||
}
|
||||
|
||||
before := atomic.LoadUint32(&requestCount)
|
||||
|
||||
e.addr += "2"
|
||||
a, err := getBlocking(&c, e.addr)
|
||||
if _, ok := err.(*tcpip.ErrTimeout); !ok {
|
||||
t.Errorf("got getBlocking(_, %s) = (%s, %s), want = (_, %s)", e.addr, a, err, &tcpip.ErrTimeout{})
|
||||
}
|
||||
|
||||
if got, want := int(atomic.LoadUint32(&requestCount)-before), c.resolutionAttempts; got != want {
|
||||
t.Errorf("got link address request count = %d, want = %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheResolutionTimeout(t *testing.T) {
|
||||
resolverDelay := 500 * time.Millisecond
|
||||
expiration := resolverDelay / 10
|
||||
var c linkAddrCache
|
||||
linkRes := &testLinkAddressResolver{cache: &c, delay: resolverDelay}
|
||||
c.init(newEmptyNIC(), expiration, 1*time.Millisecond, 3, linkRes)
|
||||
|
||||
e := testAddrs[0]
|
||||
a, err := getBlocking(&c, e.addr)
|
||||
if _, ok := err.(*tcpip.ErrTimeout); !ok {
|
||||
t.Errorf("got getBlocking(_, %s) = (%s, %s), want = (_, %s)", e.addr, a, err, &tcpip.ErrTimeout{})
|
||||
}
|
||||
}
|
||||
+380
-435
File diff suppressed because it is too large
Load Diff
@@ -266,30 +266,6 @@ func (n *neighborCache) setConfig(config NUDConfigurations) {
|
||||
n.state.SetConfig(config)
|
||||
}
|
||||
|
||||
var _ neighborTable = (*neighborCache)(nil)
|
||||
|
||||
func (n *neighborCache) neighbors() ([]NeighborEntry, tcpip.Error) {
|
||||
return n.entries(), nil
|
||||
}
|
||||
|
||||
func (n *neighborCache) get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
|
||||
entry, ch, err := n.entry(addr, localAddr, onResolve)
|
||||
return entry.LinkAddr, ch, err
|
||||
}
|
||||
|
||||
func (n *neighborCache) remove(addr tcpip.Address) tcpip.Error {
|
||||
if !n.removeEntry(addr) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *neighborCache) removeAll() tcpip.Error {
|
||||
n.clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleProbe handles a neighbor probe as defined by RFC 4861 section 7.2.3.
|
||||
//
|
||||
// Validation of the probe is expected to be handled by the caller.
|
||||
@@ -331,17 +307,8 @@ func (n *neighborCache) handleUpperLevelConfirmation(addr tcpip.Address) {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *neighborCache) nudConfig() (NUDConfigurations, tcpip.Error) {
|
||||
return n.config(), nil
|
||||
}
|
||||
|
||||
func (n *neighborCache) setNUDConfig(c NUDConfigurations) tcpip.Error {
|
||||
n.setConfig(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNeighborCache(nic *nic, r LinkAddressResolver) *neighborCache {
|
||||
n := &neighborCache{
|
||||
func (n *neighborCache) init(nic *nic, r LinkAddressResolver) {
|
||||
*n = neighborCache{
|
||||
nic: nic,
|
||||
state: NewNUDState(nic.stack.nudConfigs, nic.stack.randomGenerator),
|
||||
linkRes: r,
|
||||
@@ -349,5 +316,4 @@ func newNeighborCache(nic *nic, r LinkAddressResolver) *neighborCache {
|
||||
n.mu.Lock()
|
||||
n.mu.cache = make(map[tcpip.Address]*neighborEntry, neighborCacheSize)
|
||||
n.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func newTestNeighborResolver(nudDisp NUDDispatcher, config NUDConfigurations, cl
|
||||
entries: newTestEntryStore(),
|
||||
delay: typicalLatency,
|
||||
}
|
||||
linkRes.neigh = newNeighborCache(&nic{
|
||||
linkRes.neigh.init(&nic{
|
||||
stack: &Stack{
|
||||
clock: clock,
|
||||
nudDisp: nudDisp,
|
||||
@@ -187,7 +187,7 @@ func (s *testEntryStore) set(i int, linkAddr tcpip.LinkAddress) {
|
||||
// neighbor probe.
|
||||
type testNeighborResolver struct {
|
||||
clock tcpip.Clock
|
||||
neigh *neighborCache
|
||||
neigh neighborCache
|
||||
entries *testEntryStore
|
||||
delay time.Duration
|
||||
onLinkAddressRequest func()
|
||||
|
||||
@@ -239,16 +239,16 @@ func entryTestSetup(c NUDConfigurations) (*neighborEntry, *testNUDDispatcher, *e
|
||||
|
||||
var linkRes entryTestLinkResolver
|
||||
// Stub out the neighbor cache to verify deletion from the cache.
|
||||
neigh := newNeighborCache(&nic, &linkRes)
|
||||
l := linkResolver{
|
||||
resolver: &linkRes,
|
||||
neighborTable: neigh,
|
||||
l := &linkResolver{
|
||||
resolver: &linkRes,
|
||||
}
|
||||
entry := newNeighborEntry(neigh, entryTestAddr1 /* remoteAddr */, neigh.state)
|
||||
neigh.mu.Lock()
|
||||
neigh.mu.cache[entryTestAddr1] = entry
|
||||
neigh.mu.Unlock()
|
||||
nic.linkAddrResolvers = map[tcpip.NetworkProtocolNumber]linkResolver{
|
||||
l.neigh.init(&nic, &linkRes)
|
||||
|
||||
entry := newNeighborEntry(&l.neigh, entryTestAddr1 /* remoteAddr */, l.neigh.state)
|
||||
l.neigh.mu.Lock()
|
||||
l.neigh.mu.cache[entryTestAddr1] = entry
|
||||
l.neigh.mu.Unlock()
|
||||
nic.linkAddrResolvers = map[tcpip.NetworkProtocolNumber]*linkResolver{
|
||||
header.IPv6ProtocolNumber: l,
|
||||
}
|
||||
|
||||
|
||||
+23
-41
@@ -24,37 +24,23 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
type neighborTable interface {
|
||||
neighbors() ([]NeighborEntry, tcpip.Error)
|
||||
addStaticEntry(tcpip.Address, tcpip.LinkAddress)
|
||||
get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error)
|
||||
remove(tcpip.Address) tcpip.Error
|
||||
removeAll() tcpip.Error
|
||||
|
||||
handleProbe(tcpip.Address, tcpip.LinkAddress)
|
||||
handleConfirmation(tcpip.Address, tcpip.LinkAddress, ReachabilityConfirmationFlags)
|
||||
handleUpperLevelConfirmation(tcpip.Address)
|
||||
|
||||
nudConfig() (NUDConfigurations, tcpip.Error)
|
||||
setNUDConfig(NUDConfigurations) tcpip.Error
|
||||
}
|
||||
|
||||
var _ NetworkInterface = (*nic)(nil)
|
||||
|
||||
type linkResolver struct {
|
||||
resolver LinkAddressResolver
|
||||
|
||||
neighborTable neighborTable
|
||||
neigh neighborCache
|
||||
}
|
||||
|
||||
func (l *linkResolver) getNeighborLinkAddress(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
|
||||
return l.neighborTable.get(addr, localAddr, onResolve)
|
||||
entry, ch, err := l.neigh.entry(addr, localAddr, onResolve)
|
||||
return entry.LinkAddr, ch, err
|
||||
}
|
||||
|
||||
func (l *linkResolver) confirmReachable(addr tcpip.Address) {
|
||||
l.neighborTable.handleUpperLevelConfirmation(addr)
|
||||
l.neigh.handleUpperLevelConfirmation(addr)
|
||||
}
|
||||
|
||||
var _ NetworkInterface = (*nic)(nil)
|
||||
|
||||
// nic represents a "network interface card" to which the networking stack is
|
||||
// attached.
|
||||
type nic struct {
|
||||
@@ -70,7 +56,7 @@ type nic struct {
|
||||
// The network endpoints themselves may be modified by calling the interface's
|
||||
// methods, but the map reference and entries must be constant.
|
||||
networkEndpoints map[tcpip.NetworkProtocolNumber]NetworkEndpoint
|
||||
linkAddrResolvers map[tcpip.NetworkProtocolNumber]linkResolver
|
||||
linkAddrResolvers map[tcpip.NetworkProtocolNumber]*linkResolver
|
||||
|
||||
// enabled is set to 1 when the NIC is enabled and 0 when it is disabled.
|
||||
//
|
||||
@@ -165,7 +151,7 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
|
||||
context: ctx,
|
||||
stats: makeNICStats(),
|
||||
networkEndpoints: make(map[tcpip.NetworkProtocolNumber]NetworkEndpoint),
|
||||
linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]linkResolver),
|
||||
linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]*linkResolver),
|
||||
}
|
||||
nic.linkResQueue.init(nic)
|
||||
nic.mu.packetEPs = make(map[tcpip.NetworkProtocolNumber]*packetEndpointList)
|
||||
@@ -185,17 +171,8 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
|
||||
|
||||
if resolutionRequired {
|
||||
if r, ok := netEP.(LinkAddressResolver); ok {
|
||||
l := linkResolver{
|
||||
resolver: r,
|
||||
}
|
||||
|
||||
if stack.useNeighborCache {
|
||||
l.neighborTable = newNeighborCache(nic, r)
|
||||
} else {
|
||||
cache := new(linkAddrCache)
|
||||
cache.init(nic, ageLimit, resolutionTimeout, resolutionAttempts, r)
|
||||
l.neighborTable = cache
|
||||
}
|
||||
l := &linkResolver{resolver: r}
|
||||
l.neigh.init(nic, r)
|
||||
nic.linkAddrResolvers[r.LinkAddressProtocol()] = l
|
||||
}
|
||||
}
|
||||
@@ -640,7 +617,7 @@ func (n *nic) getLinkAddress(addr, localAddr tcpip.Address, protocol tcpip.Netwo
|
||||
|
||||
func (n *nic) neighbors(protocol tcpip.NetworkProtocolNumber) ([]NeighborEntry, tcpip.Error) {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
return linkRes.neighborTable.neighbors()
|
||||
return linkRes.neigh.entries(), nil
|
||||
}
|
||||
|
||||
return nil, &tcpip.ErrNotSupported{}
|
||||
@@ -648,7 +625,7 @@ func (n *nic) neighbors(protocol tcpip.NetworkProtocolNumber) ([]NeighborEntry,
|
||||
|
||||
func (n *nic) addStaticNeighbor(addr tcpip.Address, protocol tcpip.NetworkProtocolNumber, linkAddress tcpip.LinkAddress) tcpip.Error {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
linkRes.neighborTable.addStaticEntry(addr, linkAddress)
|
||||
linkRes.neigh.addStaticEntry(addr, linkAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -657,7 +634,10 @@ func (n *nic) addStaticNeighbor(addr tcpip.Address, protocol tcpip.NetworkProtoc
|
||||
|
||||
func (n *nic) removeNeighbor(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) tcpip.Error {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
return linkRes.neighborTable.remove(addr)
|
||||
if !linkRes.neigh.removeEntry(addr) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tcpip.ErrNotSupported{}
|
||||
@@ -665,7 +645,8 @@ func (n *nic) removeNeighbor(protocol tcpip.NetworkProtocolNumber, addr tcpip.Ad
|
||||
|
||||
func (n *nic) clearNeighbors(protocol tcpip.NetworkProtocolNumber) tcpip.Error {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
return linkRes.neighborTable.removeAll()
|
||||
linkRes.neigh.clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tcpip.ErrNotSupported{}
|
||||
@@ -923,7 +904,7 @@ func (n *nic) Name() string {
|
||||
// nudConfigs gets the NUD configurations for n.
|
||||
func (n *nic) nudConfigs(protocol tcpip.NetworkProtocolNumber) (NUDConfigurations, tcpip.Error) {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
return linkRes.neighborTable.nudConfig()
|
||||
return linkRes.neigh.config(), nil
|
||||
}
|
||||
|
||||
return NUDConfigurations{}, &tcpip.ErrNotSupported{}
|
||||
@@ -936,7 +917,8 @@ func (n *nic) nudConfigs(protocol tcpip.NetworkProtocolNumber) (NUDConfiguration
|
||||
func (n *nic) setNUDConfigs(protocol tcpip.NetworkProtocolNumber, c NUDConfigurations) tcpip.Error {
|
||||
if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
c.resetInvalidFields()
|
||||
return linkRes.neighborTable.setNUDConfig(c)
|
||||
linkRes.neigh.setConfig(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tcpip.ErrNotSupported{}
|
||||
@@ -979,7 +961,7 @@ func (n *nic) isValidForOutgoing(ep AssignableAddressEndpoint) bool {
|
||||
// HandleNeighborProbe implements NetworkInterface.
|
||||
func (n *nic) HandleNeighborProbe(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, linkAddr tcpip.LinkAddress) tcpip.Error {
|
||||
if l, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
l.neighborTable.handleProbe(addr, linkAddr)
|
||||
l.neigh.handleProbe(addr, linkAddr)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -989,7 +971,7 @@ func (n *nic) HandleNeighborProbe(protocol tcpip.NetworkProtocolNumber, addr tcp
|
||||
// HandleNeighborConfirmation implements NetworkInterface.
|
||||
func (n *nic) HandleNeighborConfirmation(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) tcpip.Error {
|
||||
if l, ok := n.linkAddrResolvers[protocol]; ok {
|
||||
l.neighborTable.handleConfirmation(addr, linkAddr, flags)
|
||||
l.neigh.handleConfirmation(addr, linkAddr, flags)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@ func TestNUDFunctions(t *testing.T) {
|
||||
clock := faketime.NewManualClock()
|
||||
s := stack.New(stack.Options{
|
||||
NUDConfigs: stack.DefaultNUDConfigurations(),
|
||||
UseNeighborCache: true,
|
||||
NetworkProtocols: test.netProtoFactory,
|
||||
Clock: clock,
|
||||
})
|
||||
@@ -206,7 +205,6 @@ func TestDefaultNUDConfigurations(t *testing.T) {
|
||||
// address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: stack.DefaultNUDConfigurations(),
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -261,7 +259,6 @@ func TestNUDConfigurationsBaseReachableTime(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -318,7 +315,6 @@ func TestNUDConfigurationsMinRandomFactor(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -398,7 +394,6 @@ func TestNUDConfigurationsMaxRandomFactor(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -460,7 +455,6 @@ func TestNUDConfigurationsRetransmitTimer(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -512,7 +506,6 @@ func TestNUDConfigurationsDelayFirstProbeTime(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -564,7 +557,6 @@ func TestNUDConfigurationsMaxMulticastProbes(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -616,7 +608,6 @@ func TestNUDConfigurationsMaxUnicastProbes(t *testing.T) {
|
||||
// providing link address resolution is specified (e.g. ARP or IPv6).
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol},
|
||||
NUDConfigs: c,
|
||||
UseNeighborCache: true,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
|
||||
@@ -53,7 +53,7 @@ type Route struct {
|
||||
|
||||
// linkRes is set if link address resolution is enabled for this protocol on
|
||||
// the route's NIC.
|
||||
linkRes linkResolver
|
||||
linkRes *linkResolver
|
||||
}
|
||||
|
||||
type routeInfo struct {
|
||||
@@ -184,7 +184,7 @@ func makeRoute(netProto tcpip.NetworkProtocolNumber, gateway, localAddr, remoteA
|
||||
return r
|
||||
}
|
||||
|
||||
if r.linkRes.resolver == nil {
|
||||
if r.linkRes == nil {
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ func (r *Route) IsResolutionRequired() bool {
|
||||
}
|
||||
|
||||
func (r *Route) isResolutionRequiredRLocked() bool {
|
||||
return len(r.mu.remoteLinkAddress) == 0 && r.linkRes.resolver != nil && r.isValidForOutgoingRLocked() && !r.local()
|
||||
return len(r.mu.remoteLinkAddress) == 0 && r.linkRes != nil && r.isValidForOutgoingRLocked() && !r.local()
|
||||
}
|
||||
|
||||
func (r *Route) isValidForOutgoing() bool {
|
||||
@@ -528,7 +528,7 @@ func (r *Route) IsOutboundBroadcast() bool {
|
||||
// "Reachable" is defined as having full-duplex communication between the
|
||||
// local and remote ends of the route.
|
||||
func (r *Route) ConfirmReachable() {
|
||||
if r.linkRes.resolver != nil {
|
||||
if r.linkRes != nil {
|
||||
r.linkRes.confirmReachable(r.nextHop())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,12 +434,6 @@ type Stack struct {
|
||||
// nudConfigs is the default NUD configurations used by interfaces.
|
||||
nudConfigs NUDConfigurations
|
||||
|
||||
// useNeighborCache indicates whether ARP and NDP packets should be handled
|
||||
// by the NIC's neighborCache instead of linkAddrCache.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4658): Remove this field.
|
||||
useNeighborCache bool
|
||||
|
||||
// nudDisp is the NUD event dispatcher that is used to send the netstack
|
||||
// integrator NUD related events.
|
||||
nudDisp NUDDispatcher
|
||||
@@ -516,17 +510,6 @@ type Options struct {
|
||||
// NUDConfigs is the default NUD configurations used by interfaces.
|
||||
NUDConfigs NUDConfigurations
|
||||
|
||||
// UseNeighborCache is unused.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4658): Remove this field.
|
||||
UseNeighborCache bool
|
||||
|
||||
// UseLinkAddrCache indicates that the legacy link address cache should be
|
||||
// used for link resolution.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4658): Remove this field.
|
||||
UseLinkAddrCache bool
|
||||
|
||||
// NUDDisp is the NUD event dispatcher that an integrator can provide to
|
||||
// receive NUD related events.
|
||||
NUDDisp NUDDispatcher
|
||||
@@ -666,7 +649,6 @@ func New(opts Options) *Stack {
|
||||
icmpRateLimiter: NewICMPRateLimiter(),
|
||||
seed: generateRandUint32(),
|
||||
nudConfigs: opts.NUDConfigs,
|
||||
useNeighborCache: !opts.UseLinkAddrCache,
|
||||
uniqueIDGenerator: opts.UniqueID,
|
||||
nudDisp: opts.NUDDisp,
|
||||
randomGenerator: mathrand.New(randSrc),
|
||||
|
||||
@@ -4324,7 +4324,6 @@ func TestClearNeighborCacheOnNICDisable(t *testing.T) {
|
||||
clock := faketime.NewManualClock()
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
UseNeighborCache: true,
|
||||
Clock: clock,
|
||||
})
|
||||
e := channel.New(0, 0, "")
|
||||
|
||||
@@ -487,30 +487,25 @@ func TestGetLinkAddress(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, useNeighborCache := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("UseNeighborCache=%t", useNeighborCache), func(t *testing.T) {
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
UseNeighborCache: useNeighborCache,
|
||||
}
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
}
|
||||
|
||||
host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID)
|
||||
host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID)
|
||||
|
||||
ch := make(chan stack.LinkResolutionResult, 1)
|
||||
err := host1Stack.GetLinkAddress(host1NICID, test.remoteAddr, "", test.netProto, func(r stack.LinkResolutionResult) {
|
||||
ch <- r
|
||||
})
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||
t.Fatalf("got host1Stack.GetLinkAddress(%d, %s, '', %d, _) = %s, want = %s", host1NICID, test.remoteAddr, test.netProto, err, &tcpip.ErrWouldBlock{})
|
||||
}
|
||||
wantRes := stack.LinkResolutionResult{Success: test.expectedOk}
|
||||
if test.expectedOk {
|
||||
wantRes.LinkAddress = linkAddr2
|
||||
}
|
||||
if diff := cmp.Diff(wantRes, <-ch); diff != "" {
|
||||
t.Fatalf("link resolution result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
ch := make(chan stack.LinkResolutionResult, 1)
|
||||
err := host1Stack.GetLinkAddress(host1NICID, test.remoteAddr, "", test.netProto, func(r stack.LinkResolutionResult) {
|
||||
ch <- r
|
||||
})
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||
t.Fatalf("got host1Stack.GetLinkAddress(%d, %s, '', %d, _) = %s, want = %s", host1NICID, test.remoteAddr, test.netProto, err, &tcpip.ErrWouldBlock{})
|
||||
}
|
||||
wantRes := stack.LinkResolutionResult{Success: test.expectedOk}
|
||||
if test.expectedOk {
|
||||
wantRes.LinkAddress = linkAddr2
|
||||
}
|
||||
if diff := cmp.Diff(wantRes, <-ch); diff != "" {
|
||||
t.Fatalf("link resolution result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -587,66 +582,61 @@ func TestRouteResolvedFields(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, useNeighborCache := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("UseNeighborCache=%t", useNeighborCache), func(t *testing.T) {
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
UseNeighborCache: useNeighborCache,
|
||||
}
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
}
|
||||
|
||||
host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID)
|
||||
r, err := host1Stack.FindRoute(host1NICID, "", test.remoteAddr, test.netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("host1Stack.FindRoute(%d, '', %s, %d, false): %s", host1NICID, test.remoteAddr, test.netProto, err)
|
||||
}
|
||||
defer r.Release()
|
||||
host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID)
|
||||
r, err := host1Stack.FindRoute(host1NICID, "", test.remoteAddr, test.netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("host1Stack.FindRoute(%d, '', %s, %d, false): %s", host1NICID, test.remoteAddr, test.netProto, err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
var wantRouteInfo stack.RouteInfo
|
||||
wantRouteInfo.LocalLinkAddress = linkAddr1
|
||||
wantRouteInfo.LocalAddress = test.localAddr
|
||||
wantRouteInfo.RemoteAddress = test.remoteAddr
|
||||
wantRouteInfo.NetProto = test.netProto
|
||||
wantRouteInfo.Loop = stack.PacketOut
|
||||
wantRouteInfo.RemoteLinkAddress = test.expectedLinkAddr
|
||||
var wantRouteInfo stack.RouteInfo
|
||||
wantRouteInfo.LocalLinkAddress = linkAddr1
|
||||
wantRouteInfo.LocalAddress = test.localAddr
|
||||
wantRouteInfo.RemoteAddress = test.remoteAddr
|
||||
wantRouteInfo.NetProto = test.netProto
|
||||
wantRouteInfo.Loop = stack.PacketOut
|
||||
wantRouteInfo.RemoteLinkAddress = test.expectedLinkAddr
|
||||
|
||||
ch := make(chan stack.ResolvedFieldsResult, 1)
|
||||
ch := make(chan stack.ResolvedFieldsResult, 1)
|
||||
|
||||
if !test.immediatelyResolvable {
|
||||
wantUnresolvedRouteInfo := wantRouteInfo
|
||||
wantUnresolvedRouteInfo.RemoteLinkAddress = ""
|
||||
if !test.immediatelyResolvable {
|
||||
wantUnresolvedRouteInfo := wantRouteInfo
|
||||
wantUnresolvedRouteInfo.RemoteLinkAddress = ""
|
||||
|
||||
err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
})
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||
t.Errorf("got r.ResolvedFields(_) = %s, want = %s", err, &tcpip.ErrWouldBlock{})
|
||||
}
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: test.expectedSuccess}, <-ch, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if !test.expectedSuccess {
|
||||
return
|
||||
}
|
||||
|
||||
// At this point the neighbor table should be populated so the route
|
||||
// should be immediately resolvable.
|
||||
}
|
||||
|
||||
if err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
}); err != nil {
|
||||
t.Errorf("r.ResolvedFields(_): %s", err)
|
||||
}
|
||||
select {
|
||||
case routeResolveRes := <-ch:
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: true}, routeResolveRes, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result from resolved route mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected route to be immediately resolvable")
|
||||
}
|
||||
err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
})
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||
t.Errorf("got r.ResolvedFields(_) = %s, want = %s", err, &tcpip.ErrWouldBlock{})
|
||||
}
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: test.expectedSuccess}, <-ch, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if !test.expectedSuccess {
|
||||
return
|
||||
}
|
||||
|
||||
// At this point the neighbor table should be populated so the route
|
||||
// should be immediately resolvable.
|
||||
}
|
||||
|
||||
if err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
}); err != nil {
|
||||
t.Errorf("r.ResolvedFields(_): %s", err)
|
||||
}
|
||||
select {
|
||||
case routeResolveRes := <-ch:
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: true}, routeResolveRes, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result from resolved route mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected route to be immediately resolvable")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1065,7 +1055,6 @@ func TestTCPConfirmNeighborReachability(t *testing.T) {
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
|
||||
Clock: clock,
|
||||
UseNeighborCache: true,
|
||||
}
|
||||
host1StackOpts := stackOpts
|
||||
host1StackOpts.NUDDisp = &nudDisp
|
||||
|
||||
Reference in New Issue
Block a user