summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network/arp
diff options
context:
space:
mode:
authorGhanan Gowripalan <ghanan@google.com>2021-02-08 19:03:54 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-08 19:05:45 -0800
commit39251f31cb92d6c2b053416d04e195e290b106f2 (patch)
treebf3c80dc631655f48fc0b9686cfe2af2e6a4ab74 /pkg/tcpip/network/arp
parentcfa4633c3d206aa2f9abdaac60d053162244ee6d (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/BUILD2
-rw-r--r--pkg/tcpip/network/arp/arp.go77
-rw-r--r--pkg/tcpip/network/arp/arp_test.go50
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)
+ }
+}