Skip to content

Commit 25d1691

Browse files
LjhAUMEMRPRX
authored andcommitted
Finalmask: Add XICMP (relies mKCP/QUIC or WireGuard) (#5633)
https://t.me/projectXtls/1473
1 parent a6ec3b6 commit 25d1691

8 files changed

Lines changed: 981 additions & 1 deletion

File tree

infra/conf/transport_internet.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
2727
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
2828
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
29+
"github.com/xtls/xray-core/transport/internet/finalmask/xicmp"
2930
"github.com/xtls/xray-core/transport/internet/httpupgrade"
3031
"github.com/xtls/xray-core/transport/internet/hysteria"
3132
"github.com/xtls/xray-core/transport/internet/kcp"
@@ -1240,6 +1241,7 @@ var (
12401241
"mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) },
12411242
"salamander": func() interface{} { return new(Salamander) },
12421243
"xdns": func() interface{} { return new(Xdns) },
1244+
"xicmp": func() interface{} { return new(Xicmp) },
12431245
}, "type", "settings")
12441246
)
12451247

@@ -1328,6 +1330,24 @@ func (c *Xdns) Build() (proto.Message, error) {
13281330
}, nil
13291331
}
13301332

1333+
type Xicmp struct {
1334+
ListenIp string `json:"listenIp"`
1335+
Id uint16 `json:"id"`
1336+
}
1337+
1338+
func (c *Xicmp) Build() (proto.Message, error) {
1339+
config := &xicmp.Config{
1340+
Ip: c.ListenIp,
1341+
Id: int32(c.Id),
1342+
}
1343+
1344+
if config.Ip == "" {
1345+
config.Ip = "0.0.0.0"
1346+
}
1347+
1348+
return config, nil
1349+
}
1350+
13311351
type Mask struct {
13321352
Type string `json:"type"`
13331353
Settings *json.RawMessage `json:"settings"`

transport/internet/finalmask/xdns/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
209209

210210
encoded, err := encode(p, c.clientID, c.domain)
211211
if err != nil {
212-
errors.LogDebug(context.Background(), "xdns encode err", err)
212+
errors.LogDebug(context.Background(), "xdns encode err ", err)
213213
return 0, errors.New("xdns encode").Base(err)
214214
}
215215

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
package xicmp
2+
3+
import (
4+
"context"
5+
"io"
6+
"net"
7+
"strings"
8+
"sync"
9+
"time"
10+
11+
"github.com/xtls/xray-core/common/crypto"
12+
"github.com/xtls/xray-core/common/errors"
13+
"golang.org/x/net/icmp"
14+
"golang.org/x/net/ipv4"
15+
"golang.org/x/net/ipv6"
16+
)
17+
18+
const (
19+
initPollDelay = 500 * time.Millisecond
20+
maxPollDelay = 10 * time.Second
21+
pollDelayMultiplier = 2.0
22+
pollLimit = 16
23+
)
24+
25+
type packet struct {
26+
p []byte
27+
addr net.Addr
28+
}
29+
30+
type seqStatus struct {
31+
needSeqByte bool
32+
seqByte byte
33+
received bool
34+
}
35+
36+
type xicmpConnClient struct {
37+
conn net.PacketConn
38+
icmpConn *icmp.PacketConn
39+
40+
typ icmp.Type
41+
id int
42+
seq int
43+
proto int
44+
seqStatus map[int]*seqStatus
45+
46+
pollChan chan struct{}
47+
readQueue chan *packet
48+
writeQueue chan *packet
49+
50+
closed bool
51+
mutex sync.Mutex
52+
}
53+
54+
func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
55+
if !end {
56+
return nil, errors.New("xicmp requires being at the outermost level")
57+
}
58+
59+
network := "ip4:icmp"
60+
typ := icmp.Type(ipv4.ICMPTypeEcho)
61+
proto := 1
62+
if strings.Contains(c.Ip, ":") {
63+
network = "ip6:ipv6-icmp"
64+
typ = ipv6.ICMPTypeEchoRequest
65+
proto = 58
66+
}
67+
68+
icmpConn, err := icmp.ListenPacket(network, c.Ip)
69+
if err != nil {
70+
return nil, errors.New("xicmp listen err").Base(err)
71+
}
72+
73+
if c.Id == 0 {
74+
c.Id = int32(crypto.RandBetween(0, 65535))
75+
}
76+
77+
conn := &xicmpConnClient{
78+
conn: raw,
79+
icmpConn: icmpConn,
80+
81+
typ: typ,
82+
id: int(c.Id),
83+
seq: 1,
84+
proto: proto,
85+
seqStatus: make(map[int]*seqStatus),
86+
87+
pollChan: make(chan struct{}, pollLimit),
88+
readQueue: make(chan *packet, 128),
89+
writeQueue: make(chan *packet, 128),
90+
}
91+
92+
go conn.recvLoop()
93+
go conn.sendLoop()
94+
95+
return conn, nil
96+
}
97+
98+
func (c *xicmpConnClient) encode(p []byte) ([]byte, error) {
99+
c.mutex.Lock()
100+
defer c.mutex.Unlock()
101+
102+
needSeqByte := false
103+
var seqByte byte
104+
data := p
105+
if len(p) > 0 {
106+
needSeqByte = true
107+
seqByte = p[0]
108+
}
109+
110+
msg := icmp.Message{
111+
Type: c.typ,
112+
Code: 0,
113+
Body: &icmp.Echo{
114+
ID: c.id,
115+
Seq: c.seq,
116+
Data: data,
117+
},
118+
}
119+
120+
buf, err := msg.Marshal(nil)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
if len(buf) > 8192 {
126+
return nil, errors.New("xicmp len(buf) > 8192")
127+
}
128+
129+
c.seqStatus[c.seq] = &seqStatus{
130+
needSeqByte: needSeqByte,
131+
seqByte: seqByte,
132+
received: false,
133+
}
134+
135+
c.seq++
136+
137+
if c.seq == 65536 {
138+
c.seq = 1
139+
}
140+
141+
return buf, nil
142+
}
143+
144+
func (c *xicmpConnClient) recvLoop() {
145+
for {
146+
if c.closed {
147+
break
148+
}
149+
150+
var buf [8192]byte
151+
152+
n, addr, err := c.icmpConn.ReadFrom(buf[:])
153+
if err != nil {
154+
continue
155+
}
156+
157+
msg, err := icmp.ParseMessage(c.proto, buf[:n])
158+
if err != nil {
159+
continue
160+
}
161+
162+
if msg.Type != ipv4.ICMPTypeEchoReply && msg.Type != ipv6.ICMPTypeEchoReply {
163+
continue
164+
}
165+
166+
echo, ok := msg.Body.(*icmp.Echo)
167+
if !ok {
168+
continue
169+
}
170+
171+
seqStatus, ok := c.seqStatus[echo.Seq]
172+
173+
if !ok {
174+
continue
175+
}
176+
177+
if seqStatus.received {
178+
continue
179+
}
180+
181+
if seqStatus.needSeqByte {
182+
if len(echo.Data) <= 1 {
183+
continue
184+
}
185+
if echo.Data[0] == seqStatus.seqByte {
186+
continue
187+
}
188+
echo.Data = echo.Data[1:]
189+
}
190+
191+
if len(echo.Data) > 0 {
192+
seqStatus.received = true
193+
194+
buf := make([]byte, len(echo.Data))
195+
copy(buf, echo.Data)
196+
select {
197+
case c.readQueue <- &packet{
198+
p: buf,
199+
addr: &net.UDPAddr{IP: addr.(*net.IPAddr).IP},
200+
}:
201+
default:
202+
}
203+
204+
select {
205+
case c.pollChan <- struct{}{}:
206+
default:
207+
}
208+
}
209+
}
210+
211+
close(c.pollChan)
212+
close(c.readQueue)
213+
}
214+
215+
func (c *xicmpConnClient) sendLoop() {
216+
var addr net.Addr
217+
218+
pollDelay := initPollDelay
219+
pollTimer := time.NewTimer(pollDelay)
220+
for {
221+
var p *packet
222+
pollTimerExpired := false
223+
224+
select {
225+
case p = <-c.writeQueue:
226+
default:
227+
select {
228+
case p = <-c.writeQueue:
229+
case <-c.pollChan:
230+
case <-pollTimer.C:
231+
pollTimerExpired = true
232+
}
233+
}
234+
235+
if p != nil {
236+
addr = p.addr
237+
238+
select {
239+
case <-c.pollChan:
240+
default:
241+
}
242+
} else if addr != nil {
243+
encoded, _ := c.encode(nil)
244+
p = &packet{
245+
p: encoded,
246+
addr: addr,
247+
}
248+
}
249+
250+
if pollTimerExpired {
251+
pollDelay = time.Duration(float64(pollDelay) * pollDelayMultiplier)
252+
if pollDelay > maxPollDelay {
253+
pollDelay = maxPollDelay
254+
}
255+
} else {
256+
if !pollTimer.Stop() {
257+
<-pollTimer.C
258+
}
259+
pollDelay = initPollDelay
260+
}
261+
pollTimer.Reset(pollDelay)
262+
263+
if c.closed {
264+
return
265+
}
266+
267+
if p != nil {
268+
_, err := c.icmpConn.WriteTo(p.p, p.addr)
269+
if err != nil {
270+
errors.LogDebug(context.Background(), "xicmp writeto err ", err)
271+
}
272+
}
273+
}
274+
}
275+
276+
func (c *xicmpConnClient) Size() int32 {
277+
return 0
278+
}
279+
280+
func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
281+
packet, ok := <-c.readQueue
282+
if !ok {
283+
return 0, nil, io.EOF
284+
}
285+
n = copy(p, packet.p)
286+
if n != len(packet.p) {
287+
return 0, nil, io.ErrShortBuffer
288+
}
289+
return n, packet.addr, nil
290+
}
291+
292+
func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
293+
encoded, err := c.encode(p)
294+
if err != nil {
295+
return 0, errors.New("xicmp encode").Base(err)
296+
}
297+
298+
c.mutex.Lock()
299+
defer c.mutex.Unlock()
300+
301+
if c.closed {
302+
return 0, errors.New("xicmp closed")
303+
}
304+
305+
select {
306+
case c.writeQueue <- &packet{
307+
p: encoded,
308+
addr: &net.IPAddr{IP: addr.(*net.UDPAddr).IP},
309+
}:
310+
return len(p), nil
311+
default:
312+
return 0, errors.New("xicmp queue full")
313+
}
314+
}
315+
316+
func (c *xicmpConnClient) Close() error {
317+
c.mutex.Lock()
318+
defer c.mutex.Unlock()
319+
320+
if c.closed {
321+
return nil
322+
}
323+
324+
c.closed = true
325+
close(c.writeQueue)
326+
327+
_ = c.icmpConn.Close()
328+
return c.conn.Close()
329+
}
330+
331+
func (c *xicmpConnClient) LocalAddr() net.Addr {
332+
return &net.UDPAddr{
333+
IP: c.icmpConn.LocalAddr().(*net.IPAddr).IP,
334+
Port: c.id,
335+
}
336+
}
337+
338+
func (c *xicmpConnClient) SetDeadline(t time.Time) error {
339+
return c.icmpConn.SetDeadline(t)
340+
}
341+
342+
func (c *xicmpConnClient) SetReadDeadline(t time.Time) error {
343+
return c.icmpConn.SetReadDeadline(t)
344+
}
345+
346+
func (c *xicmpConnClient) SetWriteDeadline(t time.Time) error {
347+
return c.icmpConn.SetWriteDeadline(t)
348+
}

0 commit comments

Comments
 (0)