Implement TCP and ICMP rejects

This commit is contained in:
世界 2024-10-22 21:18:32 +08:00
parent 1793988a6d
commit e95737eccb
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 256 additions and 107 deletions

View file

@ -23,6 +23,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
const WithGVisor = true const WithGVisor = true
@ -79,7 +80,7 @@ func (t *GVisor) Start() error {
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) { tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort) source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort) destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
pErr := t.handler.PrepareConnection(source, destination) pErr := t.handler.PrepareConnection(N.NetworkTCP, source, destination)
if pErr != nil { if pErr != nil {
r.Complete(gWriteUnreachable(t.stack, r.Packet(), err) == os.ErrInvalid) r.Complete(gWriteUnreachable(t.stack, r.Packet(), err) == os.ErrInvalid)
return return
@ -96,28 +97,21 @@ func (t *GVisor) Start() error {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
if !t.endpointIndependentNat { if !t.endpointIndependentNat {
udpForwarder := udp.NewForwarder(ipStack, func(r *udp.ForwarderRequest) { udpForwarder := udp.NewForwarder(ipStack, func(r *udp.ForwarderRequest) {
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
pErr := t.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil {
gWriteUnreachable(t.stack, r.Packet(), err)
r.Packet().DecRef()
return
}
var wq waiter.Queue var wq waiter.Queue
endpoint, err := r.CreateEndpoint(&wq) endpoint, err := r.CreateEndpoint(&wq)
if err != nil { if err != nil {
return return
} }
udpConn := gonet.NewUDPConn(&wq, endpoint)
lAddr := udpConn.RemoteAddr()
rAddr := udpConn.LocalAddr()
if lAddr == nil || rAddr == nil {
endpoint.Abort()
return
}
source := M.SocksaddrFromNet(lAddr)
destination := M.SocksaddrFromNet(rAddr)
pErr := t.handler.PrepareConnection(source, destination)
if pErr != nil {
gWriteUnreachable(t.stack, r.Packet(), pErr)
r.Packet().DecRef()
return
}
go func() { go func() {
ctx, conn := canceler.NewPacketConn(t.ctx, bufio.NewUnbindPacketConnWithAddr(udpConn, destination), time.Duration(t.udpTimeout)*time.Second) ctx, conn := canceler.NewPacketConn(t.ctx, bufio.NewUnbindPacketConnWithAddr(gonet.NewUDPConn(&wq, endpoint), destination), t.udpTimeout)
t.handler.NewPacketConnectionEx(ctx, conn, source, destination, nil) t.handler.NewPacketConnectionEx(ctx, conn, source, destination, nil)
}() }()
}) })

View file

@ -199,15 +199,15 @@ func gWriteUnreachable(gStack *stack.Stack, packet *stack.PacketBuffer, err erro
return nil return nil
} else if errors.Is(err, syscall.ENETUNREACH) { } else if errors.Is(err, syscall.ENETUNREACH) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber { if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPNetProhibited) return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPNetUnreachable)
} else { } else {
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute) return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute)
} }
} else if errors.Is(err, syscall.EHOSTUNREACH) { } else if errors.Is(err, syscall.EHOSTUNREACH) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber { if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPHostProhibited) return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPHostUnreachable)
} else { } else {
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute) return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPAddrUnreachable)
} }
} else if errors.Is(err, syscall.ECONNREFUSED) { } else if errors.Is(err, syscall.ECONNREFUSED) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber { if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {

View file

@ -57,7 +57,7 @@ func (f *UDPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Pac
func rangeIterate(r stack.Range, fn func(*buffer.View)) func rangeIterate(r stack.Range, fn func(*buffer.View))
func (f *UDPForwarder) PreparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { func (f *UDPForwarder) PreparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
pErr := f.handler.PrepareConnection(source, destination) pErr := f.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil { if pErr != nil {
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer), pErr) gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer), pErr)
return false, nil, nil, nil return false, nil, nil, nil

View file

@ -3,8 +3,6 @@
package tun package tun
import ( import (
"time"
"github.com/sagernet/gvisor/pkg/buffer" "github.com/sagernet/gvisor/pkg/buffer"
"github.com/sagernet/gvisor/pkg/tcpip" "github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet" "github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
@ -18,6 +16,7 @@ import (
"github.com/sagernet/sing/common/canceler" "github.com/sagernet/sing/common/canceler"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
type Mixed struct { type Mixed struct {
@ -51,23 +50,22 @@ func (m *Mixed) Start() error {
return err return err
} }
if !m.endpointIndependentNat { if !m.endpointIndependentNat {
udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) { udpForwarder := udp.NewForwarder(ipStack, func(r *udp.ForwarderRequest) {
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
pErr := m.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil {
gWriteUnreachable(m.stack, r.Packet(), err)
r.Packet().DecRef()
return
}
var wq waiter.Queue var wq waiter.Queue
endpoint, err := request.CreateEndpoint(&wq) endpoint, err := r.CreateEndpoint(&wq)
if err != nil { if err != nil {
return return
} }
udpConn := gonet.NewUDPConn(&wq, endpoint)
lAddr := udpConn.RemoteAddr()
rAddr := udpConn.LocalAddr()
if lAddr == nil || rAddr == nil {
endpoint.Abort()
return
}
go func() { go func() {
source := M.SocksaddrFromNet(lAddr) ctx, conn := canceler.NewPacketConn(m.ctx, bufio.NewUnbindPacketConnWithAddr(gonet.NewUDPConn(&wq, endpoint), destination), m.udpTimeout)
destination := M.SocksaddrFromNet(rAddr)
ctx, conn := canceler.NewPacketConn(m.ctx, bufio.NewUnbindPacketConnWithAddr(udpConn, destination), time.Duration(m.udpTimeout)*time.Second)
m.handler.NewPacketConnectionEx(ctx, conn, source, destination, nil) m.handler.NewPacketConnectionEx(ctx, conn, source, destination, nil)
}() }()
}) })
@ -229,7 +227,7 @@ func (m *Mixed) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
} }
switch ipHdr.TransportProtocol() { switch ipHdr.TransportProtocol() {
case header.TCPProtocolNumber: case header.TCPProtocolNumber:
err = m.processIPv6TCP(ipHdr, ipHdr.Payload()) writeBack, err = m.processIPv6TCP(ipHdr, ipHdr.Payload())
case header.UDPProtocolNumber: case header.UDPProtocolNumber:
writeBack = false writeBack = false
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{

View file

@ -2,6 +2,7 @@ package tun
import ( import (
"context" "context"
"errors"
"net" "net"
"net/netip" "net/netip"
"syscall" "syscall"
@ -258,10 +259,10 @@ func (s *System) processPacket(packet []byte) bool {
writeBack bool writeBack bool
err error err error
) )
switch ipVersion := packet[0] >> 4; ipVersion { switch ipVersion := header.IPVersion(packet); ipVersion {
case 4: case header.IPv4Version:
writeBack, err = s.processIPv4(packet) writeBack, err = s.processIPv4(packet)
case 6: case header.IPv6Version:
writeBack, err = s.processIPv6(packet) writeBack, err = s.processIPv6(packet)
default: default:
err = E.New("ip: unknown version: ", ipVersion) err = E.New("ip: unknown version: ", ipVersion)
@ -306,11 +307,11 @@ func (s *System) acceptLoop(listener net.Listener) {
} }
func (s *System) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) { func (s *System) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) {
writeBack = true
destination := ipHdr.DestinationAddr() destination := ipHdr.DestinationAddr()
if destination == s.broadcastAddr || !destination.IsGlobalUnicast() { if destination == s.broadcastAddr || !destination.IsGlobalUnicast() {
return return
} }
writeBack = true
switch ipHdr.TransportProtocol() { switch ipHdr.TransportProtocol() {
case header.TCPProtocolNumber: case header.TCPProtocolNumber:
writeBack, err = s.processIPv4TCP(ipHdr, ipHdr.Payload()) writeBack, err = s.processIPv4TCP(ipHdr, ipHdr.Payload())
@ -324,13 +325,13 @@ func (s *System) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) {
} }
func (s *System) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) { func (s *System) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
writeBack = true
if !ipHdr.DestinationAddr().IsGlobalUnicast() { if !ipHdr.DestinationAddr().IsGlobalUnicast() {
return return
} }
writeBack = true
switch ipHdr.TransportProtocol() { switch ipHdr.TransportProtocol() {
case header.TCPProtocolNumber: case header.TCPProtocolNumber:
err = s.processIPv6TCP(ipHdr, ipHdr.Payload()) writeBack, err = s.processIPv6TCP(ipHdr, ipHdr.Payload())
case header.UDPProtocolNumber: case header.UDPProtocolNumber:
err = s.processIPv6UDP(ipHdr, ipHdr.Payload()) err = s.processIPv6UDP(ipHdr, ipHdr.Payload())
case header.ICMPv6ProtocolNumber: case header.ICMPv6ProtocolNumber:
@ -343,7 +344,7 @@ func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, err
source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort()) source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort())
destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort()) destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort())
if !destination.Addr().IsGlobalUnicast() { if !destination.Addr().IsGlobalUnicast() {
return true, nil return false, nil
} else if source.Addr() == s.inet4ServerAddress && source.Port() == s.tcpPort { } else if source.Addr() == s.inet4ServerAddress && source.Port() == s.tcpPort {
session := s.tcpNat.LookupBack(destination.Port()) session := s.tcpNat.LookupBack(destination.Port())
if session == nil { if session == nil {
@ -356,8 +357,17 @@ func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, err
} else { } else {
natPort, err := s.tcpNat.Lookup(source, destination, s.handler) natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
if err != nil { if err != nil {
// TODO: implement ICMP port unreachable if errors.Is(err, ErrDrop) {
return false, nil return false, nil
} else if errors.Is(err, syscall.ENETUNREACH) {
return false, s.rejectIPv4WithICMP(ipHdr, header.ICMPv4NetUnreachable)
} else if errors.Is(err, syscall.EHOSTUNREACH) {
return false, s.rejectIPv4WithICMP(ipHdr, header.ICMPv4HostUnreachable)
} else if errors.Is(err, syscall.ECONNREFUSED) {
return false, s.rejectIPv4WithICMP(ipHdr, header.ICMPv4PortUnreachable)
} else {
return false, s.resetIPv4TCP(ipHdr, tcpHdr)
}
} }
ipHdr.SetSourceAddr(s.inet4Address) ipHdr.SetSourceAddr(s.inet4Address)
tcpHdr.SetSourcePort(natPort) tcpHdr.SetSourcePort(natPort)
@ -377,33 +387,84 @@ func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, err
return true, nil return true, nil
} }
func (s *System) resetIPv4TCP(packet header.IPv4, header header.TCP) error { func (s *System) resetIPv4TCP(origIPHdr header.IPv4, origTCPHdr header.TCP) error {
return nil 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 {
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
} else {
newPacket.Advance(-s.frontHeadroom)
}
return common.Error(s.tun.Write(newPacket.Bytes()))
} }
func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) error { func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) (bool, error) {
source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort()) source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort())
destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort()) destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort())
if !destination.Addr().IsGlobalUnicast() { if !destination.Addr().IsGlobalUnicast() {
return nil return false, nil
} else if source.Addr() == s.inet6ServerAddress && source.Port() == s.tcpPort6 { } else if source.Addr() == s.inet6ServerAddress && source.Port() == s.tcpPort6 {
session := s.tcpNat.LookupBack(destination.Port()) session := s.tcpNat.LookupBack(destination.Port())
if session == nil { if session == nil {
return E.New("ipv6: tcp: session not found: ", destination.Port()) return false, E.New("ipv6: tcp: session not found: ", destination.Port())
} }
ipHdr.SetSourceAddr(session.Destination.Addr()) ipHdr.SetSourceAddr(session.Destination.Addr())
tcpHdr.SetSourcePort(session.Destination.Port()) tcpHdr.SetSourcePort(session.Destination.Port())
ipHdr.SetSourceAddr(session.Source.Addr()) ipHdr.SetDestinationAddr(session.Source.Addr())
tcpHdr.SetDestinationPort(session.Source.Port()) tcpHdr.SetDestinationPort(session.Source.Port())
} else { } else {
natPort, err := s.tcpNat.Lookup(source, destination, s.handler) natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
if err != nil { if err != nil {
// TODO: implement ICMP port unreachable if errors.Is(err, ErrDrop) {
return nil return false, nil
} else if errors.Is(err, syscall.ENETUNREACH) {
return false, s.rejectIPv6WithICMP(ipHdr, header.ICMPv6NetworkUnreachable)
} else if errors.Is(err, syscall.EHOSTUNREACH) {
return false, s.rejectIPv6WithICMP(ipHdr, header.ICMPv6AddressUnreachable)
} else if errors.Is(err, syscall.ECONNREFUSED) {
return false, s.rejectIPv6WithICMP(ipHdr, header.ICMPv6PortUnreachable)
} else {
return false, s.resetIPv6TCP(ipHdr, tcpHdr)
}
} }
ipHdr.SetSourceAddr(s.inet6Address) ipHdr.SetSourceAddr(s.inet6Address)
tcpHdr.SetSourcePort(natPort) tcpHdr.SetSourcePort(natPort)
ipHdr.SetSourceAddr(s.inet6ServerAddress) ipHdr.SetDestinationAddr(s.inet6ServerAddress)
tcpHdr.SetDestinationPort(s.tcpPort6) tcpHdr.SetDestinationPort(s.tcpPort6)
} }
if !s.txChecksumOffload { if !s.txChecksumOffload {
@ -414,7 +475,51 @@ func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) error {
} else { } else {
tcpHdr.SetChecksum(0) tcpHdr.SetChecksum(0)
} }
return nil 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 {
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
} else {
newPacket.Advance(-s.frontHeadroom)
}
return common.Error(s.tun.Write(newPacket.Bytes()))
} }
func (s *System) processIPv4UDP(ipHdr header.IPv4, udpHdr header.UDP) error { func (s *System) processIPv4UDP(ipHdr header.IPv4, udpHdr header.UDP) error {
@ -444,9 +549,28 @@ func (s *System) processIPv6UDP(ipHdr header.IPv6, udpHdr header.UDP) error {
} }
func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
pErr := s.handler.PrepareConnection(source, destination) pErr := s.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil { if pErr != nil {
// TODO: implement ICMP port unreachable if errors.Is(pErr, ErrDrop) {
} else if source.IsIPv4() {
ipHdr := userData.(header.IPv4)
if errors.Is(pErr, syscall.ENETUNREACH) {
s.rejectIPv4WithICMP(ipHdr, header.ICMPv4NetUnreachable)
} else if errors.Is(pErr, syscall.EHOSTUNREACH) {
s.rejectIPv4WithICMP(ipHdr, header.ICMPv4HostUnreachable)
} else {
s.rejectIPv4WithICMP(ipHdr, header.ICMPv4PortUnreachable)
}
} else {
ipHdr := userData.(header.IPv6)
if errors.Is(pErr, syscall.ENETUNREACH) {
s.rejectIPv6WithICMP(ipHdr, header.ICMPv6NetworkUnreachable)
} else if errors.Is(pErr, syscall.EHOSTUNREACH) {
s.rejectIPv6WithICMP(ipHdr, header.ICMPv6AddressUnreachable)
} else {
s.rejectIPv6WithICMP(ipHdr, header.ICMPv6PortUnreachable)
}
}
return false, nil, nil, nil return false, nil, nil, nil
} }
var writer N.PacketWriter var writer N.PacketWriter
@ -492,6 +616,45 @@ func (s *System) processIPv4ICMP(ipHdr header.IPv4, icmpHdr header.ICMPv4) error
return nil 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 { func (s *System) processIPv6ICMP(ipHdr header.IPv6, icmpHdr header.ICMPv6) error {
if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 { if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 {
return nil return nil
@ -508,57 +671,50 @@ func (s *System) processIPv6ICMP(ipHdr header.IPv6, icmpHdr header.ICMPv6) error
return nil return nil
} }
/*func (s *System) WritePacket4(buffer *buf.Buffer, source netip.AddrPort, destination netip.AddrPort) error { func (s *System) rejectIPv6WithICMP(ipHdr header.IPv6, code header.ICMPv6Code) error {
packet := buf.Get(header.IPv4MinimumSize + header.UDPMinimumSize + buffer.Len()) frontHeadroom := s.frontHeadroom + PacketOffset
ipHdr := header.IPv4(packet) mtu := s.mtu
ipHdr.Encode(&header.IPv4Fields{ const maxIPv6Data = header.IPv6MinimumMTU - header.IPv6FixedHeaderSize
TotalLength: uint16(len(packet)), if mtu > maxIPv6Data {
Protocol: uint8(header.UDPProtocolNumber), mtu = maxIPv6Data
SrcAddr: source.Addr(),
DstAddr: destination.Addr(),
})
ipHdr.SetHeaderLength(header.IPv4MinimumSize)
udpHdr := header.UDP(ipHdr.Payload())
udpHdr.Encode(&header.UDPFields{
SrcPort: source.Port(),
DstPort: destination.Port(),
Length: uint16(header.UDPMinimumSize + buffer.Len()),
})
copy(udpHdr.Payload(), buffer.Bytes())
if !s.txChecksumOffload {
...
} else {
udpHdr.SetChecksum(0)
} }
ipHdr.SetChecksum(0) available := mtu - header.ICMPv6ErrorHeaderSize
ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) if available < header.IPv6MinimumSize {
return common.Error(s.tun.Write(packet)) 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 {
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
} else {
newPacket.Advance(-s.frontHeadroom)
}
return common.Error(s.tun.Write(newPacket.Bytes()))
} }
func (s *System) WritePacket6(buffer *buf.Buffer, source netip.AddrPort, destination netip.AddrPort) error {
packet := buf.Get(header.IPv6MinimumSize + header.UDPMinimumSize + buffer.Len())
ipHdr := header.IPv6(packet)
ipHdr.Encode(&header.IPv6Fields{
PayloadLength: uint16(header.UDPMinimumSize + buffer.Len()),
TransportProtocol: header.UDPProtocolNumber,
SrcAddr: source.Addr(),
DstAddr: destination.Addr(),
})
udpHdr := header.UDP(ipHdr.Payload())
udpHdr.Encode(&header.UDPFields{
SrcPort: source.Port(),
DstPort: destination.Port(),
Length: uint16(header.UDPMinimumSize + buffer.Len()),
})
copy(udpHdr.Payload(), buffer.Bytes())
if !s.txChecksumOffload {
...
} else {
udpHdr.SetChecksum(0)
}
return common.Error(s.tun.Write(packet))
}*/
type systemUDPPacketWriter4 struct { type systemUDPPacketWriter4 struct {
tun Tun tun Tun
frontHeadroom int frontHeadroom int

View file

@ -7,6 +7,7 @@ import (
"time" "time"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
type TCPNat struct { type TCPNat struct {
@ -77,7 +78,7 @@ func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort, handl
if loaded { if loaded {
return port, nil return port, nil
} }
pErr := handler.PrepareConnection(M.SocksaddrFromNetIP(source), M.SocksaddrFromNetIP(destination)) pErr := handler.PrepareConnection(N.NetworkTCP, M.SocksaddrFromNetIP(source), M.SocksaddrFromNetIP(destination))
if pErr != nil { if pErr != nil {
return 0, pErr return 0, pErr
} }

2
tun.go
View file

@ -16,7 +16,7 @@ import (
) )
type Handler interface { type Handler interface {
PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error
N.TCPConnectionHandlerEx N.TCPConnectionHandlerEx
N.UDPConnectionHandlerEx N.UDPConnectionHandlerEx
} }