mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
5818663ebe
commit
fc99a7ebf0
+10
-3
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 = ðHdrBuf[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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user