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/network/arp | |
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/network/arp')
-rw-r--r-- | pkg/tcpip/network/arp/BUILD | 2 | ||||
-rw-r--r-- | pkg/tcpip/network/arp/arp.go | 77 | ||||
-rw-r--r-- | pkg/tcpip/network/arp/arp_test.go | 50 |
3 files changed, 123 insertions, 6 deletions
diff --git a/pkg/tcpip/network/arp/BUILD b/pkg/tcpip/network/arp/BUILD index 933845269..29c8cdffd 100644 --- a/pkg/tcpip/network/arp/BUILD +++ b/pkg/tcpip/network/arp/BUILD @@ -10,10 +10,12 @@ go_library( ], visibility = ["//visibility:public"], deps = [ + "//pkg/sync", "//pkg/tcpip", "//pkg/tcpip/buffer", "//pkg/tcpip/header", "//pkg/tcpip/header/parse", + "//pkg/tcpip/network/internal/ip", "//pkg/tcpip/stack", ], ) diff --git a/pkg/tcpip/network/arp/arp.go b/pkg/tcpip/network/arp/arp.go index 5d7803537..3fcdea119 100644 --- a/pkg/tcpip/network/arp/arp.go +++ b/pkg/tcpip/network/arp/arp.go @@ -22,10 +22,12 @@ import ( "reflect" "sync/atomic" + "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/buffer" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/header/parse" + "gvisor.dev/gvisor/pkg/tcpip/network/internal/ip" "gvisor.dev/gvisor/pkg/tcpip/stack" ) @@ -34,6 +36,7 @@ const ( ProtocolNumber = header.ARPProtocolNumber ) +var _ stack.DuplicateAddressDetector = (*endpoint)(nil) var _ stack.LinkAddressResolver = (*endpoint)(nil) // ARP endpoints need to implement stack.NetworkEndpoint because the stack @@ -52,6 +55,35 @@ type endpoint struct { nic stack.NetworkInterface stats sharedStats + + mu struct { + sync.Mutex + + dad ip.DAD + } +} + +// CheckDuplicateAddress implements stack.DuplicateAddressDetector. +func (e *endpoint) CheckDuplicateAddress(addr tcpip.Address, h stack.DADCompletionHandler) stack.DADCheckAddressDisposition { + e.mu.Lock() + defer e.mu.Unlock() + return e.mu.dad.CheckDuplicateAddressLocked(addr, h) +} + +// SetDADConfigurations implements stack.DuplicateAddressDetector. +func (e *endpoint) SetDADConfigurations(c stack.DADConfigurations) { + e.mu.Lock() + defer e.mu.Unlock() + e.mu.dad.SetConfigsLocked(c) +} + +// DuplicateAddressProtocol implements stack.DuplicateAddressDetector. +func (*endpoint) DuplicateAddressProtocol() tcpip.NetworkProtocolNumber { + return header.IPv4ProtocolNumber +} + +func (e *endpoint) SendDADMessage(addr tcpip.Address) tcpip.Error { + return e.sendARPRequest(header.IPv4Any, addr, header.EthernetBroadcastAddress) } func (e *endpoint) Enable() tcpip.Error { @@ -199,6 +231,10 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) { addr := tcpip.Address(h.ProtocolAddressSender()) linkAddr := tcpip.LinkAddress(h.HardwareAddressSender()) + e.mu.Lock() + e.mu.dad.StopLocked(addr, false /* aborted */) + e.mu.Unlock() + // The solicited, override, and isRouter flags are not available for ARP; // they are only available for IPv6 Neighbor Advertisements. switch err := e.nic.HandleNeighborConfirmation(header.IPv4ProtocolNumber, addr, linkAddr, stack.ReachabilityConfirmationFlags{ @@ -227,9 +263,9 @@ func (e *endpoint) Stats() stack.NetworkEndpointStats { var _ stack.NetworkProtocol = (*protocol)(nil) -// protocol implements stack.NetworkProtocol and stack.LinkAddressResolver. type protocol struct { - stack *stack.Stack + stack *stack.Stack + options Options } func (p *protocol) Number() tcpip.NetworkProtocolNumber { return ProtocolNumber } @@ -246,6 +282,14 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, dispatcher stack.Tran nic: nic, } + e.mu.Lock() + e.mu.dad.Init(&e.mu, p.options.DADConfigs, ip.DADOptions{ + Clock: p.stack.Clock(), + Protocol: e, + NICID: nic.ID(), + }) + e.mu.Unlock() + tcpip.InitStatCounters(reflect.ValueOf(&e.stats.localStats).Elem()) stackStats := p.stack.Stats() @@ -286,8 +330,12 @@ func (e *endpoint) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot return &tcpip.ErrBadLocalAddress{} } + return e.sendARPRequest(localAddr, targetAddr, remoteLinkAddr) +} + +func (e *endpoint) sendARPRequest(localAddr, targetAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress) tcpip.Error { pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(e.nic.MaxHeaderLength()) + header.ARPSize, + ReserveHeaderBytes: int(e.MaxHeaderLength()), }) h := header.ARP(pkt.NetworkHeader().Push(header.ARPSize)) pkt.NetworkProtocolNumber = ProtocolNumber @@ -302,6 +350,8 @@ func (e *endpoint) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot if n := copy(h.ProtocolAddressTarget(), targetAddr); n != header.IPv4AddressSize { panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize)) } + + stats := e.stats.arp if err := e.nic.WritePacketToRemote(remoteLinkAddr, nil /* gso */, ProtocolNumber, pkt); err != nil { stats.outgoingRequestsDropped.Increment() return err @@ -342,9 +392,24 @@ func (*protocol) Parse(pkt *stack.PacketBuffer) (proto tcpip.TransportProtocolNu return 0, false, parse.ARP(pkt) } +// Options holds options to configure a protocol. +type Options struct { + // DADConfigs is the default DAD configurations used by ARP endpoints. + DADConfigs stack.DADConfigurations +} + +// NewProtocolWithOptions returns an ARP network protocol factory that +// will return an ARP network protocol with the provided options. +func NewProtocolWithOptions(opts Options) stack.NetworkProtocolFactory { + return func(s *stack.Stack) stack.NetworkProtocol { + return &protocol{ + stack: s, + options: opts, + } + } +} + // NewProtocol returns an ARP network protocol. func NewProtocol(s *stack.Stack) stack.NetworkProtocol { - return &protocol{ - stack: s, - } + return NewProtocolWithOptions(Options{})(s) } diff --git a/pkg/tcpip/network/arp/arp_test.go b/pkg/tcpip/network/arp/arp_test.go index c8b9ff9fc..018d6a578 100644 --- a/pkg/tcpip/network/arp/arp_test.go +++ b/pkg/tcpip/network/arp/arp_test.go @@ -659,3 +659,53 @@ func TestLinkAddressRequest(t *testing.T) { }) } } + +func TestDADARPRequestPacket(t *testing.T) { + s := stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocolWithOptions(arp.Options{ + DADConfigs: stack.DADConfigurations{ + DupAddrDetectTransmits: 1, + RetransmitTimer: time.Second, + }, + }), ipv4.NewProtocol}, + }) + e := channel.New(1, defaultMTU, stackLinkAddr) + if err := s.CreateNIC(nicID, e); err != nil { + t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err) + } + + if res, err := s.CheckDuplicateAddress(nicID, header.IPv4ProtocolNumber, remoteAddr, func(stack.DADResult) {}); err != nil { + t.Fatalf("s.CheckDuplicateAddress(%d, %d, %s, _): %s", nicID, header.IPv4ProtocolNumber, remoteAddr, err) + } else if res != stack.DADStarting { + t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, header.IPv4ProtocolNumber, remoteAddr, res, stack.DADStarting) + } + + pkt, ok := e.ReadContext(context.Background()) + if !ok { + t.Fatal("expected to send an ARP request") + } + + if pkt.Route.RemoteLinkAddress != header.EthernetBroadcastAddress { + t.Errorf("got pkt.Route.RemoteLinkAddress = %s, want = %s", pkt.Route.RemoteLinkAddress, header.EthernetBroadcastAddress) + } + + req := header.ARP(stack.PayloadSince(pkt.Pkt.NetworkHeader())) + if !req.IsValid() { + t.Errorf("got req.IsValid() = false, want = true") + } + if got := req.Op(); got != header.ARPRequest { + t.Errorf("got req.Op() = %d, want = %d", got, header.ARPRequest) + } + if got := tcpip.LinkAddress(req.HardwareAddressSender()); got != stackLinkAddr { + t.Errorf("got req.HardwareAddressSender() = %s, want = %s", got, stackLinkAddr) + } + if got := tcpip.Address(req.ProtocolAddressSender()); got != header.IPv4Any { + t.Errorf("got req.ProtocolAddressSender() = %s, want = %s", got, header.IPv4Any) + } + if got, want := tcpip.LinkAddress(req.HardwareAddressTarget()), tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00"); got != want { + t.Errorf("got req.HardwareAddressTarget() = %s, want = %s", got, want) + } + if got := tcpip.Address(req.ProtocolAddressTarget()); got != remoteAddr { + t.Errorf("got req.ProtocolAddressTarget() = %s, want = %s", got, remoteAddr) + } +} |