mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add support for setting TCP segment hash.
This allows the link layer endpoints to consistenly hash a TCP segment to a single underlying queue in case a link layer endpoint does support multiple underlying queues. Updates #231 PiperOrigin-RevId: 302760664
This commit is contained in:
committed by
gVisor bot
parent
f97858011f
commit
c8eeedcc1d
@@ -405,6 +405,7 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
|
||||
eth.Encode(ethHdr)
|
||||
}
|
||||
|
||||
fd := e.fds[pkt.Hash%uint32(len(e.fds))]
|
||||
if e.Capabilities()&stack.CapabilityHardwareGSO != 0 {
|
||||
vnetHdr := virtioNetHdr{}
|
||||
if gso != nil {
|
||||
@@ -428,14 +429,14 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.Ne
|
||||
}
|
||||
|
||||
vnetHdrBuf := vnetHdrToByteSlice(&vnetHdr)
|
||||
return rawfile.NonBlockingWrite3(e.fds[0], vnetHdrBuf, pkt.Header.View(), pkt.Data.ToView())
|
||||
return rawfile.NonBlockingWrite3(fd, vnetHdrBuf, pkt.Header.View(), pkt.Data.ToView())
|
||||
}
|
||||
|
||||
if pkt.Data.Size() == 0 {
|
||||
return rawfile.NonBlockingWrite(e.fds[0], pkt.Header.View())
|
||||
return rawfile.NonBlockingWrite(fd, pkt.Header.View())
|
||||
}
|
||||
|
||||
return rawfile.NonBlockingWrite3(e.fds[0], pkt.Header.View(), pkt.Data.ToView(), nil)
|
||||
return rawfile.NonBlockingWrite3(fd, pkt.Header.View(), pkt.Data.ToView(), nil)
|
||||
}
|
||||
|
||||
// WritePackets writes outbound packets to the file descriptor. If it is not
|
||||
@@ -551,7 +552,8 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.Pac
|
||||
|
||||
packets := 0
|
||||
for packets < n {
|
||||
sent, err := rawfile.NonBlockingSendMMsg(e.fds[0], mmsgHdrs)
|
||||
fd := e.fds[pkts[packets].Hash%uint32(len(e.fds))]
|
||||
sent, err := rawfile.NonBlockingSendMMsg(fd, mmsgHdrs)
|
||||
if err != nil {
|
||||
return packets, err
|
||||
}
|
||||
|
||||
@@ -49,36 +49,42 @@ type packetInfo struct {
|
||||
}
|
||||
|
||||
type context struct {
|
||||
t *testing.T
|
||||
fds [2]int
|
||||
ep stack.LinkEndpoint
|
||||
ch chan packetInfo
|
||||
done chan struct{}
|
||||
t *testing.T
|
||||
readFDs []int
|
||||
writeFDs []int
|
||||
ep stack.LinkEndpoint
|
||||
ch chan packetInfo
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newContext(t *testing.T, opt *Options) *context {
|
||||
fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
||||
firstFDPair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Socketpair failed: %v", err)
|
||||
}
|
||||
secondFDPair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Socketpair failed: %v", err)
|
||||
}
|
||||
|
||||
done := make(chan struct{}, 1)
|
||||
done := make(chan struct{}, 2)
|
||||
opt.ClosedFunc = func(*tcpip.Error) {
|
||||
done <- struct{}{}
|
||||
}
|
||||
|
||||
opt.FDs = []int{fds[1]}
|
||||
opt.FDs = []int{firstFDPair[1], secondFDPair[1]}
|
||||
ep, err := New(opt)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create FD endpoint: %v", err)
|
||||
}
|
||||
|
||||
c := &context{
|
||||
t: t,
|
||||
fds: fds,
|
||||
ep: ep,
|
||||
ch: make(chan packetInfo, 100),
|
||||
done: done,
|
||||
t: t,
|
||||
readFDs: []int{firstFDPair[0], secondFDPair[0]},
|
||||
writeFDs: opt.FDs,
|
||||
ep: ep,
|
||||
ch: make(chan packetInfo, 100),
|
||||
done: done,
|
||||
}
|
||||
|
||||
ep.Attach(c)
|
||||
@@ -87,9 +93,14 @@ func newContext(t *testing.T, opt *Options) *context {
|
||||
}
|
||||
|
||||
func (c *context) cleanup() {
|
||||
syscall.Close(c.fds[0])
|
||||
for _, fd := range c.readFDs {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
<-c.done
|
||||
syscall.Close(c.fds[1])
|
||||
<-c.done
|
||||
for _, fd := range c.writeFDs {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBuffer) {
|
||||
@@ -136,7 +147,7 @@ func TestAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32) {
|
||||
func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32, hash uint32) {
|
||||
c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth, GSOMaxSize: gsoMaxSize})
|
||||
defer c.cleanup()
|
||||
|
||||
@@ -171,13 +182,15 @@ func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32) {
|
||||
if err := c.ep.WritePacket(r, gso, proto, stack.PacketBuffer{
|
||||
Header: hdr,
|
||||
Data: payload.ToVectorisedView(),
|
||||
Hash: hash,
|
||||
}); err != nil {
|
||||
t.Fatalf("WritePacket failed: %v", err)
|
||||
}
|
||||
|
||||
// Read from fd, then compare with what we wrote.
|
||||
// Read from the corresponding FD, then compare with what we wrote.
|
||||
b = make([]byte, mtu)
|
||||
n, err := syscall.Read(c.fds[0], b)
|
||||
fd := c.readFDs[hash%uint32(len(c.readFDs))]
|
||||
n, err := syscall.Read(fd, b)
|
||||
if err != nil {
|
||||
t.Fatalf("Read failed: %v", err)
|
||||
}
|
||||
@@ -238,7 +251,7 @@ func TestWritePacket(t *testing.T) {
|
||||
t.Run(
|
||||
fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v", eth, plen, gso),
|
||||
func(t *testing.T) {
|
||||
testWritePacket(t, plen, eth, gso)
|
||||
testWritePacket(t, plen, eth, gso, 0)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -246,6 +259,27 @@ func TestWritePacket(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashedWritePacket(t *testing.T) {
|
||||
lengths := []int{0, 100, 1000}
|
||||
eths := []bool{true, false}
|
||||
gsos := []uint32{0, 32768}
|
||||
hashes := []uint32{0, 1}
|
||||
for _, eth := range eths {
|
||||
for _, plen := range lengths {
|
||||
for _, gso := range gsos {
|
||||
for _, hash := range hashes {
|
||||
t.Run(
|
||||
fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v,Hash=%d", eth, plen, gso, hash),
|
||||
func(t *testing.T) {
|
||||
testWritePacket(t, plen, eth, gso, hash)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreserveSrcAddress(t *testing.T) {
|
||||
baddr := tcpip.LinkAddress("\xcc\xbb\xaa\x77\x88\x99")
|
||||
|
||||
@@ -270,7 +304,7 @@ func TestPreserveSrcAddress(t *testing.T) {
|
||||
|
||||
// Read from the FD, then compare with what we wrote.
|
||||
b := make([]byte, mtu)
|
||||
n, err := syscall.Read(c.fds[0], b)
|
||||
n, err := syscall.Read(c.readFDs[0], b)
|
||||
if err != nil {
|
||||
t.Fatalf("Read failed: %v", err)
|
||||
}
|
||||
@@ -314,7 +348,7 @@ func TestDeliverPacket(t *testing.T) {
|
||||
}
|
||||
|
||||
// Write packet via the file descriptor.
|
||||
if _, err := syscall.Write(c.fds[0], all); err != nil {
|
||||
if _, err := syscall.Write(c.readFDs[0], all); err != nil {
|
||||
t.Fatalf("Write failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ go_library(
|
||||
"nic.go",
|
||||
"packet_buffer.go",
|
||||
"packet_buffer_state.go",
|
||||
"rand.go",
|
||||
"registration.go",
|
||||
"route.go",
|
||||
"stack.go",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stack
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
@@ -54,6 +55,10 @@ type PacketBuffer struct {
|
||||
LinkHeader buffer.View
|
||||
NetworkHeader buffer.View
|
||||
TransportHeader buffer.View
|
||||
|
||||
// Hash is the transport layer hash of this packet. A value of zero
|
||||
// indicates no valid hash has been set.
|
||||
Hash uint32
|
||||
}
|
||||
|
||||
// Clone makes a copy of pk. It clones the Data field, which creates a new
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stack
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stack
|
||||
|
||||
import (
|
||||
mathrand "math/rand"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// lockedRandomSource provides a threadsafe rand.Source.
|
||||
type lockedRandomSource struct {
|
||||
mu sync.Mutex
|
||||
src mathrand.Source
|
||||
}
|
||||
|
||||
func (r *lockedRandomSource) Int63() (n int64) {
|
||||
r.mu.Lock()
|
||||
n = r.src.Int63()
|
||||
r.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *lockedRandomSource) Seed(seed int64) {
|
||||
r.mu.Lock()
|
||||
r.src.Seed(seed)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
@@ -20,7 +20,9 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
mathrand "math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -465,6 +467,10 @@ type Stack struct {
|
||||
// forwarder holds the packets that wait for their link-address resolutions
|
||||
// to complete, and forwards them when each resolution is done.
|
||||
forwarder *forwardQueue
|
||||
|
||||
// randomGenerator is an injectable pseudo random generator that can be
|
||||
// used when a random number is required.
|
||||
randomGenerator *mathrand.Rand
|
||||
}
|
||||
|
||||
// UniqueID is an abstract generator of unique identifiers.
|
||||
@@ -525,9 +531,16 @@ type Options struct {
|
||||
// this is non-nil.
|
||||
RawFactory RawFactory
|
||||
|
||||
// OpaqueIIDOpts hold the options for generating opaque interface identifiers
|
||||
// (IIDs) as outlined by RFC 7217.
|
||||
// OpaqueIIDOpts hold the options for generating opaque interface
|
||||
// identifiers (IIDs) as outlined by RFC 7217.
|
||||
OpaqueIIDOpts OpaqueInterfaceIdentifierOptions
|
||||
|
||||
// RandSource is an optional source to use to generate random
|
||||
// numbers. If omitted it defaults to a Source seeded by the data
|
||||
// returned by rand.Read().
|
||||
//
|
||||
// RandSource must be thread-safe.
|
||||
RandSource mathrand.Source
|
||||
}
|
||||
|
||||
// TransportEndpointInfo holds useful information about a transport endpoint
|
||||
@@ -623,6 +636,13 @@ func New(opts Options) *Stack {
|
||||
opts.UniqueID = new(uniqueIDGenerator)
|
||||
}
|
||||
|
||||
randSrc := opts.RandSource
|
||||
if randSrc == nil {
|
||||
// Source provided by mathrand.NewSource is not thread-safe so
|
||||
// we wrap it in a simple thread-safe version.
|
||||
randSrc = &lockedRandomSource{src: mathrand.NewSource(generateRandInt64())}
|
||||
}
|
||||
|
||||
// Make sure opts.NDPConfigs contains valid values only.
|
||||
opts.NDPConfigs.validate()
|
||||
|
||||
@@ -645,6 +665,7 @@ func New(opts Options) *Stack {
|
||||
ndpDisp: opts.NDPDisp,
|
||||
opaqueIIDOpts: opts.OpaqueIIDOpts,
|
||||
forwarder: newForwardQueue(),
|
||||
randomGenerator: mathrand.New(randSrc),
|
||||
}
|
||||
|
||||
// Add specified network protocols.
|
||||
@@ -1818,6 +1839,12 @@ func (s *Stack) Seed() uint32 {
|
||||
return s.seed
|
||||
}
|
||||
|
||||
// Rand returns a reference to a pseudo random generator that can be used
|
||||
// to generate random numbers as required.
|
||||
func (s *Stack) Rand() *mathrand.Rand {
|
||||
return s.randomGenerator
|
||||
}
|
||||
|
||||
func generateRandUint32() uint32 {
|
||||
b := make([]byte, 4)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
@@ -1825,3 +1852,16 @@ func generateRandUint32() uint32 {
|
||||
}
|
||||
return binary.LittleEndian.Uint32(b)
|
||||
}
|
||||
|
||||
func generateRandInt64() int64 {
|
||||
b := make([]byte, 8)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := bytes.NewReader(b)
|
||||
var v int64
|
||||
if err := binary.Read(buf, binary.LittleEndian, &v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -445,7 +445,15 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) {
|
||||
// RFC 793 section 3.4 page 35 (figure 12) outlines that a RST
|
||||
// must be sent in response to a SYN-ACK while in the listen
|
||||
// state to prevent completing a handshake from an old SYN.
|
||||
e.sendTCP(&s.route, s.id, buffer.VectorisedView{}, e.ttl, e.sendTOS, header.TCPFlagRst, s.ackNumber, 0, 0, nil, nil)
|
||||
e.sendTCP(&s.route, tcpFields{
|
||||
id: s.id,
|
||||
ttl: e.ttl,
|
||||
tos: e.sendTOS,
|
||||
flags: header.TCPFlagRst,
|
||||
seq: s.ackNumber,
|
||||
ack: 0,
|
||||
rcvWnd: 0,
|
||||
}, buffer.VectorisedView{}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -493,7 +501,15 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) {
|
||||
TSEcr: opts.TSVal,
|
||||
MSS: mssForRoute(&s.route),
|
||||
}
|
||||
e.sendSynTCP(&s.route, s.id, e.ttl, e.sendTOS, header.TCPFlagSyn|header.TCPFlagAck, cookie, s.sequenceNumber+1, ctx.rcvWnd, synOpts)
|
||||
e.sendSynTCP(&s.route, tcpFields{
|
||||
id: s.id,
|
||||
ttl: e.ttl,
|
||||
tos: e.sendTOS,
|
||||
flags: header.TCPFlagSyn | header.TCPFlagAck,
|
||||
seq: cookie,
|
||||
ack: s.sequenceNumber + 1,
|
||||
rcvWnd: ctx.rcvWnd,
|
||||
}, synOpts)
|
||||
e.stack.Stats().TCP.ListenOverflowSynCookieSent.Increment()
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +308,15 @@ func (h *handshake) synSentState(s *segment) *tcpip.Error {
|
||||
if ttl == 0 {
|
||||
ttl = s.route.DefaultTTL()
|
||||
}
|
||||
h.ep.sendSynTCP(&s.route, h.ep.ID, ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
h.ep.sendSynTCP(&s.route, tcpFields{
|
||||
id: h.ep.ID,
|
||||
ttl: ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
}, synOpts)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -361,7 +369,15 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
|
||||
SACKPermitted: h.ep.sackPermitted,
|
||||
MSS: h.ep.amss,
|
||||
}
|
||||
h.ep.sendSynTCP(&s.route, h.ep.ID, h.ep.ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
h.ep.sendSynTCP(&s.route, tcpFields{
|
||||
id: h.ep.ID,
|
||||
ttl: h.ep.ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
}, synOpts)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -550,7 +566,16 @@ func (h *handshake) execute() *tcpip.Error {
|
||||
synOpts.WS = -1
|
||||
}
|
||||
}
|
||||
h.ep.sendSynTCP(&h.ep.route, h.ep.ID, h.ep.ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
|
||||
h.ep.sendSynTCP(&h.ep.route, tcpFields{
|
||||
id: h.ep.ID,
|
||||
ttl: h.ep.ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
}, synOpts)
|
||||
|
||||
for h.state != handshakeCompleted {
|
||||
h.ep.mu.Unlock()
|
||||
@@ -573,7 +598,15 @@ func (h *handshake) execute() *tcpip.Error {
|
||||
// the connection with another ACK or data (as ACKs are never
|
||||
// retransmitted on their own).
|
||||
if h.active || !h.acked || h.deferAccept != 0 && time.Since(h.startTime) > h.deferAccept {
|
||||
h.ep.sendSynTCP(&h.ep.route, h.ep.ID, h.ep.ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
h.ep.sendSynTCP(&h.ep.route, tcpFields{
|
||||
id: h.ep.ID,
|
||||
ttl: h.ep.ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
}, synOpts)
|
||||
}
|
||||
|
||||
case wakerForNotification:
|
||||
@@ -686,18 +719,33 @@ func makeSynOptions(opts header.TCPSynOptions) []byte {
|
||||
return options[:offset]
|
||||
}
|
||||
|
||||
func (e *endpoint) sendSynTCP(r *stack.Route, id stack.TransportEndpointID, ttl, tos uint8, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size, opts header.TCPSynOptions) *tcpip.Error {
|
||||
options := makeSynOptions(opts)
|
||||
// tcpFields is a struct to carry different parameters required by the
|
||||
// send*TCP variant functions below.
|
||||
type tcpFields struct {
|
||||
id stack.TransportEndpointID
|
||||
ttl uint8
|
||||
tos uint8
|
||||
flags byte
|
||||
seq seqnum.Value
|
||||
ack seqnum.Value
|
||||
rcvWnd seqnum.Size
|
||||
opts []byte
|
||||
txHash uint32
|
||||
}
|
||||
|
||||
func (e *endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOptions) *tcpip.Error {
|
||||
tf.opts = makeSynOptions(opts)
|
||||
// We ignore SYN send errors and let the callers re-attempt send.
|
||||
if err := e.sendTCP(r, id, buffer.VectorisedView{}, ttl, tos, flags, seq, ack, rcvWnd, options, nil); err != nil {
|
||||
if err := e.sendTCP(r, tf, buffer.VectorisedView{}, nil); err != nil {
|
||||
e.stats.SendErrors.SynSendToNetworkFailed.Increment()
|
||||
}
|
||||
putOptions(options)
|
||||
putOptions(tf.opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) sendTCP(r *stack.Route, id stack.TransportEndpointID, data buffer.VectorisedView, ttl, tos uint8, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size, opts []byte, gso *stack.GSO) *tcpip.Error {
|
||||
if err := sendTCP(r, id, data, ttl, tos, flags, seq, ack, rcvWnd, opts, gso); err != nil {
|
||||
func (e *endpoint) sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
|
||||
tf.txHash = e.txHash
|
||||
if err := sendTCP(r, tf, data, gso); err != nil {
|
||||
e.stats.SendErrors.SegmentSendToNetworkFailed.Increment()
|
||||
return err
|
||||
}
|
||||
@@ -705,8 +753,8 @@ func (e *endpoint) sendTCP(r *stack.Route, id stack.TransportEndpointID, data bu
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildTCPHdr(r *stack.Route, id stack.TransportEndpointID, pkt *stack.PacketBuffer, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size, opts []byte, gso *stack.GSO) {
|
||||
optLen := len(opts)
|
||||
func buildTCPHdr(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso *stack.GSO) {
|
||||
optLen := len(tf.opts)
|
||||
hdr := &pkt.Header
|
||||
packetSize := pkt.DataSize
|
||||
off := pkt.DataOffset
|
||||
@@ -714,15 +762,15 @@ func buildTCPHdr(r *stack.Route, id stack.TransportEndpointID, pkt *stack.Packet
|
||||
tcp := header.TCP(hdr.Prepend(header.TCPMinimumSize + optLen))
|
||||
pkt.TransportHeader = buffer.View(tcp)
|
||||
tcp.Encode(&header.TCPFields{
|
||||
SrcPort: id.LocalPort,
|
||||
DstPort: id.RemotePort,
|
||||
SeqNum: uint32(seq),
|
||||
AckNum: uint32(ack),
|
||||
SrcPort: tf.id.LocalPort,
|
||||
DstPort: tf.id.RemotePort,
|
||||
SeqNum: uint32(tf.seq),
|
||||
AckNum: uint32(tf.ack),
|
||||
DataOffset: uint8(header.TCPMinimumSize + optLen),
|
||||
Flags: flags,
|
||||
WindowSize: uint16(rcvWnd),
|
||||
Flags: tf.flags,
|
||||
WindowSize: uint16(tf.rcvWnd),
|
||||
})
|
||||
copy(tcp[header.TCPMinimumSize:], opts)
|
||||
copy(tcp[header.TCPMinimumSize:], tf.opts)
|
||||
|
||||
length := uint16(hdr.UsedLength() + packetSize)
|
||||
xsum := r.PseudoHeaderChecksum(ProtocolNumber, length)
|
||||
@@ -737,13 +785,12 @@ func buildTCPHdr(r *stack.Route, id stack.TransportEndpointID, pkt *stack.Packet
|
||||
xsum = header.ChecksumVVWithOffset(pkt.Data, xsum, off, packetSize)
|
||||
tcp.SetChecksum(^tcp.CalculateChecksum(xsum))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func sendTCPBatch(r *stack.Route, id stack.TransportEndpointID, data buffer.VectorisedView, ttl, tos uint8, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size, opts []byte, gso *stack.GSO) *tcpip.Error {
|
||||
optLen := len(opts)
|
||||
if rcvWnd > 0xffff {
|
||||
rcvWnd = 0xffff
|
||||
func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
|
||||
optLen := len(tf.opts)
|
||||
if tf.rcvWnd > 0xffff {
|
||||
tf.rcvWnd = 0xffff
|
||||
}
|
||||
|
||||
mss := int(gso.MSS)
|
||||
@@ -768,14 +815,15 @@ func sendTCPBatch(r *stack.Route, id stack.TransportEndpointID, data buffer.Vect
|
||||
pkts[i].DataOffset = off
|
||||
pkts[i].DataSize = packetSize
|
||||
pkts[i].Data = data
|
||||
buildTCPHdr(r, id, &pkts[i], flags, seq, ack, rcvWnd, opts, gso)
|
||||
pkts[i].Hash = tf.txHash
|
||||
buildTCPHdr(r, tf, &pkts[i], gso)
|
||||
off += packetSize
|
||||
seq = seq.Add(seqnum.Size(packetSize))
|
||||
tf.seq = tf.seq.Add(seqnum.Size(packetSize))
|
||||
}
|
||||
if ttl == 0 {
|
||||
ttl = r.DefaultTTL()
|
||||
if tf.ttl == 0 {
|
||||
tf.ttl = r.DefaultTTL()
|
||||
}
|
||||
sent, err := r.WritePackets(gso, pkts, stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: ttl, TOS: tos})
|
||||
sent, err := r.WritePackets(gso, pkts, stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos})
|
||||
if err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.IncrementBy(uint64(n - sent))
|
||||
}
|
||||
@@ -785,14 +833,14 @@ func sendTCPBatch(r *stack.Route, id stack.TransportEndpointID, data buffer.Vect
|
||||
|
||||
// sendTCP sends a TCP segment with the provided options via the provided
|
||||
// network endpoint and under the provided identity.
|
||||
func sendTCP(r *stack.Route, id stack.TransportEndpointID, data buffer.VectorisedView, ttl, tos uint8, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size, opts []byte, gso *stack.GSO) *tcpip.Error {
|
||||
optLen := len(opts)
|
||||
if rcvWnd > 0xffff {
|
||||
rcvWnd = 0xffff
|
||||
func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
|
||||
optLen := len(tf.opts)
|
||||
if tf.rcvWnd > 0xffff {
|
||||
tf.rcvWnd = 0xffff
|
||||
}
|
||||
|
||||
if r.Loop&stack.PacketLoop == 0 && gso != nil && gso.Type == stack.GSOSW && int(gso.MSS) < data.Size() {
|
||||
return sendTCPBatch(r, id, data, ttl, tos, flags, seq, ack, rcvWnd, opts, gso)
|
||||
return sendTCPBatch(r, tf, data, gso)
|
||||
}
|
||||
|
||||
pkt := stack.PacketBuffer{
|
||||
@@ -800,18 +848,19 @@ func sendTCP(r *stack.Route, id stack.TransportEndpointID, data buffer.Vectorise
|
||||
DataOffset: 0,
|
||||
DataSize: data.Size(),
|
||||
Data: data,
|
||||
Hash: tf.txHash,
|
||||
}
|
||||
buildTCPHdr(r, id, &pkt, flags, seq, ack, rcvWnd, opts, gso)
|
||||
buildTCPHdr(r, tf, &pkt, gso)
|
||||
|
||||
if ttl == 0 {
|
||||
ttl = r.DefaultTTL()
|
||||
if tf.ttl == 0 {
|
||||
tf.ttl = r.DefaultTTL()
|
||||
}
|
||||
if err := r.WritePacket(gso, stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: ttl, TOS: tos}, pkt); err != nil {
|
||||
if err := r.WritePacket(gso, stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos}, pkt); err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.Increment()
|
||||
return err
|
||||
}
|
||||
r.Stats().TCP.SegmentsSent.Increment()
|
||||
if (flags & header.TCPFlagRst) != 0 {
|
||||
if (tf.flags & header.TCPFlagRst) != 0 {
|
||||
r.Stats().TCP.ResetsSent.Increment()
|
||||
}
|
||||
return nil
|
||||
@@ -863,7 +912,16 @@ func (e *endpoint) sendRaw(data buffer.VectorisedView, flags byte, seq, ack seqn
|
||||
sackBlocks = e.sack.Blocks[:e.sack.NumBlocks]
|
||||
}
|
||||
options := e.makeOptions(sackBlocks)
|
||||
err := e.sendTCP(&e.route, e.ID, data, e.ttl, e.sendTOS, flags, seq, ack, rcvWnd, options, e.gso)
|
||||
err := e.sendTCP(&e.route, tcpFields{
|
||||
id: e.ID,
|
||||
ttl: e.ttl,
|
||||
tos: e.sendTOS,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: rcvWnd,
|
||||
opts: options,
|
||||
}, data, e.gso)
|
||||
putOptions(options)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -581,6 +581,10 @@ type endpoint struct {
|
||||
// endpoint and at this point the endpoint is only around
|
||||
// to complete the TCP shutdown.
|
||||
closed bool
|
||||
|
||||
// txHash is the transport layer hash to be set on outbound packets
|
||||
// emitted by this endpoint.
|
||||
txHash uint32
|
||||
}
|
||||
|
||||
// UniqueID implements stack.TransportEndpoint.UniqueID.
|
||||
@@ -771,6 +775,7 @@ func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQue
|
||||
count: 9,
|
||||
},
|
||||
uniqueID: s.UniqueID(),
|
||||
txHash: s.Rand().Uint32(),
|
||||
}
|
||||
|
||||
var ss SendBufferSizeOption
|
||||
|
||||
@@ -191,7 +191,15 @@ func replyWithReset(s *segment) {
|
||||
flags |= header.TCPFlagAck
|
||||
ack = s.sequenceNumber.Add(s.logicalLen())
|
||||
}
|
||||
sendTCP(&s.route, s.id, buffer.VectorisedView{}, s.route.DefaultTTL(), stack.DefaultTOS, flags, seq, ack, 0 /* rcvWnd */, nil /* options */, nil /* gso */)
|
||||
sendTCP(&s.route, tcpFields{
|
||||
id: s.id,
|
||||
ttl: s.route.DefaultTTL(),
|
||||
tos: stack.DefaultTOS,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: 0,
|
||||
}, buffer.VectorisedView{}, nil /* gso */)
|
||||
}
|
||||
|
||||
// SetOption implements stack.TransportProtocol.SetOption.
|
||||
|
||||
Reference in New Issue
Block a user