From d207727b3a8b4fbc1037e859545276d556b2945a Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Wed, 10 May 2023 15:28:21 -0700 Subject: [PATCH] netstack: replace slice-based PacketBufferList with linked list This is effectively a rollback of cl/450976957. The original motivation never panned out, and it's easier to work with the lists. They also are easier to avoid allocations with. PiperOrigin-RevId: 531020857 --- pkg/tcpip/link/channel/channel.go | 2 +- pkg/tcpip/link/fdbased/endpoint.go | 2 +- pkg/tcpip/link/loopback/loopback.go | 2 +- pkg/tcpip/link/muxed/injectable.go | 4 +- pkg/tcpip/link/packetsocket/packetsocket.go | 2 +- pkg/tcpip/link/pipe/pipe.go | 2 +- pkg/tcpip/link/qdisc/fifo/BUILD | 1 - pkg/tcpip/link/qdisc/fifo/fifo.go | 27 ++++-- .../qdisc/fifo/packet_buffer_circular_list.go | 93 ------------------- pkg/tcpip/link/sharedmem/sharedmem.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem_server.go | 2 +- pkg/tcpip/link/sniffer/sniffer.go | 2 +- pkg/tcpip/link/xdp/endpoint.go | 6 +- .../network/internal/testutil/testutil.go | 2 +- pkg/tcpip/stack/BUILD | 12 +++ pkg/tcpip/stack/forwarding_test.go | 2 +- pkg/tcpip/stack/packet_buffer.go | 27 ++++++ pkg/tcpip/stack/packet_buffer_list.go | 69 -------------- pkg/tcpip/tests/integration/iptables_test.go | 5 +- .../tests/integration/link_resolution_test.go | 2 +- pkg/tcpip/transport/datagram_test.go | 4 +- 21 files changed, 79 insertions(+), 191 deletions(-) delete mode 100644 pkg/tcpip/link/qdisc/fifo/packet_buffer_circular_list.go delete mode 100644 pkg/tcpip/stack/packet_buffer_list.go diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go index 1e2843bef..b6df84d22 100644 --- a/pkg/tcpip/link/channel/channel.go +++ b/pkg/tcpip/link/channel/channel.go @@ -254,7 +254,7 @@ func (e *Endpoint) LinkAddress() tcpip.LinkAddress { // Multiple concurrent calls are permitted. func (e *Endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { n := 0 - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if err := e.q.Write(pkt); err != nil { if _, ok := err.(*tcpip.ErrNoBufferSpace); !ok && n == 0 { return 0, err diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go index a731d2d5e..e39a8c5d7 100644 --- a/pkg/tcpip/link/fdbased/endpoint.go +++ b/pkg/tcpip/link/fdbased/endpoint.go @@ -700,7 +700,7 @@ func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) batch := make([]stack.PacketBufferPtr, 0, BatchSize) batchFDInfo := fdInfo{fd: -1, isSocket: false} sentPackets := 0 - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if len(batch) == 0 { batchFDInfo = e.fds[pkt.Hash%uint32(len(e.fds))] } diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go index 85089e4e6..d7a02ebfe 100644 --- a/pkg/tcpip/link/loopback/loopback.go +++ b/pkg/tcpip/link/loopback/loopback.go @@ -87,7 +87,7 @@ func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) e.mu.RLock() d := e.dispatcher e.mu.RUnlock() - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { // In order to properly loop back to the inbound side we must create a // fresh packet that only contains the underlying payload with no headers // or struct fields set. diff --git a/pkg/tcpip/link/muxed/injectable.go b/pkg/tcpip/link/muxed/injectable.go index 014057892..421b6f409 100644 --- a/pkg/tcpip/link/muxed/injectable.go +++ b/pkg/tcpip/link/muxed/injectable.go @@ -102,14 +102,14 @@ func (m *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, // pkt.EgressRoute.RemoteAddress has a route registered in this endpoint. func (m *InjectableEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { i := 0 - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { endpoint, ok := m.routes[pkt.EgressRoute.RemoteAddress] if !ok { return i, &tcpip.ErrHostUnreachable{} } var tmpPkts stack.PacketBufferList - tmpPkts.PushBack(pkt) + tmpPkts.PushFront(pkt) n, err := endpoint.WritePackets(tmpPkts) if err != nil { diff --git a/pkg/tcpip/link/packetsocket/packetsocket.go b/pkg/tcpip/link/packetsocket/packetsocket.go index d309f6538..29dca2572 100644 --- a/pkg/tcpip/link/packetsocket/packetsocket.go +++ b/pkg/tcpip/link/packetsocket/packetsocket.go @@ -48,7 +48,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() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { e.Endpoint.DeliverLinkPacket(pkt.NetworkProtocolNumber, pkt) } diff --git a/pkg/tcpip/link/pipe/pipe.go b/pkg/tcpip/link/pipe/pipe.go index 789c62a0a..e0372de34 100644 --- a/pkg/tcpip/link/pipe/pipe.go +++ b/pkg/tcpip/link/pipe/pipe.go @@ -57,7 +57,7 @@ func (e *Endpoint) deliverPackets(pkts stack.PacketBufferList) { return } - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { // Create a fresh packet with pkt's payload but without struct fields // or headers set so the next link protocol can properly set the link // header. diff --git a/pkg/tcpip/link/qdisc/fifo/BUILD b/pkg/tcpip/link/qdisc/fifo/BUILD index 731606c6a..d354933d9 100644 --- a/pkg/tcpip/link/qdisc/fifo/BUILD +++ b/pkg/tcpip/link/qdisc/fifo/BUILD @@ -9,7 +9,6 @@ go_library( name = "fifo", srcs = [ "fifo.go", - "packet_buffer_circular_list.go", ], visibility = ["//visibility:public"], deps = [ diff --git a/pkg/tcpip/link/qdisc/fifo/fifo.go b/pkg/tcpip/link/qdisc/fifo/fifo.go index d8c8bb4f0..63ee1cff0 100644 --- a/pkg/tcpip/link/qdisc/fifo/fifo.go +++ b/pkg/tcpip/link/qdisc/fifo/fifo.go @@ -53,10 +53,13 @@ type discipline struct { // through the lower LinkWriter. type queueDispatcher struct { lower stack.LinkWriter + limit int mu sync.Mutex // +checklocks:mu - queue packetBufferCircularList + queue stack.PacketBufferList + // +checklocks:mu + used int newPacketWaker sleep.Waker closeWaker sleep.Waker @@ -64,8 +67,6 @@ type queueDispatcher struct { // New creates a new fifo queuing discipline with the n queues with maximum // capacity of queueLen. -// -// +checklocksignore: we don't have to hold locks during initialization. func New(lower stack.LinkWriter, n int, queueLen int) stack.QueueingDiscipline { d := &discipline{ dispatchers: make([]queueDispatcher, n), @@ -74,7 +75,7 @@ func New(lower stack.LinkWriter, n int, queueLen int) stack.QueueingDiscipline { for i := range d.dispatchers { qd := &d.dispatchers[i] qd.lower = lower - qd.queue.init(queueLen) + qd.limit = queueLen d.wg.Add(1) go func() { @@ -97,23 +98,28 @@ func (qd *queueDispatcher) dispatchLoop() { case &qd.newPacketWaker: case &qd.closeWaker: qd.mu.Lock() - for p := qd.queue.removeFront(); !p.IsNil(); p = qd.queue.removeFront() { + for p := qd.queue.Front(); !p.IsNil(); p = qd.queue.Front() { + qd.queue.Remove(p) p.DecRef() + qd.used-- } - qd.queue.decRef() + qd.queue.DecRef() qd.mu.Unlock() return default: panic("unknown waker") } qd.mu.Lock() - for pkt := qd.queue.removeFront(); !pkt.IsNil(); pkt = qd.queue.removeFront() { + for pkt := qd.queue.Front(); !pkt.IsNil(); pkt = qd.queue.Front() { + qd.queue.Remove(pkt) + qd.used-- batch.PushBack(pkt) - if batch.Len() < BatchSize && !qd.queue.isEmpty() { + if batch.Len() < BatchSize && qd.used != 0 { continue } qd.mu.Unlock() _, _ = qd.lower.WritePackets(batch) + batch.DecRef() batch.Reset() qd.mu.Lock() } @@ -133,9 +139,10 @@ func (d *discipline) WritePacket(pkt stack.PacketBufferPtr) tcpip.Error { } qd := &d.dispatchers[int(pkt.Hash)%len(d.dispatchers)] qd.mu.Lock() - haveSpace := qd.queue.hasSpace() + haveSpace := qd.used < qd.limit if haveSpace { - qd.queue.pushBack(pkt.IncRef()) + qd.queue.PushBack(pkt.IncRef()) + qd.used++ } qd.mu.Unlock() if !haveSpace { diff --git a/pkg/tcpip/link/qdisc/fifo/packet_buffer_circular_list.go b/pkg/tcpip/link/qdisc/fifo/packet_buffer_circular_list.go deleted file mode 100644 index 462ec058b..000000000 --- a/pkg/tcpip/link/qdisc/fifo/packet_buffer_circular_list.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2022 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 fifo - -import "gvisor.dev/gvisor/pkg/tcpip/stack" - -// packetBufferCircularList is a slice-backed circular list. All operations are -// O(1) unless otherwise noted. It only allocates once, during the call to -// init(). -// -// Users should call init() before using packetBufferCircularList. -// -// +stateify savable -type packetBufferCircularList struct { - pbs []stack.PacketBufferPtr - head int - size int -} - -// init initializes the list with the given size. -func (pl *packetBufferCircularList) init(size int) { - pl.pbs = make([]stack.PacketBufferPtr, size) -} - -// length returns the number of elements in the list. -// -//go:nosplit -func (pl *packetBufferCircularList) length() int { - return pl.size -} - -// hasSpace returns whether there is space left in the list. -// -//go:nosplit -func (pl *packetBufferCircularList) hasSpace() bool { - return pl.size < len(pl.pbs) -} - -// isEmpty returns whether the list is empty. -// -//go:nosplit -func (pl *packetBufferCircularList) isEmpty() bool { - return pl.size == 0 -} - -// pushBack inserts the PacketBuffer at the end of the list. -// -// Users must check beforehand that there is space via a call to hasSpace(). -// Failing to do so may clobber existing entries. -// -//go:nosplit -func (pl *packetBufferCircularList) pushBack(pb stack.PacketBufferPtr) { - next := (pl.head + pl.size) % len(pl.pbs) - pl.pbs[next] = pb - pl.size++ -} - -// removeFront returns the first element of the list or nil. -// -//go:nosplit -func (pl *packetBufferCircularList) removeFront() stack.PacketBufferPtr { - if pl.isEmpty() { - return nil - } - ret := pl.pbs[pl.head] - pl.pbs[pl.head] = nil - pl.head = (pl.head + 1) % len(pl.pbs) - pl.size-- - return ret -} - -// decRef decreases the reference count on each stack.PacketBuffer stored in -// the list. -// -// NOTE: runs in O(n) time. -// -//go:nosplit -func (pl *packetBufferCircularList) decRef() { - for i := 0; i < pl.size; i++ { - pl.pbs[(pl.head+i)%len(pl.pbs)].DecRef() - } -} diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index 1c4f08d24..3c9839e26 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -369,7 +369,7 @@ func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) var err tcpip.Error e.mu.Lock() defer e.mu.Unlock() - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if err = e.writePacketLocked(pkt.EgressRoute, pkt.NetworkProtocolNumber, pkt); err != nil { break } diff --git a/pkg/tcpip/link/sharedmem/sharedmem_server.go b/pkg/tcpip/link/sharedmem/sharedmem_server.go index 45421e237..efbf09a12 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_server.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_server.go @@ -256,7 +256,7 @@ func (e *serverEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.E var err tcpip.Error e.mu.Lock() defer e.mu.Unlock() - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if err = e.writePacketLocked(pkt.EgressRoute, pkt.NetworkProtocolNumber, pkt); err != nil { break } diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go index 470a57bc2..517da3de8 100644 --- a/pkg/tcpip/link/sniffer/sniffer.go +++ b/pkg/tcpip/link/sniffer/sniffer.go @@ -163,7 +163,7 @@ func (e *endpoint) dumpPacket(dir Direction, protocol tcpip.NetworkProtocolNumbe // higher-level protocols to write packets; it just logs the packet and // forwards the request to the lower endpoint. func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { e.dumpPacket(DirectionSend, pkt.NetworkProtocolNumber, pkt) } return e.Endpoint.WritePackets(pkts) diff --git a/pkg/tcpip/link/xdp/endpoint.go b/pkg/tcpip/link/xdp/endpoint.go index 515296feb..804da9656 100644 --- a/pkg/tcpip/link/xdp/endpoint.go +++ b/pkg/tcpip/link/xdp/endpoint.go @@ -279,7 +279,7 @@ func (ep *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) // Allocate UMEM space. In order to release the UMEM lock as soon as // possible, we allocate up-front and copy data in after releasing. - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { batch = append(batch, unix.XDPDesc{ Addr: ep.control.UMEM.AllocFrame(), Len: uint32(pkt.Size()), @@ -287,7 +287,8 @@ func (ep *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) } ep.control.UMEM.Unlock() - for i, pkt := range pkts.AsSlice() { + i := 0 + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { // Copy packets into UMEM frame. frame := ep.control.UMEM.Get(batch[i]) offset := 0 @@ -295,6 +296,7 @@ func (ep *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) offset += copy(frame[offset:], buf) } ep.control.TX.Set(index+uint32(i), batch[i]) + i++ } // Notify the kernel that there're packets to write. diff --git a/pkg/tcpip/network/internal/testutil/testutil.go b/pkg/tcpip/network/internal/testutil/testutil.go index 20113775c..93414b175 100644 --- a/pkg/tcpip/network/internal/testutil/testutil.go +++ b/pkg/tcpip/network/internal/testutil/testutil.go @@ -68,7 +68,7 @@ func (*MockLinkEndpoint) LinkAddress() tcpip.LinkAddress { return "" } // WritePackets implements LinkEndpoint.WritePackets. func (ep *MockLinkEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { var n int - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if ep.allowPackets == 0 { return n, ep.err } diff --git a/pkg/tcpip/stack/BUILD b/pkg/tcpip/stack/BUILD index 2ae482e38..d6940414b 100644 --- a/pkg/tcpip/stack/BUILD +++ b/pkg/tcpip/stack/BUILD @@ -160,6 +160,18 @@ go_template_instance( }, ) +go_template_instance( + name = "packet_buffer_list", + out = "packet_buffer_list.go", + package = "stack", + prefix = "PacketBuffer", + template = "//pkg/ilist:generic_list", + types = { + "Element": "*PacketBuffer", + "Linker": "*PacketBuffer", + }, +) + go_template_instance( name = "tuple_list", out = "tuple_list.go", diff --git a/pkg/tcpip/stack/forwarding_test.go b/pkg/tcpip/stack/forwarding_test.go index 3476bdb43..e5ceb7aff 100644 --- a/pkg/tcpip/stack/forwarding_test.go +++ b/pkg/tcpip/stack/forwarding_test.go @@ -306,7 +306,7 @@ func (e *fwdTestLinkEndpoint) LinkAddress() tcpip.LinkAddress { // WritePackets stores outbound packets into the channel. func (e *fwdTestLinkEndpoint) WritePackets(pkts PacketBufferList) (int, tcpip.Error) { n := 0 - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { select { case e.C <- pkt: default: diff --git a/pkg/tcpip/stack/packet_buffer.go b/pkg/tcpip/stack/packet_buffer.go index 9e8567c24..5b55449b1 100644 --- a/pkg/tcpip/stack/packet_buffer.go +++ b/pkg/tcpip/stack/packet_buffer.go @@ -108,6 +108,10 @@ type PacketBuffer struct { packetBufferRefs + // PacketBufferEntry is used to build an intrusive list of + // PacketBuffers. + PacketBufferEntry + // buf is the underlying buffer for the packet. See struct level docs for // details. buf bufferv2.Buffer @@ -366,6 +370,7 @@ func (pk PacketBufferPtr) headerView(typ headerType) bufferv2.View { func (pk PacketBufferPtr) Clone() PacketBufferPtr { newPk := pkPool.Get().(*PacketBuffer) newPk.reset() + newPk.PacketBufferEntry = pk.PacketBufferEntry newPk.buf = pk.buf.Clone() newPk.reserved = pk.reserved newPk.pushed = pk.pushed @@ -465,6 +470,28 @@ func (pk PacketBufferPtr) IsNil() bool { return pk == nil } +// IncRef increases the reference count on each PacketBuffer +// stored in the PacketBufferList. +func (pk *PacketBufferList) IncRef() { + for pb := pk.Front(); pb != nil; pb = pb.Next() { + pb.IncRef() + } +} + +// DecRef decreases the reference count on each PacketBuffer +// stored in the PacketBufferList. +func (pk *PacketBufferList) DecRef() { + // Using a while-loop here (instead of for-loop) because DecRef() can cause + // the pb to be recycled. If it is recycled during execution of this loop, + // there is a possibility of a data race during a call to pb.Next(). + pb := pk.Front() + for pb != nil { + next := pb.Next() + pb.DecRef() + pb = next + } +} + // headerInfo stores metadata about a header in a packet. // // +stateify savable diff --git a/pkg/tcpip/stack/packet_buffer_list.go b/pkg/tcpip/stack/packet_buffer_list.go deleted file mode 100644 index 31107c3ba..000000000 --- a/pkg/tcpip/stack/packet_buffer_list.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2022 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 stack - -// PacketBufferList is a slice-backed list. All operations are O(1) unless -// otherwise noted. It is optimized to for zero allocations when used with a -// queueing discipline. -// -// Users should call Init() before using PacketBufferList. -// -// +stateify savable -type PacketBufferList struct { - pbs []PacketBufferPtr -} - -// AsSlice returns a slice containing the packets in the list. -// -//go:nosplit -func (pl *PacketBufferList) AsSlice() []PacketBufferPtr { - return pl.pbs -} - -// Reset decrements all elements and resets the list to the empty state. -// -//go:nosplit -func (pl *PacketBufferList) Reset() { - for i, pb := range pl.pbs { - pb.DecRef() - pl.pbs[i] = nil - } - pl.pbs = pl.pbs[:0] -} - -// Len returns the number of elements in the list. -// -//go:nosplit -func (pl *PacketBufferList) Len() int { - return len(pl.pbs) -} - -// PushBack inserts the PacketBuffer at the back of the list. -// -//go:nosplit -func (pl *PacketBufferList) PushBack(pb PacketBufferPtr) { - pl.pbs = append(pl.pbs, pb) -} - -// DecRef decreases the reference count on each PacketBuffer -// stored in the list. -// -// NOTE: runs in O(n) time. -// -//go:nosplit -func (pl PacketBufferList) DecRef() { - for _, pb := range pl.pbs { - pb.DecRef() - } -} diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go index 26a858257..37c0a2218 100644 --- a/pkg/tcpip/tests/integration/iptables_test.go +++ b/pkg/tcpip/tests/integration/iptables_test.go @@ -632,7 +632,10 @@ func TestIPTableWritePackets(t *testing.T) { defer r.Release() pkts := test.genPacket(r) - for _, pkt := range pkts.AsSlice() { + pktsLen := pkts.Len() + for i := 0; i < pktsLen; i++ { + pkt := pkts.Front() + pkts.Remove(pkt) if err := r.WritePacket(stack.NetworkHeaderParams{ Protocol: header.UDPProtocolNumber, TTL: 64, diff --git a/pkg/tcpip/tests/integration/link_resolution_test.go b/pkg/tcpip/tests/integration/link_resolution_test.go index bceae181d..aee22e1eb 100644 --- a/pkg/tcpip/tests/integration/link_resolution_test.go +++ b/pkg/tcpip/tests/integration/link_resolution_test.go @@ -1698,7 +1698,7 @@ func newMonitorableLinkEndpoint(e stack.LinkEndpoint) *monitorableLinkEndpoint { } func (e *monitorableLinkEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { dstAddr := header.Ethernet(pkt.LinkHeader().Slice()).DestinationAddress() e.ch <- dstAddr } diff --git a/pkg/tcpip/transport/datagram_test.go b/pkg/tcpip/transport/datagram_test.go index 2d280a6da..17047418b 100644 --- a/pkg/tcpip/transport/datagram_test.go +++ b/pkg/tcpip/transport/datagram_test.go @@ -158,7 +158,7 @@ func (e *mockEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Err } len := pkts.Len() - for _, pkt := range pkts.AsSlice() { + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { e.pkts.PushBack(pkt.IncRef()) } @@ -176,7 +176,7 @@ func (e *mockEndpoint) releasePackets() { func (e *mockEndpoint) pktsSize() int { s := 0 - for _, pkt := range e.pkts.AsSlice() { + for pkt := e.pkts.Front(); pkt != nil; pkt = pkt.Next() { s += pkt.Size() + pkt.AvailableHeaderBytes() } return s