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
|
|
@ -654,11 +654,29 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
|||
|
||||
case header.ICMPv6EchoRequest:
|
||||
received.echoRequest.Increment()
|
||||
if len(h) < header.ICMPv6EchoMinimumSize {
|
||||
received.invalid.Increment()
|
||||
replyPayload := pkt.Data().ToBuffer()
|
||||
replyHeader := make([]byte, header.ICMPv6EchoMinimumSize)
|
||||
copy(replyHeader, h[:header.ICMPv6EchoMinimumSize])
|
||||
|
||||
// It's possible that a raw socket or per-stack default handler expects
|
||||
// to receive this packet.
|
||||
defaultHandlerHandled := false
|
||||
if dispatcher, ok := e.dispatcher.(stack.TransportDispatcherWithDefaultHandlerResult); ok {
|
||||
_, defaultHandlerHandled = dispatcher.DeliverTransportPacketWithDefaultHandlerResult(header.ICMPv6ProtocolNumber, pkt)
|
||||
} else {
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
|
||||
}
|
||||
pkt = nil
|
||||
|
||||
// Skip the built-in ICMP echo reply if the request was consumed by a
|
||||
// per-stack default handler.
|
||||
if defaultHandlerHandled {
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
|
||||
|
||||
e.sendICMPEchoReply(replyPayload, replyHeader, srcAddr, dstAddr, iph)
|
||||
|
||||
case header.ICMPv6EchoReply:
|
||||
received.echoReply.Increment()
|
||||
if len(h) < header.ICMPv6EchoMinimumSize {
|
||||
|
|
@ -666,6 +684,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
|||
return
|
||||
}
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
|
||||
|
||||
case header.ICMPv6TimeExceeded:
|
||||
received.timeExceeded.Increment()
|
||||
|
||||
|
|
@ -852,6 +871,62 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
|||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) sendICMPEchoReply(replyPayload buffer.Buffer, replyHeader []byte, srcAddr, dstAddr tcpip.Address, ipHdr header.IPv6) {
|
||||
sent := e.stats.icmp.packetsSent
|
||||
|
||||
// As per RFC 4291 section 2.7, multicast addresses must not be used as
|
||||
// source addresses in IPv6 packets.
|
||||
localAddr := dstAddr
|
||||
if header.IsV6MulticastAddress(dstAddr) {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, srcAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
if !e.protocol.allowICMPReply(header.ICMPv6EchoReply) {
|
||||
sent.rateLimited.Increment()
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
|
||||
Payload: replyPayload,
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
|
||||
replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
copy(icmp, replyHeader)
|
||||
icmp.SetType(header.ICMPv6EchoReply)
|
||||
replyData := replyPkt.Data()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
PayloadCsum: replyData.Checksum(),
|
||||
PayloadLen: replyData.Size(),
|
||||
}))
|
||||
replyTClass, _ := ipHdr.TOS()
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: r.DefaultTTL(),
|
||||
// Even though RFC 4443 does not mention anything about it, Linux uses the
|
||||
// TrafficClass of the received echo request when replying.
|
||||
// https://github.com/torvalds/linux/blob/0280e3c58f9/net/ipv6/icmp.c#L797
|
||||
TOS: replyTClass,
|
||||
}, replyPkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.echoReply.Increment()
|
||||
}
|
||||
|
||||
// LinkAddressProtocol implements stack.LinkAddressResolver.
|
||||
func (*endpoint) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return header.IPv6ProtocolNumber
|
||||
|
|
|
|||
|
|
@ -674,7 +674,7 @@ func (e *endpoint) Disable() {
|
|||
}
|
||||
|
||||
func (e *endpoint) disableLocked() {
|
||||
if !e.Enabled() {
|
||||
if !e.isEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -730,6 +730,11 @@ func (e *endpoint) MTU() uint32 {
|
|||
return networkMTU
|
||||
}
|
||||
|
||||
// EndpointHeaderSize returns the size necessary for the IPv6 header.
|
||||
func (e *endpoint) EndpointHeaderSize() uint32 {
|
||||
return header.IPv6MinimumSize
|
||||
}
|
||||
|
||||
// MaxHeaderLength returns the maximum length needed by ipv6 headers (and
|
||||
// underlying protocols).
|
||||
func (e *endpoint) MaxHeaderLength() uint16 {
|
||||
|
|
@ -819,15 +824,23 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
|
|||
return err
|
||||
}
|
||||
|
||||
// iptables filtering. All packets that reach here are locally
|
||||
// generated.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
stk := e.protocol.stack
|
||||
// iptables filtering. All packets that reach here are locally generated.
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesOutputDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
// TODO: b/486197011 - Add support for NAT re-routing in IPv6.
|
||||
if !nft.CheckOutput(pkt, r, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If the packet is manipulated as per DNAT Output rules, handle packet
|
||||
// based on destination address and do not send the packet to link
|
||||
// layer.
|
||||
|
|
@ -858,15 +871,23 @@ func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer, protocol
|
|||
return nil
|
||||
}
|
||||
|
||||
stk := e.protocol.stack
|
||||
// Postrouting NAT can only change the source address, and does not alter the
|
||||
// route or outgoing interface of the packet.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesPostroutingDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPostrouting(pkt, r, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
stats := e.stats.ip
|
||||
networkMTU, err := calculateNetworkMTU(e.nic.MTU(), uint32(len(pkt.NetworkHeader().Slice())))
|
||||
if err != nil {
|
||||
|
|
@ -1007,6 +1028,13 @@ func (e *endpoint) forwardUnicastPacket(pkt *stack.PacketBuffer) ip.ForwardingEr
|
|||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckForward(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The packet originally arrived on e so provide its NIC as the input NIC.
|
||||
ep.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
|
||||
return nil
|
||||
|
|
@ -1048,6 +1076,13 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
|||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckForward(pkt, route, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hopLimit := h.HopLimit()
|
||||
|
||||
// We need to do a deep copy of the IP packet because
|
||||
|
|
@ -1063,6 +1098,10 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
|||
// each node that forwards the packet.
|
||||
newHdr.SetHopLimit(hopLimit - 1)
|
||||
|
||||
if route.RequiresTXTransportChecksum() {
|
||||
newPkt.CalculateTransportChecksum()
|
||||
}
|
||||
|
||||
forwardToEp, ok := e.protocol.getEndpointForNIC(route.NICID())
|
||||
if !ok {
|
||||
// The interface was removed after we obtained the route.
|
||||
|
|
@ -1123,7 +1162,8 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
|||
}
|
||||
}
|
||||
|
||||
if e.protocol.stack.HandleLocal() {
|
||||
stk := e.protocol.stack
|
||||
if stk.HandleLocal() {
|
||||
addressEndpoint := e.AcquireAssignedAddress(header.IPv6(pkt.NetworkHeader().Slice()).SourceAddress(), e.nic.Promiscuous(), stack.CanBePrimaryEndpoint, true /* readOnly */)
|
||||
if addressEndpoint != nil {
|
||||
// The source address is one of our own, so we never should have gotten
|
||||
|
|
@ -1135,12 +1175,19 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
|||
}
|
||||
|
||||
// Loopback traffic skips the prerouting chain.
|
||||
inNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
inNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.IPTablesPreroutingDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPrerouting(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPrerouting can modify the backing storage of the packet, so refresh
|
||||
|
|
@ -1386,15 +1433,20 @@ func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer,
|
|||
|
||||
func (e *endpoint) deliverPacketLocally(h header.IPv6, pkt *stack.PacketBuffer, inNICName string) {
|
||||
stats := e.stats.ip
|
||||
|
||||
// iptables filtering. All packets that reach here are intended for
|
||||
// this machine and need not be forwarded.
|
||||
if ok := e.protocol.stack.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
stk := e.protocol.stack
|
||||
if ok := stk.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.IPTablesInputDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckInput(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Any returned error is only useful for terminating execution early, but
|
||||
// we have nothing left to do, so we can drop it.
|
||||
_ = e.processExtensionHeaders(h, pkt, false /* forwarding */)
|
||||
|
|
@ -2692,6 +2744,8 @@ func (p *protocol) SendRejectionError(pkt *stack.PacketBuffer, rejectWith stack.
|
|||
return p.returnError(&icmpReasonPortUnreachable{}, pkt, inputHook)
|
||||
case stack.RejectIPv6WithICMPAdminProhibited:
|
||||
return p.returnError(&icmpReasonAdministrativelyProhibited{}, pkt, inputHook)
|
||||
case stack.RejectIPv6WithTCPReset:
|
||||
return ip.RejectWithTCPReset(pkt, ProtocolNumber, p.stack, inputHook)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled %[1]T = %[1]d", rejectWith))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -525,7 +525,6 @@ func (t *timer) StateTypeName() string {
|
|||
func (t *timer) StateFields() []string {
|
||||
return []string{
|
||||
"done",
|
||||
"timer",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,7 +534,6 @@ func (t *timer) beforeSave() {}
|
|||
func (t *timer) StateSave(stateSinkObject state.Sink) {
|
||||
t.beforeSave()
|
||||
stateSinkObject.Save(0, &t.done)
|
||||
stateSinkObject.Save(1, &t.timer)
|
||||
}
|
||||
|
||||
func (t *timer) afterLoad(context.Context) {}
|
||||
|
|
@ -543,7 +541,6 @@ func (t *timer) afterLoad(context.Context) {}
|
|||
// +checklocksignore
|
||||
func (t *timer) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &t.done)
|
||||
stateSourceObject.Load(1, &t.timer)
|
||||
}
|
||||
|
||||
func (o *offLinkRoute) StateTypeName() string {
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ type timer struct {
|
|||
// done indicates to the timer that the timer was stopped.
|
||||
done *bool
|
||||
|
||||
timer tcpip.Timer
|
||||
timer tcpip.Timer `state:"nosave"`
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue