mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-04 23:40:26 +08:00
feat(p2p): add ICMP backend support
This commit is contained in:
@@ -15,7 +15,7 @@ WireGold is a pure Go Layer 3 VPN inspired by WireGuard.
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
- **Encryption**: XChaCha20-Poly1305 (AEAD) + Curve25519 key exchange + BLAKE2B integrity check
|
- **Encryption**: XChaCha20-Poly1305 (AEAD) + Curve25519 key exchange + BLAKE2B integrity check
|
||||||
- **Transport**: UDP / UDP-Lite / TCP / Raw IP
|
- **Transport**: UDP / UDP-Lite / TCP / Raw IP / ICMP
|
||||||
- **Encoding**: Optional Base16384 encoding to traverse text-only filters
|
- **Encoding**: Optional Base16384 encoding to traverse text-only filters
|
||||||
- **Anti-censorship**: XOR mask header obfuscation + randomized MTU scaling + optional double-send
|
- **Anti-censorship**: XOR mask header obfuscation + randomized MTU scaling + optional double-send
|
||||||
- **Compression**: Optional Zstd payload compression
|
- **Compression**: Optional Zstd payload compression
|
||||||
@@ -54,14 +54,17 @@ wg [-c config.yaml] [-d|w] [-g] [-h] [-p] [-l log.txt]
|
|||||||
|
|
||||||
- **macOS Mojave**: max MTU (IPv4 endpoint) is `9159`
|
- **macOS Mojave**: max MTU (IPv4 endpoint) is `9159`
|
||||||
- **IPv6 endpoint**: recommended MTU `1280–1500` to avoid oversized segment drops
|
- **IPv6 endpoint**: recommended MTU `1280–1500` to avoid oversized segment drops
|
||||||
|
- **ICMP / Raw IP endpoint**: use bare IP address without port (e.g. `0.0.0.0`), requires root/admin privileges
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
IP: 192.168.233.1
|
IP: 192.168.233.1
|
||||||
SubNet: 192.168.233.0/24
|
SubNet: 192.168.233.0/24
|
||||||
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
||||||
|
Network: udp # udp (default), udplite, tcp, ip, icmp
|
||||||
EndPoint: 0.0.0.0:56789
|
EndPoint: 0.0.0.0:56789
|
||||||
MTU: 1504
|
MTU: 1504
|
||||||
SpeedLoop: 4096
|
SpeedLoop: 4096
|
||||||
|
MaxTTL: 64
|
||||||
Mask: 0x1234567890abcdef
|
Mask: 0x1234567890abcdef
|
||||||
Base14: true
|
Base14: true
|
||||||
Peers:
|
Peers:
|
||||||
@@ -94,6 +97,9 @@ Peers:
|
|||||||
|
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
|
| `Network` | Transport protocol: `udp` (default), `udplite`, `tcp`, `ip`, `icmp` |
|
||||||
|
| `MaxTTL` | Initial TTL for outgoing packets; default `64` |
|
||||||
|
| `SpeedLoop` | Log receive throughput statistics every N packets; default `4096` |
|
||||||
| `AllowedIPs` | Prefix `x` to accept packets from the subnet without creating a system route; prefix `y` to add an internal route table entry only |
|
| `AllowedIPs` | Prefix `x` to accept packets from the subnet without creating a system route; prefix `y` to add an internal route table entry only |
|
||||||
| `Mask` | XOR mask for header obfuscation |
|
| `Mask` | XOR mask for header obfuscation |
|
||||||
| `Base14` | Enable Base16384 encoding |
|
| `Base14` | Enable Base16384 encoding |
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ WireGold 是一个纯 Go 实现的第 3 层 VPN,灵感来自 WireGuard。
|
|||||||
### 主要特性
|
### 主要特性
|
||||||
|
|
||||||
- **加密**: XChaCha20-Poly1305 (AEAD) + Curve25519 密钥交换 + BLAKE2B 完整性校验
|
- **加密**: XChaCha20-Poly1305 (AEAD) + Curve25519 密钥交换 + BLAKE2B 完整性校验
|
||||||
- **传输**: 支持 UDP / UDP-Lite / TCP / Raw IP 多种底层传输
|
- **传输**: 支持 UDP / UDP-Lite / TCP / Raw IP / ICMP 多种底层传输
|
||||||
- **编码**: 可选 Base16384 编码以穿越文本过滤
|
- **编码**: 可选 Base16384 编码以穿越文本过滤
|
||||||
- **抗审查**: XOR 掩码混淆报头 + 随机 MTU 放缩 + 可选双倍发包
|
- **抗审查**: XOR 掩码混淆报头 + 随机 MTU 放缩 + 可选双倍发包
|
||||||
- **压缩**: 可选 Zstd 数据压缩
|
- **压缩**: 可选 Zstd 数据压缩
|
||||||
@@ -53,14 +53,17 @@ wg [-c config.yaml] [-d|w] [-g] [-h] [-p] [-l log.txt]
|
|||||||
|
|
||||||
- **macOS Mojave**: 最大 MTU (IPv4 endpoint) 为 `9159`
|
- **macOS Mojave**: 最大 MTU (IPv4 endpoint) 为 `9159`
|
||||||
- **IPv6 endpoint**: 推荐 MTU `1280~1500`,避免大分片被丢弃
|
- **IPv6 endpoint**: 推荐 MTU `1280~1500`,避免大分片被丢弃
|
||||||
|
- **ICMP / Raw IP endpoint**: 使用裸 IP 地址,无需端口号 (如 `0.0.0.0`)。需要 root/管理员权限
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
IP: 192.168.233.1
|
IP: 192.168.233.1
|
||||||
SubNet: 192.168.233.0/24
|
SubNet: 192.168.233.0/24
|
||||||
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
||||||
|
Network: udp # udp (默认), udplite, tcp, ip, icmp
|
||||||
EndPoint: 0.0.0.0:56789
|
EndPoint: 0.0.0.0:56789
|
||||||
MTU: 1504
|
MTU: 1504
|
||||||
SpeedLoop: 4096
|
SpeedLoop: 4096
|
||||||
|
MaxTTL: 64
|
||||||
Mask: 0x1234567890abcdef
|
Mask: 0x1234567890abcdef
|
||||||
Base14: true
|
Base14: true
|
||||||
Peers:
|
Peers:
|
||||||
@@ -93,6 +96,9 @@ Peers:
|
|||||||
|
|
||||||
| 字段 | 说明 |
|
| 字段 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
|
| `Network` | 传输协议: `udp` (默认), `udplite`, `tcp`, `ip`, `icmp` |
|
||||||
|
| `MaxTTL` | 发包初始 TTL,默认 `64` |
|
||||||
|
| `SpeedLoop` | 每收到 N 个包时输出一次吞吐统计,默认 `4096` |
|
||||||
| `AllowedIPs` | 前缀 `x` 表示只接受该网段报文但不建系统路由;前缀 `y` 表示只添加内部路由表条目 |
|
| `AllowedIPs` | 前缀 `x` 表示只接受该网段报文但不建系统路由;前缀 `y` 表示只添加内部路由表条目 |
|
||||||
| `Mask` | XOR 掩码,用于混淆报头 |
|
| `Mask` | XOR 掩码,用于混淆报头 |
|
||||||
| `Base14` | 启用 Base16384 编码 |
|
| `Base14` | 启用 Base16384 编码 |
|
||||||
|
|||||||
7
go.mod
7
go.mod
@@ -12,12 +12,13 @@ require (
|
|||||||
github.com/fumiama/water v0.0.0-20211231134027-da391938d6ac
|
github.com/fumiama/water v0.0.0-20211231134027-da391938d6ac
|
||||||
github.com/klauspost/compress v1.18.5
|
github.com/klauspost/compress v1.18.5
|
||||||
github.com/sirupsen/logrus v1.9.4
|
github.com/sirupsen/logrus v1.9.4
|
||||||
golang.org/x/crypto v0.49.0
|
golang.org/x/crypto v0.50.0
|
||||||
|
golang.org/x/net v0.53.0
|
||||||
|
golang.org/x/sys v0.43.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fumiama/wintun v0.0.0-20211229152851-8bc97c8034c0 // indirect
|
github.com/fumiama/wintun v0.0.0-20211229152851-8bc97c8034c0 // indirect
|
||||||
golang.org/x/sys v0.43.0 // indirect
|
golang.org/x/text v0.36.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -30,9 +30,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||||
|
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
@@ -40,8 +42,8 @@ golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -208,7 +208,10 @@ func (m *Me) NetworkConfigs() []any {
|
|||||||
|
|
||||||
func (m *Me) Close() error {
|
func (m *Me) Close() error {
|
||||||
for i := 0; i < len(m.jobs); i++ {
|
for i := 0; i < len(m.jobs); i++ {
|
||||||
close(m.jobs[i])
|
jb := m.jobs[i]
|
||||||
|
if jb != nil {
|
||||||
|
close(jb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.connections = nil
|
m.connections = nil
|
||||||
if bin.IsNonNilInterface(m.conn) {
|
if bin.IsNonNilInterface(m.conn) {
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ func (m *Me) extractPeer(srcip, dstip net.IP, addr p2p.EndPoint) *Link {
|
|||||||
logrus.Warnln(file.Header(), "packet from", srcip, "to", dstip, "is refused")
|
logrus.Warnln(file.Header(), "packet from", srcip, "to", dstip, "is refused")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if bin.IsNilInterface(p.endpoint) || !p.endpoint.Euqal(addr) {
|
if bin.IsNilInterface(p.endpoint) || !p.endpoint.Equal(addr) {
|
||||||
if m.ep.Network() == "tcp" && !addr.Euqal(p.endpoint) {
|
if m.ep.Network() == "tcp" && !addr.Equal(p.endpoint) {
|
||||||
logrus.Infoln(file.Header(), "set endpoint of peer", p.peerip, "to", addr.String())
|
logrus.Infoln(file.Header(), "set endpoint of peer", p.peerip, "to", addr.String())
|
||||||
p.endpoint = addr
|
p.endpoint = addr
|
||||||
} else { // others are all no status link
|
} else { // others are all no status link
|
||||||
|
|||||||
@@ -75,16 +75,12 @@ func (l *Link) write2peer(b pbuf.Bytes, seq uint32) {
|
|||||||
if l.doublepacket {
|
if l.doublepacket {
|
||||||
err := l.write2peer1(b, seq)
|
err := l.write2peer1(b, seq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if config.ShowDebugLog {
|
logrus.Warnln("[send] double wr2peer", l.peerip, "err:", err)
|
||||||
logrus.Warnln("[send] double wr2peer", l.peerip, "err:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := l.write2peer1(b, seq)
|
err := l.write2peer1(b, seq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if config.ShowDebugLog {
|
logrus.Warnln("[send] wr2peer", l.peerip, "err:", err)
|
||||||
logrus.Warnln("[send] wr2peer", l.peerip, "err:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func Register(network string, initializer Initializer) (actual Initializer, hase
|
|||||||
type EndPoint interface {
|
type EndPoint interface {
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
Network() string
|
Network() string
|
||||||
Euqal(EndPoint) bool
|
Equal(EndPoint) bool
|
||||||
Listen() (Conn, error)
|
Listen() (Conn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
264
gold/p2p/icmp/icmp.go
Normal file
264
gold/p2p/icmp/icmp.go
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
package icmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/RomiChan/syncx"
|
||||||
|
"github.com/fumiama/WireGold/config"
|
||||||
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
"github.com/fumiama/orbyte/pbuf"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidBodyType = errors.New("invalid body type")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
echoid = os.Getpid()
|
||||||
|
)
|
||||||
|
|
||||||
|
// peerState holds per-peer ICMP echo state within a Conn.
|
||||||
|
type peerState struct {
|
||||||
|
id int
|
||||||
|
seq atomic.Uintptr
|
||||||
|
seqpool *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPeerState() *peerState {
|
||||||
|
ps := &peerState{}
|
||||||
|
ps.seqpool = &sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return int(ps.seq.Add(1))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndPoint netip.Addr
|
||||||
|
|
||||||
|
func (ep *EndPoint) String() string {
|
||||||
|
return (*netip.Addr)(ep).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Network() string {
|
||||||
|
return "icmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||||
|
if ep == nil || ep2 == nil {
|
||||||
|
return ep == nil && ep2 == nil
|
||||||
|
}
|
||||||
|
ipep2, ok := ep2.(*EndPoint)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ipep1 := ep
|
||||||
|
return (*netip.Addr)(ipep1).Compare(*(*netip.Addr)(ipep2)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// network get ipv4/ipv6 info and choose different options.
|
||||||
|
func (ep *EndPoint) network() (string, *netip.Addr) {
|
||||||
|
nw := "ip4:icmp"
|
||||||
|
if (*netip.Addr)(ep).Is6() {
|
||||||
|
nw = "ip6:ipv6-icmp"
|
||||||
|
}
|
||||||
|
return nw, (*netip.Addr)(ep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||||
|
nw, addr := ep.network()
|
||||||
|
conn, err := icmp.ListenPacket(nw, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Conn{inner: conn}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
inner *icmp.PacketConn
|
||||||
|
peers syncx.Map[netip.Addr, *peerState]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) getOrCreatePeerState(addr netip.Addr) *peerState {
|
||||||
|
if ps, ok := conn.peers.Load(addr); ok {
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
ps := newPeerState()
|
||||||
|
actual, _ := conn.peers.LoadOrStore(addr, ps)
|
||||||
|
return actual
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) Close() error {
|
||||||
|
return conn.inner.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) String() string {
|
||||||
|
return conn.inner.LocalAddr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||||
|
eps := conn.inner.LocalAddr().String()
|
||||||
|
addr, err := netip.ParseAddrPort(eps)
|
||||||
|
if err == nil {
|
||||||
|
eps = addr.Addr().String()
|
||||||
|
}
|
||||||
|
ep, _ := NewEndpoint(eps)
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) ReadFromPeer(b []byte) (n int, ep p2p.EndPoint, err error) {
|
||||||
|
buf := pbuf.NewBytes(8192)
|
||||||
|
defer buf.ManualDestroy()
|
||||||
|
var ipaddr netip.Addr
|
||||||
|
buf.V(func(data []byte) {
|
||||||
|
ok := false
|
||||||
|
var msg *icmp.Message
|
||||||
|
for !ok {
|
||||||
|
var (
|
||||||
|
cnt int
|
||||||
|
addr net.Addr
|
||||||
|
)
|
||||||
|
cnt, addr, err = conn.inner.ReadFrom(data)
|
||||||
|
if err != nil {
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv ReadFrom err:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ipaddr, err = netip.ParseAddr(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv ParseAddr err:", err, ", addr:", addr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ep, err = NewEndpoint(ipaddr.String())
|
||||||
|
if err != nil {
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv NewEndpoint err:", err, ", addr:", addr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proton := ipv4.ICMPTypeEcho.Protocol()
|
||||||
|
if ipaddr.Is6() {
|
||||||
|
proton = ipv6.ICMPTypeEchoRequest.Protocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err = icmp.ParseMessage(proton, data[:cnt])
|
||||||
|
if err != nil {
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv ParseMessage err:", err, ", addr:", addr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv4.ICMPTypeEchoReply
|
||||||
|
if ipaddr.Is6() {
|
||||||
|
ok = msg.Type == ipv6.ICMPTypeEchoRequest || msg.Type == ipv6.ICMPTypeEchoReply
|
||||||
|
}
|
||||||
|
ok = ok && msg.Code == 1
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv from", ipaddr, ", is valid:", ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body, okk := msg.Body.(*icmp.Echo)
|
||||||
|
if !okk {
|
||||||
|
err = ErrInvalidBodyType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv6.ICMPTypeEchoRequest {
|
||||||
|
ps := conn.getOrCreatePeerState(ipaddr)
|
||||||
|
ps.id = body.ID
|
||||||
|
ps.seq.Store(uintptr(body.Seq))
|
||||||
|
ps.seqpool.Put(body.Seq)
|
||||||
|
}
|
||||||
|
n = copy(b, body.Data)
|
||||||
|
if config.ShowDebugLog {
|
||||||
|
logrus.Debugln("[icmp] recv", n, "bytes data from", ipaddr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||||
|
icmpep, ok := ep.(*EndPoint)
|
||||||
|
if !ok {
|
||||||
|
return 0, p2p.ErrEndpointTypeMistatch
|
||||||
|
}
|
||||||
|
addr := (*netip.Addr)(icmpep)
|
||||||
|
ps := conn.getOrCreatePeerState(*addr)
|
||||||
|
seq := ps.seqpool.Get().(int)
|
||||||
|
id := ps.id
|
||||||
|
isrequest := id == 0
|
||||||
|
if isrequest {
|
||||||
|
id = echoid
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
ip net.IP
|
||||||
|
msg icmp.Message
|
||||||
|
)
|
||||||
|
if addr.Is4() {
|
||||||
|
x := addr.As4()
|
||||||
|
ip = x[:]
|
||||||
|
msg = icmp.Message{
|
||||||
|
Code: 1,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: id,
|
||||||
|
Seq: seq,
|
||||||
|
Data: b,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if isrequest {
|
||||||
|
msg.Type = ipv4.ICMPTypeEcho
|
||||||
|
} else {
|
||||||
|
msg.Type = ipv4.ICMPTypeEchoReply
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x := addr.As16()
|
||||||
|
ip = x[:]
|
||||||
|
msg = icmp.Message{
|
||||||
|
Code: 1,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: id,
|
||||||
|
Seq: seq,
|
||||||
|
Data: b,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if isrequest {
|
||||||
|
msg.Type = ipv6.ICMPTypeEchoRequest
|
||||||
|
} else {
|
||||||
|
msg.Type = ipv6.ICMPTypeEchoReply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf := pbuf.NewBytes(8192)
|
||||||
|
defer buf.ManualDestroy()
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
n int
|
||||||
|
)
|
||||||
|
buf.V(func(bin []byte) {
|
||||||
|
data, err = msg.Marshal(bin[:0])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = conn.inner.WriteTo(data, &net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
Zone: addr.Zone(),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
26
gold/p2p/icmp/init.go
Normal file
26
gold/p2p/icmp/init.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Package icmp for non-privileged datagram-oriented ICMP endpoints,
|
||||||
|
// currently only Darwin and Linux support this.
|
||||||
|
package icmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
"github.com/fumiama/WireGold/internal/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||||
|
addr, err := netip.ParseAddr(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*EndPoint)(&addr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
name := file.FolderName()
|
||||||
|
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||||
|
if hasexist {
|
||||||
|
panic("network " + name + " has been registered")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ func (ep *EndPoint) Network() string {
|
|||||||
return ep.addr.Network()
|
return ep.addr.Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||||
if ep == nil || ep2 == nil {
|
if ep == nil || ep2 == nil {
|
||||||
return ep == nil && ep2 == nil
|
return ep == nil && ep2 == nil
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
|||||||
|
|
||||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||||
n, addr, err := conn.conn.ReadFromIP(b)
|
n, addr, err := conn.conn.ReadFromIP(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
return n, &EndPoint{
|
return n, &EndPoint{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
ptcl: conn.ep.ptcl,
|
ptcl: conn.ep.ptcl,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (ep *EndPoint) Network() string {
|
|||||||
return ep.addr.Network()
|
return ep.addr.Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||||
if ep == nil || ep2 == nil {
|
if ep == nil || ep2 == nil {
|
||||||
return ep == nil && ep2 == nil
|
return ep == nil && ep2 == nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (ep *EndPoint) Network() string {
|
|||||||
return (*net.UDPAddr)(ep).Network()
|
return (*net.UDPAddr)(ep).Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||||
if ep == nil || ep2 == nil {
|
if ep == nil || ep2 == nil {
|
||||||
return ep == nil && ep2 == nil
|
return ep == nil && ep2 == nil
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
|||||||
|
|
||||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
return n, (*EndPoint)(addr), err
|
return n, (*EndPoint)(addr), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func (ep *EndPoint) Network() string {
|
|||||||
return "udplite"
|
return "udplite"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||||
if ep == nil || ep2 == nil {
|
if ep == nil || ep2 == nil {
|
||||||
return ep == nil && ep2 == nil
|
return ep == nil && ep2 == nil
|
||||||
}
|
}
|
||||||
@@ -52,6 +52,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
|||||||
|
|
||||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
return n, (*EndPoint)(addr), err
|
return n, (*EndPoint)(addr), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func init() {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
p, ok := peer.Me().IsInPeer(ps)
|
p, ok := peer.Me().IsInPeer(ps)
|
||||||
if ok {
|
if ok {
|
||||||
if bin.IsNilInterface(p.EndPoint()) || !p.EndPoint().Euqal(addr) {
|
if bin.IsNilInterface(p.EndPoint()) || !p.EndPoint().Equal(addr) {
|
||||||
p.SetEndPoint(addr)
|
p.SetEndPoint(addr)
|
||||||
logrus.Infoln(file.Header(), "notify set ep of peer", ps, "to", ep)
|
logrus.Infoln(file.Header(), "notify set ep of peer", ps, "to", ep)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
_ "github.com/fumiama/WireGold/gold/p2p/icmp" // support icmp connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
||||||
|
|||||||
466
upper/services/tunnel/tunnel_icmp_test.go
Normal file
466
upper/services/tunnel/tunnel_icmp_test.go
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package tunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
curve "github.com/fumiama/go-x25519"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/fumiama/WireGold/gold/link"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
icmpNS1 = "wgtest_ns1"
|
||||||
|
icmpNS2 = "wgtest_ns2"
|
||||||
|
icmpIP1 = "10.0.0.1"
|
||||||
|
icmpIP2 = "10.0.0.2"
|
||||||
|
icmpVeth1 = "veth1"
|
||||||
|
icmpVeth2 = "veth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupICMPNetns creates two network namespaces connected by a veth pair.
|
||||||
|
// It returns a cleanup function. Requires root.
|
||||||
|
func setupICMPNetns(t *testing.T) func() {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cmds := [][]string{
|
||||||
|
{"ip", "netns", "add", icmpNS1},
|
||||||
|
{"ip", "netns", "add", icmpNS2},
|
||||||
|
{"ip", "link", "add", icmpVeth1, "type", "veth", "peer", "name", icmpVeth2},
|
||||||
|
{"ip", "link", "set", icmpVeth1, "netns", icmpNS1},
|
||||||
|
{"ip", "link", "set", icmpVeth2, "netns", icmpNS2},
|
||||||
|
{"ip", "netns", "exec", icmpNS1, "ifconfig", icmpVeth1, icmpIP1, "up"},
|
||||||
|
{"ip", "netns", "exec", icmpNS2, "ifconfig", icmpVeth2, icmpIP2, "up"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, args := range cmds {
|
||||||
|
if out, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
|
||||||
|
// best-effort cleanup
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||||
|
t.Fatalf("setup netns: %v failed: %v\n%s", args, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enterNetns pins the current goroutine to its OS thread, switches into
|
||||||
|
// the named network namespace, and returns a function that restores the
|
||||||
|
// original namespace and unlocks the thread.
|
||||||
|
func enterNetns(nsName string) (func(), error) {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
origFd, err := unix.Open("/proc/self/ns/net", unix.O_RDONLY|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
return nil, fmt.Errorf("open current netns: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFd, err := unix.Open("/var/run/netns/"+nsName, unix.O_RDONLY|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
unix.Close(origFd)
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
return nil, fmt.Errorf("open target netns %s: %w", nsName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unix.Setns(targetFd, unix.CLONE_NEWNET); err != nil {
|
||||||
|
unix.Close(targetFd)
|
||||||
|
unix.Close(origFd)
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
return nil, fmt.Errorf("setns to %s: %w", nsName, err)
|
||||||
|
}
|
||||||
|
unix.Close(targetFd)
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
unix.Setns(origFd, unix.CLONE_NEWNET)
|
||||||
|
unix.Close(origFd)
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMeInNetns initializes a link.Me at dst inside the given network namespace.
|
||||||
|
// The underlying socket fd remains bound to that namespace after return.
|
||||||
|
func initMeInNetns(t testing.TB, nsName string, cfg *link.MyConfig, dst *link.Me) {
|
||||||
|
t.Helper()
|
||||||
|
var merr any
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
merr = r
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
restore, err := enterNetns(nsName)
|
||||||
|
if err != nil {
|
||||||
|
merr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer restore()
|
||||||
|
*dst = link.NewMe(cfg)
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
|
if merr != nil {
|
||||||
|
t.Fatalf("initMeInNetns(%s): %v", nsName, merr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTunnelICMP(t *testing.T) {
|
||||||
|
if os.Getuid() != 0 {
|
||||||
|
t.Skip("skipping ICMP test: requires root")
|
||||||
|
}
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sz := 1024 * i
|
||||||
|
if !t.Run(strconv.Itoa(sz), func(t *testing.T) {
|
||||||
|
testTunnelICMP(t, uint16(sz))
|
||||||
|
}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTunnelICMP(t *testing.T, mtu uint16) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||||
|
|
||||||
|
cleanup := setupICMPNetns(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
testICMPTunnel(t, true, false, nil, mtu) // plain text
|
||||||
|
testICMPTunnel(t, false, false, nil, mtu) // normal
|
||||||
|
|
||||||
|
testICMPTunnel(t, true, true, nil, mtu) // plain text + base14
|
||||||
|
testICMPTunnel(t, false, true, nil, mtu) // normal + base14
|
||||||
|
|
||||||
|
var buf [32]byte
|
||||||
|
if _, err := rand.Read(buf[:]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testICMPTunnel(t, false, false, &buf, mtu) // preshared
|
||||||
|
testICMPTunnel(t, false, true, &buf, mtu) // preshared + base14
|
||||||
|
}
|
||||||
|
|
||||||
|
func testICMPTunnel(t *testing.T, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||||
|
nw := "icmp"
|
||||||
|
fmt.Println("start", nw, "testing, mtu", mtu, "plain", isplain, "b14", isbase14, "pshk", pshk != nil)
|
||||||
|
|
||||||
|
selfpk, err := curve.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
peerpk, err := curve.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("my priv key:", hex.EncodeToString(selfpk.Private()[:]))
|
||||||
|
t.Log("my publ key:", hex.EncodeToString(selfpk.Public()[:]))
|
||||||
|
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
||||||
|
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||||
|
|
||||||
|
var m link.Me
|
||||||
|
initMeInNetns(t, icmpNS1, &link.MyConfig{
|
||||||
|
MyIPwithMask: "192.168.1.2/32",
|
||||||
|
MyEndpoint: icmpIP1,
|
||||||
|
Network: nw,
|
||||||
|
PrivateKey: selfpk.Private(),
|
||||||
|
SrcPort: 1,
|
||||||
|
DstPort: 1,
|
||||||
|
MTU: mtu,
|
||||||
|
Base14: isbase14,
|
||||||
|
}, &m)
|
||||||
|
defer m.Close()
|
||||||
|
|
||||||
|
var p link.Me
|
||||||
|
initMeInNetns(t, icmpNS2, &link.MyConfig{
|
||||||
|
MyIPwithMask: "192.168.1.3/32",
|
||||||
|
MyEndpoint: icmpIP2,
|
||||||
|
Network: nw,
|
||||||
|
PrivateKey: peerpk.Private(),
|
||||||
|
SrcPort: 1,
|
||||||
|
DstPort: 1,
|
||||||
|
MTU: mtu,
|
||||||
|
Base14: isbase14,
|
||||||
|
}, &p)
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
ppp := peerpk.Public()
|
||||||
|
spp := selfpk.Public()
|
||||||
|
if isplain {
|
||||||
|
ppp = nil
|
||||||
|
spp = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddPeer(&link.PeerConfig{
|
||||||
|
PeerIP: "192.168.1.3",
|
||||||
|
EndPoint: icmpIP2,
|
||||||
|
AllowedIPs: []string{"192.168.1.3/32"},
|
||||||
|
PubicKey: ppp,
|
||||||
|
PresharedKey: pshk,
|
||||||
|
MTU: mtu,
|
||||||
|
MTURandomRange: mtu / 2,
|
||||||
|
UseZstd: true,
|
||||||
|
DoublePacket: true,
|
||||||
|
})
|
||||||
|
p.AddPeer(&link.PeerConfig{
|
||||||
|
PeerIP: "192.168.1.2",
|
||||||
|
EndPoint: icmpIP1,
|
||||||
|
AllowedIPs: []string{"192.168.1.2/32"},
|
||||||
|
PubicKey: spp,
|
||||||
|
PresharedKey: pshk,
|
||||||
|
MTU: mtu,
|
||||||
|
MTURandomRange: mtu / 2,
|
||||||
|
UseZstd: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
tunnme, err := Create(&m, "192.168.1.3")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tunnme.Start(1, 1, 4096)
|
||||||
|
tunnpeer, err := Create(&p, "192.168.1.2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tunnpeer.Start(1, 1, 4096)
|
||||||
|
|
||||||
|
time.Sleep(time.Second) // wait link up
|
||||||
|
|
||||||
|
sendb := ([]byte)("1234")
|
||||||
|
go tunnme.Write(sendb)
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
tunnpeer.Read(buf)
|
||||||
|
if string(sendb) != string(buf) {
|
||||||
|
logrus.Errorln("error: recv", buf, "expect", sendb)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
sendb = make([]byte, mtu+4)
|
||||||
|
for i := 0; i < len(sendb); i++ {
|
||||||
|
sendb[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(sendb); i++ {
|
||||||
|
rand.Read(sendb[:i])
|
||||||
|
go tunnme.Write(sendb[:i])
|
||||||
|
rbuf := make([]byte, i)
|
||||||
|
_, err = io.ReadFull(&tunnpeer, rbuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(sendb[:i], rbuf) {
|
||||||
|
t.Fatal("error: recv", i, "bytes data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(sendb); i++ {
|
||||||
|
sendb[i] = ^byte(i)
|
||||||
|
}
|
||||||
|
tunnme.Write(sendb)
|
||||||
|
rd := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
tm := time.AfterFunc(time.Second*2, func() {
|
||||||
|
tunnme.Stop()
|
||||||
|
tunnpeer.Stop()
|
||||||
|
})
|
||||||
|
defer tm.Stop()
|
||||||
|
|
||||||
|
_, err = io.CopyBuffer(rd, &tunnpeer, make([]byte, 200))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(sendb) != rd.String() {
|
||||||
|
t.Fatal("error: recv fragmented data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTunnelICMP(b *testing.B) {
|
||||||
|
if os.Getuid() != 0 {
|
||||||
|
b.Skip("skipping ICMP benchmark: requires root")
|
||||||
|
}
|
||||||
|
benchmarkTunnelNetworkICMP(b, 4096)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTunnelICMPSmallMTU(b *testing.B) {
|
||||||
|
if os.Getuid() != 0 {
|
||||||
|
b.Skip("skipping ICMP benchmark: requires root")
|
||||||
|
}
|
||||||
|
benchmarkTunnelNetworkICMP(b, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkTunnelNetworkICMP(b *testing.B, mtu uint16) {
|
||||||
|
logrus.SetLevel(logrus.ErrorLevel)
|
||||||
|
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||||
|
|
||||||
|
cleanup := setupICMPBenchNetns(b)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sz := 1024 * i
|
||||||
|
b.Run(fmt.Sprintf("%d-plain-nob14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, true, false, nil, mtu)
|
||||||
|
})
|
||||||
|
b.Run(fmt.Sprintf("%d-normal-nob14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, false, false, nil, mtu)
|
||||||
|
})
|
||||||
|
b.Run(fmt.Sprintf("%d-plain-b14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, true, true, nil, mtu)
|
||||||
|
})
|
||||||
|
b.Run(fmt.Sprintf("%d-normal-b14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, false, true, nil, mtu)
|
||||||
|
})
|
||||||
|
var buf [32]byte
|
||||||
|
if _, err := rand.Read(buf[:]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.Run(fmt.Sprintf("%d-preshared-nob14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, false, false, &buf, mtu)
|
||||||
|
})
|
||||||
|
b.Run(fmt.Sprintf("%d-preshared-b14", sz), func(b *testing.B) {
|
||||||
|
benchmarkICMPTunnel(b, sz, false, true, &buf, mtu)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupICMPBenchNetns(b *testing.B) func() {
|
||||||
|
b.Helper()
|
||||||
|
|
||||||
|
cmds := [][]string{
|
||||||
|
{"ip", "netns", "add", icmpNS1},
|
||||||
|
{"ip", "netns", "add", icmpNS2},
|
||||||
|
{"ip", "link", "add", icmpVeth1, "type", "veth", "peer", "name", icmpVeth2},
|
||||||
|
{"ip", "link", "set", icmpVeth1, "netns", icmpNS1},
|
||||||
|
{"ip", "link", "set", icmpVeth2, "netns", icmpNS2},
|
||||||
|
{"ip", "netns", "exec", icmpNS1, "ifconfig", icmpVeth1, icmpIP1, "up"},
|
||||||
|
{"ip", "netns", "exec", icmpNS2, "ifconfig", icmpVeth2, icmpIP2, "up"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, args := range cmds {
|
||||||
|
if out, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||||
|
b.Fatalf("setup netns: %v failed: %v\n%s", args, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||||
|
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkICMPTunnel(b *testing.B, sz int, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||||
|
nw := "icmp"
|
||||||
|
|
||||||
|
selfpk, err := curve.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
peerpk, err := curve.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var m link.Me
|
||||||
|
initMeInNetns(b, icmpNS1, &link.MyConfig{
|
||||||
|
MyIPwithMask: "192.168.1.2/32",
|
||||||
|
MyEndpoint: icmpIP1,
|
||||||
|
Network: nw,
|
||||||
|
PrivateKey: selfpk.Private(),
|
||||||
|
SrcPort: 1,
|
||||||
|
DstPort: 1,
|
||||||
|
MTU: mtu,
|
||||||
|
Base14: isbase14,
|
||||||
|
}, &m)
|
||||||
|
defer m.Close()
|
||||||
|
|
||||||
|
var p link.Me
|
||||||
|
initMeInNetns(b, icmpNS2, &link.MyConfig{
|
||||||
|
MyIPwithMask: "192.168.1.3/32",
|
||||||
|
MyEndpoint: icmpIP2,
|
||||||
|
Network: nw,
|
||||||
|
PrivateKey: peerpk.Private(),
|
||||||
|
SrcPort: 1,
|
||||||
|
DstPort: 1,
|
||||||
|
MTU: mtu,
|
||||||
|
Base14: isbase14,
|
||||||
|
}, &p)
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
ppp := peerpk.Public()
|
||||||
|
spp := selfpk.Public()
|
||||||
|
if isplain {
|
||||||
|
ppp = nil
|
||||||
|
spp = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddPeer(&link.PeerConfig{
|
||||||
|
PeerIP: "192.168.1.3",
|
||||||
|
EndPoint: icmpIP2,
|
||||||
|
AllowedIPs: []string{"192.168.1.3/32"},
|
||||||
|
PubicKey: ppp,
|
||||||
|
PresharedKey: pshk,
|
||||||
|
MTU: mtu,
|
||||||
|
MTURandomRange: mtu / 2,
|
||||||
|
UseZstd: true,
|
||||||
|
DoublePacket: true,
|
||||||
|
})
|
||||||
|
p.AddPeer(&link.PeerConfig{
|
||||||
|
PeerIP: "192.168.1.2",
|
||||||
|
EndPoint: icmpIP1,
|
||||||
|
AllowedIPs: []string{"192.168.1.2/32"},
|
||||||
|
PubicKey: spp,
|
||||||
|
PresharedKey: pshk,
|
||||||
|
MTU: mtu,
|
||||||
|
MTURandomRange: mtu / 2,
|
||||||
|
UseZstd: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
tunnme, err := Create(&m, "192.168.1.3")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
tunnme.Start(1, 1, 4096)
|
||||||
|
tunnpeer, err := Create(&p, "192.168.1.2")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
tunnpeer.Start(1, 1, 4096)
|
||||||
|
|
||||||
|
time.Sleep(time.Second) // wait link up
|
||||||
|
|
||||||
|
b.SetBytes(int64(sz))
|
||||||
|
b.ResetTimer()
|
||||||
|
sendb := make([]byte, sz)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rand.Read(sendb)
|
||||||
|
go tunnme.Write(sendb)
|
||||||
|
buf := make([]byte, sz)
|
||||||
|
_, err = io.ReadFull(&tunnpeer, buf)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
time.Sleep(time.Second) // wait packets all received
|
||||||
|
|
||||||
|
tunnme.Stop()
|
||||||
|
tunnpeer.Stop()
|
||||||
|
}
|
||||||
@@ -103,14 +103,14 @@ func testTunnel(t *testing.T, nw string, isplain, isbase14 bool, pshk *[32]byte,
|
|||||||
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||||
|
|
||||||
epm := "127.0.0.1"
|
epm := "127.0.0.1"
|
||||||
if nw != "ip" {
|
if nw != "ip" && nw != "icmp" {
|
||||||
epm += ":0"
|
epm += ":0"
|
||||||
}
|
}
|
||||||
// under macos you need to run
|
// under macos you need to run
|
||||||
//
|
//
|
||||||
// sudo ifconfig lo0 alias 127.0.0.2
|
// sudo ifconfig lo0 alias 127.0.0.2
|
||||||
epp := "127.0.0.2"
|
epp := "127.0.0.2"
|
||||||
if nw != "ip" {
|
if nw != "ip" && nw != "icmp" {
|
||||||
epp += ":0"
|
epp += ":0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,14 +238,14 @@ func benchmarkTunnel(b *testing.B, sz int, nw string, isplain, isbase14 bool, ps
|
|||||||
}
|
}
|
||||||
|
|
||||||
epm := "127.0.0.1"
|
epm := "127.0.0.1"
|
||||||
if nw != "ip" {
|
if nw != "ip" && nw != "icmp" {
|
||||||
epm += ":0"
|
epm += ":0"
|
||||||
}
|
}
|
||||||
// under macos you need to run
|
// under macos you need to run
|
||||||
//
|
//
|
||||||
// sudo ifconfig lo0 alias 127.0.0.2
|
// sudo ifconfig lo0 alias 127.0.0.2
|
||||||
epp := "127.0.0.2"
|
epp := "127.0.0.2"
|
||||||
if nw != "ip" {
|
if nw != "ip" && nw != "icmp" {
|
||||||
epp += ":0"
|
epp += ":0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
curve "github.com/fumiama/go-x25519"
|
curve "github.com/fumiama/go-x25519"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
_ "github.com/fumiama/WireGold/gold/p2p/icmp" // support icmp connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
||||||
|
|||||||
Reference in New Issue
Block a user