snapshot: sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1 + SPEC 048 guard
Обновление снапшота с v0.0.0-20250811.0 на пин, которого требует sing-box после мержа 235 коммитов (upstream d620bbbf2 "Update gvisor to 20260727.0"). Прежний снапшот был взят 2026-08-04 ровно с той версии, на которой тогда стоял апстрим; разрыв возник 2026-08-05 вместе с его бампом. За год апстрим-gvisor изменил ~14 000 строк в 292 файлах. Значимое для нас — сетевой стек: tcp/connect.go (PMTU-discovery + исправление начального RTT/RTO: раньше задержка ACK внутри стека завышала стартовый таймаут на несколько RTT), tcp/snd.go, tcp/rcv.go, stack/conntrack.go, stack/packet_buffer.go. Всего 30 файлов в TCP и 37 в stack. Баг SPEC 048 апстрим НЕ исправил — проверено по коду новой версии: handleConnecting по-прежнему проверяет состояние endpoint'а, но не ep.h, а performHandshake так же зануляет h и отпускает мьютекс до Close(). Поэтому guard перенесён (12 строк) вместе со своим тестом (45 строк). Red/green проверен на новой базе: без guard'а тест падает с той же nil-паникой, что в полевом крашдампе; с ним зелёный.
This commit is contained in:
parent
ffebe42860
commit
117243aa02
293 changed files with 16413 additions and 2842 deletions
|
|
@ -74,8 +74,11 @@ type congestionControl interface {
|
|||
// Update is invoked when processing inbound acks. It's passed the
|
||||
// number of packet's that were acked by the most recent cumulative
|
||||
// acknowledgement. rtt is the round-trip time, or is set to unknownRTT
|
||||
// (above) to indicate the time is unknown.
|
||||
Update(packetsAcked int, rtt time.Duration)
|
||||
// (above) to indicate the time is unknown. ackTime is the time the
|
||||
// processed ACK arrived at the stack (its ingress timestamp), used for
|
||||
// arrival-anchored timing such as CUBIC HyStart's ACK-train detection so
|
||||
// that an ACK delayed inside the stack does not distort it.
|
||||
Update(packetsAcked int, rtt time.Duration, ackTime tcpip.MonotonicTime)
|
||||
|
||||
// PostRecovery is invoked when the sender is exiting a fast retransmit/
|
||||
// recovery phase. This provides congestion control algorithms a way
|
||||
|
|
@ -96,6 +99,7 @@ type lossRecovery interface {
|
|||
|
||||
// sender holds the state necessary to send TCP segments.
|
||||
//
|
||||
// +checklocksalias:rc.snd.ep.mu=ep.mu
|
||||
// +stateify savable
|
||||
type sender struct {
|
||||
// +checklocks:ep.mu
|
||||
|
|
@ -103,6 +107,11 @@ type sender struct {
|
|||
|
||||
ep *Endpoint
|
||||
|
||||
// finSent is set when the FIN segment is actually transmitted.
|
||||
// The endpoint may be in FIN_WAIT1/LAST_ACK while the FIN is still
|
||||
// queued behind blocked data; finSent guards closing ACK completions.
|
||||
finSent bool
|
||||
|
||||
// lr is the loss recovery algorithm used by the sender.
|
||||
lr lossRecovery
|
||||
|
||||
|
|
@ -242,13 +251,13 @@ type rtt struct {
|
|||
}
|
||||
|
||||
// +checklocks:ep.mu
|
||||
func newSender(ep *Endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint16, sndWndScale int) *sender {
|
||||
func initSender(ep *Endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint16, sndWndScale int) {
|
||||
// The sender MUST reduce the TCP data length to account for any IP or
|
||||
// TCP options that it is including in the packets that it sends.
|
||||
// See: https://tools.ietf.org/html/rfc6691#section-2
|
||||
maxPayloadSize := int(mss) - ep.maxOptionSize()
|
||||
|
||||
s := &sender{
|
||||
ep.snd = &sender{
|
||||
ep: ep,
|
||||
TCPSenderState: TCPSenderState{
|
||||
SndWnd: sndWnd,
|
||||
|
|
@ -271,59 +280,50 @@ func newSender(ep *Endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint
|
|||
set: make(map[*segment]struct{}),
|
||||
},
|
||||
}
|
||||
return newSenderHelper(ep, iss, irs, sndWnd, mss, sndWndScale, maxPayloadSize, s)
|
||||
}
|
||||
|
||||
// newSenderHelper exists to sate checklocks.
|
||||
//
|
||||
// +checklocks:ep.mu
|
||||
// +checklocksalias:s.ep.mu=ep.mu
|
||||
func newSenderHelper(ep *Endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint16, sndWndScale int, maxPayloadSize int, s *sender) *sender {
|
||||
if s.gso {
|
||||
s.ep.gso.MSS = uint16(maxPayloadSize)
|
||||
if ep.snd.gso {
|
||||
ep.snd.ep.gso.MSS = uint16(maxPayloadSize)
|
||||
}
|
||||
|
||||
s.cc = s.initCongestionControl(ep.cc)
|
||||
s.lr = s.initLossRecovery()
|
||||
s.rc.init(s, iss)
|
||||
ep.snd.cc = ep.snd.initCongestionControl(ep.cc)
|
||||
ep.snd.lr = ep.snd.initLossRecovery()
|
||||
ep.snd.rc.init(ep.snd, iss)
|
||||
|
||||
// A negative sndWndScale means that no scaling is in use, otherwise we
|
||||
// store the scaling value.
|
||||
if sndWndScale > 0 {
|
||||
s.SndWndScale = uint8(sndWndScale)
|
||||
ep.snd.SndWndScale = uint8(sndWndScale)
|
||||
}
|
||||
|
||||
s.resendTimer.init(s.ep.stack.Clock(), timerHandler(s.ep, s.retransmitTimerExpired))
|
||||
s.reorderTimer.init(s.ep.stack.Clock(), timerHandler(s.ep, s.rc.reorderTimerExpired))
|
||||
s.probeTimer.init(s.ep.stack.Clock(), timerHandler(s.ep, s.probeTimerExpired))
|
||||
s.corkTimer.init(s.ep.stack.Clock(), timerHandler(s.ep, s.corkTimerExpired))
|
||||
ep.snd.resendTimer.init(ep.snd.ep.stack.Clock(), timerHandler(ep.snd.ep, ep.snd.retransmitTimerExpired))
|
||||
ep.snd.reorderTimer.init(ep.snd.ep.stack.Clock(), timerHandler(ep.snd.ep, ep.snd.rc.reorderTimerExpired))
|
||||
ep.snd.probeTimer.init(ep.snd.ep.stack.Clock(), timerHandler(ep.snd.ep, ep.snd.probeTimerExpired))
|
||||
ep.snd.corkTimer.init(ep.snd.ep.stack.Clock(), timerHandler(ep.snd.ep, ep.snd.corkTimerExpired))
|
||||
|
||||
s.updateMaxPayloadSize(int(ep.route.MTU()), 0)
|
||||
ep.snd.updateMaxPayloadSize(int(ep.snd.ep.route.MTU()), 0)
|
||||
// Initialize SACK Scoreboard after updating max payload size as we use
|
||||
// the maxPayloadSize as the smss when determining if a segment is lost
|
||||
// etc.
|
||||
s.ep.scoreboard = NewSACKScoreboard(uint16(s.MaxPayloadSize), iss)
|
||||
ep.snd.ep.scoreboard = NewSACKScoreboard(uint16(ep.snd.MaxPayloadSize), iss)
|
||||
|
||||
// Get Stack wide config.
|
||||
var minRTO tcpip.TCPMinRTOOption
|
||||
if err := ep.stack.TransportProtocolOption(ProtocolNumber, &minRTO); err != nil {
|
||||
if err := ep.snd.ep.stack.TransportProtocolOption(ProtocolNumber, &minRTO); err != nil {
|
||||
panic(fmt.Sprintf("unable to get minRTO from stack: %s", err))
|
||||
}
|
||||
s.minRTO = time.Duration(minRTO)
|
||||
ep.snd.minRTO = time.Duration(minRTO)
|
||||
|
||||
var maxRTO tcpip.TCPMaxRTOOption
|
||||
if err := ep.stack.TransportProtocolOption(ProtocolNumber, &maxRTO); err != nil {
|
||||
if err := ep.snd.ep.stack.TransportProtocolOption(ProtocolNumber, &maxRTO); err != nil {
|
||||
panic(fmt.Sprintf("unable to get maxRTO from stack: %s", err))
|
||||
}
|
||||
s.maxRTO = time.Duration(maxRTO)
|
||||
ep.snd.maxRTO = time.Duration(maxRTO)
|
||||
|
||||
var maxRetries tcpip.TCPMaxRetriesOption
|
||||
if err := ep.stack.TransportProtocolOption(ProtocolNumber, &maxRetries); err != nil {
|
||||
if err := ep.snd.ep.stack.TransportProtocolOption(ProtocolNumber, &maxRetries); err != nil {
|
||||
panic(fmt.Sprintf("unable to get maxRetries from stack: %s", err))
|
||||
}
|
||||
s.maxRetries = uint32(maxRetries)
|
||||
|
||||
return s
|
||||
ep.snd.maxRetries = uint32(maxRetries)
|
||||
}
|
||||
|
||||
// initCongestionControl initializes the specified congestion control module and
|
||||
|
|
@ -434,6 +434,13 @@ func (s *sender) sendAck() {
|
|||
//
|
||||
// +checklocks:s.ep.mu
|
||||
func (s *sender) updateRTO(rtt time.Duration) {
|
||||
// A negative RTT sample is nonsensical and would skew SRTT/RTTVar (and thus
|
||||
// RTO). RTT samples are now anchored to a segment's ingress time, which is
|
||||
// monotonic and never after the corresponding send time, so this should not
|
||||
// occur; guard defensively rather than corrupt the estimator.
|
||||
if rtt < 0 {
|
||||
return
|
||||
}
|
||||
s.rtt.Lock()
|
||||
if !s.rtt.TCPRTTState.SRTTInited {
|
||||
s.rtt.TCPRTTState.RTTVar = rtt / 2
|
||||
|
|
@ -902,15 +909,9 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se
|
|||
}
|
||||
seg.flags = header.TCPFlagAck | header.TCPFlagFin
|
||||
segEnd = seg.sequenceNumber.Add(1)
|
||||
// Update the state to reflect that we have now
|
||||
// queued a FIN.
|
||||
s.ep.updateConnDirectionState(connDirectionStateSndClosed)
|
||||
switch s.ep.EndpointState() {
|
||||
case StateCloseWait:
|
||||
s.ep.setEndpointState(StateLastAck)
|
||||
default:
|
||||
s.ep.setEndpointState(StateFinWait1)
|
||||
}
|
||||
// FIN is now being transmitted; mark it so the receiver can tell
|
||||
// a data-only ACK apart from one that acknowledges our FIN.
|
||||
s.finSent = true
|
||||
} else {
|
||||
// We're sending a non-FIN segment.
|
||||
if seg.flags&header.TCPFlagFin != 0 {
|
||||
|
|
@ -1013,6 +1014,7 @@ func (s *sender) sendZeroWindowProbe() {
|
|||
// we re-send an ACKed byte to goad the receiver into responding.
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(zeroProbeJunk),
|
||||
Mark: s.ep.ops.GetMark(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
s.sendSegmentFromPacketBuffer(pkt, header.TCPFlagAck, s.SndUna-1)
|
||||
|
|
@ -1518,13 +1520,16 @@ func (s *sender) inRecovery() bool {
|
|||
// handleRcvdSegment is called when a segment is received; it is responsible for
|
||||
// updating the send-related state.
|
||||
// +checklocks:s.ep.mu
|
||||
// +checklocksalias:s.rc.snd.ep.mu=s.ep.mu
|
||||
func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
||||
bestRTT := unknownRTT
|
||||
|
||||
// Check if we can extract an RTT measurement from this ack.
|
||||
// Check if we can extract an RTT measurement from this ack. Measure against
|
||||
// the ACK's ingress time (rcvdSeg.rcvdTime), not the current clock: if the
|
||||
// ACK was delayed inside the stack before being processed (e.g. queued while
|
||||
// the application held the endpoint lock during a Write), using the
|
||||
// processing time would inflate the RTT sample and thus SRTT/RTO.
|
||||
if !rcvdSeg.parsedOptions.TS && s.RTTMeasureSeqNum.LessThan(rcvdSeg.ackNumber) {
|
||||
bestRTT = s.ep.stack.Clock().NowMonotonic().Sub(s.RTTMeasureTime)
|
||||
bestRTT = rcvdSeg.rcvdTime.Sub(s.RTTMeasureTime)
|
||||
s.updateRTO(bestRTT)
|
||||
s.RTTMeasureSeqNum = s.SndNxt
|
||||
}
|
||||
|
|
@ -1627,7 +1632,10 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
|||
// some new data, i.e., only if it advances the left edge of
|
||||
// the send window.
|
||||
if s.ep.SendTSOk && rcvdSeg.parsedOptions.TSEcr != 0 {
|
||||
tsRTT := s.ep.elapsed(s.ep.stack.Clock().NowMonotonic(), rcvdSeg.parsedOptions.TSEcr)
|
||||
// Compute elapsed time from the ACK's ingress time, not the current
|
||||
// clock, so an ACK delayed inside the stack before processing does
|
||||
// not inflate the timestamp-based RTT sample (and thus SRTT/RTO).
|
||||
tsRTT := s.ep.elapsed(rcvdSeg.rcvdTime, rcvdSeg.parsedOptions.TSEcr)
|
||||
s.updateRTO(tsRTT)
|
||||
// Following Linux, prefer RTT computed from ACKs to TSEcr because,
|
||||
// "broken middle-boxes or peers may corrupt TS-ECR fields"
|
||||
|
|
@ -1707,7 +1715,7 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
|||
// If we are not in fast recovery then update the congestion
|
||||
// window based on the number of acknowledged packets.
|
||||
if !s.FastRecovery.Active {
|
||||
s.cc.Update(originalOutstanding-s.Outstanding, bestRTT)
|
||||
s.cc.Update(originalOutstanding-s.Outstanding, bestRTT, rcvdSeg.rcvdTime)
|
||||
if s.FastRecovery.Last.LessThan(s.SndUna) {
|
||||
s.state = tcpip.Open
|
||||
// Update RACK when we are exiting fast or RTO
|
||||
|
|
@ -1822,8 +1830,6 @@ func (s *sender) sendSegment(seg *segment) tcpip.Error {
|
|||
// sendSegmentFromPacketBuffer sends a new segment containing the given payload,
|
||||
// flags and sequence number.
|
||||
// +checklocks:s.ep.mu
|
||||
// +checklocksalias:s.ep.rcv.ep.mu=s.ep.mu
|
||||
// +checklocksalias:s.ep.rcv.ep.snd.ep.mu=s.ep.mu
|
||||
func (s *sender) sendSegmentFromPacketBuffer(pkt *stack.PacketBuffer, flags header.TCPFlags, seq seqnum.Value) tcpip.Error {
|
||||
s.LastSendTime = s.ep.stack.Clock().NowMonotonic()
|
||||
if seq == s.RTTMeasureSeqNum {
|
||||
|
|
@ -1845,9 +1851,6 @@ func (s *sender) sendSegmentFromPacketBuffer(pkt *stack.PacketBuffer, flags head
|
|||
|
||||
// sendEmptySegment sends a new empty segment, flags and sequence number.
|
||||
// +checklocks:s.ep.mu
|
||||
// +checklocksalias:s.ep.rcv.ep.snd.ep.mu=s.ep.mu
|
||||
// +checklocksalias:s.ep.rcv.ep.mu=s.ep.mu
|
||||
// +checklocksalias:s.ep.snd.ep.mu=s.ep.mu
|
||||
func (s *sender) sendEmptySegment(flags header.TCPFlags, seq seqnum.Value) tcpip.Error {
|
||||
s.LastSendTime = s.ep.stack.Clock().NowMonotonic()
|
||||
if seq == s.RTTMeasureSeqNum {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue