// 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) } }