@@ -6,9 +6,11 @@ import (
66 "net/netip"
77 "os"
88 "runtime"
9+ "sync"
910 "time"
1011
1112 "github.com/sagernet/sing-tun"
13+ "github.com/sagernet/sing-tun/internal/gtcpip/header"
1214 "github.com/sagernet/sing/common/buf"
1315 "github.com/sagernet/sing/common/control"
1416 E "github.com/sagernet/sing/common/exceptions"
@@ -18,18 +20,28 @@ import (
1820var _ tun.DirectRouteDestination = (* Destination )(nil )
1921
2022type Destination struct {
21- conn * Conn
22- ctx context.Context
23- logger logger.ContextLogger
24- routeContext tun.DirectRouteContext
25- timeout time.Duration
23+ conn * Conn
24+ ctx context.Context
25+ logger logger.ContextLogger
26+ destination netip.Addr
27+ routeContext tun.DirectRouteContext
28+ timeout time.Duration
29+ requestAccess sync.Mutex
30+ requests map [pingRequest ]bool
31+ }
32+
33+ type pingRequest struct {
34+ Source netip.Addr
35+ Destination netip.Addr
36+ Identifier uint16
37+ Sequence uint16
2638}
2739
2840func ConnectDestination (
2941 ctx context.Context ,
3042 logger logger.ContextLogger ,
3143 controlFunc control.Func ,
32- address netip.Addr ,
44+ destination netip.Addr ,
3345 routeContext tun.DirectRouteContext ,
3446 timeout time.Duration ,
3547) (tun.DirectRouteDestination , error ) {
@@ -39,11 +51,11 @@ func ConnectDestination(
3951 )
4052 switch runtime .GOOS {
4153 case "darwin" , "ios" , "windows" :
42- conn , err = Connect (ctx , logger , false , controlFunc , address )
54+ conn , err = Connect (ctx , false , controlFunc , destination )
4355 default :
44- conn , err = Connect (ctx , logger , true , controlFunc , address )
56+ conn , err = Connect (ctx , true , controlFunc , destination )
4557 if errors .Is (err , os .ErrPermission ) {
46- conn , err = Connect (ctx , logger , false , controlFunc , address )
58+ conn , err = Connect (ctx , false , controlFunc , destination )
4759 }
4860 }
4961 if err != nil {
@@ -53,8 +65,10 @@ func ConnectDestination(
5365 conn : conn ,
5466 ctx : ctx ,
5567 logger : logger ,
68+ destination : destination ,
5669 routeContext : routeContext ,
5770 timeout : timeout ,
71+ requests : make (map [pingRequest ]bool ),
5872 }
5973 go d .loopRead ()
6074 return d , nil
@@ -76,6 +90,59 @@ func (d *Destination) loopRead() {
7690 }
7791 return
7892 }
93+ if ! d .destination .Is6 () {
94+ ipHdr := header .IPv4 (buffer .Bytes ())
95+ if ! ipHdr .IsValid (buffer .Len ()) {
96+ d .logger .ErrorContext (d .ctx , E .New ("invalid IPv4 header received" ))
97+ continue
98+ }
99+ if ipHdr .PayloadLength () < header .ICMPv4MinimumSize {
100+ d .logger .ErrorContext (d .ctx , E .New ("invalid ICMPv4 header received" ))
101+ continue
102+ }
103+ icmpHdr := header .ICMPv4 (ipHdr .Payload ())
104+ if icmpHdr .Type () != header .ICMPv4EchoReply {
105+ continue
106+ }
107+ var requestExists bool
108+ request := pingRequest {Source : ipHdr .DestinationAddr (), Destination : ipHdr .SourceAddr (), Identifier : icmpHdr .Ident (), Sequence : icmpHdr .Sequence ()}
109+ d .requestAccess .Lock ()
110+ if d .requests [request ] {
111+ requestExists = true
112+ delete (d .requests , request )
113+ }
114+ d .requestAccess .Unlock ()
115+ if ! requestExists {
116+ continue
117+ }
118+ d .logger .TraceContext (d .ctx , "read ICMPv4 echo reply from " , ipHdr .SourceAddr (), " to " , ipHdr .DestinationAddr (), " id " , icmpHdr .Ident (), " seq " , icmpHdr .Sequence ())
119+ } else {
120+ ipHdr := header .IPv6 (buffer .Bytes ())
121+ if ! ipHdr .IsValid (buffer .Len ()) {
122+ d .logger .ErrorContext (d .ctx , E .New ("invalid IPv6 header received" ))
123+ continue
124+ }
125+ if ipHdr .PayloadLength () < header .ICMPv6MinimumSize {
126+ d .logger .ErrorContext (d .ctx , E .New ("invalid ICMPv6 header received" ))
127+ continue
128+ }
129+ icmpHdr := header .ICMPv6 (ipHdr .Payload ())
130+ if icmpHdr .Type () != header .ICMPv6EchoReply {
131+ continue
132+ }
133+ var requestExists bool
134+ request := pingRequest {Source : ipHdr .DestinationAddr (), Destination : ipHdr .SourceAddr (), Identifier : icmpHdr .Ident (), Sequence : icmpHdr .Sequence ()}
135+ d .requestAccess .Lock ()
136+ if d .requests [request ] {
137+ requestExists = true
138+ delete (d .requests , request )
139+ }
140+ d .requestAccess .Unlock ()
141+ if ! requestExists {
142+ continue
143+ }
144+ d .logger .TraceContext (d .ctx , "read ICMPv6 echo reply from " , ipHdr .SourceAddr (), " to " , ipHdr .DestinationAddr (), " id " , icmpHdr .Ident (), " seq " , icmpHdr .Sequence ())
145+ }
79146 err = d .routeContext .WritePacket (buffer .Bytes ())
80147 if err != nil {
81148 d .logger .ErrorContext (d .ctx , E .Cause (err , "write ICMP echo reply" ))
@@ -85,6 +152,33 @@ func (d *Destination) loopRead() {
85152}
86153
87154func (d * Destination ) WritePacket (packet * buf.Buffer ) error {
155+ if ! d .destination .Is6 () {
156+ ipHdr := header .IPv4 (packet .Bytes ())
157+ if ! ipHdr .IsValid (packet .Len ()) {
158+ return E .New ("invalid IPv4 header" )
159+ }
160+ if ipHdr .PayloadLength () < header .ICMPv4MinimumSize {
161+ return E .New ("invalid ICMPv4 header" )
162+ }
163+ icmpHdr := header .ICMPv4 (ipHdr .Payload ())
164+ d .requestAccess .Lock ()
165+ d .requests [pingRequest {Source : ipHdr .SourceAddr (), Destination : ipHdr .DestinationAddr (), Identifier : icmpHdr .Ident (), Sequence : icmpHdr .Sequence ()}] = true
166+ d .requestAccess .Unlock ()
167+ d .logger .TraceContext (d .ctx , "write ICMPv4 echo request from " , ipHdr .SourceAddr (), " to " , ipHdr .DestinationAddr (), " id " , icmpHdr .Ident (), " seq " , icmpHdr .Sequence ())
168+ } else {
169+ ipHdr := header .IPv6 (packet .Bytes ())
170+ if ! ipHdr .IsValid (packet .Len ()) {
171+ return E .New ("invalid IPv6 header" )
172+ }
173+ if ipHdr .PayloadLength () < header .ICMPv6MinimumSize {
174+ return E .New ("invalid ICMPv6 header" )
175+ }
176+ icmpHdr := header .ICMPv6 (ipHdr .Payload ())
177+ d .requestAccess .Lock ()
178+ d .requests [pingRequest {Source : ipHdr .SourceAddr (), Destination : ipHdr .DestinationAddr (), Identifier : icmpHdr .Ident (), Sequence : icmpHdr .Sequence ()}] = true
179+ d .requestAccess .Unlock ()
180+ d .logger .TraceContext (d .ctx , "write ICMPv6 echo request from " , ipHdr .SourceAddr (), " to " , ipHdr .DestinationAddr (), " id " , icmpHdr .Ident (), " seq " , icmpHdr .Sequence ())
181+ }
88182 return d .conn .WriteIP (packet )
89183}
90184
0 commit comments