diff options
Diffstat (limited to 'pkg/tcpip/stack')
-rw-r--r-- | pkg/tcpip/stack/nic.go | 10 | ||||
-rw-r--r-- | pkg/tcpip/stack/stack_test.go | 52 |
2 files changed, 62 insertions, 0 deletions
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index 4a34805b5..8a946b4fa 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -217,6 +217,16 @@ func (n *NIC) disableLocked() { ep.Disable() } + // Clear the neighbour table (including static entries) as we cannot guarantee + // that the current neighbour table will be valid when the NIC is enabled + // again. + // + // This matches linux's behaviour at the time of writing: + // https://github.com/torvalds/linux/blob/71c061d2443814de15e177489d5cc00a4a253ef3/net/core/neighbour.c#L371 + if err := n.clearNeighbors(); err != nil && err != tcpip.ErrNotSupported { + panic(fmt.Sprintf("n.clearNeighbors(): %s", err)) + } + if !n.setEnabled(false) { panic("should have only done work to disable the NIC if it was enabled") } diff --git a/pkg/tcpip/stack/stack_test.go b/pkg/tcpip/stack/stack_test.go index 856ebf6d4..4a3f937e3 100644 --- a/pkg/tcpip/stack/stack_test.go +++ b/pkg/tcpip/stack/stack_test.go @@ -4305,3 +4305,55 @@ func TestWritePacketToRemote(t *testing.T) { } }) } + +func TestClearNeighborCacheOnNICDisable(t *testing.T) { + const ( + nicID = 1 + + ipv4Addr = tcpip.Address("\x01\x02\x03\x04") + ipv6Addr = tcpip.Address("\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04") + linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06") + ) + + s := stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, + UseNeighborCache: true, + }) + e := channel.New(0, 0, "") + e.LinkEPCapabilities |= stack.CapabilityResolutionRequired + if err := s.CreateNIC(nicID, e); err != nil { + t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) + } + + if err := s.AddStaticNeighbor(nicID, ipv4Addr, linkAddr); err != nil { + t.Fatalf("s.AddStaticNeighbor(%d, %s, %s): %s", nicID, ipv4Addr, linkAddr, err) + } + if err := s.AddStaticNeighbor(nicID, ipv6Addr, linkAddr); err != nil { + t.Fatalf("s.AddStaticNeighbor(%d, %s, %s): %s", nicID, ipv6Addr, linkAddr, err) + } + if neighbors, err := s.Neighbors(nicID); err != nil { + t.Fatalf("s.Neighbors(%d): %s", nicID, err) + } else if len(neighbors) != 2 { + t.Fatalf("got len(neighbors) = %d, want = 2; neighbors = %#v", len(neighbors), neighbors) + } + + // Disabling the NIC should clear the neighbor table. + if err := s.DisableNIC(nicID); err != nil { + t.Fatalf("s.DisableNIC(%d): %s", nicID, err) + } + if neighbors, err := s.Neighbors(nicID); err != nil { + t.Fatalf("s.Neighbors(%d): %s", nicID, err) + } else if len(neighbors) != 0 { + t.Fatalf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors) + } + + // Enabling the NIC should have an empty neighbor table. + if err := s.EnableNIC(nicID); err != nil { + t.Fatalf("s.EnableNIC(%d): %s", nicID, err) + } + if neighbors, err := s.Neighbors(nicID); err != nil { + t.Fatalf("s.Neighbors(%d): %s", nicID, err) + } else if len(neighbors) != 0 { + t.Fatalf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors) + } +} |