Refactor software GSO code.

Software GSO implementation currently has a complicated code path with
implicit assumptions that all packets to WritePackets carry same Data
and it does this to avoid allocations on the path etc. But this makes it
hard to reuse the WritePackets API.

This change breaks all such assumptions by introducing a new Vectorised
View API ReadToVV which can be used to cleanly split a VV into multiple
independent VVs. Further this change also makes packet buffers linkable
to form an intrusive list. This allows us to get rid of the array of
packet buffers that are passed in the WritePackets API call and replace
it with a list of packet buffers.

While this code does introduce some more allocations in the benchmarks
it doesn't cause any degradation.

Updates #231

PiperOrigin-RevId: 304731742
This commit is contained in:
Bhasker Hariharan
2020-04-03 18:35:55 -07:00
committed by gVisor bot
parent 5818663ebe
commit fc99a7ebf0
28 changed files with 420 additions and 230 deletions
+10 -3
View File
@@ -86,12 +86,21 @@ func (l *List) Back() Element {
return l.tail
}
// Len returns the number of elements in the list.
//
// NOTE: This is an O(n) operation.
func (l *List) Len() (count int) {
for e := l.Front(); e != nil; e = e.Next() {
count++
}
return count
}
// PushFront inserts the element e at the front of list l.
func (l *List) PushFront(e Element) {
linker := ElementMapper{}.linkerFor(e)
linker.SetNext(l.head)
linker.SetPrev(nil)
if l.head != nil {
ElementMapper{}.linkerFor(l.head).SetPrev(e)
} else {
@@ -106,7 +115,6 @@ func (l *List) PushBack(e Element) {
linker := ElementMapper{}.linkerFor(e)
linker.SetNext(nil)
linker.SetPrev(l.tail)
if l.tail != nil {
ElementMapper{}.linkerFor(l.tail).SetNext(e)
} else {
@@ -127,7 +135,6 @@ func (l *List) PushBackList(m *List) {
l.tail = m.tail
}
m.head = nil
m.tail = nil
}
+16 -6
View File
@@ -564,15 +564,25 @@ func (ts *TaskSet) unregisterEpollWaiters() {
ts.mu.RLock()
defer ts.mu.RUnlock()
// Tasks that belong to the same process could potentially point to the
// same FDTable. So we retain a map of processed ones to avoid
// processing the same FDTable multiple times.
processed := make(map[*FDTable]struct{})
for t := range ts.Root.tids {
// We can skip locking Task.mu here since the kernel is paused.
if t.fdTable != nil {
t.fdTable.forEach(func(_ int32, file *fs.File, _ *vfs.FileDescription, _ FDFlags) {
if e, ok := file.FileOperations.(*epoll.EventPoll); ok {
e.UnregisterEpollWaiters()
}
})
if t.fdTable == nil {
continue
}
if _, ok := processed[t.fdTable]; ok {
continue
}
t.fdTable.forEach(func(_ int32, file *fs.File, _ *vfs.FileDescription, _ FDFlags) {
if e, ok := file.FileOperations.(*epoll.EventPoll); ok {
e.UnregisterEpollWaiters()
}
})
processed[t.fdTable] = struct{}{}
}
}
+48 -5
View File
@@ -17,6 +17,7 @@ package buffer
import (
"bytes"
"io"
)
// View is a slice of a buffer, with convenience methods.
@@ -89,6 +90,47 @@ func (vv *VectorisedView) TrimFront(count int) {
}
}
// Read implements io.Reader.
func (vv *VectorisedView) Read(v View) (copied int, err error) {
count := len(v)
for count > 0 && len(vv.views) > 0 {
if count < len(vv.views[0]) {
vv.size -= count
copy(v[copied:], vv.views[0][:count])
vv.views[0].TrimFront(count)
copied += count
return copied, nil
}
count -= len(vv.views[0])
copy(v[copied:], vv.views[0])
copied += len(vv.views[0])
vv.RemoveFirst()
}
if copied == 0 {
return 0, io.EOF
}
return copied, nil
}
// ReadToVV reads up to n bytes from vv to dstVV and removes them from vv. It
// returns the number of bytes copied.
func (vv *VectorisedView) ReadToVV(dstVV *VectorisedView, count int) (copied int) {
for count > 0 && len(vv.views) > 0 {
if count < len(vv.views[0]) {
vv.size -= count
dstVV.AppendView(vv.views[0][:count])
vv.views[0].TrimFront(count)
copied += count
return
}
count -= len(vv.views[0])
dstVV.AppendView(vv.views[0])
copied += len(vv.views[0])
vv.RemoveFirst()
}
return copied
}
// CapLength irreversibly reduces the length of the vectorised view.
func (vv *VectorisedView) CapLength(length int) {
if length < 0 {
@@ -116,12 +158,12 @@ func (vv *VectorisedView) CapLength(length int) {
// Clone returns a clone of this VectorisedView.
// If the buffer argument is large enough to contain all the Views of this VectorisedView,
// the method will avoid allocations and use the buffer to store the Views of the clone.
func (vv VectorisedView) Clone(buffer []View) VectorisedView {
func (vv *VectorisedView) Clone(buffer []View) VectorisedView {
return VectorisedView{views: append(buffer[:0], vv.views...), size: vv.size}
}
// First returns the first view of the vectorised view.
func (vv VectorisedView) First() View {
func (vv *VectorisedView) First() View {
if len(vv.views) == 0 {
return nil
}
@@ -134,11 +176,12 @@ func (vv *VectorisedView) RemoveFirst() {
return
}
vv.size -= len(vv.views[0])
vv.views[0] = nil
vv.views = vv.views[1:]
}
// Size returns the size in bytes of the entire content stored in the vectorised view.
func (vv VectorisedView) Size() int {
func (vv *VectorisedView) Size() int {
return vv.size
}
@@ -146,7 +189,7 @@ func (vv VectorisedView) Size() int {
//
// If the vectorised view contains a single view, that view will be returned
// directly.
func (vv VectorisedView) ToView() View {
func (vv *VectorisedView) ToView() View {
if len(vv.views) == 1 {
return vv.views[0]
}
@@ -158,7 +201,7 @@ func (vv VectorisedView) ToView() View {
}
// Views returns the slice containing the all views.
func (vv VectorisedView) Views() []View {
func (vv *VectorisedView) Views() []View {
return vv.views
}
+137
View File
@@ -233,3 +233,140 @@ func TestToClone(t *testing.T) {
})
}
}
func TestVVReadToVV(t *testing.T) {
testCases := []struct {
comment string
vv VectorisedView
bytesToRead int
wantBytes string
leftVV VectorisedView
}{
{
comment: "large VV, short read",
vv: vv(30, "012345678901234567890123456789"),
bytesToRead: 10,
wantBytes: "0123456789",
leftVV: vv(20, "01234567890123456789"),
},
{
comment: "largeVV, multiple views, short read",
vv: vv(13, "123", "345", "567", "8910"),
bytesToRead: 6,
wantBytes: "123345",
leftVV: vv(7, "567", "8910"),
},
{
comment: "smallVV (multiple views), large read",
vv: vv(3, "1", "2", "3"),
bytesToRead: 10,
wantBytes: "123",
leftVV: vv(0, ""),
},
{
comment: "smallVV (single view), large read",
vv: vv(1, "1"),
bytesToRead: 10,
wantBytes: "1",
leftVV: vv(0, ""),
},
{
comment: "emptyVV, large read",
vv: vv(0, ""),
bytesToRead: 10,
wantBytes: "",
leftVV: vv(0, ""),
},
}
for _, tc := range testCases {
t.Run(tc.comment, func(t *testing.T) {
var readTo VectorisedView
inSize := tc.vv.Size()
copied := tc.vv.ReadToVV(&readTo, tc.bytesToRead)
if got, want := copied, len(tc.wantBytes); got != want {
t.Errorf("incorrect number of bytes copied returned in ReadToVV got: %d, want: %d, tc: %+v", got, want, tc)
}
if got, want := string(readTo.ToView()), tc.wantBytes; got != want {
t.Errorf("unexpected content in readTo got: %s, want: %s", got, want)
}
if got, want := tc.vv.Size(), inSize-copied; got != want {
t.Errorf("test VV has incorrect size after reading got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
}
if got, want := string(tc.vv.ToView()), string(tc.leftVV.ToView()); got != want {
t.Errorf("unexpected data left in vv after read got: %+v, want: %+v", got, want)
}
})
}
}
func TestVVRead(t *testing.T) {
testCases := []struct {
comment string
vv VectorisedView
bytesToRead int
readBytes string
leftBytes string
wantError bool
}{
{
comment: "large VV, short read",
vv: vv(30, "012345678901234567890123456789"),
bytesToRead: 10,
readBytes: "0123456789",
leftBytes: "01234567890123456789",
},
{
comment: "largeVV, multiple buffers, short read",
vv: vv(13, "123", "345", "567", "8910"),
bytesToRead: 6,
readBytes: "123345",
leftBytes: "5678910",
},
{
comment: "smallVV, large read",
vv: vv(3, "1", "2", "3"),
bytesToRead: 10,
readBytes: "123",
leftBytes: "",
},
{
comment: "smallVV, large read",
vv: vv(1, "1"),
bytesToRead: 10,
readBytes: "1",
leftBytes: "",
},
{
comment: "emptyVV, large read",
vv: vv(0, ""),
bytesToRead: 10,
readBytes: "",
wantError: true,
},
}
for _, tc := range testCases {
t.Run(tc.comment, func(t *testing.T) {
readTo := NewView(tc.bytesToRead)
inSize := tc.vv.Size()
copied, err := tc.vv.Read(readTo)
if !tc.wantError && err != nil {
t.Fatalf("unexpected error in tc.vv.Read(..) = %s", err)
}
readTo = readTo[:copied]
if got, want := copied, len(tc.readBytes); got != want {
t.Errorf("incorrect number of bytes copied returned in ReadToVV got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
}
if got, want := string(readTo), tc.readBytes; got != want {
t.Errorf("unexpected data in readTo got: %s, want: %s", got, want)
}
if got, want := tc.vv.Size(), inSize-copied; got != want {
t.Errorf("test VV has incorrect size after reading got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
}
if got, want := string(tc.vv.ToView()), tc.leftBytes; got != want {
t.Errorf("vv has incorrect data after Read got: %s, want: %s", got, want)
}
})
}
}
+6 -12
View File
@@ -28,7 +28,7 @@ import (
// PacketInfo holds all the information about an outbound packet.
type PacketInfo struct {
Pkt stack.PacketBuffer
Pkt *stack.PacketBuffer
Proto tcpip.NetworkProtocolNumber
GSO *stack.GSO
Route stack.Route
@@ -257,7 +257,7 @@ func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
route := r.Clone()
route.Release()
p := PacketInfo{
Pkt: pkt,
Pkt: &pkt,
Proto: protocol,
GSO: gso,
Route: route,
@@ -269,21 +269,15 @@ func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
}
// WritePackets stores outbound packets into the channel.
func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
// Clone r then release its resource so we only get the relevant fields from
// stack.Route without holding a reference to a NIC's endpoint.
route := r.Clone()
route.Release()
payloadView := pkts[0].Data.ToView()
n := 0
for _, pkt := range pkts {
off := pkt.DataOffset
size := pkt.DataSize
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
p := PacketInfo{
Pkt: stack.PacketBuffer{
Header: pkt.Header,
Data: buffer.NewViewFromBytes(payloadView[off : off+size]).ToVectorisedView(),
},
Pkt: pkt,
Proto: protocol,
GSO: gso,
Route: route,
@@ -301,7 +295,7 @@ func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.Pac
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
func (e *Endpoint) WriteRawPacket(vv buffer.VectorisedView) *tcpip.Error {
p := PacketInfo{
Pkt: stack.PacketBuffer{Data: vv},
Pkt: &stack.PacketBuffer{Data: vv},
Proto: 0,
GSO: nil,
}
+75 -87
View File
@@ -441,118 +441,106 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
// WritePackets writes outbound packets to the file descriptor. If it is not
// currently writable, the packet is dropped.
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
var ethHdrBuf []byte
// hdr + data
iovLen := 2
if e.hdrSize > 0 {
// Add ethernet header if needed.
ethHdrBuf = make([]byte, header.EthernetMinimumSize)
eth := header.Ethernet(ethHdrBuf)
ethHdr := &header.EthernetFields{
DstAddr: r.RemoteLinkAddress,
Type: protocol,
}
//
// NOTE: This API uses sendmmsg to batch packets. As a result the underlying FD
// picked to write the packet out has to be the same for all packets in the
// list. In other words all packets in the batch should belong to the same
// flow.
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
n := pkts.Len()
// Preserve the src address if it's set in the route.
if r.LocalLinkAddress != "" {
ethHdr.SrcAddr = r.LocalLinkAddress
} else {
ethHdr.SrcAddr = e.addr
}
eth.Encode(ethHdr)
iovLen++
}
n := len(pkts)
views := pkts[0].Data.Views()
/*
* Each boundary in views can add one more iovec.
*
* payload | | | |
* -----------------------------
* packets | | | | | | |
* -----------------------------
* iovecs | | | | | | | | |
*/
iovec := make([]syscall.Iovec, n*iovLen+len(views)-1)
mmsgHdrs := make([]rawfile.MMsgHdr, n)
i := 0
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
var ethHdrBuf []byte
iovLen := 0
if e.hdrSize > 0 {
// Add ethernet header if needed.
ethHdrBuf = make([]byte, header.EthernetMinimumSize)
eth := header.Ethernet(ethHdrBuf)
ethHdr := &header.EthernetFields{
DstAddr: r.RemoteLinkAddress,
Type: protocol,
}
iovecIdx := 0
viewIdx := 0
viewOff := 0
off := 0
nextOff := 0
for i := range pkts {
// TODO(b/134618279): Different packets may have different data
// in the future. We should handle this.
if !viewsEqual(pkts[i].Data.Views(), views) {
panic("All packets in pkts should have the same Data.")
// Preserve the src address if it's set in the route.
if r.LocalLinkAddress != "" {
ethHdr.SrcAddr = r.LocalLinkAddress
} else {
ethHdr.SrcAddr = e.addr
}
eth.Encode(ethHdr)
iovLen++
}
prevIovecIdx := iovecIdx
mmsgHdr := &mmsgHdrs[i]
mmsgHdr.Msg.Iov = &iovec[iovecIdx]
packetSize := pkts[i].DataSize
hdr := &pkts[i].Header
off = pkts[i].DataOffset
if off != nextOff {
// We stop in a different point last time.
size := packetSize
viewIdx = 0
viewOff = 0
for size > 0 {
if size >= len(views[viewIdx]) {
viewIdx++
viewOff = 0
size -= len(views[viewIdx])
} else {
viewOff = size
size = 0
var vnetHdrBuf []byte
vnetHdr := virtioNetHdr{}
if e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
if gso != nil {
vnetHdr.hdrLen = uint16(pkt.Header.UsedLength())
if gso.NeedsCsum {
vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
vnetHdr.csumStart = header.EthernetMinimumSize + gso.L3HdrLen
vnetHdr.csumOffset = gso.CsumOffset
}
if gso.Type != stack.GSONone && uint16(pkt.Data.Size()) > gso.MSS {
switch gso.Type {
case stack.GSOTCPv4:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
case stack.GSOTCPv6:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
default:
panic(fmt.Sprintf("Unknown gso type: %v", gso.Type))
}
vnetHdr.gsoSize = gso.MSS
}
}
vnetHdrBuf = vnetHdrToByteSlice(&vnetHdr)
iovLen++
}
nextOff = off + packetSize
iovecs := make([]syscall.Iovec, iovLen+1+len(pkt.Data.Views()))
mmsgHdr := &mmsgHdrs[i]
mmsgHdr.Msg.Iov = &iovecs[0]
iovecIdx := 0
if vnetHdrBuf != nil {
v := &iovecs[iovecIdx]
v.Base = &vnetHdrBuf[0]
v.Len = uint64(len(vnetHdrBuf))
iovecIdx++
}
if ethHdrBuf != nil {
v := &iovec[iovecIdx]
v := &iovecs[iovecIdx]
v.Base = &ethHdrBuf[0]
v.Len = uint64(len(ethHdrBuf))
iovecIdx++
}
v := &iovec[iovecIdx]
pktSize := uint64(0)
// Encode L3 Header
v := &iovecs[iovecIdx]
hdr := &pkt.Header
hdrView := hdr.View()
v.Base = &hdrView[0]
v.Len = uint64(len(hdrView))
pktSize += v.Len
iovecIdx++
for packetSize > 0 {
vec := &iovec[iovecIdx]
// Now encode the Transport Payload.
pktViews := pkt.Data.Views()
for i := range pktViews {
vec := &iovecs[iovecIdx]
iovecIdx++
v := views[viewIdx]
vec.Base = &v[viewOff]
s := len(v) - viewOff
if s <= packetSize {
viewIdx++
viewOff = 0
} else {
s = packetSize
viewOff += s
}
vec.Len = uint64(s)
packetSize -= s
vec.Base = &pktViews[i][0]
vec.Len = uint64(len(pktViews[i]))
pktSize += vec.Len
}
mmsgHdr.Msg.Iovlen = uint64(iovecIdx - prevIovecIdx)
mmsgHdr.Msg.Iovlen = uint64(iovecIdx)
i++
}
packets := 0
for packets < n {
fd := e.fds[pkts[packets].Hash%uint32(len(e.fds))]
fd := e.fds[pkts.Front().Hash%uint32(len(e.fds))]
sent, err := rawfile.NonBlockingSendMMsg(fd, mmsgHdrs)
if err != nil {
return packets, err
+1 -1
View File
@@ -92,7 +92,7 @@ func (e *endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, protocol tcpip.Netw
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (e *endpoint) WritePackets(*stack.Route, *stack.GSO, []stack.PacketBuffer, tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (e *endpoint) WritePackets(*stack.Route, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
panic("not implemented")
}
+1 -1
View File
@@ -87,7 +87,7 @@ func (m *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber,
// WritePackets writes outbound packets to the appropriate
// LinkInjectableEndpoint based on the RemoteAddress. HandleLocal only works if
// r.RemoteAddress has a route registered in this endpoint.
func (m *InjectableEndpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (m *InjectableEndpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
endpoint, ok := m.routes[r.RemoteAddress]
if !ok {
return 0, tcpip.ErrNoRoute
+1 -1
View File
@@ -214,7 +214,7 @@ func (e *endpoint) WritePacket(r *stack.Route, _ *stack.GSO, protocol tcpip.Netw
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (e *endpoint) WritePackets(r *stack.Route, _ *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (e *endpoint) WritePackets(r *stack.Route, _ *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
panic("not implemented")
}
+5 -9
View File
@@ -200,7 +200,7 @@ func (e *endpoint) GSOMaxSize() uint32 {
return 0
}
func (e *endpoint) dumpPacket(gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBuffer) {
func (e *endpoint) dumpPacket(gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
logPacket("send", protocol, pkt.Header.View(), gso)
}
@@ -233,20 +233,16 @@ func (e *endpoint) dumpPacket(gso *stack.GSO, protocol tcpip.NetworkProtocolNumb
// higher-level protocols to write packets; it just logs the packet and
// forwards the request to the lower endpoint.
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBuffer) *tcpip.Error {
e.dumpPacket(gso, protocol, pkt)
e.dumpPacket(gso, protocol, &pkt)
return e.lower.WritePacket(r, gso, protocol, pkt)
}
// WritePackets implements the stack.LinkEndpoint interface. It is called by
// higher-level protocols to write packets; it just logs the packet and
// forwards the request to the lower endpoint.
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
view := pkts[0].Data.ToView()
for _, pkt := range pkts {
e.dumpPacket(gso, protocol, stack.PacketBuffer{
Header: pkt.Header,
Data: view[pkt.DataOffset:][:pkt.DataSize].ToVectorisedView(),
})
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
e.dumpPacket(gso, protocol, pkt)
}
return e.lower.WritePackets(r, gso, pkts, protocol)
}
+2 -2
View File
@@ -112,9 +112,9 @@ func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
// WritePackets implements stack.LinkEndpoint.WritePackets. It is called by
// higher-level protocols to write packets. It only forwards packets to the
// lower endpoint if Wait or WaitWrite haven't been called.
func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
if !e.writeGate.Enter() {
return len(pkts), nil
return pkts.Len(), nil
}
n, err := e.lower.WritePackets(r, gso, pkts, protocol)
+3 -3
View File
@@ -71,9 +71,9 @@ func (e *countedEndpoint) WritePacket(r *stack.Route, _ *stack.GSO, protocol tcp
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (e *countedEndpoint) WritePackets(r *stack.Route, _ *stack.GSO, pkts []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
e.writeCount += len(pkts)
return len(pkts), nil
func (e *countedEndpoint) WritePackets(r *stack.Route, _ *stack.GSO, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
e.writeCount += pkts.Len()
return pkts.Len(), nil
}
func (e *countedEndpoint) WriteRawPacket(buffer.VectorisedView) *tcpip.Error {
+1 -1
View File
@@ -84,7 +84,7 @@ func (e *endpoint) WritePacket(*stack.Route, *stack.GSO, stack.NetworkHeaderPara
}
// WritePackets implements stack.NetworkEndpoint.WritePackets.
func (e *endpoint) WritePackets(*stack.Route, *stack.GSO, []stack.PacketBuffer, stack.NetworkHeaderParams) (int, *tcpip.Error) {
func (e *endpoint) WritePackets(*stack.Route, *stack.GSO, stack.PacketBufferList, stack.NetworkHeaderParams) (int, *tcpip.Error) {
return 0, tcpip.ErrNotSupported
}
+1 -1
View File
@@ -172,7 +172,7 @@ func (t *testObject) WritePacket(_ *stack.Route, _ *stack.GSO, protocol tcpip.Ne
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (t *testObject) WritePackets(_ *stack.Route, _ *stack.GSO, pkt []stack.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (t *testObject) WritePackets(_ *stack.Route, _ *stack.GSO, pkt stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
panic("not implemented")
}
+28 -9
View File
@@ -280,28 +280,47 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, params stack.Netw
}
// WritePackets implements stack.NetworkEndpoint.WritePackets.
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, params stack.NetworkHeaderParams) (int, *tcpip.Error) {
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, params stack.NetworkHeaderParams) (int, *tcpip.Error) {
if r.Loop&stack.PacketLoop != 0 {
panic("multiple packets in local loop")
}
if r.Loop&stack.PacketOut == 0 {
return len(pkts), nil
return pkts.Len(), nil
}
for pkt := pkts.Front(); pkt != nil; {
ip := e.addIPHeader(r, &pkt.Header, pkt.Data.Size(), params)
pkt.NetworkHeader = buffer.View(ip)
pkt = pkt.Next()
}
// iptables filtering. All packets that reach here are locally
// generated.
ipt := e.stack.IPTables()
for i := range pkts {
if ok := ipt.Check(stack.Output, pkts[i]); !ok {
// iptables is telling us to drop the packet.
dropped := ipt.CheckPackets(stack.Output, pkts)
if len(dropped) == 0 {
// Fast path: If no packets are to be dropped then we can just invoke the
// faster WritePackets API directly.
n, err := e.linkEP.WritePackets(r, gso, pkts, ProtocolNumber)
r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
return n, err
}
// Slow Path as we are dropping some packets in the batch degrade to
// emitting one packet at a time.
n := 0
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
if _, ok := dropped[pkt]; ok {
continue
}
ip := e.addIPHeader(r, &pkts[i].Header, pkts[i].DataSize, params)
pkts[i].NetworkHeader = buffer.View(ip)
if err := e.linkEP.WritePacket(r, gso, ProtocolNumber, *pkt); err != nil {
r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
return n, err
}
n++
}
n, err := e.linkEP.WritePackets(r, gso, pkts, ProtocolNumber)
r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
return n, err
return n, nil
}
// WriteHeaderIncludedPacket writes a packet already containing a network
+1 -1
View File
@@ -79,7 +79,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, pkt stack.P
// Only the first view in vv is accounted for by h. To account for the
// rest of vv, a shallow copy is made and the first view is removed.
// This copy is used as extra payload during the checksum calculation.
payload := pkt.Data
payload := pkt.Data.Clone(nil)
payload.RemoveFirst()
if got, want := h.Checksum(), header.ICMPv6Checksum(h, iph.SourceAddress(), iph.DestinationAddress(), payload); got != want {
received.Invalid.Increment()
+5 -7
View File
@@ -143,19 +143,17 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, params stack.Netw
}
// WritePackets implements stack.LinkEndpoint.WritePackets.
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.PacketBuffer, params stack.NetworkHeaderParams) (int, *tcpip.Error) {
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.PacketBufferList, params stack.NetworkHeaderParams) (int, *tcpip.Error) {
if r.Loop&stack.PacketLoop != 0 {
panic("not implemented")
}
if r.Loop&stack.PacketOut == 0 {
return len(pkts), nil
return pkts.Len(), nil
}
for i := range pkts {
hdr := &pkts[i].Header
size := pkts[i].DataSize
ip := e.addIPHeader(r, hdr, size, params)
pkts[i].NetworkHeader = buffer.View(ip)
for pb := pkts.Front(); pb != nil; pb = pb.Next() {
ip := e.addIPHeader(r, &pb.Header, pb.Data.Size(), params)
pb.NetworkHeader = buffer.View(ip)
}
n, err := e.linkEP.WritePackets(r, gso, pkts, ProtocolNumber)
+13 -1
View File
@@ -15,6 +15,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_library(
name = "stack",
srcs = [
@@ -29,7 +41,7 @@ go_library(
"ndp.go",
"nic.go",
"packet_buffer.go",
"packet_buffer_state.go",
"packet_buffer_list.go",
"rand.go",
"registration.go",
"route.go",
+4 -4
View File
@@ -101,7 +101,7 @@ func (f *fwdTestNetworkEndpoint) WritePacket(r *Route, gso *GSO, params NetworkH
}
// WritePackets implements LinkEndpoint.WritePackets.
func (f *fwdTestNetworkEndpoint) WritePackets(r *Route, gso *GSO, pkts []PacketBuffer, params NetworkHeaderParams) (int, *tcpip.Error) {
func (f *fwdTestNetworkEndpoint) WritePackets(r *Route, gso *GSO, pkts PacketBufferList, params NetworkHeaderParams) (int, *tcpip.Error) {
panic("not implemented")
}
@@ -260,10 +260,10 @@ func (e fwdTestLinkEndpoint) WritePacket(r *Route, gso *GSO, protocol tcpip.Netw
}
// WritePackets stores outbound packets into the channel.
func (e *fwdTestLinkEndpoint) WritePackets(r *Route, gso *GSO, pkts []PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
func (e *fwdTestLinkEndpoint) WritePackets(r *Route, gso *GSO, pkts PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
n := 0
for _, pkt := range pkts {
e.WritePacket(r, gso, protocol, pkt)
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
e.WritePacket(r, gso, protocol, *pkt)
n++
}
+17
View File
@@ -209,6 +209,23 @@ func (it *IPTables) Check(hook Hook, pkt PacketBuffer) bool {
return true
}
// CheckPackets runs pkts through the rules for hook and returns a map of packets that
// should not go forward.
//
// NOTE: unlike the Check API the returned map contains packets that should be
// dropped.
func (it *IPTables) CheckPackets(hook Hook, pkts PacketBufferList) (drop map[*PacketBuffer]struct{}) {
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
if ok := it.Check(hook, *pkt); !ok {
if drop == nil {
drop = make(map[*PacketBuffer]struct{})
}
drop[pkt] = struct{}{}
}
}
return drop
}
// Precondition: pkt.NetworkHeader is set.
func (it *IPTables) checkChain(hook Hook, pkt PacketBuffer, table Table, ruleIdx int) chainVerdict {
// Start from ruleIdx and walk the list of rules until a rule gives us

Some files were not shown because too many files have changed in this diff Show More