snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
49
pkg/tcpip/transport/datagram.go
Normal file
49
pkg/tcpip/transport/datagram.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2021 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 transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
// DatagramEndpointState is the state of a datagram-based endpoint.
|
||||
type DatagramEndpointState tcpip.EndpointState
|
||||
|
||||
// The states a datagram-based endpoint may be in.
|
||||
const (
|
||||
_ DatagramEndpointState = iota
|
||||
DatagramEndpointStateInitial
|
||||
DatagramEndpointStateBound
|
||||
DatagramEndpointStateConnected
|
||||
DatagramEndpointStateClosed
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (s DatagramEndpointState) String() string {
|
||||
switch s {
|
||||
case DatagramEndpointStateInitial:
|
||||
return "INITIAL"
|
||||
case DatagramEndpointStateBound:
|
||||
return "BOUND"
|
||||
case DatagramEndpointStateConnected:
|
||||
return "CONNECTED"
|
||||
case DatagramEndpointStateClosed:
|
||||
return "CLOSED"
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled %[1]T variant = %[1]d", s))
|
||||
}
|
||||
}
|
||||
828
pkg/tcpip/transport/icmp/endpoint.go
Normal file
828
pkg/tcpip/transport/icmp/endpoint.go
Normal file
|
|
@ -0,0 +1,828 @@
|
|||
// Copyright 2018 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 icmp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/checksum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/ports"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/internal/network"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type icmpPacket struct {
|
||||
icmpPacketEntry
|
||||
senderAddress tcpip.FullAddress
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
data *stack.PacketBuffer
|
||||
receivedAt time.Time `state:".(int64)"`
|
||||
|
||||
// tosOrTClass stores either the Type of Service for IPv4 or the Traffic Class
|
||||
// for IPv6.
|
||||
tosOrTClass uint8
|
||||
// ttlOrHopLimit stores either the TTL for IPv4 or the HopLimit for IPv6
|
||||
ttlOrHopLimit uint8
|
||||
}
|
||||
|
||||
// endpoint represents an ICMP endpoint. This struct serves as the interface
|
||||
// between users of the endpoint and the protocol implementation; it is legal to
|
||||
// have concurrent goroutines make calls into the endpoint, they are properly
|
||||
// synchronized.
|
||||
//
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
tcpip.DefaultSocketOptionsHandler
|
||||
|
||||
// The following fields are initialized at creation time and are
|
||||
// immutable.
|
||||
stack *stack.Stack
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
waiterQueue *waiter.Queue
|
||||
net network.Endpoint
|
||||
stats tcpip.TransportEndpointStats
|
||||
ops tcpip.SocketOptions
|
||||
|
||||
// The following fields are used to manage the receive queue, and are
|
||||
// protected by rcvMu.
|
||||
rcvMu sync.Mutex `state:"nosave"`
|
||||
rcvReady bool
|
||||
rcvList icmpPacketList
|
||||
rcvBufSize int
|
||||
rcvClosed bool
|
||||
|
||||
// The following fields are protected by the mu mutex.
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
// frozen indicates if the packets should be delivered to the endpoint
|
||||
// during restore.
|
||||
frozen bool
|
||||
ident uint16
|
||||
}
|
||||
|
||||
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
ep := &endpoint{
|
||||
stack: s,
|
||||
transProto: transProto,
|
||||
waiterQueue: waiterQueue,
|
||||
}
|
||||
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
ep.ops.SetSendBufferSize(32*1024, false /* notify */)
|
||||
ep.ops.SetReceiveBufferSize(32*1024, false /* notify */)
|
||||
ep.net.Init(s, netProto, transProto, &ep.ops, waiterQueue)
|
||||
|
||||
// Override with stack defaults.
|
||||
var ss tcpip.SendBufferSizeOption
|
||||
if err := s.Option(&ss); err == nil {
|
||||
ep.ops.SetSendBufferSize(int64(ss.Default), false /* notify */)
|
||||
}
|
||||
var rs tcpip.ReceiveBufferSizeOption
|
||||
if err := s.Option(&rs); err == nil {
|
||||
ep.ops.SetReceiveBufferSize(int64(rs.Default), false /* notify */)
|
||||
}
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
// WakeupWriters implements tcpip.SocketOptionsHandler.
|
||||
func (e *endpoint) WakeupWriters() {
|
||||
e.net.MaybeSignalWritable()
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
func (e *endpoint) Abort() {
|
||||
e.Close()
|
||||
}
|
||||
|
||||
// Close puts the endpoint in a closed state and frees all resources
|
||||
// associated with it.
|
||||
func (e *endpoint) Close() {
|
||||
notify := func() bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial:
|
||||
case transport.DatagramEndpointStateClosed:
|
||||
return false
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
info := e.net.Info()
|
||||
info.ID.LocalPort = e.ident
|
||||
e.stack.UnregisterTransportEndpoint([]tcpip.NetworkProtocolNumber{info.NetProto}, e.transProto, info.ID, e, ports.Flags{}, tcpip.NICID(e.ops.GetBindToDevice()))
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
|
||||
e.net.Shutdown()
|
||||
e.net.Close()
|
||||
|
||||
e.rcvMu.Lock()
|
||||
defer e.rcvMu.Unlock()
|
||||
e.rcvClosed = true
|
||||
e.rcvBufSize = 0
|
||||
for !e.rcvList.Empty() {
|
||||
p := e.rcvList.Front()
|
||||
e.rcvList.Remove(p)
|
||||
p.data.DecRef()
|
||||
}
|
||||
|
||||
return true
|
||||
}()
|
||||
|
||||
if notify {
|
||||
e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (*endpoint) ModerateRecvBuf(int) {}
|
||||
|
||||
// SetOwner implements tcpip.Endpoint.SetOwner.
|
||||
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
|
||||
e.net.SetOwner(owner)
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
|
||||
e.rcvMu.Lock()
|
||||
|
||||
if e.rcvList.Empty() {
|
||||
var err tcpip.Error = &tcpip.ErrWouldBlock{}
|
||||
if e.rcvClosed {
|
||||
e.stats.ReadErrors.ReadClosed.Increment()
|
||||
err = &tcpip.ErrClosedForReceive{}
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
return tcpip.ReadResult{}, err
|
||||
}
|
||||
|
||||
p := e.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
e.rcvList.Remove(p)
|
||||
defer p.data.DecRef()
|
||||
e.rcvBufSize -= p.data.Data().Size()
|
||||
}
|
||||
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
// Control Messages
|
||||
// TODO(https://gvisor.dev/issue/7012): Share control message code with other
|
||||
// network endpoints.
|
||||
cm := tcpip.ReceivableControlMessages{
|
||||
HasTimestamp: true,
|
||||
Timestamp: p.receivedAt,
|
||||
}
|
||||
switch netProto := e.net.NetProto(); netProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
if e.ops.GetReceiveTOS() {
|
||||
cm.HasTOS = true
|
||||
cm.TOS = p.tosOrTClass
|
||||
}
|
||||
if e.ops.GetReceivePacketInfo() {
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = p.packetInfo
|
||||
}
|
||||
if e.ops.GetReceiveTTL() {
|
||||
cm.HasTTL = true
|
||||
cm.TTL = p.ttlOrHopLimit
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
if e.ops.GetReceiveTClass() {
|
||||
cm.HasTClass = true
|
||||
// Although TClass is an 8-bit value it's read in the CMsg as a uint32.
|
||||
cm.TClass = uint32(p.tosOrTClass)
|
||||
}
|
||||
if e.ops.GetIPv6ReceivePacketInfo() {
|
||||
cm.HasIPv6PacketInfo = true
|
||||
cm.IPv6PacketInfo = tcpip.IPv6PacketInfo{
|
||||
NIC: p.packetInfo.NIC,
|
||||
Addr: p.packetInfo.DestinationAddr,
|
||||
}
|
||||
}
|
||||
if e.ops.GetReceiveHopLimit() {
|
||||
cm.HasHopLimit = true
|
||||
cm.HopLimit = p.ttlOrHopLimit
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized network protocol = %d", netProto))
|
||||
}
|
||||
|
||||
res := tcpip.ReadResult{
|
||||
Total: p.data.Data().Size(),
|
||||
ControlMessages: cm,
|
||||
}
|
||||
if opts.NeedRemoteAddr {
|
||||
res.RemoteAddr = p.senderAddress
|
||||
}
|
||||
|
||||
n, err := p.data.Data().ReadTo(dst, opts.Peek)
|
||||
if n == 0 && err != nil {
|
||||
return res, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
res.Count = n
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// prepareForWrite prepares the endpoint for sending data. In particular, it
|
||||
// binds it if it's still in the initial state. To do so, it must first
|
||||
// reacquire the mutex in exclusive mode.
|
||||
//
|
||||
// Returns true for retry if preparation should be retried.
|
||||
// +checklocksread:e.mu
|
||||
func (e *endpoint) prepareForWriteInner(to *tcpip.FullAddress) (retry bool, err tcpip.Error) {
|
||||
switch e.net.State() {
|
||||
case transport.DatagramEndpointStateInitial:
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
return false, nil
|
||||
case transport.DatagramEndpointStateBound:
|
||||
if to == nil {
|
||||
return false, &tcpip.ErrDestinationRequired{}
|
||||
}
|
||||
return false, nil
|
||||
default:
|
||||
return false, &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
e.mu.RUnlock()
|
||||
e.mu.Lock()
|
||||
defer e.mu.DowngradeLock()
|
||||
|
||||
// The state changed when we released the shared locked and re-acquired
|
||||
// it in exclusive mode. Try again.
|
||||
if e.net.State() != transport.DatagramEndpointStateInitial {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// The state is still 'initial', so try to bind the endpoint.
|
||||
if err := e.bindLocked(tcpip.FullAddress{}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Write writes data to the endpoint's peer. This method does not block
|
||||
// if the data cannot be written.
|
||||
func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
n, err := e.write(p, opts)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
e.stats.PacketsSent.Increment()
|
||||
case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
|
||||
e.stats.WriteErrors.InvalidArgs.Increment()
|
||||
case *tcpip.ErrClosedForSend:
|
||||
e.stats.WriteErrors.WriteClosed.Increment()
|
||||
case *tcpip.ErrInvalidEndpointState:
|
||||
e.stats.WriteErrors.InvalidEndpointState.Increment()
|
||||
case *tcpip.ErrHostUnreachable, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
|
||||
// Errors indicating any problem with IP routing of the packet.
|
||||
e.stats.SendErrors.NoRoute.Increment()
|
||||
default:
|
||||
// For all other errors when writing to the network layer.
|
||||
e.stats.SendErrors.SendToNetworkFailed.Increment()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (e *endpoint) prepareForWrite(opts tcpip.WriteOptions) (network.WriteContext, uint16, tcpip.Error) {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
// Prepare for write.
|
||||
for {
|
||||
retry, err := e.prepareForWriteInner(opts.To)
|
||||
if err != nil {
|
||||
return network.WriteContext{}, 0, err
|
||||
}
|
||||
|
||||
if !retry {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ctx, err := e.net.AcquireContextForWrite(opts)
|
||||
return ctx, e.ident, err
|
||||
}
|
||||
|
||||
func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
ctx, ident, err := e.prepareForWrite(opts)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ctx.Release()
|
||||
|
||||
// Prevents giant buffer allocations.
|
||||
if p.Len() > header.DatagramMaximumSize {
|
||||
return 0, &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
|
||||
v := buffer.NewView(p.Len())
|
||||
defer v.Release()
|
||||
if _, err := io.CopyN(v, p, int64(p.Len())); err != nil {
|
||||
return 0, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
n := v.Size()
|
||||
|
||||
switch netProto, pktInfo := e.net.NetProto(), ctx.PacketInfo(); netProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
if err := send4(e.stack, &ctx, ident, v, pktInfo.MaxHeaderLength); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
case header.IPv6ProtocolNumber:
|
||||
if err := send6(e.stack, &ctx, ident, v, pktInfo.LocalAddress, pktInfo.RemoteAddress, pktInfo.MaxHeaderLength); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled network protocol = %d", netProto))
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
var _ tcpip.SocketOptionsHandler = (*endpoint)(nil)
|
||||
|
||||
// HasNIC implements tcpip.SocketOptionsHandler.
|
||||
func (e *endpoint) HasNIC(id int32) bool {
|
||||
return e.stack.HasNIC(tcpip.NICID(id))
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.
|
||||
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
return e.net.SetSockOpt(opt)
|
||||
}
|
||||
|
||||
// SetSockOptInt implements tcpip.Endpoint.
|
||||
func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
return e.net.SetSockOptInt(opt, v)
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.
|
||||
func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
switch opt {
|
||||
case tcpip.ReceiveQueueSizeOption:
|
||||
v := 0
|
||||
e.rcvMu.Lock()
|
||||
if !e.rcvList.Empty() {
|
||||
p := e.rcvList.Front()
|
||||
v = p.data.Data().Size()
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
return v, nil
|
||||
|
||||
default:
|
||||
return e.net.GetSockOptInt(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.
|
||||
func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
|
||||
return e.net.GetSockOpt(opt)
|
||||
}
|
||||
|
||||
func send4(s *stack.Stack, ctx *network.WriteContext, ident uint16, data *buffer.View, maxHeaderLength uint16) tcpip.Error {
|
||||
if data.Size() < header.ICMPv4MinimumSize {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
pkt := ctx.TryNewPacketBuffer(header.ICMPv4MinimumSize+int(maxHeaderLength), buffer.Buffer{})
|
||||
if pkt == nil {
|
||||
return &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
defer pkt.DecRef()
|
||||
|
||||
icmpv4 := header.ICMPv4(pkt.TransportHeader().Push(header.ICMPv4MinimumSize))
|
||||
pkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
copy(icmpv4, data.AsSlice())
|
||||
// Set the ident to the user-specified port. Sequence number should
|
||||
// already be set by the user.
|
||||
icmpv4.SetIdent(ident)
|
||||
data.TrimFront(header.ICMPv4MinimumSize)
|
||||
|
||||
// Linux performs these basic checks.
|
||||
if icmpv4.Type() != header.ICMPv4Echo || icmpv4.Code() != 0 {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
icmpv4.SetChecksum(0)
|
||||
icmpv4.SetChecksum(^checksum.Checksum(icmpv4, checksum.Checksum(data.AsSlice(), 0)))
|
||||
pkt.Data().AppendView(data.Clone())
|
||||
|
||||
// Because this icmp endpoint is implemented in the transport layer, we can
|
||||
// only increment the 'stack-wide' stats but we can't increment the
|
||||
// 'per-NetworkEndpoint' stats.
|
||||
stats := s.Stats().ICMP.V4.PacketsSent
|
||||
|
||||
if err := ctx.WritePacket(pkt, false /* headerIncluded */); err != nil {
|
||||
stats.Dropped.Increment()
|
||||
return err
|
||||
}
|
||||
|
||||
stats.EchoRequest.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
func send6(s *stack.Stack, ctx *network.WriteContext, ident uint16, data *buffer.View, src, dst tcpip.Address, maxHeaderLength uint16) tcpip.Error {
|
||||
if data.Size() < header.ICMPv6EchoMinimumSize {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
pkt := ctx.TryNewPacketBuffer(header.ICMPv6MinimumSize+int(maxHeaderLength), buffer.Buffer{})
|
||||
if pkt == nil {
|
||||
return &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
defer pkt.DecRef()
|
||||
|
||||
icmpv6 := header.ICMPv6(pkt.TransportHeader().Push(header.ICMPv6MinimumSize))
|
||||
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
copy(icmpv6, data.AsSlice())
|
||||
// Set the ident. Sequence number is provided by the user.
|
||||
icmpv6.SetIdent(ident)
|
||||
data.TrimFront(header.ICMPv6MinimumSize)
|
||||
|
||||
if icmpv6.Type() != header.ICMPv6EchoRequest || icmpv6.Code() != 0 {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
pkt.Data().AppendView(data.Clone())
|
||||
pktData := pkt.Data()
|
||||
icmpv6.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpv6,
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
PayloadCsum: pktData.Checksum(),
|
||||
PayloadLen: pktData.Size(),
|
||||
}))
|
||||
|
||||
// Because this icmp endpoint is implemented in the transport layer, we can
|
||||
// only increment the 'stack-wide' stats but we can't increment the
|
||||
// 'per-NetworkEndpoint' stats.
|
||||
stats := s.Stats().ICMP.V6.PacketsSent
|
||||
|
||||
if err := ctx.WritePacket(pkt, false /* headerIncluded */); err != nil {
|
||||
stats.Dropped.Increment()
|
||||
return err
|
||||
}
|
||||
|
||||
stats.EchoRequest.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
func (*endpoint) Disconnect() tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
|
||||
func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
err := e.net.ConnectAndThen(addr, func(netProto tcpip.NetworkProtocolNumber, previousID, nextID stack.TransportEndpointID) tcpip.Error {
|
||||
nextID.LocalPort = e.ident
|
||||
|
||||
nextID, err := e.registerWithStack(netProto, nextID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.ident = nextID.LocalPort
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.rcvMu.Lock()
|
||||
e.rcvReady = true
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectEndpoint is not supported.
|
||||
func (*endpoint) ConnectEndpoint(tcpip.Endpoint) tcpip.Error {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
// Shutdown closes the read and/or write end of the endpoint connection
|
||||
// to its peer.
|
||||
func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
return &tcpip.ErrNotConnected{}
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
|
||||
if flags&tcpip.ShutdownWrite != 0 {
|
||||
if err := e.net.Shutdown(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if flags&tcpip.ShutdownRead != 0 {
|
||||
e.rcvMu.Lock()
|
||||
wasClosed := e.rcvClosed
|
||||
e.rcvClosed = true
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
if !wasClosed {
|
||||
e.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen is not supported by UDP, it just fails.
|
||||
func (*endpoint) Listen(int) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Accept is not supported by UDP, it just fails.
|
||||
func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
|
||||
return nil, nil, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (e *endpoint) registerWithStack(netProto tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.Error) {
|
||||
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
|
||||
if id.LocalPort != 0 {
|
||||
// The endpoint already has a local port, just attempt to
|
||||
// register it.
|
||||
return id, e.stack.RegisterTransportEndpoint([]tcpip.NetworkProtocolNumber{netProto}, e.transProto, id, e, ports.Flags{}, bindToDevice)
|
||||
}
|
||||
|
||||
// We need to find a port for the endpoint.
|
||||
_, err := e.stack.PickEphemeralPort(e.stack.SecureRNG(), func(p uint16) (bool, tcpip.Error) {
|
||||
id.LocalPort = p
|
||||
err := e.stack.RegisterTransportEndpoint([]tcpip.NetworkProtocolNumber{netProto}, e.transProto, id, e, ports.Flags{}, bindToDevice)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
return true, nil
|
||||
case *tcpip.ErrPortInUse:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
|
||||
// Don't allow binding once endpoint is not in the initial state
|
||||
// anymore.
|
||||
if e.net.State() != transport.DatagramEndpointStateInitial {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
err := e.net.BindAndThen(addr, func(boundNetProto tcpip.NetworkProtocolNumber, boundAddr tcpip.Address) tcpip.Error {
|
||||
id := stack.TransportEndpointID{
|
||||
LocalPort: addr.Port,
|
||||
LocalAddress: addr.Addr,
|
||||
}
|
||||
id, err := e.registerWithStack(boundNetProto, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.ident = id.LocalPort
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.rcvMu.Lock()
|
||||
e.rcvReady = true
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) isBroadcastOrMulticast(nicID tcpip.NICID, addr tcpip.Address) bool {
|
||||
return addr == header.IPv4Broadcast ||
|
||||
header.IsV4MulticastAddress(addr) ||
|
||||
header.IsV6MulticastAddress(addr) ||
|
||||
e.stack.IsSubnetBroadcast(nicID, e.net.NetProto(), addr)
|
||||
}
|
||||
|
||||
// Bind binds the endpoint to a specific local address and port.
|
||||
// Specifying a NIC is optional.
|
||||
func (e *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
|
||||
if addr.Addr.BitLen() != 0 && e.isBroadcastOrMulticast(addr.NIC, addr.Addr) {
|
||||
return &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
return e.bindLocked(addr)
|
||||
}
|
||||
|
||||
// GetLocalAddress returns the address to which the endpoint is bound.
|
||||
func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
addr := e.net.GetLocalAddress()
|
||||
addr.Port = e.ident
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// GetRemoteAddress returns the address to which the endpoint is connected.
|
||||
func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
if addr, connected := e.net.GetRemoteAddress(); connected {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
// Readiness returns the current readiness of the endpoint. For example, if
|
||||
// waiter.EventIn is set, the endpoint is immediately readable.
|
||||
func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
var result waiter.EventMask
|
||||
|
||||
if e.net.HasSendSpace() {
|
||||
result |= waiter.WritableEvents & mask
|
||||
}
|
||||
|
||||
// Determine if the endpoint is readable if requested.
|
||||
if (mask & waiter.ReadableEvents) != 0 {
|
||||
e.rcvMu.Lock()
|
||||
if !e.rcvList.Empty() || e.rcvClosed {
|
||||
result |= waiter.ReadableEvents
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// HandlePacket is called by the stack when new packets arrive to this transport
|
||||
// endpoint.
|
||||
func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) {
|
||||
// Only accept echo replies.
|
||||
switch e.net.NetProto() {
|
||||
case header.IPv4ProtocolNumber:
|
||||
h := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
if len(h) < header.ICMPv4MinimumSize || h.Type() != header.ICMPv4EchoReply {
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
h := header.ICMPv6(pkt.TransportHeader().Slice())
|
||||
if len(h) < header.ICMPv6MinimumSize || h.Type() != header.ICMPv6EchoReply {
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
e.rcvMu.Lock()
|
||||
|
||||
// Drop the packet if our buffer is currently full.
|
||||
if !e.rcvReady || e.rcvClosed {
|
||||
e.rcvMu.Unlock()
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.ClosedReceiver.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
rcvBufSize := e.ops.GetReceiveBufferSize()
|
||||
if e.frozen || e.rcvBufSize >= int(rcvBufSize) {
|
||||
e.rcvMu.Unlock()
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.ReceiveBufferOverflow.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
wasEmpty := e.rcvBufSize == 0
|
||||
|
||||
net := pkt.Network()
|
||||
dstAddr := net.DestinationAddress()
|
||||
// Push new packet into receive list and increment the buffer size.
|
||||
packet := &icmpPacket{
|
||||
senderAddress: tcpip.FullAddress{
|
||||
NIC: pkt.NICID,
|
||||
Addr: id.RemoteAddress,
|
||||
},
|
||||
packetInfo: tcpip.IPPacketInfo{
|
||||
// Linux does not 'prepare' [1] in_pktinfo on socket buffers destined to
|
||||
// ping sockets (unlike UDP/RAW sockets). However the interface index [2]
|
||||
// and the Header Destination Address [3] are always filled.
|
||||
// [1] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_sockglue.c#L1392
|
||||
// [2] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_input.c#L510
|
||||
// [3] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_sockglue.c#L60
|
||||
NIC: pkt.NICID,
|
||||
DestinationAddr: dstAddr,
|
||||
},
|
||||
}
|
||||
|
||||
// Save any useful information from the network header to the packet.
|
||||
packet.tosOrTClass, _ = net.TOS()
|
||||
switch pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
packet.ttlOrHopLimit = header.IPv4(pkt.NetworkHeader().Slice()).TTL()
|
||||
case header.IPv6ProtocolNumber:
|
||||
packet.ttlOrHopLimit = header.IPv6(pkt.NetworkHeader().Slice()).HopLimit()
|
||||
}
|
||||
|
||||
// ICMP socket's data includes ICMP header but no others. Trim all other
|
||||
// headers from the front of the packet.
|
||||
pktBuf := pkt.ToBuffer()
|
||||
pktBuf.TrimFront(int64(pkt.HeaderSize() - len(pkt.TransportHeader().Slice())))
|
||||
packet.data = stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: pktBuf})
|
||||
|
||||
e.rcvList.PushBack(packet)
|
||||
e.rcvBufSize += packet.data.Data().Size()
|
||||
|
||||
packet.receivedAt = e.stack.Clock().Now()
|
||||
|
||||
e.rcvMu.Unlock()
|
||||
e.stats.PacketsReceived.Increment()
|
||||
// Notify any waiters that there's data to be read now.
|
||||
if wasEmpty {
|
||||
e.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleError implements stack.TransportEndpoint.
|
||||
func (*endpoint) HandleError(stack.TransportError, *stack.PacketBuffer) {}
|
||||
|
||||
// State implements tcpip.Endpoint.State. The ICMP endpoint currently doesn't
|
||||
// expose internal socket state.
|
||||
func (e *endpoint) State() uint32 {
|
||||
return uint32(e.net.State())
|
||||
}
|
||||
|
||||
// Info returns a copy of the endpoint info.
|
||||
func (e *endpoint) Info() tcpip.EndpointInfo {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
ret := e.net.Info()
|
||||
ret.ID.LocalPort = e.ident
|
||||
return &ret
|
||||
}
|
||||
|
||||
// Stats returns a pointer to the endpoint stats.
|
||||
func (e *endpoint) Stats() tcpip.EndpointStats {
|
||||
return &e.stats
|
||||
}
|
||||
|
||||
// Wait implements stack.TransportEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
// LastError implements tcpip.Endpoint.LastError.
|
||||
func (*endpoint) LastError() tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (e *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &e.ops
|
||||
}
|
||||
|
||||
// freeze prevents any more packets from being delivered to the endpoint.
|
||||
func (e *endpoint) freeze() {
|
||||
e.mu.Lock()
|
||||
e.frozen = true
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// thaw unfreezes a previously frozen endpoint using endpoint.freeze() allows
|
||||
// new packets to be delivered again.
|
||||
func (e *endpoint) thaw() {
|
||||
e.mu.Lock()
|
||||
e.frozen = false
|
||||
e.mu.Unlock()
|
||||
}
|
||||
92
pkg/tcpip/transport/icmp/endpoint_state.go
Normal file
92
pkg/tcpip/transport/icmp/endpoint_state.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2018 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 icmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/log"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
func (p *icmpPacket) saveReceivedAt() int64 {
|
||||
return p.receivedAt.UnixNano()
|
||||
}
|
||||
|
||||
// loadReceivedAt is invoked by stateify.
|
||||
func (p *icmpPacket) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
p.receivedAt = time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad(ctx context.Context) {
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (e *endpoint) beforeSave() {
|
||||
e.freeze()
|
||||
e.stack.RegisterResumableEndpoint(e)
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *endpoint) Restore(s *stack.Stack) {
|
||||
if err := e.net.Resume(s); err != nil {
|
||||
log.Warningf("Closing the ICMP endpoint as it cannot be restored, err: %v", err)
|
||||
e.Close()
|
||||
return
|
||||
}
|
||||
|
||||
e.thaw()
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
return
|
||||
}
|
||||
|
||||
e.stack = s
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
var err tcpip.Error
|
||||
info := e.net.Info()
|
||||
info.ID.LocalPort = e.ident
|
||||
info.ID, err = e.registerWithStack(info.NetProto, info.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("e.registerWithStack(%d, %#v): %s", info.NetProto, info.ID, err))
|
||||
}
|
||||
e.ident = info.ID.LocalPort
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *endpoint) Resume() {
|
||||
e.thaw()
|
||||
}
|
||||
239
pkg/tcpip/transport/icmp/icmp_packet_list.go
Normal file
239
pkg/tcpip/transport/icmp/icmp_packet_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package icmp
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type icmpPacketElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (icmpPacketElementMapper) linkerFor(elem *icmpPacket) *icmpPacket { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpPacketList struct {
|
||||
head *icmpPacket
|
||||
tail *icmpPacket
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *icmpPacketList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) Front() *icmpPacket {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) Back() *icmpPacket {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (icmpPacketElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) PushFront(e *icmpPacket) {
|
||||
linker := icmpPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) PushFrontList(m *icmpPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
icmpPacketElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) PushBack(e *icmpPacket) {
|
||||
linker := icmpPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) PushBackList(m *icmpPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
icmpPacketElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) InsertAfter(b, e *icmpPacket) {
|
||||
bLinker := icmpPacketElementMapper{}.linkerFor(b)
|
||||
eLinker := icmpPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) InsertBefore(a, e *icmpPacket) {
|
||||
aLinker := icmpPacketElementMapper{}.linkerFor(a)
|
||||
eLinker := icmpPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *icmpPacketList) Remove(e *icmpPacket) {
|
||||
linker := icmpPacketElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
icmpPacketElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpPacketEntry struct {
|
||||
next *icmpPacket
|
||||
prev *icmpPacket
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *icmpPacketEntry) Next() *icmpPacket {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *icmpPacketEntry) Prev() *icmpPacket {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *icmpPacketEntry) SetNext(elem *icmpPacket) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *icmpPacketEntry) SetPrev(elem *icmpPacket) {
|
||||
e.prev = elem
|
||||
}
|
||||
204
pkg/tcpip/transport/icmp/icmp_state_autogen.go
Normal file
204
pkg/tcpip/transport/icmp/icmp_state_autogen.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (p *icmpPacket) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/icmp.icmpPacket"
|
||||
}
|
||||
|
||||
func (p *icmpPacket) StateFields() []string {
|
||||
return []string{
|
||||
"icmpPacketEntry",
|
||||
"senderAddress",
|
||||
"packetInfo",
|
||||
"data",
|
||||
"receivedAt",
|
||||
"tosOrTClass",
|
||||
"ttlOrHopLimit",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *icmpPacket) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *icmpPacket) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
stateSinkObject.SaveValue(4, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.icmpPacketEntry)
|
||||
stateSinkObject.Save(1, &p.senderAddress)
|
||||
stateSinkObject.Save(2, &p.packetInfo)
|
||||
stateSinkObject.Save(3, &p.data)
|
||||
stateSinkObject.Save(5, &p.tosOrTClass)
|
||||
stateSinkObject.Save(6, &p.ttlOrHopLimit)
|
||||
}
|
||||
|
||||
func (p *icmpPacket) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *icmpPacket) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.icmpPacketEntry)
|
||||
stateSourceObject.Load(1, &p.senderAddress)
|
||||
stateSourceObject.Load(2, &p.packetInfo)
|
||||
stateSourceObject.Load(3, &p.data)
|
||||
stateSourceObject.Load(5, &p.tosOrTClass)
|
||||
stateSourceObject.Load(6, &p.ttlOrHopLimit)
|
||||
stateSourceObject.LoadValue(4, new(int64), func(y any) { p.loadReceivedAt(ctx, y.(int64)) })
|
||||
}
|
||||
|
||||
func (e *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/icmp.endpoint"
|
||||
}
|
||||
|
||||
func (e *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"DefaultSocketOptionsHandler",
|
||||
"stack",
|
||||
"transProto",
|
||||
"waiterQueue",
|
||||
"net",
|
||||
"stats",
|
||||
"ops",
|
||||
"rcvReady",
|
||||
"rcvList",
|
||||
"rcvBufSize",
|
||||
"rcvClosed",
|
||||
"frozen",
|
||||
"ident",
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSinkObject.Save(1, &e.stack)
|
||||
stateSinkObject.Save(2, &e.transProto)
|
||||
stateSinkObject.Save(3, &e.waiterQueue)
|
||||
stateSinkObject.Save(4, &e.net)
|
||||
stateSinkObject.Save(5, &e.stats)
|
||||
stateSinkObject.Save(6, &e.ops)
|
||||
stateSinkObject.Save(7, &e.rcvReady)
|
||||
stateSinkObject.Save(8, &e.rcvList)
|
||||
stateSinkObject.Save(9, &e.rcvBufSize)
|
||||
stateSinkObject.Save(10, &e.rcvClosed)
|
||||
stateSinkObject.Save(11, &e.frozen)
|
||||
stateSinkObject.Save(12, &e.ident)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSourceObject.Load(1, &e.stack)
|
||||
stateSourceObject.Load(2, &e.transProto)
|
||||
stateSourceObject.Load(3, &e.waiterQueue)
|
||||
stateSourceObject.Load(4, &e.net)
|
||||
stateSourceObject.Load(5, &e.stats)
|
||||
stateSourceObject.Load(6, &e.ops)
|
||||
stateSourceObject.Load(7, &e.rcvReady)
|
||||
stateSourceObject.Load(8, &e.rcvList)
|
||||
stateSourceObject.Load(9, &e.rcvBufSize)
|
||||
stateSourceObject.Load(10, &e.rcvClosed)
|
||||
stateSourceObject.Load(11, &e.frozen)
|
||||
stateSourceObject.Load(12, &e.ident)
|
||||
stateSourceObject.AfterLoad(func() { e.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (l *icmpPacketList) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/icmp.icmpPacketList"
|
||||
}
|
||||
|
||||
func (l *icmpPacketList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *icmpPacketList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *icmpPacketList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *icmpPacketList) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *icmpPacketList) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *icmpPacketEntry) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/icmp.icmpPacketEntry"
|
||||
}
|
||||
|
||||
func (e *icmpPacketEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *icmpPacketEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpPacketEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *icmpPacketEntry) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpPacketEntry) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func (p *protocol) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/icmp.protocol"
|
||||
}
|
||||
|
||||
func (p *protocol) StateFields() []string {
|
||||
return []string{
|
||||
"stack",
|
||||
"number",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protocol) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.stack)
|
||||
stateSinkObject.Save(1, &p.number)
|
||||
}
|
||||
|
||||
func (p *protocol) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.stack)
|
||||
stateSourceObject.Load(1, &p.number)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*icmpPacket)(nil))
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*icmpPacketList)(nil))
|
||||
state.Register((*icmpPacketEntry)(nil))
|
||||
state.Register((*protocol)(nil))
|
||||
}
|
||||
150
pkg/tcpip/transport/icmp/protocol.go
Normal file
150
pkg/tcpip/transport/icmp/protocol.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2018 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 icmp contains the implementation of the ICMP and IPv6-ICMP transport
|
||||
// protocols for use in ping.
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/raw"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtocolNumber4 is the ICMP protocol number.
|
||||
ProtocolNumber4 = header.ICMPv4ProtocolNumber
|
||||
|
||||
// ProtocolNumber6 is the IPv6-ICMP protocol number.
|
||||
ProtocolNumber6 = header.ICMPv6ProtocolNumber
|
||||
)
|
||||
|
||||
// protocol implements stack.TransportProtocol.
|
||||
//
|
||||
// +stateify savable
|
||||
type protocol struct {
|
||||
stack *stack.Stack
|
||||
|
||||
number tcpip.TransportProtocolNumber
|
||||
}
|
||||
|
||||
// Number returns the ICMP protocol number.
|
||||
func (p *protocol) Number() tcpip.TransportProtocolNumber {
|
||||
return p.number
|
||||
}
|
||||
|
||||
func (p *protocol) netProto() tcpip.NetworkProtocolNumber {
|
||||
switch p.number {
|
||||
case ProtocolNumber4:
|
||||
return header.IPv4ProtocolNumber
|
||||
case ProtocolNumber6:
|
||||
return header.IPv6ProtocolNumber
|
||||
}
|
||||
panic(fmt.Sprint("unknown protocol number: ", p.number))
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new icmp endpoint. It implements
|
||||
// stack.TransportProtocol.NewEndpoint.
|
||||
func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
if netProto != p.netProto() {
|
||||
return nil, &tcpip.ErrUnknownProtocol{}
|
||||
}
|
||||
return newEndpoint(p.stack, netProto, p.number, waiterQueue)
|
||||
}
|
||||
|
||||
// NewRawEndpoint creates a new raw icmp endpoint. It implements
|
||||
// stack.TransportProtocol.NewRawEndpoint.
|
||||
func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
if netProto != p.netProto() {
|
||||
return nil, &tcpip.ErrUnknownProtocol{}
|
||||
}
|
||||
return raw.NewEndpoint(p.stack, netProto, p.number, waiterQueue)
|
||||
}
|
||||
|
||||
// MinimumPacketSize returns the minimum valid icmp packet size.
|
||||
func (p *protocol) MinimumPacketSize() int {
|
||||
switch p.number {
|
||||
case ProtocolNumber4:
|
||||
return header.ICMPv4MinimumSize
|
||||
case ProtocolNumber6:
|
||||
return header.ICMPv6MinimumSize
|
||||
}
|
||||
panic(fmt.Sprint("unknown protocol number: ", p.number))
|
||||
}
|
||||
|
||||
// ParsePorts in case of ICMP sets src to 0, dst to ICMP ID, and err to nil.
|
||||
func (p *protocol) ParsePorts(v []byte) (src, dst uint16, err tcpip.Error) {
|
||||
switch p.number {
|
||||
case ProtocolNumber4:
|
||||
hdr := header.ICMPv4(v)
|
||||
return 0, hdr.Ident(), nil
|
||||
case ProtocolNumber6:
|
||||
hdr := header.ICMPv6(v)
|
||||
return 0, hdr.Ident(), nil
|
||||
}
|
||||
panic(fmt.Sprint("unknown protocol number: ", p.number))
|
||||
}
|
||||
|
||||
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
|
||||
// that don't match any existing endpoint.
|
||||
func (*protocol) HandleUnknownDestinationPacket(stack.TransportEndpointID, *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
|
||||
return stack.UnknownDestinationPacketHandled
|
||||
}
|
||||
|
||||
// SetOption implements stack.TransportProtocol.SetOption.
|
||||
func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Option implements stack.TransportProtocol.Option.
|
||||
func (*protocol) Option(tcpip.GettableTransportProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Close implements stack.TransportProtocol.Close.
|
||||
func (*protocol) Close() {}
|
||||
|
||||
// Wait implements stack.TransportProtocol.Wait.
|
||||
func (*protocol) Wait() {}
|
||||
|
||||
// Pause implements stack.TransportProtocol.Pause.
|
||||
func (*protocol) Pause() {}
|
||||
|
||||
// Resume implements stack.TransportProtocol.Resume.
|
||||
func (*protocol) Resume() {}
|
||||
|
||||
// Restore implements stack.TransportProtocol.Restore.
|
||||
func (*protocol) Restore() {}
|
||||
|
||||
// Parse implements stack.TransportProtocol.Parse.
|
||||
func (*protocol) Parse(pkt *stack.PacketBuffer) bool {
|
||||
// Right now, the Parse() method is tied to enabled protocols passed into
|
||||
// stack.New. This works for UDP and TCP, but we handle ICMP traffic even
|
||||
// when netstack users don't pass ICMP as a supported protocol.
|
||||
return false
|
||||
}
|
||||
|
||||
// NewProtocol4 returns an ICMPv4 transport protocol.
|
||||
func NewProtocol4(s *stack.Stack) stack.TransportProtocol {
|
||||
return &protocol{stack: s, number: ProtocolNumber4}
|
||||
}
|
||||
|
||||
// NewProtocol6 returns an ICMPv6 transport protocol.
|
||||
func NewProtocol6(s *stack.Stack) stack.TransportProtocol {
|
||||
return &protocol{stack: s, number: ProtocolNumber6}
|
||||
}
|
||||
1065
pkg/tcpip/transport/internal/network/endpoint.go
Normal file
1065
pkg/tcpip/transport/internal/network/endpoint.go
Normal file
File diff suppressed because it is too large
Load diff
62
pkg/tcpip/transport/internal/network/endpoint_state.go
Normal file
62
pkg/tcpip/transport/internal/network/endpoint_state.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2021 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 network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *Endpoint) Resume(s *stack.Stack) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
e.stack = s
|
||||
for m := range e.multicastMemberships {
|
||||
if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
|
||||
return fmt.Errorf("e.stack.JoinGroup(%d, %d, %s): %s", e.netProto, m.nicID, m.multicastAddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
info := e.Info()
|
||||
|
||||
switch state := e.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound:
|
||||
if info.ID.LocalAddress.BitLen() != 0 && !e.isBroadcastOrMulticast(info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress) {
|
||||
if e.stack.CheckLocalAddress(info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress) == 0 {
|
||||
return fmt.Errorf("got e.stack.CheckLocalAddress(%d, %d, %s) = 0, want != 0", info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress)
|
||||
}
|
||||
}
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
var err tcpip.Error
|
||||
multicastLoop := e.ops.GetMulticastLoop()
|
||||
// Release the connectedRoute if present.
|
||||
if e.connectedRoute != nil {
|
||||
e.connectedRoute.Release()
|
||||
}
|
||||
e.connectedRoute, err = e.stack.FindRoute(info.RegisterNICID, info.ID.LocalAddress, info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop)
|
||||
if err != nil {
|
||||
return fmt.Errorf("e.stack.FindRoute(%d, %s, %s, %d, %t): %s", info.RegisterNICID, info.ID.LocalAddress, info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop, err)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
121
pkg/tcpip/transport/internal/network/network_state_autogen.go
Normal file
121
pkg/tcpip/transport/internal/network/network_state_autogen.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (e *Endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/internal/network.Endpoint"
|
||||
}
|
||||
|
||||
func (e *Endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"stack",
|
||||
"ops",
|
||||
"netProto",
|
||||
"transProto",
|
||||
"waiterQueue",
|
||||
"wasBound",
|
||||
"owner",
|
||||
"writeShutdown",
|
||||
"effectiveNetProto",
|
||||
"multicastMemberships",
|
||||
"ipv4TTL",
|
||||
"ipv6HopLimit",
|
||||
"multicastTTL",
|
||||
"multicastAddr",
|
||||
"multicastNICID",
|
||||
"ipv4TOS",
|
||||
"ipv6TClass",
|
||||
"info",
|
||||
"state",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Endpoint) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *Endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.stack)
|
||||
stateSinkObject.Save(1, &e.ops)
|
||||
stateSinkObject.Save(2, &e.netProto)
|
||||
stateSinkObject.Save(3, &e.transProto)
|
||||
stateSinkObject.Save(4, &e.waiterQueue)
|
||||
stateSinkObject.Save(5, &e.wasBound)
|
||||
stateSinkObject.Save(6, &e.owner)
|
||||
stateSinkObject.Save(7, &e.writeShutdown)
|
||||
stateSinkObject.Save(8, &e.effectiveNetProto)
|
||||
stateSinkObject.Save(9, &e.multicastMemberships)
|
||||
stateSinkObject.Save(10, &e.ipv4TTL)
|
||||
stateSinkObject.Save(11, &e.ipv6HopLimit)
|
||||
stateSinkObject.Save(12, &e.multicastTTL)
|
||||
stateSinkObject.Save(13, &e.multicastAddr)
|
||||
stateSinkObject.Save(14, &e.multicastNICID)
|
||||
stateSinkObject.Save(15, &e.ipv4TOS)
|
||||
stateSinkObject.Save(16, &e.ipv6TClass)
|
||||
stateSinkObject.Save(17, &e.info)
|
||||
stateSinkObject.Save(18, &e.state)
|
||||
}
|
||||
|
||||
func (e *Endpoint) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *Endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.stack)
|
||||
stateSourceObject.Load(1, &e.ops)
|
||||
stateSourceObject.Load(2, &e.netProto)
|
||||
stateSourceObject.Load(3, &e.transProto)
|
||||
stateSourceObject.Load(4, &e.waiterQueue)
|
||||
stateSourceObject.Load(5, &e.wasBound)
|
||||
stateSourceObject.Load(6, &e.owner)
|
||||
stateSourceObject.Load(7, &e.writeShutdown)
|
||||
stateSourceObject.Load(8, &e.effectiveNetProto)
|
||||
stateSourceObject.Load(9, &e.multicastMemberships)
|
||||
stateSourceObject.Load(10, &e.ipv4TTL)
|
||||
stateSourceObject.Load(11, &e.ipv6HopLimit)
|
||||
stateSourceObject.Load(12, &e.multicastTTL)
|
||||
stateSourceObject.Load(13, &e.multicastAddr)
|
||||
stateSourceObject.Load(14, &e.multicastNICID)
|
||||
stateSourceObject.Load(15, &e.ipv4TOS)
|
||||
stateSourceObject.Load(16, &e.ipv6TClass)
|
||||
stateSourceObject.Load(17, &e.info)
|
||||
stateSourceObject.Load(18, &e.state)
|
||||
}
|
||||
|
||||
func (m *multicastMembership) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/internal/network.multicastMembership"
|
||||
}
|
||||
|
||||
func (m *multicastMembership) StateFields() []string {
|
||||
return []string{
|
||||
"nicID",
|
||||
"multicastAddr",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multicastMembership) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multicastMembership) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.nicID)
|
||||
stateSinkObject.Save(1, &m.multicastAddr)
|
||||
}
|
||||
|
||||
func (m *multicastMembership) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multicastMembership) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.nicID)
|
||||
stateSourceObject.Load(1, &m.multicastAddr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*Endpoint)(nil))
|
||||
state.Register((*multicastMembership)(nil))
|
||||
}
|
||||
177
pkg/tcpip/transport/internal/noop/endpoint.go
Normal file
177
pkg/tcpip/transport/internal/noop/endpoint.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2021 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 noop contains an endpoint that implements all tcpip.Endpoint
|
||||
// functions as noops.
|
||||
package noop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// endpoint can be created, but all interactions have no effect or
|
||||
// return errors.
|
||||
//
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
tcpip.DefaultSocketOptionsHandler
|
||||
ops tcpip.SocketOptions
|
||||
}
|
||||
|
||||
// New returns an initialized noop endpoint.
|
||||
func New(stk *stack.Stack) tcpip.Endpoint {
|
||||
// ep.ops must be in a valid, initialized state for callers of
|
||||
// ep.SocketOptions.
|
||||
var ep endpoint
|
||||
ep.ops.InitHandler(&ep, stk, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
return &ep
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
func (*endpoint) Abort() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Close implements tcpip.Endpoint.Close.
|
||||
func (*endpoint) Close() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (*endpoint) ModerateRecvBuf(int) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
func (*endpoint) SetOwner(tcpip.PacketOwner) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (*endpoint) Read(io.Writer, tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
|
||||
return tcpip.ReadResult{}, &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
// Write implements tcpip.Endpoint.Write.
|
||||
func (*endpoint) Write(tcpip.Payloader, tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
return 0, &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
func (*endpoint) Disconnect() tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Connect implements tcpip.Endpoint.Connect.
|
||||
func (*endpoint) Connect(tcpip.FullAddress) tcpip.Error {
|
||||
return &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
// Shutdown implements tcpip.Endpoint.Shutdown.
|
||||
func (*endpoint) Shutdown(tcpip.ShutdownFlags) tcpip.Error {
|
||||
return &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
// Listen implements tcpip.Endpoint.Listen.
|
||||
func (*endpoint) Listen(int) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Accept implements tcpip.Endpoint.Accept.
|
||||
func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
|
||||
return nil, nil, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Bind implements tcpip.Endpoint.Bind.
|
||||
func (*endpoint) Bind(tcpip.FullAddress) tcpip.Error {
|
||||
return &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
|
||||
func (*endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
|
||||
func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
// Readiness implements tcpip.Endpoint.Readiness.
|
||||
func (*endpoint) Readiness(waiter.EventMask) waiter.EventMask {
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
|
||||
func (*endpoint) SetSockOpt(tcpip.SettableSocketOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
func (*endpoint) SetSockOptInt(tcpip.SockOptInt, int) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
func (*endpoint) GetSockOpt(tcpip.GettableSocketOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
|
||||
func (*endpoint) GetSockOptInt(tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
return 0, &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// HandlePacket implements stack.RawTransportEndpoint.HandlePacket.
|
||||
func (*endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
panic(fmt.Sprintf("unreachable: noop.endpoint should never be registered, but got packet: %+v", pkt))
|
||||
}
|
||||
|
||||
// State implements socket.Socket.State.
|
||||
func (*endpoint) State() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Wait implements stack.TransportEndpoint.Wait.
|
||||
func (*endpoint) Wait() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Release implements stack.TransportEndpoint.Release.
|
||||
func (*endpoint) Release() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// LastError implements tcpip.Endpoint.LastError.
|
||||
func (*endpoint) LastError() tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (ep *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &ep.ops
|
||||
}
|
||||
|
||||
// Info implements tcpip.Endpoint.Info.
|
||||
func (*endpoint) Info() tcpip.EndpointInfo {
|
||||
return &stack.TransportEndpointInfo{}
|
||||
}
|
||||
|
||||
// Stats returns a pointer to the endpoint stats.
|
||||
func (*endpoint) Stats() tcpip.EndpointStats {
|
||||
return &tcpip.TransportEndpointStats{}
|
||||
}
|
||||
41
pkg/tcpip/transport/internal/noop/noop_state_autogen.go
Normal file
41
pkg/tcpip/transport/internal/noop/noop_state_autogen.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package noop
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (ep *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/internal/noop.endpoint"
|
||||
}
|
||||
|
||||
func (ep *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"DefaultSocketOptionsHandler",
|
||||
"ops",
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (ep *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
ep.beforeSave()
|
||||
stateSinkObject.Save(0, &ep.DefaultSocketOptionsHandler)
|
||||
stateSinkObject.Save(1, &ep.ops)
|
||||
}
|
||||
|
||||
func (ep *endpoint) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (ep *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &ep.DefaultSocketOptionsHandler)
|
||||
stateSourceObject.Load(1, &ep.ops)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*endpoint)(nil))
|
||||
}
|
||||
623
pkg/tcpip/transport/packet/endpoint.go
Normal file
623
pkg/tcpip/transport/packet/endpoint.go
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
// 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 packet provides the implementation of packet sockets (see
|
||||
// packet(7)). Packet sockets allow applications to:
|
||||
//
|
||||
// - manually write and inspect link, network, and transport headers
|
||||
// - receive all traffic of a given network protocol, or all protocols
|
||||
//
|
||||
// Packet sockets are similar to raw sockets, but provide even more power to
|
||||
// users, letting them effectively talk directly to the network device.
|
||||
//
|
||||
// Packet sockets skip the input and output iptables chains.
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
type tpacketVersion int
|
||||
|
||||
const (
|
||||
tpacketVersion1 tpacketVersion = iota
|
||||
tpacketVersion2
|
||||
)
|
||||
|
||||
var _ stack.MappablePacketEndpoint = (*endpoint)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type packet struct {
|
||||
packetEntry
|
||||
// data holds the actual packet data, including any headers and payload.
|
||||
data *stack.PacketBuffer
|
||||
receivedAt time.Time `state:".(int64)"`
|
||||
// senderAddr is the network address of the sender.
|
||||
senderAddr tcpip.FullAddress
|
||||
// packetInfo holds additional information like the protocol
|
||||
// of the packet etc.
|
||||
packetInfo tcpip.LinkPacketInfo
|
||||
}
|
||||
|
||||
// endpoint is the packet socket implementation of tcpip.Endpoint. It is legal
|
||||
// to have goroutines make concurrent calls into the endpoint.
|
||||
//
|
||||
// Lock order:
|
||||
//
|
||||
// endpoint.mu
|
||||
// endpoint.rcvMu
|
||||
// endpoint.packetMmapMu
|
||||
//
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
tcpip.DefaultSocketOptionsHandler
|
||||
|
||||
// The following fields are initialized at creation time and are
|
||||
// immutable.
|
||||
stack *stack.Stack
|
||||
waiterQueue *waiter.Queue
|
||||
cooked bool
|
||||
ops tcpip.SocketOptions
|
||||
stats tcpip.TransportEndpointStats
|
||||
|
||||
// The following fields are used to manage the receive queue.
|
||||
rcvMu rcvMutex `state:"nosave"`
|
||||
// +checklocks:rcvMu
|
||||
rcvList packetList
|
||||
// +checklocks:rcvMu
|
||||
rcvBufSize int
|
||||
// +checklocks:rcvMu
|
||||
rcvClosed bool
|
||||
// +checklocks:rcvMu
|
||||
rcvDisabled bool
|
||||
|
||||
mu endpointRWMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
closed bool
|
||||
// +checklocks:mu
|
||||
boundNetProto tcpip.NetworkProtocolNumber
|
||||
// +checklocks:mu
|
||||
boundNIC tcpip.NICID
|
||||
|
||||
lastErrorMu lastErrorMutex `state:"nosave"`
|
||||
// +checklocks:lastErrorMu
|
||||
lastError tcpip.Error
|
||||
|
||||
packetMmapMu packetMmapRWMutex `state:"nosave"`
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapVersion tpacketVersion
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapReserve int
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapEp stack.PacketMMapEndpoint
|
||||
}
|
||||
|
||||
// NewEndpoint returns a new packet endpoint.
|
||||
func NewEndpoint(s *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) tcpip.Endpoint {
|
||||
ep := &endpoint{
|
||||
stack: s,
|
||||
cooked: cooked,
|
||||
boundNetProto: netProto,
|
||||
waiterQueue: waiterQueue,
|
||||
}
|
||||
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
ep.ops.SetReceiveBufferSize(32*1024, false /* notify */)
|
||||
|
||||
// Override with stack defaults.
|
||||
var ss tcpip.SendBufferSizeOption
|
||||
if err := s.Option(&ss); err == nil {
|
||||
ep.ops.SetSendBufferSize(int64(ss.Default), false /* notify */)
|
||||
}
|
||||
|
||||
var rs tcpip.ReceiveBufferSizeOption
|
||||
if err := s.Option(&rs); err == nil {
|
||||
ep.ops.SetReceiveBufferSize(int64(rs.Default), false /* notify */)
|
||||
}
|
||||
|
||||
s.RegisterPacketEndpoint(0, netProto, ep)
|
||||
|
||||
return ep
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
func (ep *endpoint) Abort() {
|
||||
ep.Close()
|
||||
}
|
||||
|
||||
// Close implements tcpip.Endpoint.Close.
|
||||
func (ep *endpoint) Close() {
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
if ep.closed {
|
||||
return
|
||||
}
|
||||
ep.stack.UnregisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep)
|
||||
|
||||
ep.packetMmapMu.Lock()
|
||||
if ep.packetMMapEp != nil {
|
||||
ep.packetMMapEp.Close()
|
||||
ep.packetMMapEp = nil
|
||||
}
|
||||
ep.packetMmapMu.Unlock()
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
|
||||
// Clear the receive list.
|
||||
ep.rcvClosed = true
|
||||
ep.rcvBufSize = 0
|
||||
for !ep.rcvList.Empty() {
|
||||
p := ep.rcvList.Front()
|
||||
ep.rcvList.Remove(p)
|
||||
p.data.DecRef()
|
||||
}
|
||||
|
||||
ep.closed = true
|
||||
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
}
|
||||
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (*endpoint) ModerateRecvBuf(int) {}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
|
||||
ep.rcvMu.Lock()
|
||||
|
||||
// If there's no data to read, return that read would block or that the
|
||||
// endpoint is closed.
|
||||
if ep.rcvList.Empty() {
|
||||
var err tcpip.Error = &tcpip.ErrWouldBlock{}
|
||||
if ep.rcvClosed {
|
||||
ep.stats.ReadErrors.ReadClosed.Increment()
|
||||
err = &tcpip.ErrClosedForReceive{}
|
||||
}
|
||||
ep.rcvMu.Unlock()
|
||||
return tcpip.ReadResult{}, err
|
||||
}
|
||||
|
||||
packet := ep.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
ep.rcvList.Remove(packet)
|
||||
defer packet.data.DecRef()
|
||||
ep.rcvBufSize -= packet.data.Size()
|
||||
}
|
||||
|
||||
ep.rcvMu.Unlock()
|
||||
|
||||
res := tcpip.ReadResult{
|
||||
Total: packet.data.Size(),
|
||||
ControlMessages: tcpip.ReceivableControlMessages{
|
||||
HasTimestamp: true,
|
||||
Timestamp: packet.receivedAt,
|
||||
},
|
||||
}
|
||||
if opts.NeedRemoteAddr {
|
||||
res.RemoteAddr = packet.senderAddr
|
||||
}
|
||||
if opts.NeedLinkPacketInfo {
|
||||
res.LinkPacketInfo = packet.packetInfo
|
||||
}
|
||||
|
||||
n, err := packet.data.Data().ReadTo(dst, opts.Peek)
|
||||
if n == 0 && err != nil {
|
||||
return res, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
res.Count = n
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
if !ep.stack.PacketEndpointWriteSupported() {
|
||||
return 0, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
ep.mu.Lock()
|
||||
closed := ep.closed
|
||||
nicID := ep.boundNIC
|
||||
proto := ep.boundNetProto
|
||||
ep.mu.Unlock()
|
||||
if closed {
|
||||
return 0, &tcpip.ErrClosedForSend{}
|
||||
}
|
||||
|
||||
var remote tcpip.LinkAddress
|
||||
if to := opts.To; to != nil {
|
||||
remote = to.LinkAddr
|
||||
|
||||
if n := to.NIC; n != 0 {
|
||||
nicID = n
|
||||
}
|
||||
|
||||
if p := to.Port; p != 0 {
|
||||
proto = tcpip.NetworkProtocolNumber(p)
|
||||
}
|
||||
}
|
||||
|
||||
if nicID == 0 {
|
||||
return 0, &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
// Prevents giant buffer allocations.
|
||||
if p.Len() > header.DatagramMaximumSize {
|
||||
return 0, &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
|
||||
var payload buffer.Buffer
|
||||
if _, err := payload.WriteFromReader(p, int64(p.Len())); err != nil {
|
||||
return 0, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
payloadSz := payload.Size()
|
||||
|
||||
if err := func() tcpip.Error {
|
||||
if ep.cooked {
|
||||
return ep.stack.WritePacketToRemote(nicID, remote, proto, payload)
|
||||
}
|
||||
return ep.stack.WriteRawPacket(nicID, proto, payload)
|
||||
}(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return payloadSz, nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect. Packet sockets cannot be
|
||||
// disconnected, and this function always returns tpcip.ErrNotSupported.
|
||||
func (*endpoint) Disconnect() tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Connect implements tcpip.Endpoint.Connect. Packet sockets cannot be
|
||||
// connected, and this function always returns *tcpip.ErrNotSupported.
|
||||
func (*endpoint) Connect(tcpip.FullAddress) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Shutdown implements tcpip.Endpoint.Shutdown. Packet sockets cannot be used
|
||||
// with Shutdown, and this function always returns *tcpip.ErrNotSupported.
|
||||
func (*endpoint) Shutdown(tcpip.ShutdownFlags) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Listen implements tcpip.Endpoint.Listen. Packet sockets cannot be used with
|
||||
// Listen, and this function always returns *tcpip.ErrNotSupported.
|
||||
func (*endpoint) Listen(int) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Accept implements tcpip.Endpoint.Accept. Packet sockets cannot be used with
|
||||
// Accept, and this function always returns *tcpip.ErrNotSupported.
|
||||
func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
|
||||
return nil, nil, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Bind implements tcpip.Endpoint.Bind.
|
||||
func (ep *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
|
||||
// "By default, all packets of the specified protocol type are passed
|
||||
// to a packet socket. To get packets only from a specific interface
|
||||
// use bind(2) specifying an address in a struct sockaddr_ll to bind
|
||||
// the packet socket to an interface. Fields used for binding are
|
||||
// sll_family (should be AF_PACKET), sll_protocol, and sll_ifindex."
|
||||
// - packet(7).
|
||||
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
netProto := tcpip.NetworkProtocolNumber(addr.Port)
|
||||
if netProto == 0 {
|
||||
// Do not allow unbinding the network protocol.
|
||||
netProto = ep.boundNetProto
|
||||
}
|
||||
|
||||
if ep.boundNIC == addr.NIC && ep.boundNetProto == netProto {
|
||||
// Already bound to the requested NIC and network protocol.
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(https://gvisor.dev/issue/6618): Unregister after registering the new
|
||||
// binding.
|
||||
ep.stack.UnregisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep)
|
||||
ep.boundNIC = 0
|
||||
ep.boundNetProto = 0
|
||||
|
||||
// Bind endpoint to receive packets from specific interface.
|
||||
if err := ep.stack.RegisterPacketEndpoint(addr.NIC, netProto, ep); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ep.boundNIC = addr.NIC
|
||||
ep.boundNetProto = netProto
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
|
||||
func (ep *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
ep.mu.RLock()
|
||||
defer ep.mu.RUnlock()
|
||||
|
||||
return tcpip.FullAddress{
|
||||
NIC: ep.boundNIC,
|
||||
Port: uint16(ep.boundNetProto),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
|
||||
func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
// Even a connected socket doesn't return a remote address.
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
// Readiness implements tcpip.Endpoint.Readiness.
|
||||
func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
// The endpoint is always writable.
|
||||
result := waiter.WritableEvents & mask
|
||||
|
||||
// Determine whether the endpoint is readable.
|
||||
if (mask & waiter.ReadableEvents) != 0 {
|
||||
ep.packetMmapMu.RLock()
|
||||
if ep.packetMMapEp != nil {
|
||||
result |= ep.packetMMapEp.Readiness(mask)
|
||||
}
|
||||
ep.packetMmapMu.RUnlock()
|
||||
ep.rcvMu.Lock()
|
||||
if !ep.rcvList.Empty() || ep.rcvClosed {
|
||||
result |= waiter.ReadableEvents
|
||||
}
|
||||
ep.rcvMu.Unlock()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
|
||||
func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
switch opt.(type) {
|
||||
case *tcpip.SocketDetachFilterOption:
|
||||
return nil
|
||||
case *tcpip.TpacketReq:
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
if !ep.rcvList.Empty() {
|
||||
return &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// SetSockOptInt implements tcpip.Endpoint.SetSockOptInt.
|
||||
func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
switch opt {
|
||||
case tcpip.PacketMMapVersionOption:
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
// We support up to TPACKET_V2.
|
||||
version := tpacketVersion(v)
|
||||
switch version {
|
||||
case tpacketVersion1, tpacketVersion2:
|
||||
if ep.packetMMapEp != nil {
|
||||
return &tcpip.ErrEndpointBusy{}
|
||||
}
|
||||
ep.packetMMapVersion = version
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
case tcpip.PacketMMapReserveOption:
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
if ep.packetMMapEp != nil {
|
||||
return &tcpip.ErrEndpointBusy{}
|
||||
}
|
||||
if uint32(v) > uint32(math.MaxInt32) {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
ep.packetMMapReserve = v
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) LastError() tcpip.Error {
|
||||
ep.lastErrorMu.Lock()
|
||||
defer ep.lastErrorMu.Unlock()
|
||||
|
||||
err := ep.lastError
|
||||
ep.lastError = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateLastError implements tcpip.SocketOptionsHandler.UpdateLastError.
|
||||
func (ep *endpoint) UpdateLastError(err tcpip.Error) {
|
||||
ep.lastErrorMu.Lock()
|
||||
ep.lastError = err
|
||||
ep.lastErrorMu.Unlock()
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
|
||||
switch opt.(type) {
|
||||
case *tcpip.TpacketStats:
|
||||
ep.packetMmapMu.RLock()
|
||||
defer ep.packetMmapMu.RUnlock()
|
||||
if ep.packetMMapEp == nil {
|
||||
return nil
|
||||
}
|
||||
*(opt.(*tcpip.TpacketStats)) = ep.packetMMapEp.Stats()
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
|
||||
func (ep *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
switch opt {
|
||||
case tcpip.ReceiveQueueSizeOption:
|
||||
v := 0
|
||||
ep.rcvMu.Lock()
|
||||
if !ep.rcvList.Empty() {
|
||||
p := ep.rcvList.Front()
|
||||
v = p.data.Size()
|
||||
}
|
||||
ep.rcvMu.Unlock()
|
||||
return v, nil
|
||||
|
||||
default:
|
||||
return -1, &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// handlePacket implements stack.PacketEndpoint.HandlePacket
|
||||
func (ep *endpoint) HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
ep.packetMmapMu.RLock()
|
||||
if ep.packetMMapEp != nil {
|
||||
if handled := ep.packetMMapEp.HandlePacket(nicID, netProto, pkt); handled {
|
||||
ep.packetMmapMu.RUnlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
ep.packetMmapMu.RUnlock()
|
||||
|
||||
wasEmpty := ep.handlePacketInner(nicID, netProto, pkt)
|
||||
|
||||
ep.stats.PacketsReceived.Increment()
|
||||
// Notify waiters that there's data to be read.
|
||||
if wasEmpty {
|
||||
ep.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) HandlePacketMMapCopy(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
_ = ep.handlePacketInner(nicID, netProto, pkt)
|
||||
}
|
||||
|
||||
func (ep *endpoint) handlePacketInner(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) bool {
|
||||
ep.rcvMu.Lock()
|
||||
|
||||
// Drop the packet if our buffer is currently full.
|
||||
if ep.rcvClosed {
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.Stats().DroppedPackets.Increment()
|
||||
ep.stats.ReceiveErrors.ClosedReceiver.Increment()
|
||||
return false
|
||||
}
|
||||
|
||||
rcvBufSize := ep.ops.GetReceiveBufferSize()
|
||||
if ep.rcvDisabled || ep.rcvBufSize >= int(rcvBufSize) {
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.Stats().DroppedPackets.Increment()
|
||||
ep.stats.ReceiveErrors.ReceiveBufferOverflow.Increment()
|
||||
return false
|
||||
}
|
||||
|
||||
wasEmpty := ep.rcvBufSize == 0
|
||||
|
||||
rcvdPkt := packet{
|
||||
packetInfo: tcpip.LinkPacketInfo{
|
||||
Protocol: netProto,
|
||||
PktType: pkt.PktType,
|
||||
},
|
||||
senderAddr: tcpip.FullAddress{
|
||||
NIC: nicID,
|
||||
},
|
||||
receivedAt: ep.stack.Clock().Now(),
|
||||
}
|
||||
|
||||
if len(pkt.LinkHeader().Slice()) != 0 {
|
||||
hdr := header.Ethernet(pkt.LinkHeader().Slice())
|
||||
rcvdPkt.senderAddr.LinkAddr = hdr.SourceAddress()
|
||||
}
|
||||
|
||||
// Raw packet endpoints include link-headers in received packets.
|
||||
pktBuf := pkt.ToBuffer()
|
||||
if ep.cooked {
|
||||
// Cooked packet endpoints don't include the link-headers in received
|
||||
// packets.
|
||||
pktBuf.TrimFront(int64(len(pkt.LinkHeader().Slice()) + len(pkt.VirtioNetHeader().Slice())))
|
||||
}
|
||||
rcvdPkt.data = stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: pktBuf})
|
||||
|
||||
ep.rcvList.PushBack(&rcvdPkt)
|
||||
ep.rcvBufSize += rcvdPkt.data.Size()
|
||||
ep.rcvMu.Unlock()
|
||||
return wasEmpty
|
||||
}
|
||||
|
||||
// State implements socket.Socket.State.
|
||||
func (*endpoint) State() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Info returns a copy of the endpoint info.
|
||||
func (ep *endpoint) Info() tcpip.EndpointInfo {
|
||||
ep.mu.RLock()
|
||||
defer ep.mu.RUnlock()
|
||||
return &stack.TransportEndpointInfo{NetProto: ep.boundNetProto}
|
||||
}
|
||||
|
||||
// Stats returns a pointer to the endpoint stats.
|
||||
func (ep *endpoint) Stats() tcpip.EndpointStats {
|
||||
return &ep.stats
|
||||
}
|
||||
|
||||
// SetOwner implements tcpip.Endpoint.SetOwner.
|
||||
func (*endpoint) SetOwner(tcpip.PacketOwner) {}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (ep *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &ep.ops
|
||||
}
|
||||
|
||||
// GetPacketMMapOpts implements stack.MappablePacketEndpoint.GetPacketMMapOpts.
|
||||
func (ep *endpoint) GetPacketMMapOpts(req *tcpip.TpacketReq, isRx bool) stack.PacketMMapOpts {
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
|
||||
return stack.PacketMMapOpts{
|
||||
Req: req,
|
||||
IsRx: isRx,
|
||||
Cooked: ep.cooked,
|
||||
Stack: ep.stack,
|
||||
Wq: ep.waiterQueue,
|
||||
PacketEndpoint: ep,
|
||||
Version: int(ep.packetMMapVersion),
|
||||
Reserve: uint32(ep.packetMMapReserve),
|
||||
}
|
||||
}
|
||||
|
||||
// SetPacketMMapEndpoint implements
|
||||
// stack.MappablePacketEndpoint.SetPacketMMapEndpoint.
|
||||
func (ep *endpoint) SetPacketMMapEndpoint(m stack.PacketMMapEndpoint) {
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
ep.packetMMapEp = m
|
||||
}
|
||||
|
||||
// GetPacketMMapEndpoint implements
|
||||
// stack.MappablePacketEndpoint.GetPacketMMapEndpoint.
|
||||
func (ep *endpoint) GetPacketMMapEndpoint() stack.PacketMMapEndpoint {
|
||||
ep.packetMmapMu.RLock()
|
||||
defer ep.packetMmapMu.RUnlock()
|
||||
return ep.packetMMapEp
|
||||
}
|
||||
96
pkg/tcpip/transport/packet/endpoint_mutex.go
Normal file
96
pkg/tcpip/transport/packet/endpoint_mutex.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// RWMutex is sync.RWMutex with the correctness validator.
|
||||
type endpointRWMutex struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var endpointlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type endpointlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) Lock() {
|
||||
locking.AddGLock(endpointprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) NestedLock(i endpointlockNameIndex) {
|
||||
locking.AddGLock(endpointprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) Unlock() {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(endpointprefixIndex, -1)
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) NestedUnlock(i endpointlockNameIndex) {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(endpointprefixIndex, int(i))
|
||||
}
|
||||
|
||||
// RLock locks m for reading.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RLock() {
|
||||
locking.AddGLock(endpointprefixIndex, -1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RUnlock() {
|
||||
m.mu.RUnlock()
|
||||
locking.DelGLock(endpointprefixIndex, -1)
|
||||
}
|
||||
|
||||
// RLockBypass locks m for reading without executing the validator.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RLockBypass() {
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlockBypass undoes a single RLockBypass call.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RUnlockBypass() {
|
||||
m.mu.RUnlock()
|
||||
}
|
||||
|
||||
// DowngradeLock atomically unlocks rw for writing and locks it for reading.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) DowngradeLock() {
|
||||
m.mu.DowngradeLock()
|
||||
}
|
||||
|
||||
var endpointprefixIndex *locking.MutexClass
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func endpointinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
endpointinitLockNames()
|
||||
endpointprefixIndex = locking.NewMutexClass(reflect.TypeOf(endpointRWMutex{}), endpointlockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/packet/endpoint_rcv_mutex.go
Normal file
64
pkg/tcpip/transport/packet/endpoint_rcv_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type rcvMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var rcvprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var rcvlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type rcvlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) Lock() {
|
||||
locking.AddGLock(rcvprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) NestedLock(i rcvlockNameIndex) {
|
||||
locking.AddGLock(rcvprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) Unlock() {
|
||||
locking.DelGLock(rcvprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) NestedUnlock(i rcvlockNameIndex) {
|
||||
locking.DelGLock(rcvprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func rcvinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
rcvinitLockNames()
|
||||
rcvprefixIndex = locking.NewMutexClass(reflect.TypeOf(rcvMutex{}), rcvlockNames)
|
||||
}
|
||||
74
pkg/tcpip/transport/packet/endpoint_state.go
Normal file
74
pkg/tcpip/transport/packet/endpoint_state.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 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 packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
func (p *packet) saveReceivedAt() int64 {
|
||||
return p.receivedAt.UnixNano()
|
||||
}
|
||||
|
||||
// loadReceivedAt is invoked by stateify.
|
||||
func (p *packet) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
p.receivedAt = time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (ep *endpoint) beforeSave() {
|
||||
ep.rcvMu.Lock()
|
||||
ep.rcvDisabled = true
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.RegisterResumableEndpoint(ep)
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (ep *endpoint) afterLoad(ctx context.Context) {
|
||||
if !ep.stack.IsSaveRestoreEnabled() {
|
||||
ep.mu.Lock()
|
||||
ep.stack = stack.RestoreStackFromContext(ctx)
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
ep.stack.RegisterRestoredEndpoint(ep)
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (ep *endpoint) Restore(_ *stack.Stack) {
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
if err := ep.stack.RegisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep); err != nil {
|
||||
panic(fmt.Sprintf("RegisterPacketEndpoint(%d, %d, _): %s", ep.boundNIC, ep.boundNetProto, err))
|
||||
}
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
ep.rcvDisabled = false
|
||||
ep.rcvMu.Unlock()
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (ep *endpoint) Resume() {
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
ep.rcvDisabled = false
|
||||
}
|
||||
64
pkg/tcpip/transport/packet/last_error_mutex.go
Normal file
64
pkg/tcpip/transport/packet/last_error_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type lastErrorMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var lastErrorprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var lastErrorlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type lastErrorlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Lock() {
|
||||
locking.AddGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedLock(i lastErrorlockNameIndex) {
|
||||
locking.AddGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Unlock() {
|
||||
locking.DelGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedUnlock(i lastErrorlockNameIndex) {
|
||||
locking.DelGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func lastErrorinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
lastErrorinitLockNames()
|
||||
lastErrorprefixIndex = locking.NewMutexClass(reflect.TypeOf(lastErrorMutex{}), lastErrorlockNames)
|
||||
}
|
||||
239
pkg/tcpip/transport/packet/packet_list.go
Normal file
239
pkg/tcpip/transport/packet/packet_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package packet
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type packetElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (packetElementMapper) linkerFor(elem *packet) *packet { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type packetList struct {
|
||||
head *packet
|
||||
tail *packet
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *packetList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) Front() *packet {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) Back() *packet {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (packetElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) PushFront(e *packet) {
|
||||
linker := packetElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
packetElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) PushFrontList(m *packetList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
packetElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
packetElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) PushBack(e *packet) {
|
||||
linker := packetElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
packetElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) PushBackList(m *packetList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
packetElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
packetElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) InsertAfter(b, e *packet) {
|
||||
bLinker := packetElementMapper{}.linkerFor(b)
|
||||
eLinker := packetElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
packetElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) InsertBefore(a, e *packet) {
|
||||
aLinker := packetElementMapper{}.linkerFor(a)
|
||||
eLinker := packetElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
packetElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *packetList) Remove(e *packet) {
|
||||
linker := packetElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
packetElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
packetElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type packetEntry struct {
|
||||
next *packet
|
||||
prev *packet
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *packetEntry) Next() *packet {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *packetEntry) Prev() *packet {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *packetEntry) SetNext(elem *packet) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *packetEntry) SetPrev(elem *packet) {
|
||||
e.prev = elem
|
||||
}
|
||||
96
pkg/tcpip/transport/packet/packet_mmap_mutex.go
Normal file
96
pkg/tcpip/transport/packet/packet_mmap_mutex.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// RWMutex is sync.RWMutex with the correctness validator.
|
||||
type packetMmapRWMutex struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var packetMmaplockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type packetMmaplockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) Lock() {
|
||||
locking.AddGLock(packetMmapprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) NestedLock(i packetMmaplockNameIndex) {
|
||||
locking.AddGLock(packetMmapprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) Unlock() {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, -1)
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) NestedUnlock(i packetMmaplockNameIndex) {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, int(i))
|
||||
}
|
||||
|
||||
// RLock locks m for reading.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RLock() {
|
||||
locking.AddGLock(packetMmapprefixIndex, -1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RUnlock() {
|
||||
m.mu.RUnlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, -1)
|
||||
}
|
||||
|
||||
// RLockBypass locks m for reading without executing the validator.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RLockBypass() {
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlockBypass undoes a single RLockBypass call.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RUnlockBypass() {
|
||||
m.mu.RUnlock()
|
||||
}
|
||||
|
||||
// DowngradeLock atomically unlocks rw for writing and locks it for reading.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) DowngradeLock() {
|
||||
m.mu.DowngradeLock()
|
||||
}
|
||||
|
||||
var packetMmapprefixIndex *locking.MutexClass
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func packetMmapinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
packetMmapinitLockNames()
|
||||
packetMmapprefixIndex = locking.NewMutexClass(reflect.TypeOf(packetMmapRWMutex{}), packetMmaplockNames)
|
||||
}
|
||||
181
pkg/tcpip/transport/packet/packet_state_autogen.go
Normal file
181
pkg/tcpip/transport/packet/packet_state_autogen.go
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (p *packet) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/packet.packet"
|
||||
}
|
||||
|
||||
func (p *packet) StateFields() []string {
|
||||
return []string{
|
||||
"packetEntry",
|
||||
"data",
|
||||
"receivedAt",
|
||||
"senderAddr",
|
||||
"packetInfo",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *packet) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *packet) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
stateSinkObject.SaveValue(2, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.packetEntry)
|
||||
stateSinkObject.Save(1, &p.data)
|
||||
stateSinkObject.Save(3, &p.senderAddr)
|
||||
stateSinkObject.Save(4, &p.packetInfo)
|
||||
}
|
||||
|
||||
func (p *packet) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *packet) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.packetEntry)
|
||||
stateSourceObject.Load(1, &p.data)
|
||||
stateSourceObject.Load(3, &p.senderAddr)
|
||||
stateSourceObject.Load(4, &p.packetInfo)
|
||||
stateSourceObject.LoadValue(2, new(int64), func(y any) { p.loadReceivedAt(ctx, y.(int64)) })
|
||||
}
|
||||
|
||||
func (ep *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/packet.endpoint"
|
||||
}
|
||||
|
||||
func (ep *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"DefaultSocketOptionsHandler",
|
||||
"stack",
|
||||
"waiterQueue",
|
||||
"cooked",
|
||||
"ops",
|
||||
"stats",
|
||||
"rcvList",
|
||||
"rcvBufSize",
|
||||
"rcvClosed",
|
||||
"rcvDisabled",
|
||||
"closed",
|
||||
"boundNetProto",
|
||||
"boundNIC",
|
||||
"lastError",
|
||||
"packetMMapVersion",
|
||||
"packetMMapReserve",
|
||||
"packetMMapEp",
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (ep *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
ep.beforeSave()
|
||||
stateSinkObject.Save(0, &ep.DefaultSocketOptionsHandler)
|
||||
stateSinkObject.Save(1, &ep.stack)
|
||||
stateSinkObject.Save(2, &ep.waiterQueue)
|
||||
stateSinkObject.Save(3, &ep.cooked)
|
||||
stateSinkObject.Save(4, &ep.ops)
|
||||
stateSinkObject.Save(5, &ep.stats)
|
||||
stateSinkObject.Save(6, &ep.rcvList)
|
||||
stateSinkObject.Save(7, &ep.rcvBufSize)
|
||||
stateSinkObject.Save(8, &ep.rcvClosed)
|
||||
stateSinkObject.Save(9, &ep.rcvDisabled)
|
||||
stateSinkObject.Save(10, &ep.closed)
|
||||
stateSinkObject.Save(11, &ep.boundNetProto)
|
||||
stateSinkObject.Save(12, &ep.boundNIC)
|
||||
stateSinkObject.Save(13, &ep.lastError)
|
||||
stateSinkObject.Save(14, &ep.packetMMapVersion)
|
||||
stateSinkObject.Save(15, &ep.packetMMapReserve)
|
||||
stateSinkObject.Save(16, &ep.packetMMapEp)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (ep *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &ep.DefaultSocketOptionsHandler)
|
||||
stateSourceObject.Load(1, &ep.stack)
|
||||
stateSourceObject.Load(2, &ep.waiterQueue)
|
||||
stateSourceObject.Load(3, &ep.cooked)
|
||||
stateSourceObject.Load(4, &ep.ops)
|
||||
stateSourceObject.Load(5, &ep.stats)
|
||||
stateSourceObject.Load(6, &ep.rcvList)
|
||||
stateSourceObject.Load(7, &ep.rcvBufSize)
|
||||
stateSourceObject.Load(8, &ep.rcvClosed)
|
||||
stateSourceObject.Load(9, &ep.rcvDisabled)
|
||||
stateSourceObject.Load(10, &ep.closed)
|
||||
stateSourceObject.Load(11, &ep.boundNetProto)
|
||||
stateSourceObject.Load(12, &ep.boundNIC)
|
||||
stateSourceObject.Load(13, &ep.lastError)
|
||||
stateSourceObject.Load(14, &ep.packetMMapVersion)
|
||||
stateSourceObject.Load(15, &ep.packetMMapReserve)
|
||||
stateSourceObject.Load(16, &ep.packetMMapEp)
|
||||
stateSourceObject.AfterLoad(func() { ep.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (l *packetList) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/packet.packetList"
|
||||
}
|
||||
|
||||
func (l *packetList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *packetList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *packetList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *packetList) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *packetList) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *packetEntry) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/packet.packetEntry"
|
||||
}
|
||||
|
||||
func (e *packetEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *packetEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *packetEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *packetEntry) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *packetEntry) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*packet)(nil))
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*packetList)(nil))
|
||||
state.Register((*packetEntry)(nil))
|
||||
}
|
||||
783
pkg/tcpip/transport/raw/endpoint.go
Normal file
783
pkg/tcpip/transport/raw/endpoint.go
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
// 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 raw provides the implementation of raw sockets (see raw(7)). Raw
|
||||
// sockets allow applications to:
|
||||
//
|
||||
// - manually write and inspect transport layer headers and payloads
|
||||
// - receive all traffic of a given transport protocol (e.g. ICMP or UDP)
|
||||
// - optionally write and inspect network layer headers of packets
|
||||
//
|
||||
// Raw sockets don't have any notion of ports, and incoming packets are
|
||||
// demultiplexed solely by protocol number. Thus, a raw UDP endpoint will
|
||||
// receive every UDP packet received by netstack. bind(2) and connect(2) can be
|
||||
// used to filter incoming packets by source and destination.
|
||||
package raw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/checksum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/internal/network"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type rawPacket struct {
|
||||
rawPacketEntry
|
||||
// data holds the actual packet data, including any headers and
|
||||
// payload.
|
||||
data *stack.PacketBuffer
|
||||
receivedAt time.Time `state:".(int64)"`
|
||||
// senderAddr is the network address of the sender.
|
||||
senderAddr tcpip.FullAddress
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
|
||||
// tosOrTClass stores either the Type of Service for IPv4 or the Traffic Class
|
||||
// for IPv6.
|
||||
tosOrTClass uint8
|
||||
// ttlOrHopLimit stores either the TTL for IPv4 or the HopLimit for IPv6
|
||||
ttlOrHopLimit uint8
|
||||
}
|
||||
|
||||
// endpoint is the raw socket implementation of tcpip.Endpoint. It is legal to
|
||||
// have goroutines make concurrent calls into the endpoint.
|
||||
//
|
||||
// Lock order:
|
||||
//
|
||||
// endpoint.mu
|
||||
// endpoint.rcvMu
|
||||
//
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
tcpip.DefaultSocketOptionsHandler
|
||||
|
||||
// The following fields are initialized at creation time and are
|
||||
// immutable.
|
||||
stack *stack.Stack
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
waiterQueue *waiter.Queue
|
||||
associated bool
|
||||
|
||||
net network.Endpoint
|
||||
stats tcpip.TransportEndpointStats
|
||||
ops tcpip.SocketOptions
|
||||
|
||||
rcvMu sync.Mutex `state:"nosave"`
|
||||
// +checklocks:rcvMu
|
||||
rcvList rawPacketList
|
||||
// +checklocks:rcvMu
|
||||
rcvBufSize int
|
||||
// +checklocks:rcvMu
|
||||
rcvClosed bool
|
||||
// +checklocks:rcvMu
|
||||
rcvDisabled bool
|
||||
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
|
||||
// ipv6ChecksumOffset indicates the offset to populate the IPv6 checksum at.
|
||||
//
|
||||
// A negative value indicates no checksum should be calculated.
|
||||
//
|
||||
// +checklocks:mu
|
||||
ipv6ChecksumOffset int
|
||||
// icmp6Filter holds the filter for ICMPv6 packets.
|
||||
//
|
||||
// +checklocks:mu
|
||||
icmpv6Filter tcpip.ICMPv6Filter
|
||||
}
|
||||
|
||||
// NewEndpoint returns a raw endpoint for the given protocols.
|
||||
func NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return newEndpoint(stack, netProto, transProto, waiterQueue, true /* associated */)
|
||||
}
|
||||
|
||||
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue, associated bool) (tcpip.Endpoint, tcpip.Error) {
|
||||
// Calculating the upper-layer checksum is disabled by default for raw IPv6
|
||||
// endpoints, unless the upper-layer protocol is ICMPv6.
|
||||
//
|
||||
// As per RFC 3542 section 3.1,
|
||||
//
|
||||
// The kernel will calculate and insert the ICMPv6 checksum for ICMPv6
|
||||
// raw sockets, since this checksum is mandatory.
|
||||
ipv6ChecksumOffset := -1
|
||||
if netProto == header.IPv6ProtocolNumber && transProto == header.ICMPv6ProtocolNumber {
|
||||
ipv6ChecksumOffset = header.ICMPv6ChecksumOffset
|
||||
}
|
||||
|
||||
e := &endpoint{
|
||||
stack: s,
|
||||
transProto: transProto,
|
||||
waiterQueue: waiterQueue,
|
||||
associated: associated,
|
||||
ipv6ChecksumOffset: ipv6ChecksumOffset,
|
||||
}
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
e.ops.SetMulticastLoop(true)
|
||||
e.ops.SetHeaderIncluded(!associated)
|
||||
e.ops.SetSendBufferSize(32*1024, false /* notify */)
|
||||
e.ops.SetReceiveBufferSize(32*1024, false /* notify */)
|
||||
e.net.Init(s, netProto, transProto, &e.ops, waiterQueue)
|
||||
|
||||
// Override with stack defaults.
|
||||
var ss tcpip.SendBufferSizeOption
|
||||
if err := s.Option(&ss); err == nil {
|
||||
e.ops.SetSendBufferSize(int64(ss.Default), false /* notify */)
|
||||
}
|
||||
|
||||
var rs tcpip.ReceiveBufferSizeOption
|
||||
if err := s.Option(&rs); err == nil {
|
||||
e.ops.SetReceiveBufferSize(int64(rs.Default), false /* notify */)
|
||||
}
|
||||
|
||||
// Unassociated endpoints are write-only and users call Write() with IP
|
||||
// headers included. Because they're write-only, We don't need to
|
||||
// register with the stack.
|
||||
if !associated {
|
||||
e.ops.SetReceiveBufferSize(0, false /* notify */)
|
||||
e.waiterQueue = nil
|
||||
return e, nil
|
||||
}
|
||||
|
||||
if err := e.stack.RegisterRawTransportEndpoint(netProto, e.transProto, e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// WakeupWriters implements tcpip.SocketOptionsHandler.
|
||||
func (e *endpoint) WakeupWriters() {
|
||||
e.net.MaybeSignalWritable()
|
||||
}
|
||||
|
||||
// HasNIC implements tcpip.SocketOptionsHandler.
|
||||
func (e *endpoint) HasNIC(id int32) bool {
|
||||
return e.stack.HasNIC(tcpip.NICID(id))
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
func (e *endpoint) Abort() {
|
||||
e.Close()
|
||||
}
|
||||
|
||||
// Close implements tcpip.Endpoint.Close.
|
||||
func (e *endpoint) Close() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if e.net.State() == transport.DatagramEndpointStateClosed {
|
||||
return
|
||||
}
|
||||
|
||||
e.net.Close()
|
||||
|
||||
if !e.associated {
|
||||
return
|
||||
}
|
||||
|
||||
e.stack.UnregisterRawTransportEndpoint(e.net.NetProto(), e.transProto, e)
|
||||
|
||||
e.rcvMu.Lock()
|
||||
defer e.rcvMu.Unlock()
|
||||
|
||||
// Clear the receive list.
|
||||
e.rcvClosed = true
|
||||
e.rcvBufSize = 0
|
||||
for !e.rcvList.Empty() {
|
||||
p := e.rcvList.Front()
|
||||
e.rcvList.Remove(p)
|
||||
p.data.DecRef()
|
||||
}
|
||||
|
||||
e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
}
|
||||
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (*endpoint) ModerateRecvBuf(int) {}
|
||||
|
||||
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
|
||||
e.net.SetOwner(owner)
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
|
||||
e.rcvMu.Lock()
|
||||
|
||||
// If there's no data to read, return that read would block or that the
|
||||
// endpoint is closed.
|
||||
if e.rcvList.Empty() {
|
||||
var err tcpip.Error = &tcpip.ErrWouldBlock{}
|
||||
if e.rcvClosed {
|
||||
e.stats.ReadErrors.ReadClosed.Increment()
|
||||
err = &tcpip.ErrClosedForReceive{}
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
return tcpip.ReadResult{}, err
|
||||
}
|
||||
|
||||
pkt := e.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
e.rcvList.Remove(pkt)
|
||||
defer pkt.data.DecRef()
|
||||
e.rcvBufSize -= pkt.data.Data().Size()
|
||||
}
|
||||
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
// Control Messages
|
||||
// TODO(https://gvisor.dev/issue/7012): Share control message code with other
|
||||
// network endpoints.
|
||||
cm := tcpip.ReceivableControlMessages{
|
||||
HasTimestamp: true,
|
||||
Timestamp: pkt.receivedAt,
|
||||
}
|
||||
switch netProto := e.net.NetProto(); netProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
if e.ops.GetReceiveTOS() {
|
||||
cm.HasTOS = true
|
||||
cm.TOS = pkt.tosOrTClass
|
||||
}
|
||||
if e.ops.GetReceiveTTL() {
|
||||
cm.HasTTL = true
|
||||
cm.TTL = pkt.ttlOrHopLimit
|
||||
}
|
||||
if e.ops.GetReceivePacketInfo() {
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = pkt.packetInfo
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
if e.ops.GetReceiveTClass() {
|
||||
cm.HasTClass = true
|
||||
// Although TClass is an 8-bit value it's read in the CMsg as a uint32.
|
||||
cm.TClass = uint32(pkt.tosOrTClass)
|
||||
}
|
||||
if e.ops.GetReceiveHopLimit() {
|
||||
cm.HasHopLimit = true
|
||||
cm.HopLimit = pkt.ttlOrHopLimit
|
||||
}
|
||||
if e.ops.GetIPv6ReceivePacketInfo() {
|
||||
cm.HasIPv6PacketInfo = true
|
||||
cm.IPv6PacketInfo = tcpip.IPv6PacketInfo{
|
||||
NIC: pkt.packetInfo.NIC,
|
||||
Addr: pkt.packetInfo.DestinationAddr,
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized network protocol = %d", netProto))
|
||||
}
|
||||
|
||||
res := tcpip.ReadResult{
|
||||
Total: pkt.data.Data().Size(),
|
||||
ControlMessages: cm,
|
||||
}
|
||||
if opts.NeedRemoteAddr {
|
||||
res.RemoteAddr = pkt.senderAddr
|
||||
}
|
||||
|
||||
n, err := pkt.data.Data().ReadTo(dst, opts.Peek)
|
||||
if n == 0 && err != nil {
|
||||
return res, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
res.Count = n
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Write implements tcpip.Endpoint.Write.
|
||||
func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
netProto := e.net.NetProto()
|
||||
// We can create, but not write to, unassociated IPv6 endpoints.
|
||||
if !e.associated && netProto == header.IPv6ProtocolNumber {
|
||||
return 0, &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
if opts.To != nil {
|
||||
// Raw sockets do not support sending to a IPv4 address on a IPv6 endpoint.
|
||||
if netProto == header.IPv6ProtocolNumber && opts.To.Addr.BitLen() != header.IPv6AddressSizeBits {
|
||||
return 0, &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
}
|
||||
|
||||
n, err := e.write(p, opts)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
e.stats.PacketsSent.Increment()
|
||||
case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
|
||||
e.stats.WriteErrors.InvalidArgs.Increment()
|
||||
case *tcpip.ErrClosedForSend:
|
||||
e.stats.WriteErrors.WriteClosed.Increment()
|
||||
case *tcpip.ErrInvalidEndpointState:
|
||||
e.stats.WriteErrors.InvalidEndpointState.Increment()
|
||||
case *tcpip.ErrHostUnreachable, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
|
||||
// Errors indicating any problem with IP routing of the packet.
|
||||
e.stats.SendErrors.NoRoute.Increment()
|
||||
default:
|
||||
// For all other errors when writing to the network layer.
|
||||
e.stats.SendErrors.SendToNetworkFailed.Increment()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
e.mu.Lock()
|
||||
ctx, err := e.net.AcquireContextForWrite(opts)
|
||||
ipv6ChecksumOffset := e.ipv6ChecksumOffset
|
||||
e.mu.Unlock()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ctx.Release()
|
||||
|
||||
if p.Len() > int(ctx.MTU()) {
|
||||
return 0, &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
|
||||
// Prevents giant buffer allocations.
|
||||
if p.Len() > header.DatagramMaximumSize {
|
||||
return 0, &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
|
||||
var payload buffer.Buffer
|
||||
defer payload.Release()
|
||||
if _, err := payload.WriteFromReader(p, int64(p.Len())); err != nil {
|
||||
return 0, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
payloadSz := payload.Size()
|
||||
|
||||
if packetInfo := ctx.PacketInfo(); packetInfo.NetProto == header.IPv6ProtocolNumber && ipv6ChecksumOffset >= 0 {
|
||||
// Make sure we can fit the checksum.
|
||||
if payload.Size() < int64(ipv6ChecksumOffset+checksum.Size) {
|
||||
return 0, &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
payloadView, _ := payload.PullUp(ipv6ChecksumOffset, int(payload.Size())-ipv6ChecksumOffset)
|
||||
xsum := header.PseudoHeaderChecksum(e.transProto, packetInfo.LocalAddress, packetInfo.RemoteAddress, uint16(payload.Size()))
|
||||
checksum.Put(payloadView.AsSlice(), 0)
|
||||
xsum = checksum.Combine(payload.Checksum(0), xsum)
|
||||
checksum.Put(payloadView.AsSlice(), ^xsum)
|
||||
}
|
||||
|
||||
pkt := ctx.TryNewPacketBuffer(int(ctx.PacketInfo().MaxHeaderLength), payload.Clone())
|
||||
if pkt == nil {
|
||||
return 0, &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
defer pkt.DecRef()
|
||||
|
||||
if err := ctx.WritePacket(pkt, e.ops.GetHeaderIncluded()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return payloadSz, nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
func (*endpoint) Disconnect() tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Connect implements tcpip.Endpoint.Connect.
|
||||
func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
|
||||
netProto := e.net.NetProto()
|
||||
|
||||
// Raw sockets do not support connecting to a IPv4 address on a IPv6 endpoint.
|
||||
if netProto == header.IPv6ProtocolNumber && addr.Addr.BitLen() != header.IPv6AddressSizeBits {
|
||||
return &tcpip.ErrAddressFamilyNotSupported{}
|
||||
}
|
||||
|
||||
return e.net.ConnectAndThen(addr, func(_ tcpip.NetworkProtocolNumber, _, _ stack.TransportEndpointID) tcpip.Error {
|
||||
if e.associated {
|
||||
// Re-register the endpoint with the appropriate NIC.
|
||||
if err := e.stack.RegisterRawTransportEndpoint(netProto, e.transProto, e); err != nil {
|
||||
return err
|
||||
}
|
||||
e.stack.UnregisterRawTransportEndpoint(netProto, e.transProto, e)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown implements tcpip.Endpoint.Shutdown. It's a noop for raw sockets.
|
||||
func (e *endpoint) Shutdown(tcpip.ShutdownFlags) tcpip.Error {
|
||||
if e.net.State() != transport.DatagramEndpointStateConnected {
|
||||
return &tcpip.ErrNotConnected{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements tcpip.Endpoint.Listen.
|
||||
func (*endpoint) Listen(int) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Accept implements tcpip.Endpoint.Accept.
|
||||
func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
|
||||
return nil, nil, &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// Bind implements tcpip.Endpoint.Bind.
|
||||
func (e *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
|
||||
return e.net.BindAndThen(addr, func(netProto tcpip.NetworkProtocolNumber, _ tcpip.Address) tcpip.Error {
|
||||
if !e.associated {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Re-register the endpoint with the appropriate NIC.
|
||||
if err := e.stack.RegisterRawTransportEndpoint(netProto, e.transProto, e); err != nil {
|
||||
return err
|
||||
}
|
||||
e.stack.UnregisterRawTransportEndpoint(netProto, e.transProto, e)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
|
||||
func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
a := e.net.GetLocalAddress()
|
||||
// Linux returns the protocol in the port field.
|
||||
a.Port = uint16(e.transProto)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
|
||||
func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
// Even a connected socket doesn't return a remote address.
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
// Readiness implements tcpip.Endpoint.Readiness.
|
||||
func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
var result waiter.EventMask
|
||||
|
||||
if e.net.HasSendSpace() {
|
||||
result |= waiter.WritableEvents & mask
|
||||
}
|
||||
|
||||
// Determine whether the endpoint is readable.
|
||||
if (mask & waiter.ReadableEvents) != 0 {
|
||||
e.rcvMu.Lock()
|
||||
if !e.rcvList.Empty() || e.rcvClosed {
|
||||
result |= waiter.ReadableEvents
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
|
||||
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
switch opt := opt.(type) {
|
||||
case *tcpip.SocketDetachFilterOption:
|
||||
return nil
|
||||
|
||||
case *tcpip.ICMPv6Filter:
|
||||
if e.net.NetProto() != header.IPv6ProtocolNumber {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
if e.transProto != header.ICMPv6ProtocolNumber {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.icmpv6Filter = *opt
|
||||
return nil
|
||||
default:
|
||||
return e.net.SetSockOpt(opt)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
switch opt {
|
||||
case tcpip.IPv6Checksum:
|
||||
if e.net.NetProto() != header.IPv6ProtocolNumber {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
if e.transProto == header.ICMPv6ProtocolNumber {
|
||||
// As per RFC 3542 section 3.1,
|
||||
//
|
||||
// An attempt to set IPV6_CHECKSUM for an ICMPv6 socket will fail.
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
// Make sure the offset is aligned properly if checksum is requested.
|
||||
if v > 0 && v%checksum.Size != 0 {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.ipv6ChecksumOffset = v
|
||||
return nil
|
||||
default:
|
||||
return e.net.SetSockOptInt(opt, v)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
|
||||
switch opt := opt.(type) {
|
||||
case *tcpip.ICMPv6Filter:
|
||||
if e.net.NetProto() != header.IPv6ProtocolNumber {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
if e.transProto != header.ICMPv6ProtocolNumber {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
*opt = e.icmpv6Filter
|
||||
return nil
|
||||
|
||||
default:
|
||||
return e.net.GetSockOpt(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
|
||||
func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
switch opt {
|
||||
case tcpip.ReceiveQueueSizeOption:
|
||||
v := 0
|
||||
e.rcvMu.Lock()
|
||||
if !e.rcvList.Empty() {
|
||||
p := e.rcvList.Front()
|
||||
v = p.data.Data().Size()
|
||||
}
|
||||
e.rcvMu.Unlock()
|
||||
return v, nil
|
||||
|
||||
case tcpip.IPv6Checksum:
|
||||
if e.net.NetProto() != header.IPv6ProtocolNumber {
|
||||
return 0, &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.ipv6ChecksumOffset, nil
|
||||
|
||||
default:
|
||||
return e.net.GetSockOptInt(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePacket implements stack.RawTransportEndpoint.HandlePacket.
|
||||
func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
notifyReadableEvents := func() bool {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
e.rcvMu.Lock()
|
||||
defer e.rcvMu.Unlock()
|
||||
|
||||
// Drop the packet if our buffer is currently full or if this is an unassociated
|
||||
// endpoint (i.e endpoint created w/ IPPROTO_RAW). Such endpoints are send only
|
||||
// See: https://man7.org/linux/man-pages/man7/raw.7.html
|
||||
//
|
||||
// An IPPROTO_RAW socket is send only. If you really want to receive
|
||||
// all IP packets, use a packet(7) socket with the ETH_P_IP protocol.
|
||||
// Note that packet sockets don't reassemble IP fragments, unlike raw
|
||||
// sockets.
|
||||
if e.rcvClosed || !e.associated {
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.ClosedReceiver.Increment()
|
||||
return false
|
||||
}
|
||||
|
||||
rcvBufSize := e.ops.GetReceiveBufferSize()
|
||||
if e.rcvDisabled || e.rcvBufSize >= int(rcvBufSize) {
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
e.stats.ReceiveErrors.ReceiveBufferOverflow.Increment()
|
||||
return false
|
||||
}
|
||||
|
||||
net := pkt.Network()
|
||||
dstAddr := net.DestinationAddress()
|
||||
srcAddr := net.SourceAddress()
|
||||
info := e.net.Info()
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial:
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
// If connected, only accept packets from the remote address we
|
||||
// connected to.
|
||||
if info.ID.RemoteAddress != srcAddr {
|
||||
return false
|
||||
}
|
||||
|
||||
// Connected sockets may also have been bound to a specific
|
||||
// address/NIC.
|
||||
fallthrough
|
||||
case transport.DatagramEndpointStateBound:
|
||||
// If bound to a NIC, only accept data for that NIC.
|
||||
if info.BindNICID != 0 && info.BindNICID != pkt.NICID {
|
||||
return false
|
||||
}
|
||||
|
||||
// If bound to an address, only accept data for that address.
|
||||
if info.BindAddr != (tcpip.Address{}) && info.BindAddr != dstAddr {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
|
||||
wasEmpty := e.rcvBufSize == 0
|
||||
|
||||
// Push new packet into receive list and increment the buffer size.
|
||||
packet := &rawPacket{
|
||||
senderAddr: tcpip.FullAddress{
|
||||
NIC: pkt.NICID,
|
||||
Addr: srcAddr,
|
||||
},
|
||||
packetInfo: tcpip.IPPacketInfo{
|
||||
// TODO(gvisor.dev/issue/3556): dstAddr may be a multicast or broadcast
|
||||
// address. LocalAddr should hold a unicast address that can be
|
||||
// used to respond to the incoming packet.
|
||||
LocalAddr: dstAddr,
|
||||
DestinationAddr: dstAddr,
|
||||
NIC: pkt.NICID,
|
||||
},
|
||||
}
|
||||
|
||||
// Save any useful information from the network header to the packet.
|
||||
packet.tosOrTClass, _ = pkt.Network().TOS()
|
||||
switch pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
packet.ttlOrHopLimit = header.IPv4(pkt.NetworkHeader().Slice()).TTL()
|
||||
case header.IPv6ProtocolNumber:
|
||||
packet.ttlOrHopLimit = header.IPv6(pkt.NetworkHeader().Slice()).HopLimit()
|
||||
}
|
||||
|
||||
// Raw IPv4 endpoints return the IP header, but IPv6 endpoints do not.
|
||||
// We copy headers' underlying bytes because pkt.*Header may point to
|
||||
// the middle of a slice, and another struct may point to the "outer"
|
||||
// slice. Save/restore doesn't support overlapping slices and will fail.
|
||||
//
|
||||
// TODO(https://gvisor.dev/issue/6517): Avoid the copy once S/R supports
|
||||
// overlapping slices.
|
||||
transportHeader := pkt.TransportHeader().Slice()
|
||||
var combinedBuf buffer.Buffer
|
||||
defer combinedBuf.Release()
|
||||
switch info.NetProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
networkHeader := pkt.NetworkHeader().Slice()
|
||||
headers := buffer.NewView(len(networkHeader) + len(transportHeader))
|
||||
headers.Write(networkHeader)
|
||||
headers.Write(transportHeader)
|
||||
combinedBuf = buffer.MakeWithView(headers)
|
||||
pktBuf := pkt.Data().ToBuffer()
|
||||
combinedBuf.Merge(&pktBuf)
|
||||
case header.IPv6ProtocolNumber:
|
||||
/*if e.transProto == header.ICMPv6ProtocolNumber {
|
||||
if len(transportHeader) < header.ICMPv6MinimumSize {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.icmpv6Filter.ShouldDeny(uint8(header.ICMPv6(transportHeader).Type())) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
combinedBuf = buffer.MakeWithView(pkt.TransportHeader().View())
|
||||
pktBuf := pkt.Data().ToBuffer()
|
||||
combinedBuf.Merge(&pktBuf)
|
||||
|
||||
if checksumOffset := e.ipv6ChecksumOffset; checksumOffset >= 0 {
|
||||
bufSize := int(combinedBuf.Size())
|
||||
if bufSize < checksumOffset+checksum.Size {
|
||||
// Message too small to fit checksum.
|
||||
return false
|
||||
}
|
||||
|
||||
xsum := header.PseudoHeaderChecksum(e.transProto, srcAddr, dstAddr, uint16(bufSize))
|
||||
xsum = checksum.Combine(combinedBuf.Checksum(0), xsum)
|
||||
if xsum != 0xFFFF {
|
||||
// Invalid checksum.
|
||||
return false
|
||||
}
|
||||
}*/
|
||||
networkHeader := pkt.NetworkHeader().Slice()
|
||||
headers := buffer.NewView(len(networkHeader) + len(transportHeader))
|
||||
headers.Write(networkHeader)
|
||||
headers.Write(transportHeader)
|
||||
combinedBuf = buffer.MakeWithView(headers)
|
||||
pktBuf := pkt.Data().ToBuffer()
|
||||
combinedBuf.Merge(&pktBuf)
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized protocol number = %d", info.NetProto))
|
||||
}
|
||||
|
||||
packet.data = stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: combinedBuf.Clone()})
|
||||
packet.receivedAt = e.stack.Clock().Now()
|
||||
|
||||
e.rcvList.PushBack(packet)
|
||||
e.rcvBufSize += packet.data.Data().Size()
|
||||
e.stats.PacketsReceived.Increment()
|
||||
|
||||
// Notify waiters that there is data to be read now.
|
||||
return wasEmpty
|
||||
}()
|
||||
|
||||
if notifyReadableEvents {
|
||||
e.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
// State implements socket.Socket.State.
|
||||
func (e *endpoint) State() uint32 {
|
||||
return uint32(e.net.State())
|
||||
}
|
||||
|
||||
// Info returns a copy of the endpoint info.
|
||||
func (e *endpoint) Info() tcpip.EndpointInfo {
|
||||
ret := e.net.Info()
|
||||
return &ret
|
||||
}
|
||||
|
||||
// Stats returns a pointer to the endpoint stats.
|
||||
func (e *endpoint) Stats() tcpip.EndpointStats {
|
||||
return &e.stats
|
||||
}
|
||||
|
||||
// Wait implements stack.TransportEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
// LastError implements tcpip.Endpoint.LastError.
|
||||
func (*endpoint) LastError() tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (e *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &e.ops
|
||||
}
|
||||
|
||||
func (e *endpoint) setReceiveDisabled(v bool) {
|
||||
e.rcvMu.Lock()
|
||||
defer e.rcvMu.Unlock()
|
||||
e.rcvDisabled = v
|
||||
}
|
||||
78
pkg/tcpip/transport/raw/endpoint_state.go
Normal file
78
pkg/tcpip/transport/raw/endpoint_state.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2018 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 raw
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/log"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
func (p *rawPacket) saveReceivedAt() int64 {
|
||||
return p.receivedAt.UnixNano()
|
||||
}
|
||||
|
||||
// loadReceivedAt is invoked by stateify.
|
||||
func (p *rawPacket) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
p.receivedAt = time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad(ctx context.Context) {
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (e *endpoint) beforeSave() {
|
||||
e.setReceiveDisabled(true)
|
||||
e.stack.RegisterResumableEndpoint(e)
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *endpoint) Restore(s *stack.Stack) {
|
||||
if err := e.net.Resume(s); err != nil {
|
||||
log.Warningf("Closing the raw endpoint as it cannot be restored, err: %v", err)
|
||||
e.Close()
|
||||
return
|
||||
}
|
||||
e.setReceiveDisabled(false)
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
return
|
||||
}
|
||||
|
||||
e.stack = s
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
if e.associated {
|
||||
netProto := e.net.NetProto()
|
||||
if err := e.stack.RegisterRawTransportEndpoint(netProto, e.transProto, e); err != nil {
|
||||
panic("RegisterRawTransportEndpoint failed during restore")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *endpoint) Resume() {
|
||||
e.setReceiveDisabled(false)
|
||||
}
|
||||
55
pkg/tcpip/transport/raw/protocol.go
Normal file
55
pkg/tcpip/transport/raw/protocol.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// 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 raw
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/internal/noop"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/packet"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// EndpointFactory implements stack.RawFactory.
|
||||
//
|
||||
// +stateify savable
|
||||
type EndpointFactory struct{}
|
||||
|
||||
// NewUnassociatedEndpoint implements stack.RawFactory.NewUnassociatedEndpoint.
|
||||
func (EndpointFactory) NewUnassociatedEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return newEndpoint(stack, netProto, transProto, waiterQueue, false /* associated */)
|
||||
}
|
||||
|
||||
// NewPacketEndpoint implements stack.RawFactory.NewPacketEndpoint.
|
||||
func (EndpointFactory) NewPacketEndpoint(stack *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return packet.NewEndpoint(stack, cooked, netProto, waiterQueue), nil
|
||||
}
|
||||
|
||||
// CreateOnlyFactory implements stack.RawFactory. It allows creation of raw
|
||||
// endpoints that do not support reading, writing, binding, etc.
|
||||
//
|
||||
// +stateify savable
|
||||
type CreateOnlyFactory struct{}
|
||||
|
||||
// NewUnassociatedEndpoint implements stack.RawFactory.NewUnassociatedEndpoint.
|
||||
func (CreateOnlyFactory) NewUnassociatedEndpoint(stk *stack.Stack, _ tcpip.NetworkProtocolNumber, _ tcpip.TransportProtocolNumber, _ *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return noop.New(stk), nil
|
||||
}
|
||||
|
||||
// NewPacketEndpoint implements stack.RawFactory.NewPacketEndpoint.
|
||||
func (CreateOnlyFactory) NewPacketEndpoint(*stack.Stack, bool, tcpip.NetworkProtocolNumber, *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
// This isn't needed by anything, so it isn't implemented.
|
||||
return nil, &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
239
pkg/tcpip/transport/raw/raw_packet_list.go
Normal file
239
pkg/tcpip/transport/raw/raw_packet_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package raw
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type rawPacketElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (rawPacketElementMapper) linkerFor(elem *rawPacket) *rawPacket { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type rawPacketList struct {
|
||||
head *rawPacket
|
||||
tail *rawPacket
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *rawPacketList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) Front() *rawPacket {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) Back() *rawPacket {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (rawPacketElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) PushFront(e *rawPacket) {
|
||||
linker := rawPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
rawPacketElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) PushFrontList(m *rawPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
rawPacketElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
rawPacketElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) PushBack(e *rawPacket) {
|
||||
linker := rawPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
rawPacketElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) PushBackList(m *rawPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
rawPacketElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
rawPacketElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) InsertAfter(b, e *rawPacket) {
|
||||
bLinker := rawPacketElementMapper{}.linkerFor(b)
|
||||
eLinker := rawPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
rawPacketElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) InsertBefore(a, e *rawPacket) {
|
||||
aLinker := rawPacketElementMapper{}.linkerFor(a)
|
||||
eLinker := rawPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
rawPacketElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *rawPacketList) Remove(e *rawPacket) {
|
||||
linker := rawPacketElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
rawPacketElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
rawPacketElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type rawPacketEntry struct {
|
||||
next *rawPacket
|
||||
prev *rawPacket
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *rawPacketEntry) Next() *rawPacket {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *rawPacketEntry) Prev() *rawPacket {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *rawPacketEntry) SetNext(elem *rawPacket) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *rawPacketEntry) SetPrev(elem *rawPacket) {
|
||||
e.prev = elem
|
||||
}
|
||||
222
pkg/tcpip/transport/raw/raw_state_autogen.go
Normal file
222
pkg/tcpip/transport/raw/raw_state_autogen.go
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package raw
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (p *rawPacket) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.rawPacket"
|
||||
}
|
||||
|
||||
func (p *rawPacket) StateFields() []string {
|
||||
return []string{
|
||||
"rawPacketEntry",
|
||||
"data",
|
||||
"receivedAt",
|
||||
"senderAddr",
|
||||
"packetInfo",
|
||||
"tosOrTClass",
|
||||
"ttlOrHopLimit",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *rawPacket) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *rawPacket) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
stateSinkObject.SaveValue(2, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.rawPacketEntry)
|
||||
stateSinkObject.Save(1, &p.data)
|
||||
stateSinkObject.Save(3, &p.senderAddr)
|
||||
stateSinkObject.Save(4, &p.packetInfo)
|
||||
stateSinkObject.Save(5, &p.tosOrTClass)
|
||||
stateSinkObject.Save(6, &p.ttlOrHopLimit)
|
||||
}
|
||||
|
||||
func (p *rawPacket) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *rawPacket) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.rawPacketEntry)
|
||||
stateSourceObject.Load(1, &p.data)
|
||||
stateSourceObject.Load(3, &p.senderAddr)
|
||||
stateSourceObject.Load(4, &p.packetInfo)
|
||||
stateSourceObject.Load(5, &p.tosOrTClass)
|
||||
stateSourceObject.Load(6, &p.ttlOrHopLimit)
|
||||
stateSourceObject.LoadValue(2, new(int64), func(y any) { p.loadReceivedAt(ctx, y.(int64)) })
|
||||
}
|
||||
|
||||
func (e *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.endpoint"
|
||||
}
|
||||
|
||||
func (e *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"DefaultSocketOptionsHandler",
|
||||
"stack",
|
||||
"transProto",
|
||||
"waiterQueue",
|
||||
"associated",
|
||||
"net",
|
||||
"stats",
|
||||
"ops",
|
||||
"rcvList",
|
||||
"rcvBufSize",
|
||||
"rcvClosed",
|
||||
"rcvDisabled",
|
||||
"ipv6ChecksumOffset",
|
||||
"icmpv6Filter",
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSinkObject.Save(1, &e.stack)
|
||||
stateSinkObject.Save(2, &e.transProto)
|
||||
stateSinkObject.Save(3, &e.waiterQueue)
|
||||
stateSinkObject.Save(4, &e.associated)
|
||||
stateSinkObject.Save(5, &e.net)
|
||||
stateSinkObject.Save(6, &e.stats)
|
||||
stateSinkObject.Save(7, &e.ops)
|
||||
stateSinkObject.Save(8, &e.rcvList)
|
||||
stateSinkObject.Save(9, &e.rcvBufSize)
|
||||
stateSinkObject.Save(10, &e.rcvClosed)
|
||||
stateSinkObject.Save(11, &e.rcvDisabled)
|
||||
stateSinkObject.Save(12, &e.ipv6ChecksumOffset)
|
||||
stateSinkObject.Save(13, &e.icmpv6Filter)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSourceObject.Load(1, &e.stack)
|
||||
stateSourceObject.Load(2, &e.transProto)
|
||||
stateSourceObject.Load(3, &e.waiterQueue)
|
||||
stateSourceObject.Load(4, &e.associated)
|
||||
stateSourceObject.Load(5, &e.net)
|
||||
stateSourceObject.Load(6, &e.stats)
|
||||
stateSourceObject.Load(7, &e.ops)
|
||||
stateSourceObject.Load(8, &e.rcvList)
|
||||
stateSourceObject.Load(9, &e.rcvBufSize)
|
||||
stateSourceObject.Load(10, &e.rcvClosed)
|
||||
stateSourceObject.Load(11, &e.rcvDisabled)
|
||||
stateSourceObject.Load(12, &e.ipv6ChecksumOffset)
|
||||
stateSourceObject.Load(13, &e.icmpv6Filter)
|
||||
stateSourceObject.AfterLoad(func() { e.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (e *EndpointFactory) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.EndpointFactory"
|
||||
}
|
||||
|
||||
func (e *EndpointFactory) StateFields() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (e *EndpointFactory) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *EndpointFactory) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
}
|
||||
|
||||
func (e *EndpointFactory) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *EndpointFactory) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func (c *CreateOnlyFactory) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.CreateOnlyFactory"
|
||||
}
|
||||
|
||||
func (c *CreateOnlyFactory) StateFields() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (c *CreateOnlyFactory) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *CreateOnlyFactory) StateSave(stateSinkObject state.Sink) {
|
||||
c.beforeSave()
|
||||
}
|
||||
|
||||
func (c *CreateOnlyFactory) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *CreateOnlyFactory) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func (l *rawPacketList) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.rawPacketList"
|
||||
}
|
||||
|
||||
func (l *rawPacketList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *rawPacketList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *rawPacketList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *rawPacketList) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *rawPacketList) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *rawPacketEntry) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/raw.rawPacketEntry"
|
||||
}
|
||||
|
||||
func (e *rawPacketEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *rawPacketEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *rawPacketEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *rawPacketEntry) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *rawPacketEntry) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*rawPacket)(nil))
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*EndpointFactory)(nil))
|
||||
state.Register((*CreateOnlyFactory)(nil))
|
||||
state.Register((*rawPacketList)(nil))
|
||||
state.Register((*rawPacketEntry)(nil))
|
||||
}
|
||||
724
pkg/tcpip/transport/tcp/accept.go
Normal file
724
pkg/tcpip/transport/tcp/accept.go
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/ports"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// tsLen is the length, in bits, of the timestamp in the SYN cookie.
|
||||
tsLen = 8
|
||||
|
||||
// tsMask is a mask for timestamp values (i.e., tsLen bits).
|
||||
tsMask = (1 << tsLen) - 1
|
||||
|
||||
// tsOffset is the offset, in bits, of the timestamp in the SYN cookie.
|
||||
tsOffset = 24
|
||||
|
||||
// hashMask is the mask for hash values (i.e., tsOffset bits).
|
||||
hashMask = (1 << tsOffset) - 1
|
||||
|
||||
// maxTSDiff is the maximum allowed difference between a received cookie
|
||||
// timestamp and the current timestamp. If the difference is greater
|
||||
// than maxTSDiff, the cookie is expired.
|
||||
maxTSDiff = 2
|
||||
)
|
||||
|
||||
// mssTable is a slice containing the possible MSS values that we
|
||||
// encode in the SYN cookie with two bits.
|
||||
var mssTable = []uint16{536, 1300, 1440, 1460}
|
||||
|
||||
func encodeMSS(mss uint16) uint32 {
|
||||
for i := len(mssTable) - 1; i > 0; i-- {
|
||||
if mss >= mssTable[i] {
|
||||
return uint32(i)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// listenContext is used by a listening endpoint to store state used while
|
||||
// listening for connections. This struct is allocated by the listen goroutine
|
||||
// and must not be accessed or have its methods called concurrently as they
|
||||
// may mutate the stored objects.
|
||||
type listenContext struct {
|
||||
stack *stack.Stack
|
||||
protocol *protocol
|
||||
|
||||
// rcvWnd is the receive window that is sent by this listening context
|
||||
// in the initial SYN-ACK.
|
||||
rcvWnd seqnum.Size
|
||||
|
||||
// nonce are random bytes that are initialized once when the context
|
||||
// is created and used to seed the hash function when generating
|
||||
// the SYN cookie.
|
||||
nonce [2][sha1.BlockSize]byte
|
||||
|
||||
// listenEP is a reference to the listening endpoint associated with
|
||||
// this context. Can be nil if the context is created by the forwarder.
|
||||
listenEP *Endpoint
|
||||
|
||||
// hasherMu protects hasher.
|
||||
hasherMu hasherMutex
|
||||
// hasher is the hash function used to generate a SYN cookie.
|
||||
hasher hash.Hash
|
||||
|
||||
// v6Only is true if listenEP is a dual stack socket and has the
|
||||
// IPV6_V6ONLY option set.
|
||||
v6Only bool
|
||||
|
||||
// netProto indicates the network protocol(IPv4/v6) for the listening
|
||||
// endpoint.
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// timeStamp returns an 8-bit timestamp with a granularity of 64 seconds.
|
||||
func timeStamp(clock tcpip.Clock) uint32 {
|
||||
return uint32(clock.NowMonotonic().Sub(tcpip.MonotonicTime{}).Seconds()) >> 6 & tsMask
|
||||
}
|
||||
|
||||
// newListenContext creates a new listen context.
|
||||
func newListenContext(stk *stack.Stack, protocol *protocol, listenEP *Endpoint, rcvWnd seqnum.Size, v6Only bool, netProto tcpip.NetworkProtocolNumber) *listenContext {
|
||||
l := &listenContext{
|
||||
stack: stk,
|
||||
protocol: protocol,
|
||||
rcvWnd: rcvWnd,
|
||||
hasher: sha1.New(),
|
||||
v6Only: v6Only,
|
||||
netProto: netProto,
|
||||
listenEP: listenEP,
|
||||
}
|
||||
|
||||
for i := range l.nonce {
|
||||
if _, err := io.ReadFull(stk.SecureRNG().Reader, l.nonce[i][:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// cookieHash calculates the cookieHash for the given id, timestamp and nonce
|
||||
// index. The hash is used to create and validate cookies.
|
||||
func (l *listenContext) cookieHash(id stack.TransportEndpointID, ts uint32, nonceIndex int) uint32 {
|
||||
// Initialize block with fixed-size data: local ports and v.
|
||||
var payload [8]byte
|
||||
binary.BigEndian.PutUint16(payload[0:], id.LocalPort)
|
||||
binary.BigEndian.PutUint16(payload[2:], id.RemotePort)
|
||||
binary.BigEndian.PutUint32(payload[4:], ts)
|
||||
|
||||
// Feed everything to the hasher.
|
||||
l.hasherMu.Lock()
|
||||
l.hasher.Reset()
|
||||
|
||||
// Per hash.Hash.Writer:
|
||||
//
|
||||
// It never returns an error.
|
||||
l.hasher.Write(payload[:])
|
||||
l.hasher.Write(l.nonce[nonceIndex][:])
|
||||
l.hasher.Write(id.LocalAddress.AsSlice())
|
||||
l.hasher.Write(id.RemoteAddress.AsSlice())
|
||||
|
||||
// Finalize the calculation of the hash and return the first 4 bytes.
|
||||
h := l.hasher.Sum(nil)
|
||||
l.hasherMu.Unlock()
|
||||
|
||||
return binary.BigEndian.Uint32(h[:])
|
||||
}
|
||||
|
||||
// createCookie creates a SYN cookie for the given id and incoming sequence
|
||||
// number.
|
||||
func (l *listenContext) createCookie(id stack.TransportEndpointID, seq seqnum.Value, data uint32) seqnum.Value {
|
||||
ts := timeStamp(l.stack.Clock())
|
||||
v := l.cookieHash(id, 0, 0) + uint32(seq) + (ts << tsOffset)
|
||||
v += (l.cookieHash(id, ts, 1) + data) & hashMask
|
||||
return seqnum.Value(v)
|
||||
}
|
||||
|
||||
// isCookieValid checks if the supplied cookie is valid for the given id and
|
||||
// sequence number. If it is, it also returns the data originally encoded in the
|
||||
// cookie when createCookie was called.
|
||||
func (l *listenContext) isCookieValid(id stack.TransportEndpointID, cookie seqnum.Value, seq seqnum.Value) (uint32, bool) {
|
||||
ts := timeStamp(l.stack.Clock())
|
||||
v := uint32(cookie) - l.cookieHash(id, 0, 0) - uint32(seq)
|
||||
cookieTS := v >> tsOffset
|
||||
if ((ts - cookieTS) & tsMask) > maxTSDiff {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return (v - l.cookieHash(id, cookieTS, 1)) & hashMask, true
|
||||
}
|
||||
|
||||
// createConnectingEndpoint creates a new endpoint in a connecting state, with
|
||||
// the connection parameters given by the arguments. The newly created endpoint
|
||||
// will be locked.
|
||||
// +checklocksacquire:n.mu
|
||||
func (l *listenContext) createConnectingEndpoint(s *segment, rcvdSynOpts header.TCPSynOptions, queue *waiter.Queue) (n *Endpoint, _ tcpip.Error) {
|
||||
// Create a new endpoint.
|
||||
netProto := l.netProto
|
||||
if netProto == 0 {
|
||||
netProto = s.pkt.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
route, err := l.stack.FindRoute(s.pkt.NICID, s.pkt.Network().DestinationAddress(), s.pkt.Network().SourceAddress(), s.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return nil, err // +checklocksignore
|
||||
}
|
||||
|
||||
n = newEndpoint(l.stack, l.protocol, netProto, queue)
|
||||
n.mu.Lock()
|
||||
n.ops.SetV6Only(l.v6Only)
|
||||
n.TransportEndpointInfo.ID = s.id
|
||||
n.boundNICID = s.pkt.NICID
|
||||
n.route = route
|
||||
n.effectiveNetProtos = []tcpip.NetworkProtocolNumber{s.pkt.NetworkProtocolNumber}
|
||||
n.ops.SetReceiveBufferSize(int64(l.rcvWnd), false /* notify */)
|
||||
n.amss = calculateAdvertisedMSS(n.userMSS, n.route)
|
||||
n.setEndpointState(StateConnecting)
|
||||
|
||||
n.maybeEnableTimestamp(rcvdSynOpts)
|
||||
n.maybeEnableSACKPermitted(rcvdSynOpts)
|
||||
|
||||
n.initGSO()
|
||||
|
||||
// Bootstrap the auto tuning algorithm. Starting at zero will result in
|
||||
// a large step function on the first window adjustment causing the
|
||||
// window to grow to a really large value.
|
||||
initWnd := n.initialReceiveWindow()
|
||||
n.rcvQueueMu.Lock()
|
||||
n.RcvAutoParams.PrevCopiedBytes = initWnd
|
||||
n.rcvQueueMu.Unlock()
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// startHandshake creates a new endpoint in connecting state and then sends
|
||||
// the SYN-ACK for the TCP 3-way handshake. It returns the state of the
|
||||
// handshake in progress, which includes the new endpoint in the SYN-RCVD
|
||||
// state.
|
||||
//
|
||||
// On success, a handshake h is returned.
|
||||
//
|
||||
// NOTE: h.ep.mu is not held and must be acquired if any state needs to be
|
||||
// modified.
|
||||
//
|
||||
// Precondition: if l.listenEP != nil, l.listenEP.mu must be locked.
|
||||
func (l *listenContext) startHandshake(s *segment, opts header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (h *handshake, _ tcpip.Error) {
|
||||
// Create new endpoint.
|
||||
irs := s.sequenceNumber
|
||||
isn := generateSecureISN(s.id, l.stack.Clock(), l.protocol.seqnumSecret)
|
||||
ep, err := l.createConnectingEndpoint(s, opts, queue)
|
||||
if err != nil {
|
||||
return nil, err // +checklocksignore
|
||||
}
|
||||
|
||||
ep.owner = owner
|
||||
|
||||
// listenEP is nil when listenContext is used by tcp.Forwarder.
|
||||
deferAccept := time.Duration(0)
|
||||
if l.listenEP != nil {
|
||||
if l.listenEP.EndpointState() != StateListen {
|
||||
|
||||
// Ensure we release any registrations done by the newly
|
||||
// created endpoint.
|
||||
ep.mu.Unlock()
|
||||
ep.Close()
|
||||
|
||||
return nil, &tcpip.ErrConnectionAborted{} // +checklocksignore
|
||||
}
|
||||
|
||||
// Propagate any inheritable options from the listening endpoint
|
||||
// to the newly created endpoint.
|
||||
l.listenEP.propagateInheritableOptionsLocked(ep) // +checklocksforce
|
||||
|
||||
if !ep.reserveTupleLocked() {
|
||||
ep.mu.Unlock()
|
||||
ep.Close()
|
||||
|
||||
return nil, &tcpip.ErrConnectionAborted{} // +checklocksignore
|
||||
}
|
||||
|
||||
deferAccept = l.listenEP.deferAccept
|
||||
}
|
||||
|
||||
// Register new endpoint so that packets are routed to it.
|
||||
if err := ep.stack.RegisterTransportEndpoint(
|
||||
ep.effectiveNetProtos,
|
||||
ProtocolNumber,
|
||||
ep.TransportEndpointInfo.ID,
|
||||
ep,
|
||||
ep.boundPortFlags,
|
||||
ep.boundBindToDevice,
|
||||
); err != nil {
|
||||
ep.mu.Unlock()
|
||||
ep.Close()
|
||||
|
||||
ep.drainClosingSegmentQueue()
|
||||
|
||||
return nil, err // +checklocksignore
|
||||
}
|
||||
|
||||
ep.isRegistered = true
|
||||
|
||||
// Initialize and start the handshake.
|
||||
h = ep.newPassiveHandshake(isn, irs, opts, deferAccept)
|
||||
h.listenEP = l.listenEP
|
||||
h.start()
|
||||
h.ep.mu.Unlock()
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// performHandshake performs a TCP 3-way handshake. On success, the new
|
||||
// established endpoint is returned.
|
||||
//
|
||||
// Precondition: if l.listenEP != nil, l.listenEP.mu must be locked.
|
||||
func (l *listenContext) performHandshake(s *segment, opts header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*Endpoint, tcpip.Error) {
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
|
||||
queue.EventRegister(&waitEntry)
|
||||
defer queue.EventUnregister(&waitEntry)
|
||||
|
||||
h, err := l.startHandshake(s, opts, queue, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// performHandshake is used by the Forwarder which will block till the
|
||||
// handshake either succeeds or fails. We do this by registering for
|
||||
// events above and block on the notification channel.
|
||||
<-notifyCh
|
||||
|
||||
ep := h.ep
|
||||
ep.mu.Lock()
|
||||
if !ep.EndpointState().connected() {
|
||||
ep.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
ep.stats.FailedConnectionAttempts.Increment()
|
||||
ep.h = nil
|
||||
ep.mu.Unlock()
|
||||
ep.Close()
|
||||
ep.notifyAborted()
|
||||
ep.drainClosingSegmentQueue()
|
||||
err := ep.LastError()
|
||||
if err == nil {
|
||||
// If err was nil then return the best error we can to indicate
|
||||
// a connection failure.
|
||||
err = &tcpip.ErrConnectionAborted{}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ep.isConnectNotified = true
|
||||
|
||||
// Transfer any state from the completed handshake to the endpoint.
|
||||
//
|
||||
// Update the receive window scaling. We can't do it before the
|
||||
// handshake because it's possible that the peer doesn't support window
|
||||
// scaling.
|
||||
ep.rcv.RcvWndScale = ep.h.effectiveRcvWndScale()
|
||||
|
||||
// Clean up handshake state stored in the endpoint so that it can be
|
||||
// GCed.
|
||||
ep.h = nil
|
||||
ep.mu.Unlock()
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
// propagateInheritableOptionsLocked propagates any options set on the listening
|
||||
// endpoint to the newly created endpoint.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocks:n.mu
|
||||
func (e *Endpoint) propagateInheritableOptionsLocked(n *Endpoint) {
|
||||
n.userTimeout = e.userTimeout
|
||||
n.portFlags = e.portFlags
|
||||
n.boundBindToDevice = e.boundBindToDevice
|
||||
n.boundPortFlags = e.boundPortFlags
|
||||
n.userMSS = e.userMSS
|
||||
}
|
||||
|
||||
// reserveTupleLocked reserves an accepted endpoint's tuple.
|
||||
//
|
||||
// Precondition: e.propagateInheritableOptionsLocked has been called.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) reserveTupleLocked() bool {
|
||||
dest := tcpip.FullAddress{
|
||||
Addr: e.TransportEndpointInfo.ID.RemoteAddress,
|
||||
Port: e.TransportEndpointInfo.ID.RemotePort,
|
||||
}
|
||||
portRes := ports.Reservation{
|
||||
Networks: e.effectiveNetProtos,
|
||||
Transport: ProtocolNumber,
|
||||
Addr: e.TransportEndpointInfo.ID.LocalAddress,
|
||||
Port: e.TransportEndpointInfo.ID.LocalPort,
|
||||
Flags: e.boundPortFlags,
|
||||
BindToDevice: e.boundBindToDevice,
|
||||
Dest: dest,
|
||||
}
|
||||
if !e.stack.ReserveTuple(portRes) {
|
||||
e.stack.Stats().TCP.FailedPortReservations.Increment()
|
||||
return false
|
||||
}
|
||||
|
||||
e.isPortReserved = true
|
||||
e.boundDest = dest
|
||||
return true
|
||||
}
|
||||
|
||||
// notifyAborted wakes up any waiters on registered, but not accepted
|
||||
// endpoints.
|
||||
//
|
||||
// This is strictly not required normally as a socket that was never accepted
|
||||
// can't really have any registered waiters except when stack.Wait() is called
|
||||
// which waits for all registered endpoints to stop and expects an EventHUp.
|
||||
func (e *Endpoint) notifyAborted() {
|
||||
e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
}
|
||||
|
||||
func (e *Endpoint) acceptQueueIsFull() bool {
|
||||
e.acceptMu.Lock()
|
||||
full := e.acceptQueue.isFull()
|
||||
e.acceptMu.Unlock()
|
||||
return full
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type acceptQueue struct {
|
||||
// NB: this could be an endpointList, but ilist only permits endpoints to
|
||||
// belong to one list at a time, and endpoints are already stored in the
|
||||
// dispatcher's list.
|
||||
endpoints list.List `state:".([]*Endpoint)"`
|
||||
|
||||
// pendingEndpoints is a set of all endpoints for which a handshake is
|
||||
// in progress.
|
||||
pendingEndpoints map[*Endpoint]struct{}
|
||||
|
||||
// capacity is the maximum number of endpoints that can be in endpoints.
|
||||
capacity int
|
||||
}
|
||||
|
||||
func (a *acceptQueue) isFull() bool {
|
||||
return a.endpoints.Len() >= a.capacity
|
||||
}
|
||||
|
||||
// handleListenSegment is called when a listening endpoint receives a segment
|
||||
// and needs to handle it.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Error {
|
||||
e.rcvQueueMu.Lock()
|
||||
rcvClosed := e.RcvClosed
|
||||
e.rcvQueueMu.Unlock()
|
||||
if rcvClosed || s.flags.Contains(header.TCPFlagSyn|header.TCPFlagAck) {
|
||||
// If the endpoint is shutdown, reply with reset.
|
||||
//
|
||||
// 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.
|
||||
return replyWithReset(e.stack, s, e.sendTOS, e.ipv4TTL, e.ipv6HopLimit)
|
||||
}
|
||||
|
||||
switch {
|
||||
case s.flags.Contains(header.TCPFlagRst):
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
return nil
|
||||
|
||||
case s.flags.Contains(header.TCPFlagSyn):
|
||||
if e.acceptQueueIsFull() {
|
||||
e.stack.Stats().TCP.ListenOverflowSynDrop.Increment()
|
||||
e.stats.ReceiveErrors.ListenOverflowSynDrop.Increment()
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
opts := parseSynSegmentOptions(s)
|
||||
|
||||
useSynCookies, err := func() (bool, tcpip.Error) {
|
||||
var alwaysUseSynCookies tcpip.TCPAlwaysUseSynCookies
|
||||
if err := e.stack.TransportProtocolOption(header.TCPProtocolNumber, &alwaysUseSynCookies); err != nil {
|
||||
panic(fmt.Sprintf("TransportProtocolOption(%d, %T) = %s", header.TCPProtocolNumber, alwaysUseSynCookies, err))
|
||||
}
|
||||
if alwaysUseSynCookies {
|
||||
return true, nil
|
||||
}
|
||||
e.acceptMu.Lock()
|
||||
defer e.acceptMu.Unlock()
|
||||
|
||||
// The capacity of the accepted queue would always be one greater than the
|
||||
// listen backlog. But, the SYNRCVD connections count is always checked
|
||||
// against the listen backlog value for Linux parity reason.
|
||||
// https://github.com/torvalds/linux/blob/7acac4b3196/include/net/inet_connection_sock.h#L280
|
||||
if len(e.acceptQueue.pendingEndpoints) == e.acceptQueue.capacity-1 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
h, err := ctx.startHandshake(s, opts, &waiter.Queue{}, e.owner)
|
||||
if err != nil {
|
||||
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
e.stats.FailedConnectionAttempts.Increment()
|
||||
return false, err
|
||||
}
|
||||
e.acceptQueue.pendingEndpoints[h.ep] = struct{}{}
|
||||
|
||||
return false, nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !useSynCookies {
|
||||
return nil
|
||||
}
|
||||
|
||||
net := s.pkt.Network()
|
||||
route, err := e.stack.FindRoute(s.pkt.NICID, net.DestinationAddress(), net.SourceAddress(), s.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer route.Release()
|
||||
|
||||
// Send SYN without window scaling because we currently
|
||||
// don't encode this information in the cookie.
|
||||
//
|
||||
// Enable Timestamp option if the original syn did have
|
||||
// the timestamp option specified.
|
||||
//
|
||||
// Use the user supplied MSS on the listening socket for
|
||||
// new connections, if available.
|
||||
synOpts := header.TCPSynOptions{
|
||||
WS: -1,
|
||||
TS: opts.TS,
|
||||
TSEcr: opts.TSVal,
|
||||
MSS: calculateAdvertisedMSS(e.userMSS, route),
|
||||
}
|
||||
if opts.TS {
|
||||
offset := e.protocol.tsOffset(net.DestinationAddress(), net.SourceAddress())
|
||||
now := e.stack.Clock().NowMonotonic()
|
||||
synOpts.TSVal = offset.TSVal(now)
|
||||
}
|
||||
cookie := ctx.createCookie(s.id, s.sequenceNumber, encodeMSS(opts.MSS))
|
||||
fields := tcpFields{
|
||||
id: s.id,
|
||||
ttl: calculateTTL(route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: header.TCPFlagSyn | header.TCPFlagAck,
|
||||
seq: cookie,
|
||||
ack: s.sequenceNumber + 1,
|
||||
rcvWnd: ctx.rcvWnd,
|
||||
expOptVal: e.getExperimentOptionValue(route),
|
||||
}
|
||||
if err := e.sendSynTCP(route, fields, synOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
e.stack.Stats().TCP.ListenOverflowSynCookieSent.Increment()
|
||||
return nil
|
||||
|
||||
case s.flags.Contains(header.TCPFlagAck):
|
||||
iss := s.ackNumber - 1
|
||||
irs := s.sequenceNumber - 1
|
||||
|
||||
// As an edge case when SYN-COOKIES are in use and we receive a
|
||||
// segment that has data and is valid we should check if it
|
||||
// already matches a created endpoint and redirect the segment
|
||||
// rather than try and create a new endpoint. This can happen
|
||||
// where the final ACK for the handshake and other data packets
|
||||
// arrive at the same time and are queued to the listening
|
||||
// endpoint before the listening endpoint has had time to
|
||||
// process the first ACK and create the endpoint that matches
|
||||
// the incoming packet's full 5 tuple.
|
||||
netProtos := []tcpip.NetworkProtocolNumber{s.pkt.NetworkProtocolNumber}
|
||||
// If the local address is an IPv4 Address then also look for IPv6
|
||||
// dual stack endpoints.
|
||||
if s.id.LocalAddress.To4() != (tcpip.Address{}) {
|
||||
netProtos = []tcpip.NetworkProtocolNumber{header.IPv4ProtocolNumber, header.IPv6ProtocolNumber}
|
||||
}
|
||||
for _, netProto := range netProtos {
|
||||
if newEP := e.stack.FindTransportEndpoint(netProto, ProtocolNumber, s.id, s.pkt.NICID); newEP != nil && newEP != e {
|
||||
tcpEP := newEP.(*Endpoint)
|
||||
if !tcpEP.EndpointState().connected() {
|
||||
continue
|
||||
}
|
||||
if !tcpEP.enqueueSegment(s) {
|
||||
// Just silently drop the segment as we failed
|
||||
// to queue, we don't want to generate a RST
|
||||
// further below or try and create a new
|
||||
// endpoint etc.
|
||||
return nil
|
||||
}
|
||||
tcpEP.notifyProcessor()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Since SYN cookies are in use this is potentially an ACK to a
|
||||
// SYN-ACK we sent but don't have a half open connection state
|
||||
// as cookies are being used to protect against a potential SYN
|
||||
// flood. In such cases validate the cookie and if valid create
|
||||
// a fully connected endpoint and deliver to the accept queue.
|
||||
//
|
||||
// If not, silently drop the ACK to avoid leaking information
|
||||
// when under a potential syn flood attack.
|
||||
//
|
||||
// Validate the cookie.
|
||||
data, ok := ctx.isCookieValid(s.id, iss, irs)
|
||||
if !ok || int(data) >= len(mssTable) {
|
||||
e.stack.Stats().TCP.ListenOverflowInvalidSynCookieRcvd.Increment()
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
|
||||
// When not using SYN cookies, as per RFC 793, section 3.9, page 64:
|
||||
// Any acknowledgment is bad if it arrives on a connection still in
|
||||
// the LISTEN state. An acceptable reset segment should be formed
|
||||
// for any arriving ACK-bearing segment. The RST should be
|
||||
// formatted as follows:
|
||||
//
|
||||
// <SEQ=SEG.ACK><CTL=RST>
|
||||
//
|
||||
// Send a reset as this is an ACK for which there is no
|
||||
// half open connections and we are not using cookies
|
||||
// yet.
|
||||
//
|
||||
// The only time we should reach here when a connection
|
||||
// was opened and closed really quickly and a delayed
|
||||
// ACK was received from the sender.
|
||||
return replyWithReset(e.stack, s, e.sendTOS, e.ipv4TTL, e.ipv6HopLimit)
|
||||
}
|
||||
|
||||
// Keep hold of acceptMu until the new endpoint is in the accept queue (or
|
||||
// if there is an error), to guarantee that we will keep our spot in the
|
||||
// queue even if another handshake from the syn queue completes.
|
||||
e.acceptMu.Lock()
|
||||
if e.acceptQueue.isFull() {
|
||||
// Silently drop the ack as the application can't accept
|
||||
// the connection at this point. The ack will be
|
||||
// retransmitted by the sender anyway and we can
|
||||
// complete the connection at the time of retransmit if
|
||||
// the backlog has space.
|
||||
e.acceptMu.Unlock()
|
||||
e.stack.Stats().TCP.ListenOverflowAckDrop.Increment()
|
||||
e.stats.ReceiveErrors.ListenOverflowAckDrop.Increment()
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
e.stack.Stats().TCP.ListenOverflowSynCookieRcvd.Increment()
|
||||
// Create newly accepted endpoint and deliver it.
|
||||
rcvdSynOptions := header.TCPSynOptions{
|
||||
MSS: mssTable[data],
|
||||
// Disable Window scaling as original SYN is
|
||||
// lost.
|
||||
WS: -1,
|
||||
}
|
||||
|
||||
// When syn cookies are in use we enable timestamp only
|
||||
// if the ack specifies the timestamp option assuming
|
||||
// that the other end did in fact negotiate the
|
||||
// timestamp option in the original SYN.
|
||||
if s.parsedOptions.TS {
|
||||
rcvdSynOptions.TS = true
|
||||
rcvdSynOptions.TSVal = s.parsedOptions.TSVal
|
||||
rcvdSynOptions.TSEcr = s.parsedOptions.TSEcr
|
||||
}
|
||||
|
||||
n, err := ctx.createConnectingEndpoint(s, rcvdSynOptions, &waiter.Queue{})
|
||||
if err != nil {
|
||||
e.acceptMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// Propagate any inheritable options from the listening endpoint
|
||||
// to the newly created endpoint.
|
||||
e.propagateInheritableOptionsLocked(n)
|
||||
|
||||
if !n.reserveTupleLocked() {
|
||||
n.mu.Unlock()
|
||||
e.acceptMu.Unlock()
|
||||
n.Close()
|
||||
|
||||
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
e.stats.FailedConnectionAttempts.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register new endpoint so that packets are routed to it.
|
||||
if err := n.stack.RegisterTransportEndpoint(
|
||||
n.effectiveNetProtos,
|
||||
ProtocolNumber,
|
||||
n.TransportEndpointInfo.ID,
|
||||
n,
|
||||
n.boundPortFlags,
|
||||
n.boundBindToDevice,
|
||||
); err != nil {
|
||||
n.mu.Unlock()
|
||||
e.acceptMu.Unlock()
|
||||
n.Close()
|
||||
|
||||
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
e.stats.FailedConnectionAttempts.Increment()
|
||||
return err
|
||||
}
|
||||
|
||||
n.isRegistered = true
|
||||
net := s.pkt.Network()
|
||||
n.TSOffset = n.protocol.tsOffset(net.DestinationAddress(), net.SourceAddress())
|
||||
|
||||
// Switch state to connected.
|
||||
n.isConnectNotified = true
|
||||
h := handshake{
|
||||
ep: n,
|
||||
iss: iss,
|
||||
ackNum: irs + 1,
|
||||
rcvWnd: seqnum.Size(n.initialReceiveWindow()),
|
||||
sndWnd: s.window,
|
||||
rcvWndScale: e.rcvWndScaleForHandshake(),
|
||||
sndWndScale: rcvdSynOptions.WS,
|
||||
mss: rcvdSynOptions.MSS,
|
||||
sampleRTTWithTSOnly: true,
|
||||
}
|
||||
h.ep.AssertLockHeld(n)
|
||||
h.transitionToStateEstablishedLocked(s)
|
||||
n.mu.Unlock()
|
||||
|
||||
// Requeue the segment if the ACK completing the handshake has more info
|
||||
// to be processed by the newly established endpoint.
|
||||
if (s.flags.Contains(header.TCPFlagFin) || s.payloadSize() > 0) && n.enqueueSegment(s) {
|
||||
n.notifyProcessor()
|
||||
}
|
||||
|
||||
e.stack.Stats().TCP.PassiveConnectionOpenings.Increment()
|
||||
|
||||
// Deliver the endpoint to the accept queue.
|
||||
e.acceptQueue.endpoints.PushBack(n)
|
||||
e.acceptMu.Unlock()
|
||||
|
||||
e.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
return nil
|
||||
|
||||
default:
|
||||
e.stack.Stats().DroppedPackets.Increment()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/accept_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/accept_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type acceptMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var acceptprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var acceptlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type acceptlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) Lock() {
|
||||
locking.AddGLock(acceptprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) NestedLock(i acceptlockNameIndex) {
|
||||
locking.AddGLock(acceptprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) Unlock() {
|
||||
locking.DelGLock(acceptprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) NestedUnlock(i acceptlockNameIndex) {
|
||||
locking.DelGLock(acceptprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func acceptinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
acceptinitLockNames()
|
||||
acceptprefixIndex = locking.NewMutexClass(reflect.TypeOf(acceptMutex{}), acceptlockNames)
|
||||
}
|
||||
1532
pkg/tcpip/transport/tcp/connect.go
Normal file
1532
pkg/tcpip/transport/tcp/connect.go
Normal file
File diff suppressed because it is too large
Load diff
30
pkg/tcpip/transport/tcp/connect_unsafe.go
Normal file
30
pkg/tcpip/transport/tcp/connect_unsafe.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// optionsToArray converts a slice of capacity >-= maxOptionSize to an array.
|
||||
//
|
||||
// optionsToArray panics if the capacity of options is smaller than
|
||||
// maxOptionSize.
|
||||
func optionsToArray(options []byte) *[maxOptionSize]byte {
|
||||
// Reslice to full capacity.
|
||||
options = options[0:maxOptionSize]
|
||||
return (*[maxOptionSize]byte)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&options)).Data))
|
||||
}
|
||||
318
pkg/tcpip/transport/tcp/cubic.go
Normal file
318
pkg/tcpip/transport/tcp/cubic.go
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
// effectivelyInfinity is an initialization value used for round-trip times
|
||||
// that are then set using min. It is equal to approximately 100 years: large
|
||||
// enough that it will always be greater than a real TCP round-trip time, and
|
||||
// small enough that it fits in time.Duration.
|
||||
const effectivelyInfinity = time.Duration(math.MaxInt64)
|
||||
|
||||
const (
|
||||
// RTT = round-trip time.
|
||||
|
||||
// The delay increase sensitivity is determined by minRTTThresh and
|
||||
// maxRTTThresh. Smaller values of minRTTThresh may cause spurious exits
|
||||
// from slow start. Larger values of maxRTTThresh may result in slow start
|
||||
// not exiting until loss is encountered for connections on large RTT paths.
|
||||
minRTTThresh = 4 * time.Millisecond
|
||||
maxRTTThresh = 16 * time.Millisecond
|
||||
|
||||
// minRTTDivisor is a fraction of RTT to compute the delay threshold. A
|
||||
// smaller value would mean a larger threshold and thus less sensitivity to
|
||||
// delay increase, and vice versa.
|
||||
minRTTDivisor = 8
|
||||
|
||||
// nRTTSample is the minimum number of RTT samples in the round before
|
||||
// considering whether to exit the round due to increased RTT.
|
||||
nRTTSample = 8
|
||||
|
||||
// ackDelta is the maximum time between ACKs for them to be considered part
|
||||
// of the same ACK Train during HyStart
|
||||
ackDelta = 2 * time.Millisecond
|
||||
)
|
||||
|
||||
// cubicState stores the variables related to TCP CUBIC congestion
|
||||
// control algorithm state.
|
||||
//
|
||||
// See: https://tools.ietf.org/html/rfc8312.
|
||||
// +stateify savable
|
||||
type cubicState struct {
|
||||
TCPCubicState
|
||||
|
||||
// numCongestionEvents tracks the number of congestion events since last
|
||||
// RTO.
|
||||
numCongestionEvents int
|
||||
|
||||
s *sender
|
||||
}
|
||||
|
||||
// newCubicCC returns a partially initialized cubic state with the constants
|
||||
// beta and c set and t set to current time.
|
||||
//
|
||||
// +checklocks:s.ep.mu
|
||||
func newCubicCC(s *sender) *cubicState {
|
||||
now := s.ep.stack.Clock().NowMonotonic()
|
||||
return &cubicState{
|
||||
TCPCubicState: TCPCubicState{
|
||||
T: now,
|
||||
Beta: 0.7,
|
||||
C: 0.4,
|
||||
// By this point, the sender has initialized it's initial sequence
|
||||
// number.
|
||||
EndSeq: s.SndNxt,
|
||||
LastRTT: effectivelyInfinity,
|
||||
CurrRTT: effectivelyInfinity,
|
||||
LastAck: now,
|
||||
RoundStart: now,
|
||||
},
|
||||
s: s,
|
||||
}
|
||||
}
|
||||
|
||||
// enterCongestionAvoidance is used to initialize cubic in cases where we exit
|
||||
// SlowStart without a real congestion event taking place. This can happen when
|
||||
// a connection goes back to slow start due to a retransmit and we exceed the
|
||||
// previously lowered ssThresh without experiencing packet loss.
|
||||
//
|
||||
// Refer: https://tools.ietf.org/html/rfc8312#section-4.8
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) enterCongestionAvoidance() {
|
||||
// See: https://tools.ietf.org/html/rfc8312#section-4.7 &
|
||||
// https://tools.ietf.org/html/rfc8312#section-4.8
|
||||
if c.numCongestionEvents == 0 {
|
||||
c.K = 0
|
||||
c.T = c.s.ep.stack.Clock().NowMonotonic()
|
||||
c.WLastMax = c.WMax
|
||||
c.WMax = float64(c.s.SndCwnd)
|
||||
}
|
||||
}
|
||||
|
||||
// updateHyStart tracks packet round-trip time (rtt) to find a safe threshold
|
||||
// to exit slow start without triggering packet loss. It updates the SSThresh
|
||||
// when it does.
|
||||
//
|
||||
// Implementation of HyStart follows the algorithm from the Linux kernel, rather
|
||||
// than RFC 9406 (https://www.rfc-editor.org/rfc/rfc9406.html). Briefly, the
|
||||
// Linux kernel algorithm is based directly on the original HyStart paper
|
||||
// (https://doi.org/10.1016/j.comnet.2011.01.014), and differs from the RFC in
|
||||
// that two detection algorithms run in parallel ('ACK train' and 'Delay
|
||||
// increase'). The RFC version includes only the latter algorithm and adds an
|
||||
// intermediate phase called Conservative Slow Start, which is not implemented
|
||||
// here.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) updateHyStart(rtt time.Duration) {
|
||||
if rtt < 0 {
|
||||
// negative indicates unknown
|
||||
return
|
||||
}
|
||||
now := c.s.ep.stack.Clock().NowMonotonic()
|
||||
if c.EndSeq.LessThan(c.s.SndUna) {
|
||||
c.beginHyStartRound(now)
|
||||
}
|
||||
// ACK train
|
||||
if now.Sub(c.LastAck) < ackDelta && // ensures acks are part of the same "train"
|
||||
c.LastRTT < effectivelyInfinity {
|
||||
c.LastAck = now
|
||||
if thresh := c.LastRTT / 2; now.Sub(c.RoundStart) > thresh {
|
||||
c.s.Ssthresh = c.s.SndCwnd
|
||||
}
|
||||
}
|
||||
|
||||
// Delay increase
|
||||
c.CurrRTT = min(c.CurrRTT, rtt)
|
||||
c.SampleCount++
|
||||
|
||||
if c.SampleCount >= nRTTSample && c.LastRTT < effectivelyInfinity {
|
||||
// i.e. LastRTT/minRTTDivisor, but clamped to minRTTThresh & maxRTTThresh
|
||||
thresh := max(
|
||||
minRTTThresh,
|
||||
min(maxRTTThresh, c.LastRTT/minRTTDivisor),
|
||||
)
|
||||
if c.CurrRTT >= (c.LastRTT + thresh) {
|
||||
// Triggered HyStart safe exit threshold
|
||||
c.s.Ssthresh = c.s.SndCwnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) beginHyStartRound(now tcpip.MonotonicTime) {
|
||||
c.EndSeq = c.s.SndNxt
|
||||
c.SampleCount = 0
|
||||
c.LastRTT = c.CurrRTT
|
||||
c.CurrRTT = effectivelyInfinity
|
||||
c.LastAck = now
|
||||
c.RoundStart = now
|
||||
}
|
||||
|
||||
// updateSlowStart will update the congestion window as per the slow-start
|
||||
// algorithm used by NewReno. If after adjusting the congestion window we cross
|
||||
// the ssThresh then it will return the number of packets that must be consumed
|
||||
// in congestion avoidance mode.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) updateSlowStart(packetsAcked int) int {
|
||||
// Don't let the congestion window cross into the congestion
|
||||
// avoidance range.
|
||||
newcwnd := c.s.SndCwnd + packetsAcked
|
||||
enterCA := false
|
||||
if newcwnd >= c.s.Ssthresh {
|
||||
newcwnd = c.s.Ssthresh
|
||||
c.s.SndCAAckCount = 0
|
||||
enterCA = true
|
||||
}
|
||||
|
||||
packetsAcked -= newcwnd - c.s.SndCwnd
|
||||
c.s.SndCwnd = newcwnd
|
||||
if enterCA {
|
||||
c.enterCongestionAvoidance()
|
||||
}
|
||||
return packetsAcked
|
||||
}
|
||||
|
||||
// Update updates cubic's internal state variables. It must be called on every
|
||||
// ACK received.
|
||||
// Refer: https://tools.ietf.org/html/rfc8312#section-4
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) Update(packetsAcked int, rtt time.Duration) {
|
||||
if c.s.Ssthresh == InitialSsthresh && c.s.SndCwnd < c.s.Ssthresh {
|
||||
c.updateHyStart(rtt)
|
||||
}
|
||||
if c.s.SndCwnd < c.s.Ssthresh {
|
||||
packetsAcked = c.updateSlowStart(packetsAcked)
|
||||
if packetsAcked == 0 {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.s.rtt.Lock()
|
||||
srtt := c.s.rtt.TCPRTTState.SRTT
|
||||
c.s.rtt.Unlock()
|
||||
c.s.SndCwnd = c.getCwnd(packetsAcked, c.s.SndCwnd, srtt)
|
||||
}
|
||||
}
|
||||
|
||||
// cubicCwnd computes the CUBIC congestion window after t seconds from last
|
||||
// congestion event.
|
||||
func (c *cubicState) cubicCwnd(t float64) float64 {
|
||||
return c.C*math.Pow(t, 3.0) + c.WMax
|
||||
}
|
||||
|
||||
// getCwnd returns the current congestion window as computed by CUBIC.
|
||||
// Refer: https://tools.ietf.org/html/rfc8312#section-4
|
||||
func (c *cubicState) getCwnd(packetsAcked, sndCwnd int, srtt time.Duration) int {
|
||||
elapsed := c.s.ep.stack.Clock().NowMonotonic().Sub(c.T)
|
||||
elapsedSeconds := elapsed.Seconds()
|
||||
|
||||
// Compute the window as per Cubic after 'elapsed' time
|
||||
// since last congestion event.
|
||||
c.WC = c.cubicCwnd(elapsedSeconds - c.K)
|
||||
|
||||
// Compute the TCP friendly estimate of the congestion window.
|
||||
c.WEst = c.WMax*c.Beta + (3.0*((1.0-c.Beta)/(1.0+c.Beta)))*(elapsedSeconds/srtt.Seconds())
|
||||
|
||||
// Make sure in the TCP friendly region CUBIC performs at least
|
||||
// as well as Reno.
|
||||
if c.WC < c.WEst && float64(sndCwnd) < c.WEst {
|
||||
// TCP Friendly region of cubic.
|
||||
return int(c.WEst)
|
||||
}
|
||||
|
||||
// In Concave/Convex region of CUBIC, calculate what CUBIC window
|
||||
// will be after 1 RTT and use that to grow congestion window
|
||||
// for every ack.
|
||||
tEst := (elapsed + srtt).Seconds()
|
||||
wtRtt := c.cubicCwnd(tEst - c.K)
|
||||
// As per 4.3 for each received ACK cwnd must be incremented
|
||||
// by (w_cubic(t+RTT) - cwnd/cwnd.
|
||||
cwnd := float64(sndCwnd)
|
||||
for i := 0; i < packetsAcked; i++ {
|
||||
// Concave/Convex regions of cubic have the same formulas.
|
||||
// See: https://tools.ietf.org/html/rfc8312#section-4.3
|
||||
cwnd += (wtRtt - cwnd) / cwnd
|
||||
}
|
||||
return int(cwnd)
|
||||
}
|
||||
|
||||
// HandleLossDetected implements congestionControl.HandleLossDetected.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) HandleLossDetected() {
|
||||
// See: https://tools.ietf.org/html/rfc8312#section-4.5
|
||||
c.numCongestionEvents++
|
||||
c.T = c.s.ep.stack.Clock().NowMonotonic()
|
||||
c.WLastMax = c.WMax
|
||||
c.WMax = float64(c.s.SndCwnd)
|
||||
|
||||
c.fastConvergence()
|
||||
c.reduceSlowStartThreshold()
|
||||
}
|
||||
|
||||
// HandleRTOExpired implements congestionContrl.HandleRTOExpired.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) HandleRTOExpired() {
|
||||
// See: https://tools.ietf.org/html/rfc8312#section-4.6
|
||||
c.T = c.s.ep.stack.Clock().NowMonotonic()
|
||||
c.numCongestionEvents = 0
|
||||
c.WLastMax = c.WMax
|
||||
c.WMax = float64(c.s.SndCwnd)
|
||||
|
||||
c.fastConvergence()
|
||||
|
||||
// We lost a packet, so reduce ssthresh.
|
||||
c.reduceSlowStartThreshold()
|
||||
|
||||
// Reduce the congestion window to 1, i.e., enter slow-start. Per
|
||||
// RFC 5681, page 7, we must use 1 regardless of the value of the
|
||||
// initial congestion window.
|
||||
c.s.SndCwnd = 1
|
||||
}
|
||||
|
||||
// fastConvergence implements the logic for Fast Convergence algorithm as
|
||||
// described in https://tools.ietf.org/html/rfc8312#section-4.6.
|
||||
func (c *cubicState) fastConvergence() {
|
||||
if c.WMax < c.WLastMax {
|
||||
c.WLastMax = c.WMax
|
||||
c.WMax = c.WMax * (1.0 + c.Beta) / 2.0
|
||||
} else {
|
||||
c.WLastMax = c.WMax
|
||||
}
|
||||
// Recompute k as wMax may have changed.
|
||||
c.K = math.Cbrt(c.WMax * (1 - c.Beta) / c.C)
|
||||
}
|
||||
|
||||
// PostRecovery implements congestionControl.PostRecovery.
|
||||
func (c *cubicState) PostRecovery() {
|
||||
c.T = c.s.ep.stack.Clock().NowMonotonic()
|
||||
}
|
||||
|
||||
// reduceSlowStartThreshold returns new SsThresh as described in
|
||||
// https://tools.ietf.org/html/rfc8312#section-4.7.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) reduceSlowStartThreshold() {
|
||||
c.s.Ssthresh = int(math.Max(float64(c.s.SndCwnd)*c.Beta, 2.0))
|
||||
}
|
||||
534
pkg/tcpip/transport/tcp/dispatcher.go
Normal file
534
pkg/tcpip/transport/tcp/dispatcher.go
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sleep"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/hash/jenkins"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// epQueue is a queue of endpoints.
|
||||
//
|
||||
// +stateify savable
|
||||
type epQueue struct {
|
||||
mu epQueueMutex `state:"nosave"`
|
||||
list endpointList
|
||||
}
|
||||
|
||||
// enqueue adds e to the queue if the endpoint is not already on the queue.
|
||||
func (q *epQueue) enqueue(e *Endpoint) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
e.pendingProcessingMu.Lock()
|
||||
defer e.pendingProcessingMu.Unlock()
|
||||
|
||||
if e.pendingProcessing {
|
||||
return
|
||||
}
|
||||
q.list.PushBack(e)
|
||||
e.pendingProcessing = true
|
||||
}
|
||||
|
||||
// dequeue removes and returns the first element from the queue if available,
|
||||
// returns nil otherwise.
|
||||
func (q *epQueue) dequeue() *Endpoint {
|
||||
q.mu.Lock()
|
||||
if e := q.list.Front(); e != nil {
|
||||
q.list.Remove(e)
|
||||
e.pendingProcessingMu.Lock()
|
||||
e.pendingProcessing = false
|
||||
e.pendingProcessingMu.Unlock()
|
||||
q.mu.Unlock()
|
||||
return e
|
||||
}
|
||||
q.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// empty returns true if the queue is empty, false otherwise.
|
||||
func (q *epQueue) empty() bool {
|
||||
q.mu.Lock()
|
||||
v := q.list.Empty()
|
||||
q.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
|
||||
// processor is responsible for processing packets queued to a tcp endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type processor struct {
|
||||
epQ epQueue
|
||||
sleeper sleep.Sleeper `state:"nosave"`
|
||||
newEndpointWaker sleep.Waker `state:"nosave"`
|
||||
closeWaker sleep.Waker `state:"nosave"`
|
||||
pauseWaker sleep.Waker `state:"nosave"`
|
||||
pauseChan chan struct{} `state:"nosave"`
|
||||
resumeChan chan struct{} `state:"nosave"`
|
||||
}
|
||||
|
||||
func (p *processor) close() {
|
||||
p.closeWaker.Assert()
|
||||
}
|
||||
|
||||
func (p *processor) queueEndpoint(ep *Endpoint) {
|
||||
// Queue an endpoint for processing by the processor goroutine.
|
||||
p.epQ.enqueue(ep)
|
||||
p.newEndpointWaker.Assert()
|
||||
}
|
||||
|
||||
// deliverAccepted delivers a passively connected endpoint to the accept queue
|
||||
// of its associated listening endpoint.
|
||||
//
|
||||
// +checklocks:ep.mu
|
||||
func deliverAccepted(ep *Endpoint) bool {
|
||||
lEP := ep.h.listenEP
|
||||
lEP.acceptMu.Lock()
|
||||
|
||||
// Remove endpoint from list of pendingEndpoints as the handshake is now
|
||||
// complete.
|
||||
delete(lEP.acceptQueue.pendingEndpoints, ep)
|
||||
// Deliver this endpoint to the listening socket's accept queue.
|
||||
if lEP.acceptQueue.capacity == 0 {
|
||||
lEP.acceptMu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// NOTE: We always queue the endpoint and on purpose do not check if
|
||||
// accept queue is full at this point. This is similar to linux because
|
||||
// two racing incoming ACK's can both pass the acceptQueue.isFull check
|
||||
// and proceed to ESTABLISHED state. In such a case its better to
|
||||
// deliver both even if it temporarily exceeds the queue limit rather
|
||||
// than drop a connection that is fully connected.
|
||||
//
|
||||
// For reference see:
|
||||
// https://github.com/torvalds/linux/blob/169e77764adc041b1dacba84ea90516a895d43b2/net/ipv4/tcp_minisocks.c#L764
|
||||
// https://github.com/torvalds/linux/blob/169e77764adc041b1dacba84ea90516a895d43b2/net/ipv4/tcp_ipv4.c#L1500
|
||||
lEP.acceptQueue.endpoints.PushBack(ep)
|
||||
lEP.acceptMu.Unlock()
|
||||
ep.h.listenEP.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// handleConnecting is responsible for TCP processing for an endpoint in one of
|
||||
// the connecting states.
|
||||
func handleConnecting(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
}
|
||||
cleanup := func() {
|
||||
ep.mu.Unlock()
|
||||
ep.drainClosingSegmentQueue()
|
||||
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
}
|
||||
if !ep.EndpointState().connecting() {
|
||||
// If the endpoint has already transitioned out of a connecting
|
||||
// stage then just return (only possible if it was closed or
|
||||
// timed out by the time we got around to processing the wakeup.
|
||||
ep.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if err := ep.h.processSegments(); err != nil { // +checklocksforce:ep.h.ep.mu
|
||||
// handshake failed. clean up the tcp endpoint and handshake
|
||||
// state.
|
||||
if lEP := ep.h.listenEP; lEP != nil {
|
||||
lEP.acceptMu.Lock()
|
||||
delete(lEP.acceptQueue.pendingEndpoints, ep)
|
||||
lEP.acceptMu.Unlock()
|
||||
}
|
||||
ep.handshakeFailed(err)
|
||||
cleanup()
|
||||
return
|
||||
}
|
||||
|
||||
if ep.EndpointState() == StateEstablished && ep.h.listenEP != nil {
|
||||
ep.isConnectNotified = true
|
||||
ep.stack.Stats().TCP.PassiveConnectionOpenings.Increment()
|
||||
if !deliverAccepted(ep) {
|
||||
ep.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
|
||||
cleanup()
|
||||
return
|
||||
}
|
||||
}
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
|
||||
// handleConnected is responsible for TCP processing for an endpoint in one of
|
||||
// the connected states(StateEstablished, StateFinWait1 etc.)
|
||||
func handleConnected(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
}
|
||||
|
||||
if !ep.EndpointState().connected() {
|
||||
// If the endpoint has already transitioned out of a connected
|
||||
// state then just return (only possible if it was closed or
|
||||
// timed out by the time we got around to processing the wakeup.
|
||||
ep.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: We read this outside of e.mu lock which means that by the time
|
||||
// we get to handleSegments the endpoint may not be in ESTABLISHED. But
|
||||
// this should be fine as all normal shutdown states are handled by
|
||||
// handleSegmentsLocked.
|
||||
switch err := ep.handleSegmentsLocked(); {
|
||||
case err != nil:
|
||||
// Send any active resets if required.
|
||||
ep.resetConnectionLocked(err)
|
||||
fallthrough
|
||||
case ep.EndpointState() == StateClose:
|
||||
ep.mu.Unlock()
|
||||
ep.drainClosingSegmentQueue()
|
||||
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
return
|
||||
case ep.EndpointState() == StateTimeWait:
|
||||
startTimeWait(ep)
|
||||
}
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
|
||||
// startTimeWait starts a new goroutine to handle TIME-WAIT.
|
||||
//
|
||||
// +checklocks:ep.mu
|
||||
func startTimeWait(ep *Endpoint) {
|
||||
// Disable close timer as we are now entering real TIME_WAIT.
|
||||
if ep.finWait2Timer != nil {
|
||||
ep.finWait2Timer.Stop()
|
||||
}
|
||||
// Wake up any waiters before we start TIME-WAIT.
|
||||
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
timeWaitDuration := ep.getTimeWaitDuration()
|
||||
ep.timeWaitTimer = ep.stack.Clock().AfterFunc(timeWaitDuration, ep.timeWaitTimerExpired)
|
||||
}
|
||||
|
||||
// handleTimeWait is responsible for TCP processing for an endpoint in TIME-WAIT
|
||||
// state.
|
||||
func handleTimeWait(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
}
|
||||
|
||||
if ep.EndpointState() != StateTimeWait {
|
||||
// If the endpoint has already transitioned out of a TIME-WAIT
|
||||
// state then just return (only possible if it was closed or
|
||||
// timed out by the time we got around to processing the wakeup.
|
||||
ep.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
extendTimeWait, reuseTW := ep.handleTimeWaitSegments()
|
||||
if reuseTW != nil {
|
||||
ep.transitionToStateCloseLocked()
|
||||
ep.mu.Unlock()
|
||||
ep.drainClosingSegmentQueue()
|
||||
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
|
||||
reuseTW()
|
||||
return
|
||||
}
|
||||
if extendTimeWait {
|
||||
ep.timeWaitTimer.Reset(ep.getTimeWaitDuration())
|
||||
}
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
|
||||
// handleListen is responsible for TCP processing for an endpoint in LISTEN
|
||||
// state.
|
||||
func handleListen(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
}
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
if ep.EndpointState() != StateListen {
|
||||
// If the endpoint has already transitioned out of a LISTEN
|
||||
// state then just return (only possible if it was closed or
|
||||
// shutdown).
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < maxSegmentsPerWake; i++ {
|
||||
s := ep.segmentQueue.dequeue()
|
||||
if s == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/4690): Better handle errors instead of
|
||||
// silently dropping.
|
||||
_ = ep.handleListenSegment(ep.listenCtx, s)
|
||||
s.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
// start runs the main loop for a processor which is responsible for all TCP
|
||||
// processing for TCP endpoints.
|
||||
func (p *processor) start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
defer p.sleeper.Done()
|
||||
|
||||
for {
|
||||
switch w := p.sleeper.Fetch(true); {
|
||||
case w == &p.closeWaker:
|
||||
return
|
||||
case w == &p.pauseWaker:
|
||||
if !p.epQ.empty() {
|
||||
p.newEndpointWaker.Assert()
|
||||
p.pauseWaker.Assert()
|
||||
continue
|
||||
} else {
|
||||
p.pauseChan <- struct{}{}
|
||||
<-p.resumeChan
|
||||
}
|
||||
case w == &p.newEndpointWaker:
|
||||
for {
|
||||
ep := p.epQ.dequeue()
|
||||
if ep == nil {
|
||||
break
|
||||
}
|
||||
if ep.segmentQueue.empty() {
|
||||
continue
|
||||
}
|
||||
switch state := ep.EndpointState(); {
|
||||
case state.connecting():
|
||||
handleConnecting(ep)
|
||||
case state.connected() && state != StateTimeWait:
|
||||
handleConnected(ep)
|
||||
case state == StateTimeWait:
|
||||
handleTimeWait(ep)
|
||||
case state == StateListen:
|
||||
handleListen(ep)
|
||||
case state == StateError || state == StateClose:
|
||||
// Try to redeliver any still queued
|
||||
// packets to another endpoint or send a
|
||||
// RST if it can't be delivered.
|
||||
ep.mu.Lock()
|
||||
if st := ep.EndpointState(); st == StateError || st == StateClose {
|
||||
ep.drainClosingSegmentQueue()
|
||||
}
|
||||
ep.mu.Unlock()
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected tcp state in processor: %v", state))
|
||||
}
|
||||
// If there are more segments to process and the
|
||||
// endpoint lock is not held by user then
|
||||
// requeue this endpoint for processing.
|
||||
if !ep.segmentQueue.empty() && !ep.isOwnedByUser() {
|
||||
p.epQ.enqueue(ep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pause pauses the processor loop.
|
||||
func (p *processor) pause() chan struct{} {
|
||||
p.pauseWaker.Assert()
|
||||
return p.pauseChan
|
||||
}
|
||||
|
||||
// resume resumes a previously paused loop.
|
||||
//
|
||||
// Precondition: Pause must have been called previously.
|
||||
func (p *processor) resume() {
|
||||
p.resumeChan <- struct{}{}
|
||||
}
|
||||
|
||||
// dispatcher manages a pool of TCP endpoint processors which are responsible
|
||||
// for the processing of inbound segments. This fixed pool of processor
|
||||
// goroutines do full tcp processing. The processor is selected based on the
|
||||
// hash of the endpoint id to ensure that delivery for the same endpoint happens
|
||||
// in-order.
|
||||
//
|
||||
// +stateify savable
|
||||
type dispatcher struct {
|
||||
processors []processor
|
||||
wg sync.WaitGroup `state:"nosave"`
|
||||
hasher jenkinsHasher
|
||||
mu dispatcherMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
paused bool
|
||||
// +checklocks:mu
|
||||
closed bool
|
||||
}
|
||||
|
||||
// init initializes a dispatcher and starts the main loop for all the processors
|
||||
// owned by this dispatcher.
|
||||
func (d *dispatcher) init(rng *rand.Rand, nProcessors int) {
|
||||
d.close()
|
||||
d.wait()
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
d.closed = false
|
||||
d.processors = make([]processor, nProcessors)
|
||||
d.hasher = jenkinsHasher{seed: rng.Uint32()}
|
||||
d.startLocked()
|
||||
}
|
||||
|
||||
// +checklocks:d.mu
|
||||
func (d *dispatcher) startLocked() {
|
||||
if d.closed {
|
||||
return
|
||||
}
|
||||
for i := range d.processors {
|
||||
p := &d.processors[i]
|
||||
p.sleeper.AddWaker(&p.newEndpointWaker)
|
||||
p.sleeper.AddWaker(&p.closeWaker)
|
||||
p.sleeper.AddWaker(&p.pauseWaker)
|
||||
p.pauseChan = make(chan struct{})
|
||||
p.resumeChan = make(chan struct{})
|
||||
d.wg.Add(1)
|
||||
// NB: sleeper-waker registration must happen synchronously to avoid races
|
||||
// with `close`. It's possible to pull all this logic into `start`, but
|
||||
// that results in a heap-allocated function literal.
|
||||
go p.start(&d.wg)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dispatcher) start() {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
d.startLocked()
|
||||
}
|
||||
|
||||
// close closes a dispatcher and its processors.
|
||||
func (d *dispatcher) close() {
|
||||
d.mu.Lock()
|
||||
d.closed = true
|
||||
d.mu.Unlock()
|
||||
for i := range d.processors {
|
||||
d.processors[i].close()
|
||||
}
|
||||
}
|
||||
|
||||
// wait waits for all processor goroutines to end.
|
||||
func (d *dispatcher) wait() {
|
||||
d.wg.Wait()
|
||||
}
|
||||
|
||||
// queuePacket queues an incoming packet to the matching tcp endpoint and
|
||||
// also queues the endpoint to a processor queue for processing.
|
||||
func (d *dispatcher) queuePacket(stackEP stack.TransportEndpoint, id stack.TransportEndpointID, clock tcpip.Clock, pkt *stack.PacketBuffer) {
|
||||
d.mu.Lock()
|
||||
closed := d.closed
|
||||
d.mu.Unlock()
|
||||
|
||||
if closed {
|
||||
return
|
||||
}
|
||||
|
||||
ep := stackEP.(*Endpoint)
|
||||
|
||||
s, err := newIncomingSegment(id, clock, pkt)
|
||||
if err != nil {
|
||||
ep.stack.Stats().TCP.InvalidSegmentsReceived.Increment()
|
||||
ep.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
defer s.DecRef()
|
||||
|
||||
if !s.csumValid {
|
||||
ep.stack.Stats().TCP.ChecksumErrors.Increment()
|
||||
ep.stats.ReceiveErrors.ChecksumErrors.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
ep.stack.Stats().TCP.ValidSegmentsReceived.Increment()
|
||||
ep.stats.SegmentsReceived.Increment()
|
||||
if (s.flags & header.TCPFlagRst) != 0 {
|
||||
ep.stack.Stats().TCP.ResetsReceived.Increment()
|
||||
}
|
||||
|
||||
if !ep.enqueueSegment(s) {
|
||||
return
|
||||
}
|
||||
|
||||
// Only wakeup the processor if endpoint lock is not held by a user
|
||||
// goroutine as endpoint.UnlockUser will wake up the processor if the
|
||||
// segment queue is not empty.
|
||||
if !ep.isOwnedByUser() {
|
||||
d.selectProcessor(id).queueEndpoint(ep)
|
||||
}
|
||||
}
|
||||
|
||||
// selectProcessor uses a hash of the transport endpoint ID to queue the
|
||||
// endpoint to a specific processor. This is required to main TCP ordering as
|
||||
// queueing the same endpoint to multiple processors can *potentially* result in
|
||||
// out of order processing of incoming segments. It also ensures that a dispatcher
|
||||
// evenly loads the processor goroutines.
|
||||
func (d *dispatcher) selectProcessor(id stack.TransportEndpointID) *processor {
|
||||
return &d.processors[d.hasher.hash(id)%uint32(len(d.processors))]
|
||||
}
|
||||
|
||||
// pause pauses a dispatcher and all its processor goroutines.
|
||||
func (d *dispatcher) pause() {
|
||||
d.mu.Lock()
|
||||
d.paused = true
|
||||
d.mu.Unlock()
|
||||
for i := range d.processors {
|
||||
<-d.processors[i].pause()
|
||||
}
|
||||
}
|
||||
|
||||
// resume resumes a previously paused dispatcher and its processor goroutines.
|
||||
// Calling resume on a dispatcher that was never paused is a no-op.
|
||||
func (d *dispatcher) resume() {
|
||||
d.mu.Lock()
|
||||
|
||||
if !d.paused {
|
||||
// If this was a restore run the stack is a new instance and
|
||||
// it was never paused, so just return as there is nothing to
|
||||
// resume.
|
||||
d.mu.Unlock()
|
||||
return
|
||||
}
|
||||
d.paused = false
|
||||
d.mu.Unlock()
|
||||
for i := range d.processors {
|
||||
d.processors[i].resume()
|
||||
}
|
||||
}
|
||||
|
||||
// jenkinsHasher contains state needed to for a jenkins hash.
|
||||
//
|
||||
// +stateify savable
|
||||
type jenkinsHasher struct {
|
||||
seed uint32
|
||||
}
|
||||
|
||||
// hash hashes the provided TransportEndpointID using the jenkins hash
|
||||
// algorithm.
|
||||
func (j jenkinsHasher) hash(id stack.TransportEndpointID) uint32 {
|
||||
var payload [4]byte
|
||||
binary.LittleEndian.PutUint16(payload[0:], id.LocalPort)
|
||||
binary.LittleEndian.PutUint16(payload[2:], id.RemotePort)
|
||||
|
||||
h := jenkins.Sum32(j.seed)
|
||||
h.Write(payload[:])
|
||||
h.Write(id.LocalAddress.AsSlice())
|
||||
h.Write(id.RemoteAddress.AsSlice())
|
||||
return h.Sum32()
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/dispatcher_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/dispatcher_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type dispatcherMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var dispatcherprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var dispatcherlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type dispatcherlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) Lock() {
|
||||
locking.AddGLock(dispatcherprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) NestedLock(i dispatcherlockNameIndex) {
|
||||
locking.AddGLock(dispatcherprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) Unlock() {
|
||||
locking.DelGLock(dispatcherprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) NestedUnlock(i dispatcherlockNameIndex) {
|
||||
locking.DelGLock(dispatcherprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func dispatcherinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
dispatcherinitLockNames()
|
||||
dispatcherprefixIndex = locking.NewMutexClass(reflect.TypeOf(dispatcherMutex{}), dispatcherlockNames)
|
||||
}
|
||||
3367
pkg/tcpip/transport/tcp/endpoint.go
Normal file
3367
pkg/tcpip/transport/tcp/endpoint.go
Normal file
File diff suppressed because it is too large
Load diff
347
pkg/tcpip/transport/tcp/endpoint_state.go
Normal file
347
pkg/tcpip/transport/tcp/endpoint_state.go
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/atomicbitops"
|
||||
"github.com/sagernet/gvisor/pkg/log"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/ports"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// logDisconnectOnce ensures we don't spam logs when many connections are terminated.
|
||||
var logDisconnectOnce sync.Once
|
||||
|
||||
func logDisconnect() {
|
||||
logDisconnectOnce.Do(func() {
|
||||
log.Infof("One or more TCP connections terminated during save")
|
||||
})
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (e *Endpoint) beforeSave() {
|
||||
// Stop incoming packets.
|
||||
e.segmentQueue.freeze()
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
epState := e.EndpointState()
|
||||
switch {
|
||||
case epState == StateInitial || epState == StateBound:
|
||||
case epState.connected() || epState.handshake():
|
||||
if !e.route.HasSaveRestoreCapability() {
|
||||
if !e.route.HasDisconnectOkCapability() {
|
||||
panic(&tcpip.ErrSaveRejection{
|
||||
Err: fmt.Errorf("endpoint cannot be saved in connected state: local %s:%d, remote %s:%d", e.TransportEndpointInfo.ID.LocalAddress, e.TransportEndpointInfo.ID.LocalPort, e.TransportEndpointInfo.ID.RemoteAddress, e.TransportEndpointInfo.ID.RemotePort),
|
||||
})
|
||||
}
|
||||
logDisconnect()
|
||||
e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
|
||||
e.mu.Unlock()
|
||||
e.Close()
|
||||
e.mu.Lock()
|
||||
}
|
||||
fallthrough
|
||||
case epState == StateListen:
|
||||
// Nothing to do.
|
||||
case epState.closed():
|
||||
// Nothing to do.
|
||||
default:
|
||||
panic(fmt.Sprintf("endpoint in unknown state %v", e.EndpointState()))
|
||||
}
|
||||
|
||||
e.stack.RegisterResumableEndpoint(e)
|
||||
}
|
||||
|
||||
// saveEndpoints is invoked by stateify.
|
||||
func (a *acceptQueue) saveEndpoints() []*Endpoint {
|
||||
acceptedEndpoints := make([]*Endpoint, a.endpoints.Len())
|
||||
for i, e := 0, a.endpoints.Front(); e != nil; i, e = i+1, e.Next() {
|
||||
acceptedEndpoints[i] = e.Value.(*Endpoint)
|
||||
}
|
||||
return acceptedEndpoints
|
||||
}
|
||||
|
||||
// loadEndpoints is invoked by stateify.
|
||||
func (a *acceptQueue) loadEndpoints(_ context.Context, acceptedEndpoints []*Endpoint) {
|
||||
for _, ep := range acceptedEndpoints {
|
||||
a.endpoints.PushBack(ep)
|
||||
}
|
||||
}
|
||||
|
||||
// saveState is invoked by stateify.
|
||||
func (e *Endpoint) saveState() EndpointState {
|
||||
return e.EndpointState()
|
||||
}
|
||||
|
||||
// Endpoint loading must be done in the following ordering by their state, to
|
||||
// avoid dangling connecting w/o listening peer, and to avoid conflicts in port
|
||||
// reservation.
|
||||
var (
|
||||
connectedLoading sync.WaitGroup
|
||||
listenLoading sync.WaitGroup
|
||||
connectingLoading sync.WaitGroup
|
||||
)
|
||||
|
||||
// Bound endpoint loading happens last.
|
||||
|
||||
// loadState is invoked by stateify.
|
||||
func (e *Endpoint) loadState(_ context.Context, epState EndpointState) {
|
||||
// This is to ensure that the loading wait groups include all applicable
|
||||
// endpoints before any asynchronous calls to the Wait() methods.
|
||||
// For restore purposes we treat all endpoints with state after
|
||||
// StateEstablished and before StateClosed like connected endpoint.
|
||||
if epState.connected() {
|
||||
connectedLoading.Add(1)
|
||||
}
|
||||
switch {
|
||||
case epState == StateListen:
|
||||
listenLoading.Add(1)
|
||||
case epState.connecting():
|
||||
connectingLoading.Add(1)
|
||||
}
|
||||
// Directly update the state here rather than using e.setEndpointState
|
||||
// as the endpoint is still being loaded and the stack reference is not
|
||||
// yet initialized.
|
||||
e.state.Store(uint32(epState))
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *Endpoint) afterLoad(ctx context.Context) {
|
||||
// RacyLoad() can be used because we are initializing e.
|
||||
e.origEndpointState = e.state.RacyLoad()
|
||||
// Restore the endpoint to InitialState as it will be moved to
|
||||
// its origEndpointState during Restore.
|
||||
e.state = atomicbitops.FromUint32(uint32(StateInitial))
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
if !e.EndpointState().closed() {
|
||||
e.keepalive.timer.init(s.Clock(), timerHandler(e, e.keepaliveTimerExpired))
|
||||
}
|
||||
if snd := e.snd; snd != nil {
|
||||
snd.resendTimer.init(s.Clock(), timerHandler(e, e.snd.retransmitTimerExpired))
|
||||
snd.reorderTimer.init(s.Clock(), timerHandler(e, e.snd.rc.reorderTimerExpired))
|
||||
snd.probeTimer.init(s.Clock(), timerHandler(e, e.snd.probeTimerExpired))
|
||||
snd.corkTimer.init(s.Clock(), timerHandler(e, e.snd.corkTimerExpired))
|
||||
}
|
||||
saveRestoreEnabled := e.stack.IsSaveRestoreEnabled()
|
||||
if !saveRestoreEnabled {
|
||||
e.stack = s
|
||||
e.protocol = protocolFromStack(s)
|
||||
}
|
||||
e.ops.InitHandler(e, e.stack, GetTCPSendBufferLimits, GetTCPReceiveBufferLimits)
|
||||
e.segmentQueue.thaw()
|
||||
|
||||
e.mu.Lock()
|
||||
id := e.ID
|
||||
e.mu.Unlock()
|
||||
|
||||
bind := func() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if !saveRestoreEnabled {
|
||||
addr, _, err := e.checkV4MappedLocked(tcpip.FullAddress{Addr: e.BindAddr, Port: e.TransportEndpointInfo.ID.LocalPort}, true /* bind */)
|
||||
if err != nil {
|
||||
panic("unable to parse BindAddr: " + err.String())
|
||||
}
|
||||
portRes := ports.Reservation{
|
||||
Networks: e.effectiveNetProtos,
|
||||
Transport: ProtocolNumber,
|
||||
Addr: addr.Addr,
|
||||
Port: addr.Port,
|
||||
Flags: e.boundPortFlags,
|
||||
BindToDevice: e.boundBindToDevice,
|
||||
Dest: e.boundDest,
|
||||
}
|
||||
if ok := e.stack.ReserveTuple(portRes); !ok {
|
||||
panic(fmt.Sprintf("unable to re-reserve tuple (%v, %q, %d, %+v, %d, %v)", e.effectiveNetProtos, addr.Addr, addr.Port, e.boundPortFlags, e.boundBindToDevice, e.boundDest))
|
||||
}
|
||||
}
|
||||
e.isPortReserved = true
|
||||
|
||||
// Mark endpoint as bound.
|
||||
e.setEndpointState(StateBound)
|
||||
}
|
||||
|
||||
epState := EndpointState(e.origEndpointState)
|
||||
switch {
|
||||
case epState.connected():
|
||||
bind()
|
||||
if e.connectingAddress.BitLen() == 0 {
|
||||
e.connectingAddress = e.TransportEndpointInfo.ID.RemoteAddress
|
||||
// This endpoint is accepted by netstack but not yet by
|
||||
// the app. If the endpoint is IPv6 but the remote
|
||||
// address is IPv4, we need to connect as IPv6 so that
|
||||
// dual-stack mode can be properly activated.
|
||||
if e.NetProto == header.IPv6ProtocolNumber && e.TransportEndpointInfo.ID.RemoteAddress.BitLen() != header.IPv6AddressSizeBits {
|
||||
e.connectingAddress = tcpip.AddrFrom16Slice(append(
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff},
|
||||
e.TransportEndpointInfo.ID.RemoteAddress.AsSlice()...,
|
||||
))
|
||||
}
|
||||
}
|
||||
// Reset the scoreboard to reinitialize the sack information as
|
||||
// we do not restore SACK information.
|
||||
e.scoreboard.Reset()
|
||||
if saveRestoreEnabled {
|
||||
// Unregister the endpoint before registering again during Connect.
|
||||
e.stack.UnregisterTransportEndpoint(e.effectiveNetProtos, header.TCPProtocolNumber, e.TransportEndpointInfo.ID, e, e.boundPortFlags, e.boundBindToDevice)
|
||||
}
|
||||
e.mu.Lock()
|
||||
err := e.connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.TransportEndpointInfo.ID.RemotePort}, false /* handshake */)
|
||||
if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
|
||||
log.Warningf("TCP endpoint connect failed for connected endpoint with ID: %+v err: %v", id, err)
|
||||
e.mu.Unlock()
|
||||
e.Close()
|
||||
connectedLoading.Done()
|
||||
return
|
||||
}
|
||||
e.state.Store(e.origEndpointState)
|
||||
// For FIN-WAIT-2 and TIME-WAIT we need to start the appropriate timers so
|
||||
// that the socket is closed correctly.
|
||||
switch epState {
|
||||
case StateFinWait2:
|
||||
e.finWait2Timer = e.stack.Clock().AfterFunc(e.tcpLingerTimeout, e.finWait2TimerExpired)
|
||||
case StateTimeWait:
|
||||
e.timeWaitTimer = e.stack.Clock().AfterFunc(e.getTimeWaitDuration(), e.timeWaitTimerExpired)
|
||||
}
|
||||
|
||||
if e.ops.GetCorkOption() {
|
||||
// Rearm the timer if TCP_CORK is enabled which will
|
||||
// drain all the segments in the queue after restore.
|
||||
e.snd.corkTimer.enable(MinRTO)
|
||||
}
|
||||
e.mu.Unlock()
|
||||
connectedLoading.Done()
|
||||
case epState == StateListen:
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
if !saveRestoreEnabled {
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
bind()
|
||||
e.acceptMu.Lock()
|
||||
backlog := e.acceptQueue.capacity
|
||||
e.acceptMu.Unlock()
|
||||
if err := e.Listen(backlog); err != nil {
|
||||
panic("endpoint listening failed: " + err.String())
|
||||
}
|
||||
e.LockUser()
|
||||
if e.shutdownFlags != 0 {
|
||||
e.shutdownLocked(e.shutdownFlags)
|
||||
}
|
||||
e.UnlockUser()
|
||||
listenLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
e.LockUser()
|
||||
// All endpoints will be moved to initial state after
|
||||
// restore. Set endpoint to its originial listen state.
|
||||
e.setEndpointState(StateListen)
|
||||
// Initialize the listening context.
|
||||
rcvWnd := seqnum.Size(e.receiveBufferAvailable())
|
||||
e.listenCtx = newListenContext(e.stack, e.protocol, e, rcvWnd, e.ops.GetV6Only(), e.NetProto)
|
||||
e.UnlockUser()
|
||||
listenLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
}
|
||||
case epState == StateConnecting:
|
||||
// Initial SYN hasn't been sent yet so initiate a connect.
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
listenLoading.Wait()
|
||||
bind()
|
||||
err := e.Connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.TransportEndpointInfo.ID.RemotePort})
|
||||
if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
|
||||
log.Warningf("TCP endpoint connect failed for connecting endpoint with ID: %+v err: %v", id, err)
|
||||
e.Close()
|
||||
}
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
case epState == StateSynSent || epState == StateSynRecv:
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
listenLoading.Wait()
|
||||
// Initial SYN has been sent/received so we should bind the
|
||||
// ports start the retransmit timer for the SYNs and let it
|
||||
// naturally complete the connection.
|
||||
bind()
|
||||
e.mu.Lock()
|
||||
e.setEndpointState(epState)
|
||||
r, err := e.stack.FindRoute(e.boundNICID, e.TransportEndpointInfo.ID.LocalAddress, e.TransportEndpointInfo.ID.RemoteAddress, e.effectiveNetProtos[0], false /* multicastLoop */)
|
||||
if err != nil {
|
||||
e.mu.Unlock()
|
||||
log.Warningf("FindRoute failed when restoring endpoint w/ ID: %+v err: %v", id, err)
|
||||
e.Close()
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
return
|
||||
}
|
||||
e.route = r
|
||||
timer, err := newBackoffTimer(e.stack.Clock(), InitialRTO, MaxRTO, timerHandler(e, e.h.retransmitHandlerLocked))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("newBackOffTimer(_, %s, %s, _) failed: %s", InitialRTO, MaxRTO, err))
|
||||
}
|
||||
e.h.retransmitTimer = timer
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
e.mu.Unlock()
|
||||
}()
|
||||
case epState == StateBound:
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
listenLoading.Wait()
|
||||
connectingLoading.Wait()
|
||||
bind()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
case epState == StateClose:
|
||||
e.isPortReserved = false
|
||||
e.state.Store(uint32(StateClose))
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
case epState == StateError:
|
||||
e.state.Store(uint32(StateError))
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *Endpoint) Resume() {
|
||||
e.segmentQueue.thaw()
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/ep_queue_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/ep_queue_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type epQueueMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var epQueueprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var epQueuelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type epQueuelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) Lock() {
|
||||
locking.AddGLock(epQueueprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) NestedLock(i epQueuelockNameIndex) {
|
||||
locking.AddGLock(epQueueprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) Unlock() {
|
||||
locking.DelGLock(epQueueprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) NestedUnlock(i epQueuelockNameIndex) {
|
||||
locking.DelGLock(epQueueprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func epQueueinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
epQueueinitLockNames()
|
||||
epQueueprefixIndex = locking.NewMutexClass(reflect.TypeOf(epQueueMutex{}), epQueuelockNames)
|
||||
}
|
||||
231
pkg/tcpip/transport/tcp/forwarder.go
Normal file
231
pkg/tcpip/transport/tcp/forwarder.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// Forwarder is a connection request forwarder, which allows clients to decide
|
||||
// what to do with a connection request, for example: ignore it, send a RST, or
|
||||
// attempt to complete the 3-way handshake.
|
||||
//
|
||||
// The canonical way of using it is to pass the Forwarder.HandlePacket function
|
||||
// to stack.SetTransportProtocolHandler.
|
||||
type Forwarder struct {
|
||||
stack *stack.Stack
|
||||
|
||||
maxInFlight int
|
||||
handler func(*ForwarderRequest)
|
||||
|
||||
mu forwarderMutex
|
||||
inFlight map[stack.TransportEndpointID]struct{}
|
||||
listen *listenContext
|
||||
}
|
||||
|
||||
// NewForwarder allocates and initializes a new forwarder with the given
|
||||
// maximum number of in-flight connection attempts. Once the maximum is reached
|
||||
// new incoming connection requests will be ignored.
|
||||
//
|
||||
// If rcvWnd is set to zero, the default buffer size is used instead.
|
||||
func NewForwarder(s *stack.Stack, rcvWnd, maxInFlight int, handler func(*ForwarderRequest)) *Forwarder {
|
||||
if rcvWnd == 0 {
|
||||
rcvWnd = DefaultReceiveBufferSize
|
||||
}
|
||||
return &Forwarder{
|
||||
stack: s,
|
||||
maxInFlight: maxInFlight,
|
||||
handler: handler,
|
||||
inFlight: make(map[stack.TransportEndpointID]struct{}),
|
||||
listen: newListenContext(s, protocolFromStack(s), nil /* listenEP */, seqnum.Size(rcvWnd), true, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePacket handles a packet if it is of interest to the forwarder (i.e., if
|
||||
// it's a SYN packet), returning true if it's the case. Otherwise the packet
|
||||
// is not handled and false is returned.
|
||||
//
|
||||
// This function is expected to be passed as an argument to the
|
||||
// stack.SetTransportProtocolHandler function.
|
||||
func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
s, err := newIncomingSegment(id, f.stack.Clock(), pkt)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer s.DecRef()
|
||||
|
||||
// We only care about well-formed SYN packets (not SYN-ACK) packets.
|
||||
if !s.csumValid || !s.flags.Contains(header.TCPFlagSyn) || s.flags.Contains(header.TCPFlagAck) {
|
||||
return false
|
||||
}
|
||||
|
||||
opts := parseSynSegmentOptions(s)
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
// We have an inflight request for this id, ignore this one for now.
|
||||
if _, ok := f.inFlight[id]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ignore the segment if we're beyond the limit.
|
||||
if len(f.inFlight) >= f.maxInFlight {
|
||||
f.stack.Stats().TCP.ForwardMaxInFlightDrop.Increment()
|
||||
return true
|
||||
}
|
||||
|
||||
// Launch a new goroutine to handle the request.
|
||||
f.inFlight[id] = struct{}{}
|
||||
s.IncRef()
|
||||
go f.handler(&ForwarderRequest{ // S/R-SAFE: not used by Sentry.
|
||||
forwarder: f,
|
||||
segment: s,
|
||||
synOptions: opts,
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ForwarderRequest represents a connection request received by the forwarder
|
||||
// and passed to the client. Clients must eventually call Complete() on it, and
|
||||
// may optionally create an endpoint to represent it via CreateEndpoint.
|
||||
type ForwarderRequest struct {
|
||||
mu forwarderRequestMutex
|
||||
forwarder *Forwarder
|
||||
segment *segment
|
||||
synOptions header.TCPSynOptions
|
||||
}
|
||||
|
||||
// ID returns the 4-tuple (src address, src port, dst address, dst port) that
|
||||
// represents the connection request.
|
||||
func (r *ForwarderRequest) ID() stack.TransportEndpointID {
|
||||
return r.segment.id
|
||||
}
|
||||
|
||||
// Complete completes the request, and optionally sends a RST segment back to the
|
||||
// sender.
|
||||
func (r *ForwarderRequest) Complete(sendReset bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.segment == nil {
|
||||
panic("Completing already completed forwarder request")
|
||||
}
|
||||
|
||||
// Remove request from the forwarder.
|
||||
r.forwarder.mu.Lock()
|
||||
delete(r.forwarder.inFlight, r.segment.id)
|
||||
r.forwarder.mu.Unlock()
|
||||
|
||||
if sendReset {
|
||||
replyWithReset(r.forwarder.stack, r.segment, stack.DefaultTOS, tcpip.UseDefaultIPv4TTL, tcpip.UseDefaultIPv6HopLimit)
|
||||
}
|
||||
|
||||
// Release all resources.
|
||||
r.segment.DecRef()
|
||||
r.segment = nil
|
||||
r.forwarder = nil
|
||||
}
|
||||
|
||||
// CreateEndpoint creates a TCP endpoint for the connection request, performing
|
||||
// the 3-way handshake in the process.
|
||||
func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.segment == nil {
|
||||
return nil, &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
f := r.forwarder
|
||||
ep, err := f.listen.performHandshake(r.segment, header.TCPSynOptions{
|
||||
MSS: r.synOptions.MSS,
|
||||
WS: r.synOptions.WS,
|
||||
TS: r.synOptions.TS,
|
||||
TSVal: r.synOptions.TSVal,
|
||||
TSEcr: r.synOptions.TSEcr,
|
||||
SACKPermitted: r.synOptions.SACKPermitted,
|
||||
}, queue, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
// ForwardedPacketExperimentOption returns the experiment option value from the
|
||||
// forwarded packet and a bool indicating whether an experiment option value was
|
||||
// found.
|
||||
func (r *ForwarderRequest) ForwardedPacketExperimentOption() (uint16, bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
switch r.segment.pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
h := header.IPv4(r.segment.pkt.NetworkHeader().Slice())
|
||||
opts := h.Options()
|
||||
iter := opts.MakeIterator()
|
||||
for {
|
||||
opt, done, err := iter.Next()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
if done {
|
||||
return 0, false
|
||||
}
|
||||
if opt.Type() == header.IPv4OptionExperimentType {
|
||||
return opt.(*header.IPv4OptionExperiment).Value(), true
|
||||
}
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
h := header.IPv6(r.segment.pkt.NetworkHeader().Slice())
|
||||
v := r.segment.pkt.NetworkHeader().View()
|
||||
if v != nil {
|
||||
v.TrimFront(header.IPv6MinimumSize)
|
||||
}
|
||||
buf := buffer.MakeWithView(v)
|
||||
buf.Append(r.segment.pkt.TransportHeader().View())
|
||||
dataBuf := r.segment.pkt.Data().ToBuffer()
|
||||
buf.Merge(&dataBuf)
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(h.NextHeader()), buf)
|
||||
|
||||
for {
|
||||
hdr, done, err := it.Next()
|
||||
if done || err != nil {
|
||||
break
|
||||
}
|
||||
if h, ok := hdr.(header.IPv6ExperimentExtHdr); ok {
|
||||
hdr.Release()
|
||||
return h.Value, true
|
||||
}
|
||||
hdr.Release()
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected network protocol number %d", r.segment.pkt.NetworkProtocolNumber))
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (r *ForwarderRequest) Packet() *stack.PacketBuffer {
|
||||
return r.segment.pkt
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/forwarder_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/forwarder_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type forwarderMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var forwarderprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var forwarderlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type forwarderlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) Lock() {
|
||||
locking.AddGLock(forwarderprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) NestedLock(i forwarderlockNameIndex) {
|
||||
locking.AddGLock(forwarderprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) Unlock() {
|
||||
locking.DelGLock(forwarderprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) NestedUnlock(i forwarderlockNameIndex) {
|
||||
locking.DelGLock(forwarderprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func forwarderinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
forwarderinitLockNames()
|
||||
forwarderprefixIndex = locking.NewMutexClass(reflect.TypeOf(forwarderMutex{}), forwarderlockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/forwarder_request_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/forwarder_request_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type forwarderRequestMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var forwarderRequestprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var forwarderRequestlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type forwarderRequestlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderRequestMutex) Lock() {
|
||||
locking.AddGLock(forwarderRequestprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderRequestMutex) NestedLock(i forwarderRequestlockNameIndex) {
|
||||
locking.AddGLock(forwarderRequestprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderRequestMutex) Unlock() {
|
||||
locking.DelGLock(forwarderRequestprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderRequestMutex) NestedUnlock(i forwarderRequestlockNameIndex) {
|
||||
locking.DelGLock(forwarderRequestprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func forwarderRequestinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
forwarderRequestinitLockNames()
|
||||
forwarderRequestprefixIndex = locking.NewMutexClass(reflect.TypeOf(forwarderRequestMutex{}), forwarderRequestlockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/hasher_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/hasher_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type hasherMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var hasherprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var hasherlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type hasherlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *hasherMutex) Lock() {
|
||||
locking.AddGLock(hasherprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *hasherMutex) NestedLock(i hasherlockNameIndex) {
|
||||
locking.AddGLock(hasherprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *hasherMutex) Unlock() {
|
||||
locking.DelGLock(hasherprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *hasherMutex) NestedUnlock(i hasherlockNameIndex) {
|
||||
locking.DelGLock(hasherprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func hasherinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
hasherinitLockNames()
|
||||
hasherprefixIndex = locking.NewMutexClass(reflect.TypeOf(hasherMutex{}), hasherlockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/keepalive_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/keepalive_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type keepaliveMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var keepaliveprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var keepalivelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type keepalivelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *keepaliveMutex) Lock() {
|
||||
locking.AddGLock(keepaliveprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *keepaliveMutex) NestedLock(i keepalivelockNameIndex) {
|
||||
locking.AddGLock(keepaliveprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *keepaliveMutex) Unlock() {
|
||||
locking.DelGLock(keepaliveprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *keepaliveMutex) NestedUnlock(i keepalivelockNameIndex) {
|
||||
locking.DelGLock(keepaliveprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func keepaliveinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
keepaliveinitLockNames()
|
||||
keepaliveprefixIndex = locking.NewMutexClass(reflect.TypeOf(keepaliveMutex{}), keepalivelockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/last_error_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/last_error_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type lastErrorMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var lastErrorprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var lastErrorlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type lastErrorlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Lock() {
|
||||
locking.AddGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedLock(i lastErrorlockNameIndex) {
|
||||
locking.AddGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Unlock() {
|
||||
locking.DelGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedUnlock(i lastErrorlockNameIndex) {
|
||||
locking.DelGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func lastErrorinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
lastErrorinitLockNames()
|
||||
lastErrorprefixIndex = locking.NewMutexClass(reflect.TypeOf(lastErrorMutex{}), lastErrorlockNames)
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/pending_processing_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/pending_processing_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type pendingProcessingMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var pendingProcessingprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var pendingProcessinglockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type pendingProcessinglockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *pendingProcessingMutex) Lock() {
|
||||
locking.AddGLock(pendingProcessingprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *pendingProcessingMutex) NestedLock(i pendingProcessinglockNameIndex) {
|
||||
locking.AddGLock(pendingProcessingprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *pendingProcessingMutex) Unlock() {
|
||||
locking.DelGLock(pendingProcessingprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *pendingProcessingMutex) NestedUnlock(i pendingProcessinglockNameIndex) {
|
||||
locking.DelGLock(pendingProcessingprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func pendingProcessinginitLockNames() {}
|
||||
|
||||
func init() {
|
||||
pendingProcessinginitLockNames()
|
||||
pendingProcessingprefixIndex = locking.NewMutexClass(reflect.TypeOf(pendingProcessingMutex{}), pendingProcessinglockNames)
|
||||
}
|
||||
606
pkg/tcpip/transport/tcp/protocol.go
Normal file
606
pkg/tcpip/transport/tcp/protocol.go
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
// Copyright 2018 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 tcp contains the implementation of the TCP transport protocol.
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header/parse"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/internal/tcp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/raw"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtocolNumber is the tcp protocol number.
|
||||
ProtocolNumber = header.TCPProtocolNumber
|
||||
|
||||
// MinBufferSize is the smallest size of a receive or send buffer.
|
||||
MinBufferSize = 4 << 10 // 4096 bytes.
|
||||
|
||||
// DefaultSendBufferSize is the default size of the send buffer for
|
||||
// an endpoint.
|
||||
DefaultSendBufferSize = 1 << 20 // 1MB
|
||||
|
||||
// DefaultReceiveBufferSize is the default size of the receive buffer
|
||||
// for an endpoint.
|
||||
DefaultReceiveBufferSize = 1 << 20 // 1MB
|
||||
|
||||
// MaxBufferSize is the largest size a receive/send buffer can grow to.
|
||||
MaxBufferSize = 4 << 20 // 4MB
|
||||
|
||||
// DefaultTCPLingerTimeout is the amount of time that sockets linger in
|
||||
// FIN_WAIT_2 state before being marked closed.
|
||||
DefaultTCPLingerTimeout = 60 * time.Second
|
||||
|
||||
// MaxTCPLingerTimeout is the maximum amount of time that sockets
|
||||
// linger in FIN_WAIT_2 state before being marked closed.
|
||||
MaxTCPLingerTimeout = 120 * time.Second
|
||||
|
||||
// DefaultTCPTimeWaitTimeout is the amount of time that sockets linger
|
||||
// in TIME_WAIT state before being marked closed.
|
||||
DefaultTCPTimeWaitTimeout = 60 * time.Second
|
||||
|
||||
// DefaultSynRetries is the default value for the number of SYN retransmits
|
||||
// before a connect is aborted.
|
||||
DefaultSynRetries = 6
|
||||
|
||||
// DefaultKeepaliveIdle is the idle time for a connection before keep-alive
|
||||
// probes are sent.
|
||||
DefaultKeepaliveIdle = 2 * time.Hour
|
||||
|
||||
// DefaultKeepaliveInterval is the time between two successive keep-alive
|
||||
// probes.
|
||||
DefaultKeepaliveInterval = 75 * time.Second
|
||||
|
||||
// DefaultKeepaliveCount is the number of keep-alive probes that are sent
|
||||
// before declaring the connection dead.
|
||||
DefaultKeepaliveCount = 9
|
||||
)
|
||||
|
||||
const (
|
||||
ccReno = "reno"
|
||||
ccCubic = "cubic"
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type protocol struct {
|
||||
stack *stack.Stack
|
||||
|
||||
mu protocolRWMutex `state:"nosave"`
|
||||
sackEnabled bool
|
||||
recovery tcpip.TCPRecovery
|
||||
delayEnabled bool
|
||||
alwaysUseSynCookies bool
|
||||
sendBufferSize tcpip.TCPSendBufferSizeRangeOption
|
||||
recvBufferSize tcpip.TCPReceiveBufferSizeRangeOption
|
||||
congestionControl string
|
||||
availableCongestionControl []string
|
||||
moderateReceiveBuffer bool
|
||||
lingerTimeout time.Duration
|
||||
timeWaitTimeout time.Duration
|
||||
timeWaitReuse tcpip.TCPTimeWaitReuseOption
|
||||
minRTO time.Duration
|
||||
maxRTO time.Duration
|
||||
maxRetries uint32
|
||||
synRetries uint8
|
||||
dispatcher dispatcher
|
||||
|
||||
// probe, if not nil, will be invoked any time an endpoint receives a
|
||||
// TCP segment.
|
||||
//
|
||||
// This is immutable after creation.
|
||||
probe TCPProbeFunc `state:"nosave"`
|
||||
|
||||
// The following secrets are initialized once and stay unchanged after.
|
||||
seqnumSecret [16]byte
|
||||
tsOffsetSecret [16]byte
|
||||
}
|
||||
|
||||
// Number returns the tcp protocol number.
|
||||
func (*protocol) Number() tcpip.TransportProtocolNumber {
|
||||
return ProtocolNumber
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new tcp endpoint.
|
||||
func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return newEndpoint(p.stack, p, netProto, waiterQueue), nil
|
||||
}
|
||||
|
||||
// NewRawEndpoint creates a new raw TCP endpoint. Raw TCP sockets are currently
|
||||
// unsupported. It implements stack.TransportProtocol.NewRawEndpoint.
|
||||
func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return raw.NewEndpoint(p.stack, netProto, header.TCPProtocolNumber, waiterQueue)
|
||||
}
|
||||
|
||||
// MinimumPacketSize returns the minimum valid tcp packet size.
|
||||
func (*protocol) MinimumPacketSize() int {
|
||||
return header.TCPMinimumSize
|
||||
}
|
||||
|
||||
// ParsePorts returns the source and destination ports stored in the given tcp
|
||||
// packet.
|
||||
func (*protocol) ParsePorts(v []byte) (src, dst uint16, err tcpip.Error) {
|
||||
h := header.TCP(v)
|
||||
return h.SourcePort(), h.DestinationPort(), nil
|
||||
}
|
||||
|
||||
// QueuePacket queues packets targeted at an endpoint after hashing the packet
|
||||
// to a specific processing queue. Each queue is serviced by its own processor
|
||||
// goroutine which is responsible for dequeuing and doing full TCP dispatch of
|
||||
// the packet.
|
||||
func (p *protocol) QueuePacket(ep stack.TransportEndpoint, id stack.TransportEndpointID, pkt *stack.PacketBuffer) {
|
||||
p.dispatcher.queuePacket(ep, id, p.stack.Clock(), pkt)
|
||||
}
|
||||
|
||||
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
|
||||
// that don't match any existing endpoint.
|
||||
//
|
||||
// RFC 793, page 36, states that "If the connection does not exist (CLOSED) then
|
||||
// a reset is sent in response to any incoming segment except another reset. In
|
||||
// particular, SYNs addressed to a non-existent connection are rejected by this
|
||||
// means."
|
||||
func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
|
||||
s, err := newIncomingSegment(id, p.stack.Clock(), pkt)
|
||||
if err != nil {
|
||||
return stack.UnknownDestinationPacketMalformed
|
||||
}
|
||||
defer s.DecRef()
|
||||
if !s.csumValid {
|
||||
return stack.UnknownDestinationPacketMalformed
|
||||
}
|
||||
|
||||
if !s.flags.Contains(header.TCPFlagRst) {
|
||||
replyWithReset(p.stack, s, stack.DefaultTOS, tcpip.UseDefaultIPv4TTL, tcpip.UseDefaultIPv6HopLimit)
|
||||
}
|
||||
|
||||
return stack.UnknownDestinationPacketHandled
|
||||
}
|
||||
|
||||
func (p *protocol) tsOffset(src, dst tcpip.Address) tcp.TSOffset {
|
||||
// Initialize a random tsOffset that will be added to the recentTS
|
||||
// everytime the timestamp is sent when the Timestamp option is enabled.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc7323#section-5.4 for details on
|
||||
// why this is required.
|
||||
h := sha256.New()
|
||||
|
||||
// Per hash.Hash.Writer:
|
||||
//
|
||||
// It never returns an error.
|
||||
_, _ = h.Write(p.tsOffsetSecret[:])
|
||||
_, _ = h.Write(src.AsSlice())
|
||||
_, _ = h.Write(dst.AsSlice())
|
||||
return tcp.NewTSOffset(binary.LittleEndian.Uint32(h.Sum(nil)[:4]))
|
||||
}
|
||||
|
||||
// replyWithReset replies to the given segment with a reset segment.
|
||||
//
|
||||
// If the relevant TTL has its reset value (0 for ipv4TTL, -1 for ipv6HopLimit),
|
||||
// then the route's default TTL will be used.
|
||||
func replyWithReset(st *stack.Stack, s *segment, tos, ipv4TTL uint8, ipv6HopLimit int16) tcpip.Error {
|
||||
net := s.pkt.Network()
|
||||
route, err := st.FindRoute(s.pkt.NICID, net.DestinationAddress(), net.SourceAddress(), s.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer route.Release()
|
||||
|
||||
ttl := calculateTTL(route, ipv4TTL, ipv6HopLimit)
|
||||
|
||||
// Get the seqnum from the packet if the ack flag is set.
|
||||
seq := seqnum.Value(0)
|
||||
ack := seqnum.Value(0)
|
||||
flags := header.TCPFlagRst
|
||||
// As per RFC 793 page 35 (Reset Generation)
|
||||
// 1. If the connection does not exist (CLOSED) then a reset is sent
|
||||
// in response to any incoming segment except another reset. In
|
||||
// particular, SYNs addressed to a non-existent connection are rejected
|
||||
// by this means.
|
||||
|
||||
// If the incoming segment has an ACK field, the reset takes its
|
||||
// sequence number from the ACK field of the segment, otherwise the
|
||||
// reset has sequence number zero and the ACK field is set to the sum
|
||||
// of the sequence number and segment length of the incoming segment.
|
||||
// The connection remains in the CLOSED state.
|
||||
if s.flags.Contains(header.TCPFlagAck) {
|
||||
seq = s.ackNumber
|
||||
} else {
|
||||
flags |= header.TCPFlagAck
|
||||
ack = s.sequenceNumber.Add(s.logicalLen())
|
||||
}
|
||||
|
||||
var expOptVal uint16
|
||||
if s.ep != nil {
|
||||
expOptVal = s.ep.getExperimentOptionValue(route)
|
||||
}
|
||||
hdrSize := header.TCPMinimumSize + int(route.MaxHeaderLength())
|
||||
if route.NetProto() == header.IPv6ProtocolNumber && expOptVal != 0 {
|
||||
hdrSize += header.IPv6ExperimentHdrLength
|
||||
}
|
||||
p := stack.NewPacketBuffer(stack.PacketBufferOptions{ReserveHeaderBytes: hdrSize})
|
||||
defer p.DecRef()
|
||||
|
||||
return sendTCP(route, tcpFields{
|
||||
id: s.id,
|
||||
ttl: ttl,
|
||||
tos: tos,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: 0,
|
||||
expOptVal: expOptVal,
|
||||
}, p, stack.GSO{}, nil /* PacketOwner */)
|
||||
}
|
||||
|
||||
// SetOption implements stack.TransportProtocol.SetOption.
|
||||
func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) tcpip.Error {
|
||||
switch v := option.(type) {
|
||||
case *tcpip.TCPSACKEnabled:
|
||||
p.mu.Lock()
|
||||
p.sackEnabled = bool(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPRecovery:
|
||||
p.mu.Lock()
|
||||
p.recovery = *v
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPDelayEnabled:
|
||||
p.mu.Lock()
|
||||
p.delayEnabled = bool(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPSendBufferSizeRangeOption:
|
||||
if v.Min <= 0 || v.Default < v.Min || v.Default > v.Max {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.sendBufferSize = *v
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPReceiveBufferSizeRangeOption:
|
||||
if v.Min <= 0 || v.Default < v.Min || v.Default > v.Max {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.recvBufferSize = *v
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.CongestionControlOption:
|
||||
for _, c := range p.availableCongestionControl {
|
||||
if string(*v) == c {
|
||||
p.mu.Lock()
|
||||
p.congestionControl = string(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// linux returns ENOENT when an invalid congestion control
|
||||
// is specified.
|
||||
return &tcpip.ErrNoSuchFile{}
|
||||
|
||||
case *tcpip.TCPModerateReceiveBufferOption:
|
||||
p.mu.Lock()
|
||||
p.moderateReceiveBuffer = bool(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPLingerTimeoutOption:
|
||||
p.mu.Lock()
|
||||
if *v < 0 {
|
||||
p.lingerTimeout = 0
|
||||
} else {
|
||||
p.lingerTimeout = time.Duration(*v)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPTimeWaitTimeoutOption:
|
||||
p.mu.Lock()
|
||||
if *v < 0 {
|
||||
p.timeWaitTimeout = 0
|
||||
} else {
|
||||
p.timeWaitTimeout = time.Duration(*v)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPTimeWaitReuseOption:
|
||||
if *v < tcpip.TCPTimeWaitReuseDisabled || *v > tcpip.TCPTimeWaitReuseLoopbackOnly {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.timeWaitReuse = *v
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMinRTOOption:
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if *v < 0 {
|
||||
p.minRTO = MinRTO
|
||||
} else if minRTO := time.Duration(*v); minRTO <= p.maxRTO {
|
||||
p.minRTO = minRTO
|
||||
} else {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMaxRTOOption:
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if *v < 0 {
|
||||
p.maxRTO = MaxRTO
|
||||
} else if maxRTO := time.Duration(*v); maxRTO >= p.minRTO {
|
||||
p.maxRTO = maxRTO
|
||||
} else {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMaxRetriesOption:
|
||||
p.mu.Lock()
|
||||
p.maxRetries = uint32(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPAlwaysUseSynCookies:
|
||||
p.mu.Lock()
|
||||
p.alwaysUseSynCookies = bool(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPSynRetriesOption:
|
||||
if *v < 1 {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.synRetries = uint8(*v)
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// Option implements stack.TransportProtocol.Option.
|
||||
func (p *protocol) Option(option tcpip.GettableTransportProtocolOption) tcpip.Error {
|
||||
switch v := option.(type) {
|
||||
case *tcpip.TCPSACKEnabled:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPSACKEnabled(p.sackEnabled)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPRecovery:
|
||||
p.mu.RLock()
|
||||
*v = p.recovery
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPDelayEnabled:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPDelayEnabled(p.delayEnabled)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPSendBufferSizeRangeOption:
|
||||
p.mu.RLock()
|
||||
*v = p.sendBufferSize
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPReceiveBufferSizeRangeOption:
|
||||
p.mu.RLock()
|
||||
*v = p.recvBufferSize
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.CongestionControlOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.CongestionControlOption(p.congestionControl)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPAvailableCongestionControlOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPAvailableCongestionControlOption(strings.Join(p.availableCongestionControl, " "))
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPModerateReceiveBufferOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPModerateReceiveBufferOption(p.moderateReceiveBuffer)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPLingerTimeoutOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPLingerTimeoutOption(p.lingerTimeout)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPTimeWaitTimeoutOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPTimeWaitTimeoutOption(p.timeWaitTimeout)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPTimeWaitReuseOption:
|
||||
p.mu.RLock()
|
||||
*v = p.timeWaitReuse
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMinRTOOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPMinRTOOption(p.minRTO)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMaxRTOOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPMaxRTOOption(p.maxRTO)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPMaxRetriesOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPMaxRetriesOption(p.maxRetries)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPAlwaysUseSynCookies:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPAlwaysUseSynCookies(p.alwaysUseSynCookies)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPSynRetriesOption:
|
||||
p.mu.RLock()
|
||||
*v = tcpip.TCPSynRetriesOption(p.synRetries)
|
||||
p.mu.RUnlock()
|
||||
return nil
|
||||
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// SendBufferSize implements stack.SendBufSizeProto.
|
||||
func (p *protocol) SendBufferSize() tcpip.TCPSendBufferSizeRangeOption {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.sendBufferSize
|
||||
}
|
||||
|
||||
// Close implements stack.TransportProtocol.Close.
|
||||
func (p *protocol) Close() {
|
||||
p.dispatcher.close()
|
||||
}
|
||||
|
||||
// Wait implements stack.TransportProtocol.Wait.
|
||||
func (p *protocol) Wait() {
|
||||
p.dispatcher.wait()
|
||||
}
|
||||
|
||||
// Pause implements stack.TransportProtocol.Pause.
|
||||
func (p *protocol) Pause() {
|
||||
p.dispatcher.pause()
|
||||
}
|
||||
|
||||
// Resume implements stack.TransportProtocol.Resume.
|
||||
func (p *protocol) Resume() {
|
||||
p.dispatcher.resume()
|
||||
}
|
||||
|
||||
// Restore implements stack.TransportProtocol.Restore.
|
||||
func (p *protocol) Restore() {
|
||||
p.dispatcher.start()
|
||||
}
|
||||
|
||||
// Parse implements stack.TransportProtocol.Parse.
|
||||
func (*protocol) Parse(pkt *stack.PacketBuffer) bool {
|
||||
return parse.TCP(pkt)
|
||||
}
|
||||
|
||||
// NewProtocol returns a TCP transport protocol with Reno congestion control.
|
||||
func NewProtocol(s *stack.Stack) stack.TransportProtocol {
|
||||
return newProtocol(s, ccReno, nil)
|
||||
}
|
||||
|
||||
// NewProtocolProbe returns a TCP transport protocol with Reno congestion
|
||||
// control and the given probe.
|
||||
//
|
||||
// The probe will be invoked on every segment received by TCP endpoints. The
|
||||
// probe function is passed a copy of the TCP endpoint state before and after
|
||||
// processing of the segment.
|
||||
func NewProtocolProbe(probe TCPProbeFunc) func(*stack.Stack) stack.TransportProtocol {
|
||||
return func(s *stack.Stack) stack.TransportProtocol {
|
||||
return newProtocol(s, ccReno, probe)
|
||||
}
|
||||
}
|
||||
|
||||
// NewProtocolCUBIC returns a TCP transport protocol with CUBIC congestion
|
||||
// control.
|
||||
//
|
||||
// TODO(b/345835636): Remove this and make CUBIC the default across the board.
|
||||
func NewProtocolCUBIC(s *stack.Stack) stack.TransportProtocol {
|
||||
return newProtocol(s, ccCubic, nil)
|
||||
}
|
||||
|
||||
func newProtocol(s *stack.Stack, cc string, probe TCPProbeFunc) stack.TransportProtocol {
|
||||
rng := s.SecureRNG()
|
||||
var seqnumSecret [16]byte
|
||||
var tsOffsetSecret [16]byte
|
||||
if n, err := rng.Reader.Read(seqnumSecret[:]); err != nil || n != len(seqnumSecret) {
|
||||
panic(fmt.Sprintf("Read() failed: %v", err))
|
||||
}
|
||||
if n, err := rng.Reader.Read(tsOffsetSecret[:]); err != nil || n != len(tsOffsetSecret) {
|
||||
panic(fmt.Sprintf("Read() failed: %v", err))
|
||||
}
|
||||
p := protocol{
|
||||
stack: s,
|
||||
sendBufferSize: tcpip.TCPSendBufferSizeRangeOption{
|
||||
Min: MinBufferSize,
|
||||
Default: DefaultSendBufferSize,
|
||||
Max: MaxBufferSize,
|
||||
},
|
||||
recvBufferSize: tcpip.TCPReceiveBufferSizeRangeOption{
|
||||
Min: MinBufferSize,
|
||||
Default: DefaultReceiveBufferSize,
|
||||
Max: MaxBufferSize,
|
||||
},
|
||||
sackEnabled: true,
|
||||
congestionControl: cc,
|
||||
availableCongestionControl: []string{ccReno, ccCubic},
|
||||
moderateReceiveBuffer: true,
|
||||
lingerTimeout: DefaultTCPLingerTimeout,
|
||||
timeWaitTimeout: DefaultTCPTimeWaitTimeout,
|
||||
timeWaitReuse: tcpip.TCPTimeWaitReuseLoopbackOnly,
|
||||
synRetries: DefaultSynRetries,
|
||||
minRTO: MinRTO,
|
||||
maxRTO: MaxRTO,
|
||||
maxRetries: MaxRetries,
|
||||
recovery: tcpip.TCPRACKLossDetection,
|
||||
seqnumSecret: seqnumSecret,
|
||||
tsOffsetSecret: tsOffsetSecret,
|
||||
probe: probe,
|
||||
}
|
||||
p.dispatcher.init(s.InsecureRNG(), runtime.GOMAXPROCS(0))
|
||||
return &p
|
||||
}
|
||||
|
||||
// protocolFromStack retrieves the tcp.protocol instance from stack s.
|
||||
func protocolFromStack(s *stack.Stack) *protocol {
|
||||
return s.TransportProtocolInstance(ProtocolNumber).(*protocol)
|
||||
}
|
||||
96
pkg/tcpip/transport/tcp/protocol_mutex.go
Normal file
96
pkg/tcpip/transport/tcp/protocol_mutex.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// RWMutex is sync.RWMutex with the correctness validator.
|
||||
type protocolRWMutex struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var protocollockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type protocollockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) Lock() {
|
||||
locking.AddGLock(protocolprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) NestedLock(i protocollockNameIndex) {
|
||||
locking.AddGLock(protocolprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) Unlock() {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(protocolprefixIndex, -1)
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) NestedUnlock(i protocollockNameIndex) {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(protocolprefixIndex, int(i))
|
||||
}
|
||||
|
||||
// RLock locks m for reading.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) RLock() {
|
||||
locking.AddGLock(protocolprefixIndex, -1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) RUnlock() {
|
||||
m.mu.RUnlock()
|
||||
locking.DelGLock(protocolprefixIndex, -1)
|
||||
}
|
||||
|
||||
// RLockBypass locks m for reading without executing the validator.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) RLockBypass() {
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlockBypass undoes a single RLockBypass call.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) RUnlockBypass() {
|
||||
m.mu.RUnlock()
|
||||
}
|
||||
|
||||
// DowngradeLock atomically unlocks rw for writing and locks it for reading.
|
||||
// +checklocksignore
|
||||
func (m *protocolRWMutex) DowngradeLock() {
|
||||
m.mu.DowngradeLock()
|
||||
}
|
||||
|
||||
var protocolprefixIndex *locking.MutexClass
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func protocolinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
protocolinitLockNames()
|
||||
protocolprefixIndex = locking.NewMutexClass(reflect.TypeOf(protocolRWMutex{}), protocollockNames)
|
||||
}
|
||||
459
pkg/tcpip/transport/tcp/rack.go
Normal file
459
pkg/tcpip/transport/tcp/rack.go
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
// 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 tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
const (
|
||||
// wcDelayedACKTimeout is the recommended maximum delayed ACK timer
|
||||
// value as defined in the RFC. It stands for worst case delayed ACK
|
||||
// timer (WCDelAckT). When FlightSize is 1, PTO is inflated by
|
||||
// WCDelAckT time to compensate for a potential long delayed ACK timer
|
||||
// at the receiver.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.
|
||||
wcDelayedACKTimeout = 200 * time.Millisecond
|
||||
|
||||
// tcpRACKRecoveryThreshold is the number of loss recoveries for which
|
||||
// the reorder window is inflated and after that the reorder window is
|
||||
// reset to its initial value of minRTT/4.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2.
|
||||
tcpRACKRecoveryThreshold = 16
|
||||
)
|
||||
|
||||
// RACK is a loss detection algorithm used in TCP to detect packet loss and
|
||||
// reordering using transmission timestamp of the packets instead of packet or
|
||||
// sequence counts. To use RACK, SACK should be enabled on the connection.
|
||||
|
||||
// rackControl stores the rack related fields.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-6.1
|
||||
//
|
||||
// +stateify savable
|
||||
type rackControl struct {
|
||||
TCPRACKState
|
||||
|
||||
// exitedRecovery indicates if the connection is exiting loss recovery.
|
||||
// This flag is set if the sender is leaving the recovery after
|
||||
// receiving an ACK and is reset during updating of reorder window.
|
||||
exitedRecovery bool
|
||||
|
||||
// minRTT is the estimated minimum RTT of the connection.
|
||||
minRTT time.Duration
|
||||
|
||||
// tlpRxtOut indicates whether there is an unacknowledged
|
||||
// TLP retransmission.
|
||||
tlpRxtOut bool
|
||||
|
||||
// tlpHighRxt the value of sender.sndNxt at the time of sending
|
||||
// a TLP retransmission.
|
||||
tlpHighRxt seqnum.Value
|
||||
|
||||
// snd is a reference to the sender.
|
||||
snd *sender
|
||||
}
|
||||
|
||||
// init initializes RACK specific fields.
|
||||
func (rc *rackControl) init(snd *sender, iss seqnum.Value) {
|
||||
rc.FACK = iss
|
||||
rc.ReoWndIncr = 1
|
||||
rc.snd = snd
|
||||
}
|
||||
|
||||
// update will update the RACK related fields when an ACK has been received.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-09#section-6.2
|
||||
func (rc *rackControl) update(seg *segment, ackSeg *segment) {
|
||||
rtt := rc.snd.ep.stack.Clock().NowMonotonic().Sub(seg.xmitTime)
|
||||
|
||||
// If the ACK is for a retransmitted packet, do not update if it is a
|
||||
// spurious inference which is determined by below checks:
|
||||
// 1. When Timestamping option is available, if the TSVal is less than
|
||||
// the transmit time of the most recent retransmitted packet.
|
||||
// 2. When RTT calculated for the packet is less than the smoothed RTT
|
||||
// for the connection.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
|
||||
// step 2
|
||||
if seg.xmitCount > 1 {
|
||||
if ackSeg.parsedOptions.TS && ackSeg.parsedOptions.TSEcr != 0 {
|
||||
if ackSeg.parsedOptions.TSEcr < rc.snd.ep.tsVal(seg.xmitTime) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if rtt < rc.minRTT {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rc.RTT = rtt
|
||||
|
||||
// The sender can either track a simple global minimum of all RTT
|
||||
// measurements from the connection, or a windowed min-filtered value
|
||||
// of recent RTT measurements. This implementation keeps track of the
|
||||
// simple global minimum of all RTTs for the connection.
|
||||
if rtt < rc.minRTT || rc.minRTT == 0 {
|
||||
rc.minRTT = rtt
|
||||
}
|
||||
|
||||
// Update rc.xmitTime and rc.endSequence to the transmit time and
|
||||
// ending sequence number of the packet which has been acknowledged
|
||||
// most recently.
|
||||
endSeq := seg.sequenceNumber.Add(seqnum.Size(seg.payloadSize()))
|
||||
if rc.XmitTime.Before(seg.xmitTime) || (seg.xmitTime == rc.XmitTime && rc.EndSequence.LessThan(endSeq)) {
|
||||
rc.XmitTime = seg.xmitTime
|
||||
rc.EndSequence = endSeq
|
||||
}
|
||||
}
|
||||
|
||||
// detectReorder detects if packet reordering has been observed.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
|
||||
// - Step 3: Detect data segment reordering.
|
||||
// To detect reordering, the sender looks for original data segments being
|
||||
// delivered out of order. To detect such cases, the sender tracks the
|
||||
// highest sequence selectively or cumulatively acknowledged in the RACK.fack
|
||||
// variable. The name "fack" stands for the most "Forward ACK" (this term is
|
||||
// adopted from [FACK]). If a never retransmitted segment that's below
|
||||
// RACK.fack is (selectively or cumulatively) acknowledged, it has been
|
||||
// delivered out of order. The sender sets RACK.reord to TRUE if such segment
|
||||
// is identified.
|
||||
func (rc *rackControl) detectReorder(seg *segment) {
|
||||
endSeq := seg.sequenceNumber.Add(seqnum.Size(seg.payloadSize()))
|
||||
if rc.FACK.LessThan(endSeq) {
|
||||
rc.FACK = endSeq
|
||||
return
|
||||
}
|
||||
|
||||
if endSeq.LessThan(rc.FACK) && seg.xmitCount == 1 {
|
||||
rc.Reord = true
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rackControl) setDSACKSeen(dsackSeen bool) {
|
||||
rc.DSACKSeen = dsackSeen
|
||||
}
|
||||
|
||||
// shouldSchedulePTO dictates whether we should schedule a PTO or not.
|
||||
// See https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.1.
|
||||
func (s *sender) shouldSchedulePTO() bool {
|
||||
// Schedule PTO only if RACK loss detection is enabled.
|
||||
return s.ep.tcpRecovery&tcpip.TCPRACKLossDetection != 0 &&
|
||||
// The connection supports SACK.
|
||||
s.ep.SACKPermitted &&
|
||||
// The connection is not in loss recovery.
|
||||
(s.state != tcpip.RTORecovery && s.state != tcpip.SACKRecovery) &&
|
||||
// The connection has no SACKed sequences in the SACK scoreboard.
|
||||
s.ep.scoreboard.Sacked() == 0
|
||||
}
|
||||
|
||||
// schedulePTO schedules the probe timeout as defined in
|
||||
// https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.1.
|
||||
//
|
||||
// +checklocks:s.ep.mu
|
||||
func (s *sender) schedulePTO() {
|
||||
pto := time.Second
|
||||
s.rtt.Lock()
|
||||
if s.rtt.TCPRTTState.SRTTInited && s.rtt.TCPRTTState.SRTT > 0 {
|
||||
pto = s.rtt.TCPRTTState.SRTT * 2
|
||||
if s.Outstanding == 1 {
|
||||
pto += wcDelayedACKTimeout
|
||||
}
|
||||
}
|
||||
s.rtt.Unlock()
|
||||
|
||||
now := s.ep.stack.Clock().NowMonotonic()
|
||||
if s.resendTimer.enabled() {
|
||||
if now.Add(pto).After(s.resendTimer.target) {
|
||||
pto = s.resendTimer.target.Sub(now)
|
||||
}
|
||||
s.resendTimer.disable()
|
||||
}
|
||||
|
||||
s.probeTimer.enable(pto)
|
||||
}
|
||||
|
||||
// probeTimerExpired is the same as TLP_send_probe() as defined in
|
||||
// https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.2.
|
||||
//
|
||||
// +checklocks:s.ep.mu
|
||||
func (s *sender) probeTimerExpired() tcpip.Error {
|
||||
if s.probeTimer.isUninitialized() || !s.probeTimer.checkExpiration() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dataSent bool
|
||||
if s.writeNext != nil && s.writeNext.xmitCount == 0 && s.Outstanding < s.SndCwnd {
|
||||
dataSent = s.maybeSendSegment(s.writeNext, int(s.ep.scoreboard.SMSS()), s.SndUna.Add(s.SndWnd))
|
||||
if dataSent {
|
||||
s.Outstanding += s.pCount(s.writeNext, s.MaxPayloadSize)
|
||||
s.updateWriteNext(s.writeNext.Next())
|
||||
}
|
||||
}
|
||||
|
||||
if !dataSent && !s.rc.tlpRxtOut {
|
||||
var highestSeqXmit *segment
|
||||
for highestSeqXmit = s.writeList.Front(); highestSeqXmit != nil; highestSeqXmit = highestSeqXmit.Next() {
|
||||
if highestSeqXmit.xmitCount == 0 {
|
||||
// Nothing in writeList is transmitted, no need to send a probe.
|
||||
highestSeqXmit = nil
|
||||
break
|
||||
}
|
||||
if highestSeqXmit.Next() == nil || highestSeqXmit.Next().xmitCount == 0 {
|
||||
// Either everything in writeList has been transmitted or the next
|
||||
// sequence has not been transmitted. Either way this is the highest
|
||||
// sequence segment that was transmitted.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if highestSeqXmit != nil {
|
||||
dataSent = s.maybeSendSegment(highestSeqXmit, int(s.ep.scoreboard.SMSS()), s.SndUna.Add(s.SndWnd))
|
||||
if dataSent {
|
||||
s.rc.tlpRxtOut = true
|
||||
s.rc.tlpHighRxt = s.SndNxt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Whether or not the probe was sent, the sender must arm the resend timer,
|
||||
// not the probe timer. This ensures that the sender does not send repeated,
|
||||
// back-to-back tail loss probes.
|
||||
s.postXmit(dataSent, false /* shouldScheduleProbe */)
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectTLPRecovery detects if recovery was accomplished by the loss probes
|
||||
// and updates TLP state accordingly.
|
||||
// See https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.6.3.
|
||||
//
|
||||
// +checklocks:s.ep.mu
|
||||
func (s *sender) detectTLPRecovery(ack seqnum.Value, rcvdSeg *segment) {
|
||||
if !(s.ep.SACKPermitted && s.rc.tlpRxtOut) {
|
||||
return
|
||||
}
|
||||
|
||||
// Step 1.
|
||||
if s.isDupAck(rcvdSeg) && ack == s.rc.tlpHighRxt {
|
||||
var sbAboveTLPHighRxt bool
|
||||
for _, sb := range rcvdSeg.parsedOptions.SACKBlocks {
|
||||
if s.rc.tlpHighRxt.LessThan(sb.End) {
|
||||
sbAboveTLPHighRxt = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !sbAboveTLPHighRxt {
|
||||
// TLP episode is complete.
|
||||
s.rc.tlpRxtOut = false
|
||||
}
|
||||
}
|
||||
|
||||
if s.rc.tlpRxtOut && s.rc.tlpHighRxt.LessThanEq(ack) {
|
||||
// TLP episode is complete.
|
||||
s.rc.tlpRxtOut = false
|
||||
if !checkDSACK(rcvdSeg) {
|
||||
// Step 2. Either the original packet or the retransmission (in the
|
||||
// form of a probe) was lost. Invoke a congestion control response
|
||||
// equivalent to fast recovery.
|
||||
s.cc.HandleLossDetected()
|
||||
s.enterRecovery()
|
||||
s.leaveRecovery()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updateRACKReorderWindow updates the reorder window.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
|
||||
// - Step 4: Update RACK reordering window
|
||||
// To handle the prevalent small degree of reordering, RACK.reo_wnd serves as
|
||||
// an allowance for settling time before marking a packet lost. RACK starts
|
||||
// initially with a conservative window of min_RTT/4. If no reordering has
|
||||
// been observed RACK uses reo_wnd of zero during loss recovery, in order to
|
||||
// retransmit quickly, or when the number of DUPACKs exceeds the classic
|
||||
// DUPACKthreshold.
|
||||
//
|
||||
// +checklocks:rc.snd.ep.mu
|
||||
func (rc *rackControl) updateRACKReorderWindow() {
|
||||
dsackSeen := rc.DSACKSeen
|
||||
snd := rc.snd
|
||||
|
||||
// React to DSACK once per round trip.
|
||||
// If SND.UNA < RACK.rtt_seq:
|
||||
// RACK.dsack = false
|
||||
if snd.SndUna.LessThan(rc.RTTSeq) {
|
||||
dsackSeen = false
|
||||
}
|
||||
|
||||
// If RACK.dsack:
|
||||
// RACK.reo_wnd_incr += 1
|
||||
// RACK.dsack = false
|
||||
// RACK.rtt_seq = SND.NXT
|
||||
// RACK.reo_wnd_persist = 16
|
||||
if dsackSeen {
|
||||
rc.ReoWndIncr++
|
||||
dsackSeen = false
|
||||
rc.RTTSeq = snd.SndNxt
|
||||
rc.ReoWndPersist = tcpRACKRecoveryThreshold
|
||||
} else if rc.exitedRecovery {
|
||||
// Else if exiting loss recovery:
|
||||
// RACK.reo_wnd_persist -= 1
|
||||
// If RACK.reo_wnd_persist <= 0:
|
||||
// RACK.reo_wnd_incr = 1
|
||||
rc.ReoWndPersist--
|
||||
if rc.ReoWndPersist <= 0 {
|
||||
rc.ReoWndIncr = 1
|
||||
}
|
||||
rc.exitedRecovery = false
|
||||
}
|
||||
|
||||
// Reorder window is zero during loss recovery, or when the number of
|
||||
// DUPACKs exceeds the classic DUPACKthreshold.
|
||||
// If RACK.reord is FALSE:
|
||||
// If in loss recovery: (If in fast or timeout recovery)
|
||||
// RACK.reo_wnd = 0
|
||||
// Return
|
||||
// Else if RACK.pkts_sacked >= RACK.dupthresh:
|
||||
// RACK.reo_wnd = 0
|
||||
// return
|
||||
if !rc.Reord {
|
||||
if snd.state == tcpip.RTORecovery || snd.state == tcpip.SACKRecovery {
|
||||
rc.ReoWnd = 0
|
||||
return
|
||||
}
|
||||
|
||||
if snd.SackedOut >= nDupAckThreshold {
|
||||
rc.ReoWnd = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate reorder window.
|
||||
// RACK.reo_wnd = RACK.min_RTT / 4 * RACK.reo_wnd_incr
|
||||
// RACK.reo_wnd = min(RACK.reo_wnd, SRTT)
|
||||
snd.rtt.Lock()
|
||||
srtt := snd.rtt.TCPRTTState.SRTT
|
||||
snd.rtt.Unlock()
|
||||
rc.ReoWnd = time.Duration((int64(rc.minRTT) / 4) * int64(rc.ReoWndIncr))
|
||||
if srtt < rc.ReoWnd {
|
||||
rc.ReoWnd = srtt
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rackControl) exitRecovery() {
|
||||
rc.exitedRecovery = true
|
||||
}
|
||||
|
||||
// detectLoss marks the segment as lost if the reordering window has elapsed
|
||||
// and the ACK is not received. It will also arm the reorder timer.
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2 Step 5.
|
||||
//
|
||||
// +checklocks:rc.snd.ep.mu
|
||||
func (rc *rackControl) detectLoss(rcvTime tcpip.MonotonicTime) int {
|
||||
var timeout time.Duration
|
||||
numLost := 0
|
||||
for seg := rc.snd.writeList.Front(); seg != nil && seg.xmitCount != 0; seg = seg.Next() {
|
||||
if rc.snd.ep.scoreboard.IsSACKED(seg.sackBlock()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if seg.lost && seg.xmitCount == 1 {
|
||||
numLost++
|
||||
continue
|
||||
}
|
||||
|
||||
endSeq := seg.sequenceNumber.Add(seqnum.Size(seg.payloadSize()))
|
||||
if seg.xmitTime.Before(rc.XmitTime) || (seg.xmitTime == rc.XmitTime && rc.EndSequence.LessThan(endSeq)) {
|
||||
timeRemaining := seg.xmitTime.Sub(rcvTime) + rc.RTT + rc.ReoWnd
|
||||
if timeRemaining <= 0 {
|
||||
seg.lost = true
|
||||
numLost++
|
||||
} else if timeRemaining > timeout {
|
||||
timeout = timeRemaining
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if timeout != 0 && !rc.snd.reorderTimer.enabled() {
|
||||
rc.snd.reorderTimer.enable(timeout)
|
||||
}
|
||||
return numLost
|
||||
}
|
||||
|
||||
// reorderTimerExpired will retransmit the segments which have not been acked
|
||||
// before the reorder timer expired.
|
||||
//
|
||||
// +checklocks:rc.snd.ep.mu
|
||||
func (rc *rackControl) reorderTimerExpired() tcpip.Error {
|
||||
if rc.snd.reorderTimer.isUninitialized() || !rc.snd.reorderTimer.checkExpiration() {
|
||||
return nil
|
||||
}
|
||||
|
||||
numLost := rc.detectLoss(rc.snd.ep.stack.Clock().NowMonotonic())
|
||||
if numLost == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fastRetransmit := false
|
||||
if !rc.snd.FastRecovery.Active {
|
||||
rc.snd.cc.HandleLossDetected()
|
||||
rc.snd.enterRecovery()
|
||||
fastRetransmit = true
|
||||
}
|
||||
|
||||
rc.DoRecovery(nil, fastRetransmit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoRecovery implements lossRecovery.DoRecovery.
|
||||
//
|
||||
// +checklocks:rc.snd.ep.mu
|
||||
func (rc *rackControl) DoRecovery(_ *segment, fastRetransmit bool) {
|
||||
snd := rc.snd
|
||||
if fastRetransmit {
|
||||
snd.resendSegment()
|
||||
}
|
||||
|
||||
var dataSent bool
|
||||
// Iterate the writeList and retransmit the segments which are marked
|
||||
// as lost by RACK.
|
||||
for seg := snd.writeList.Front(); seg != nil && seg.xmitCount > 0; seg = seg.Next() {
|
||||
if seg == snd.writeNext {
|
||||
break
|
||||
}
|
||||
|
||||
if !seg.lost {
|
||||
continue
|
||||
}
|
||||
|
||||
// Reset seg.lost as it is already SACKed.
|
||||
if snd.ep.scoreboard.IsSACKED(seg.sackBlock()) {
|
||||
seg.lost = false
|
||||
continue
|
||||
}
|
||||
|
||||
// Check the congestion window after entering recovery.
|
||||
if snd.Outstanding >= snd.SndCwnd {
|
||||
break
|
||||
}
|
||||
|
||||
if sent := snd.maybeSendSegment(seg, int(snd.ep.scoreboard.SMSS()), snd.SndUna.Add(snd.SndWnd)); !sent {
|
||||
break
|
||||
}
|
||||
dataSent = true
|
||||
snd.Outstanding += snd.pCount(seg, snd.MaxPayloadSize)
|
||||
}
|
||||
|
||||
snd.postXmit(dataSent, true /* shouldScheduleProbe */)
|
||||
}
|
||||
616
pkg/tcpip/transport/tcp/rcv.go
Normal file
616
pkg/tcpip/transport/tcp/rcv.go
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"math"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
// receiver holds the state necessary to receive TCP segments and turn them
|
||||
// into a stream of bytes.
|
||||
//
|
||||
// +stateify savable
|
||||
type receiver struct {
|
||||
TCPReceiverState
|
||||
ep *Endpoint
|
||||
|
||||
// rcvWnd is the non-scaled receive window last advertised to the peer.
|
||||
rcvWnd seqnum.Size
|
||||
|
||||
// rcvWUP is the RcvNxt value at the last window update sent.
|
||||
rcvWUP seqnum.Value
|
||||
|
||||
// prevBufused is the snapshot of endpoint rcvBufUsed taken when we
|
||||
// advertise a receive window.
|
||||
prevBufUsed int
|
||||
|
||||
closed bool
|
||||
|
||||
// pendingRcvdSegments is bounded by the receive buffer size of the
|
||||
// endpoint.
|
||||
pendingRcvdSegments segmentHeap
|
||||
|
||||
// Time when the last ack was received.
|
||||
lastRcvdAckTime tcpip.MonotonicTime
|
||||
}
|
||||
|
||||
func newReceiver(ep *Endpoint, irs seqnum.Value, rcvWnd seqnum.Size, rcvWndScale uint8) *receiver {
|
||||
return &receiver{
|
||||
ep: ep,
|
||||
TCPReceiverState: TCPReceiverState{
|
||||
RcvNxt: irs + 1,
|
||||
RcvAcc: irs.Add(rcvWnd + 1),
|
||||
RcvWndScale: rcvWndScale,
|
||||
},
|
||||
rcvWnd: rcvWnd,
|
||||
rcvWUP: irs + 1,
|
||||
lastRcvdAckTime: ep.stack.Clock().NowMonotonic(),
|
||||
}
|
||||
}
|
||||
|
||||
// acceptable checks if the segment sequence number range is acceptable
|
||||
// according to the table on page 26 of RFC 793.
|
||||
func (r *receiver) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool {
|
||||
// r.rcvWnd could be much larger than the window size we advertised in our
|
||||
// outgoing packets, we should use what we have advertised for acceptability
|
||||
// test.
|
||||
scaledWindowSize := r.rcvWnd >> r.RcvWndScale
|
||||
if scaledWindowSize > math.MaxUint16 {
|
||||
// This is what we actually put in the Window field.
|
||||
scaledWindowSize = math.MaxUint16
|
||||
}
|
||||
advertisedWindowSize := scaledWindowSize << r.RcvWndScale
|
||||
return header.Acceptable(segSeq, segLen, r.RcvNxt, r.RcvNxt.Add(advertisedWindowSize))
|
||||
}
|
||||
|
||||
// currentWindow returns the available space in the window that was advertised
|
||||
// last to our peer.
|
||||
func (r *receiver) currentWindow() (curWnd seqnum.Size) {
|
||||
endOfWnd := r.rcvWUP.Add(r.rcvWnd)
|
||||
if endOfWnd.LessThan(r.RcvNxt) {
|
||||
// return 0 if r.RcvNxt is past the end of the previously advertised window.
|
||||
// This can happen because we accept a large segment completely even if
|
||||
// accepting it causes it to partially exceed the advertised window.
|
||||
return 0
|
||||
}
|
||||
return r.RcvNxt.Size(endOfWnd)
|
||||
}
|
||||
|
||||
// getSendParams returns the parameters needed by the sender when building
|
||||
// segments to send.
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) getSendParams() (RcvNxt seqnum.Value, rcvWnd seqnum.Size) {
|
||||
newWnd := r.ep.selectWindow()
|
||||
curWnd := r.currentWindow()
|
||||
unackLen := int(r.ep.snd.MaxSentAck.Size(r.RcvNxt))
|
||||
bufUsed := r.ep.receiveBufferUsed()
|
||||
|
||||
// Grow the right edge of the window only for payloads larger than the
|
||||
// the segment overhead OR if the application is actively consuming data.
|
||||
//
|
||||
// Avoiding growing the right edge otherwise, addresses a situation below:
|
||||
// An application has been slow in reading data and we have burst of
|
||||
// incoming segments lengths < segment overhead. Here, our available free
|
||||
// memory would reduce drastically when compared to the advertised receive
|
||||
// window.
|
||||
//
|
||||
// For example: With incoming 512 bytes segments, segment overhead of
|
||||
// 552 bytes (at the time of writing this comment), with receive window
|
||||
// starting from 1MB and with rcvAdvWndScale being 1, buffer would reach 0
|
||||
// when the curWnd is still 19436 bytes, because for every incoming segment
|
||||
// newWnd would reduce by (552+512) >> rcvAdvWndScale (current value 1),
|
||||
// while curWnd would reduce by 512 bytes.
|
||||
// Such a situation causes us to keep tail dropping the incoming segments
|
||||
// and never advertise zero receive window to the peer.
|
||||
//
|
||||
// Linux does a similar check for minimal sk_buff size (128):
|
||||
// https://github.com/torvalds/linux/blob/d5beb3140f91b1c8a3d41b14d729aefa4dcc58bc/net/ipv4/tcp_input.c#L783
|
||||
//
|
||||
// Also, if the application is reading the data, we keep growing the right
|
||||
// edge, as we are still advertising a window that we think can be serviced.
|
||||
toGrow := unackLen >= SegOverheadSize || bufUsed <= r.prevBufUsed
|
||||
|
||||
// Update RcvAcc only if new window is > previously advertised window. We
|
||||
// should never shrink the acceptable sequence space once it has been
|
||||
// advertised the peer. If we shrink the acceptable sequence space then we
|
||||
// would end up dropping bytes that might already be in flight.
|
||||
// ==================================================== sequence space.
|
||||
// ^ ^ ^ ^
|
||||
// rcvWUP RcvNxt RcvAcc new RcvAcc
|
||||
// <=====curWnd ===>
|
||||
// <========= newWnd > curWnd ========= >
|
||||
if r.RcvNxt.Add(curWnd).LessThan(r.RcvNxt.Add(newWnd)) && toGrow {
|
||||
// If the new window moves the right edge, then update RcvAcc.
|
||||
r.RcvAcc = r.RcvNxt.Add(newWnd)
|
||||
} else {
|
||||
if newWnd == 0 {
|
||||
// newWnd is zero but we can't advertise a zero as it would cause window
|
||||
// to shrink so just increment a metric to record this event.
|
||||
r.ep.stats.ReceiveErrors.WantZeroRcvWindow.Increment()
|
||||
}
|
||||
newWnd = curWnd
|
||||
}
|
||||
|
||||
// Apply silly-window avoidance when recovering from zero-window situation.
|
||||
// Keep advertising zero receive window up until the new window reaches a
|
||||
// threshold.
|
||||
if r.rcvWnd == 0 && newWnd != 0 {
|
||||
r.ep.rcvQueueMu.Lock()
|
||||
if crossed, above := r.ep.windowCrossedACKThresholdLocked(int(newWnd), int(r.ep.ops.GetReceiveBufferSize())); !crossed && !above {
|
||||
newWnd = 0
|
||||
}
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
}
|
||||
|
||||
// Stash away the non-scaled receive window as we use it for measuring
|
||||
// receiver's estimated RTT.
|
||||
r.rcvWnd = newWnd
|
||||
r.rcvWUP = r.RcvNxt
|
||||
r.prevBufUsed = bufUsed
|
||||
scaledWnd := r.rcvWnd >> r.RcvWndScale
|
||||
if scaledWnd == 0 {
|
||||
// Increment a metric if we are advertising an actual zero window.
|
||||
r.ep.stats.ReceiveErrors.ZeroRcvWindowState.Increment()
|
||||
}
|
||||
|
||||
// If we started off with a window larger than what can he held in
|
||||
// the 16bit window field, we ceil the value to the max value.
|
||||
if scaledWnd > math.MaxUint16 {
|
||||
scaledWnd = seqnum.Size(math.MaxUint16)
|
||||
|
||||
// Ensure that the stashed receive window always reflects what
|
||||
// is being advertised.
|
||||
r.rcvWnd = scaledWnd << r.RcvWndScale
|
||||
}
|
||||
return r.RcvNxt, scaledWnd
|
||||
}
|
||||
|
||||
// nonZeroWindow is called when the receive window grows from zero to nonzero;
|
||||
// in such cases we may need to send an ack to indicate to our peer that it can
|
||||
// resume sending data.
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) nonZeroWindow() {
|
||||
// Immediately send an ack.
|
||||
r.ep.snd.sendAck()
|
||||
}
|
||||
|
||||
// consumeSegment attempts to consume a segment that was received by r. The
|
||||
// segment may have just been received or may have been received earlier but
|
||||
// wasn't ready to be consumed then.
|
||||
//
|
||||
// Returns true if the segment was consumed, false if it cannot be consumed
|
||||
// yet because of a missing segment.
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) consumeSegment(s *segment, segSeq seqnum.Value, segLen seqnum.Size) bool {
|
||||
if segLen > 0 {
|
||||
// If the segment doesn't include the seqnum we're expecting to
|
||||
// consume now, we're missing a segment. We cannot proceed until
|
||||
// we receive that segment though.
|
||||
if !r.RcvNxt.InWindow(segSeq, segLen) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Trim segment to eliminate already acknowledged data.
|
||||
if segSeq.LessThan(r.RcvNxt) {
|
||||
diff := segSeq.Size(r.RcvNxt)
|
||||
segLen -= diff
|
||||
segSeq.UpdateForward(diff)
|
||||
s.sequenceNumber.UpdateForward(diff)
|
||||
s.TrimFront(diff)
|
||||
}
|
||||
|
||||
// Move segment to ready-to-deliver list. Wakeup any waiters.
|
||||
r.ep.readyToRead(s)
|
||||
|
||||
} else if segSeq != r.RcvNxt {
|
||||
return false
|
||||
}
|
||||
|
||||
// Update the segment that we're expecting to consume.
|
||||
r.RcvNxt = segSeq.Add(segLen)
|
||||
|
||||
// In cases of a misbehaving sender which could send more than the
|
||||
// advertised window, we could end up in a situation where we get a
|
||||
// segment that exceeds the window advertised. Instead of partially
|
||||
// accepting the segment and discarding bytes beyond the advertised
|
||||
// window, we accept the whole segment and make sure r.RcvAcc is moved
|
||||
// forward to match r.RcvNxt to indicate that the window is now closed.
|
||||
//
|
||||
// In absence of this check the r.acceptable() check fails and accepts
|
||||
// segments that should be dropped because rcvWnd is calculated as
|
||||
// the size of the interval (RcvNxt, RcvAcc] which becomes extremely
|
||||
// large if RcvAcc is ever less than RcvNxt.
|
||||
if r.RcvAcc.LessThan(r.RcvNxt) {
|
||||
r.RcvAcc = r.RcvNxt
|
||||
}
|
||||
|
||||
// Trim SACK Blocks to remove any SACK information that covers
|
||||
// sequence numbers that have been consumed.
|
||||
TrimSACKBlockList(&r.ep.sack, r.RcvNxt)
|
||||
|
||||
// Handle FIN or FIN-ACK.
|
||||
if s.flags.Contains(header.TCPFlagFin) {
|
||||
r.RcvNxt++
|
||||
|
||||
// Send ACK immediately.
|
||||
r.ep.snd.sendAck()
|
||||
|
||||
// Tell any readers that no more data will come.
|
||||
r.closed = true
|
||||
r.ep.readyToRead(nil)
|
||||
|
||||
// We just received a FIN, our next state depends on whether we sent a
|
||||
// FIN already or not.
|
||||
switch r.ep.EndpointState() {
|
||||
case StateEstablished:
|
||||
r.ep.setEndpointState(StateCloseWait)
|
||||
case StateFinWait1:
|
||||
if s.flags.Contains(header.TCPFlagAck) && s.ackNumber == r.ep.snd.SndNxt {
|
||||
// FIN-ACK, transition to TIME-WAIT.
|
||||
r.ep.setEndpointState(StateTimeWait)
|
||||
} else {
|
||||
// Simultaneous close, expecting a final ACK.
|
||||
r.ep.setEndpointState(StateClosing)
|
||||
}
|
||||
case StateFinWait2:
|
||||
r.ep.setEndpointState(StateTimeWait)
|
||||
}
|
||||
|
||||
// Flush out any pending segments, except the very first one if
|
||||
// it happens to be the one we're handling now because the
|
||||
// caller is using it.
|
||||
first := 0
|
||||
if len(r.pendingRcvdSegments) != 0 && r.pendingRcvdSegments[0] == s {
|
||||
first = 1
|
||||
}
|
||||
|
||||
for i := first; i < len(r.pendingRcvdSegments); i++ {
|
||||
r.PendingBufUsed -= r.pendingRcvdSegments[i].segMemSize()
|
||||
r.pendingRcvdSegments[i].DecRef()
|
||||
// Note that slice truncation does not allow garbage
|
||||
// collection of truncated items, thus truncated items
|
||||
// must be set to nil to avoid memory leaks.
|
||||
r.pendingRcvdSegments[i] = nil
|
||||
}
|
||||
r.pendingRcvdSegments = r.pendingRcvdSegments[:first]
|
||||
r.ep.updateConnDirectionState(connDirectionStateRcvClosed)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle ACK (not FIN-ACK, which we handled above) during one of the
|
||||
// shutdown states.
|
||||
if s.flags.Contains(header.TCPFlagAck) && s.ackNumber == r.ep.snd.SndNxt {
|
||||
switch r.ep.EndpointState() {
|
||||
case StateFinWait1:
|
||||
r.ep.setEndpointState(StateFinWait2)
|
||||
if e := r.ep; e.closed {
|
||||
// The socket has been closed and we are in
|
||||
// FIN-WAIT-2 so start the FIN-WAIT-2 timer.
|
||||
e.finWait2Timer = e.stack.Clock().AfterFunc(e.tcpLingerTimeout, e.finWait2TimerExpired)
|
||||
}
|
||||
|
||||
case StateClosing:
|
||||
r.ep.setEndpointState(StateTimeWait)
|
||||
case StateLastAck:
|
||||
r.ep.transitionToStateCloseLocked()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// updateRTT updates the receiver RTT measurement based on the sequence number
|
||||
// of the received segment.
|
||||
func (r *receiver) updateRTT() {
|
||||
// From: https://public.lanl.gov/radiant/pubs/drs/sc2001-poster.pdf
|
||||
//
|
||||
// A system that is only transmitting acknowledgements can still
|
||||
// estimate the round-trip time by observing the time between when a byte
|
||||
// is first acknowledged and the receipt of data that is at least one
|
||||
// window beyond the sequence number that was acknowledged.
|
||||
r.ep.rcvQueueMu.Lock()
|
||||
if r.ep.RcvAutoParams.RTTMeasureTime == (tcpip.MonotonicTime{}) {
|
||||
// New measurement.
|
||||
r.ep.RcvAutoParams.RTTMeasureTime = r.ep.stack.Clock().NowMonotonic()
|
||||
r.ep.RcvAutoParams.RTTMeasureSeqNumber = r.RcvNxt.Add(r.rcvWnd)
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
return
|
||||
}
|
||||
if r.RcvNxt.LessThan(r.ep.RcvAutoParams.RTTMeasureSeqNumber) {
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
return
|
||||
}
|
||||
rtt := r.ep.stack.Clock().NowMonotonic().Sub(r.ep.RcvAutoParams.RTTMeasureTime)
|
||||
// We only store the minimum observed RTT here as this is only used in
|
||||
// absence of a SRTT available from either timestamps or a sender
|
||||
// measurement of RTT.
|
||||
if r.ep.RcvAutoParams.RTT == 0 || rtt < r.ep.RcvAutoParams.RTT {
|
||||
r.ep.RcvAutoParams.RTT = rtt
|
||||
}
|
||||
r.ep.RcvAutoParams.RTTMeasureTime = r.ep.stack.Clock().NowMonotonic()
|
||||
r.ep.RcvAutoParams.RTTMeasureSeqNumber = r.RcvNxt.Add(r.rcvWnd)
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
}
|
||||
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, closed bool) (drop bool, err tcpip.Error) {
|
||||
r.ep.rcvQueueMu.Lock()
|
||||
rcvClosed := r.ep.RcvClosed || r.closed
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
|
||||
// If we are in one of the shutdown states then we need to do
|
||||
// additional checks before we try and process the segment.
|
||||
switch state {
|
||||
case StateCloseWait, StateClosing, StateLastAck:
|
||||
if !s.sequenceNumber.LessThanEq(r.RcvNxt) {
|
||||
// Just drop the segment as we have
|
||||
// already received a FIN and this
|
||||
// segment is after the sequence number
|
||||
// for the FIN.
|
||||
return true, nil
|
||||
}
|
||||
fallthrough
|
||||
case StateFinWait1, StateFinWait2:
|
||||
// If the ACK acks something not yet sent then we send an ACK.
|
||||
//
|
||||
// RFC793, page 37: If the connection is in a synchronized state,
|
||||
// (ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK,
|
||||
// TIME-WAIT), any unacceptable segment (out of window sequence number
|
||||
// or unacceptable acknowledgment number) must elicit only an empty
|
||||
// acknowledgment segment containing the current send-sequence number
|
||||
// and an acknowledgment indicating the next sequence number expected
|
||||
// to be received, and the connection remains in the same state.
|
||||
//
|
||||
// Just as on Linux, we do not apply this behavior when state is
|
||||
// ESTABLISHED.
|
||||
// Linux receive processing for all states except ESTABLISHED and
|
||||
// TIME_WAIT is here where if the ACK check fails, we attempt to
|
||||
// reply back with an ACK with correct seq/ack numbers.
|
||||
// https://github.com/torvalds/linux/blob/v5.8/net/ipv4/tcp_input.c#L6186
|
||||
// The ESTABLISHED state processing is here where if the ACK check
|
||||
// fails, we ignore the packet:
|
||||
// https://github.com/torvalds/linux/blob/v5.8/net/ipv4/tcp_input.c#L5591
|
||||
if r.ep.snd.SndNxt.LessThan(s.ackNumber) {
|
||||
r.ep.snd.maybeSendOutOfWindowAck(s)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If we are closed for reads (either due to an
|
||||
// incoming FIN or the user calling shutdown(..,
|
||||
// SHUT_RD) then any data past the RcvNxt should
|
||||
// trigger a RST.
|
||||
endDataSeq := s.sequenceNumber.Add(seqnum.Size(s.payloadSize()))
|
||||
if state != StateCloseWait && rcvClosed && r.RcvNxt.LessThan(endDataSeq) {
|
||||
return true, &tcpip.ErrConnectionAborted{}
|
||||
}
|
||||
if state == StateFinWait1 {
|
||||
break
|
||||
}
|
||||
|
||||
// If it's a retransmission of an old data segment
|
||||
// or a pure ACK then allow it.
|
||||
if s.sequenceNumber.Add(s.logicalLen()).LessThanEq(r.RcvNxt) ||
|
||||
s.logicalLen() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// In FIN-WAIT2 if the socket is fully
|
||||
// closed(not owned by application on our end
|
||||
// then the only acceptable segment is a
|
||||
// FIN. Since FIN can technically also carry
|
||||
// data we verify that the segment carrying a
|
||||
// FIN ends at exactly e.RcvNxt+1.
|
||||
//
|
||||
// From RFC793 page 25.
|
||||
//
|
||||
// For sequence number purposes, the SYN is
|
||||
// considered to occur before the first actual
|
||||
// data octet of the segment in which it occurs,
|
||||
// while the FIN is considered to occur after
|
||||
// the last actual data octet in a segment in
|
||||
// which it occurs.
|
||||
if closed && (!s.flags.Contains(header.TCPFlagFin) || s.sequenceNumber.Add(s.logicalLen()) != r.RcvNxt+1) {
|
||||
return true, &tcpip.ErrConnectionAborted{}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't care about receive processing anymore if the receive side
|
||||
// is closed.
|
||||
//
|
||||
// NOTE: We still want to permit a FIN as it's possible only our
|
||||
// end has closed and the peer is yet to send a FIN. Hence we
|
||||
// compare only the payload.
|
||||
segEnd := s.sequenceNumber.Add(seqnum.Size(s.payloadSize()))
|
||||
if rcvClosed && !segEnd.LessThanEq(r.RcvNxt) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// handleRcvdSegment handles TCP segments directed at the connection managed by
|
||||
// r as they arrive. It is called by the protocol main loop.
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err tcpip.Error) {
|
||||
state := r.ep.EndpointState()
|
||||
closed := r.ep.closed
|
||||
|
||||
segLen := seqnum.Size(s.payloadSize())
|
||||
segSeq := s.sequenceNumber
|
||||
|
||||
// If the sequence number range is outside the acceptable range, just
|
||||
// send an ACK and stop further processing of the segment.
|
||||
// This is according to RFC 793, page 68.
|
||||
if !r.acceptable(segSeq, segLen) {
|
||||
r.ep.snd.maybeSendOutOfWindowAck(s)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if state != StateEstablished {
|
||||
drop, err := r.handleRcvdSegmentClosing(s, state, closed)
|
||||
if drop || err != nil {
|
||||
return drop, err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the time of the last ack.
|
||||
r.lastRcvdAckTime = r.ep.stack.Clock().NowMonotonic()
|
||||
|
||||
// Defer segment processing if it can't be consumed now.
|
||||
if !r.consumeSegment(s, segSeq, segLen) {
|
||||
if segLen > 0 || s.flags.Contains(header.TCPFlagFin) {
|
||||
// We only store the segment if it's within our buffer
|
||||
// size limit.
|
||||
//
|
||||
// Only use 75% of the receive buffer queue for
|
||||
// out-of-order segments. This ensures that we always
|
||||
// leave some space for the inorder segments to arrive
|
||||
// allowing pending segments to be processed and
|
||||
// delivered to the user.
|
||||
//
|
||||
// The ratio must be at least 50% (the size of rwnd) to
|
||||
// leave space for retransmitted dropped packets. 51%
|
||||
// would make recovery slow when there are multiple
|
||||
// drops by necessitating multiple round trips. 100%
|
||||
// would enable the buffer to be totally full of
|
||||
// out-of-order data and stall the connection.
|
||||
//
|
||||
// An ideal solution is to ensure that there are at
|
||||
// least N bytes free when N bytes are missing, but we
|
||||
// don't have that computed at this point in the stack.
|
||||
if rcvBufSize := r.ep.ops.GetReceiveBufferSize(); rcvBufSize > 0 && (r.PendingBufUsed+int(segLen)) < int(rcvBufSize-rcvBufSize/4) {
|
||||
r.ep.rcvQueueMu.Lock()
|
||||
r.PendingBufUsed += s.segMemSize()
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
s.IncRef()
|
||||
heap.Push(&r.pendingRcvdSegments, s)
|
||||
UpdateSACKBlocks(&r.ep.sack, segSeq, segSeq.Add(segLen), r.RcvNxt)
|
||||
}
|
||||
|
||||
// Immediately send an ack so that the peer knows it may
|
||||
// have to retransmit.
|
||||
r.ep.snd.sendAck()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Since we consumed a segment update the receiver's RTT estimate
|
||||
// if required.
|
||||
if segLen > 0 {
|
||||
r.updateRTT()
|
||||
}
|
||||
|
||||
// By consuming the current segment, we may have filled a gap in the
|
||||
// sequence number domain that allows pending segments to be consumed
|
||||
// now. So try to do it.
|
||||
for !r.closed && r.pendingRcvdSegments.Len() > 0 {
|
||||
s := r.pendingRcvdSegments[0]
|
||||
segLen := seqnum.Size(s.payloadSize())
|
||||
segSeq := s.sequenceNumber
|
||||
|
||||
// Skip segment altogether if it has already been acknowledged.
|
||||
if !segSeq.Add(segLen-1).LessThan(r.RcvNxt) &&
|
||||
!r.consumeSegment(s, segSeq, segLen) {
|
||||
break
|
||||
}
|
||||
|
||||
heap.Pop(&r.pendingRcvdSegments)
|
||||
r.ep.rcvQueueMu.Lock()
|
||||
r.PendingBufUsed -= s.segMemSize()
|
||||
r.ep.rcvQueueMu.Unlock()
|
||||
s.DecRef()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// handleTimeWaitSegment handles inbound segments received when the endpoint
|
||||
// has entered the TIME_WAIT state.
|
||||
// +checklocks:r.ep.mu
|
||||
// +checklocksalias:r.ep.snd.ep.mu=r.ep.mu
|
||||
func (r *receiver) handleTimeWaitSegment(s *segment) (resetTimeWait bool, newSyn bool) {
|
||||
segSeq := s.sequenceNumber
|
||||
segLen := seqnum.Size(s.payloadSize())
|
||||
|
||||
// Just silently drop any RST packets in TIME_WAIT. We do not support
|
||||
// TIME_WAIT assassination as a result we confirm w/ fix 1 as described
|
||||
// in https://tools.ietf.org/html/rfc1337#section-3.
|
||||
//
|
||||
// This behavior overrides RFC793 page 70 where we transition to CLOSED
|
||||
// on receiving RST, which is also default Linux behavior.
|
||||
// On Linux the RST can be ignored by setting sysctl net.ipv4.tcp_rfc1337.
|
||||
//
|
||||
// As we do not yet support PAWS, we are being conservative in ignoring
|
||||
// RSTs by default.
|
||||
if s.flags.Contains(header.TCPFlagRst) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// If it's a SYN and the sequence number is higher than any seen before
|
||||
// for this connection then try and redirect it to a listening endpoint
|
||||
// if available.
|
||||
//
|
||||
// RFC 1122:
|
||||
// "When a connection is [...] on TIME-WAIT state [...]
|
||||
// [a TCP] MAY accept a new SYN from the remote TCP to
|
||||
// reopen the connection directly, if it:
|
||||
|
||||
// (1) assigns its initial sequence number for the new
|
||||
// connection to be larger than the largest sequence
|
||||
// number it used on the previous connection incarnation,
|
||||
// and
|
||||
|
||||
// (2) returns to TIME-WAIT state if the SYN turns out
|
||||
// to be an old duplicate".
|
||||
if s.flags.Contains(header.TCPFlagSyn) && r.RcvNxt.LessThan(segSeq) {
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Drop the segment if it does not contain an ACK.
|
||||
if !s.flags.Contains(header.TCPFlagAck) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Update Timestamp if required. See RFC7323, section-4.3.
|
||||
if r.ep.SendTSOk && s.parsedOptions.TS {
|
||||
r.ep.updateRecentTimestamp(s.parsedOptions.TSVal, r.ep.snd.MaxSentAck, segSeq)
|
||||
}
|
||||
|
||||
if segSeq.Add(1) == r.RcvNxt && s.flags.Contains(header.TCPFlagFin) {
|
||||
// If it's a FIN-ACK then resetTimeWait and send an ACK, as it
|
||||
// indicates our final ACK could have been lost.
|
||||
r.ep.snd.sendAck()
|
||||
return true, false
|
||||
}
|
||||
|
||||
// If the sequence number range is outside the acceptable range or
|
||||
// carries data then just send an ACK. This is according to RFC 793,
|
||||
// page 37.
|
||||
//
|
||||
// NOTE: In TIME_WAIT the only acceptable sequence number is RcvNxt.
|
||||
if segSeq != r.RcvNxt || segLen != 0 {
|
||||
r.ep.snd.sendAck()
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/rcv_queue_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/rcv_queue_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type rcvQueueMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var rcvQueueprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var rcvQueuelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type rcvQueuelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvQueueMutex) Lock() {
|
||||
locking.AddGLock(rcvQueueprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvQueueMutex) NestedLock(i rcvQueuelockNameIndex) {
|
||||
locking.AddGLock(rcvQueueprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvQueueMutex) Unlock() {
|
||||
locking.DelGLock(rcvQueueprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvQueueMutex) NestedUnlock(i rcvQueuelockNameIndex) {
|
||||
locking.DelGLock(rcvQueueprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func rcvQueueinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
rcvQueueinitLockNames()
|
||||
rcvQueueprefixIndex = locking.NewMutexClass(reflect.TypeOf(rcvQueueMutex{}), rcvQueuelockNames)
|
||||
}
|
||||
118
pkg/tcpip/transport/tcp/reno.go
Normal file
118
pkg/tcpip/transport/tcp/reno.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// renoState stores the variables related to TCP New Reno congestion
|
||||
// control algorithm.
|
||||
//
|
||||
// +stateify savable
|
||||
type renoState struct {
|
||||
s *sender
|
||||
}
|
||||
|
||||
// newRenoCC initializes the state for the NewReno congestion control algorithm.
|
||||
func newRenoCC(s *sender) *renoState {
|
||||
return &renoState{s: s}
|
||||
}
|
||||
|
||||
// updateSlowStart will update the congestion window as per the slow-start
|
||||
// algorithm used by NewReno. If after adjusting the congestion window
|
||||
// we cross the SSthreshold then it will return the number of packets that
|
||||
// must be consumed in congestion avoidance mode.
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) updateSlowStart(packetsAcked int) int {
|
||||
// Don't let the congestion window cross into the congestion
|
||||
// avoidance range.
|
||||
newcwnd := r.s.SndCwnd + packetsAcked
|
||||
if newcwnd >= r.s.Ssthresh {
|
||||
newcwnd = r.s.Ssthresh
|
||||
r.s.SndCAAckCount = 0
|
||||
}
|
||||
|
||||
packetsAcked -= newcwnd - r.s.SndCwnd
|
||||
r.s.SndCwnd = newcwnd
|
||||
return packetsAcked
|
||||
}
|
||||
|
||||
// updateCongestionAvoidance will update congestion window in congestion
|
||||
// avoidance mode as described in RFC5681 section 3.1
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) updateCongestionAvoidance(packetsAcked int) {
|
||||
// Consume the packets in congestion avoidance mode.
|
||||
r.s.SndCAAckCount += packetsAcked
|
||||
if r.s.SndCAAckCount >= r.s.SndCwnd {
|
||||
r.s.SndCwnd += r.s.SndCAAckCount / r.s.SndCwnd
|
||||
r.s.SndCAAckCount = r.s.SndCAAckCount % r.s.SndCwnd
|
||||
}
|
||||
}
|
||||
|
||||
// reduceSlowStartThreshold reduces the slow-start threshold per RFC 5681,
|
||||
// page 6, eq. 4. It is called when we detect congestion in the network.
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) reduceSlowStartThreshold() {
|
||||
r.s.Ssthresh = r.s.Outstanding / 2
|
||||
if r.s.Ssthresh < 2 {
|
||||
r.s.Ssthresh = 2
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates the congestion state based on the number of packets that
|
||||
// were acknowledged.
|
||||
// Update implements congestionControl.Update.
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) Update(packetsAcked int, _ time.Duration) {
|
||||
if r.s.SndCwnd < r.s.Ssthresh {
|
||||
packetsAcked = r.updateSlowStart(packetsAcked)
|
||||
if packetsAcked == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.updateCongestionAvoidance(packetsAcked)
|
||||
}
|
||||
|
||||
// HandleLossDetected implements congestionControl.HandleLossDetected.
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) HandleLossDetected() {
|
||||
// A retransmit was triggered due to nDupAckThreshold or when RACK
|
||||
// detected loss. Reduce our slow start threshold.
|
||||
r.reduceSlowStartThreshold()
|
||||
}
|
||||
|
||||
// HandleRTOExpired implements congestionControl.HandleRTOExpired.
|
||||
//
|
||||
// +checklocks:r.s.ep.mu
|
||||
func (r *renoState) HandleRTOExpired() {
|
||||
// We lost a packet, so reduce ssthresh.
|
||||
r.reduceSlowStartThreshold()
|
||||
|
||||
// Reduce the congestion window to 1, i.e., enter slow-start. Per
|
||||
// RFC 5681, page 7, we must use 1 regardless of the value of the
|
||||
// initial congestion window.
|
||||
r.s.SndCwnd = 1
|
||||
}
|
||||
|
||||
// PostRecovery implements congestionControl.PostRecovery.
|
||||
func (r *renoState) PostRecovery() {
|
||||
// noop.
|
||||
}
|
||||
68
pkg/tcpip/transport/tcp/reno_recovery.go
Normal file
68
pkg/tcpip/transport/tcp/reno_recovery.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// 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 tcp
|
||||
|
||||
// renoRecovery stores the variables related to TCP Reno loss recovery
|
||||
// algorithm.
|
||||
//
|
||||
// +stateify savable
|
||||
type renoRecovery struct {
|
||||
s *sender
|
||||
}
|
||||
|
||||
func newRenoRecovery(s *sender) *renoRecovery {
|
||||
return &renoRecovery{s: s}
|
||||
}
|
||||
|
||||
// +checklocks:rr.s.ep.mu
|
||||
func (rr *renoRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) {
|
||||
ack := rcvdSeg.ackNumber
|
||||
snd := rr.s
|
||||
|
||||
// We are in fast recovery mode. Ignore the ack if it's out of range.
|
||||
if !ack.InRange(snd.SndUna, snd.SndNxt+1) {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't count this as a duplicate if it is carrying data or
|
||||
// updating the window.
|
||||
if rcvdSeg.logicalLen() != 0 || snd.SndWnd != rcvdSeg.window {
|
||||
return
|
||||
}
|
||||
|
||||
// Inflate the congestion window if we're getting duplicate acks
|
||||
// for the packet we retransmitted.
|
||||
if !fastRetransmit && ack == snd.FastRecovery.First {
|
||||
// We received a dup, inflate the congestion window by 1 packet
|
||||
// if we're not at the max yet. Only inflate the window if
|
||||
// regular FastRecovery is in use, RFC6675 does not require
|
||||
// inflating cwnd on duplicate ACKs.
|
||||
if snd.SndCwnd < snd.FastRecovery.MaxCwnd {
|
||||
snd.SndCwnd++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// A partial ack was received. Retransmit this packet and remember it
|
||||
// so that we don't retransmit it again.
|
||||
//
|
||||
// We don't inflate the window because we're putting the same packet
|
||||
// back onto the wire.
|
||||
//
|
||||
// N.B. The retransmit timer will be reset by the caller.
|
||||
snd.FastRecovery.First = ack
|
||||
snd.DupAckCount = 0
|
||||
snd.resendSegment()
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/rtt_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/rtt_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type rttMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var rttprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var rttlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type rttlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *rttMutex) Lock() {
|
||||
locking.AddGLock(rttprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rttMutex) NestedLock(i rttlockNameIndex) {
|
||||
locking.AddGLock(rttprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *rttMutex) Unlock() {
|
||||
locking.DelGLock(rttprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rttMutex) NestedUnlock(i rttlockNameIndex) {
|
||||
locking.DelGLock(rttprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func rttinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
rttinitLockNames()
|
||||
rttprefixIndex = locking.NewMutexClass(reflect.TypeOf(rttMutex{}), rttlockNames)
|
||||
}
|
||||
105
pkg/tcpip/transport/tcp/sack.go
Normal file
105
pkg/tcpip/transport/tcp/sack.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxSACKBlocks is the maximum number of SACK blocks stored
|
||||
// at receiver side.
|
||||
MaxSACKBlocks = 6
|
||||
)
|
||||
|
||||
// UpdateSACKBlocks updates the list of SACK blocks to include the segment
|
||||
// specified by segStart->segEnd. If the segment happens to be an out of order
|
||||
// delivery then the first block in the sack.blocks always includes the
|
||||
// segment identified by segStart->segEnd.
|
||||
func UpdateSACKBlocks(sack *SACKInfo, segStart seqnum.Value, segEnd seqnum.Value, rcvNxt seqnum.Value) {
|
||||
newSB := header.SACKBlock{Start: segStart, End: segEnd}
|
||||
|
||||
// Ignore any invalid SACK blocks or blocks that are before rcvNxt as
|
||||
// those bytes have already been acked.
|
||||
if newSB.End.LessThanEq(newSB.Start) || newSB.End.LessThan(rcvNxt) {
|
||||
return
|
||||
}
|
||||
|
||||
if sack.NumBlocks == 0 {
|
||||
sack.Blocks[0] = newSB
|
||||
sack.NumBlocks = 1
|
||||
return
|
||||
}
|
||||
n := 0
|
||||
for i := 0; i < sack.NumBlocks; i++ {
|
||||
start, end := sack.Blocks[i].Start, sack.Blocks[i].End
|
||||
if end.LessThanEq(rcvNxt) {
|
||||
// Discard any sack blocks that are before rcvNxt as
|
||||
// those have already been acked.
|
||||
continue
|
||||
}
|
||||
if newSB.Start.LessThanEq(end) && start.LessThanEq(newSB.End) {
|
||||
// Merge this SACK block into newSB and discard this SACK
|
||||
// block.
|
||||
if start.LessThan(newSB.Start) {
|
||||
newSB.Start = start
|
||||
}
|
||||
if newSB.End.LessThan(end) {
|
||||
newSB.End = end
|
||||
}
|
||||
} else {
|
||||
// Save this block.
|
||||
sack.Blocks[n] = sack.Blocks[i]
|
||||
n++
|
||||
}
|
||||
}
|
||||
if rcvNxt.LessThan(newSB.Start) {
|
||||
// If this was an out of order segment then make sure that the
|
||||
// first SACK block is the one that includes the segment.
|
||||
//
|
||||
// See the first bullet point in
|
||||
// https://tools.ietf.org/html/rfc2018#section-4
|
||||
if n == MaxSACKBlocks {
|
||||
// If the number of SACK blocks is equal to
|
||||
// MaxSACKBlocks then discard the last SACK block.
|
||||
n--
|
||||
}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
sack.Blocks[i+1] = sack.Blocks[i]
|
||||
}
|
||||
sack.Blocks[0] = newSB
|
||||
n++
|
||||
}
|
||||
sack.NumBlocks = n
|
||||
}
|
||||
|
||||
// TrimSACKBlockList updates the sack block list by removing/modifying any block
|
||||
// where start is < rcvNxt.
|
||||
func TrimSACKBlockList(sack *SACKInfo, rcvNxt seqnum.Value) {
|
||||
n := 0
|
||||
for i := 0; i < sack.NumBlocks; i++ {
|
||||
if sack.Blocks[i].End.LessThanEq(rcvNxt) {
|
||||
continue
|
||||
}
|
||||
if sack.Blocks[i].Start.LessThan(rcvNxt) {
|
||||
// Shrink this SACK block.
|
||||
sack.Blocks[i].Start = rcvNxt
|
||||
}
|
||||
sack.Blocks[n] = sack.Blocks[i]
|
||||
n++
|
||||
}
|
||||
sack.NumBlocks = n
|
||||
}
|
||||
122
pkg/tcpip/transport/tcp/sack_recovery.go
Normal file
122
pkg/tcpip/transport/tcp/sack_recovery.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// 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 tcp
|
||||
|
||||
import "github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
|
||||
// sackRecovery stores the variables related to TCP SACK loss recovery
|
||||
// algorithm.
|
||||
//
|
||||
// +stateify savable
|
||||
type sackRecovery struct {
|
||||
s *sender
|
||||
}
|
||||
|
||||
func newSACKRecovery(s *sender) *sackRecovery {
|
||||
return &sackRecovery{s: s}
|
||||
}
|
||||
|
||||
// handleSACKRecovery implements the loss recovery phase as described in RFC6675
|
||||
// section 5, step C.
|
||||
// +checklocks:sr.s.ep.mu
|
||||
func (sr *sackRecovery) handleSACKRecovery(limit int, end seqnum.Value) (dataSent bool) {
|
||||
snd := sr.s
|
||||
snd.SetPipe()
|
||||
|
||||
if smss := int(snd.ep.scoreboard.SMSS()); limit > smss {
|
||||
// Cap segment size limit to s.smss as SACK recovery requires
|
||||
// that all retransmissions or new segments send during recovery
|
||||
// be of <= SMSS.
|
||||
limit = smss
|
||||
}
|
||||
|
||||
nextSegHint := snd.writeList.Front()
|
||||
for snd.Outstanding < snd.SndCwnd {
|
||||
var nextSeg *segment
|
||||
var rescueRtx bool
|
||||
nextSeg, nextSegHint, rescueRtx = snd.NextSeg(nextSegHint)
|
||||
if nextSeg == nil {
|
||||
return dataSent
|
||||
}
|
||||
if !snd.isAssignedSequenceNumber(nextSeg) || snd.SndNxt.LessThanEq(nextSeg.sequenceNumber) {
|
||||
// New data being sent.
|
||||
|
||||
// Step C.3 described below is handled by
|
||||
// maybeSendSegment which increments sndNxt when
|
||||
// a segment is transmitted.
|
||||
//
|
||||
// Step C.3 "If any of the data octets sent in
|
||||
// (C.1) are above HighData, HighData must be
|
||||
// updated to reflect the transmission of
|
||||
// previously unsent data."
|
||||
//
|
||||
// We pass s.smss as the limit as the Step 2) requires that
|
||||
// new data sent should be of size s.smss or less.
|
||||
if sent := snd.maybeSendSegment(nextSeg, limit, end); !sent {
|
||||
return dataSent
|
||||
}
|
||||
dataSent = true
|
||||
snd.Outstanding++
|
||||
snd.updateWriteNext(nextSeg.Next())
|
||||
continue
|
||||
}
|
||||
|
||||
// Now handle the retransmission case where we matched either step 1,3 or 4
|
||||
// of the NextSeg algorithm.
|
||||
// RFC 6675, Step C.4.
|
||||
//
|
||||
// "The estimate of the amount of data outstanding in the network
|
||||
// must be updated by incrementing pipe by the number of octets
|
||||
// transmitted in (C.1)."
|
||||
snd.Outstanding++
|
||||
dataSent = true
|
||||
snd.sendSegment(nextSeg)
|
||||
|
||||
segEnd := nextSeg.sequenceNumber.Add(nextSeg.logicalLen())
|
||||
if rescueRtx {
|
||||
// We do the last part of rule (4) of NextSeg here to update
|
||||
// RescueRxt as until this point we don't know if we are going
|
||||
// to use the rescue transmission.
|
||||
snd.FastRecovery.RescueRxt = snd.FastRecovery.Last
|
||||
} else {
|
||||
// RFC 6675, Step C.2
|
||||
//
|
||||
// "If any of the data octets sent in (C.1) are below
|
||||
// HighData, HighRxt MUST be set to the highest sequence
|
||||
// number of the retransmitted segment unless NextSeg ()
|
||||
// rule (4) was invoked for this retransmission."
|
||||
snd.FastRecovery.HighRxt = segEnd - 1
|
||||
}
|
||||
}
|
||||
return dataSent
|
||||
}
|
||||
|
||||
// +checklocks:sr.s.ep.mu
|
||||
func (sr *sackRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) {
|
||||
snd := sr.s
|
||||
if fastRetransmit {
|
||||
snd.resendSegment()
|
||||
}
|
||||
|
||||
// We are in fast recovery mode. Ignore the ack if it's out of range.
|
||||
if ack := rcvdSeg.ackNumber; !ack.InRange(snd.SndUna, snd.SndNxt+1) {
|
||||
return
|
||||
}
|
||||
|
||||
// RFC 6675 recovery algorithm step C 1-5.
|
||||
end := snd.SndUna.Add(snd.SndWnd)
|
||||
dataSent := sr.handleSACKRecovery(snd.MaxPayloadSize, end)
|
||||
snd.postXmit(dataSent, true /* shouldScheduleProbe */)
|
||||
}
|
||||
306
pkg/tcpip/transport/tcp/sack_scoreboard.go
Normal file
306
pkg/tcpip/transport/tcp/sack_scoreboard.go
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/btree"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxSACKBlocks is the maximum number of distinct SACKBlocks the
|
||||
// scoreboard will track. Once there are 100 distinct blocks, new
|
||||
// insertions will fail.
|
||||
maxSACKBlocks = 100
|
||||
|
||||
// defaultBtreeDegree is set to 2 as btree.New(2) results in a 2-3-4
|
||||
// tree.
|
||||
defaultBtreeDegree = 2
|
||||
)
|
||||
|
||||
// SACKScoreboard stores a set of disjoint SACK ranges.
|
||||
//
|
||||
// +stateify savable
|
||||
type SACKScoreboard struct {
|
||||
// smss is defined in RFC5681 as following:
|
||||
//
|
||||
// The SMSS is the size of the largest segment that the sender can
|
||||
// transmit. This value can be based on the maximum transmission unit
|
||||
// of the network, the path MTU discovery [RFC1191, RFC4821] algorithm,
|
||||
// RMSS (see next item), or other factors. The size does not include
|
||||
// the TCP/IP headers and options.
|
||||
smss uint16
|
||||
maxSACKED seqnum.Value
|
||||
sacked seqnum.Size `state:"nosave"`
|
||||
ranges *btree.BTree `state:"nosave"`
|
||||
}
|
||||
|
||||
// NewSACKScoreboard returns a new SACK Scoreboard.
|
||||
func NewSACKScoreboard(smss uint16, iss seqnum.Value) *SACKScoreboard {
|
||||
return &SACKScoreboard{
|
||||
smss: smss,
|
||||
ranges: btree.New(defaultBtreeDegree),
|
||||
maxSACKED: iss,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset erases all known range information from the SACK scoreboard.
|
||||
func (s *SACKScoreboard) Reset() {
|
||||
s.ranges = btree.New(defaultBtreeDegree)
|
||||
s.sacked = 0
|
||||
}
|
||||
|
||||
// Insert inserts/merges the provided SACKBlock into the scoreboard.
|
||||
func (s *SACKScoreboard) Insert(r header.SACKBlock) {
|
||||
if s.ranges.Len() >= maxSACKBlocks {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if we can merge the new range with a range before or after it.
|
||||
var toDelete []btree.Item
|
||||
if s.maxSACKED.LessThan(r.End - 1) {
|
||||
s.maxSACKED = r.End - 1
|
||||
}
|
||||
s.ranges.AscendGreaterOrEqual(r, func(i btree.Item) bool {
|
||||
if i == r {
|
||||
return true
|
||||
}
|
||||
sacked := i.(header.SACKBlock)
|
||||
// There is a hole between these two SACK blocks, so we can't
|
||||
// merge anymore.
|
||||
if r.End.LessThan(sacked.Start) {
|
||||
return false
|
||||
}
|
||||
// There is some overlap at this point, merge the blocks and
|
||||
// delete the other one.
|
||||
//
|
||||
// ----sS--------sE
|
||||
// r.S---------------rE
|
||||
// -------sE
|
||||
if sacked.End.LessThan(r.End) {
|
||||
// sacked is contained in the newly inserted range.
|
||||
// Delete this block.
|
||||
toDelete = append(toDelete, i)
|
||||
return true
|
||||
}
|
||||
// sacked covers a range past end of the newly inserted
|
||||
// block.
|
||||
r.End = sacked.End
|
||||
toDelete = append(toDelete, i)
|
||||
return true
|
||||
})
|
||||
|
||||
s.ranges.DescendLessOrEqual(r, func(i btree.Item) bool {
|
||||
if i == r {
|
||||
return true
|
||||
}
|
||||
sacked := i.(header.SACKBlock)
|
||||
// sA------sE
|
||||
// rA----rE
|
||||
if sacked.End.LessThan(r.Start) {
|
||||
return false
|
||||
}
|
||||
// The previous range extends into the current block. Merge it
|
||||
// into the newly inserted range and delete the other one.
|
||||
//
|
||||
// <-rA---rE----<---rE--->
|
||||
// sA--------------sE
|
||||
r.Start = sacked.Start
|
||||
// Extend r to cover sacked if sacked extends past r.
|
||||
if r.End.LessThan(sacked.End) {
|
||||
r.End = sacked.End
|
||||
}
|
||||
toDelete = append(toDelete, i)
|
||||
return true
|
||||
})
|
||||
for _, i := range toDelete {
|
||||
if sb := s.ranges.Delete(i); sb != nil {
|
||||
sb := i.(header.SACKBlock)
|
||||
s.sacked -= sb.Start.Size(sb.End)
|
||||
}
|
||||
}
|
||||
|
||||
replaced := s.ranges.ReplaceOrInsert(r)
|
||||
if replaced == nil {
|
||||
s.sacked += r.Start.Size(r.End)
|
||||
}
|
||||
}
|
||||
|
||||
// IsSACKED returns true if the a given range of sequence numbers denoted by r
|
||||
// are already covered by SACK information in the scoreboard.
|
||||
func (s *SACKScoreboard) IsSACKED(r header.SACKBlock) bool {
|
||||
if s.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
found := false
|
||||
s.ranges.DescendLessOrEqual(r, func(i btree.Item) bool {
|
||||
sacked := i.(header.SACKBlock)
|
||||
if sacked.End.LessThan(r.Start) {
|
||||
return false
|
||||
}
|
||||
if sacked.Contains(r) {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
// String returns human-readable state of the scoreboard structure.
|
||||
func (s *SACKScoreboard) String() string {
|
||||
var str strings.Builder
|
||||
str.WriteString("SACKScoreboard: {")
|
||||
s.ranges.Ascend(func(i btree.Item) bool {
|
||||
str.WriteString(fmt.Sprintf("%v,", i))
|
||||
return true
|
||||
})
|
||||
str.WriteString("}\n")
|
||||
return str.String()
|
||||
}
|
||||
|
||||
// Delete removes all SACK information prior to seq.
|
||||
func (s *SACKScoreboard) Delete(seq seqnum.Value) {
|
||||
if s.Empty() {
|
||||
return
|
||||
}
|
||||
toDelete := []btree.Item{}
|
||||
toInsert := []btree.Item{}
|
||||
r := header.SACKBlock{seq, seq.Add(1)}
|
||||
s.ranges.DescendLessOrEqual(r, func(i btree.Item) bool {
|
||||
if i == r {
|
||||
return true
|
||||
}
|
||||
sb := i.(header.SACKBlock)
|
||||
toDelete = append(toDelete, i)
|
||||
if sb.End.LessThanEq(seq) {
|
||||
s.sacked -= sb.Start.Size(sb.End)
|
||||
} else {
|
||||
newSB := header.SACKBlock{seq, sb.End}
|
||||
toInsert = append(toInsert, newSB)
|
||||
s.sacked -= sb.Start.Size(seq)
|
||||
}
|
||||
return true
|
||||
})
|
||||
for _, sb := range toDelete {
|
||||
s.ranges.Delete(sb)
|
||||
}
|
||||
for _, sb := range toInsert {
|
||||
s.ranges.ReplaceOrInsert(sb)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy provides a copy of the SACK scoreboard.
|
||||
func (s *SACKScoreboard) Copy() (sackBlocks []header.SACKBlock, maxSACKED seqnum.Value) {
|
||||
s.ranges.Ascend(func(i btree.Item) bool {
|
||||
sackBlocks = append(sackBlocks, i.(header.SACKBlock))
|
||||
return true
|
||||
})
|
||||
return sackBlocks, s.maxSACKED
|
||||
}
|
||||
|
||||
// IsRangeLost implements the IsLost(SeqNum) operation defined in RFC 6675
|
||||
// section 4 but operates on a range of sequence numbers and returns true if
|
||||
// there are at least nDupAckThreshold SACK blocks greater than the range being
|
||||
// checked or if at least (nDupAckThreshold-1)*s.smss bytes have been SACKED
|
||||
// with sequence numbers greater than the block being checked.
|
||||
func (s *SACKScoreboard) IsRangeLost(r header.SACKBlock) bool {
|
||||
if s.Empty() {
|
||||
return false
|
||||
}
|
||||
nDupSACK := 0
|
||||
nDupSACKBytes := seqnum.Size(0)
|
||||
isLost := false
|
||||
|
||||
// We need to check if the immediate lower (if any) sacked
|
||||
// range contains or partially overlaps with r.
|
||||
searchMore := true
|
||||
s.ranges.DescendLessOrEqual(r, func(i btree.Item) bool {
|
||||
sacked := i.(header.SACKBlock)
|
||||
if sacked.Contains(r) {
|
||||
searchMore = false
|
||||
return false
|
||||
}
|
||||
if sacked.End.LessThanEq(r.Start) {
|
||||
// all sequence numbers covered by sacked are below
|
||||
// r so we continue searching.
|
||||
return false
|
||||
}
|
||||
// There is a partial overlap. In this case we r.Start is
|
||||
// between sacked.Start & sacked.End and r.End extends beyond
|
||||
// sacked.End.
|
||||
// Move r.Start to sacked.End and continuing searching blocks
|
||||
// above r.Start.
|
||||
r.Start = sacked.End
|
||||
return false
|
||||
})
|
||||
|
||||
if !searchMore {
|
||||
return isLost
|
||||
}
|
||||
|
||||
s.ranges.AscendGreaterOrEqual(r, func(i btree.Item) bool {
|
||||
sacked := i.(header.SACKBlock)
|
||||
if sacked.Contains(r) {
|
||||
return false
|
||||
}
|
||||
nDupSACKBytes += sacked.Start.Size(sacked.End)
|
||||
nDupSACK++
|
||||
if nDupSACK >= nDupAckThreshold || nDupSACKBytes >= seqnum.Size((nDupAckThreshold-1)*s.smss) {
|
||||
isLost = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return isLost
|
||||
}
|
||||
|
||||
// IsLost implements the IsLost(SeqNum) operation defined in RFC3517 section
|
||||
// 4.
|
||||
//
|
||||
// This routine returns whether the given sequence number is considered to be
|
||||
// lost. The routine returns true when either nDupAckThreshold discontiguous
|
||||
// SACKed sequences have arrived above 'SeqNum' or (nDupAckThreshold * SMSS)
|
||||
// bytes with sequence numbers greater than 'SeqNum' have been SACKed.
|
||||
// Otherwise, the routine returns false.
|
||||
func (s *SACKScoreboard) IsLost(seq seqnum.Value) bool {
|
||||
return s.IsRangeLost(header.SACKBlock{seq, seq.Add(1)})
|
||||
}
|
||||
|
||||
// Empty returns true if the SACK scoreboard has no entries, false otherwise.
|
||||
func (s *SACKScoreboard) Empty() bool {
|
||||
return s.ranges.Len() == 0
|
||||
}
|
||||
|
||||
// Sacked returns the current number of bytes held in the SACK scoreboard.
|
||||
func (s *SACKScoreboard) Sacked() seqnum.Size {
|
||||
return s.sacked
|
||||
}
|
||||
|
||||
// MaxSACKED returns the highest sequence number ever inserted in the SACK
|
||||
// scoreboard.
|
||||
func (s *SACKScoreboard) MaxSACKED() seqnum.Value {
|
||||
return s.maxSACKED
|
||||
}
|
||||
|
||||
// SMSS returns the sender's MSS as held by the SACK scoreboard.
|
||||
func (s *SACKScoreboard) SMSS() uint16 {
|
||||
return s.smss
|
||||
}
|
||||
251
pkg/tcpip/transport/tcp/segment.go
Normal file
251
pkg/tcpip/transport/tcp/segment.go
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// queueFlags are used to indicate which queue of an endpoint a particular segment
|
||||
// belongs to. This is used to track memory accounting correctly.
|
||||
type queueFlags uint8
|
||||
|
||||
const (
|
||||
// SegOverheadSize is the size of an empty seg in memory including packet
|
||||
// buffer overhead. It is advised to use SegOverheadSize instead of segSize
|
||||
// in all cases where accounting for segment memory overhead is important.
|
||||
SegOverheadSize = segSize + stack.PacketBufferStructSize + header.IPv4MaximumHeaderSize
|
||||
|
||||
recvQ queueFlags = 1 << iota
|
||||
sendQ
|
||||
)
|
||||
|
||||
var segmentPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &segment{}
|
||||
},
|
||||
}
|
||||
|
||||
// segment represents a TCP segment. It holds the payload and parsed TCP segment
|
||||
// information, and can be added to intrusive lists.
|
||||
// segment is mostly immutable, the only field allowed to change is data.
|
||||
//
|
||||
// +stateify savable
|
||||
type segment struct {
|
||||
segmentEntry
|
||||
segmentRefs
|
||||
|
||||
ep *Endpoint
|
||||
qFlags queueFlags
|
||||
id stack.TransportEndpointID `state:"manual"`
|
||||
|
||||
pkt *stack.PacketBuffer
|
||||
|
||||
sequenceNumber seqnum.Value
|
||||
ackNumber seqnum.Value
|
||||
flags header.TCPFlags
|
||||
window seqnum.Size
|
||||
// csum is only populated for received segments.
|
||||
csum uint16
|
||||
// csumValid is true if the csum in the received segment is valid.
|
||||
csumValid bool
|
||||
|
||||
// parsedOptions stores the parsed values from the options in the segment.
|
||||
parsedOptions header.TCPOptions
|
||||
options []byte `state:".([]byte)"`
|
||||
hasNewSACKInfo bool
|
||||
rcvdTime tcpip.MonotonicTime
|
||||
// xmitTime is the last transmit time of this segment.
|
||||
xmitTime tcpip.MonotonicTime
|
||||
xmitCount uint32
|
||||
|
||||
// acked indicates if the segment has already been SACKed.
|
||||
acked bool
|
||||
|
||||
// dataMemSize is the memory used by pkt initially. The value is used for
|
||||
// memory accounting in the receive buffer instead of pkt.MemSize() because
|
||||
// packet contents can be modified, so relying on the computed memory size
|
||||
// to "free" reserved bytes could leak memory in the receiver.
|
||||
dataMemSize int
|
||||
|
||||
// lost indicates if the segment is marked as lost by RACK.
|
||||
lost bool
|
||||
}
|
||||
|
||||
func newIncomingSegment(id stack.TransportEndpointID, clock tcpip.Clock, pkt *stack.PacketBuffer) (*segment, error) {
|
||||
hdr := header.TCP(pkt.TransportHeader().Slice())
|
||||
var srcAddr tcpip.Address
|
||||
var dstAddr tcpip.Address
|
||||
switch netProto := pkt.NetworkProtocolNumber; netProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
hdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
srcAddr = hdr.SourceAddress()
|
||||
dstAddr = hdr.DestinationAddress()
|
||||
case header.IPv6ProtocolNumber:
|
||||
hdr := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
srcAddr = hdr.SourceAddress()
|
||||
dstAddr = hdr.DestinationAddress()
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown network protocol number %d", netProto))
|
||||
}
|
||||
|
||||
csum, csumValid, ok := header.TCPValid(
|
||||
hdr,
|
||||
func() uint16 { return pkt.Data().Checksum() },
|
||||
uint16(pkt.Data().Size()),
|
||||
srcAddr,
|
||||
dstAddr,
|
||||
pkt.RXChecksumValidated)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("header data offset does not respect size constraints: %d < offset < %d, got offset=%d", header.TCPMinimumSize, len(hdr), hdr.DataOffset())
|
||||
}
|
||||
|
||||
s := newSegment()
|
||||
s.id = id
|
||||
s.options = hdr[header.TCPMinimumSize:]
|
||||
s.parsedOptions = header.ParseTCPOptions(hdr[header.TCPMinimumSize:])
|
||||
s.sequenceNumber = seqnum.Value(hdr.SequenceNumber())
|
||||
s.ackNumber = seqnum.Value(hdr.AckNumber())
|
||||
s.flags = hdr.Flags()
|
||||
s.window = seqnum.Size(hdr.WindowSize())
|
||||
s.rcvdTime = clock.NowMonotonic()
|
||||
s.dataMemSize = pkt.MemSize()
|
||||
s.pkt = pkt.Clone()
|
||||
s.csumValid = csumValid
|
||||
|
||||
if !s.pkt.RXChecksumValidated {
|
||||
s.csum = csum
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func newOutgoingSegment(id stack.TransportEndpointID, clock tcpip.Clock, buf buffer.Buffer) *segment {
|
||||
s := newSegment()
|
||||
s.id = id
|
||||
s.rcvdTime = clock.NowMonotonic()
|
||||
s.pkt = stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: buf})
|
||||
s.dataMemSize = s.pkt.MemSize()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *segment) clone() *segment {
|
||||
t := newSegment()
|
||||
t.id = s.id
|
||||
t.sequenceNumber = s.sequenceNumber
|
||||
t.ackNumber = s.ackNumber
|
||||
t.flags = s.flags
|
||||
t.window = s.window
|
||||
t.rcvdTime = s.rcvdTime
|
||||
t.xmitTime = s.xmitTime
|
||||
t.xmitCount = s.xmitCount
|
||||
t.ep = s.ep
|
||||
t.qFlags = s.qFlags
|
||||
t.dataMemSize = s.dataMemSize
|
||||
t.pkt = s.pkt.Clone()
|
||||
return t
|
||||
}
|
||||
|
||||
func newSegment() *segment {
|
||||
s := segmentPool.Get().(*segment)
|
||||
*s = segment{}
|
||||
s.InitRefs()
|
||||
return s
|
||||
}
|
||||
|
||||
// merge merges data in oth and clears oth.
|
||||
func (s *segment) merge(oth *segment) {
|
||||
s.pkt.Data().Merge(oth.pkt.Data())
|
||||
s.dataMemSize = s.pkt.MemSize()
|
||||
oth.dataMemSize = oth.pkt.MemSize()
|
||||
}
|
||||
|
||||
// setOwner sets the owning endpoint for this segment. Its required
|
||||
// to be called to ensure memory accounting for receive/send buffer
|
||||
// queues is done properly.
|
||||
func (s *segment) setOwner(ep *Endpoint, qFlags queueFlags) {
|
||||
switch qFlags {
|
||||
case recvQ:
|
||||
ep.updateReceiveMemUsed(s.segMemSize())
|
||||
case sendQ:
|
||||
// no memory account for sendQ yet.
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected queue flag %b", qFlags))
|
||||
}
|
||||
s.ep = ep
|
||||
s.qFlags = qFlags
|
||||
}
|
||||
|
||||
func (s *segment) DecRef() {
|
||||
s.segmentRefs.DecRef(func() {
|
||||
if s.ep != nil {
|
||||
switch s.qFlags {
|
||||
case recvQ:
|
||||
s.ep.updateReceiveMemUsed(-s.segMemSize())
|
||||
case sendQ:
|
||||
// no memory accounting for sendQ yet.
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected queue flag %b set for segment", s.qFlags))
|
||||
}
|
||||
}
|
||||
s.pkt.DecRef()
|
||||
s.pkt = nil
|
||||
segmentPool.Put(s)
|
||||
})
|
||||
}
|
||||
|
||||
// logicalLen is the segment length in the sequence number space. It's defined
|
||||
// as the data length plus one for each of the SYN and FIN bits set.
|
||||
func (s *segment) logicalLen() seqnum.Size {
|
||||
l := seqnum.Size(s.payloadSize())
|
||||
if s.flags.Contains(header.TCPFlagSyn) {
|
||||
l++
|
||||
}
|
||||
if s.flags.Contains(header.TCPFlagFin) {
|
||||
l++
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// payloadSize is the size of s.data.
|
||||
func (s *segment) payloadSize() int {
|
||||
return s.pkt.Data().Size()
|
||||
}
|
||||
|
||||
// segMemSize is the amount of memory used to hold the segment data and
|
||||
// the associated metadata.
|
||||
func (s *segment) segMemSize() int {
|
||||
return segSize + s.dataMemSize
|
||||
}
|
||||
|
||||
// sackBlock returns a header.SACKBlock that represents this segment.
|
||||
func (s *segment) sackBlock() header.SACKBlock {
|
||||
return header.SACKBlock{Start: s.sequenceNumber, End: s.sequenceNumber.Add(s.logicalLen())}
|
||||
}
|
||||
|
||||
func (s *segment) TrimFront(ackLeft seqnum.Size) {
|
||||
s.pkt.Data().TrimFront(int(ackLeft))
|
||||
}
|
||||
|
||||
func (s *segment) ReadTo(dst io.Writer, peek bool) (int, error) {
|
||||
return s.pkt.Data().ReadTo(dst, peek)
|
||||
}
|
||||
51
pkg/tcpip/transport/tcp/segment_heap.go
Normal file
51
pkg/tcpip/transport/tcp/segment_heap.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import "container/heap"
|
||||
|
||||
type segmentHeap []*segment
|
||||
|
||||
var _ heap.Interface = (*segmentHeap)(nil)
|
||||
|
||||
// Len returns the length of h.
|
||||
func (h *segmentHeap) Len() int {
|
||||
return len(*h)
|
||||
}
|
||||
|
||||
// Less determines whether the i-th element of h is less than the j-th element.
|
||||
func (h *segmentHeap) Less(i, j int) bool {
|
||||
return (*h)[i].sequenceNumber.LessThan((*h)[j].sequenceNumber)
|
||||
}
|
||||
|
||||
// Swap swaps the i-th and j-th elements of h.
|
||||
func (h *segmentHeap) Swap(i, j int) {
|
||||
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
|
||||
}
|
||||
|
||||
// Push adds x as the last element of h.
|
||||
func (h *segmentHeap) Push(x any) {
|
||||
*h = append(*h, x.(*segment))
|
||||
}
|
||||
|
||||
// Pop removes the last element of h and returns it.
|
||||
func (h *segmentHeap) Pop() any {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
old[n-1] = nil
|
||||
*h = old[:n-1]
|
||||
return x
|
||||
}
|
||||
99
pkg/tcpip/transport/tcp/segment_queue.go
Normal file
99
pkg/tcpip/transport/tcp/segment_queue.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
// segmentQueue is a bounded, thread-safe queue of TCP segments.
|
||||
//
|
||||
// +stateify savable
|
||||
type segmentQueue struct {
|
||||
mu segmentQueueMutex `state:"nosave"`
|
||||
list segmentList `state:"wait"`
|
||||
ep *Endpoint
|
||||
frozen bool
|
||||
}
|
||||
|
||||
// emptyLocked determines if the queue is empty.
|
||||
// Preconditions: q.mu must be held.
|
||||
func (q *segmentQueue) emptyLocked() bool {
|
||||
return q.list.Empty()
|
||||
}
|
||||
|
||||
// empty determines if the queue is empty.
|
||||
func (q *segmentQueue) empty() bool {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
return q.emptyLocked()
|
||||
}
|
||||
|
||||
// enqueue adds the given segment to the queue.
|
||||
//
|
||||
// Returns true when the segment is successfully added to the queue, in which
|
||||
// case ownership of the reference is transferred to the queue. And returns
|
||||
// false if the queue is full, in which case ownership is retained by the
|
||||
// caller.
|
||||
func (q *segmentQueue) enqueue(s *segment) bool {
|
||||
// q.ep.receiveBufferParams() must be called without holding q.mu to
|
||||
// avoid lock order inversion.
|
||||
bufSz := q.ep.ops.GetReceiveBufferSize()
|
||||
used := q.ep.receiveMemUsed()
|
||||
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
// Allow zero sized segments (ACK/FIN/RSTs etc even if the segment queue
|
||||
// is currently full).
|
||||
allow := (used <= int(bufSz) || s.payloadSize() == 0) && !q.frozen
|
||||
|
||||
if allow {
|
||||
s.IncRef()
|
||||
q.list.PushBack(s)
|
||||
// Set the owner now that the endpoint owns the segment.
|
||||
s.setOwner(q.ep, recvQ)
|
||||
}
|
||||
|
||||
return allow
|
||||
}
|
||||
|
||||
// dequeue removes and returns the next segment from queue, if one exists.
|
||||
// Ownership is transferred to the caller, who is responsible for decrementing
|
||||
// the ref count when done.
|
||||
func (q *segmentQueue) dequeue() *segment {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
s := q.list.Front()
|
||||
if s != nil {
|
||||
q.list.Remove(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// freeze prevents any more segments from being added to the queue. i.e all
|
||||
// future segmentQueue.enqueue will return false and not add the segment to the
|
||||
// queue till the queue is unfroze with a corresponding segmentQueue.thaw call.
|
||||
func (q *segmentQueue) freeze() {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
q.frozen = true
|
||||
}
|
||||
|
||||
// thaw unfreezes a previously frozen queue using segmentQueue.freeze() and
|
||||
// allows new segments to be queued again.
|
||||
func (q *segmentQueue) thaw() {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
q.frozen = false
|
||||
}
|
||||
64
pkg/tcpip/transport/tcp/segment_queue_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/segment_queue_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type segmentQueueMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var segmentQueueprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var segmentQueuelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type segmentQueuelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *segmentQueueMutex) Lock() {
|
||||
locking.AddGLock(segmentQueueprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *segmentQueueMutex) NestedLock(i segmentQueuelockNameIndex) {
|
||||
locking.AddGLock(segmentQueueprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *segmentQueueMutex) Unlock() {
|
||||
locking.DelGLock(segmentQueueprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *segmentQueueMutex) NestedUnlock(i segmentQueuelockNameIndex) {
|
||||
locking.DelGLock(segmentQueueprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func segmentQueueinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
segmentQueueinitLockNames()
|
||||
segmentQueueprefixIndex = locking.NewMutexClass(reflect.TypeOf(segmentQueueMutex{}), segmentQueuelockNames)
|
||||
}
|
||||
35
pkg/tcpip/transport/tcp/segment_state.go
Normal file
35
pkg/tcpip/transport/tcp/segment_state.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// saveOptions is invoked by stateify.
|
||||
func (s *segment) saveOptions() []byte {
|
||||
// We cannot save s.options directly as it may point to s.data's trimmed
|
||||
// tail, which is not allowed by state framework (in-struct pointer).
|
||||
b := make([]byte, 0, cap(s.options))
|
||||
return append(b, s.options...)
|
||||
}
|
||||
|
||||
// loadOptions is invoked by stateify.
|
||||
func (s *segment) loadOptions(_ context.Context, options []byte) {
|
||||
// NOTE: We cannot point s.options back into s.data's trimmed tail. But
|
||||
// it is OK as they do not need to aliased. Plus, options is already
|
||||
// allocated so there is no cost here.
|
||||
s.options = options
|
||||
}
|
||||
23
pkg/tcpip/transport/tcp/segment_unsafe.go
Normal file
23
pkg/tcpip/transport/tcp/segment_unsafe.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// 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 tcp
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
segSize = int(unsafe.Sizeof(segment{}))
|
||||
)
|
||||
1905
pkg/tcpip/transport/tcp/snd.go
Normal file
1905
pkg/tcpip/transport/tcp/snd.go
Normal file
File diff suppressed because it is too large
Load diff
64
pkg/tcpip/transport/tcp/snd_queue_mutex.go
Normal file
64
pkg/tcpip/transport/tcp/snd_queue_mutex.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type sndQueueMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var sndQueueprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var sndQueuelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type sndQueuelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *sndQueueMutex) Lock() {
|
||||
locking.AddGLock(sndQueueprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *sndQueueMutex) NestedLock(i sndQueuelockNameIndex) {
|
||||
locking.AddGLock(sndQueueprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *sndQueueMutex) Unlock() {
|
||||
locking.DelGLock(sndQueueprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *sndQueueMutex) NestedUnlock(i sndQueuelockNameIndex) {
|
||||
locking.DelGLock(sndQueueprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func sndQueueinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
sndQueueinitLockNames()
|
||||
sndQueueprefixIndex = locking.NewMutexClass(reflect.TypeOf(sndQueueMutex{}), sndQueuelockNames)
|
||||
}
|
||||
480
pkg/tcpip/transport/tcp/state.go
Normal file
480
pkg/tcpip/transport/tcp/state.go
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/atomicbitops"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/internal/tcp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
// TCPProbeFunc is the expected function type for a TCP probe function to be
|
||||
// passed to stack.AddTCPProbe.
|
||||
type TCPProbeFunc func(s *TCPEndpointState)
|
||||
|
||||
// TCPCubicState is used to hold a copy of the internal cubic state when the
|
||||
// TCPProbeFunc is invoked.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPCubicState struct {
|
||||
// WLastMax is the previous wMax value.
|
||||
WLastMax float64
|
||||
|
||||
// WMax is the value of the congestion window at the time of the last
|
||||
// congestion event.
|
||||
WMax float64
|
||||
|
||||
// T is the time when the current congestion avoidance was entered.
|
||||
T tcpip.MonotonicTime
|
||||
|
||||
// TimeSinceLastCongestion denotes the time since the current
|
||||
// congestion avoidance was entered.
|
||||
TimeSinceLastCongestion time.Duration
|
||||
|
||||
// C is the cubic constant as specified in RFC8312, page 11.
|
||||
C float64
|
||||
|
||||
// K is the time period (in seconds) that the above function takes to
|
||||
// increase the current window size to WMax if there are no further
|
||||
// congestion events and is calculated using the following equation:
|
||||
//
|
||||
// K = cubic_root(WMax*(1-beta_cubic)/C) (Eq. 2, page 5)
|
||||
K float64
|
||||
|
||||
// Beta is the CUBIC multiplication decrease factor. That is, when a
|
||||
// congestion event is detected, CUBIC reduces its cwnd to
|
||||
// WC(0)=WMax*beta_cubic.
|
||||
Beta float64
|
||||
|
||||
// WC is window computed by CUBIC at time TimeSinceLastCongestion. It's
|
||||
// calculated using the formula:
|
||||
//
|
||||
// WC(TimeSinceLastCongestion) = C*(t-K)^3 + WMax (Eq. 1)
|
||||
WC float64
|
||||
|
||||
// WEst is the window computed by CUBIC at time
|
||||
// TimeSinceLastCongestion+RTT i.e WC(TimeSinceLastCongestion+RTT).
|
||||
WEst float64
|
||||
|
||||
// EndSeq is the sequence number that, when cumulatively ACK'd, ends the
|
||||
// HyStart round.
|
||||
EndSeq seqnum.Value
|
||||
|
||||
// CurrRTT is the minimum round-trip time from the current round.
|
||||
CurrRTT time.Duration
|
||||
|
||||
// LastRTT is the minimum round-trip time from the previous round.
|
||||
LastRTT time.Duration
|
||||
|
||||
// SampleCount is the number of samples from the current round.
|
||||
SampleCount uint
|
||||
|
||||
// LastAck is the time we received the most recent ACK (or start of round if
|
||||
// more recent).
|
||||
LastAck tcpip.MonotonicTime
|
||||
|
||||
// RoundStart is the time we started the most recent HyStart round.
|
||||
RoundStart tcpip.MonotonicTime
|
||||
}
|
||||
|
||||
// TCPRACKState is used to hold a copy of the internal RACK state when the
|
||||
// TCPProbeFunc is invoked.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPRACKState struct {
|
||||
// XmitTime is the transmission timestamp of the most recent
|
||||
// acknowledged segment.
|
||||
XmitTime tcpip.MonotonicTime
|
||||
|
||||
// EndSequence is the ending TCP sequence number of the most recent
|
||||
// acknowledged segment.
|
||||
EndSequence seqnum.Value
|
||||
|
||||
// FACK is the highest selectively or cumulatively acknowledged
|
||||
// sequence.
|
||||
FACK seqnum.Value
|
||||
|
||||
// RTT is the round trip time of the most recently delivered packet on
|
||||
// the connection (either cumulatively acknowledged or selectively
|
||||
// acknowledged) that was not marked invalid as a possible spurious
|
||||
// retransmission.
|
||||
RTT time.Duration
|
||||
|
||||
// Reord is true iff reordering has been detected on this connection.
|
||||
Reord bool
|
||||
|
||||
// DSACKSeen is true iff the connection has seen a DSACK.
|
||||
DSACKSeen bool
|
||||
|
||||
// ReoWnd is the reordering window time used for recording packet
|
||||
// transmission times. It is used to defer the moment at which RACK
|
||||
// marks a packet lost.
|
||||
ReoWnd time.Duration
|
||||
|
||||
// ReoWndIncr is the multiplier applied to adjust reorder window.
|
||||
ReoWndIncr uint8
|
||||
|
||||
// ReoWndPersist is the number of loss recoveries before resetting
|
||||
// reorder window.
|
||||
ReoWndPersist int8
|
||||
|
||||
// RTTSeq is the SND.NXT when RTT is updated.
|
||||
RTTSeq seqnum.Value
|
||||
}
|
||||
|
||||
// TCPEndpointID is the unique 4 tuple that identifies a given endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPEndpointID struct {
|
||||
// LocalPort is the local port associated with the endpoint.
|
||||
LocalPort uint16
|
||||
|
||||
// LocalAddress is the local [network layer] address associated with
|
||||
// the endpoint.
|
||||
LocalAddress tcpip.Address
|
||||
|
||||
// RemotePort is the remote port associated with the endpoint.
|
||||
RemotePort uint16
|
||||
|
||||
// RemoteAddress it the remote [network layer] address associated with
|
||||
// the endpoint.
|
||||
RemoteAddress tcpip.Address
|
||||
}
|
||||
|
||||
// TCPFastRecoveryState holds a copy of the internal fast recovery state of a
|
||||
// TCP endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPFastRecoveryState struct {
|
||||
// Active if true indicates the endpoint is in fast recovery. The
|
||||
// following fields are only meaningful when Active is true.
|
||||
Active bool
|
||||
|
||||
// First is the first unacknowledged sequence number being recovered.
|
||||
First seqnum.Value
|
||||
|
||||
// Last is the 'recover' sequence number that indicates the point at
|
||||
// which we should exit recovery barring any timeouts etc.
|
||||
Last seqnum.Value
|
||||
|
||||
// MaxCwnd is the maximum value we are permitted to grow the congestion
|
||||
// window during recovery. This is set at the time we enter recovery.
|
||||
// It exists to avoid attacks where the receiver intentionally sends
|
||||
// duplicate acks to artificially inflate the sender's cwnd.
|
||||
MaxCwnd int
|
||||
|
||||
// HighRxt is the highest sequence number which has been retransmitted
|
||||
// during the current loss recovery phase. See: RFC 6675 Section 2 for
|
||||
// details.
|
||||
HighRxt seqnum.Value
|
||||
|
||||
// RescueRxt is the highest sequence number which has been
|
||||
// optimistically retransmitted to prevent stalling of the ACK clock
|
||||
// when there is loss at the end of the window and no new data is
|
||||
// available for transmission. See: RFC 6675 Section 2 for details.
|
||||
RescueRxt seqnum.Value
|
||||
}
|
||||
|
||||
// TCPReceiverState holds a copy of the internal state of the receiver for a
|
||||
// given TCP endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPReceiverState struct {
|
||||
// RcvNxt is the TCP variable RCV.NXT.
|
||||
RcvNxt seqnum.Value
|
||||
|
||||
// RcvAcc is one beyond the last acceptable sequence number. That is,
|
||||
// the "largest" sequence value that the receiver has announced to its
|
||||
// peer that it's willing to accept. This may be different than RcvNxt
|
||||
// + (last advertised receive window) if the receive window is reduced;
|
||||
// in that case we have to reduce the window as we receive more data
|
||||
// instead of shrinking it.
|
||||
RcvAcc seqnum.Value
|
||||
|
||||
// RcvWndScale is the window scaling to use for inbound segments.
|
||||
RcvWndScale uint8
|
||||
|
||||
// PendingBufUsed is the number of bytes pending in the receive queue.
|
||||
PendingBufUsed int
|
||||
}
|
||||
|
||||
// TCPRTTState holds a copy of information about the endpoint's round trip
|
||||
// time.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPRTTState struct {
|
||||
// SRTT is the smoothed round trip time defined in section 2 of RFC
|
||||
// 6298.
|
||||
SRTT time.Duration
|
||||
|
||||
// RTTVar is the round-trip time variation as defined in section 2 of
|
||||
// RFC 6298.
|
||||
RTTVar time.Duration
|
||||
|
||||
// SRTTInited if true indicates that a valid RTT measurement has been
|
||||
// completed.
|
||||
SRTTInited bool
|
||||
}
|
||||
|
||||
// TCPSenderState holds a copy of the internal state of the sender for a given
|
||||
// TCP Endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPSenderState struct {
|
||||
// LastSendTime is the timestamp at which we sent the last segment.
|
||||
LastSendTime tcpip.MonotonicTime
|
||||
|
||||
// DupAckCount is the number of Duplicate ACKs received. It is used for
|
||||
// fast retransmit.
|
||||
DupAckCount int
|
||||
|
||||
// SndCwnd is the size of the sending congestion window in packets.
|
||||
SndCwnd int
|
||||
|
||||
// Ssthresh is the threshold between slow start and congestion
|
||||
// avoidance.
|
||||
Ssthresh int
|
||||
|
||||
// SndCAAckCount is the number of packets acknowledged during
|
||||
// congestion avoidance. When enough packets have been ack'd (typically
|
||||
// cwnd packets), the congestion window is incremented by one.
|
||||
SndCAAckCount int
|
||||
|
||||
// Outstanding is the number of packets that have been sent but not yet
|
||||
// acknowledged.
|
||||
Outstanding int
|
||||
|
||||
// SackedOut is the number of packets which have been selectively
|
||||
// acked.
|
||||
SackedOut int
|
||||
|
||||
// SndWnd is the send window size in bytes.
|
||||
SndWnd seqnum.Size
|
||||
|
||||
// SndUna is the next unacknowledged sequence number.
|
||||
SndUna seqnum.Value
|
||||
|
||||
// SndNxt is the sequence number of the next segment to be sent.
|
||||
SndNxt seqnum.Value
|
||||
|
||||
// RTTMeasureSeqNum is the sequence number being used for the latest
|
||||
// RTT measurement.
|
||||
RTTMeasureSeqNum seqnum.Value
|
||||
|
||||
// RTTMeasureTime is the time when the RTTMeasureSeqNum was sent.
|
||||
RTTMeasureTime tcpip.MonotonicTime
|
||||
|
||||
// Closed indicates that the caller has closed the endpoint for
|
||||
// sending.
|
||||
Closed bool
|
||||
|
||||
// RTO is the retransmit timeout as defined in section of 2 of RFC
|
||||
// 6298.
|
||||
RTO time.Duration
|
||||
|
||||
// RTTState holds information about the endpoint's round trip time.
|
||||
RTTState TCPRTTState
|
||||
|
||||
// MaxPayloadSize is the maximum size of the payload of a given
|
||||
// segment. It is initialized on demand.
|
||||
MaxPayloadSize int
|
||||
|
||||
// SndWndScale is the number of bits to shift left when reading the
|
||||
// send window size from a segment.
|
||||
SndWndScale uint8
|
||||
|
||||
// MaxSentAck is the highest acknowledgement number sent till now.
|
||||
MaxSentAck seqnum.Value
|
||||
|
||||
// FastRecovery holds the fast recovery state for the endpoint.
|
||||
FastRecovery TCPFastRecoveryState
|
||||
|
||||
// Cubic holds the state related to CUBIC congestion control.
|
||||
Cubic TCPCubicState
|
||||
|
||||
// RACKState holds the state related to RACK loss detection algorithm.
|
||||
RACKState TCPRACKState
|
||||
|
||||
// RetransmitTS records the timestamp used to detect spurious recovery.
|
||||
RetransmitTS uint32
|
||||
|
||||
// SpuriousRecovery indicates if the sender entered recovery spuriously.
|
||||
SpuriousRecovery bool
|
||||
}
|
||||
|
||||
// TCPSACKInfo holds TCP SACK related information for a given TCP endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPSACKInfo struct {
|
||||
// Blocks is the list of SACK Blocks that identify the out of order
|
||||
// segments held by a given TCP endpoint.
|
||||
Blocks []header.SACKBlock
|
||||
|
||||
// ReceivedBlocks are the SACK blocks received by this endpoint from
|
||||
// the peer endpoint.
|
||||
ReceivedBlocks []header.SACKBlock
|
||||
|
||||
// MaxSACKED is the highest sequence number that has been SACKED by the
|
||||
// peer.
|
||||
MaxSACKED seqnum.Value
|
||||
}
|
||||
|
||||
// RcvBufAutoTuneParams holds state related to TCP receive buffer auto-tuning.
|
||||
//
|
||||
// +stateify savable
|
||||
type RcvBufAutoTuneParams struct {
|
||||
// MeasureTime is the time at which the current measurement was
|
||||
// started.
|
||||
MeasureTime tcpip.MonotonicTime
|
||||
|
||||
// CopiedBytes is the number of bytes copied to user space since this
|
||||
// measure began.
|
||||
CopiedBytes int
|
||||
|
||||
// PrevCopiedBytes is the number of bytes copied to userspace in the
|
||||
// previous RTT period.
|
||||
PrevCopiedBytes int
|
||||
|
||||
// RcvBufSize is the auto tuned receive buffer size.
|
||||
RcvBufSize int
|
||||
|
||||
// RTT is the smoothed RTT as measured by observing the time between
|
||||
// when a byte is first acknowledged and the receipt of data that is at
|
||||
// least one window beyond the sequence number that was acknowledged.
|
||||
RTT time.Duration
|
||||
|
||||
// RTTVar is the "round-trip time variation" as defined in section 2 of
|
||||
// RFC6298.
|
||||
RTTVar time.Duration
|
||||
|
||||
// RTTMeasureSeqNumber is the highest acceptable sequence number at the
|
||||
// time this RTT measurement period began.
|
||||
RTTMeasureSeqNumber seqnum.Value
|
||||
|
||||
// RTTMeasureTime is the absolute time at which the current RTT
|
||||
// measurement period began.
|
||||
RTTMeasureTime tcpip.MonotonicTime
|
||||
|
||||
// Disabled is true if an explicit receive buffer is set for the
|
||||
// endpoint.
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// TCPRcvBufState contains information about the state of an endpoint's receive
|
||||
// socket buffer.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPRcvBufState struct {
|
||||
// RcvBufUsed is the amount of bytes actually held in the receive
|
||||
// socket buffer for the endpoint.
|
||||
RcvBufUsed int
|
||||
|
||||
// RcvBufAutoTuneParams is used to hold state variables to compute the
|
||||
// auto tuned receive buffer size.
|
||||
RcvAutoParams RcvBufAutoTuneParams
|
||||
|
||||
// RcvClosed if true, indicates the endpoint has been closed for
|
||||
// reading.
|
||||
RcvClosed bool
|
||||
}
|
||||
|
||||
// TCPSndBufState contains information about the state of an endpoint's send
|
||||
// socket buffer.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPSndBufState struct {
|
||||
// SndBufSize is the size of the socket send buffer.
|
||||
SndBufSize int
|
||||
|
||||
// SndBufUsed is the number of bytes held in the socket send buffer.
|
||||
SndBufUsed int
|
||||
|
||||
// SndClosed indicates that the endpoint has been closed for sends.
|
||||
SndClosed bool
|
||||
|
||||
// PacketTooBigCount is used to notify the main protocol routine how
|
||||
// many times a "packet too big" control packet is received.
|
||||
PacketTooBigCount int
|
||||
|
||||
// SndMTU is the smallest MTU seen in the control packets received.
|
||||
SndMTU int
|
||||
|
||||
// AutoTuneSndBufDisabled indicates that the auto tuning of send buffer
|
||||
// is disabled.
|
||||
AutoTuneSndBufDisabled atomicbitops.Uint32
|
||||
}
|
||||
|
||||
// TCPEndpointStateInner contains the members of TCPEndpointState used directly
|
||||
// (that is, not within another containing struct) within the endpoint's
|
||||
// internal implementation.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPEndpointStateInner struct {
|
||||
// TSOffset is a randomized offset added to the value of the TSVal
|
||||
// field in the timestamp option.
|
||||
TSOffset tcp.TSOffset
|
||||
|
||||
// SACKPermitted is set to true if the peer sends the TCPSACKPermitted
|
||||
// option in the SYN/SYN-ACK.
|
||||
SACKPermitted bool
|
||||
|
||||
// SendTSOk is used to indicate when the TS Option has been negotiated.
|
||||
// When sendTSOk is true every non-RST segment should carry a TS as per
|
||||
// RFC7323#section-1.1.
|
||||
SendTSOk bool
|
||||
|
||||
// RecentTS is the timestamp that should be sent in the TSEcr field of
|
||||
// the timestamp for future segments sent by the endpoint. This field
|
||||
// is updated if required when a new segment is received by this
|
||||
// endpoint.
|
||||
RecentTS uint32
|
||||
}
|
||||
|
||||
// TCPEndpointState is a copy of the internal state of a TCP endpoint.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPEndpointState struct {
|
||||
// TCPEndpointStateInner contains the members of TCPEndpointState used
|
||||
// by the endpoint's internal implementation.
|
||||
TCPEndpointStateInner
|
||||
|
||||
// ID is a copy of the TransportEndpointID for the endpoint.
|
||||
ID TCPEndpointID
|
||||
|
||||
// SegTime denotes the absolute time when this segment was received.
|
||||
SegTime tcpip.MonotonicTime
|
||||
|
||||
// RcvBufState contains information about the state of the endpoint's
|
||||
// receive socket buffer.
|
||||
RcvBufState TCPRcvBufState
|
||||
|
||||
// SndBufState contains information about the state of the endpoint's
|
||||
// send socket buffer.
|
||||
SndBufState TCPSndBufState
|
||||
|
||||
// SACK holds TCP SACK related information for this endpoint.
|
||||
SACK TCPSACKInfo
|
||||
|
||||
// Receiver holds variables related to the TCP receiver for the
|
||||
// endpoint.
|
||||
Receiver TCPReceiverState
|
||||
|
||||
// Sender holds state related to the TCP Sender for the endpoint.
|
||||
Sender TCPSenderState
|
||||
}
|
||||
239
pkg/tcpip/transport/tcp/tcp_endpoint_list.go
Normal file
239
pkg/tcpip/transport/tcp/tcp_endpoint_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package tcp
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type endpointElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (endpointElementMapper) linkerFor(elem *Endpoint) *Endpoint { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type endpointList struct {
|
||||
head *Endpoint
|
||||
tail *Endpoint
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *endpointList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) Front() *Endpoint {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) Back() *Endpoint {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (endpointElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) PushFront(e *Endpoint) {
|
||||
linker := endpointElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
endpointElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) PushFrontList(m *endpointList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
endpointElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
endpointElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) PushBack(e *Endpoint) {
|
||||
linker := endpointElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
endpointElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) PushBackList(m *endpointList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
endpointElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
endpointElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) InsertAfter(b, e *Endpoint) {
|
||||
bLinker := endpointElementMapper{}.linkerFor(b)
|
||||
eLinker := endpointElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
endpointElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) InsertBefore(a, e *Endpoint) {
|
||||
aLinker := endpointElementMapper{}.linkerFor(a)
|
||||
eLinker := endpointElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
endpointElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *endpointList) Remove(e *Endpoint) {
|
||||
linker := endpointElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
endpointElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
endpointElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type endpointEntry struct {
|
||||
next *Endpoint
|
||||
prev *Endpoint
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *endpointEntry) Next() *Endpoint {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *endpointEntry) Prev() *Endpoint {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *endpointEntry) SetNext(elem *Endpoint) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *endpointEntry) SetPrev(elem *Endpoint) {
|
||||
e.prev = elem
|
||||
}
|
||||
239
pkg/tcpip/transport/tcp/tcp_segment_list.go
Normal file
239
pkg/tcpip/transport/tcp/tcp_segment_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package tcp
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type segmentElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (segmentElementMapper) linkerFor(elem *segment) *segment { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type segmentList struct {
|
||||
head *segment
|
||||
tail *segment
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *segmentList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) Front() *segment {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) Back() *segment {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (segmentElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) PushFront(e *segment) {
|
||||
linker := segmentElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
segmentElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) PushFrontList(m *segmentList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
segmentElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
segmentElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) PushBack(e *segment) {
|
||||
linker := segmentElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
segmentElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) PushBackList(m *segmentList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
segmentElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
segmentElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) InsertAfter(b, e *segment) {
|
||||
bLinker := segmentElementMapper{}.linkerFor(b)
|
||||
eLinker := segmentElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
segmentElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) InsertBefore(a, e *segment) {
|
||||
aLinker := segmentElementMapper{}.linkerFor(a)
|
||||
eLinker := segmentElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
segmentElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *segmentList) Remove(e *segment) {
|
||||
linker := segmentElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
segmentElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
segmentElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type segmentEntry struct {
|
||||
next *segment
|
||||
prev *segment
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *segmentEntry) Next() *segment {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *segmentEntry) Prev() *segment {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *segmentEntry) SetNext(elem *segment) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *segmentEntry) SetPrev(elem *segment) {
|
||||
e.prev = elem
|
||||
}
|
||||
141
pkg/tcpip/transport/tcp/tcp_segment_refs.go
Normal file
141
pkg/tcpip/transport/tcp/tcp_segment_refs.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/atomicbitops"
|
||||
"github.com/sagernet/gvisor/pkg/refs"
|
||||
)
|
||||
|
||||
// enableLogging indicates whether reference-related events should be logged (with
|
||||
// stack traces). This is false by default and should only be set to true for
|
||||
// debugging purposes, as it can generate an extremely large amount of output
|
||||
// and drastically degrade performance.
|
||||
const segmentenableLogging = false
|
||||
|
||||
// obj is used to customize logging. Note that we use a pointer to T so that
|
||||
// we do not copy the entire object when passed as a format parameter.
|
||||
var segmentobj *segment
|
||||
|
||||
// Refs implements refs.RefCounter. It keeps a reference count using atomic
|
||||
// operations and calls the destructor when the count reaches zero.
|
||||
//
|
||||
// NOTE: Do not introduce additional fields to the Refs struct. It is used by
|
||||
// many filesystem objects, and we want to keep it as small as possible (i.e.,
|
||||
// the same size as using an int64 directly) to avoid taking up extra cache
|
||||
// space. In general, this template should not be extended at the cost of
|
||||
// performance. If it does not offer enough flexibility for a particular object
|
||||
// (example: b/187877947), we should implement the RefCounter/CheckedObject
|
||||
// interfaces manually.
|
||||
//
|
||||
// +stateify savable
|
||||
type segmentRefs struct {
|
||||
// refCount is composed of two fields:
|
||||
//
|
||||
// [32-bit speculative references]:[32-bit real references]
|
||||
//
|
||||
// Speculative references are used for TryIncRef, to avoid a CompareAndSwap
|
||||
// loop. See IncRef, DecRef and TryIncRef for details of how these fields are
|
||||
// used.
|
||||
refCount atomicbitops.Int64
|
||||
}
|
||||
|
||||
// InitRefs initializes r with one reference and, if enabled, activates leak
|
||||
// checking.
|
||||
func (r *segmentRefs) InitRefs() {
|
||||
r.refCount.RacyStore(1)
|
||||
refs.Register(r)
|
||||
}
|
||||
|
||||
// RefType implements refs.CheckedObject.RefType.
|
||||
func (r *segmentRefs) RefType() string {
|
||||
return fmt.Sprintf("%T", segmentobj)[1:]
|
||||
}
|
||||
|
||||
// LeakMessage implements refs.CheckedObject.LeakMessage.
|
||||
func (r *segmentRefs) LeakMessage() string {
|
||||
return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs())
|
||||
}
|
||||
|
||||
// LogRefs implements refs.CheckedObject.LogRefs.
|
||||
func (r *segmentRefs) LogRefs() bool {
|
||||
return segmentenableLogging
|
||||
}
|
||||
|
||||
// ReadRefs returns the current number of references. The returned count is
|
||||
// inherently racy and is unsafe to use without external synchronization.
|
||||
func (r *segmentRefs) ReadRefs() int64 {
|
||||
return r.refCount.Load()
|
||||
}
|
||||
|
||||
// IncRef implements refs.RefCounter.IncRef.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *segmentRefs) IncRef() {
|
||||
v := r.refCount.Add(1)
|
||||
if segmentenableLogging {
|
||||
refs.LogIncRef(r, v)
|
||||
}
|
||||
if v <= 1 {
|
||||
panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType()))
|
||||
}
|
||||
}
|
||||
|
||||
// TryIncRef implements refs.TryRefCounter.TryIncRef.
|
||||
//
|
||||
// To do this safely without a loop, a speculative reference is first acquired
|
||||
// on the object. This allows multiple concurrent TryIncRef calls to distinguish
|
||||
// other TryIncRef calls from genuine references held.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *segmentRefs) TryIncRef() bool {
|
||||
const speculativeRef = 1 << 32
|
||||
if v := r.refCount.Add(speculativeRef); int32(v) == 0 {
|
||||
|
||||
r.refCount.Add(-speculativeRef)
|
||||
return false
|
||||
}
|
||||
|
||||
v := r.refCount.Add(-speculativeRef + 1)
|
||||
if segmentenableLogging {
|
||||
refs.LogTryIncRef(r, v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DecRef implements refs.RefCounter.DecRef.
|
||||
//
|
||||
// Note that speculative references are counted here. Since they were added
|
||||
// prior to real references reaching zero, they will successfully convert to
|
||||
// real references. In other words, we see speculative references only in the
|
||||
// following case:
|
||||
//
|
||||
// A: TryIncRef [speculative increase => sees non-negative references]
|
||||
// B: DecRef [real decrease]
|
||||
// A: TryIncRef [transform speculative to real]
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *segmentRefs) DecRef(destroy func()) {
|
||||
v := r.refCount.Add(-1)
|
||||
if segmentenableLogging {
|
||||
refs.LogDecRef(r, v)
|
||||
}
|
||||
switch {
|
||||
case v < 0:
|
||||
panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType()))
|
||||
|
||||
case v == 0:
|
||||
refs.Unregister(r)
|
||||
|
||||
if destroy != nil {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *segmentRefs) afterLoad(context.Context) {
|
||||
if r.ReadRefs() > 0 {
|
||||
refs.Register(r)
|
||||
}
|
||||
}
|
||||
1935
pkg/tcpip/transport/tcp/tcp_state_autogen.go
Normal file
1935
pkg/tcpip/transport/tcp/tcp_state_autogen.go
Normal file
File diff suppressed because it is too large
Load diff
3
pkg/tcpip/transport/tcp/tcp_unsafe_state_autogen.go
Normal file
3
pkg/tcpip/transport/tcp/tcp_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package tcp
|
||||
160
pkg/tcpip/transport/tcp/timer.go
Normal file
160
pkg/tcpip/transport/tcp/timer.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// Copyright 2018 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 tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
type timerState int
|
||||
|
||||
const (
|
||||
// The timer has not been initialized yet or has been cleaned up.
|
||||
timerUninitialized timerState = iota
|
||||
// The timer is disabled.
|
||||
timerStateDisabled
|
||||
// The timer is enabled, but the clock timer may be set to an earlier
|
||||
// expiration time due to a previous orphaned state.
|
||||
timerStateEnabled
|
||||
// The timer is disabled, but the clock timer is enabled, which means that
|
||||
// it will cause a spurious wakeup unless the timer is enabled before the
|
||||
// clock timer fires.
|
||||
timerStateOrphaned
|
||||
)
|
||||
|
||||
// timer is a timer implementation that reduces the interactions with the
|
||||
// clock timer infrastructure by letting timers run (and potentially
|
||||
// eventually expire) even if they are stopped. It makes it cheaper to
|
||||
// disable/reenable timers at the expense of spurious wakes. This is useful for
|
||||
// cases when the same timer is disabled/reenabled repeatedly with relatively
|
||||
// long timeouts farther into the future.
|
||||
//
|
||||
// TCP retransmit timers benefit from this because they the timeouts are long
|
||||
// (currently at least 200ms), and get disabled when acks are received, and
|
||||
// reenabled when new pending segments are sent.
|
||||
//
|
||||
// It is advantageous to avoid interacting with the clock because it acquires
|
||||
// a global mutex and performs O(log n) operations, where n is the global number
|
||||
// of timers, whenever a timer is enabled or disabled, and may make a syscall.
|
||||
//
|
||||
// This struct is thread-compatible.
|
||||
type timer struct {
|
||||
state timerState
|
||||
|
||||
clock tcpip.Clock
|
||||
|
||||
// target is the expiration time of the current timer. It is only
|
||||
// meaningful in the enabled state.
|
||||
target tcpip.MonotonicTime
|
||||
|
||||
// clockTarget is the expiration time of the clock timer. It is
|
||||
// meaningful in the enabled and orphaned states.
|
||||
clockTarget tcpip.MonotonicTime
|
||||
|
||||
// timer is the clock timer used to wait on.
|
||||
timer tcpip.Timer
|
||||
|
||||
// callback is the function that's called when the timer expires.
|
||||
callback func()
|
||||
}
|
||||
|
||||
// init initializes the timer. Once it expires the function callback
|
||||
// passed will be called.
|
||||
func (t *timer) init(clock tcpip.Clock, f func()) {
|
||||
t.state = timerStateDisabled
|
||||
t.clock = clock
|
||||
t.callback = f
|
||||
}
|
||||
|
||||
// cleanup frees all resources associated with the timer.
|
||||
func (t *timer) cleanup() {
|
||||
if t.timer == nil {
|
||||
// No cleanup needed.
|
||||
return
|
||||
}
|
||||
t.timer.Stop()
|
||||
*t = timer{}
|
||||
}
|
||||
|
||||
// isUninitialized returns true if the timer is in the uninitialized state. This
|
||||
// is only true if init() has never been called or if cleanup has been called.
|
||||
func (t *timer) isUninitialized() bool {
|
||||
return t.state == timerUninitialized
|
||||
}
|
||||
|
||||
// checkExpiration checks if the given timer has actually expired, it should be
|
||||
// called whenever the callback function is called, and is used to check if it's
|
||||
// a spurious timer expiration (due to a previously orphaned timer) or a
|
||||
// legitimate one.
|
||||
func (t *timer) checkExpiration() bool {
|
||||
// Transition to fully disabled state if we're just consuming an
|
||||
// orphaned timer.
|
||||
if t.state == timerStateOrphaned {
|
||||
t.state = timerStateDisabled
|
||||
return false
|
||||
}
|
||||
|
||||
// The timer is enabled, but it may have expired early. Check if that's
|
||||
// the case, and if so, reset the runtime timer to the correct time.
|
||||
now := t.clock.NowMonotonic()
|
||||
if now.Before(t.target) {
|
||||
t.clockTarget = t.target
|
||||
t.timer.Reset(t.target.Sub(now))
|
||||
return false
|
||||
}
|
||||
|
||||
// The timer has actually expired, disable it for now and inform the
|
||||
// caller.
|
||||
t.state = timerStateDisabled
|
||||
return true
|
||||
}
|
||||
|
||||
// disable disables the timer, leaving it in an orphaned state if it wasn't
|
||||
// already disabled.
|
||||
func (t *timer) disable() {
|
||||
if t.state != timerStateDisabled {
|
||||
t.state = timerStateOrphaned
|
||||
}
|
||||
}
|
||||
|
||||
// enabled returns true if the timer is currently enabled, false otherwise.
|
||||
func (t *timer) enabled() bool {
|
||||
return t.state == timerStateEnabled
|
||||
}
|
||||
|
||||
// enable enables the timer, programming the runtime timer if necessary.
|
||||
func (t *timer) enable(d time.Duration) {
|
||||
t.target = t.clock.NowMonotonic().Add(d)
|
||||
|
||||
// Check if we need to set the runtime timer.
|
||||
if t.state == timerStateDisabled || t.target.Before(t.clockTarget) {
|
||||
t.clockTarget = t.target
|
||||
t.resetOrStart(d)
|
||||
}
|
||||
|
||||
t.state = timerStateEnabled
|
||||
}
|
||||
|
||||
// resetOrStart creates the timer if it doesn't already exist or resets it with
|
||||
// the given duration if it does.
|
||||
func (t *timer) resetOrStart(d time.Duration) {
|
||||
if t.timer == nil {
|
||||
t.timer = t.clock.AfterFunc(d, t.callback)
|
||||
} else {
|
||||
t.timer.Reset(d)
|
||||
}
|
||||
}
|
||||
417
pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go
Normal file
417
pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
// Copyright 2018 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 tcpconntrack implements a TCP connection tracking object. It allows
|
||||
// users with access to a segment stream to figure out when a connection is
|
||||
// established, reset, and closed (and in the last case, who closed first).
|
||||
package tcpconntrack
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/seqnum"
|
||||
)
|
||||
|
||||
// Result is returned when the state of a TCB is updated in response to a
|
||||
// segment.
|
||||
type Result int
|
||||
|
||||
const (
|
||||
// ResultDrop indicates that the segment should be dropped.
|
||||
ResultDrop Result = iota
|
||||
|
||||
// ResultConnecting indicates that the connection remains in a
|
||||
// connecting state.
|
||||
ResultConnecting
|
||||
|
||||
// ResultAlive indicates that the connection remains alive (connected).
|
||||
ResultAlive
|
||||
|
||||
// ResultReset indicates that the connection was reset.
|
||||
ResultReset
|
||||
|
||||
// ResultClosedByResponder indicates that the connection was gracefully
|
||||
// closed, and the reply stream was closed first.
|
||||
ResultClosedByResponder
|
||||
|
||||
// ResultClosedByOriginator indicates that the connection was gracefully
|
||||
// closed, and the original stream was closed first.
|
||||
ResultClosedByOriginator
|
||||
)
|
||||
|
||||
// maxWindowShift is the maximum shift value of the per the windows scale
|
||||
// option defined by RFC 1323.
|
||||
const maxWindowShift = 14
|
||||
|
||||
// TCB is a TCP Control Block. It holds state necessary to keep track of a TCP
|
||||
// connection and inform the caller when the connection has been closed.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCB struct {
|
||||
reply stream
|
||||
original stream
|
||||
|
||||
// State handlers. hdr is not guaranteed to contain bytes beyond the TCP
|
||||
// header itself, i.e. it may not contain the payload.
|
||||
// TODO(b/341946753): Restore them when netstack is savable.
|
||||
handlerReply func(tcb *TCB, hdr header.TCP, dataLen int) Result `state:"nosave"`
|
||||
handlerOriginal func(tcb *TCB, hdr header.TCP, dataLen int) Result `state:"nosave"`
|
||||
|
||||
// firstFin holds a pointer to the first stream to send a FIN.
|
||||
firstFin *stream
|
||||
|
||||
// state is the current state of the stream.
|
||||
state Result
|
||||
}
|
||||
|
||||
// Init initializes the state of the TCB according to the initial SYN.
|
||||
func (t *TCB) Init(initialSyn header.TCP, dataLen int) Result {
|
||||
t.handlerReply = synSentStateReply
|
||||
t.handlerOriginal = synSentStateOriginal
|
||||
|
||||
iss := seqnum.Value(initialSyn.SequenceNumber())
|
||||
t.original.una = iss
|
||||
t.original.nxt = iss.Add(logicalLenSyn(initialSyn, dataLen))
|
||||
t.original.end = t.original.nxt
|
||||
// TODO(gvisor.dev/issue/6734): Cache TCP options instead of re-parsing them.
|
||||
// Because original and reply are streams, scale applies to the reply; it is
|
||||
// the receive window in the reply direction.
|
||||
t.reply.shiftCnt = header.ParseSynOptions(initialSyn.Options(), false /* isAck */).WS
|
||||
|
||||
// Even though "end" is a sequence number, we don't know the initial
|
||||
// receive sequence number yet, so we store the window size until we get
|
||||
// a SYN from the server.
|
||||
t.reply.una = 0
|
||||
t.reply.nxt = 0
|
||||
t.reply.end = seqnum.Value(initialSyn.WindowSize())
|
||||
t.state = ResultConnecting
|
||||
return t.state
|
||||
}
|
||||
|
||||
// UpdateStateReply updates the state of the TCB based on the supplied reply
|
||||
// segment.
|
||||
func (t *TCB) UpdateStateReply(tcp header.TCP, dataLen int) Result {
|
||||
st := t.handlerReply(t, tcp, dataLen)
|
||||
if st != ResultDrop {
|
||||
t.state = st
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
// UpdateStateOriginal updates the state of the TCB based on the supplied
|
||||
// original segment.
|
||||
func (t *TCB) UpdateStateOriginal(tcp header.TCP, dataLen int) Result {
|
||||
st := t.handlerOriginal(t, tcp, dataLen)
|
||||
if st != ResultDrop {
|
||||
t.state = st
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
// State returns the current state of the TCB.
|
||||
func (t *TCB) State() Result {
|
||||
return t.state
|
||||
}
|
||||
|
||||
// IsAlive returns true as long as the connection is established(Alive)
|
||||
// or connecting state.
|
||||
func (t *TCB) IsAlive() bool {
|
||||
return !t.reply.rstSeen && !t.original.rstSeen && (!t.reply.closed() || !t.original.closed())
|
||||
}
|
||||
|
||||
// OriginalSendSequenceNumber returns the snd.NXT for the original stream.
|
||||
func (t *TCB) OriginalSendSequenceNumber() seqnum.Value {
|
||||
return t.original.nxt
|
||||
}
|
||||
|
||||
// ReplySendSequenceNumber returns the snd.NXT for the reply stream.
|
||||
func (t *TCB) ReplySendSequenceNumber() seqnum.Value {
|
||||
return t.reply.nxt
|
||||
}
|
||||
|
||||
// adapResult modifies the supplied "Result" according to the state of the TCB;
|
||||
// if r is anything other than "Alive", or if one of the streams isn't closed
|
||||
// yet, it is returned unmodified. Otherwise it's converted to either
|
||||
// ClosedByOriginator or ClosedByResponder depending on which stream was closed
|
||||
// first.
|
||||
func (t *TCB) adaptResult(r Result) Result {
|
||||
// Check the unmodified case.
|
||||
if r != ResultAlive || !t.reply.closed() || !t.original.closed() {
|
||||
return r
|
||||
}
|
||||
|
||||
// Find out which was closed first.
|
||||
if t.firstFin == &t.original {
|
||||
return ResultClosedByOriginator
|
||||
}
|
||||
|
||||
return ResultClosedByResponder
|
||||
}
|
||||
|
||||
// synSentStateReply is the state handler for reply segments when the
|
||||
// connection is in SYN-SENT state.
|
||||
func synSentStateReply(t *TCB, tcp header.TCP, dataLen int) Result {
|
||||
flags := tcp.Flags()
|
||||
ackPresent := flags&header.TCPFlagAck != 0
|
||||
ack := seqnum.Value(tcp.AckNumber())
|
||||
|
||||
// Ignore segment if ack is present but not acceptable.
|
||||
if ackPresent && !(ack-1).InRange(t.original.una, t.original.nxt) {
|
||||
return ResultConnecting
|
||||
}
|
||||
|
||||
// If reset is specified, we will let the packet through no matter what
|
||||
// but we will also destroy the connection if the ACK is present (and
|
||||
// implicitly acceptable).
|
||||
if flags&header.TCPFlagRst != 0 {
|
||||
if ackPresent {
|
||||
t.reply.rstSeen = true
|
||||
return ResultReset
|
||||
}
|
||||
return ResultConnecting
|
||||
}
|
||||
|
||||
// Ignore segment if SYN is not set.
|
||||
if flags&header.TCPFlagSyn == 0 {
|
||||
return ResultConnecting
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/6734): Cache TCP options instead of re-parsing them.
|
||||
// Because original and reply are streams, scale applies to the reply; it is
|
||||
// the receive window in the original direction.
|
||||
t.original.shiftCnt = header.ParseSynOptions(tcp.Options(), ackPresent).WS
|
||||
|
||||
// Window scaling works only when both ends use the scale option.
|
||||
if t.original.shiftCnt != -1 && t.reply.shiftCnt != -1 {
|
||||
// Per RFC 1323 section 2.3:
|
||||
//
|
||||
// "If a Window Scale option is received with a shift.cnt value exceeding
|
||||
// 14, the TCP should log the error but use 14 instead of the specified
|
||||
// value."
|
||||
if t.original.shiftCnt > maxWindowShift {
|
||||
t.original.shiftCnt = maxWindowShift
|
||||
}
|
||||
if t.reply.shiftCnt > maxWindowShift {
|
||||
t.original.shiftCnt = maxWindowShift
|
||||
}
|
||||
} else {
|
||||
t.original.shiftCnt = 0
|
||||
t.reply.shiftCnt = 0
|
||||
}
|
||||
// Update state informed by this SYN.
|
||||
irs := seqnum.Value(tcp.SequenceNumber())
|
||||
t.reply.una = irs
|
||||
t.reply.nxt = irs.Add(logicalLen(tcp, dataLen, seqnum.Size(t.reply.end) /* end currently holds the receive window size */))
|
||||
t.reply.end <<= t.reply.shiftCnt
|
||||
t.reply.end.UpdateForward(seqnum.Size(irs))
|
||||
|
||||
windowSize := t.original.windowSize(tcp)
|
||||
t.original.end = t.original.una.Add(windowSize)
|
||||
|
||||
// If the ACK was set (it is acceptable), update our unacknowledgement
|
||||
// tracking.
|
||||
if ackPresent {
|
||||
// Advance the "una" and "end" indices of the original stream.
|
||||
if t.original.una.LessThan(ack) {
|
||||
t.original.una = ack
|
||||
}
|
||||
|
||||
if end := ack.Add(seqnum.Size(windowSize)); t.original.end.LessThan(end) {
|
||||
t.original.end = end
|
||||
}
|
||||
}
|
||||
|
||||
// Update handlers so that new calls will be handled by new state.
|
||||
t.handlerReply = allOtherReply
|
||||
t.handlerOriginal = allOtherOriginal
|
||||
|
||||
return ResultAlive
|
||||
}
|
||||
|
||||
// synSentStateOriginal is the state handler for original segments when the
|
||||
// connection is in SYN-SENT state.
|
||||
func synSentStateOriginal(t *TCB, tcp header.TCP, _ int) Result {
|
||||
// Drop original segments that aren't retransmits of the original one.
|
||||
if tcp.Flags() != header.TCPFlagSyn || tcp.SequenceNumber() != uint32(t.original.una) {
|
||||
return ResultDrop
|
||||
}
|
||||
|
||||
// Update the receive window. We only remember the largest value seen.
|
||||
if wnd := seqnum.Value(tcp.WindowSize()); wnd > t.reply.end {
|
||||
t.reply.end = wnd
|
||||
}
|
||||
|
||||
return ResultConnecting
|
||||
}
|
||||
|
||||
// update updates the state of reply and original streams, given the supplied
|
||||
// reply segment. For original segments, this same function can be called with
|
||||
// swapped reply/original streams.
|
||||
func update(tcp header.TCP, reply, original *stream, firstFin **stream, dataLen int) Result {
|
||||
// Ignore segments out of the window.
|
||||
s := seqnum.Value(tcp.SequenceNumber())
|
||||
if !reply.acceptable(s, seqnum.Size(dataLen)) {
|
||||
return ResultAlive
|
||||
}
|
||||
|
||||
flags := tcp.Flags()
|
||||
if flags&header.TCPFlagRst != 0 {
|
||||
reply.rstSeen = true
|
||||
return ResultReset
|
||||
}
|
||||
|
||||
// Ignore segments that don't have the ACK flag, and those with the SYN
|
||||
// flag.
|
||||
if flags&header.TCPFlagAck == 0 || flags&header.TCPFlagSyn != 0 {
|
||||
return ResultAlive
|
||||
}
|
||||
|
||||
// Ignore segments that acknowledge not yet sent data.
|
||||
ack := seqnum.Value(tcp.AckNumber())
|
||||
if original.nxt.LessThan(ack) {
|
||||
return ResultAlive
|
||||
}
|
||||
|
||||
// Advance the "una" and "end" indices of the original stream.
|
||||
if original.una.LessThan(ack) {
|
||||
original.una = ack
|
||||
}
|
||||
|
||||
if end := ack.Add(original.windowSize(tcp)); original.end.LessThan(end) {
|
||||
original.end = end
|
||||
}
|
||||
|
||||
// Advance the "nxt" index of the reply stream.
|
||||
end := s.Add(logicalLen(tcp, dataLen, reply.rwndSize()))
|
||||
if reply.nxt.LessThan(end) {
|
||||
reply.nxt = end
|
||||
}
|
||||
|
||||
// Note the index of the FIN segment. And stash away a pointer to the
|
||||
// first stream to see a FIN.
|
||||
if flags&header.TCPFlagFin != 0 && !reply.finSeen {
|
||||
reply.finSeen = true
|
||||
reply.fin = end - 1
|
||||
|
||||
if *firstFin == nil {
|
||||
*firstFin = reply
|
||||
}
|
||||
}
|
||||
|
||||
return ResultAlive
|
||||
}
|
||||
|
||||
// allOtherReply is the state handler for reply segments in all states
|
||||
// except SYN-SENT.
|
||||
func allOtherReply(t *TCB, tcp header.TCP, dataLen int) Result {
|
||||
return t.adaptResult(update(tcp, &t.reply, &t.original, &t.firstFin, dataLen))
|
||||
}
|
||||
|
||||
// allOtherOriginal is the state handler for original segments in all states
|
||||
// except SYN-SENT.
|
||||
func allOtherOriginal(t *TCB, tcp header.TCP, dataLen int) Result {
|
||||
return t.adaptResult(update(tcp, &t.original, &t.reply, &t.firstFin, dataLen))
|
||||
}
|
||||
|
||||
// streams holds the state of a TCP unidirectional stream.
|
||||
//
|
||||
// +stateify savable
|
||||
type stream struct {
|
||||
// The interval [una, end) is the allowed interval as defined by the
|
||||
// receiver, i.e., anything less than una has already been acknowledged
|
||||
// and anything greater than or equal to end is beyond the receiver
|
||||
// window. The interval [una, nxt) is the acknowledgable range, whose
|
||||
// right edge indicates the sequence number of the next byte to be sent
|
||||
// by the sender, i.e., anything greater than or equal to nxt hasn't
|
||||
// been sent yet.
|
||||
una seqnum.Value
|
||||
nxt seqnum.Value
|
||||
end seqnum.Value
|
||||
|
||||
// finSeen indicates if a FIN has already been sent on this stream.
|
||||
finSeen bool
|
||||
|
||||
// fin is the sequence number of the FIN. It is only valid after finSeen
|
||||
// is set to true.
|
||||
fin seqnum.Value
|
||||
|
||||
// rstSeen indicates if a RST has already been sent on this stream.
|
||||
rstSeen bool
|
||||
|
||||
// shiftCnt is the shift of the window scale of the receiver of the stream,
|
||||
// i.e. in a stream from A to B it is B's receive window scale. It cannot be
|
||||
// greater than maxWindowScale.
|
||||
shiftCnt int
|
||||
}
|
||||
|
||||
// acceptable determines if the segment with the given sequence number and data
|
||||
// length is acceptable, i.e., if it's within the [una, end) window or, in case
|
||||
// the window is zero, if it's a packet with no payload and sequence number
|
||||
// equal to una.
|
||||
func (s *stream) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool {
|
||||
return header.Acceptable(segSeq, segLen, s.una, s.end)
|
||||
}
|
||||
|
||||
// closed determines if the stream has already been closed. This happens when
|
||||
// a FIN has been set by the sender and acknowledged by the receiver.
|
||||
func (s *stream) closed() bool {
|
||||
return s.finSeen && s.fin.LessThan(s.una)
|
||||
}
|
||||
|
||||
// rwndSize returns the stream's receive window size.
|
||||
func (s *stream) rwndSize() seqnum.Size {
|
||||
return s.una.Size(s.end)
|
||||
}
|
||||
|
||||
// windowSize returns the stream's window size accounting for scale.
|
||||
func (s *stream) windowSize(tcp header.TCP) seqnum.Size {
|
||||
return seqnum.Size(tcp.WindowSize()) << s.shiftCnt
|
||||
}
|
||||
|
||||
// logicalLenSyn calculates the logical length of a SYN (without ACK) segment.
|
||||
// It is similar to logicalLen, but does not impose a window size requirement
|
||||
// because of the SYN.
|
||||
func logicalLenSyn(tcp header.TCP, dataLen int) seqnum.Size {
|
||||
length := seqnum.Size(dataLen)
|
||||
flags := tcp.Flags()
|
||||
if flags&header.TCPFlagSyn != 0 {
|
||||
length++
|
||||
}
|
||||
if flags&header.TCPFlagFin != 0 {
|
||||
length++
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
// logicalLen calculates the logical length of the TCP segment.
|
||||
func logicalLen(tcp header.TCP, dataLen int, windowSize seqnum.Size) seqnum.Size {
|
||||
// If the segment is too large, TCP trims the payload per RFC 793 page 70.
|
||||
length := logicalLenSyn(tcp, dataLen)
|
||||
if length > windowSize {
|
||||
length = windowSize
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty returns true if tcb is not initialized.
|
||||
func (t *TCB) IsEmpty() bool {
|
||||
if t.reply != (stream{}) || t.original != (stream{}) {
|
||||
return false
|
||||
}
|
||||
|
||||
if t.firstFin != nil || t.state != ResultDrop {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package tcpconntrack
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (t *TCB) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/tcpconntrack.TCB"
|
||||
}
|
||||
|
||||
func (t *TCB) StateFields() []string {
|
||||
return []string{
|
||||
"reply",
|
||||
"original",
|
||||
"firstFin",
|
||||
"state",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCB) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TCB) StateSave(stateSinkObject state.Sink) {
|
||||
t.beforeSave()
|
||||
stateSinkObject.Save(0, &t.reply)
|
||||
stateSinkObject.Save(1, &t.original)
|
||||
stateSinkObject.Save(2, &t.firstFin)
|
||||
stateSinkObject.Save(3, &t.state)
|
||||
}
|
||||
|
||||
func (t *TCB) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TCB) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &t.reply)
|
||||
stateSourceObject.Load(1, &t.original)
|
||||
stateSourceObject.Load(2, &t.firstFin)
|
||||
stateSourceObject.Load(3, &t.state)
|
||||
}
|
||||
|
||||
func (s *stream) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/tcpconntrack.stream"
|
||||
}
|
||||
|
||||
func (s *stream) StateFields() []string {
|
||||
return []string{
|
||||
"una",
|
||||
"nxt",
|
||||
"end",
|
||||
"finSeen",
|
||||
"fin",
|
||||
"rstSeen",
|
||||
"shiftCnt",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *stream) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.una)
|
||||
stateSinkObject.Save(1, &s.nxt)
|
||||
stateSinkObject.Save(2, &s.end)
|
||||
stateSinkObject.Save(3, &s.finSeen)
|
||||
stateSinkObject.Save(4, &s.fin)
|
||||
stateSinkObject.Save(5, &s.rstSeen)
|
||||
stateSinkObject.Save(6, &s.shiftCnt)
|
||||
}
|
||||
|
||||
func (s *stream) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *stream) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.una)
|
||||
stateSourceObject.Load(1, &s.nxt)
|
||||
stateSourceObject.Load(2, &s.end)
|
||||
stateSourceObject.Load(3, &s.finSeen)
|
||||
stateSourceObject.Load(4, &s.fin)
|
||||
stateSourceObject.Load(5, &s.rstSeen)
|
||||
stateSourceObject.Load(6, &s.shiftCnt)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*TCB)(nil))
|
||||
state.Register((*stream)(nil))
|
||||
}
|
||||
16
pkg/tcpip/transport/transport.go
Normal file
16
pkg/tcpip/transport/transport.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2021 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 transport supports transport protocols.
|
||||
package transport
|
||||
3
pkg/tcpip/transport/transport_state_autogen.go
Normal file
3
pkg/tcpip/transport/transport_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package transport
|
||||
1098
pkg/tcpip/transport/udp/endpoint.go
Normal file
1098
pkg/tcpip/transport/udp/endpoint.go
Normal file
File diff suppressed because it is too large
Load diff
96
pkg/tcpip/transport/udp/endpoint_state.go
Normal file
96
pkg/tcpip/transport/udp/endpoint_state.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2018 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 udp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/log"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
func (p *udpPacket) saveReceivedAt() int64 {
|
||||
return p.receivedAt.UnixNano()
|
||||
}
|
||||
|
||||
// loadReceivedAt is invoked by stateify.
|
||||
func (p *udpPacket) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
p.receivedAt = time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad(ctx context.Context) {
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (e *endpoint) beforeSave() {
|
||||
e.freeze()
|
||||
e.stack.RegisterResumableEndpoint(e)
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *endpoint) Restore(s *stack.Stack) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if err := e.net.Resume(s); err != nil {
|
||||
log.Warningf("Closing the UDP endpoint as it cannot be restored, err: %v", err)
|
||||
e.closeLocked()
|
||||
return
|
||||
}
|
||||
|
||||
// Unfreeze the endpoint to handle packets.
|
||||
e.frozen = false
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
return
|
||||
}
|
||||
e.stack = s
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
// Our saved state had a port, but we don't actually have a
|
||||
// reservation. We need to remove the port from our state, but still
|
||||
// pass it to the reservation machinery.
|
||||
var err tcpip.Error
|
||||
id := e.net.Info().ID
|
||||
id.LocalPort = e.localPort
|
||||
id.RemotePort = e.remotePort
|
||||
id, e.boundBindToDevice, err = e.registerWithStack(e.effectiveNetProtos, id)
|
||||
if err != nil {
|
||||
panic("registering udp endpoint with the stack failed during restore")
|
||||
}
|
||||
e.localPort = id.LocalPort
|
||||
e.remotePort = id.RemotePort
|
||||
default:
|
||||
panic("unhandled state")
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *endpoint) Resume() {
|
||||
e.thaw()
|
||||
}
|
||||
112
pkg/tcpip/transport/udp/forwarder.go
Normal file
112
pkg/tcpip/transport/udp/forwarder.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// 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 udp
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// ForwarderHandler handles incoming requests. Returning true marks the
|
||||
// request as handled, returning false marks the request as unhandled.
|
||||
// Stack may send an ICMP port unreachable message for unhandled requests.
|
||||
type ForwarderHandler func(*ForwarderRequest) (handled bool)
|
||||
|
||||
// Forwarder is a session request forwarder, which allows clients to decide
|
||||
// what to do with a session request, for example: ignore it, or process it.
|
||||
//
|
||||
// The canonical way of using it is to pass the Forwarder.HandlePacket function
|
||||
// to stack.SetTransportProtocolHandler.
|
||||
type Forwarder struct {
|
||||
handler ForwarderHandler
|
||||
|
||||
stack *stack.Stack
|
||||
}
|
||||
|
||||
// NewForwarder allocates and initializes a new forwarder.
|
||||
func NewForwarder(s *stack.Stack, handler ForwarderHandler) *Forwarder {
|
||||
return &Forwarder{
|
||||
stack: s,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePacket handles all packets.
|
||||
//
|
||||
// This function is expected to be passed as an argument to the
|
||||
// stack.SetTransportProtocolHandler function.
|
||||
func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
return f.handler(&ForwarderRequest{
|
||||
stack: f.stack,
|
||||
id: id,
|
||||
pkt: pkt.Clone(),
|
||||
})
|
||||
}
|
||||
|
||||
// ForwarderRequest represents a session request received by the forwarder and
|
||||
// passed to the client. Clients may optionally create an endpoint to represent
|
||||
// it via CreateEndpoint.
|
||||
type ForwarderRequest struct {
|
||||
stack *stack.Stack
|
||||
id stack.TransportEndpointID
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
// ID returns the 4-tuple (src address, src port, dst address, dst port) that
|
||||
// represents the session request.
|
||||
func (r *ForwarderRequest) ID() stack.TransportEndpointID {
|
||||
return r.id
|
||||
}
|
||||
|
||||
// CreateEndpoint creates a connected UDP endpoint for the session request.
|
||||
func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
ep := newEndpoint(r.stack, r.pkt.NetworkProtocolNumber, queue)
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
netHdr := r.pkt.Network()
|
||||
if err := ep.net.Bind(tcpip.FullAddress{NIC: r.pkt.NICID, Addr: netHdr.DestinationAddress(), Port: r.id.LocalPort}); err != nil {
|
||||
ep.closeLocked()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ep.net.Connect(tcpip.FullAddress{NIC: r.pkt.NICID, Addr: netHdr.SourceAddress(), Port: r.id.RemotePort}); err != nil {
|
||||
ep.closeLocked()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.stack.RegisterTransportEndpoint([]tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}, ProtocolNumber, r.id, ep, ep.portFlags, tcpip.NICID(ep.ops.GetBindToDevice())); err != nil {
|
||||
ep.closeLocked()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ep.localPort = r.id.LocalPort
|
||||
ep.remotePort = r.id.RemotePort
|
||||
ep.effectiveNetProtos = []tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}
|
||||
ep.boundPortFlags = ep.portFlags
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
ep.rcvReady = true
|
||||
ep.rcvMu.Unlock()
|
||||
|
||||
ep.HandlePacket(r.id, r.pkt)
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
func (r *ForwarderRequest) Packet() *stack.PacketBuffer {
|
||||
return r.pkt
|
||||
}
|
||||
138
pkg/tcpip/transport/udp/protocol.go
Normal file
138
pkg/tcpip/transport/udp/protocol.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2018 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 udp contains the implementation of the UDP transport protocol.
|
||||
package udp
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header/parse"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/raw"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtocolNumber is the udp protocol number.
|
||||
ProtocolNumber = header.UDPProtocolNumber
|
||||
|
||||
// MinBufferSize is the smallest size of a receive or send buffer.
|
||||
MinBufferSize = 4 << 10 // 4KiB bytes.
|
||||
|
||||
// DefaultSendBufferSize is the default size of the send buffer for
|
||||
// an endpoint.
|
||||
DefaultSendBufferSize = 32 << 10 // 32KiB
|
||||
|
||||
// DefaultReceiveBufferSize is the default size of the receive buffer
|
||||
// for an endpoint.
|
||||
DefaultReceiveBufferSize = 32 << 10 // 32KiB
|
||||
|
||||
// MaxBufferSize is the largest size a receive/send buffer can grow to.
|
||||
MaxBufferSize = 4 << 20 // 4MiB
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type protocol struct {
|
||||
stack *stack.Stack
|
||||
}
|
||||
|
||||
// Number returns the udp protocol number.
|
||||
func (*protocol) Number() tcpip.TransportProtocolNumber {
|
||||
return ProtocolNumber
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new udp endpoint.
|
||||
func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return newEndpoint(p.stack, netProto, waiterQueue), nil
|
||||
}
|
||||
|
||||
// NewRawEndpoint creates a new raw UDP endpoint. It implements
|
||||
// stack.TransportProtocol.NewRawEndpoint.
|
||||
func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
return raw.NewEndpoint(p.stack, netProto, header.UDPProtocolNumber, waiterQueue)
|
||||
}
|
||||
|
||||
// MinimumPacketSize returns the minimum valid udp packet size.
|
||||
func (*protocol) MinimumPacketSize() int {
|
||||
return header.UDPMinimumSize
|
||||
}
|
||||
|
||||
// ParsePorts returns the source and destination ports stored in the given udp
|
||||
// packet.
|
||||
func (*protocol) ParsePorts(v []byte) (src, dst uint16, err tcpip.Error) {
|
||||
h := header.UDP(v)
|
||||
return h.SourcePort(), h.DestinationPort(), nil
|
||||
}
|
||||
|
||||
// HandleUnknownDestinationPacket handles packets that are targeted at this
|
||||
// protocol but don't match any existing endpoint.
|
||||
func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
|
||||
hdr := header.UDP(pkt.TransportHeader().Slice())
|
||||
netHdr := pkt.Network()
|
||||
lengthValid, csumValid := header.UDPValid(
|
||||
hdr,
|
||||
func() uint16 { return pkt.Data().Checksum() },
|
||||
uint16(pkt.Data().Size()),
|
||||
pkt.NetworkProtocolNumber,
|
||||
netHdr.SourceAddress(),
|
||||
netHdr.DestinationAddress(),
|
||||
pkt.RXChecksumValidated)
|
||||
if !lengthValid {
|
||||
p.stack.Stats().UDP.MalformedPacketsReceived.Increment()
|
||||
return stack.UnknownDestinationPacketMalformed
|
||||
}
|
||||
|
||||
if !csumValid {
|
||||
p.stack.Stats().UDP.ChecksumErrors.Increment()
|
||||
return stack.UnknownDestinationPacketMalformed
|
||||
}
|
||||
|
||||
return stack.UnknownDestinationPacketUnhandled
|
||||
}
|
||||
|
||||
// SetOption implements stack.TransportProtocol.SetOption.
|
||||
func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Option implements stack.TransportProtocol.Option.
|
||||
func (*protocol) Option(tcpip.GettableTransportProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Close implements stack.TransportProtocol.Close.
|
||||
func (*protocol) Close() {}
|
||||
|
||||
// Wait implements stack.TransportProtocol.Wait.
|
||||
func (*protocol) Wait() {}
|
||||
|
||||
// Pause implements stack.TransportProtocol.Pause.
|
||||
func (*protocol) Pause() {}
|
||||
|
||||
// Resume implements stack.TransportProtocol.Resume.
|
||||
func (*protocol) Resume() {}
|
||||
|
||||
// Restore implements stack.TransportProtocol.Restore.
|
||||
func (*protocol) Restore() {}
|
||||
|
||||
// Parse implements stack.TransportProtocol.Parse.
|
||||
func (*protocol) Parse(pkt *stack.PacketBuffer) bool {
|
||||
return parse.UDP(pkt)
|
||||
}
|
||||
|
||||
// NewProtocol returns a UDP transport protocol.
|
||||
func NewProtocol(s *stack.Stack) stack.TransportProtocol {
|
||||
return &protocol{stack: s}
|
||||
}
|
||||
239
pkg/tcpip/transport/udp/udp_packet_list.go
Normal file
239
pkg/tcpip/transport/udp/udp_packet_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package udp
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type udpPacketElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (udpPacketElementMapper) linkerFor(elem *udpPacket) *udpPacket { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type udpPacketList struct {
|
||||
head *udpPacket
|
||||
tail *udpPacket
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *udpPacketList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) Front() *udpPacket {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) Back() *udpPacket {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (udpPacketElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) PushFront(e *udpPacket) {
|
||||
linker := udpPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
udpPacketElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) PushFrontList(m *udpPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
udpPacketElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
udpPacketElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) PushBack(e *udpPacket) {
|
||||
linker := udpPacketElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
udpPacketElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) PushBackList(m *udpPacketList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
udpPacketElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
udpPacketElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) InsertAfter(b, e *udpPacket) {
|
||||
bLinker := udpPacketElementMapper{}.linkerFor(b)
|
||||
eLinker := udpPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
udpPacketElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) InsertBefore(a, e *udpPacket) {
|
||||
aLinker := udpPacketElementMapper{}.linkerFor(a)
|
||||
eLinker := udpPacketElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
udpPacketElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *udpPacketList) Remove(e *udpPacket) {
|
||||
linker := udpPacketElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
udpPacketElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
udpPacketElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type udpPacketEntry struct {
|
||||
next *udpPacket
|
||||
prev *udpPacket
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *udpPacketEntry) Next() *udpPacket {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *udpPacketEntry) Prev() *udpPacket {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *udpPacketEntry) SetNext(elem *udpPacket) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *udpPacketEntry) SetPrev(elem *udpPacket) {
|
||||
e.prev = elem
|
||||
}
|
||||
225
pkg/tcpip/transport/udp/udp_state_autogen.go
Normal file
225
pkg/tcpip/transport/udp/udp_state_autogen.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package udp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (p *udpPacket) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/udp.udpPacket"
|
||||
}
|
||||
|
||||
func (p *udpPacket) StateFields() []string {
|
||||
return []string{
|
||||
"udpPacketEntry",
|
||||
"netProto",
|
||||
"senderAddress",
|
||||
"destinationAddress",
|
||||
"packetInfo",
|
||||
"pkt",
|
||||
"receivedAt",
|
||||
"tosOrTClass",
|
||||
"ttlOrHopLimit",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *udpPacket) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *udpPacket) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
stateSinkObject.SaveValue(6, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.udpPacketEntry)
|
||||
stateSinkObject.Save(1, &p.netProto)
|
||||
stateSinkObject.Save(2, &p.senderAddress)
|
||||
stateSinkObject.Save(3, &p.destinationAddress)
|
||||
stateSinkObject.Save(4, &p.packetInfo)
|
||||
stateSinkObject.Save(5, &p.pkt)
|
||||
stateSinkObject.Save(7, &p.tosOrTClass)
|
||||
stateSinkObject.Save(8, &p.ttlOrHopLimit)
|
||||
}
|
||||
|
||||
func (p *udpPacket) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *udpPacket) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.udpPacketEntry)
|
||||
stateSourceObject.Load(1, &p.netProto)
|
||||
stateSourceObject.Load(2, &p.senderAddress)
|
||||
stateSourceObject.Load(3, &p.destinationAddress)
|
||||
stateSourceObject.Load(4, &p.packetInfo)
|
||||
stateSourceObject.Load(5, &p.pkt)
|
||||
stateSourceObject.Load(7, &p.tosOrTClass)
|
||||
stateSourceObject.Load(8, &p.ttlOrHopLimit)
|
||||
stateSourceObject.LoadValue(6, new(int64), func(y any) { p.loadReceivedAt(ctx, y.(int64)) })
|
||||
}
|
||||
|
||||
func (e *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/udp.endpoint"
|
||||
}
|
||||
|
||||
func (e *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"DefaultSocketOptionsHandler",
|
||||
"stack",
|
||||
"waiterQueue",
|
||||
"net",
|
||||
"stats",
|
||||
"ops",
|
||||
"rcvReady",
|
||||
"rcvList",
|
||||
"rcvBufSize",
|
||||
"rcvClosed",
|
||||
"lastError",
|
||||
"portFlags",
|
||||
"boundBindToDevice",
|
||||
"boundPortFlags",
|
||||
"readShutdown",
|
||||
"effectiveNetProtos",
|
||||
"frozen",
|
||||
"localPort",
|
||||
"remotePort",
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSinkObject.Save(1, &e.stack)
|
||||
stateSinkObject.Save(2, &e.waiterQueue)
|
||||
stateSinkObject.Save(3, &e.net)
|
||||
stateSinkObject.Save(4, &e.stats)
|
||||
stateSinkObject.Save(5, &e.ops)
|
||||
stateSinkObject.Save(6, &e.rcvReady)
|
||||
stateSinkObject.Save(7, &e.rcvList)
|
||||
stateSinkObject.Save(8, &e.rcvBufSize)
|
||||
stateSinkObject.Save(9, &e.rcvClosed)
|
||||
stateSinkObject.Save(10, &e.lastError)
|
||||
stateSinkObject.Save(11, &e.portFlags)
|
||||
stateSinkObject.Save(12, &e.boundBindToDevice)
|
||||
stateSinkObject.Save(13, &e.boundPortFlags)
|
||||
stateSinkObject.Save(14, &e.readShutdown)
|
||||
stateSinkObject.Save(15, &e.effectiveNetProtos)
|
||||
stateSinkObject.Save(16, &e.frozen)
|
||||
stateSinkObject.Save(17, &e.localPort)
|
||||
stateSinkObject.Save(18, &e.remotePort)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.DefaultSocketOptionsHandler)
|
||||
stateSourceObject.Load(1, &e.stack)
|
||||
stateSourceObject.Load(2, &e.waiterQueue)
|
||||
stateSourceObject.Load(3, &e.net)
|
||||
stateSourceObject.Load(4, &e.stats)
|
||||
stateSourceObject.Load(5, &e.ops)
|
||||
stateSourceObject.Load(6, &e.rcvReady)
|
||||
stateSourceObject.Load(7, &e.rcvList)
|
||||
stateSourceObject.Load(8, &e.rcvBufSize)
|
||||
stateSourceObject.Load(9, &e.rcvClosed)
|
||||
stateSourceObject.Load(10, &e.lastError)
|
||||
stateSourceObject.Load(11, &e.portFlags)
|
||||
stateSourceObject.Load(12, &e.boundBindToDevice)
|
||||
stateSourceObject.Load(13, &e.boundPortFlags)
|
||||
stateSourceObject.Load(14, &e.readShutdown)
|
||||
stateSourceObject.Load(15, &e.effectiveNetProtos)
|
||||
stateSourceObject.Load(16, &e.frozen)
|
||||
stateSourceObject.Load(17, &e.localPort)
|
||||
stateSourceObject.Load(18, &e.remotePort)
|
||||
stateSourceObject.AfterLoad(func() { e.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (p *protocol) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/udp.protocol"
|
||||
}
|
||||
|
||||
func (p *protocol) StateFields() []string {
|
||||
return []string{
|
||||
"stack",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protocol) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.stack)
|
||||
}
|
||||
|
||||
func (p *protocol) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.stack)
|
||||
}
|
||||
|
||||
func (l *udpPacketList) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/udp.udpPacketList"
|
||||
}
|
||||
|
||||
func (l *udpPacketList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *udpPacketList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *udpPacketList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *udpPacketList) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *udpPacketList) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *udpPacketEntry) StateTypeName() string {
|
||||
return "pkg/tcpip/transport/udp.udpPacketEntry"
|
||||
}
|
||||
|
||||
func (e *udpPacketEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *udpPacketEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *udpPacketEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *udpPacketEntry) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *udpPacketEntry) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*udpPacket)(nil))
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*protocol)(nil))
|
||||
state.Register((*udpPacketList)(nil))
|
||||
state.Register((*udpPacketEntry)(nil))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue