package tun import ( "context" "net" "net/netip" "syscall" "time" "github.com/sagernet/sing-tun/internal/gtcpip/checksum" "github.com/sagernet/sing-tun/internal/gtcpip/header" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/udpnat2" ) var ErrIncludeAllNetworks = E.New("`system` and `mixed` stack are not available when `includeAllNetworks` is enabled. See https://github.com/SagerNet/sing-tun/issues/25") type System struct { ctx context.Context tun Tun tunName string mtu int handler Handler logger logger.Logger inet4Prefixes []netip.Prefix inet6Prefixes []netip.Prefix inet4ServerAddress netip.Addr inet4Address netip.Addr inet6ServerAddress netip.Addr inet6Address netip.Addr broadcastAddr netip.Addr udpTimeout time.Duration tcpListener net.Listener tcpListener6 net.Listener tcpPort uint16 tcpPort6 uint16 tcpNat *TCPNat udpNat *udpnat.Service bindInterface bool interfaceFinder control.InterfaceFinder frontHeadroom int txChecksumOffload bool } type Session struct { SourceAddress netip.Addr DestinationAddress netip.Addr SourcePort uint16 DestinationPort uint16 } func NewSystem(options StackOptions) (Stack, error) { stack := &System{ ctx: options.Context, tun: options.Tun, tunName: options.TunOptions.Name, mtu: int(options.TunOptions.MTU), udpTimeout: options.UDPTimeout, handler: options.Handler, logger: options.Logger, inet4Prefixes: options.TunOptions.Inet4Address, inet6Prefixes: options.TunOptions.Inet6Address, broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address), bindInterface: options.ForwarderBindInterface, interfaceFinder: options.InterfaceFinder, } if len(options.TunOptions.Inet4Address) > 0 { if !HasNextAddress(options.TunOptions.Inet4Address[0], 1) { return nil, E.New("need one more IPv4 address in first prefix for system stack") } stack.inet4ServerAddress = options.TunOptions.Inet4Address[0].Addr() stack.inet4Address = stack.inet4ServerAddress.Next() } if len(options.TunOptions.Inet6Address) > 0 { if !HasNextAddress(options.TunOptions.Inet6Address[0], 1) { return nil, E.New("need one more IPv6 address in first prefix for system stack") } stack.inet6ServerAddress = options.TunOptions.Inet6Address[0].Addr() stack.inet6Address = stack.inet6ServerAddress.Next() } if !stack.inet4Address.IsValid() && !stack.inet6Address.IsValid() { return nil, E.New("missing interface address") } return stack, nil } func (s *System) Close() error { return common.Close( s.tcpListener, s.tcpListener6, ) } func (s *System) Start() error { err := s.start() if err != nil { return err } go s.tunLoop() return nil } func (s *System) start() error { err := fixWindowsFirewall() if err != nil { return E.Cause(err, "fix windows firewall for system stack") } var listener net.ListenConfig if s.bindInterface { listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error { bindErr := control.BindToInterface0(s.interfaceFinder, conn, network, address, s.tunName, -1, true) if bindErr != nil { s.logger.Warn("bind forwarder to interface: ", bindErr) } return nil }) } var tcpListener net.Listener if s.inet4Address.IsValid() { for i := 0; i < 3; i++ { tcpListener, err = listener.Listen(s.ctx, "tcp4", net.JoinHostPort(s.inet4ServerAddress.String(), "0")) if !retryableListenError(err) { break } time.Sleep(time.Second) } if err != nil { return err } s.tcpListener = tcpListener s.tcpPort = M.SocksaddrFromNet(tcpListener.Addr()).Port go s.acceptLoop(tcpListener) } if s.inet6Address.IsValid() { for i := 0; i < 3; i++ { tcpListener, err = listener.Listen(s.ctx, "tcp6", net.JoinHostPort(s.inet6ServerAddress.String(), "0")) if !retryableListenError(err) { break } time.Sleep(time.Second) } if err != nil { return err } s.tcpListener6 = tcpListener s.tcpPort6 = M.SocksaddrFromNet(tcpListener.Addr()).Port go s.acceptLoop(tcpListener) } s.tcpNat = NewNat(s.ctx, s.udpTimeout) s.udpNat = udpnat.New(s.handler, s.preparePacketConnection, s.udpTimeout, false) return nil } func (s *System) tunLoop() { if winTun, isWinTun := s.tun.(WinTun); isWinTun { s.wintunLoop(winTun) return } if linuxTUN, isLinuxTUN := s.tun.(LinuxTUN); isLinuxTUN { s.frontHeadroom = linuxTUN.FrontHeadroom() s.txChecksumOffload = linuxTUN.TXChecksumOffload() batchSize := linuxTUN.BatchSize() if batchSize > 1 { s.batchLoop(linuxTUN, batchSize) return } } packetBuffer := make([]byte, s.mtu+PacketOffset) for { n, err := s.tun.Read(packetBuffer) if err != nil { if E.IsClosed(err) { return } s.logger.Error(E.Cause(err, "read packet")) } if n < header.IPv4MinimumSize { continue } rawPacket := packetBuffer[:n] packet := packetBuffer[PacketOffset:n] if s.processPacket(packet) { _, err = s.tun.Write(rawPacket) if err != nil { s.logger.Trace(E.Cause(err, "write packet")) } } } } func (s *System) wintunLoop(winTun WinTun) { for { packet, release, err := winTun.ReadPacket() if err != nil { return } if len(packet) < header.IPv4MinimumSize { release() continue } if s.processPacket(packet) { _, err = winTun.Write(packet) if err != nil { s.logger.Trace(E.Cause(err, "write packet")) } } release() } } func (s *System) batchLoop(linuxTUN LinuxTUN, batchSize int) { packetBuffers := make([][]byte, batchSize) writeBuffers := make([][]byte, batchSize) packetSizes := make([]int, batchSize) for i := range packetBuffers { packetBuffers[i] = make([]byte, s.mtu+s.frontHeadroom) } for { n, err := linuxTUN.BatchRead(packetBuffers, s.frontHeadroom, packetSizes) if err != nil { if E.IsClosed(err) { return } s.logger.Error(E.Cause(err, "batch read packet")) } if n == 0 { continue } for i := 0; i < n; i++ { packetSize := packetSizes[i] if packetSize < header.IPv4MinimumSize { continue } packetBuffer := packetBuffers[i] packet := packetBuffer[s.frontHeadroom : s.frontHeadroom+packetSize] if s.processPacket(packet) { writeBuffers = append(writeBuffers, packetBuffer[:s.frontHeadroom+packetSize]) } } if len(writeBuffers) > 0 { _, err = linuxTUN.BatchWrite(writeBuffers, s.frontHeadroom) if err != nil { s.logger.Trace(E.Cause(err, "batch write packet")) } writeBuffers = writeBuffers[:0] } } } func (s *System) processPacket(packet []byte) bool { var ( writeBack bool err error ) switch ipVersion := header.IPVersion(packet); ipVersion { case header.IPv4Version: writeBack, err = s.processIPv4(packet) case header.IPv6Version: writeBack, err = s.processIPv6(packet) default: err = E.New("ip: unknown version: ", ipVersion) } if err != nil { s.logger.Trace(err) return false } return writeBack } func (s *System) acceptLoop(listener net.Listener) { for { conn, err := listener.Accept() if err != nil { return } connPort := M.SocksaddrFromNet(conn.RemoteAddr()).Port session := s.tcpNat.LookupBack(connPort) if session == nil { s.logger.Trace(E.New("unknown session with port ", connPort)) continue } destination := M.SocksaddrFromNetIP(session.Destination) if destination.Addr.Is4() { for _, prefix := range s.inet4Prefixes { if prefix.Contains(destination.Addr) { destination.Addr = netip.AddrFrom4([4]byte{127, 0, 0, 1}) break } } } else { for _, prefix := range s.inet6Prefixes { if prefix.Contains(destination.Addr) { destination.Addr = netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) break } } } go s.handler.NewConnectionEx(s.ctx, conn, M.SocksaddrFromNetIP(session.Source), destination, nil) } } func (s *System) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) { destination := ipHdr.DestinationAddr() if destination == s.broadcastAddr || !destination.IsGlobalUnicast() { return } writeBack = true switch ipHdr.TransportProtocol() { case header.TCPProtocolNumber: writeBack, err = s.processIPv4TCP(ipHdr, ipHdr.Payload()) case header.UDPProtocolNumber: writeBack = false err = s.processIPv4UDP(ipHdr, ipHdr.Payload()) case header.ICMPv4ProtocolNumber: err = s.processIPv4ICMP(ipHdr, ipHdr.Payload()) } return } func (s *System) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) { if !ipHdr.DestinationAddr().IsGlobalUnicast() { return } writeBack = true switch ipHdr.TransportProtocol() { case header.TCPProtocolNumber: writeBack, err = s.processIPv6TCP(ipHdr, ipHdr.Payload()) case header.UDPProtocolNumber: err = s.processIPv6UDP(ipHdr, ipHdr.Payload()) case header.ICMPv6ProtocolNumber: err = s.processIPv6ICMP(ipHdr, ipHdr.Payload()) } return } func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, error) { source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort()) destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort()) if !destination.Addr().IsGlobalUnicast() { return false, nil } else if source.Addr() == s.inet4ServerAddress && source.Port() == s.tcpPort { session := s.tcpNat.LookupBack(destination.Port()) if session == nil { return false, E.New("ipv4: tcp: session not found: ", destination.Port()) } ipHdr.SetSourceAddr(session.Destination.Addr()) tcpHdr.SetSourcePort(session.Destination.Port()) ipHdr.SetDestinationAddr(session.Source.Addr()) tcpHdr.SetDestinationPort(session.Source.Port()) } else { natPort, err := s.tcpNat.Lookup(source, destination, s.handler) if err != nil { if err == ErrDrop { return false, nil } else { return false, s.resetIPv4TCP(ipHdr, tcpHdr) } } ipHdr.SetSourceAddr(s.inet4Address) tcpHdr.SetSourcePort(natPort) ipHdr.SetDestinationAddr(s.inet4ServerAddress) tcpHdr.SetDestinationPort(s.tcpPort) } if !s.txChecksumOffload { tcpHdr.SetChecksum(0) tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum( header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()), ))) } else { tcpHdr.SetChecksum(0) } ipHdr.SetChecksum(0) ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) return true, nil } func (s *System) resetIPv4TCP(origIPHdr header.IPv4, origTCPHdr header.TCP) error { frontHeadroom := s.frontHeadroom + PacketOffset newPacket := buf.NewSize(frontHeadroom + header.IPv4MinimumSize + header.TCPMinimumSize) defer newPacket.Release() newPacket.Resize(frontHeadroom, header.IPv4MinimumSize+header.TCPMinimumSize) ipHdr := header.IPv4(newPacket.Bytes()) ipHdr.Encode(&header.IPv4Fields{ TotalLength: uint16(newPacket.Len()), Protocol: uint8(header.TCPProtocolNumber), SrcAddr: origIPHdr.DestinationAddr(), DstAddr: origIPHdr.SourceAddr(), }) tcpHdr := header.TCP(ipHdr.Payload()) fields := header.TCPFields{ SrcPort: origTCPHdr.DestinationPort(), DstPort: origTCPHdr.SourcePort(), DataOffset: header.TCPMinimumSize, Flags: header.TCPFlagRst, } if origTCPHdr.Flags()&header.TCPFlagAck != 0 { fields.SeqNum = origTCPHdr.AckNumber() } else { fields.Flags |= header.TCPFlagAck ackNum := origTCPHdr.SequenceNumber() + uint32(len(origTCPHdr.Payload())) if origTCPHdr.Flags()&header.TCPFlagSyn != 0 { ackNum++ } if origTCPHdr.Flags()&header.TCPFlagFin != 0 { ackNum++ } fields.AckNum = ackNum } tcpHdr.Encode(&fields) if !s.txChecksumOffload { tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), header.TCPMinimumSize))) } ipHdr.SetChecksum(0) ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) if PacketOffset > 0 { PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv4Version) } else { newPacket.Advance(-s.frontHeadroom) } return common.Error(s.tun.Write(newPacket.Bytes())) } func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) (bool, error) { source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort()) destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort()) if !destination.Addr().IsGlobalUnicast() { return false, nil } else if source.Addr() == s.inet6ServerAddress && source.Port() == s.tcpPort6 { session := s.tcpNat.LookupBack(destination.Port()) if session == nil { return false, E.New("ipv6: tcp: session not found: ", destination.Port()) } ipHdr.SetSourceAddr(session.Destination.Addr()) tcpHdr.SetSourcePort(session.Destination.Port()) ipHdr.SetDestinationAddr(session.Source.Addr()) tcpHdr.SetDestinationPort(session.Source.Port()) } else { natPort, err := s.tcpNat.Lookup(source, destination, s.handler) if err != nil { if err == ErrDrop { return false, nil } else { return false, s.resetIPv6TCP(ipHdr, tcpHdr) } } ipHdr.SetSourceAddr(s.inet6Address) tcpHdr.SetSourcePort(natPort) ipHdr.SetDestinationAddr(s.inet6ServerAddress) tcpHdr.SetDestinationPort(s.tcpPort6) } if !s.txChecksumOffload { tcpHdr.SetChecksum(0) tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum( header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()), ))) } else { tcpHdr.SetChecksum(0) } return true, nil } func (s *System) resetIPv6TCP(origIPHdr header.IPv6, origTCPHdr header.TCP) error { frontHeadroom := s.frontHeadroom + PacketOffset newPacket := buf.NewSize(frontHeadroom + header.IPv6MinimumSize + header.TCPMinimumSize) defer newPacket.Release() newPacket.Resize(frontHeadroom, header.IPv6MinimumSize+header.TCPMinimumSize) ipHdr := header.IPv6(newPacket.Bytes()) ipHdr.Encode(&header.IPv6Fields{ PayloadLength: uint16(header.TCPMinimumSize), TransportProtocol: header.TCPProtocolNumber, SrcAddr: origIPHdr.DestinationAddr(), DstAddr: origIPHdr.SourceAddr(), }) tcpHdr := header.TCP(ipHdr.Payload()) fields := header.TCPFields{ SrcPort: origTCPHdr.DestinationPort(), DstPort: origTCPHdr.SourcePort(), DataOffset: header.TCPMinimumSize, Flags: header.TCPFlagRst, } if origTCPHdr.Flags()&header.TCPFlagAck != 0 { fields.SeqNum = origTCPHdr.AckNumber() } else { fields.Flags |= header.TCPFlagAck ackNum := origTCPHdr.SequenceNumber() + uint32(len(origTCPHdr.Payload())) if origTCPHdr.Flags()&header.TCPFlagSyn != 0 { ackNum++ } if origTCPHdr.Flags()&header.TCPFlagFin != 0 { ackNum++ } fields.AckNum = ackNum } tcpHdr.Encode(&fields) if !s.txChecksumOffload { tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), header.TCPMinimumSize))) } if PacketOffset > 0 { PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version) } else { newPacket.Advance(-s.frontHeadroom) } return common.Error(s.tun.Write(newPacket.Bytes())) } func (s *System) processIPv4UDP(ipHdr header.IPv4, udpHdr header.UDP) error { if ipHdr.Flags()&header.IPv4FlagMoreFragments != 0 { return E.New("ipv4: fragment dropped") } if ipHdr.FragmentOffset() != 0 { return E.New("ipv4: udp: fragment dropped") } source := M.SocksaddrFrom(ipHdr.SourceAddr(), udpHdr.SourcePort()) destination := M.SocksaddrFrom(ipHdr.DestinationAddr(), udpHdr.DestinationPort()) if !destination.Addr.IsGlobalUnicast() { return nil } s.udpNat.NewPacket([][]byte{udpHdr.Payload()}, source, destination, ipHdr) return nil } func (s *System) processIPv6UDP(ipHdr header.IPv6, udpHdr header.UDP) error { source := M.SocksaddrFrom(ipHdr.SourceAddr(), udpHdr.SourcePort()) destination := M.SocksaddrFrom(ipHdr.DestinationAddr(), udpHdr.DestinationPort()) if !destination.Addr.IsGlobalUnicast() { return nil } s.udpNat.NewPacket([][]byte{udpHdr.Payload()}, source, destination, ipHdr) return nil } func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { pErr := s.handler.PrepareConnection(N.NetworkUDP, source, destination) if pErr != nil { if pErr != ErrDrop { if source.IsIPv4() { ipHdr := userData.(header.IPv4) s.rejectIPv4WithICMP(ipHdr, header.ICMPv4PortUnreachable) } else { ipHdr := userData.(header.IPv6) s.rejectIPv6WithICMP(ipHdr, header.ICMPv6PortUnreachable) } } return false, nil, nil, nil } var writer N.PacketWriter if source.IsIPv4() { packet := userData.(header.IPv4) headerLen := packet.HeaderLength() + header.UDPMinimumSize headerCopy := make([]byte, headerLen) copy(headerCopy, packet[:headerLen]) writer = &systemUDPPacketWriter4{ s.tun, s.frontHeadroom + PacketOffset, headerCopy, source.AddrPort(), s.txChecksumOffload, } } else { packet := userData.(header.IPv6) headerLen := len(packet) - int(packet.PayloadLength()) + header.UDPMinimumSize headerCopy := make([]byte, headerLen) copy(headerCopy, packet[:headerLen]) writer = &systemUDPPacketWriter6{ s.tun, s.frontHeadroom + PacketOffset, headerCopy, source.AddrPort(), s.txChecksumOffload, } } return true, s.ctx, writer, nil } func (s *System) processIPv4ICMP(ipHdr header.IPv4, icmpHdr header.ICMPv4) error { if icmpHdr.Type() != header.ICMPv4Echo || icmpHdr.Code() != 0 { return nil } icmpHdr.SetType(header.ICMPv4EchoReply) sourceAddress := ipHdr.SourceAddr() ipHdr.SetSourceAddr(ipHdr.DestinationAddr()) ipHdr.SetDestinationAddr(sourceAddress) icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr[:header.ICMPv4MinimumSize], checksum.Checksum(icmpHdr.Payload(), 0))) ipHdr.SetChecksum(0) ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) return nil } func (s *System) rejectIPv4WithICMP(ipHdr header.IPv4, code header.ICMPv4Code) error { frontHeadroom := s.frontHeadroom + PacketOffset mtu := s.mtu const maxIPData = header.IPv4MinimumProcessableDatagramSize - header.IPv4MinimumSize if mtu > maxIPData { mtu = maxIPData } available := mtu - header.ICMPv4MinimumSize if available < len(ipHdr)+header.ICMPv4MinimumErrorPayloadSize { return nil } payload := ipHdr if len(payload) > available { payload = payload[:available] } newPacket := buf.NewSize(frontHeadroom + header.IPv4MinimumSize + header.ICMPv4MinimumSize + len(payload)) defer newPacket.Release() newPacket.Resize(frontHeadroom, header.IPv4MinimumSize+header.ICMPv4MinimumSize+len(payload)) newIPHdr := header.IPv4(newPacket.Bytes()) newIPHdr.Encode(&header.IPv4Fields{ TotalLength: uint16(newPacket.Len()), Protocol: uint8(header.ICMPv4ProtocolNumber), SrcAddr: ipHdr.DestinationAddr(), DstAddr: ipHdr.SourceAddr(), }) newIPHdr.SetChecksum(^newIPHdr.CalculateChecksum()) icmpHdr := header.ICMPv4(newIPHdr.Payload()) icmpHdr.SetType(header.ICMPv4DstUnreachable) icmpHdr.SetCode(code) icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr[:header.ICMPv4MinimumSize], checksum.Checksum(payload, 0))) copy(icmpHdr.Payload(), payload) if PacketOffset > 0 { newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET } else { newPacket.Advance(-s.frontHeadroom) } return common.Error(s.tun.Write(newPacket.Bytes())) } func (s *System) processIPv6ICMP(ipHdr header.IPv6, icmpHdr header.ICMPv6) error { if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 { return nil } icmpHdr.SetType(header.ICMPv6EchoReply) sourceAddress := ipHdr.SourceAddr() ipHdr.SetSourceAddr(ipHdr.DestinationAddr()) ipHdr.SetDestinationAddr(sourceAddress) icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ Header: icmpHdr, Src: ipHdr.SourceAddress(), Dst: ipHdr.DestinationAddress(), })) return nil } func (s *System) rejectIPv6WithICMP(ipHdr header.IPv6, code header.ICMPv6Code) error { frontHeadroom := s.frontHeadroom + PacketOffset mtu := s.mtu const maxIPv6Data = header.IPv6MinimumMTU - header.IPv6FixedHeaderSize if mtu > maxIPv6Data { mtu = maxIPv6Data } available := mtu - header.ICMPv6ErrorHeaderSize if available < header.IPv6MinimumSize { return nil } payload := ipHdr if len(payload) > available { payload = payload[:available] } newPacket := buf.NewSize(frontHeadroom + header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + len(payload)) defer newPacket.Release() newPacket.Resize(frontHeadroom, header.IPv6MinimumSize+header.ICMPv6DstUnreachableMinimumSize+len(payload)) newIPHdr := header.IPv6(newPacket.Bytes()) newIPHdr.Encode(&header.IPv6Fields{ PayloadLength: uint16(header.ICMPv6DstUnreachableMinimumSize + len(payload)), TransportProtocol: header.ICMPv6ProtocolNumber, SrcAddr: ipHdr.DestinationAddr(), DstAddr: ipHdr.SourceAddr(), }) icmpHdr := header.ICMPv6(newIPHdr.Payload()) icmpHdr.SetType(header.ICMPv6DstUnreachable) icmpHdr.SetCode(code) icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ Header: icmpHdr[:header.ICMPv6DstUnreachableMinimumSize], Src: newIPHdr.SourceAddress(), Dst: newIPHdr.DestinationAddress(), PayloadCsum: checksum.Checksum(payload, 0), PayloadLen: len(payload), })) copy(icmpHdr.Payload(), payload) if PacketOffset > 0 { PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version) } else { newPacket.Advance(-s.frontHeadroom) } return common.Error(s.tun.Write(newPacket.Bytes())) } type systemUDPPacketWriter4 struct { tun Tun frontHeadroom int header []byte source netip.AddrPort txChecksumOffload bool } func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { newPacket := buf.NewSize(w.frontHeadroom + len(w.header) + buffer.Len()) defer newPacket.Release() newPacket.Resize(w.frontHeadroom, 0) newPacket.Write(w.header) newPacket.Write(buffer.Bytes()) ipHdr := header.IPv4(newPacket.Bytes()) ipHdr.SetTotalLength(uint16(newPacket.Len())) ipHdr.SetDestinationAddress(ipHdr.SourceAddress()) ipHdr.SetSourceAddr(destination.Addr) udpHdr := header.UDP(ipHdr.Payload()) udpHdr.SetDestinationPort(udpHdr.SourcePort()) udpHdr.SetSourcePort(destination.Port) udpHdr.SetLength(uint16(buffer.Len() + header.UDPMinimumSize)) if !w.txChecksumOffload { udpHdr.SetChecksum(0) udpHdr.SetChecksum(^checksum.Checksum(udpHdr.Payload(), udpHdr.CalculateChecksum( header.PseudoHeaderChecksum(header.UDPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()), ))) } else { udpHdr.SetChecksum(0) } ipHdr.SetChecksum(0) ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) if PacketOffset > 0 { PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv4Version) } else { newPacket.Advance(-w.frontHeadroom) } return common.Error(w.tun.Write(newPacket.Bytes())) } type systemUDPPacketWriter6 struct { tun Tun frontHeadroom int header []byte source netip.AddrPort txChecksumOffload bool } func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { newPacket := buf.NewSize(w.frontHeadroom + len(w.header) + buffer.Len()) defer newPacket.Release() newPacket.Resize(w.frontHeadroom, 0) newPacket.Write(w.header) newPacket.Write(buffer.Bytes()) ipHdr := header.IPv6(newPacket.Bytes()) udpLen := uint16(header.UDPMinimumSize + buffer.Len()) ipHdr.SetPayloadLength(udpLen) ipHdr.SetDestinationAddress(ipHdr.SourceAddress()) ipHdr.SetSourceAddr(destination.Addr) udpHdr := header.UDP(ipHdr.Payload()) udpHdr.SetDestinationPort(udpHdr.SourcePort()) udpHdr.SetSourcePort(destination.Port) udpHdr.SetLength(udpLen) if !w.txChecksumOffload { udpHdr.SetChecksum(0) udpHdr.SetChecksum(^checksum.Checksum(udpHdr.Payload(), udpHdr.CalculateChecksum( header.PseudoHeaderChecksum(header.UDPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()), ))) } else { udpHdr.SetChecksum(0) } if PacketOffset > 0 { PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version) } else { newPacket.Advance(-w.frontHeadroom) } return common.Error(w.tun.Write(newPacket.Bytes())) }