Skip to content

Commit 5d906c1

Browse files
committed
Add local redirect
1 parent d3c7ff5 commit 5d906c1

File tree

4 files changed

+163
-87
lines changed

4 files changed

+163
-87
lines changed

stack.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ type Stack interface {
2020
}
2121

2222
type StackOptions struct {
23-
Context context.Context
24-
Tun Tun
25-
TunOptions Options
26-
UDPTimeout time.Duration
27-
Handler Handler
28-
Logger logger.Logger
29-
ForwarderBindInterface bool
30-
IncludeAllNetworks bool
31-
InterfaceFinder control.InterfaceFinder
23+
Context context.Context
24+
Tun Tun
25+
TunOptions Options
26+
UDPTimeout time.Duration
27+
Inet4LocalRedirectAddress []netip.Addr
28+
Inet6LocalRedirectAddress []netip.Addr
29+
Handler Handler
30+
Logger logger.Logger
31+
ForwarderBindInterface bool
32+
IncludeAllNetworks bool
33+
InterfaceFinder control.InterfaceFinder
3234
}
3335

3436
func NewStack(

stack_gvisor.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ const WithGVisor = true
2626
const DefaultNIC tcpip.NICID = 1
2727

2828
type GVisor struct {
29-
ctx context.Context
30-
tun GVisorTun
31-
udpTimeout time.Duration
32-
broadcastAddr netip.Addr
33-
handler Handler
34-
logger logger.Logger
35-
stack *stack.Stack
36-
endpoint stack.LinkEndpoint
29+
ctx context.Context
30+
tun GVisorTun
31+
udpTimeout time.Duration
32+
inet4LocalRedirectAddress []netip.Addr
33+
inet6LocalRedirectAddress []netip.Addr
34+
broadcastAddr netip.Addr
35+
handler Handler
36+
logger logger.Logger
37+
stack *stack.Stack
38+
endpoint stack.LinkEndpoint
3739
}
3840

3941
type GVisorTun interface {
@@ -50,12 +52,14 @@ func NewGVisor(
5052
}
5153

5254
gStack := &GVisor{
53-
ctx: options.Context,
54-
tun: gTun,
55-
udpTimeout: options.UDPTimeout,
56-
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
57-
handler: options.Handler,
58-
logger: options.Logger,
55+
ctx: options.Context,
56+
tun: gTun,
57+
udpTimeout: options.UDPTimeout,
58+
inet4LocalRedirectAddress: options.Inet4LocalRedirectAddress,
59+
inet6LocalRedirectAddress: options.Inet6LocalRedirectAddress,
60+
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
61+
handler: options.Handler,
62+
logger: options.Logger,
5963
}
6064
return gStack, nil
6165
}
@@ -70,7 +74,7 @@ func (t *GVisor) Start() error {
7074
if err != nil {
7175
return err
7276
}
73-
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTCPForwarder(t.ctx, ipStack, t.handler).HandlePacket)
77+
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTCPForwarderWithLocalRedirect(t.ctx, ipStack, t.handler, t.inet4LocalRedirectAddress, t.inet6LocalRedirectAddress, t.tun).HandlePacket)
7478
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
7579
t.stack = ipStack
7680
t.endpoint = linkEndpoint

stack_gvisor_tcp.go

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,75 @@ package tun
44

55
import (
66
"context"
7+
"net/netip"
78

9+
"github.com/sagernet/gvisor/pkg/tcpip"
10+
"github.com/sagernet/gvisor/pkg/tcpip/header"
811
"github.com/sagernet/gvisor/pkg/tcpip/stack"
912
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
13+
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
14+
"github.com/sagernet/sing/common"
15+
"github.com/sagernet/sing/common/bufio"
1016
M "github.com/sagernet/sing/common/metadata"
1117
N "github.com/sagernet/sing/common/network"
1218
)
1319

1420
type TCPForwarder struct {
15-
ctx context.Context
16-
stack *stack.Stack
17-
handler Handler
18-
forwarder *tcp.Forwarder
21+
ctx context.Context
22+
stack *stack.Stack
23+
handler Handler
24+
inet4LocalRedirectAddress []tcpip.Address
25+
inet6LocalRedirectAddress []tcpip.Address
26+
tun GVisorTun
27+
forwarder *tcp.Forwarder
1928
}
2029

2130
func NewTCPForwarder(ctx context.Context, stack *stack.Stack, handler Handler) *TCPForwarder {
31+
return NewTCPForwarderWithLocalRedirect(ctx, stack, handler, nil, nil, nil)
32+
}
33+
34+
func NewTCPForwarderWithLocalRedirect(ctx context.Context, stack *stack.Stack, handler Handler, inet4LocalRedirectAddress []netip.Addr, inet6LocalRedirectAddress []netip.Addr, tun GVisorTun) *TCPForwarder {
2235
forwarder := &TCPForwarder{
23-
ctx: ctx,
24-
stack: stack,
25-
handler: handler,
36+
ctx: ctx,
37+
stack: stack,
38+
handler: handler,
39+
inet4LocalRedirectAddress: common.Map(inet4LocalRedirectAddress, AddressFromAddr),
40+
inet6LocalRedirectAddress: common.Map(inet6LocalRedirectAddress, AddressFromAddr),
41+
tun: tun,
2642
}
2743
forwarder.forwarder = tcp.NewForwarder(stack, 0, 1024, forwarder.Forward)
2844
return forwarder
2945
}
3046

3147
func (f *TCPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
48+
for _, inet4LocalRedirectAddress := range f.inet4LocalRedirectAddress {
49+
if id.LocalAddress == inet4LocalRedirectAddress {
50+
ipHdr := pkt.Network().(header.IPv4)
51+
ipHdr.SetDestinationAddressWithChecksumUpdate(ipHdr.SourceAddress())
52+
ipHdr.SetSourceAddressWithChecksumUpdate(inet4LocalRedirectAddress)
53+
tcpHdr := header.TCP(pkt.TransportHeader().Slice())
54+
tcpHdr.SetChecksum(0)
55+
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
56+
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
57+
)))
58+
bufio.WriteVectorised(f.tun, pkt.AsSlices())
59+
return true
60+
}
61+
}
62+
for _, inet6LocalRedirectAddress := range f.inet6LocalRedirectAddress {
63+
if id.LocalAddress == inet6LocalRedirectAddress {
64+
ipHdr := pkt.Network().(header.IPv6)
65+
ipHdr.SetDestinationAddress(ipHdr.SourceAddress())
66+
ipHdr.SetSourceAddress(inet6LocalRedirectAddress)
67+
tcpHdr := header.TCP(pkt.TransportHeader().Slice())
68+
tcpHdr.SetChecksum(0)
69+
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
70+
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
71+
)))
72+
bufio.WriteVectorised(f.tun, pkt.AsSlices())
73+
return true
74+
}
75+
}
3276
return f.forwarder.HandlePacket(id, pkt)
3377
}
3478

stack_system.go

Lines changed: 82 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,32 @@ import (
2222
var ErrIncludeAllNetworks = E.New("`system` and `mixed` stack are not available when `includeAllNetworks` is enabled. See https://github.com/SagerNet/sing-tun/issues/25")
2323

2424
type System struct {
25-
ctx context.Context
26-
tun Tun
27-
tunName string
28-
mtu int
29-
handler Handler
30-
logger logger.Logger
31-
inet4Prefixes []netip.Prefix
32-
inet6Prefixes []netip.Prefix
33-
inet4ServerAddress netip.Addr
34-
inet4Address netip.Addr
35-
inet6ServerAddress netip.Addr
36-
inet6Address netip.Addr
37-
broadcastAddr netip.Addr
38-
udpTimeout time.Duration
39-
tcpListener net.Listener
40-
tcpListener6 net.Listener
41-
tcpPort uint16
42-
tcpPort6 uint16
43-
tcpNat *TCPNat
44-
udpNat *udpnat.Service
45-
bindInterface bool
46-
interfaceFinder control.InterfaceFinder
47-
frontHeadroom int
48-
txChecksumOffload bool
25+
ctx context.Context
26+
tun Tun
27+
tunName string
28+
mtu int
29+
handler Handler
30+
logger logger.Logger
31+
inet4Prefixes []netip.Prefix
32+
inet6Prefixes []netip.Prefix
33+
inet4ServerAddress netip.Addr
34+
inet4Address netip.Addr
35+
inet6ServerAddress netip.Addr
36+
inet6Address netip.Addr
37+
broadcastAddr netip.Addr
38+
udpTimeout time.Duration
39+
inet4LocalRedirectAddress []netip.Addr
40+
inet6LocalRedirectAddress []netip.Addr
41+
tcpListener net.Listener
42+
tcpListener6 net.Listener
43+
tcpPort uint16
44+
tcpPort6 uint16
45+
tcpNat *TCPNat
46+
udpNat *udpnat.Service
47+
bindInterface bool
48+
interfaceFinder control.InterfaceFinder
49+
frontHeadroom int
50+
txChecksumOffload bool
4951
}
5052

5153
type Session struct {
@@ -57,18 +59,20 @@ type Session struct {
5759

5860
func NewSystem(options StackOptions) (Stack, error) {
5961
stack := &System{
60-
ctx: options.Context,
61-
tun: options.Tun,
62-
tunName: options.TunOptions.Name,
63-
mtu: int(options.TunOptions.MTU),
64-
udpTimeout: options.UDPTimeout,
65-
handler: options.Handler,
66-
logger: options.Logger,
67-
inet4Prefixes: options.TunOptions.Inet4Address,
68-
inet6Prefixes: options.TunOptions.Inet6Address,
69-
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
70-
bindInterface: options.ForwarderBindInterface,
71-
interfaceFinder: options.InterfaceFinder,
62+
ctx: options.Context,
63+
tun: options.Tun,
64+
tunName: options.TunOptions.Name,
65+
mtu: int(options.TunOptions.MTU),
66+
udpTimeout: options.UDPTimeout,
67+
inet4LocalRedirectAddress: options.Inet4LocalRedirectAddress,
68+
inet6LocalRedirectAddress: options.Inet6LocalRedirectAddress,
69+
handler: options.Handler,
70+
logger: options.Logger,
71+
inet4Prefixes: options.TunOptions.Inet4Address,
72+
inet6Prefixes: options.TunOptions.Inet6Address,
73+
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
74+
bindInterface: options.ForwarderBindInterface,
75+
interfaceFinder: options.InterfaceFinder,
7276
}
7377
if len(options.TunOptions.Inet4Address) > 0 {
7478
if !HasNextAddress(options.TunOptions.Inet4Address[0], 1) {
@@ -352,18 +356,29 @@ func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, err
352356
ipHdr.SetDestinationAddr(session.Source.Addr())
353357
tcpHdr.SetDestinationPort(session.Source.Port())
354358
} else {
355-
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
356-
if err != nil {
357-
if err == ErrDrop {
358-
return false, nil
359-
} else {
360-
return false, s.resetIPv4TCP(ipHdr, tcpHdr)
359+
var localRedirect bool
360+
for _, inet4LocalRedirectAddress := range s.inet4LocalRedirectAddress {
361+
if destination.Addr() == inet4LocalRedirectAddress {
362+
ipHdr.SetDestinationAddr(ipHdr.SourceAddr())
363+
ipHdr.SetSourceAddr(inet4LocalRedirectAddress)
364+
localRedirect = true
365+
break
366+
}
367+
}
368+
if !localRedirect {
369+
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
370+
if err != nil {
371+
if err == ErrDrop {
372+
return false, nil
373+
} else {
374+
return false, s.resetIPv4TCP(ipHdr, tcpHdr)
375+
}
361376
}
377+
ipHdr.SetSourceAddr(s.inet4Address)
378+
tcpHdr.SetSourcePort(natPort)
379+
ipHdr.SetDestinationAddr(s.inet4ServerAddress)
380+
tcpHdr.SetDestinationPort(s.tcpPort)
362381
}
363-
ipHdr.SetSourceAddr(s.inet4Address)
364-
tcpHdr.SetSourcePort(natPort)
365-
ipHdr.SetDestinationAddr(s.inet4ServerAddress)
366-
tcpHdr.SetDestinationPort(s.tcpPort)
367382
}
368383
if !s.txChecksumOffload {
369384
tcpHdr.SetChecksum(0)
@@ -439,18 +454,29 @@ func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) (bool, err
439454
ipHdr.SetDestinationAddr(session.Source.Addr())
440455
tcpHdr.SetDestinationPort(session.Source.Port())
441456
} else {
442-
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
443-
if err != nil {
444-
if err == ErrDrop {
445-
return false, nil
446-
} else {
447-
return false, s.resetIPv6TCP(ipHdr, tcpHdr)
457+
var localRedirect bool
458+
for _, inet6LocalRedirectAddress := range s.inet6LocalRedirectAddress {
459+
if destination.Addr() == inet6LocalRedirectAddress {
460+
ipHdr.SetDestinationAddr(ipHdr.SourceAddr())
461+
ipHdr.SetSourceAddr(inet6LocalRedirectAddress)
462+
localRedirect = true
463+
break
464+
}
465+
}
466+
if !localRedirect {
467+
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
468+
if err != nil {
469+
if err == ErrDrop {
470+
return false, nil
471+
} else {
472+
return false, s.resetIPv6TCP(ipHdr, tcpHdr)
473+
}
448474
}
475+
ipHdr.SetSourceAddr(s.inet6Address)
476+
tcpHdr.SetSourcePort(natPort)
477+
ipHdr.SetDestinationAddr(s.inet6ServerAddress)
478+
tcpHdr.SetDestinationPort(s.tcpPort6)
449479
}
450-
ipHdr.SetSourceAddr(s.inet6Address)
451-
tcpHdr.SetSourcePort(natPort)
452-
ipHdr.SetDestinationAddr(s.inet6ServerAddress)
453-
tcpHdr.SetDestinationPort(s.tcpPort6)
454480
}
455481
if !s.txChecksumOffload {
456482
tcpHdr.SetChecksum(0)

0 commit comments

Comments
 (0)