Skip to content

Commit 784bb58

Browse files
committed
Add ping proxy support
1 parent 618be14 commit 784bb58

15 files changed

+587
-17
lines changed

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ require (
66
github.com/go-ole/go-ole v1.3.0
77
github.com/google/btree v1.1.3
88
github.com/sagernet/fswatch v0.1.1
9-
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
9+
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72
1010
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
1111
github.com/sagernet/nftables v0.3.0-beta.4
12-
github.com/sagernet/sing v0.6.0-beta.2
12+
github.com/sagernet/sing v0.6.1
1313
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
1414
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
15-
golang.org/x/net v0.26.0
16-
golang.org/x/sys v0.26.0
15+
golang.org/x/net v0.35.0
16+
golang.org/x/sys v0.30.0
1717
)
1818

1919
require (

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,28 @@ github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8Ku
1616
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1717
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
1818
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
19-
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
20-
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
19+
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72 h1:Jgv6N59yiVMEwimTcFV1EVcu2Aa7R2Wh1ZAYNzWP2qA=
20+
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
2121
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
2222
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
2323
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
2424
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
25-
github.com/sagernet/sing v0.6.0-beta.2 h1:Dcutp3kxrsZes9q3oTiHQhYYjQvDn5rwp1OI9fDLYwQ=
26-
github.com/sagernet/sing v0.6.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
25+
github.com/sagernet/sing v0.6.1 h1:mJ6e7Ir2wtCoGLbdnnXWBsNJu5YHtbXmv66inoE0zFA=
26+
github.com/sagernet/sing v0.6.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
2727
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
2828
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
2929
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
3030
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
3131
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
3232
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
3333
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
34-
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
35-
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
34+
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
35+
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
3636
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
3737
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
3838
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39-
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
40-
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
39+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
40+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4141
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
4242
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
4343
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/gtcpip/header/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ type Network interface {
8686
// SourceAddress returns the value of the "source address" field.
8787
SourceAddress() tcpip.Address
8888

89+
SourceAddr() netip.Addr
90+
8991
// DestinationAddress returns the value of the "destination address"
9092
// field.
9193
DestinationAddress() tcpip.Address
@@ -98,6 +100,8 @@ type Network interface {
98100
// SetSourceAddress sets the value of the "source address" field.
99101
SetSourceAddress(tcpip.Address)
100102

103+
SetSourceAddr(netip.Addr)
104+
101105
// SetDestinationAddress sets the value of the "destination address"
102106
// field.
103107
SetDestinationAddress(tcpip.Address)

route_mapping.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package tun
2+
3+
import (
4+
"net/netip"
5+
"time"
6+
7+
"github.com/sagernet/sing/common"
8+
"github.com/sagernet/sing/contrab/freelru"
9+
"github.com/sagernet/sing/contrab/maphash"
10+
)
11+
12+
type DirectRouteSession struct {
13+
// IPVersion uint8
14+
// Network uint8
15+
Source netip.Addr
16+
Destination netip.Addr
17+
}
18+
19+
type RouteMapping struct {
20+
status freelru.Cache[DirectRouteSession, DirectRouteAction]
21+
}
22+
23+
func NewRouteMapping(timeout time.Duration) *RouteMapping {
24+
status := common.Must1(freelru.NewSharded[DirectRouteSession, DirectRouteAction](1024, maphash.NewHasher[DirectRouteSession]().Hash32))
25+
status.SetHealthCheck(func(session DirectRouteSession, action DirectRouteAction) bool {
26+
return !action.Timeout()
27+
})
28+
status.SetOnEvict(func(session DirectRouteSession, action DirectRouteAction) {
29+
action.Close()
30+
})
31+
return &RouteMapping{status}
32+
}
33+
34+
func (m *RouteMapping) Lookup(session DirectRouteSession, constructor func() DirectRouteAction) DirectRouteAction {
35+
var created DirectRouteAction
36+
action, updated, ok := m.status.GetAndRefreshOrAdd(session, func() (DirectRouteAction, bool) {
37+
created = constructor()
38+
return created, created != nil && !created.Timeout()
39+
})
40+
if !ok {
41+
return created
42+
}
43+
if updated && action.Timeout() {
44+
action = constructor()
45+
m.status.Add(session, action)
46+
}
47+
return action
48+
}

route_nat.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package tun
2+
3+
import (
4+
"net/netip"
5+
"sync"
6+
7+
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
8+
"github.com/sagernet/sing-tun/internal/gtcpip/header"
9+
)
10+
11+
type NatMapping struct {
12+
access sync.RWMutex
13+
sessions map[DirectRouteSession]DirectRouteContext
14+
ipRewrite bool
15+
}
16+
17+
func NewNatMapping(ipRewrite bool) *NatMapping {
18+
return &NatMapping{
19+
sessions: make(map[DirectRouteSession]DirectRouteContext),
20+
ipRewrite: ipRewrite,
21+
}
22+
}
23+
24+
func (m *NatMapping) CreateSession(session DirectRouteSession, context DirectRouteContext) {
25+
if m.ipRewrite {
26+
session.Source = netip.Addr{}
27+
}
28+
m.access.Lock()
29+
m.sessions[session] = context
30+
m.access.Unlock()
31+
}
32+
33+
func (m *NatMapping) DeleteSession(session DirectRouteSession) {
34+
if m.ipRewrite {
35+
session.Source = netip.Addr{}
36+
}
37+
m.access.Lock()
38+
delete(m.sessions, session)
39+
m.access.Unlock()
40+
}
41+
42+
func (m *NatMapping) WritePacket(packet []byte) (bool, error) {
43+
var routeSession DirectRouteSession
44+
switch header.IPVersion(packet) {
45+
case header.IPv4Version:
46+
ipHdr := header.IPv4(packet)
47+
routeSession.Source = ipHdr.DestinationAddr()
48+
routeSession.Destination = ipHdr.SourceAddr()
49+
case header.IPv6Version:
50+
ipHdr := header.IPv6(packet)
51+
routeSession.Source = ipHdr.DestinationAddr()
52+
routeSession.Destination = ipHdr.SourceAddr()
53+
default:
54+
return false, nil
55+
}
56+
m.access.RLock()
57+
context, loaded := m.sessions[routeSession]
58+
m.access.RUnlock()
59+
if !loaded {
60+
return false, nil
61+
}
62+
return true, context.WritePacket(packet)
63+
}
64+
65+
type NatWriter struct {
66+
inet4Address netip.Addr
67+
inet6Address netip.Addr
68+
}
69+
70+
func NewNatWriter(inet4Address netip.Addr, inet6Address netip.Addr) *NatWriter {
71+
return &NatWriter{
72+
inet4Address: inet4Address,
73+
inet6Address: inet6Address,
74+
}
75+
}
76+
77+
func (w *NatWriter) RewritePacket(packet []byte) {
78+
var ipHdr header.Network
79+
var bindAddr netip.Addr
80+
switch header.IPVersion(packet) {
81+
case header.IPv4Version:
82+
ipHdr = header.IPv4(packet)
83+
bindAddr = w.inet4Address
84+
case header.IPv6Version:
85+
ipHdr = header.IPv6(packet)
86+
bindAddr = w.inet6Address
87+
default:
88+
return
89+
}
90+
ipHdr.SetSourceAddr(bindAddr)
91+
switch ipHdr.TransportProtocol() {
92+
case header.ICMPv4ProtocolNumber:
93+
icmpHdr := header.ICMPv4(packet)
94+
icmpHdr.SetChecksum(0)
95+
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr[:header.ICMPv4MinimumSize], checksum.Checksum(icmpHdr.Payload(), 0)))
96+
case header.ICMPv6ProtocolNumber:
97+
icmpHdr := header.ICMPv6(packet)
98+
icmpHdr.SetChecksum(0)
99+
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
100+
Header: icmpHdr,
101+
Src: ipHdr.SourceAddress(),
102+
Dst: ipHdr.DestinationAddress(),
103+
}))
104+
}
105+
if ipHdr4, isIPv4 := ipHdr.(header.IPv4); isIPv4 {
106+
ipHdr4.SetChecksum(0)
107+
ipHdr4.SetChecksum(^ipHdr4.CalculateChecksum())
108+
}
109+
}

route_nat_gvisor.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//go:build with_gvisor
2+
3+
package tun
4+
5+
import (
6+
"github.com/sagernet/gvisor/pkg/tcpip"
7+
"github.com/sagernet/gvisor/pkg/tcpip/header"
8+
stack "github.com/sagernet/gvisor/pkg/tcpip/stack"
9+
"github.com/sagernet/sing/common/buf"
10+
)
11+
12+
type DirectRouteDestination interface {
13+
DirectRouteAction
14+
WritePacket(packet *buf.Buffer) error
15+
WritePacketBuffer(packetBuffer *stack.PacketBuffer) error
16+
}
17+
18+
func (w *NatWriter) RewritePacketBuffer(packetBuffer *stack.PacketBuffer) {
19+
var bindAddr tcpip.Address
20+
if packetBuffer.NetworkProtocolNumber == header.IPv4ProtocolNumber {
21+
bindAddr = AddressFromAddr(w.inet4Address)
22+
} else {
23+
bindAddr = AddressFromAddr(w.inet6Address)
24+
}
25+
/*var ipHdr header.Network
26+
switch packetBuffer.NetworkProtocolNumber {
27+
case header.IPv4ProtocolNumber:
28+
ipHdr = header.IPv4(packetBuffer.NetworkHeader().Slice())
29+
case header.IPv6ProtocolNumber:
30+
ipHdr = header.IPv6(packetBuffer.NetworkHeader().Slice())
31+
default:
32+
return
33+
}*/
34+
ipHdr := packetBuffer.Network()
35+
oldAddr := ipHdr.SourceAddress()
36+
if checksumHdr, needChecksum := ipHdr.(header.ChecksummableNetwork); needChecksum {
37+
checksumHdr.SetSourceAddressWithChecksumUpdate(bindAddr)
38+
} else {
39+
ipHdr.SetSourceAddress(bindAddr)
40+
}
41+
switch packetBuffer.TransportProtocolNumber {
42+
case header.TCPProtocolNumber:
43+
tcpHdr := header.TCP(packetBuffer.TransportHeader().Slice())
44+
tcpHdr.UpdateChecksumPseudoHeaderAddress(oldAddr, bindAddr, true)
45+
case header.UDPProtocolNumber:
46+
udpHdr := header.UDP(packetBuffer.TransportHeader().Slice())
47+
udpHdr.UpdateChecksumPseudoHeaderAddress(oldAddr, bindAddr, true)
48+
}
49+
}

route_nat_non_gvisor.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build !with_gvisor
2+
3+
package tun
4+
5+
import (
6+
"github.com/sagernet/sing/common/buf"
7+
)
8+
9+
type DirectRouteDestination interface {
10+
DirectRouteAction
11+
WritePacket(packet *buf.Buffer) error
12+
}

stack_gvisor.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ func (t *GVisor) Start() error {
7272
}
7373
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTCPForwarder(t.ctx, ipStack, t.handler).HandlePacket)
7474
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
75+
icmpForwarder := NewICMPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout)
76+
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
77+
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
7578
t.stack = ipStack
7679
t.endpoint = linkEndpoint
7780
return nil

0 commit comments

Comments
 (0)