blob: 018d6a5784a95b0c3e27efde26e23da6958021ca [file] [log] [blame]
// 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 arp_test
import (
"context"
"fmt"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
)
const (
nicID = 1
stackAddr = tcpip.Address("\x0a\x00\x00\x01")
stackLinkAddr = tcpip.LinkAddress("\x0a\x0a\x0b\x0b\x0c\x0c")
remoteAddr = tcpip.Address("\x0a\x00\x00\x02")
remoteLinkAddr = tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06")
unknownAddr = tcpip.Address("\x0a\x00\x00\x03")
defaultChannelSize = 1
defaultMTU = 65536
// eventChanSize defines the size of event channels used by the neighbor
// cache's event dispatcher. The size chosen here needs to be sufficient to
// queue all the events received during tests before consumption.
// If eventChanSize is too small, the tests may deadlock.
eventChanSize = 32
)
type eventType uint8
const (
entryAdded eventType = iota
entryChanged
entryRemoved
)
func (t eventType) String() string {
switch t {
case entryAdded:
return "add"
case entryChanged:
return "change"
case entryRemoved:
return "remove"
default:
return fmt.Sprintf("unknown (%d)", t)
}
}
type eventInfo struct {
eventType eventType
nicID tcpip.NICID
entry stack.NeighborEntry
}
func (e eventInfo) String() string {
return fmt.Sprintf("%s event for NIC #%d, %#v", e.eventType, e.nicID, e.entry)
}
// arpDispatcher implements NUDDispatcher to validate the dispatching of
// events upon certain NUD state machine events.
type arpDispatcher struct {
// C is where events are queued
C chan eventInfo
}
var _ stack.NUDDispatcher = (*arpDispatcher)(nil)
func (d *arpDispatcher) OnNeighborAdded(nicID tcpip.NICID, entry stack.NeighborEntry) {
e := eventInfo{
eventType: entryAdded,
nicID: nicID,
entry: entry,
}
d.C <- e
}
func (d *arpDispatcher) OnNeighborChanged(nicID tcpip.NICID, entry stack.NeighborEntry) {
e := eventInfo{
eventType: entryChanged,
nicID: nicID,
entry: entry,
}
d.C <- e
}
func (d *arpDispatcher) OnNeighborRemoved(nicID tcpip.NICID, entry stack.NeighborEntry) {
e := eventInfo{
eventType: entryRemoved,
nicID: nicID,
entry: entry,
}
d.C <- e
}
func (d *arpDispatcher) waitForEvent(ctx context.Context, want eventInfo) error {
select {
case got := <-d.C:
if diff := cmp.Diff(want, got, cmp.AllowUnexported(got), cmpopts.IgnoreFields(stack.NeighborEntry{}, "UpdatedAtNanos")); diff != "" {
return fmt.Errorf("got invalid event (-want +got):\n%s", diff)
}
case <-ctx.Done():
return fmt.Errorf("%s for %s", ctx.Err(), want)
}
return nil
}
func (d *arpDispatcher) waitForEventWithTimeout(want eventInfo, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return d.waitForEvent(ctx, want)
}
func (d *arpDispatcher) nextEvent() (eventInfo, bool) {
select {
case event := <-d.C:
return event, true
default:
return eventInfo{}, false
}
}
type testContext struct {
s *stack.Stack
linkEP *channel.Endpoint
nudDisp *arpDispatcher
}
func newTestContext(t *testing.T) *testContext {
c := stack.DefaultNUDConfigurations()
// Transition from Reachable to Stale almost immediately to test if receiving
// probes refreshes positive reachability.
c.BaseReachableTime = time.Microsecond
d := arpDispatcher{
// Create an event channel large enough so the neighbor cache doesn't block
// while dispatching events. Blocking could interfere with the timing of
// NUD transitions.
C: make(chan eventInfo, eventChanSize),
}
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, arp.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4},
NUDConfigs: c,
NUDDisp: &d,
})
ep := channel.New(defaultChannelSize, defaultMTU, stackLinkAddr)
ep.LinkEPCapabilities |= stack.CapabilityResolutionRequired
wep := stack.LinkEndpoint(ep)
if testing.Verbose() {
wep = sniffer.New(ep)
}
if err := s.CreateNIC(nicID, wep); err != nil {
t.Fatalf("CreateNIC failed: %v", err)
}
if err := s.AddAddress(nicID, ipv4.ProtocolNumber, stackAddr); err != nil {
t.Fatalf("AddAddress for ipv4 failed: %v", err)
}
s.SetRouteTable([]tcpip.Route{{
Destination: header.IPv4EmptySubnet,
NIC: nicID,
}})
return &testContext{
s: s,
linkEP: ep,
nudDisp: &d,
}
}
func (c *testContext) cleanup() {
c.linkEP.Close()
}
func TestMalformedPacket(t *testing.T) {
c := newTestContext(t)
defer c.cleanup()
v := make(buffer.View, header.ARPSize)
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: v.ToVectorisedView(),
})
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
}
if got := c.s.Stats().ARP.MalformedPacketsReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.MalformedPacketsReceived.Value() = %d, want = 1", got)
}
}
func TestDisabledEndpoint(t *testing.T) {
c := newTestContext(t)
defer c.cleanup()
ep, err := c.s.GetNetworkEndpoint(nicID, header.ARPProtocolNumber)
if err != nil {
t.Fatalf("GetNetworkEndpoint(%d, header.ARPProtocolNumber) failed: %s", nicID, err)
}
ep.Disable()
v := make(buffer.View, header.ARPSize)
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: v.ToVectorisedView(),
})
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
}
if got := c.s.Stats().ARP.DisabledPacketsReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.DisabledPacketsReceived.Value() = %d, want = 1", got)
}
}
func TestDirectReply(t *testing.T) {
c := newTestContext(t)
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.ARPReply)
copy(h.HardwareAddressSender(), senderMAC)
copy(h.ProtocolAddressSender(), senderIPv4)
copy(h.HardwareAddressTarget(), stackLinkAddr)
copy(h.ProtocolAddressTarget(), stackAddr)
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: v.ToVectorisedView(),
})
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
}
if got := c.s.Stats().ARP.RepliesReceived.Value(); got != 1 {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
}
}
func TestDirectRequest(t *testing.T) {
c := newTestContext(t)
defer c.cleanup()
tests := []struct {
name string
senderAddr tcpip.Address
senderLinkAddr tcpip.LinkAddress
targetAddr tcpip.Address
isValid bool
}{
{
name: "Loopback",
senderAddr: stackAddr,
senderLinkAddr: stackLinkAddr,
targetAddr: stackAddr,
isValid: true,
},
{
name: "Remote",
senderAddr: remoteAddr,
senderLinkAddr: remoteLinkAddr,
targetAddr: stackAddr,
isValid: true,
},
{
name: "RemoteInvalidTarget",
senderAddr: remoteAddr,
senderLinkAddr: remoteLinkAddr,
targetAddr: unknownAddr,
isValid: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
packetsRecv := c.s.Stats().ARP.PacketsReceived.Value()
requestsRecv := c.s.Stats().ARP.RequestsReceived.Value()
requestsRecvUnknownAddr := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value()
outgoingReplies := c.s.Stats().ARP.OutgoingRepliesSent.Value()
// Inject an incoming ARP request.
v := make(buffer.View, header.ARPSize)
h := header.ARP(v)
h.SetIPv4OverEthernet()
h.SetOp(header.ARPRequest)
copy(h.HardwareAddressSender(), test.senderLinkAddr)
copy(h.ProtocolAddressSender(), test.senderAddr)
copy(h.ProtocolAddressTarget(), test.targetAddr)
c.linkEP.InjectInbound(arp.ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: v.ToVectorisedView(),
}))
if got, want := c.s.Stats().ARP.PacketsReceived.Value(), packetsRecv+1; got != want {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, want)
}
if got, want := c.s.Stats().ARP.RequestsReceived.Value(), requestsRecv+1; got != want {
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, want)
}
if !test.isValid {
// No packets should be sent after receiving an invalid ARP request.
// There is no need to perform a blocking read here, since packets are
// sent in the same function that handles ARP requests.
if pkt, ok := c.linkEP.Read(); ok {
t.Errorf("unexpected packet sent with network protocol number %d", pkt.Proto)
}
if got, want := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value(), requestsRecvUnknownAddr+1; got != want {
t.Errorf("got c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value() = %d, want = %d", got, want)
}
if got, want := c.s.Stats().ARP.OutgoingRepliesSent.Value(), outgoingReplies; got != want {
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, want)
}
return
}
if got, want := c.s.Stats().ARP.OutgoingRepliesSent.Value(), outgoingReplies+1; got != want {
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, want)
}
// Verify an ARP response was sent.
pi, ok := c.linkEP.Read()
if !ok {
t.Fatal("expected ARP response to be sent, got none")
}
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, 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)
}
// Verify the sender was saved in the neighbor cache.
wantEvent := eventInfo{
eventType: entryAdded,
nicID: nicID,
entry: stack.NeighborEntry{
Addr: test.senderAddr,
LinkAddr: tcpip.LinkAddress(test.senderLinkAddr),
State: stack.Stale,
},
}
if err := c.nudDisp.waitForEventWithTimeout(wantEvent, time.Second); err != nil {
t.Fatal(err)
}
neighbors, err := c.s.Neighbors(nicID, ipv4.ProtocolNumber)
if err != nil {
t.Fatalf("c.s.Neighbors(%d, %d): %s", nicID, ipv4.ProtocolNumber, err)
}
neighborByAddr := make(map[tcpip.Address]stack.NeighborEntry)
for _, n := range neighbors {
if existing, ok := neighborByAddr[n.Addr]; ok {
if diff := cmp.Diff(existing, n); diff != "" {
t.Fatalf("duplicate neighbor entry found (-existing +got):\n%s", diff)
}
t.Fatalf("exact neighbor entry duplicate found for addr=%s", n.Addr)
}
neighborByAddr[n.Addr] = n
}
neigh, ok := neighborByAddr[test.senderAddr]
if !ok {
t.Fatalf("expected neighbor entry with Addr = %s", test.senderAddr)
}
if got, want := neigh.LinkAddr, test.senderLinkAddr; got != want {
t.Errorf("got neighbor LinkAddr = %s, want = %s", got, want)
}
if got, want := neigh.State, stack.Stale; got != want {
t.Errorf("got neighbor State = %s, want = %s", got, want)
}
// No more events should be dispatched
for {
event, ok := c.nudDisp.nextEvent()
if !ok {
break
}
t.Errorf("unexpected %s", event)
}
})
}
}
var _ stack.LinkEndpoint = (*testLinkEndpoint)(nil)
type testLinkEndpoint struct {
stack.LinkEndpoint
writeErr tcpip.Error
}
func (t *testLinkEndpoint) WritePacket(r stack.RouteInfo, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
if t.writeErr != nil {
return t.writeErr
}
return t.LinkEndpoint.WritePacket(r, gso, protocol, pkt)
}
func TestLinkAddressRequest(t *testing.T) {
const nicID = 1
testAddr := tcpip.Address([]byte{1, 2, 3, 4})
tests := []struct {
name string
nicAddr tcpip.Address
localAddr tcpip.Address
remoteLinkAddr tcpip.LinkAddress
linkErr tcpip.Error
expectedErr tcpip.Error
expectedLocalAddr tcpip.Address
expectedRemoteLinkAddr tcpip.LinkAddress
expectedRequestsSent uint64
expectedRequestBadLocalAddressErrors uint64
expectedRequestInterfaceHasNoLocalAddressErrors uint64
expectedRequestDroppedErrors uint64
}{
{
name: "Unicast",
nicAddr: stackAddr,
localAddr: stackAddr,
remoteLinkAddr: remoteLinkAddr,
expectedLocalAddr: stackAddr,
expectedRemoteLinkAddr: remoteLinkAddr,
expectedRequestsSent: 1,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Multicast",
nicAddr: stackAddr,
localAddr: stackAddr,
remoteLinkAddr: "",
expectedLocalAddr: stackAddr,
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
expectedRequestsSent: 1,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Unicast with unspecified source",
nicAddr: stackAddr,
localAddr: "",
remoteLinkAddr: remoteLinkAddr,
expectedLocalAddr: stackAddr,
expectedRemoteLinkAddr: remoteLinkAddr,
expectedRequestsSent: 1,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Multicast with unspecified source",
nicAddr: stackAddr,
localAddr: "",
remoteLinkAddr: "",
expectedLocalAddr: stackAddr,
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
expectedRequestsSent: 1,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Unicast with unassigned address",
nicAddr: stackAddr,
localAddr: testAddr,
remoteLinkAddr: remoteLinkAddr,
expectedErr: &tcpip.ErrBadLocalAddress{},
expectedRequestsSent: 0,
expectedRequestBadLocalAddressErrors: 1,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Multicast with unassigned address",
nicAddr: stackAddr,
localAddr: testAddr,
remoteLinkAddr: "",
expectedErr: &tcpip.ErrBadLocalAddress{},
expectedRequestsSent: 0,
expectedRequestBadLocalAddressErrors: 1,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 0,
},
{
name: "Unicast with no local address available",
nicAddr: "",
localAddr: "",
remoteLinkAddr: remoteLinkAddr,
expectedErr: &tcpip.ErrNetworkUnreachable{},
expectedRequestsSent: 0,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 1,
expectedRequestDroppedErrors: 0,
},
{
name: "Multicast with no local address available",
nicAddr: "",
localAddr: "",
remoteLinkAddr: "",
expectedErr: &tcpip.ErrNetworkUnreachable{},
expectedRequestsSent: 0,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 1,
expectedRequestDroppedErrors: 0,
},
{
name: "Link error",
nicAddr: stackAddr,
localAddr: stackAddr,
remoteLinkAddr: remoteLinkAddr,
linkErr: &tcpip.ErrInvalidEndpointState{},
expectedErr: &tcpip.ErrInvalidEndpointState{},
expectedRequestsSent: 0,
expectedRequestBadLocalAddressErrors: 0,
expectedRequestInterfaceHasNoLocalAddressErrors: 0,
expectedRequestDroppedErrors: 1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol},
})
linkEP := channel.New(defaultChannelSize, defaultMTU, stackLinkAddr)
if err := s.CreateNIC(nicID, &testLinkEndpoint{LinkEndpoint: linkEP, writeErr: test.linkErr}); err != nil {
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
}
ep, err := s.GetNetworkEndpoint(nicID, arp.ProtocolNumber)
if err != nil {
t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, arp.ProtocolNumber, err)
}
linkRes, ok := ep.(stack.LinkAddressResolver)
if !ok {
t.Fatalf("expected %T to implement stack.LinkAddressResolver", ep)
}
if len(test.nicAddr) != 0 {
if err := s.AddAddress(nicID, ipv4.ProtocolNumber, test.nicAddr); err != nil {
t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, ipv4.ProtocolNumber, test.nicAddr, err)
}
}
{
err := linkRes.LinkAddressRequest(remoteAddr, test.localAddr, test.remoteLinkAddr)
if diff := cmp.Diff(test.expectedErr, err); diff != "" {
t.Fatalf("unexpected error from p.LinkAddressRequest(%s, %s, %s, _), (-want, +got):\n%s", remoteAddr, test.localAddr, test.remoteLinkAddr, diff)
}
}
if got := s.Stats().ARP.OutgoingRequestsSent.Value(); got != test.expectedRequestsSent {
t.Errorf("got s.Stats().ARP.OutgoingRequestsSent.Value() = %d, want = %d", got, test.expectedRequestsSent)
}
if got := s.Stats().ARP.OutgoingRequestInterfaceHasNoLocalAddressErrors.Value(); got != test.expectedRequestInterfaceHasNoLocalAddressErrors {
t.Errorf("got s.Stats().ARP.OutgoingRequestInterfaceHasNoLocalAddressErrors.Value() = %d, want = %d", got, test.expectedRequestInterfaceHasNoLocalAddressErrors)
}
if got := s.Stats().ARP.OutgoingRequestBadLocalAddressErrors.Value(); got != test.expectedRequestBadLocalAddressErrors {
t.Errorf("got s.Stats().ARP.OutgoingRequestBadLocalAddressErrors.Value() = %d, want = %d", got, test.expectedRequestBadLocalAddressErrors)
}
if got := s.Stats().ARP.OutgoingRequestsDropped.Value(); got != test.expectedRequestDroppedErrors {
t.Errorf("got s.Stats().ARP.OutgoingRequestsDropped.Value() = %d, want = %d", got, test.expectedRequestDroppedErrors)
}
if test.expectedErr != nil {
return
}
pkt, ok := linkEP.Read()
if !ok {
t.Fatal("expected to send a link address request")
}
if pkt.Route.RemoteLinkAddress != test.expectedRemoteLinkAddr {
t.Errorf("got pkt.Route.RemoteLinkAddress = %s, want = %s", pkt.Route.RemoteLinkAddress, test.expectedRemoteLinkAddr)
}
rep := header.ARP(stack.PayloadSince(pkt.Pkt.NetworkHeader()))
if got := rep.Op(); got != header.ARPRequest {
t.Errorf("got Op = %d, want = %d", got, header.ARPRequest)
}
if got := tcpip.LinkAddress(rep.HardwareAddressSender()); got != stackLinkAddr {
t.Errorf("got HardwareAddressSender = %s, want = %s", got, stackLinkAddr)
}
if got := tcpip.Address(rep.ProtocolAddressSender()); got != test.expectedLocalAddr {
t.Errorf("got ProtocolAddressSender = %s, want = %s", got, test.expectedLocalAddr)
}
if got, want := tcpip.LinkAddress(rep.HardwareAddressTarget()), tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00"); got != want {
t.Errorf("got HardwareAddressTarget = %s, want = %s", got, want)
}
if got := tcpip.Address(rep.ProtocolAddressTarget()); got != remoteAddr {
t.Errorf("got ProtocolAddressTarget = %s, want = %s", got, remoteAddr)
}
})
}
}
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)
}
}