mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack/tcp: software segmentation offload
Right now, we send each tcp packet separately, we call one system call per-packet. This patch allows to generate multiple tcp packets and send them by sendmmsg. The arguable part of this CL is a way how to handle multiple headers. This CL adds the next field to the Prepandable buffer. Nginx test results: Server Software: nginx/1.15.9 Server Hostname: 10.138.0.2 Server Port: 8080 Document Path: /10m.txt Document Length: 10485760 bytes w/o gso: Concurrency Level: 5 Time taken for tests: 5.491 seconds Complete requests: 100 Failed requests: 0 Total transferred: 1048600200 bytes HTML transferred: 1048576000 bytes Requests per second: 18.21 [#/sec] (mean) Time per request: 274.525 [ms] (mean) Time per request: 54.905 [ms] (mean, across all concurrent requests) Transfer rate: 186508.03 [Kbytes/sec] received sw-gso: Concurrency Level: 5 Time taken for tests: 3.852 seconds Complete requests: 100 Failed requests: 0 Total transferred: 1048600200 bytes HTML transferred: 1048576000 bytes Requests per second: 25.96 [#/sec] (mean) Time per request: 192.576 [ms] (mean) Time per request: 38.515 [ms] (mean, across all concurrent requests) Transfer rate: 265874.92 [Kbytes/sec] received w/o gso: $ ./tcp_benchmark --client --duration 15 --ideal [SUM] 0.0-15.1 sec 2.20 GBytes 1.25 Gbits/sec software gso: $ tcp_benchmark --client --duration 15 --ideal --gso $((1<<16)) --swgso [SUM] 0.0-15.1 sec 3.99 GBytes 2.26 Gbits/sec PiperOrigin-RevId: 276112677
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
build_file: "repo/scripts/swgso_tests.sh"
|
||||
|
||||
action {
|
||||
define_artifacts {
|
||||
regex: "**/sponge_log.xml"
|
||||
regex: "**/sponge_log.log"
|
||||
regex: "**/outputs.zip"
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,11 @@ func NewPrependableFromView(v View) Prependable {
|
||||
return Prependable{buf: v, usedIdx: 0}
|
||||
}
|
||||
|
||||
// NewEmptyPrependableFromView creates a new prependable buffer from a View.
|
||||
func NewEmptyPrependableFromView(v View) Prependable {
|
||||
return Prependable{buf: v, usedIdx: len(v)}
|
||||
}
|
||||
|
||||
// View returns a View of the backing buffer that contains all prepended
|
||||
// data so far.
|
||||
func (p Prependable) View() View {
|
||||
|
||||
@@ -36,10 +36,14 @@ go_test(
|
||||
name = "header_x_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"checksum_test.go",
|
||||
"ipversion_test.go",
|
||||
"tcp_test.go",
|
||||
],
|
||||
deps = [":header"],
|
||||
deps = [
|
||||
":header",
|
||||
"//pkg/tcpip/buffer",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
|
||||
@@ -23,11 +23,17 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
)
|
||||
|
||||
func calculateChecksum(buf []byte, initial uint32) uint16 {
|
||||
func calculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
|
||||
v := initial
|
||||
|
||||
if odd {
|
||||
v += uint32(buf[0])
|
||||
buf = buf[1:]
|
||||
}
|
||||
|
||||
l := len(buf)
|
||||
if l&1 != 0 {
|
||||
odd = l&1 != 0
|
||||
if odd {
|
||||
l--
|
||||
v += uint32(buf[l]) << 8
|
||||
}
|
||||
@@ -36,7 +42,7 @@ func calculateChecksum(buf []byte, initial uint32) uint16 {
|
||||
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
|
||||
}
|
||||
|
||||
return ChecksumCombine(uint16(v), uint16(v>>16))
|
||||
return ChecksumCombine(uint16(v), uint16(v>>16)), odd
|
||||
}
|
||||
|
||||
// Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
|
||||
@@ -44,7 +50,8 @@ func calculateChecksum(buf []byte, initial uint32) uint16 {
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func Checksum(buf []byte, initial uint16) uint16 {
|
||||
return calculateChecksum(buf, uint32(initial))
|
||||
s, _ := calculateChecksum(buf, false, uint32(initial))
|
||||
return s
|
||||
}
|
||||
|
||||
// ChecksumVV calculates the checksum (as defined in RFC 1071) of the bytes in
|
||||
@@ -52,19 +59,40 @@ func Checksum(buf []byte, initial uint16) uint16 {
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func ChecksumVV(vv buffer.VectorisedView, initial uint16) uint16 {
|
||||
var odd bool
|
||||
return ChecksumVVWithOffset(vv, initial, 0, vv.Size())
|
||||
}
|
||||
|
||||
// ChecksumVVWithOffset calculates the checksum (as defined in RFC 1071) of the
|
||||
// bytes in the given VectorizedView.
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func ChecksumVVWithOffset(vv buffer.VectorisedView, initial uint16, off int, size int) uint16 {
|
||||
odd := false
|
||||
sum := initial
|
||||
for _, v := range vv.Views() {
|
||||
if len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
s := uint32(sum)
|
||||
if odd {
|
||||
s += uint32(v[0])
|
||||
v = v[1:]
|
||||
|
||||
if off >= len(v) {
|
||||
off -= len(v)
|
||||
continue
|
||||
}
|
||||
odd = len(v)&1 != 0
|
||||
sum = calculateChecksum(v, s)
|
||||
v = v[off:]
|
||||
|
||||
l := len(v)
|
||||
if l > size {
|
||||
l = size
|
||||
}
|
||||
v = v[:l]
|
||||
|
||||
sum, odd = calculateChecksum(v, odd, uint32(sum))
|
||||
|
||||
size -= len(v)
|
||||
if size == 0 {
|
||||
break
|
||||
}
|
||||
off = 0
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2019 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 header provides the implementation of the encoding and decoding of
|
||||
// network protocol headers.
|
||||
package header_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
func TestChecksumVVWithOffset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
vv buffer.VectorisedView
|
||||
off, size int
|
||||
initial uint16
|
||||
want uint16
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{1, 9, 0, 5, 4}),
|
||||
}),
|
||||
off: 0,
|
||||
size: 0,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "OneView",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{1, 9, 0, 5, 4}),
|
||||
}),
|
||||
off: 0,
|
||||
size: 5,
|
||||
want: 1294,
|
||||
},
|
||||
{
|
||||
name: "TwoViews",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{1, 9, 0, 5, 4}),
|
||||
buffer.NewViewFromBytes([]byte{4, 3, 7, 1, 2, 123}),
|
||||
}),
|
||||
off: 0,
|
||||
size: 11,
|
||||
want: 33819,
|
||||
},
|
||||
{
|
||||
name: "TwoViewsWithOffset",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{98, 1, 9, 0, 5, 4}),
|
||||
buffer.NewViewFromBytes([]byte{4, 3, 7, 1, 2, 123}),
|
||||
}),
|
||||
off: 1,
|
||||
size: 11,
|
||||
want: 33819,
|
||||
},
|
||||
{
|
||||
name: "ThreeViewsWithOffset",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{98, 1, 9, 0, 5, 4}),
|
||||
buffer.NewViewFromBytes([]byte{98, 1, 9, 0, 5, 4}),
|
||||
buffer.NewViewFromBytes([]byte{4, 3, 7, 1, 2, 123}),
|
||||
}),
|
||||
off: 7,
|
||||
size: 11,
|
||||
want: 33819,
|
||||
},
|
||||
{
|
||||
name: "ThreeViewsWithInitial",
|
||||
vv: buffer.NewVectorisedView(0, []buffer.View{
|
||||
buffer.NewViewFromBytes([]byte{77, 11, 33, 0, 55, 44}),
|
||||
buffer.NewViewFromBytes([]byte{98, 1, 9, 0, 5, 4}),
|
||||
buffer.NewViewFromBytes([]byte{4, 3, 7, 1, 2, 123, 99}),
|
||||
}),
|
||||
initial: 77,
|
||||
off: 7,
|
||||
size: 11,
|
||||
want: 33896,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got, want := header.ChecksumVVWithOffset(tc.vv, tc.initial, tc.off, tc.size), tc.want; got != want {
|
||||
t.Errorf("header.ChecksumVVWithOffset(%v) = %v, want: %v", tc, got, tc.want)
|
||||
}
|
||||
v := tc.vv.ToView()
|
||||
v.TrimFront(tc.off)
|
||||
v.CapLength(tc.size)
|
||||
if got, want := header.Checksum(v, tc.initial), tc.want; got != want {
|
||||
t.Errorf("header.Checksum(%v) = %v, want: %v", tc, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func (e *Endpoint) MTU() uint32 {
|
||||
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
caps := stack.LinkEndpointCapabilities(0)
|
||||
if e.GSO {
|
||||
caps |= stack.CapabilityGSO
|
||||
caps |= stack.CapabilityHardwareGSO
|
||||
}
|
||||
return caps
|
||||
}
|
||||
@@ -134,6 +134,33 @@ func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets stores outbound packets into the channel.
|
||||
func (e *Endpoint) WritePackets(_ *stack.Route, gso *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
payloadView := payload.ToView()
|
||||
n := 0
|
||||
packetLoop:
|
||||
for i := range hdrs {
|
||||
hdr := &hdrs[i].Hdr
|
||||
off := hdrs[i].Off
|
||||
size := hdrs[i].Size
|
||||
p := PacketInfo{
|
||||
Header: hdr.View(),
|
||||
Proto: protocol,
|
||||
Payload: buffer.NewViewFromBytes(payloadView[off : off+size]),
|
||||
GSO: gso,
|
||||
}
|
||||
|
||||
select {
|
||||
case e.C <- p:
|
||||
n++
|
||||
default:
|
||||
break packetLoop
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *Endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
p := PacketInfo{
|
||||
|
||||
@@ -165,6 +165,9 @@ type Options struct {
|
||||
// disabled.
|
||||
GSOMaxSize uint32
|
||||
|
||||
// SoftwareGSOEnabled indicates whether software GSO is enabled or not.
|
||||
SoftwareGSOEnabled bool
|
||||
|
||||
// PacketDispatchMode specifies the type of inbound dispatcher to be
|
||||
// used for this endpoint.
|
||||
PacketDispatchMode PacketDispatchMode
|
||||
@@ -242,7 +245,11 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
||||
}
|
||||
if isSocket {
|
||||
if opts.GSOMaxSize != 0 {
|
||||
e.caps |= stack.CapabilityGSO
|
||||
if opts.SoftwareGSOEnabled {
|
||||
e.caps |= stack.CapabilitySoftwareGSO
|
||||
} else {
|
||||
e.caps |= stack.CapabilityHardwareGSO
|
||||
}
|
||||
e.gsoMaxSize = opts.GSOMaxSize
|
||||
}
|
||||
}
|
||||
@@ -397,7 +404,7 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
eth.Encode(ethHdr)
|
||||
}
|
||||
|
||||
if e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
vnetHdr := virtioNetHdr{}
|
||||
vnetHdrBuf := vnetHdrToByteSlice(&vnetHdr)
|
||||
if gso != nil {
|
||||
@@ -430,6 +437,123 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return rawfile.NonBlockingWrite3(e.fds[0], hdr.View(), payload.ToView(), nil)
|
||||
}
|
||||
|
||||
// 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, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, 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,
|
||||
}
|
||||
|
||||
// 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(hdrs)
|
||||
|
||||
views := payload.Views()
|
||||
/*
|
||||
* Each bondary in views can add one more iovec.
|
||||
*
|
||||
* payload | | | |
|
||||
* -----------------------------
|
||||
* packets | | | | | | |
|
||||
* -----------------------------
|
||||
* iovecs | | | | | | | | |
|
||||
*/
|
||||
iovec := make([]syscall.Iovec, n*iovLen+len(views)-1)
|
||||
mmsgHdrs := make([]rawfile.MMsgHdr, n)
|
||||
|
||||
iovecIdx := 0
|
||||
viewIdx := 0
|
||||
viewOff := 0
|
||||
off := 0
|
||||
nextOff := 0
|
||||
for i := range hdrs {
|
||||
prevIovecIdx := iovecIdx
|
||||
mmsgHdr := &mmsgHdrs[i]
|
||||
mmsgHdr.Msg.Iov = &iovec[iovecIdx]
|
||||
packetSize := hdrs[i].Size
|
||||
hdr := &hdrs[i].Hdr
|
||||
|
||||
off = hdrs[i].Off
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
nextOff = off + packetSize
|
||||
|
||||
if ethHdrBuf != nil {
|
||||
v := &iovec[iovecIdx]
|
||||
v.Base = ðHdrBuf[0]
|
||||
v.Len = uint64(len(ethHdrBuf))
|
||||
iovecIdx++
|
||||
}
|
||||
|
||||
v := &iovec[iovecIdx]
|
||||
hdrView := hdr.View()
|
||||
v.Base = &hdrView[0]
|
||||
v.Len = uint64(len(hdrView))
|
||||
iovecIdx++
|
||||
|
||||
for packetSize > 0 {
|
||||
vec := &iovec[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
|
||||
}
|
||||
|
||||
mmsgHdr.Msg.Iovlen = uint64(iovecIdx - prevIovecIdx)
|
||||
}
|
||||
|
||||
packets := 0
|
||||
for packets < n {
|
||||
sent, err := rawfile.NonBlockingSendMMsg(e.fds[0], mmsgHdrs)
|
||||
if err != nil {
|
||||
return packets, err
|
||||
}
|
||||
packets += sent
|
||||
mmsgHdrs = mmsgHdrs[sent:]
|
||||
}
|
||||
return packets, nil
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
return rawfile.NonBlockingWrite(e.fds[0], packet.ToView())
|
||||
|
||||
@@ -53,7 +53,7 @@ func newReadVDispatcher(fd int, e *endpoint) (linkDispatcher, error) {
|
||||
d := &readVDispatcher{fd: fd, e: e}
|
||||
d.views = make([]buffer.View, len(BufConfig))
|
||||
iovLen := len(BufConfig)
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
iovLen++
|
||||
}
|
||||
d.iovecs = make([]syscall.Iovec, iovLen)
|
||||
@@ -63,7 +63,7 @@ func newReadVDispatcher(fd int, e *endpoint) (linkDispatcher, error) {
|
||||
func (d *readVDispatcher) allocateViews(bufConfig []int) {
|
||||
var vnetHdr [virtioNetHdrSize]byte
|
||||
vnetHdrOff := 0
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
// The kernel adds virtioNetHdr before each packet, but
|
||||
// we don't use it, so so we allocate a buffer for it,
|
||||
// add it in iovecs but don't add it in a view.
|
||||
@@ -106,7 +106,7 @@ func (d *readVDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
// Skip virtioNetHdr which is added before each packet, it
|
||||
// isn't used and it isn't in a view.
|
||||
n -= virtioNetHdrSize
|
||||
@@ -195,7 +195,7 @@ func newRecvMMsgDispatcher(fd int, e *endpoint) (linkDispatcher, error) {
|
||||
}
|
||||
d.iovecs = make([][]syscall.Iovec, MaxMsgsPerRecv)
|
||||
iovLen := len(BufConfig)
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
// virtioNetHdr is prepended before each packet.
|
||||
iovLen++
|
||||
}
|
||||
@@ -226,7 +226,7 @@ func (d *recvMMsgDispatcher) allocateViews(bufConfig []int) {
|
||||
for k := 0; k < len(d.views); k++ {
|
||||
var vnetHdr [virtioNetHdrSize]byte
|
||||
vnetHdrOff := 0
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
// The kernel adds virtioNetHdr before each packet, but
|
||||
// we don't use it, so so we allocate a buffer for it,
|
||||
// add it in iovecs but don't add it in a view.
|
||||
@@ -262,7 +262,7 @@ func (d *recvMMsgDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
// Process each of received packets.
|
||||
for k := 0; k < nMsgs; k++ {
|
||||
n := int(d.msgHdrs[k].Len)
|
||||
if d.e.Capabilities()&stack.CapabilityGSO != 0 {
|
||||
if d.e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
n -= virtioNetHdrSize
|
||||
}
|
||||
if n <= d.e.hdrSize {
|
||||
|
||||
@@ -90,6 +90,11 @@ func (e *endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependa
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (e *endpoint) WritePackets(_ *stack.Route, _ *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
// Reject the packet if it's shorter than an ethernet header.
|
||||
|
||||
@@ -84,12 +84,23 @@ func (m *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber,
|
||||
m.dispatcher.DeliverNetworkPacket(m, "" /* remote */, "" /* local */, protocol, vv, nil /* linkHeader */)
|
||||
}
|
||||
|
||||
// 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, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
endpoint, ok := m.routes[r.RemoteAddress]
|
||||
if !ok {
|
||||
return 0, tcpip.ErrNoRoute
|
||||
}
|
||||
return endpoint.WritePackets(r, gso, hdrs, payload, protocol)
|
||||
}
|
||||
|
||||
// WritePacket 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) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
func (m *InjectableEndpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
if endpoint, ok := m.routes[r.RemoteAddress]; ok {
|
||||
return endpoint.WritePacket(r, nil /* gso */, hdr, payload, protocol)
|
||||
return endpoint.WritePacket(r, gso, hdr, payload, protocol)
|
||||
}
|
||||
return tcpip.ErrNoRoute
|
||||
}
|
||||
|
||||
@@ -16,5 +16,8 @@ go_library(
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = ["//pkg/tcpip"],
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
@@ -101,6 +102,16 @@ func NonBlockingWrite3(fd int, b1, b2, b3 []byte) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NonBlockingSendMMsg sends multiple messages on a socket.
|
||||
func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
|
||||
n, _, e := syscall.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
|
||||
if e != 0 {
|
||||
return 0, TranslateErrno(e)
|
||||
}
|
||||
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// PollEvent represents the pollfd structure passed to a poll() system call.
|
||||
type PollEvent struct {
|
||||
FD int32
|
||||
|
||||
@@ -212,6 +212,11 @@ func (e *endpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.Prependa
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (e *endpoint) WritePackets(r *stack.Route, _ *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
v := packet.ToView()
|
||||
|
||||
@@ -193,10 +193,7 @@ func (e *endpoint) GSOMaxSize() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// WritePacket 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) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
func (e *endpoint) dumpPacket(gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) {
|
||||
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
|
||||
logPacket("send", protocol, hdr.View(), gso)
|
||||
}
|
||||
@@ -223,9 +220,27 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WritePacket 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) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
e.dumpPacket(gso, hdr, payload, protocol)
|
||||
return e.lower.WritePacket(r, gso, hdr, payload, protocol)
|
||||
}
|
||||
|
||||
// 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, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
view := payload.ToView()
|
||||
for _, d := range hdrs {
|
||||
e.dumpPacket(gso, d.Hdr, buffer.NewVectorisedView(d.Size, []buffer.View{view[d.Off:][:d.Size]}), protocol)
|
||||
}
|
||||
return e.lower.WritePackets(r, gso, hdrs, payload, protocol)
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
|
||||
|
||||
@@ -109,6 +109,19 @@ func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return err
|
||||
}
|
||||
|
||||
// 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, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
if !e.writeGate.Enter() {
|
||||
return len(hdrs), nil
|
||||
}
|
||||
|
||||
n, err := e.lower.WritePackets(r, gso, hdrs, payload, protocol)
|
||||
e.writeGate.Leave()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *Endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
if !e.writeGate.Enter() {
|
||||
|
||||
@@ -70,6 +70,12 @@ func (e *countedEndpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.P
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (e *countedEndpoint) WritePackets(r *stack.Route, _ *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
e.writeCount += len(hdrs)
|
||||
return len(hdrs), nil
|
||||
}
|
||||
|
||||
func (e *countedEndpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
e.writeCount++
|
||||
return nil
|
||||
|
||||
@@ -83,6 +83,11 @@ func (e *endpoint) WritePacket(*stack.Route, *stack.GSO, buffer.Prependable, buf
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
// WritePackets implements stack.NetworkEndpoint.WritePackets.
|
||||
func (e *endpoint) WritePackets(*stack.Route, *stack.GSO, []stack.PacketDescriptor, buffer.VectorisedView, stack.NetworkHeaderParams, stack.PacketLooping) (int, *tcpip.Error) {
|
||||
return 0, tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, payload buffer.VectorisedView, loop stack.PacketLooping) *tcpip.Error {
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -171,6 +171,11 @@ func (t *testObject) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prepen
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (t *testObject) WritePackets(_ *stack.Route, _ *stack.GSO, hdr []stack.PacketDescriptor, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *testObject) WriteRawPacket(_ buffer.VectorisedView) *tcpip.Error {
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -198,10 +198,9 @@ func (e *endpoint) writePacketFragments(r *stack.Route, gso *stack.GSO, hdr buff
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) *tcpip.Error {
|
||||
func (e *endpoint) addIPHeader(r *stack.Route, hdr *buffer.Prependable, payloadSize int, params stack.NetworkHeaderParams) {
|
||||
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
|
||||
length := uint16(hdr.UsedLength() + payload.Size())
|
||||
length := uint16(hdr.UsedLength() + payloadSize)
|
||||
id := uint32(0)
|
||||
if length > header.IPv4MaximumHeaderSize+8 {
|
||||
// Packets of 68 bytes or less are required by RFC 791 to not be
|
||||
@@ -219,6 +218,11 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
DstAddr: r.RemoteAddress,
|
||||
})
|
||||
ip.SetChecksum(^ip.CalculateChecksum())
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) *tcpip.Error {
|
||||
e.addIPHeader(r, &hdr, payload.Size(), params)
|
||||
|
||||
if loop&stack.PacketLoop != 0 {
|
||||
views := make([]buffer.View, 1, 1+len(payload.Views()))
|
||||
@@ -242,6 +246,23 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePackets implements stack.NetworkEndpoint.WritePackets.
|
||||
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) (int, *tcpip.Error) {
|
||||
if loop&stack.PacketLoop != 0 {
|
||||
panic("multiple packets in local loop")
|
||||
}
|
||||
if loop&stack.PacketOut == 0 {
|
||||
return len(hdrs), nil
|
||||
}
|
||||
|
||||
for i := range hdrs {
|
||||
e.addIPHeader(r, &hdrs[i].Hdr, hdrs[i].Size, params)
|
||||
}
|
||||
n, err := e.linkEP.WritePackets(r, gso, hdrs, payload, ProtocolNumber)
|
||||
r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteHeaderIncludedPacket writes a packet already containing a network
|
||||
// header through the given route.
|
||||
func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, payload buffer.VectorisedView, loop stack.PacketLooping) *tcpip.Error {
|
||||
|
||||
@@ -97,9 +97,8 @@ func (e *endpoint) GSOMaxSize() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) *tcpip.Error {
|
||||
length := uint16(hdr.UsedLength() + payload.Size())
|
||||
func (e *endpoint) addIPHeader(r *stack.Route, hdr *buffer.Prependable, payloadSize int, params stack.NetworkHeaderParams) {
|
||||
length := uint16(hdr.UsedLength() + payloadSize)
|
||||
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
PayloadLength: length,
|
||||
@@ -109,6 +108,11 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
SrcAddr: r.LocalAddress,
|
||||
DstAddr: r.RemoteAddress,
|
||||
})
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) *tcpip.Error {
|
||||
e.addIPHeader(r, &hdr, payload.Size(), params)
|
||||
|
||||
if loop&stack.PacketLoop != 0 {
|
||||
views := make([]buffer.View, 1, 1+len(payload.Views()))
|
||||
@@ -127,6 +131,26 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return e.linkEP.WritePacket(r, gso, hdr, payload, ProtocolNumber)
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, hdrs []stack.PacketDescriptor, payload buffer.VectorisedView, params stack.NetworkHeaderParams, loop stack.PacketLooping) (int, *tcpip.Error) {
|
||||
if loop&stack.PacketLoop != 0 {
|
||||
panic("not implemented")
|
||||
}
|
||||
if loop&stack.PacketOut == 0 {
|
||||
return len(hdrs), nil
|
||||
}
|
||||
|
||||
for i := range hdrs {
|
||||
hdr := &hdrs[i].Hdr
|
||||
size := hdrs[i].Size
|
||||
e.addIPHeader(r, hdr, size, params)
|
||||
}
|
||||
|
||||
n, err := e.linkEP.WritePackets(r, gso, hdrs, payload, ProtocolNumber)
|
||||
r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteHeaderIncludedPacker implements stack.NetworkEndpoint. It is not yet
|
||||
// supported by IPv6.
|
||||
func (*endpoint) WriteHeaderIncludedPacket(r *stack.Route, payload buffer.VectorisedView, loop stack.PacketLooping) *tcpip.Error {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user