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
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))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue