diff --git a/pkg/tcpip/link/ethernet/BUILD b/pkg/tcpip/link/ethernet/BUILD index f592665ff..26a9bd04a 100644 --- a/pkg/tcpip/link/ethernet/BUILD +++ b/pkg/tcpip/link/ethernet/BUILD @@ -26,5 +26,6 @@ go_test( "//pkg/tcpip/header", "//pkg/tcpip/link/channel", "//pkg/tcpip/stack", + "@com_github_google_go_cmp//cmp:go_default_library", ], ) diff --git a/pkg/tcpip/link/ethernet/ethernet.go b/pkg/tcpip/link/ethernet/ethernet.go index 3d93cde49..33a2543c6 100644 --- a/pkg/tcpip/link/ethernet/ethernet.go +++ b/pkg/tcpip/link/ethernet/ethernet.go @@ -64,10 +64,21 @@ func (e *Endpoint) DeliverNetworkPacket(_ tcpip.NetworkProtocolNumber, pkt stack if !ok { return } + eth := header.Ethernet(hdr) + dst := eth.DestinationAddress() + if dst == header.EthernetBroadcastAddress { + pkt.PktType = tcpip.PacketBroadcast + } else if header.IsMulticastEthernetAddress(dst) { + pkt.PktType = tcpip.PacketMulticast + } else if dst == e.LinkAddress() { + pkt.PktType = tcpip.PacketHost + } else { + pkt.PktType = tcpip.PacketOtherHost + } // Note, there is no need to check the destination link address here since // the ethernet hardware filters frames based on their destination addresses. - e.Endpoint.DeliverNetworkPacket(header.Ethernet(hdr).Type() /* protocol */, pkt) + e.Endpoint.DeliverNetworkPacket(eth.Type() /* protocol */, pkt) } // Capabilities implements stack.LinkEndpoint. diff --git a/pkg/tcpip/link/ethernet/ethernet_test.go b/pkg/tcpip/link/ethernet/ethernet_test.go index 0980caa76..ad7c3d814 100644 --- a/pkg/tcpip/link/ethernet/ethernet_test.go +++ b/pkg/tcpip/link/ethernet/ethernet_test.go @@ -19,6 +19,7 @@ import ( "os" "testing" + "github.com/google/go-cmp/cmp" "gvisor.dev/gvisor/pkg/bufferv2" "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/tcpip" @@ -30,47 +31,94 @@ import ( var _ stack.NetworkDispatcher = (*testNetworkDispatcher)(nil) +type deliveredPacket struct { + protocol tcpip.NetworkProtocolNumber + packet stack.PacketBufferPtr +} + type testNetworkDispatcher struct { - networkPackets int + networkPackets []deliveredPacket } -func (t *testNetworkDispatcher) DeliverNetworkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { - t.networkPackets++ +func (t *testNetworkDispatcher) DeliverNetworkPacket(proto tcpip.NetworkProtocolNumber, pb stack.PacketBufferPtr) { + t.networkPackets = append(t.networkPackets, deliveredPacket{protocol: proto, packet: pb}) } -func (*testNetworkDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (*testNetworkDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { panic("not implemented") } func TestDeliverNetworkPacket(t *testing.T) { + const ( - linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06") - otherLinkAddr1 = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x07") - otherLinkAddr2 = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x08") + linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06") + otherLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x07") ) - e := ethernet.New(channel.New(0, 0, linkAddr)) - var networkDispatcher testNetworkDispatcher - e.Attach(&networkDispatcher) + for _, testCase := range []struct { + name string + dstAddr tcpip.LinkAddress + pktType tcpip.PacketType + }{ + { + name: "unicast", + dstAddr: linkAddr, + pktType: tcpip.PacketHost, + }, + { + name: "broadcast", + dstAddr: header.EthernetBroadcastAddress, + pktType: tcpip.PacketBroadcast, + }, + { + name: "multicast", + dstAddr: tcpip.LinkAddress("\xFF\x00\x00\x00\x05\x07"), + pktType: tcpip.PacketMulticast, + }, + { + name: "other host", + dstAddr: tcpip.LinkAddress("\x02\x02\x03\x04\x05\x08"), + pktType: tcpip.PacketOtherHost, + }, + } { + t.Run(testCase.name, func(t *testing.T) { - if networkDispatcher.networkPackets != 0 { - t.Fatalf("got networkDispatcher.networkPackets = %d, want = 0", networkDispatcher.networkPackets) - } + e := ethernet.New(channel.New(0, 0, linkAddr)) + var networkDispatcher testNetworkDispatcher + e.Attach(&networkDispatcher) - // An ethernet frame with a destination link address that is not assigned to - // our ethernet link endpoint should still be delivered to the network - // dispatcher since the ethernet endpoint is not expected to filter frames. - eth := make([]byte, header.EthernetMinimumSize) - header.Ethernet(eth).Encode(&header.EthernetFields{ - SrcAddr: otherLinkAddr1, - DstAddr: otherLinkAddr2, - Type: header.IPv4ProtocolNumber, - }) - p := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: bufferv2.MakeWithData(eth)}) - defer p.DecRef() - e.DeliverNetworkPacket(0, p) - if networkDispatcher.networkPackets != 1 { - t.Fatalf("got networkDispatcher.networkPackets = %d, want = 1", networkDispatcher.networkPackets) + if got, want := len(networkDispatcher.networkPackets), 0; got != want { + t.Fatalf("got networkDispatcher.networkPackets = %d, want = %d", got, want) + } + + const networkProtocol = header.IPv4ProtocolNumber + + // An ethernet frame with a destination link address that is not assigned to + // our ethernet link endpoint should still be delivered to the network + // dispatcher since the ethernet endpoint is not expected to filter frames. + eth := make([]byte, header.EthernetMinimumSize) + header.Ethernet(eth).Encode(&header.EthernetFields{ + SrcAddr: otherLinkAddr, + DstAddr: testCase.dstAddr, + Type: networkProtocol, + }) + p := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: bufferv2.MakeWithData(eth)}) + defer p.DecRef() + e.DeliverNetworkPacket(0, p) + if got, want := len(networkDispatcher.networkPackets), 1; got != want { + t.Fatalf("got networkDispatcher.networkPackets = %d, want = %d", got, want) + } + delivered := networkDispatcher.networkPackets[0] + if diff := cmp.Diff(delivered.packet.LinkHeader().Slice(), eth); diff != "" { + t.Errorf("LinkHeader mismatch (-want +got):\n%s", diff) + } + if got, want := delivered.protocol, networkProtocol; got != want { + t.Errorf("got delivered.protocol = %d, want = %d", got, want) + } + if got, want := delivered.packet.PktType, testCase.pktType; got != want { + t.Errorf("got delivered.packet.PktType = %d, want = %d", got, want) + } + }) } } diff --git a/pkg/tcpip/link/fdbased/endpoint_test.go b/pkg/tcpip/link/fdbased/endpoint_test.go index 95faab674..283271879 100644 --- a/pkg/tcpip/link/fdbased/endpoint_test.go +++ b/pkg/tcpip/link/fdbased/endpoint_test.go @@ -137,7 +137,7 @@ func (c *context) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt c.ch <- packetInfo{protocol, pkt} } -func (c *context) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (c *context) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { c.t.Fatal("DeliverLinkPacket not implemented") } @@ -575,7 +575,7 @@ func (d *fakeNetworkDispatcher) DeliverNetworkPacket(_ tcpip.NetworkProtocolNumb d.pkts = append(d.pkts, pkt) } -func (*fakeNetworkDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (*fakeNetworkDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { panic("not implemented") } diff --git a/pkg/tcpip/link/nested/nested.go b/pkg/tcpip/link/nested/nested.go index cbf1ffc53..1a327d84a 100644 --- a/pkg/tcpip/link/nested/nested.go +++ b/pkg/tcpip/link/nested/nested.go @@ -61,12 +61,12 @@ func (e *Endpoint) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pk } // DeliverLinkPacket implements stack.NetworkDispatcher. -func (e *Endpoint) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr, incoming bool) { +func (e *Endpoint) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) { e.mu.RLock() d := e.dispatcher e.mu.RUnlock() if d != nil { - d.DeliverLinkPacket(protocol, pkt, incoming) + d.DeliverLinkPacket(protocol, pkt) } } diff --git a/pkg/tcpip/link/nested/nested_test.go b/pkg/tcpip/link/nested/nested_test.go index 89a8e8741..dca401a0c 100644 --- a/pkg/tcpip/link/nested/nested_test.go +++ b/pkg/tcpip/link/nested/nested_test.go @@ -57,7 +57,7 @@ func (d *counterDispatcher) DeliverNetworkPacket(tcpip.NetworkProtocolNumber, st d.count++ } -func (*counterDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (*counterDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { panic("not implemented") } diff --git a/pkg/tcpip/link/packetsocket/packetsocket.go b/pkg/tcpip/link/packetsocket/packetsocket.go index 12186b877..d309f6538 100644 --- a/pkg/tcpip/link/packetsocket/packetsocket.go +++ b/pkg/tcpip/link/packetsocket/packetsocket.go @@ -41,7 +41,7 @@ func New(lower stack.LinkEndpoint) stack.LinkEndpoint { // DeliverNetworkPacket implements stack.NetworkDispatcher. func (e *endpoint) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) { - e.Endpoint.DeliverLinkPacket(protocol, pkt, true /* incoming */) + e.Endpoint.DeliverLinkPacket(protocol, pkt) e.Endpoint.DeliverNetworkPacket(protocol, pkt) } @@ -49,7 +49,7 @@ func (e *endpoint) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pk // WritePackets implements stack.LinkEndpoint. func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { for _, pkt := range pkts.AsSlice() { - e.Endpoint.DeliverLinkPacket(pkt.NetworkProtocolNumber, pkt, false /* incoming */) + e.Endpoint.DeliverLinkPacket(pkt.NetworkProtocolNumber, pkt) } return e.Endpoint.WritePackets(pkts) diff --git a/pkg/tcpip/link/packetsocket/packetsocket_test.go b/pkg/tcpip/link/packetsocket/packetsocket_test.go index 7771f2a9d..8fcac81ee 100644 --- a/pkg/tcpip/link/packetsocket/packetsocket_test.go +++ b/pkg/tcpip/link/packetsocket/packetsocket_test.go @@ -59,7 +59,6 @@ var _ stack.NetworkDispatcher = (*testNetworkDispatcher)(nil) type linkPacketInfo struct { pkt stack.PacketBufferPtr protocol tcpip.NetworkProtocolNumber - incoming bool } type networkPacketInfo struct { @@ -99,11 +98,10 @@ func (t *testNetworkDispatcher) DeliverNetworkPacket(protocol tcpip.NetworkProto t.networkPacket = networkPacket } -func (t *testNetworkDispatcher) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr, incoming bool) { +func (t *testNetworkDispatcher) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) { linkPacket := linkPacketInfo{ pkt: pkt.IncRef(), protocol: protocol, - incoming: incoming, } if t.linkPacket != (linkPacketInfo{}) { @@ -128,6 +126,7 @@ func TestPacketDispatch(t *testing.T) { pkt.NetworkProtocolNumber = protocol { + pkt.PktType = tcpip.PacketOutgoing var pkts stack.PacketBufferList pkts.PushBack(pkt) if n, err := ep.WritePackets(pkts); err != nil { @@ -139,18 +138,19 @@ func TestPacketDispatch(t *testing.T) { if want := (networkPacketInfo{}); d.networkPacket != want { t.Errorf("got d.networkPacket = %#v, want = %#v", d.networkPacket, want) } - if want := (linkPacketInfo{pkt: pkt, protocol: protocol, incoming: false}); d.linkPacket != want { + if want := (linkPacketInfo{pkt: pkt, protocol: protocol}); d.linkPacket != want { t.Errorf("got d.linkPacket = %#v, want = %#v", d.linkPacket, want) } } d.reset() { + pkt.PktType = tcpip.PacketHost nullEP.disp.DeliverNetworkPacket(protocol, pkt) if want := (networkPacketInfo{pkt: pkt, protocol: protocol}); d.networkPacket != want { t.Errorf("got d.networkPacket = %#v, want = %#v", d.networkPacket, want) } - if want := (linkPacketInfo{pkt: pkt, protocol: protocol, incoming: true}); d.linkPacket != want { + if want := (linkPacketInfo{pkt: pkt, protocol: protocol}); d.linkPacket != want { t.Errorf("got d.linkPacket = %#v, want = %#v", d.linkPacket, want) } } diff --git a/pkg/tcpip/link/sharedmem/sharedmem_test.go b/pkg/tcpip/link/sharedmem/sharedmem_test.go index d7c219107..ab1951238 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_test.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_test.go @@ -154,7 +154,7 @@ func (c *testContext) DeliverNetworkPacket(proto tcpip.NetworkProtocolNumber, pk c.packetCh <- struct{}{} } -func (c *testContext) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (c *testContext) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { c.t.Fatal("DeliverLinkPacket not implemented") } diff --git a/pkg/tcpip/link/waitable/waitable.go b/pkg/tcpip/link/waitable/waitable.go index 12e5daa31..25d9a7fc3 100644 --- a/pkg/tcpip/link/waitable/waitable.go +++ b/pkg/tcpip/link/waitable/waitable.go @@ -63,12 +63,12 @@ func (e *Endpoint) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pk } // DeliverLinkPacket implements stack.NetworkDispatcher. -func (e *Endpoint) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr, incoming bool) { +func (e *Endpoint) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) { if !e.dispatchGate.Enter() { return } - e.dispatcher.DeliverLinkPacket(protocol, pkt, incoming) + e.dispatcher.DeliverLinkPacket(protocol, pkt) e.dispatchGate.Leave() } diff --git a/pkg/tcpip/link/waitable/waitable_test.go b/pkg/tcpip/link/waitable/waitable_test.go index 9d7655b63..c62a07cce 100644 --- a/pkg/tcpip/link/waitable/waitable_test.go +++ b/pkg/tcpip/link/waitable/waitable_test.go @@ -43,7 +43,7 @@ func (e *countedEndpoint) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNum e.dispatchCount++ } -func (*countedEndpoint) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr, bool) { +func (*countedEndpoint) DeliverLinkPacket(tcpip.NetworkProtocolNumber, stack.PacketBufferPtr) { panic("not implemented") } diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index 38b71f67a..1513c23d9 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -386,6 +386,8 @@ func (n *nic) writePacket(pkt PacketBufferPtr) tcpip.Error { } func (n *nic) writeRawPacket(pkt PacketBufferPtr) tcpip.Error { + // Always an outgoing packet. + pkt.PktType = tcpip.PacketOutgoing if err := n.qDisc.WritePacket(pkt); err != nil { if _, ok := err.(*tcpip.ErrNoBufferSpace); ok { n.stats.txPacketsDroppedNoBufferSpace.Increment() @@ -738,7 +740,7 @@ func (n *nic) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt Pac n.gro.dispatch(pkt, protocol, networkEndpoint) } -func (n *nic) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt PacketBufferPtr, incoming bool) { +func (n *nic) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt PacketBufferPtr) { // Deliver to interested packet endpoints without holding NIC lock. var packetEPPkt PacketBufferPtr defer func() { @@ -764,11 +766,13 @@ func (n *nic) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt Packet // populate it in the packet buffer we provide to packet endpoints as // packet endpoints inspect link headers. packetEPPkt.LinkHeader().Consume(len(pkt.LinkHeader().Slice())) - - if incoming { + packetEPPkt.PktType = pkt.PktType + // Assume the packet is for us if the packet type is unset. + // The packet type is set to PacketOutgoing when sending packets so + // this may only be unset for incoming packets where link endpoints + // have not set it. + if packetEPPkt.PktType == 0 { packetEPPkt.PktType = tcpip.PacketHost - } else { - packetEPPkt.PktType = tcpip.PacketOutgoing } } @@ -785,7 +789,7 @@ func (n *nic) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt Packet n.packetEPsMu.Unlock() // On Linux, only ETH_P_ALL endpoints get outbound packets. - if incoming && protoEPsOK { + if pkt.PktType != tcpip.PacketOutgoing && protoEPsOK { protoEPs.forEach(deliverPacketEPs) } if anyEPsOK { diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index e06abbcda..2e7f8b87a 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -1034,7 +1034,7 @@ type NetworkDispatcher interface { // This method should be called with both incoming and outgoing packets. // // If the link-layer has a header, the packet's link header must be populated. - DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt PacketBufferPtr, incoming bool) + DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt PacketBufferPtr) } // LinkEndpointCapabilities is the type associated with the capabilities