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
|
|
@ -203,10 +203,6 @@ type Options struct {
|
|||
// include CapabilitySaveRestore
|
||||
SaveRestore bool
|
||||
|
||||
// DisconnectOk if true, indicates that this NIC capability set should
|
||||
// include CapabilityDisconnectOk.
|
||||
DisconnectOk bool
|
||||
|
||||
// GSOMaxSize is the maximum GSO packet size. It is zero if GSO is
|
||||
// disabled.
|
||||
GSOMaxSize uint32
|
||||
|
|
@ -240,19 +236,21 @@ type Options struct {
|
|||
// ProcessorsPerChannel is the number of goroutines used to handle packets
|
||||
// from each FD.
|
||||
ProcessorsPerChannel int
|
||||
|
||||
// IsPacketSocket indicates whether each FD is a packet socket.
|
||||
// If nil, getsockname will be called.
|
||||
IsPacketSocket []bool
|
||||
|
||||
// PreConfigured indicates that socket setup (getsockname, setsockopt)
|
||||
// has already been performed on the host.
|
||||
PreConfigured bool
|
||||
}
|
||||
|
||||
// fanoutID is used for AF_PACKET based endpoints to enable PACKET_FANOUT
|
||||
// support in the host kernel. This allows us to use multiple FD's to receive
|
||||
// from the same underlying NIC. The fanoutID needs to be the same for a given
|
||||
// set of FD's that point to the same NIC. Trying to set the PACKET_FANOUT
|
||||
// option for an FD with a fanoutID already in use by another FD for a different
|
||||
// NIC will return an EINVAL.
|
||||
//
|
||||
// Since fanoutID must be unique within the network namespace, we start with
|
||||
// the PID to avoid collisions. The only way to be sure of avoiding collisions
|
||||
// is to run in a new network namespace.
|
||||
var fanoutID atomicbitops.Int32 = atomicbitops.FromInt32(int32(unix.Getpid()))
|
||||
// fallbackFanoutID is used only when PACKET_FANOUT_FLAG_UNIQUEID is not
|
||||
// supported by the host kernel. It preserves the PID-seeded best-effort behavior:
|
||||
// seed from unix.Getpid() and increment per endpoint. This is not
|
||||
// collision-free across sentries that share a network namespace.
|
||||
var fallbackFanoutID atomicbitops.Int32 = atomicbitops.FromInt32(int32(unix.Getpid()))
|
||||
|
||||
// New creates a new fd-based endpoint.
|
||||
//
|
||||
|
|
@ -279,10 +277,6 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||
caps |= stack.CapabilitySaveRestore
|
||||
}
|
||||
|
||||
if opts.DisconnectOk {
|
||||
caps |= stack.CapabilityDisconnectOk
|
||||
}
|
||||
|
||||
if len(opts.FDs) == 0 {
|
||||
return nil, fmt.Errorf("opts.FD is empty, at least one FD must be specified")
|
||||
}
|
||||
|
|
@ -307,17 +301,18 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Increment fanoutID to ensure that we don't re-use the same fanoutID
|
||||
// for the next endpoint.
|
||||
fid := fanoutID.Add(1)
|
||||
// Fanout id allocated by the kernel for this endpoint. All AF_PACKET FDs
|
||||
// belonging to this endpoint must use the same id. -1 means no AF_PACKET
|
||||
// FD has allocated an id yet; 0 is a valid fanout id.
|
||||
fid := int32(-1)
|
||||
|
||||
// Create per channel dispatchers.
|
||||
for _, fd := range opts.FDs {
|
||||
for i, fd := range opts.FDs {
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
return nil, fmt.Errorf("unix.SetNonblock(%v) failed: %v", fd, err)
|
||||
}
|
||||
|
||||
isSocket, err := isSocketFD(fd)
|
||||
isSocket, err := IsSocketFD(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -334,7 +329,34 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||
opts.ProcessorsPerChannel = max(1, runtime.GOMAXPROCS(0)/len(opts.FDs))
|
||||
}
|
||||
|
||||
inboundDispatcher, err := createInboundDispatcher(e, fd, isSocket, fid, opts)
|
||||
var isPacket bool
|
||||
if opts.PreConfigured {
|
||||
if opts.IsPacketSocket != nil && i < len(opts.IsPacketSocket) {
|
||||
isPacket = opts.IsPacketSocket[i]
|
||||
} else {
|
||||
return nil, fmt.Errorf("PreConfigured is true but IsPacketSocket is missing or too short (index %d, len %d)", i, len(opts.IsPacketSocket))
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
isPacket, err = IsPacketSocket(fd, isSocket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if isPacket && !opts.PreConfigured {
|
||||
var err error
|
||||
if fid < 0 {
|
||||
fid, err = CreatePacketFanoutGroup(fd)
|
||||
} else {
|
||||
err = JoinPacketFanoutGroup(fd, fid)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to enable PACKET_FANOUT option: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
inboundDispatcher, err := createInboundDispatcher(e, fd, isSocket, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("createInboundDispatcher(...) = %v", err)
|
||||
}
|
||||
|
|
@ -344,7 +366,7 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||
return e, nil
|
||||
}
|
||||
|
||||
func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32, opts *Options) (linkDispatcher, error) {
|
||||
func createInboundDispatcher(e *endpoint, fd int, isSocket bool, opts *Options) (linkDispatcher, error) {
|
||||
// By default use the readv() dispatcher as it works with all kinds of
|
||||
// FDs (tap/tun/unix domain sockets and af_packet).
|
||||
inboundDispatcher, err := newReadVDispatcher(fd, e, opts)
|
||||
|
|
@ -353,38 +375,6 @@ func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32, opts
|
|||
}
|
||||
|
||||
if isSocket {
|
||||
sa, err := unix.Getsockname(fd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unix.Getsockname(%d) = %v", fd, err)
|
||||
}
|
||||
switch sa.(type) {
|
||||
case *unix.SockaddrLinklayer:
|
||||
// Enable PACKET_FANOUT mode if the underlying socket is of type
|
||||
// AF_PACKET. We do not enable PACKET_FANOUT_FLAG_DEFRAG as that will
|
||||
// prevent gvisor from receiving fragmented packets and the host does the
|
||||
// reassembly on our behalf before delivering the fragments. This makes it
|
||||
// hard to test fragmentation reassembly code in Netstack.
|
||||
//
|
||||
// See: include/uapi/linux/if_packet.h (struct fanout_args).
|
||||
//
|
||||
// NOTE: We are using SetSockOptInt here even though the underlying
|
||||
// option is actually a struct. The code follows the example in the
|
||||
// kernel documentation as described at the link below:
|
||||
//
|
||||
// See: https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
|
||||
//
|
||||
// This works out because the actual implementation for the option zero
|
||||
// initializes the structure and will initialize the max_members field
|
||||
// to a proper value if zero.
|
||||
//
|
||||
// See: https://github.com/torvalds/linux/blob/7acac4b3196caee5e21fb5ea53f8bc124e6a16fc/net/packet/af_packet.c#L3881
|
||||
const fanoutType = unix.PACKET_FANOUT_HASH
|
||||
fanoutArg := (int(fID) & 0xffff) | fanoutType<<16
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg); err != nil {
|
||||
return nil, fmt.Errorf("failed to enable PACKET_FANOUT option: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
switch e.packetDispatchMode {
|
||||
case PacketMMap:
|
||||
inboundDispatcher, err = newPacketMMapDispatcher(fd, e, opts)
|
||||
|
|
@ -407,7 +397,82 @@ func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32, opts
|
|||
return inboundDispatcher, nil
|
||||
}
|
||||
|
||||
func isSocketFD(fd int) (bool, error) {
|
||||
// IsPacketSocket checks if the FD is an AF_PACKET socket.
|
||||
func IsPacketSocket(fd int, isSocket bool) (bool, error) {
|
||||
if !isSocket {
|
||||
return false, nil
|
||||
}
|
||||
sa, err := unix.Getsockname(fd)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unix.Getsockname(%d) = %v", fd, err)
|
||||
}
|
||||
_, ok := sa.(*unix.SockaddrLinklayer)
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// CreatePacketFanoutGroup enables PACKET_FANOUT for the first AF_PACKET socket
|
||||
// in an endpoint and returns the fanout id the group joined.
|
||||
//
|
||||
// All AF_PACKET FDs that back the same endpoint must join the same fanout
|
||||
// group so the host kernel consistently hashes packets for a flow to one FD.
|
||||
// Fanout ids are unique within the Linux network namespace that owns the
|
||||
// sockets; reusing an id for a different NIC in that namespace fails with
|
||||
// EINVAL.
|
||||
//
|
||||
// We ask the kernel to allocate the id via PACKET_FANOUT_FLAG_UNIQUEID so the
|
||||
// id is guaranteed unique within the namespace even when multiple sentries
|
||||
// share it. If that setsockopt fails (e.g. the host kernel predates
|
||||
// PACKET_FANOUT_FLAG_UNIQUEID), we fall back to the PID-seeded
|
||||
// fallbackFanoutID, which is best-effort and not collision-free across
|
||||
// sentries that share a network namespace.
|
||||
//
|
||||
// We do not enable PACKET_FANOUT_FLAG_DEFRAG as that will prevent gvisor from
|
||||
// receiving fragmented packets and the host does the reassembly on our behalf
|
||||
// before delivering the fragments. This makes it hard to test fragmentation
|
||||
// reassembly code in Netstack.
|
||||
//
|
||||
// See: include/uapi/linux/if_packet.h (struct fanout_args).
|
||||
//
|
||||
// NOTE: We are using SetSockOptInt here even though the underlying option is
|
||||
// actually a struct. The code follows the example in the kernel documentation
|
||||
// as described at the link below:
|
||||
//
|
||||
// See: https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
|
||||
//
|
||||
// This works out because the actual implementation for the option zero
|
||||
// initializes the structure and will initialize the max_members field to a
|
||||
// proper value if zero.
|
||||
//
|
||||
// See: https://github.com/torvalds/linux/blob/7acac4b3196caee5e21fb5ea53f8bc124e6a16fc/net/packet/af_packet.c#L3881
|
||||
func CreatePacketFanoutGroup(fd int) (int32, error) {
|
||||
const fanoutType = unix.PACKET_FANOUT_HASH
|
||||
fanoutArg := (fanoutType | unix.PACKET_FANOUT_FLAG_UNIQUEID) << 16
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg); err != nil {
|
||||
uniqueIDErr := err
|
||||
fallbackID := fallbackFanoutID.Add(1)
|
||||
fanoutArg = (int(fallbackID) & 0xffff) | fanoutType<<16
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg); err != nil {
|
||||
return 0, fmt.Errorf("UNIQUEID failed (%v); fallback fanout id %d also failed: %v", uniqueIDErr, fanoutArg&0xffff, err)
|
||||
}
|
||||
return int32(fanoutArg & 0xffff), nil
|
||||
}
|
||||
|
||||
fanoutArg, err := unix.GetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("getsockopt(PACKET_FANOUT) failed: %v", err)
|
||||
}
|
||||
return int32(fanoutArg & 0xffff), nil
|
||||
}
|
||||
|
||||
// JoinPacketFanoutGroup joins the FD to the specified fanout group.
|
||||
func JoinPacketFanoutGroup(fd int, fID int32) error {
|
||||
const fanoutType = unix.PACKET_FANOUT_HASH
|
||||
fanoutArg := (int(fID) & 0xffff) | fanoutType<<16
|
||||
return unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg)
|
||||
}
|
||||
|
||||
// IsSocketFD checks if the FD is a socket.
|
||||
func IsSocketFD(fd int) (bool, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Fstat(fd, &stat); err != nil {
|
||||
return false, fmt.Errorf("unix.Fstat(%v,...) failed: %v", fd, err)
|
||||
|
|
@ -892,7 +957,7 @@ func (e *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber,
|
|||
// NewInjectable creates a new fd-based InjectableEndpoint.
|
||||
func NewInjectable(fd int, mtu uint32, capabilities stack.LinkEndpointCapabilities) (*InjectableEndpoint, error) {
|
||||
unix.SetNonblock(fd, true)
|
||||
isSocket, err := isSocketFD(fd)
|
||||
isSocket, err := IsSocketFD(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,5 +92,5 @@ func endpointinitLockNames() {}
|
|||
|
||||
func init() {
|
||||
endpointinitLockNames()
|
||||
endpointprefixIndex = locking.NewMutexClass(reflect.TypeOf(endpointRWMutex{}), endpointlockNames)
|
||||
endpointprefixIndex = locking.NewMutexClass(reflect.TypeFor[endpointRWMutex](), endpointlockNames)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ func (o *Options) StateFields() []string {
|
|||
"ClosedFunc",
|
||||
"Address",
|
||||
"SaveRestore",
|
||||
"DisconnectOk",
|
||||
"GSOMaxSize",
|
||||
"GVisorGSOEnabled",
|
||||
"PacketDispatchMode",
|
||||
|
|
@ -122,6 +121,8 @@ func (o *Options) StateFields() []string {
|
|||
"InterfaceIndex",
|
||||
"GRO",
|
||||
"ProcessorsPerChannel",
|
||||
"IsPacketSocket",
|
||||
"PreConfigured",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,16 +137,17 @@ func (o *Options) StateSave(stateSinkObject state.Sink) {
|
|||
stateSinkObject.Save(3, &o.ClosedFunc)
|
||||
stateSinkObject.Save(4, &o.Address)
|
||||
stateSinkObject.Save(5, &o.SaveRestore)
|
||||
stateSinkObject.Save(6, &o.DisconnectOk)
|
||||
stateSinkObject.Save(7, &o.GSOMaxSize)
|
||||
stateSinkObject.Save(8, &o.GVisorGSOEnabled)
|
||||
stateSinkObject.Save(9, &o.PacketDispatchMode)
|
||||
stateSinkObject.Save(10, &o.TXChecksumOffload)
|
||||
stateSinkObject.Save(11, &o.RXChecksumOffload)
|
||||
stateSinkObject.Save(12, &o.MaxSyscallHeaderBytes)
|
||||
stateSinkObject.Save(13, &o.InterfaceIndex)
|
||||
stateSinkObject.Save(14, &o.GRO)
|
||||
stateSinkObject.Save(15, &o.ProcessorsPerChannel)
|
||||
stateSinkObject.Save(6, &o.GSOMaxSize)
|
||||
stateSinkObject.Save(7, &o.GVisorGSOEnabled)
|
||||
stateSinkObject.Save(8, &o.PacketDispatchMode)
|
||||
stateSinkObject.Save(9, &o.TXChecksumOffload)
|
||||
stateSinkObject.Save(10, &o.RXChecksumOffload)
|
||||
stateSinkObject.Save(11, &o.MaxSyscallHeaderBytes)
|
||||
stateSinkObject.Save(12, &o.InterfaceIndex)
|
||||
stateSinkObject.Save(13, &o.GRO)
|
||||
stateSinkObject.Save(14, &o.ProcessorsPerChannel)
|
||||
stateSinkObject.Save(15, &o.IsPacketSocket)
|
||||
stateSinkObject.Save(16, &o.PreConfigured)
|
||||
}
|
||||
|
||||
func (o *Options) afterLoad(context.Context) {}
|
||||
|
|
@ -158,16 +160,17 @@ func (o *Options) StateLoad(ctx context.Context, stateSourceObject state.Source)
|
|||
stateSourceObject.Load(3, &o.ClosedFunc)
|
||||
stateSourceObject.Load(4, &o.Address)
|
||||
stateSourceObject.Load(5, &o.SaveRestore)
|
||||
stateSourceObject.Load(6, &o.DisconnectOk)
|
||||
stateSourceObject.Load(7, &o.GSOMaxSize)
|
||||
stateSourceObject.Load(8, &o.GVisorGSOEnabled)
|
||||
stateSourceObject.Load(9, &o.PacketDispatchMode)
|
||||
stateSourceObject.Load(10, &o.TXChecksumOffload)
|
||||
stateSourceObject.Load(11, &o.RXChecksumOffload)
|
||||
stateSourceObject.Load(12, &o.MaxSyscallHeaderBytes)
|
||||
stateSourceObject.Load(13, &o.InterfaceIndex)
|
||||
stateSourceObject.Load(14, &o.GRO)
|
||||
stateSourceObject.Load(15, &o.ProcessorsPerChannel)
|
||||
stateSourceObject.Load(6, &o.GSOMaxSize)
|
||||
stateSourceObject.Load(7, &o.GVisorGSOEnabled)
|
||||
stateSourceObject.Load(8, &o.PacketDispatchMode)
|
||||
stateSourceObject.Load(9, &o.TXChecksumOffload)
|
||||
stateSourceObject.Load(10, &o.RXChecksumOffload)
|
||||
stateSourceObject.Load(11, &o.MaxSyscallHeaderBytes)
|
||||
stateSourceObject.Load(12, &o.InterfaceIndex)
|
||||
stateSourceObject.Load(13, &o.GRO)
|
||||
stateSourceObject.Load(14, &o.ProcessorsPerChannel)
|
||||
stateSourceObject.Load(15, &o.IsPacketSocket)
|
||||
stateSourceObject.Load(16, &o.PreConfigured)
|
||||
}
|
||||
|
||||
func (e *InjectableEndpoint) StateTypeName() string {
|
||||
|
|
|
|||
|
|
@ -92,5 +92,5 @@ func injectableEndpointinitLockNames() {}
|
|||
|
||||
func init() {
|
||||
injectableEndpointinitLockNames()
|
||||
injectableEndpointprefixIndex = locking.NewMutexClass(reflect.TypeOf(injectableEndpointRWMutex{}), injectableEndpointlockNames)
|
||||
injectableEndpointprefixIndex = locking.NewMutexClass(reflect.TypeFor[injectableEndpointRWMutex](), injectableEndpointlockNames)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,5 +60,5 @@ func processorinitLockNames() {}
|
|||
|
||||
func init() {
|
||||
processorinitLockNames()
|
||||
processorprefixIndex = locking.NewMutexClass(reflect.TypeOf(processorMutex{}), processorlockNames)
|
||||
processorprefixIndex = locking.NewMutexClass(reflect.TypeFor[processorMutex](), processorlockNames)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ type processor struct {
|
|||
|
||||
func (p *processor) start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
defer p.sleeper.Done()
|
||||
for {
|
||||
switch w := p.sleeper.Fetch(true); {
|
||||
case w == &p.packetWaker:
|
||||
|
|
@ -128,9 +127,10 @@ func (m *processorManager) start() {
|
|||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (m *processorManager) afterLoad(context.Context) {
|
||||
m.wg.Add(len(m.processors))
|
||||
m.start()
|
||||
func (m *processorManager) afterLoad(ctx context.Context) {
|
||||
// Close all the old/saved processors. There are new NICs and
|
||||
// processors created during restore.
|
||||
m.close()
|
||||
}
|
||||
|
||||
func (m *processorManager) connectionHash(cid *connectionID) uint32 {
|
||||
|
|
@ -215,34 +215,47 @@ func tcpipConnectionID(pkt *stack.PacketBuffer) (connectionID, bool) {
|
|||
return cid, true
|
||||
}
|
||||
ipHdr := header.IPv6(h)
|
||||
cid.srcAddr = ipHdr.SourceAddressSlice()
|
||||
cid.dstAddr = ipHdr.DestinationAddressSlice()
|
||||
cid.proto = header.IPv6ProtocolNumber
|
||||
|
||||
var tcpHdr header.TCP
|
||||
if tcpip.TransportProtocolNumber(ipHdr.NextHeader()) == header.TCPProtocolNumber {
|
||||
tcpHdr = header.TCP(h[header.IPv6FixedHeaderSize:][:tcpSrcDstPortLen])
|
||||
if !header.IsExtensionHeader(ipHdr.NextHeader()) {
|
||||
// Known transport protocols(not just TCP) store the src and dst ports
|
||||
// in the first 4 bytes after the IPv6 fixed header.
|
||||
tcpHdr := header.TCP(h[header.IPv6FixedHeaderSize:][:tcpSrcDstPortLen])
|
||||
cid.srcPort = tcpHdr.SourcePort()
|
||||
cid.dstPort = tcpHdr.DestinationPort()
|
||||
} else {
|
||||
// Slow path for IPv6 extension headers :(.
|
||||
dataBuf := pkt.Data().ToBuffer()
|
||||
dataBuf.TrimFront(header.IPv6MinimumSize)
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(ipHdr.NextHeader()), dataBuf)
|
||||
defer it.Release()
|
||||
// All fragment packets need to be processed by the same goroutine, so
|
||||
// only record the ports if this is not a fragment packet.
|
||||
var isFragment bool
|
||||
for {
|
||||
hdr, done, err := it.Next()
|
||||
if done || err != nil {
|
||||
break
|
||||
}
|
||||
if fh, ok := hdr.(header.IPv6FragmentExtHdr); ok && !fh.IsAtomic() {
|
||||
isFragment = true
|
||||
}
|
||||
hdr.Release()
|
||||
}
|
||||
h, ok = pkt.Data().PullUp(int(it.HeaderOffset()) + tcpSrcDstPortLen)
|
||||
if !ok {
|
||||
return cid, true
|
||||
if !isFragment {
|
||||
h, ok = pkt.Data().PullUp(int(it.HeaderOffset()) + tcpSrcDstPortLen)
|
||||
if !ok {
|
||||
return cid, true
|
||||
}
|
||||
// Known transport protocols store the src and dst ports
|
||||
// in the first 4 bytes after the IPv6 fixed header.
|
||||
tcpHdr := header.TCP(h[it.HeaderOffset():][:tcpSrcDstPortLen])
|
||||
cid.srcPort = tcpHdr.SourcePort()
|
||||
cid.dstPort = tcpHdr.DestinationPort()
|
||||
}
|
||||
tcpHdr = header.TCP(h[it.HeaderOffset():][:tcpSrcDstPortLen])
|
||||
}
|
||||
cid.srcAddr = ipHdr.SourceAddressSlice()
|
||||
cid.dstAddr = ipHdr.DestinationAddressSlice()
|
||||
cid.srcPort = tcpHdr.SourcePort()
|
||||
cid.dstPort = tcpHdr.DestinationPort()
|
||||
cid.proto = header.IPv6ProtocolNumber
|
||||
default:
|
||||
return cid, true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue