diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go index 1e2843bef..a5d3c5796 100644 --- a/pkg/tcpip/link/channel/channel.go +++ b/pkg/tcpip/link/channel/channel.go @@ -288,3 +288,6 @@ func (*Endpoint) ARPHardwareType() header.ARPHardwareType { // AddHeader implements stack.LinkEndpoint.AddHeader. func (*Endpoint) AddHeader(stack.PacketBufferPtr) {} + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (*Endpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } diff --git a/pkg/tcpip/link/ethernet/ethernet.go b/pkg/tcpip/link/ethernet/ethernet.go index 33a2543c6..e6f1e4d11 100644 --- a/pkg/tcpip/link/ethernet/ethernet.go +++ b/pkg/tcpip/link/ethernet/ethernet.go @@ -60,11 +60,10 @@ func (e *Endpoint) MTU() uint32 { // DeliverNetworkPacket implements stack.NetworkDispatcher. func (e *Endpoint) DeliverNetworkPacket(_ tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) { - hdr, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) - if !ok { + if !e.ParseHeader(pkt) { return } - eth := header.Ethernet(hdr) + eth := header.Ethernet(pkt.LinkHeader().Slice()) dst := eth.DestinationAddress() if dst == header.EthernetBroadcastAddress { pkt.PktType = tcpip.PacketBroadcast @@ -113,3 +112,9 @@ func (*Endpoint) AddHeader(pkt stack.PacketBufferPtr) { } eth.Encode(&fields) } + +// ParseHeader implements stack.LinkEndpoint. +func (*Endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + _, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) + return ok +} diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go index 974fcb0ce..05b3630d3 100644 --- a/pkg/tcpip/link/fdbased/endpoint.go +++ b/pkg/tcpip/link/fdbased/endpoint.go @@ -528,6 +528,20 @@ func (e *endpoint) AddHeader(pkt stack.PacketBufferPtr) { } } +func (e *endpoint) parseHeader(pkt stack.PacketBufferPtr) bool { + _, ok := pkt.LinkHeader().Consume(e.hdrSize) + return ok + +} + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (e *endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + if e.hdrSize > 0 { + return e.parseHeader(pkt) + } + return true +} + // writePacket writes outbound packets to the file descriptor. If it is not // currently writable, the packet is dropped. func (e *endpoint) writePacket(pkt stack.PacketBufferPtr) tcpip.Error { diff --git a/pkg/tcpip/link/fdbased/packet_dispatchers.go b/pkg/tcpip/link/fdbased/packet_dispatchers.go index 7d5ced987..0c2539808 100644 --- a/pkg/tcpip/link/fdbased/packet_dispatchers.go +++ b/pkg/tcpip/link/fdbased/packet_dispatchers.go @@ -189,11 +189,10 @@ func (d *readVDispatcher) dispatch() (bool, tcpip.Error) { var p tcpip.NetworkProtocolNumber if d.e.hdrSize > 0 { - hdr, ok := pkt.LinkHeader().Consume(d.e.hdrSize) - if !ok { + if !d.e.parseHeader(pkt) { return false, nil } - p = header.Ethernet(hdr).Type() + p = header.Ethernet(pkt.LinkHeader().Slice()).Type() } else { // We don't get any indication of what the packet is, so try to guess // if it's an IPv4 or IPv6 packet. diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go index 85089e4e6..563a182c2 100644 --- a/pkg/tcpip/link/loopback/loopback.go +++ b/pkg/tcpip/link/loopback/loopback.go @@ -107,4 +107,8 @@ func (*endpoint) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareLoopback } +// AddHeader implements stack.LinkEndpoint. func (*endpoint) AddHeader(stack.PacketBufferPtr) {} + +// ParseHeader implements stack.LinkEndpoint. +func (*endpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } diff --git a/pkg/tcpip/link/muxed/injectable.go b/pkg/tcpip/link/muxed/injectable.go index d5eaa8239..26990ba28 100644 --- a/pkg/tcpip/link/muxed/injectable.go +++ b/pkg/tcpip/link/muxed/injectable.go @@ -147,6 +147,9 @@ func (*InjectableEndpoint) ARPHardwareType() header.ARPHardwareType { // AddHeader implements stack.LinkEndpoint.AddHeader. func (*InjectableEndpoint) AddHeader(stack.PacketBufferPtr) {} +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (*InjectableEndpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } + // NewInjectableEndpoint creates a new multi-endpoint injectable endpoint. func NewInjectableEndpoint(routes map[tcpip.Address]stack.InjectableLinkEndpoint) *InjectableEndpoint { return &InjectableEndpoint{ diff --git a/pkg/tcpip/link/nested/nested.go b/pkg/tcpip/link/nested/nested.go index 1a327d84a..b20ce9004 100644 --- a/pkg/tcpip/link/nested/nested.go +++ b/pkg/tcpip/link/nested/nested.go @@ -147,3 +147,8 @@ func (e *Endpoint) ARPHardwareType() header.ARPHardwareType { func (e *Endpoint) AddHeader(pkt stack.PacketBufferPtr) { e.child.AddHeader(pkt) } + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (e *Endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + return e.child.ParseHeader(pkt) +} diff --git a/pkg/tcpip/link/packetsocket/packetsocket_test.go b/pkg/tcpip/link/packetsocket/packetsocket_test.go index 8fcac81ee..655497ca4 100644 --- a/pkg/tcpip/link/packetsocket/packetsocket_test.go +++ b/pkg/tcpip/link/packetsocket/packetsocket_test.go @@ -53,6 +53,7 @@ func (e *nullEndpoint) IsAttached() bool { return e.disp != func (*nullEndpoint) Wait() {} func (*nullEndpoint) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareNone } func (*nullEndpoint) AddHeader(stack.PacketBufferPtr) {} +func (*nullEndpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } var _ stack.NetworkDispatcher = (*testNetworkDispatcher)(nil) diff --git a/pkg/tcpip/link/pipe/pipe.go b/pkg/tcpip/link/pipe/pipe.go index 789c62a0a..f2c4858ed 100644 --- a/pkg/tcpip/link/pipe/pipe.go +++ b/pkg/tcpip/link/pipe/pipe.go @@ -123,3 +123,6 @@ func (*Endpoint) ARPHardwareType() header.ARPHardwareType { // AddHeader implements stack.LinkEndpoint. func (*Endpoint) AddHeader(stack.PacketBufferPtr) {} + +// ParseHeader implements stack.LinkEndpoint. +func (*Endpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index 324c704a1..2ed7caab7 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -354,6 +354,21 @@ func (e *endpoint) AddHeader(pkt stack.PacketBufferPtr) { }) } +func (e *endpoint) parseHeader(pkt stack.PacketBufferPtr) bool { + _, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) + return ok +} + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (e *endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + // Add ethernet header if needed. + if len(e.addr) == 0 { + return true + } + + return e.parseHeader(pkt) +} + func (e *endpoint) AddVirtioNetHeader(pkt stack.PacketBufferPtr) { virtio := header.VirtioNetHeader(pkt.VirtioNetHeader().Push(header.VirtioNetHeaderSize)) virtio.Encode(&header.VirtioNetHeaderFields{}) @@ -447,13 +462,12 @@ func (e *endpoint) dispatchLoop(d stack.NetworkDispatcher) { } var proto tcpip.NetworkProtocolNumber - if e.addr != "" { - hdr, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) - if !ok { + if len(e.addr) != 0 { + if !e.parseHeader(pkt) { pkt.DecRef() continue } - proto = header.Ethernet(hdr).Type() + proto = header.Ethernet(pkt.LinkHeader().Slice()).Type() } else { // We don't get any indication of what the packet is, so try to guess // if it's an IPv4 or IPv6 packet. diff --git a/pkg/tcpip/link/sharedmem/sharedmem_server.go b/pkg/tcpip/link/sharedmem/sharedmem_server.go index f29c98da1..c0840bd34 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_server.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_server.go @@ -217,6 +217,21 @@ func (e *serverEndpoint) AddHeader(pkt stack.PacketBufferPtr) { }) } +func (e *serverEndpoint) parseHeader(pkt stack.PacketBufferPtr) bool { + _, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) + return ok +} + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (e *serverEndpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + // Add ethernet header if needed. + if len(e.addr) == 0 { + return true + } + + return e.parseHeader(pkt) +} + func (e *serverEndpoint) AddVirtioNetHeader(pkt stack.PacketBufferPtr) { virtio := header.VirtioNetHeader(pkt.VirtioNetHeader().Push(header.VirtioNetHeaderSize)) virtio.Encode(&header.VirtioNetHeaderFields{}) @@ -304,13 +319,12 @@ func (e *serverEndpoint) dispatchLoop(d stack.NetworkDispatcher) { } } var proto tcpip.NetworkProtocolNumber - if e.addr != "" { - hdr, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) - if !ok { + if len(e.addr) != 0 { + if !e.parseHeader(pkt) { pkt.DecRef() continue } - proto = header.Ethernet(hdr).Type() + proto = header.Ethernet(pkt.LinkHeader().Slice()).Type() } else { // We don't get any indication of what the packet is, so try to guess // if it's an IPv4 or IPv6 packet. diff --git a/pkg/tcpip/link/waitable/waitable.go b/pkg/tcpip/link/waitable/waitable.go index 4000c06f9..7977e51b7 100644 --- a/pkg/tcpip/link/waitable/waitable.go +++ b/pkg/tcpip/link/waitable/waitable.go @@ -161,3 +161,8 @@ func (e *Endpoint) ARPHardwareType() header.ARPHardwareType { func (e *Endpoint) AddHeader(pkt stack.PacketBufferPtr) { e.lower.AddHeader(pkt) } + +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (e *Endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + return e.lower.ParseHeader(pkt) +} diff --git a/pkg/tcpip/link/waitable/waitable_test.go b/pkg/tcpip/link/waitable/waitable_test.go index c62a07cce..b22f2b059 100644 --- a/pkg/tcpip/link/waitable/waitable_test.go +++ b/pkg/tcpip/link/waitable/waitable_test.go @@ -92,6 +92,11 @@ func (*countedEndpoint) AddHeader(stack.PacketBufferPtr) { panic("unimplemented") } +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (*countedEndpoint) ParseHeader(stack.PacketBufferPtr) bool { + panic("unimplemented") +} + func TestWaitWrite(t *testing.T) { ep := &countedEndpoint{} wep := New(ep) diff --git a/pkg/tcpip/link/xdp/endpoint.go b/pkg/tcpip/link/xdp/endpoint.go index 81b938adb..7c34b8791 100644 --- a/pkg/tcpip/link/xdp/endpoint.go +++ b/pkg/tcpip/link/xdp/endpoint.go @@ -246,6 +246,12 @@ func (ep *endpoint) AddHeader(pkt stack.PacketBufferPtr) { }) } +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (ep *endpoint) ParseHeader(pkt stack.PacketBufferPtr) bool { + _, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize) + return ok +} + // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. func (ep *endpoint) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareEther @@ -361,8 +367,8 @@ func (ep *endpoint) dispatch() (bool, tcpip.Error) { Payload: buffer.MakeWithView(view), }) // AF_XDP packets always have a link header. - if _, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize); !ok { - panic(fmt.Sprintf("LinkHeader().Consume(%d) must succeed", header.EthernetMinimumSize)) + if !ep.ParseHeader(pkt) { + panic("ParseHeader(_) must succeed") } d.DeliverNetworkPacket(netProto, pkt) pkt.DecRef() diff --git a/pkg/tcpip/network/internal/testutil/testutil.go b/pkg/tcpip/network/internal/testutil/testutil.go index 629c6bd60..390eaa50e 100644 --- a/pkg/tcpip/network/internal/testutil/testutil.go +++ b/pkg/tcpip/network/internal/testutil/testutil.go @@ -94,6 +94,9 @@ func (*MockLinkEndpoint) ARPHardwareType() header.ARPHardwareType { return heade // AddHeader implements LinkEndpoint.AddHeader. func (*MockLinkEndpoint) AddHeader(stack.PacketBufferPtr) {} +// ParseHeader implements LinkEndpoint.ParseHeader. +func (*MockLinkEndpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } + // Close releases all resources. func (ep *MockLinkEndpoint) Close() { for _, pkt := range ep.WrittenPackets { diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go index 4611342e3..7d890a9ba 100644 --- a/pkg/tcpip/network/ip_test.go +++ b/pkg/tcpip/network/ip_test.go @@ -229,6 +229,11 @@ func (*testObject) AddHeader(stack.PacketBufferPtr) { panic("not implemented") } +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (*testObject) ParseHeader(stack.PacketBufferPtr) bool { + panic("not implemented") +} + type testContext struct { s *stack.Stack } diff --git a/pkg/tcpip/stack/forwarding_test.go b/pkg/tcpip/stack/forwarding_test.go index 00de02501..c54807b21 100644 --- a/pkg/tcpip/stack/forwarding_test.go +++ b/pkg/tcpip/stack/forwarding_test.go @@ -331,6 +331,9 @@ func (*fwdTestLinkEndpoint) ARPHardwareType() header.ARPHardwareType { // AddHeader implements stack.LinkEndpoint.AddHeader. func (*fwdTestLinkEndpoint) AddHeader(PacketBufferPtr) {} +// ParseHeader implements stack.LinkEndpoint.ParseHeader. +func (*fwdTestLinkEndpoint) ParseHeader(PacketBufferPtr) bool { return true } + func fwdTestNetFactory(t *testing.T, proto *fwdTestNetworkProtocol) (*faketime.ManualClock, *fwdTestLinkEndpoint, *fwdTestLinkEndpoint) { clock := faketime.NewManualClock() // Create a stack with the network protocol and two NICs. diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index bafe633d5..3e8a0a3ea 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -386,6 +386,13 @@ func (n *nic) writePacket(pkt PacketBufferPtr) tcpip.Error { return n.writeRawPacket(pkt) } +func (n *nic) writeRawPacketWithLinkHeaderInPayload(pkt PacketBufferPtr) tcpip.Error { + if !n.NetworkLinkEndpoint.ParseHeader(pkt) { + return &tcpip.ErrMalformedHeader{} + } + return n.writeRawPacket(pkt) +} + func (n *nic) writeRawPacket(pkt PacketBufferPtr) tcpip.Error { // Always an outgoing packet. pkt.PktType = tcpip.PacketOutgoing diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index a76e53287..4cd720820 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -1065,6 +1065,9 @@ type LinkWriter interface { // WritePackets writes packets. Must not be called with an empty list of // packet buffers. // + // Each packet must have the link-layer header set, if the link requires + // one. + // // WritePackets may modify the packet buffers, and takes ownership of the PacketBufferList. // it is not safe to use the PacketBufferList after a call to WritePackets. WritePackets(PacketBufferList) (int, tcpip.Error) @@ -1121,6 +1124,9 @@ type NetworkLinkEndpoint interface { // AddHeader adds a link layer header to the packet if required. AddHeader(PacketBufferPtr) + + // ParseHeader parses the link layer header to the packet. + ParseHeader(PacketBufferPtr) bool } // QueueingDiscipline provides a queueing strategy for outgoing packets (e.g diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index d0b4df7d7..382fcdd49 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -1887,7 +1887,7 @@ func (s *Stack) WriteRawPacket(nicID tcpip.NICID, proto tcpip.NetworkProtocolNum }) defer pkt.DecRef() pkt.NetworkProtocolNumber = proto - return nic.writeRawPacket(pkt) + return nic.writeRawPacketWithLinkHeaderInPayload(pkt) } // NetworkProtocolInstance returns the protocol instance in the stack for the diff --git a/pkg/tcpip/transport/datagram_test.go b/pkg/tcpip/transport/datagram_test.go index 0ff2dbf4b..9879f2149 100644 --- a/pkg/tcpip/transport/datagram_test.go +++ b/pkg/tcpip/transport/datagram_test.go @@ -169,6 +169,7 @@ func (e *mockEndpoint) IsAttached() bool { return e.disp != func (*mockEndpoint) Wait() {} func (*mockEndpoint) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareNone } func (*mockEndpoint) AddHeader(stack.PacketBufferPtr) {} +func (*mockEndpoint) ParseHeader(stack.PacketBufferPtr) bool { return true } func (e *mockEndpoint) releasePackets() { e.pkts.DecRef() e.pkts = stack.PacketBufferList{} diff --git a/pkg/tcpip/transport/packet/BUILD b/pkg/tcpip/transport/packet/BUILD index c07f25e4a..7b9874a7f 100644 --- a/pkg/tcpip/transport/packet/BUILD +++ b/pkg/tcpip/transport/packet/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library") +load("//tools:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") package( @@ -37,3 +37,19 @@ go_library( "//pkg/waiter", ], ) + +go_test( + name = "packet_test", + srcs = ["packet_test.go"], + deps = [ + "//pkg/tcpip", + "//pkg/tcpip/faketime", + "//pkg/tcpip/header", + "//pkg/tcpip/link/channel", + "//pkg/tcpip/link/ethernet", + "//pkg/tcpip/stack", + "//pkg/tcpip/transport/raw", + "//pkg/waiter", + "@com_github_google_go_cmp//cmp:go_default_library", + ], +) diff --git a/pkg/tcpip/transport/packet/packet_test.go b/pkg/tcpip/transport/packet/packet_test.go new file mode 100644 index 000000000..89199e93f --- /dev/null +++ b/pkg/tcpip/transport/packet/packet_test.go @@ -0,0 +1,114 @@ +// Copyright 2023 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 packet_test + +import ( + "bytes" + "testing" + + "github.com/google/go-cmp/cmp" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/faketime" + "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" + "gvisor.dev/gvisor/pkg/tcpip/transport/raw" + "gvisor.dev/gvisor/pkg/waiter" +) + +func TestWriteRaw(t *testing.T) { + const nicID = 1 + + tests := []struct { + name string + len int + expectErr tcpip.Error + }{ + { + name: "small", + len: header.EthernetMinimumSize - 1, + expectErr: &tcpip.ErrMalformedHeader{}, + }, + { + name: "exact", + len: header.EthernetMinimumSize, + expectErr: nil, + }, + { + name: "bigger", + len: header.EthernetMinimumSize + 1, + expectErr: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + s := stack.New(stack.Options{ + RawFactory: &raw.EndpointFactory{}, + AllowPacketEndpointWrite: true, + Clock: &faketime.NullClock{}, + }) + defer s.Destroy() + + chEP := channel.New(1, header.IPv6MinimumMTU, "") + if err := s.CreateNIC(nicID, ethernet.New(chEP)); err != nil { + t.Errorf("CreateNIC(%d, _) failed: %s", nicID, err) + } + + var wq waiter.Queue + ep, err := s.NewPacketEndpoint(false /* cooked */, 0 /* netProto */, &wq) + if err != nil { + t.Fatalf("s.NewPacketEndpoint(false, 0, _): %s", err) + } + defer ep.Close() + + bindAddr := tcpip.FullAddress{NIC: nicID} + if err := ep.Bind(bindAddr); err != nil { + t.Fatalf("ep.Bind(%#v): %s", bindAddr, err) + } + + data := make([]byte, test.len) + for i := range data { + data[i] = byte(i) + } + + var r bytes.Reader + r.Reset(data) + n, err := ep.Write(&r, tcpip.WriteOptions{}) + if diff := cmp.Diff(test.expectErr, err); diff != "" { + t.Fatalf("ep.Write(..) mismatch:\n%s", diff) + } + if test.expectErr != nil { + return + } + if want := int64(len(data)); n != want { + t.Errorf("got ep.Write(..) = %d, want = %d", n, want) + } + pkt := chEP.Read() + if pkt.IsNil() { + t.Fatal("Packet wasn't written out") + } + defer pkt.DecRef() + + if diff := cmp.Diff(data, stack.PayloadSince(pkt.LinkHeader()).AsSlice()); diff != "" { + t.Errorf("packet data mismatch:\n%s", diff) + } + if len := len(pkt.LinkHeader().Slice()); len != header.EthernetMinimumSize { + t.Errorf("got len(pkt.LinkHeader().Slice()) = %d, want = %d", len, header.EthernetMinimumSize) + } + }) + } +}