// 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" "strconv" "testing" "time" "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 ( stackLinkAddr = tcpip.LinkAddress("\x0a\x0a\x0b\x0b\x0c\x0c") stackAddr1 = tcpip.Address("\x0a\x00\x00\x01") stackAddr2 = tcpip.Address("\x0a\x00\x00\x02") stackAddrBad = tcpip.Address("\x0a\x00\x00\x03") ) type testContext struct { t *testing.T linkEP *channel.Endpoint s *stack.Stack } func newTestContext(t *testing.T) *testContext { s := stack.New(stack.Options{ NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), arp.NewProtocol()}, TransportProtocols: []stack.TransportProtocol{icmp.NewProtocol4()}, }) const defaultMTU = 65536 ep := channel.New(256, defaultMTU, stackLinkAddr) wep := stack.LinkEndpoint(ep) if testing.Verbose() { wep = sniffer.New(ep) } if err := s.CreateNIC(1, wep); err != nil { t.Fatalf("CreateNIC failed: %v", err) } if err := s.AddAddress(1, ipv4.ProtocolNumber, stackAddr1); err != nil { t.Fatalf("AddAddress for ipv4 failed: %v", err) } if err := s.AddAddress(1, ipv4.ProtocolNumber, stackAddr2); err != nil { t.Fatalf("AddAddress for ipv4 failed: %v", err) } if err := s.AddAddress(1, arp.ProtocolNumber, arp.ProtocolAddress); err != nil { t.Fatalf("AddAddress for arp failed: %v", err) } s.SetRouteTable([]tcpip.Route{{ Destination: header.IPv4EmptySubnet, NIC: 1, }}) return &testContext{ t: t, s: s, linkEP: ep, } } func (c *testContext) cleanup() { c.linkEP.Close() } func TestDirectRequest(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.ARPRequest) copy(h.HardwareAddressSender(), senderMAC) copy(h.ProtocolAddressSender(), senderIPv4) inject := func(addr tcpip.Address) { copy(h.ProtocolAddressTarget(), addr) c.linkEP.InjectInbound(arp.ProtocolNumber, tcpip.PacketBuffer{ Data: v.ToVectorisedView(), }) } for i, address := range []tcpip.Address{stackAddr1, stackAddr2} { t.Run(strconv.Itoa(i), func(t *testing.T) { inject(address) pi, _ := c.linkEP.ReadContext(context.Background()) if pi.Proto != arp.ProtocolNumber { t.Fatalf("expected ARP response, got network protocol number %d", pi.Proto) } rep := header.ARP(pi.Pkt.Header.View()) if !rep.IsValid() { t.Fatalf("invalid ARP response pi.Pkt.Header.UsedLength()=%d", pi.Pkt.Header.UsedLength()) } 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) } }) } inject(stackAddrBad) // Sleep tests are gross, but this will only potentially flake // if there's a bug. If there is no bug this will reliably // succeed. ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) if pkt, ok := c.linkEP.ReadContext(ctx); ok { t.Errorf("stackAddrBad: unexpected packet sent, Proto=%v", pkt.Proto) } }