diff options
author | Ghanan Gowripalan <ghanan@google.com> | 2021-02-08 19:03:54 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-08 19:05:45 -0800 |
commit | 39251f31cb92d6c2b053416d04e195e290b106f2 (patch) | |
tree | bf3c80dc631655f48fc0b9686cfe2af2e6a4ab74 /pkg/tcpip/tests | |
parent | cfa4633c3d206aa2f9abdaac60d053162244ee6d (diff) |
Support performing DAD for any address
...as long as the network protocol supports duplicate address detection.
This CL provides the facilities for a netstack integrator to perform
DAD.
DHCP recommends that clients effectively perform DAD before accepting an
offer. As per RFC 2131 section 4.4.1 pg 38,
The client SHOULD perform a check on the suggested address to ensure
that the address is not already in use. For example, if the client
is on a network that supports ARP, the client may issue an ARP request
for the suggested request.
The implementation of ARP-based IPv4 DAD effectively operates the same
as IPv6's NDP DAD - using ARP requests and responses in place of
NDP neighbour solicitations and advertisements, respectively.
DAD performed by calls to (*Stack).CheckDuplicateAddress don't interfere
with DAD performed when a new IPv6 address is added. This is so that
integrator requests to check for duplicate addresses aren't unexpectedly
aborted when addresses are removed.
A network package internal package provides protocol agnostic DAD state
management that specific protocols that provide DAD can use.
Fixes #4550.
Tests:
- internal/ip_test.*
- integration_test.TestDAD
- arp_test.TestDADARPRequestPacket
- ipv6.TestCheckDuplicateAddress
PiperOrigin-RevId: 356405593
Diffstat (limited to 'pkg/tcpip/tests')
-rw-r--r-- | pkg/tcpip/tests/integration/link_resolution_test.go | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/pkg/tcpip/tests/integration/link_resolution_test.go b/pkg/tcpip/tests/integration/link_resolution_test.go index 553ec950f..824f81a42 100644 --- a/pkg/tcpip/tests/integration/link_resolution_test.go +++ b/pkg/tcpip/tests/integration/link_resolution_test.go @@ -1199,3 +1199,148 @@ func TestTCPConfirmNeighborReachability(t *testing.T) { }) } } + +func TestDAD(t *testing.T) { + const ( + host1NICID = 1 + host2NICID = 4 + ) + + dadConfigs := stack.DADConfigurations{ + DupAddrDetectTransmits: 1, + RetransmitTimer: time.Second, + } + + tests := []struct { + name string + netProto tcpip.NetworkProtocolNumber + dadNetProto tcpip.NetworkProtocolNumber + remoteAddr tcpip.Address + expectedResolved bool + }{ + { + name: "IPv4 own address", + netProto: ipv4.ProtocolNumber, + dadNetProto: arp.ProtocolNumber, + remoteAddr: ipv4Addr1.AddressWithPrefix.Address, + expectedResolved: true, + }, + { + name: "IPv6 own address", + netProto: ipv6.ProtocolNumber, + dadNetProto: ipv6.ProtocolNumber, + remoteAddr: ipv6Addr1.AddressWithPrefix.Address, + expectedResolved: true, + }, + { + name: "IPv4 duplicate address", + netProto: ipv4.ProtocolNumber, + dadNetProto: arp.ProtocolNumber, + remoteAddr: ipv4Addr2.AddressWithPrefix.Address, + expectedResolved: false, + }, + { + name: "IPv6 duplicate address", + netProto: ipv6.ProtocolNumber, + dadNetProto: ipv6.ProtocolNumber, + remoteAddr: ipv6Addr2.AddressWithPrefix.Address, + expectedResolved: false, + }, + { + name: "IPv4 no duplicate address", + netProto: ipv4.ProtocolNumber, + dadNetProto: arp.ProtocolNumber, + remoteAddr: ipv4Addr3.AddressWithPrefix.Address, + expectedResolved: true, + }, + { + name: "IPv6 no duplicate address", + netProto: ipv6.ProtocolNumber, + dadNetProto: ipv6.ProtocolNumber, + remoteAddr: ipv6Addr3.AddressWithPrefix.Address, + expectedResolved: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + clock := faketime.NewManualClock() + stackOpts := stack.Options{ + Clock: clock, + NetworkProtocols: []stack.NetworkProtocolFactory{ + arp.NewProtocol, + ipv4.NewProtocol, + ipv6.NewProtocol, + }, + } + + host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID) + + // DAD should be disabled by default. + if res, err := host1Stack.CheckDuplicateAddress(host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { + t.Errorf("unexpectedly called DAD completion handler when DAD was supposed to be disabled") + }); err != nil { + t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", host1NICID, test.netProto, test.remoteAddr, err) + } else if res != stack.DADDisabled { + t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", host1NICID, test.netProto, test.remoteAddr, res, stack.DADDisabled) + } + + // Enable DAD then attempt to check if an address is duplicated. + netEP, err := host1Stack.GetNetworkEndpoint(host1NICID, test.dadNetProto) + if err != nil { + t.Fatalf("host1Stack.GetNetworkEndpoint(%d, %d): %s", host1NICID, test.dadNetProto, err) + } + dad, ok := netEP.(stack.DuplicateAddressDetector) + if !ok { + t.Fatalf("expected %T to implement stack.DuplicateAddressDetector", netEP) + } + dad.SetDADConfigurations(dadConfigs) + ch := make(chan stack.DADResult, 3) + if res, err := host1Stack.CheckDuplicateAddress(host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { + ch <- r + }); err != nil { + t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", host1NICID, test.netProto, test.remoteAddr, err) + } else if res != stack.DADStarting { + t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", host1NICID, test.netProto, test.remoteAddr, res, stack.DADStarting) + } + + expectResults := 1 + if test.expectedResolved { + const delta = time.Nanosecond + clock.Advance(time.Duration(dadConfigs.DupAddrDetectTransmits)*dadConfigs.RetransmitTimer - delta) + select { + case r := <-ch: + t.Fatalf("unexpectedly got DAD result before the DAD timeout; r = %#v", r) + default: + } + + // If we expect the resolve to succeed try requesting DAD again on the + // same address. The handler for the new request should be called once + // the original DAD request completes. + expectResults = 2 + if res, err := host1Stack.CheckDuplicateAddress(host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { + ch <- r + }); err != nil { + t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", host1NICID, test.netProto, test.remoteAddr, err) + } else if res != stack.DADAlreadyRunning { + t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", host1NICID, test.netProto, test.remoteAddr, res, stack.DADAlreadyRunning) + } + + clock.Advance(delta) + } + + for i := 0; i < expectResults; i++ { + if diff := cmp.Diff(stack.DADResult{Resolved: test.expectedResolved}, <-ch); diff != "" { + t.Errorf("(i=%d) DAD result mismatch (-want +got):\n%s", i, diff) + } + } + + // Should have no more results. + select { + case r := <-ch: + t.Errorf("unexpectedly got an extra DAD result; r = %#v", r) + default: + } + }) + } +} |