Remove unused ICMP replies & Improve tcpbuf options

This commit is contained in:
世界 2024-11-06 17:10:13 +08:00
parent 24206c3edd
commit a5cb9f4f5f
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 122 additions and 289 deletions

View file

@ -5,7 +5,7 @@ package tun
import ( import (
"context" "context"
"net/netip" "net/netip"
"os" "runtime"
"time" "time"
"github.com/sagernet/gvisor/pkg/tcpip" "github.com/sagernet/gvisor/pkg/tcpip"
@ -17,6 +17,7 @@ import (
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp" "github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp" "github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp" "github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/gvisor/pkg/waiter"
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"
@ -77,17 +78,35 @@ func (t *GVisor) Start() error {
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort) destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
pErr := t.handler.PrepareConnection(N.NetworkTCP, 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(pErr != ErrDrop)
return return
} }
conn := &gLazyConn{ var (
parentCtx: t.ctx, wq waiter.Queue
stack: t.stack, endpoint tcpip.Endpoint
request: r, tErr tcpip.Error
localAddr: source.TCPAddr(), )
remoteAddr: destination.TCPAddr(), handshakeCtx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-t.ctx.Done():
wq.Notify(wq.Events())
case <-handshakeCtx.Done():
}
}()
endpoint, tErr = r.CreateEndpoint(&wq)
cancel()
if tErr != nil {
r.Complete(true)
return
} }
go t.handler.NewConnectionEx(t.ctx, conn, source, destination, nil) r.Complete(false)
endpoint.SocketOptions().SetKeepAlive(true)
keepAliveIdle := tcpip.KeepaliveIdleOption(15 * time.Second)
endpoint.SetSockOpt(&keepAliveIdle)
keepAliveInterval := tcpip.KeepaliveIntervalOption(15 * time.Second)
endpoint.SetSockOpt(&keepAliveInterval)
go t.handler.NewConnectionEx(t.ctx, gonet.NewTCPConn(&wq, endpoint), source, destination, nil)
}) })
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket) ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
@ -134,30 +153,47 @@ func newGVisorStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
icmp.NewProtocol6, icmp.NewProtocol6,
}, },
}) })
tErr := ipStack.CreateNIC(defaultNIC, ep) err := ipStack.CreateNIC(defaultNIC, ep)
if tErr != nil { if err != nil {
return nil, E.New("create nic: ", gonet.TranslateNetstackError(tErr)) return nil, gonet.TranslateNetstackError(err)
} }
ipStack.SetRouteTable([]tcpip.Route{ ipStack.SetRouteTable([]tcpip.Route{
{Destination: header.IPv4EmptySubnet, NIC: defaultNIC}, {Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
{Destination: header.IPv6EmptySubnet, NIC: defaultNIC}, {Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
}) })
ipStack.SetSpoofing(defaultNIC, true) err = ipStack.SetSpoofing(defaultNIC, true)
ipStack.SetPromiscuousMode(defaultNIC, true) if err != nil {
bufSize := 20 * 1024 return nil, gonet.TranslateNetstackError(err)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{ }
Min: 1, err = ipStack.SetPromiscuousMode(defaultNIC, true)
Default: bufSize, if err != nil {
Max: bufSize, return nil, gonet.TranslateNetstackError(err)
}) }
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{
Min: 1,
Default: bufSize,
Max: bufSize,
})
sOpt := tcpip.TCPSACKEnabled(true) sOpt := tcpip.TCPSACKEnabled(true)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt) ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
mOpt := tcpip.TCPModerateReceiveBufferOption(true) mOpt := tcpip.TCPModerateReceiveBufferOption(true)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt) ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
if runtime.GOOS == "windows" {
tcpRecoveryOpt := tcpip.TCPRecovery(0)
err = ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpRecoveryOpt)
}
tcpRXBufOpt := tcpip.TCPReceiveBufferSizeRangeOption{
Min: tcpRXBufMinSize,
Default: tcpRXBufDefSize,
Max: tcpRXBufMaxSize,
}
err = ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpRXBufOpt)
if err != nil {
return nil, gonet.TranslateNetstackError(err)
}
tcpTXBufOpt := tcpip.TCPSendBufferSizeRangeOption{
Min: tcpTXBufMinSize,
Default: tcpTXBufDefSize,
Max: tcpTXBufMaxSize,
}
err = ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpTXBufOpt)
if err != nil {
return nil, gonet.TranslateNetstackError(err)
}
return ipStack, nil return ipStack, nil
} }

View file

@ -1,228 +0,0 @@
//go:build with_gvisor
package tun
import (
"context"
"errors"
"net"
"os"
"syscall"
"time"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/waiter"
)
type gLazyConn struct {
tcpConn *gonet.TCPConn
parentCtx context.Context
stack *stack.Stack
request *tcp.ForwarderRequest
localAddr net.Addr
remoteAddr net.Addr
handshakeDone bool
handshakeErr error
}
func (c *gLazyConn) HandshakeContext(ctx context.Context) error {
if c.handshakeDone {
return nil
}
defer func() {
c.handshakeDone = true
}()
var (
wq waiter.Queue
endpoint tcpip.Endpoint
)
handshakeCtx, cancel := context.WithCancel(ctx)
go func() {
select {
case <-c.parentCtx.Done():
wq.Notify(wq.Events())
case <-handshakeCtx.Done():
}
}()
endpoint, err := c.request.CreateEndpoint(&wq)
cancel()
if err != nil {
gErr := gonet.TranslateNetstackError(err)
c.handshakeErr = gErr
c.request.Complete(true)
return gErr
}
c.request.Complete(false)
endpoint.SocketOptions().SetKeepAlive(true)
keepAliveIdle := tcpip.KeepaliveIdleOption(15 * time.Second)
endpoint.SetSockOpt(&keepAliveIdle)
keepAliveInterval := tcpip.KeepaliveIntervalOption(15 * time.Second)
endpoint.SetSockOpt(&keepAliveInterval)
tcpConn := gonet.NewTCPConn(&wq, endpoint)
c.tcpConn = tcpConn
return nil
}
func (c *gLazyConn) HandshakeFailure(err error) error {
if c.handshakeDone {
return nil
}
c.request.Complete(gWriteUnreachable(c.stack, c.request.Packet(), err) == os.ErrInvalid)
c.handshakeDone = true
c.handshakeErr = err
return nil
}
func (c *gLazyConn) HandshakeSuccess() error {
return c.HandshakeContext(context.Background())
}
func (c *gLazyConn) Read(b []byte) (n int, err error) {
if !c.handshakeDone {
err = c.HandshakeContext(context.Background())
if err != nil {
return
}
} else if c.handshakeErr != nil {
return 0, c.handshakeErr
}
return c.tcpConn.Read(b)
}
func (c *gLazyConn) Write(b []byte) (n int, err error) {
if !c.handshakeDone {
err = c.HandshakeContext(context.Background())
if err != nil {
return
}
} else if c.handshakeErr != nil {
return 0, c.handshakeErr
}
return c.tcpConn.Write(b)
}
func (c *gLazyConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *gLazyConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *gLazyConn) SetDeadline(t time.Time) error {
if !c.handshakeDone {
err := c.HandshakeContext(context.Background())
if err != nil {
return err
}
} else if c.handshakeErr != nil {
return c.handshakeErr
}
return c.tcpConn.SetDeadline(t)
}
func (c *gLazyConn) SetReadDeadline(t time.Time) error {
if !c.handshakeDone {
err := c.HandshakeContext(context.Background())
if err != nil {
return err
}
} else if c.handshakeErr != nil {
return c.handshakeErr
}
return c.tcpConn.SetReadDeadline(t)
}
func (c *gLazyConn) SetWriteDeadline(t time.Time) error {
if !c.handshakeDone {
err := c.HandshakeContext(context.Background())
if err != nil {
return err
}
} else if c.handshakeErr != nil {
return c.handshakeErr
}
return c.tcpConn.SetWriteDeadline(t)
}
func (c *gLazyConn) Close() error {
if !c.handshakeDone {
c.request.Complete(true)
c.handshakeErr = net.ErrClosed
return nil
} else if c.handshakeErr != nil {
return nil
}
return c.tcpConn.Close()
}
func (c *gLazyConn) CloseRead() error {
if !c.handshakeDone {
c.request.Complete(true)
c.handshakeErr = net.ErrClosed
return nil
} else if c.handshakeErr != nil {
return nil
}
return c.tcpConn.CloseRead()
}
func (c *gLazyConn) CloseWrite() error {
if !c.handshakeDone {
c.request.Complete(true)
c.handshakeErr = net.ErrClosed
return nil
} else if c.handshakeErr != nil {
return nil
}
return c.tcpConn.CloseRead()
}
func (c *gLazyConn) ReaderReplaceable() bool {
return c.handshakeDone && c.handshakeErr == nil
}
func (c *gLazyConn) WriterReplaceable() bool {
return c.handshakeDone && c.handshakeErr == nil
}
func (c *gLazyConn) Upstream() any {
return c.tcpConn
}
func gWriteUnreachable(gStack *stack.Stack, packet *stack.PacketBuffer, err error) error {
if errors.Is(err, ErrDrop) {
return nil
} else if errors.Is(err, syscall.ENETUNREACH) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPNetUnreachable)
} else {
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute)
}
} else if errors.Is(err, syscall.EHOSTUNREACH) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPHostUnreachable)
} else {
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPAddrUnreachable)
}
} else if errors.Is(err, syscall.ECONNREFUSED) {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPPortUnreachable)
} else {
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPPortUnreachable)
}
}
return os.ErrInvalid
}
func gWriteUnreachable4(gStack *stack.Stack, packet *stack.PacketBuffer, icmpCode stack.RejectIPv4WithICMPType) error {
return gonet.TranslateNetstackError(gStack.NetworkProtocolInstance(header.IPv4ProtocolNumber).(stack.RejectIPv4WithHandler).SendRejectionError(packet, icmpCode, true))
}
func gWriteUnreachable6(gStack *stack.Stack, packet *stack.PacketBuffer, icmpCode stack.RejectIPv6WithICMPType) error {
return gonet.TranslateNetstackError(gStack.NetworkProtocolInstance(header.IPv6ProtocolNumber).(stack.RejectIPv6WithHandler).SendRejectionError(packet, icmpCode, true))
}

View file

@ -0,0 +1,18 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build with_gvisor && !ios
package tun
import "github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
const (
tcpRXBufMinSize = tcp.MinBufferSize
tcpRXBufDefSize = tcp.DefaultSendBufferSize
tcpRXBufMaxSize = 8 << 20 // 8MiB
tcpTXBufMinSize = tcp.MinBufferSize
tcpTXBufDefSize = tcp.DefaultReceiveBufferSize
tcpTXBufMaxSize = 6 << 20 // 6MiB
)

View file

@ -0,0 +1,21 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build with_gvisor
package tun
import "github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
const (
// tcp{RX,TX}Buf{Min,Def,Max}Size mirror gVisor defaults. We leave these
// unchanged on iOS for now as to not increase pressure towards the
// NetworkExtension memory limit.
tcpRXBufMinSize = tcp.MinBufferSize
tcpRXBufDefSize = tcp.DefaultSendBufferSize
tcpRXBufMaxSize = tcp.MaxBufferSize
tcpTXBufMinSize = tcp.MinBufferSize
tcpTXBufDefSize = tcp.DefaultReceiveBufferSize
tcpTXBufMaxSize = tcp.MaxBufferSize
)

View file

@ -59,7 +59,9 @@ 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(N.NetworkUDP, source, destination) pErr := f.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil { if pErr != nil {
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer), pErr) if pErr != ErrDrop {
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer), pErr)
}
return false, nil, nil, nil return false, nil, nil, nil
} }
var sourceNetwork tcpip.NetworkProtocolNumber var sourceNetwork tcpip.NetworkProtocolNumber
@ -147,3 +149,11 @@ func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Sock
route.Stats().UDP.PacketsSent.Increment() route.Stats().UDP.PacketsSent.Increment()
return nil return nil
} }
func gWriteUnreachable(gStack *stack.Stack, packet *stack.PacketBuffer, err error) error {
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
return gonet.TranslateNetstackError(gStack.NetworkProtocolInstance(header.IPv4ProtocolNumber).(stack.RejectIPv4WithHandler).SendRejectionError(packet, stack.RejectIPv4WithICMPPortUnreachable, true))
} else {
return gonet.TranslateNetstackError(gStack.NetworkProtocolInstance(header.IPv6ProtocolNumber).(stack.RejectIPv6WithHandler).SendRejectionError(packet, stack.RejectIPv6WithICMPPortUnreachable, true))
}
}

View file

@ -2,7 +2,6 @@ package tun
import ( import (
"context" "context"
"errors"
"net" "net"
"net/netip" "net/netip"
"syscall" "syscall"
@ -357,14 +356,8 @@ 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 {
if errors.Is(err, ErrDrop) { if 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 { } else {
return false, s.resetIPv4TCP(ipHdr, tcpHdr) return false, s.resetIPv4TCP(ipHdr, tcpHdr)
} }
@ -450,14 +443,8 @@ func (s *System) processIPv6TCP(ipHdr header.IPv6, 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 {
if errors.Is(err, ErrDrop) { if err == ErrDrop {
return false, 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 { } else {
return false, s.resetIPv6TCP(ipHdr, tcpHdr) return false, s.resetIPv6TCP(ipHdr, tcpHdr)
} }
@ -551,23 +538,12 @@ 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(N.NetworkUDP, source, destination) pErr := s.handler.PrepareConnection(N.NetworkUDP, source, destination)
if pErr != nil { if pErr != nil {
if errors.Is(pErr, ErrDrop) { if pErr != ErrDrop {
} else if source.IsIPv4() { if source.IsIPv4() {
ipHdr := userData.(header.IPv4) 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) 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 { } else {
ipHdr := userData.(header.IPv6)
s.rejectIPv6WithICMP(ipHdr, header.ICMPv6PortUnreachable) s.rejectIPv6WithICMP(ipHdr, header.ICMPv6PortUnreachable)
} }
} }

View file

@ -224,7 +224,6 @@ func open(name string, vnetHdr bool) (int, error) {
func (t *NativeTun) configure(tunLink netlink.Link) error { func (t *NativeTun) configure(tunLink netlink.Link) error {
err := netlink.LinkSetMTU(tunLink, int(t.options.MTU)) err := netlink.LinkSetMTU(tunLink, int(t.options.MTU))
if errors.Is(err, unix.EPERM) { if errors.Is(err, unix.EPERM) {
// unprivileged
return nil return nil
} else if err != nil { } else if err != nil {
return err return err
@ -293,16 +292,17 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
} }
func (t *NativeTun) Start() error { func (t *NativeTun) Start() error {
if t.options.FileDescriptor != 0 {
return nil
}
tunLink, err := netlink.LinkByName(t.options.Name) tunLink, err := netlink.LinkByName(t.options.Name)
if err != nil { if err != nil {
return err return err
} }
err = netlink.LinkSetUp(tunLink) err = netlink.LinkSetUp(tunLink)
if errors.Is(err, unix.EPERM) { if err != nil {
// unprivileged
return nil
} else if err != nil {
return err return err
} }