Do not filter frames in ethernet link endpoint

Ethernet frames are usually filtered at the hardware-level so there is
no need to filter the frames in software.

For test purposes, a new link endpoint was introduced to filter frames
based on their destination.

PiperOrigin-RevId: 350422941
This commit is contained in:
Ghanan Gowripalan
2021-01-06 14:02:45 -08:00
committed by gVisor bot
parent abe9d9f67f
commit 7817e3b5e4
6 changed files with 125 additions and 11 deletions
+15 -1
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "go_library")
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
@@ -13,3 +13,17 @@ go_library(
"//pkg/tcpip/stack",
],
)
go_test(
name = "ethernet_test",
size = "small",
srcs = ["ethernet_test.go"],
deps = [
":ethernet",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
"//pkg/tcpip/link/channel",
"//pkg/tcpip/stack",
],
)
+3 -3
View File
@@ -49,10 +49,10 @@ func (e *Endpoint) DeliverNetworkPacket(_, _ tcpip.LinkAddress, _ tcpip.NetworkP
return
}
// Note, there is no need to check the destination link address here since
// the ethernet hardware filters frames based on their destination addresses.
eth := header.Ethernet(hdr)
if dst := eth.DestinationAddress(); dst == e.Endpoint.LinkAddress() || dst == header.EthernetBroadcastAddress || header.IsMulticastEthernetAddress(dst) {
e.Endpoint.DeliverNetworkPacket(eth.SourceAddress() /* remote */, dst /* local */, eth.Type() /* protocol */, pkt)
}
e.Endpoint.DeliverNetworkPacket(eth.SourceAddress() /* remote */, eth.DestinationAddress() /* local */, eth.Type() /* protocol */, pkt)
}
// Capabilities implements stack.LinkEndpoint.
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2021 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 ethernet_test
import (
"testing"
"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/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _ stack.NetworkDispatcher = (*testNetworkDispatcher)(nil)
type testNetworkDispatcher struct {
networkPackets int
}
func (t *testNetworkDispatcher) DeliverNetworkPacket(_, _ tcpip.LinkAddress, _ tcpip.NetworkProtocolNumber, _ *stack.PacketBuffer) {
t.networkPackets++
}
func (*testNetworkDispatcher) DeliverOutboundPacket(_, _ tcpip.LinkAddress, _ tcpip.NetworkProtocolNumber, _ *stack.PacketBuffer) {
}
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")
)
e := ethernet.New(channel.New(0, 0, linkAddr))
var networkDispatcher testNetworkDispatcher
e.Attach(&networkDispatcher)
if networkDispatcher.networkPackets != 0 {
t.Fatalf("got networkDispatcher.networkPackets = %d, want = 0", networkDispatcher.networkPackets)
}
// 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 := buffer.NewView(header.EthernetMinimumSize)
header.Ethernet(eth).Encode(&header.EthernetFields{
SrcAddr: otherLinkAddr1,
DstAddr: otherLinkAddr2,
Type: header.IPv4ProtocolNumber,
})
e.DeliverNetworkPacket("", "", 0, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: eth.ToVectorisedView(),
}))
if networkDispatcher.networkPackets != 1 {
t.Fatalf("got networkDispatcher.networkPackets = %d, want = 1", networkDispatcher.networkPackets)
}
}
+1
View File
@@ -19,6 +19,7 @@ go_test(
"//pkg/tcpip/link/channel",
"//pkg/tcpip/link/ethernet",
"//pkg/tcpip/link/loopback",
"//pkg/tcpip/link/nested",
"//pkg/tcpip/link/pipe",
"//pkg/tcpip/network/arp",
"//pkg/tcpip/network/ipv4",
+33 -4
View File
@@ -21,7 +21,9 @@ import (
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/link/nested"
"gvisor.dev/gvisor/pkg/tcpip/link/pipe"
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
@@ -31,6 +33,33 @@ import (
"gvisor.dev/gvisor/pkg/waiter"
)
var _ stack.NetworkDispatcher = (*endpointWithDestinationCheck)(nil)
var _ stack.LinkEndpoint = (*endpointWithDestinationCheck)(nil)
// newEthernetEndpoint returns an ethernet link endpoint that wraps an inner
// link endpoint and checks the destination link address before delivering
// network packets to the network dispatcher.
//
// See ethernet.Endpoint for more details.
func newEthernetEndpoint(ep stack.LinkEndpoint) *endpointWithDestinationCheck {
var e endpointWithDestinationCheck
e.Endpoint.Init(ethernet.New(ep), &e)
return &e
}
// endpointWithDestinationCheck is a link endpoint that checks the destination
// link address before delivering network packets to the network dispatcher.
type endpointWithDestinationCheck struct {
nested.Endpoint
}
// DeliverNetworkPacket implements stack.NetworkDispatcher.
func (e *endpointWithDestinationCheck) DeliverNetworkPacket(src, dst tcpip.LinkAddress, proto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
if dst == e.Endpoint.LinkAddress() || dst == header.EthernetBroadcastAddress || header.IsMulticastEthernetAddress(dst) {
e.Endpoint.DeliverNetworkPacket(src, dst, proto, pkt)
}
}
func TestForwarding(t *testing.T) {
const (
host1NICID = 1
@@ -209,16 +238,16 @@ func TestForwarding(t *testing.T) {
host1NIC, routerNIC1 := pipe.New(linkAddr1, linkAddr2)
routerNIC2, host2NIC := pipe.New(linkAddr3, linkAddr4)
if err := host1Stack.CreateNIC(host1NICID, ethernet.New(host1NIC)); err != nil {
if err := host1Stack.CreateNIC(host1NICID, newEthernetEndpoint(host1NIC)); err != nil {
t.Fatalf("host1Stack.CreateNIC(%d, _): %s", host1NICID, err)
}
if err := routerStack.CreateNIC(routerNICID1, ethernet.New(routerNIC1)); err != nil {
if err := routerStack.CreateNIC(routerNICID1, newEthernetEndpoint(routerNIC1)); err != nil {
t.Fatalf("routerStack.CreateNIC(%d, _): %s", routerNICID1, err)
}
if err := routerStack.CreateNIC(routerNICID2, ethernet.New(routerNIC2)); err != nil {
if err := routerStack.CreateNIC(routerNICID2, newEthernetEndpoint(routerNIC2)); err != nil {
t.Fatalf("routerStack.CreateNIC(%d, _): %s", routerNICID2, err)
}
if err := host2Stack.CreateNIC(host2NICID, ethernet.New(host2NIC)); err != nil {
if err := host2Stack.CreateNIC(host2NICID, newEthernetEndpoint(host2NIC)); err != nil {
t.Fatalf("host2Stack.CreateNIC(%d, _): %s", host2NICID, err)
}
@@ -22,7 +22,6 @@ import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/link/pipe"
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
@@ -133,10 +132,10 @@ func TestPing(t *testing.T) {
host1NIC, host2NIC := pipe.New(linkAddr1, linkAddr2)
if err := host1Stack.CreateNIC(host1NICID, ethernet.New(host1NIC)); err != nil {
if err := host1Stack.CreateNIC(host1NICID, newEthernetEndpoint(host1NIC)); err != nil {
t.Fatalf("host1Stack.CreateNIC(%d, _): %s", host1NICID, err)
}
if err := host2Stack.CreateNIC(host2NICID, ethernet.New(host2NIC)); err != nil {
if err := host2Stack.CreateNIC(host2NICID, newEthernetEndpoint(host2NIC)); err != nil {
t.Fatalf("host2Stack.CreateNIC(%d, _): %s", host2NICID, err)
}