mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-05 16:00:28 +08:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb49d35f65 | ||
|
|
60a98e4cae | ||
|
|
0c3f9111f5 | ||
|
|
763b9e3d77 | ||
|
|
9d986bb1d7 | ||
|
|
fc7f1d9744 | ||
|
|
5ff8d27fe4 | ||
|
|
28c388aca9 | ||
|
|
cb2fe9bd21 | ||
|
|
06853c6552 | ||
|
|
58cb7e09a8 | ||
|
|
04a3c9a10b | ||
|
|
4ffacafb23 | ||
|
|
8fa23be251 | ||
|
|
1c665c68fb | ||
|
|
7d25f46813 | ||
|
|
0482f001ec | ||
|
|
1bbec7f8f9 | ||
|
|
1a1327b6e8 | ||
|
|
9a63b3c886 | ||
|
|
39d8d5b755 | ||
|
|
c7bbcb9fb7 | ||
|
|
5d04567ec9 | ||
|
|
739cf863f1 | ||
|
|
17e1f6cac9 | ||
|
|
32af3ce142 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test $(go list ./...)
|
||||
run: sudo go test $(go list ./...) # ip test needs sudo
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
|
||||
@@ -44,7 +44,6 @@ Mask: 0x1234567890abcdef
|
||||
Peers:
|
||||
-
|
||||
IP: "192.168.233.2"
|
||||
SubNet: 192.168.233.0/24
|
||||
PublicKey: 徯萃嵾爻燸攗窍褃冔蒔犡緇袿屿組待族砇嘀
|
||||
PresharedKey: 瀸敀爅崾嘊嵜紼樴稍毯攣矐訷蟷扛嬋庩崛昀
|
||||
EndPoint: 1.2.3.4:56789
|
||||
@@ -58,7 +57,6 @@ Peers:
|
||||
AllowTrans: true
|
||||
-
|
||||
IP: "192.168.233.3"
|
||||
SubNet: 192.168.233.0/24
|
||||
PublicKey: 牢喨粷詸衭譛浾蘹櫠砙杹蟫瑳叩刋橋経挵蘀
|
||||
PresharedKey: 竅琚喫従痸告烈兇厕趭萨假蔛瀇譄施烸蝫瘀
|
||||
EndPoint: ""
|
||||
|
||||
@@ -8,20 +8,12 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// EndPoint 一个终结点的信息
|
||||
type EndPoint struct {
|
||||
Host string `yaml:"Host"`
|
||||
Port int64 `yaml:"Port"`
|
||||
Poly uint64 `yaml:"Poly"` // Poly 是 port 随机切换算法的生成多项式, 0 为禁用
|
||||
ReconnectSeconds int64 `yaml:"ReconnectSeconds"` // ReconnectSeconds 断开重连间隔, 每次到时即向对端通报并切换到新的端口, 0 为禁用
|
||||
FECMethod string `yaml:"FECMethod"` // FECMethod 可选 1/2 2/3
|
||||
}
|
||||
|
||||
// Config WireGold 配置文件
|
||||
type Config struct {
|
||||
IP string `yaml:"IP"`
|
||||
SubNet string `yaml:"SubNet"`
|
||||
PrivateKey string `yaml:"PrivateKey"`
|
||||
Network string `yaml:"Network"` // Network udp, tcp or ws (WIP)
|
||||
EndPoint string `yaml:"EndPoint"`
|
||||
MTU int64 `yaml:"MTU"`
|
||||
SpeedLoop uint16 `yaml:"SpeedLoop"`
|
||||
@@ -32,7 +24,6 @@ type Config struct {
|
||||
// Peer 对端信息
|
||||
type Peer struct {
|
||||
IP string `yaml:"IP"`
|
||||
SubNet string `yaml:"SubNet"`
|
||||
PublicKey string `yaml:"PublicKey"`
|
||||
PresharedKey string `yaml:"PresharedKey"`
|
||||
EndPoint string `yaml:"EndPoint"`
|
||||
|
||||
3
go.mod
3
go.mod
@@ -3,7 +3,8 @@ module github.com/fumiama/WireGold
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1
|
||||
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20220412110131-4481822068bb
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/fumiama/go-x25519 v1.0.0
|
||||
|
||||
6
go.sum
6
go.sum
@@ -1,5 +1,7 @@
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1 h1:g4pTnDJUW4VbJ9NvoRfUvdjDrHz/6QhfN/LoIIpICbo=
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562 h1:snfw7FNFym1eNnLrQ/VCf80LiQo9C7jHgrunZDwiRcY=
|
||||
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package head
|
||||
|
||||
// Notify 是 map[peerip]endpoint
|
||||
type Notify = map[string]string
|
||||
// Notify 是 map[peerip]{network, endpoint}
|
||||
type Notify = map[string][2]string
|
||||
|
||||
// Query 是 peerips 组成的数组
|
||||
type Query = []string
|
||||
|
||||
@@ -12,6 +12,13 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const PacketHeadLen = 60
|
||||
|
||||
var (
|
||||
ErrBadCRCChecksum = errors.New("bad crc checksum")
|
||||
ErrDataLenLT60 = errors.New("data len < 60")
|
||||
)
|
||||
|
||||
type PacketFlags uint16
|
||||
|
||||
func (pf PacketFlags) IsValid() bool {
|
||||
@@ -70,10 +77,14 @@ type Packet struct {
|
||||
Hash [32]byte
|
||||
// crc64 包头字段的 checksum 值,可以认为在一定时间内唯一
|
||||
crc64 uint64
|
||||
// Data 承载的数据
|
||||
Data []byte
|
||||
// data 承载的数据
|
||||
data []byte
|
||||
// Data 当前的偏移
|
||||
a, b int
|
||||
// 记录还有多少字节未到达
|
||||
rembytes int
|
||||
// 是否经由 helper.MakeBytes 创建 Data
|
||||
buffered bool
|
||||
}
|
||||
|
||||
// NewPacket 生成一个新包
|
||||
@@ -85,32 +96,36 @@ func NewPacket(proto uint8, srcPort uint16, dst net.IP, dstPort uint16, data []b
|
||||
p.SrcPort = srcPort
|
||||
p.DstPort = dstPort
|
||||
p.Dst = dst
|
||||
p.Data = data
|
||||
p.data = data
|
||||
p.b = len(data)
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal 将 data 的数据解码到自身
|
||||
func (p *Packet) Unmarshal(data []byte) (complete bool, err error) {
|
||||
if len(data) < 60 {
|
||||
err = errors.New("data len < 60")
|
||||
err = ErrDataLenLT60
|
||||
return
|
||||
}
|
||||
p.crc64 = binary.LittleEndian.Uint64(data[52:60])
|
||||
p.crc64 = binary.LittleEndian.Uint64(data[52:PacketHeadLen])
|
||||
if crc64.Checksum(data[:52], crc64.MakeTable(crc64.ISO)) != p.crc64 {
|
||||
err = errors.New("bad crc checksum")
|
||||
err = ErrBadCRCChecksum
|
||||
return
|
||||
}
|
||||
|
||||
sz := p.idxdatsz & 0x0000ffff
|
||||
if sz == 0 && len(p.Data) == 0 {
|
||||
sz := p.Len()
|
||||
if sz == 0 && len(p.data) == 0 {
|
||||
p.idxdatsz = binary.LittleEndian.Uint32(data[:4])
|
||||
sz = p.idxdatsz & 0x0000ffff
|
||||
if int(sz)+52 == len(data) {
|
||||
p.Data = data[52:]
|
||||
sz = p.Len()
|
||||
if sz+52 == len(data) {
|
||||
p.data = data[52:]
|
||||
p.b = len(p.data)
|
||||
p.rembytes = 0
|
||||
} else {
|
||||
p.Data = make([]byte, sz)
|
||||
p.rembytes = int(sz)
|
||||
p.data = helper.MakeBytes(sz)
|
||||
p.buffered = true
|
||||
p.b = sz
|
||||
p.rembytes = sz
|
||||
}
|
||||
pt := binary.LittleEndian.Uint16(data[4:6])
|
||||
p.Proto = uint8(pt)
|
||||
@@ -131,7 +146,7 @@ func (p *Packet) Unmarshal(data []byte) (complete bool, err error) {
|
||||
}
|
||||
|
||||
if p.rembytes > 0 {
|
||||
p.rembytes -= copy(p.Data[flags.Offset():], data[60:])
|
||||
p.rembytes -= copy(p.data[flags.Offset():], data[PacketHeadLen:])
|
||||
logrus.Debugln("[packet] copied frag", hex.EncodeToString(p.Hash[:]), "rembytes:", p.rembytes)
|
||||
}
|
||||
|
||||
@@ -149,18 +164,19 @@ func (p *Packet) Marshal(src net.IP, teatype uint8, additional uint16, datasz ui
|
||||
}
|
||||
|
||||
if src != nil {
|
||||
p.idxdatsz = (uint32(teatype) << 27) | (uint32(additional&0x07ff) << 16) | datasz&0xffff
|
||||
p.Src = src
|
||||
offset &= 0x1fff
|
||||
if dontfrag {
|
||||
offset |= 0x4000
|
||||
}
|
||||
if hasmore {
|
||||
offset |= 0x2000
|
||||
}
|
||||
p.Flags = PacketFlags(offset)
|
||||
p.idxdatsz = (uint32(teatype) << 27) | (uint32(additional&0x07ff) << 16) | datasz&0xffff
|
||||
}
|
||||
|
||||
offset &= 0x1fff
|
||||
if dontfrag {
|
||||
offset |= 0x4000
|
||||
}
|
||||
if hasmore {
|
||||
offset |= 0x2000
|
||||
}
|
||||
p.Flags = PacketFlags(offset)
|
||||
|
||||
return helper.OpenWriterF(func(w *helper.Writer) {
|
||||
w.WriteUInt32(p.idxdatsz)
|
||||
w.WriteUInt16((uint16(p.TTL) << 8) | uint16(p.Proto))
|
||||
@@ -171,14 +187,14 @@ func (p *Packet) Marshal(src net.IP, teatype uint8, additional uint16, datasz ui
|
||||
w.Write(p.Dst.To4())
|
||||
w.Write(p.Hash[:])
|
||||
w.WriteUInt64(crc64.Checksum(w.Bytes(), crc64.MakeTable(crc64.ISO)))
|
||||
w.Write(p.Data)
|
||||
w.Write(p.Body())
|
||||
})
|
||||
}
|
||||
|
||||
// FillHash 生成 p.Data 的 Hash
|
||||
func (p *Packet) FillHash() {
|
||||
h := blake2b.New256()
|
||||
_, err := h.Write(p.Data)
|
||||
_, err := h.Write(p.Body())
|
||||
if err != nil {
|
||||
logrus.Error("[packet] err when fill hash:", err)
|
||||
return
|
||||
@@ -189,7 +205,7 @@ func (p *Packet) FillHash() {
|
||||
// IsVaildHash 验证 packet 合法性
|
||||
func (p *Packet) IsVaildHash() bool {
|
||||
h := blake2b.New256()
|
||||
_, err := h.Write(p.Data)
|
||||
_, err := h.Write(p.Body())
|
||||
if err != nil {
|
||||
logrus.Error("[packet] err when check hash:", err)
|
||||
return false
|
||||
@@ -219,3 +235,47 @@ func (p *Packet) Len() int {
|
||||
func (p *Packet) Put() {
|
||||
PutPacket(p)
|
||||
}
|
||||
|
||||
// Body returns data
|
||||
func (p *Packet) Body() []byte {
|
||||
return p.data[p.a:p.b]
|
||||
}
|
||||
|
||||
func (p *Packet) BodyLen() int {
|
||||
return p.b - p.a
|
||||
}
|
||||
|
||||
func (p *Packet) SetBody(b []byte, buffered bool) {
|
||||
p.a = 0
|
||||
p.b = len(b)
|
||||
if len(b) <= cap(p.data) {
|
||||
p.data = p.data[:len(b)]
|
||||
copy(p.data, b)
|
||||
if buffered {
|
||||
helper.PutBytes(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
if p.buffered {
|
||||
helper.PutBytes(p.data)
|
||||
}
|
||||
p.data = b
|
||||
p.buffered = buffered
|
||||
}
|
||||
|
||||
func (p *Packet) CropBody(a, b int) {
|
||||
if b > len(p.data) {
|
||||
b = len(p.data)
|
||||
}
|
||||
if a < 0 || b < 0 || a > b {
|
||||
return
|
||||
}
|
||||
p.a, p.b = a, b
|
||||
}
|
||||
|
||||
func (p *Packet) Copy() *Packet {
|
||||
newp := SelectPacket()
|
||||
*newp = *p
|
||||
newp.buffered = false
|
||||
return newp
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package head
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
var packetPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
@@ -16,6 +20,12 @@ func SelectPacket() *Packet {
|
||||
// PutPacket 将 Packet 放回池中
|
||||
func PutPacket(p *Packet) {
|
||||
p.idxdatsz = 0
|
||||
p.Data = nil
|
||||
if p.buffered {
|
||||
helper.PutBytes(p.data)
|
||||
p.buffered = false
|
||||
}
|
||||
p.a, p.b = 0, 0
|
||||
p.data = nil
|
||||
p.rembytes = 0
|
||||
packetPool.Put(p)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"math/bits"
|
||||
mrand "math/rand"
|
||||
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -51,13 +52,13 @@ func expandkeyunit(v1, v2 byte) (v uint16) {
|
||||
return
|
||||
}
|
||||
|
||||
// Encode 使用 xchacha20poly1305 和密钥序列加密
|
||||
// Encode by aead and put b into pool
|
||||
func (l *Link) Encode(teatype uint8, additional uint16, b []byte) (eb []byte) {
|
||||
if len(b) == 0 || teatype >= 32 {
|
||||
return
|
||||
}
|
||||
if l.keys[0] == nil {
|
||||
eb = make([]byte, len(b))
|
||||
eb = helper.MakeBytes(len(b))
|
||||
copy(eb, b)
|
||||
return
|
||||
}
|
||||
@@ -70,13 +71,14 @@ func (l *Link) Encode(teatype uint8, additional uint16, b []byte) (eb []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
// Decode 使用 xchacha20poly1305 和密钥序列解密
|
||||
// Decode by aead and put b into pool
|
||||
func (l *Link) Decode(teatype uint8, additional uint16, b []byte) (db []byte, err error) {
|
||||
if len(b) == 0 || teatype >= 32 {
|
||||
return
|
||||
}
|
||||
if l.keys[0] == nil {
|
||||
db = b
|
||||
db = helper.MakeBytes(len(b))
|
||||
copy(db, b)
|
||||
return
|
||||
}
|
||||
aead := l.keys[teatype]
|
||||
@@ -86,11 +88,10 @@ func (l *Link) Decode(teatype uint8, additional uint16, b []byte) (db []byte, er
|
||||
return decode(aead, additional, b)
|
||||
}
|
||||
|
||||
// encode 使用 xchacha20poly1305 加密
|
||||
func encode(aead cipher.AEAD, additional uint16, b []byte) []byte {
|
||||
nsz := aead.NonceSize()
|
||||
// Accocate capacity for all the stuffs.
|
||||
buf := make([]byte, 2+nsz+len(b)+aead.Overhead())
|
||||
buf := helper.MakeBytes(2 + nsz + len(b) + aead.Overhead())
|
||||
binary.LittleEndian.PutUint16(buf[:2], additional)
|
||||
nonce := buf[2 : 2+nsz]
|
||||
// Select a random nonce
|
||||
@@ -103,7 +104,6 @@ func encode(aead cipher.AEAD, additional uint16, b []byte) []byte {
|
||||
return nonce[:nsz+len(eb)]
|
||||
}
|
||||
|
||||
// decode 使用 xchacha20poly1305 解密
|
||||
func decode(aead cipher.AEAD, additional uint16, b []byte) ([]byte, error) {
|
||||
nsz := aead.NonceSize()
|
||||
if len(b) < nsz {
|
||||
@@ -117,7 +117,7 @@ func decode(aead cipher.AEAD, additional uint16, b []byte) ([]byte, error) {
|
||||
// Decrypt the message and check it wasn't tampered with.
|
||||
var buf [2]byte
|
||||
binary.LittleEndian.PutUint16(buf[:], additional)
|
||||
return aead.Open(nil, nonce, ciphertext, buf[:])
|
||||
return aead.Open(helper.SelectWriter().Bytes(), nonce, ciphertext, buf[:])
|
||||
}
|
||||
|
||||
// xorenc 按 8 字节, 以初始 m.mask 循环异或编码 data
|
||||
|
||||
@@ -7,10 +7,15 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPerrNotExist = errors.New("peer not exist")
|
||||
)
|
||||
|
||||
// Link 是本机到 peer 的连接抽象
|
||||
type Link struct {
|
||||
// peer 的公钥
|
||||
@@ -24,7 +29,9 @@ type Link struct {
|
||||
// peer 的虚拟 ip
|
||||
peerip net.IP
|
||||
// peer 的公网 endpoint
|
||||
endpoint *net.UDPAddr
|
||||
endpoint p2p.EndPoint
|
||||
// peer 在设置的原始值
|
||||
rawep string
|
||||
// 本机允许接收/发送的 ip 网段
|
||||
allowedips []*net.IPNet
|
||||
// 连接所用对称加密密钥集
|
||||
@@ -55,7 +62,7 @@ func (m *Me) Connect(peer string) (*Link, error) {
|
||||
if ok {
|
||||
return p, nil
|
||||
}
|
||||
return nil, errors.New("peer not exist")
|
||||
return nil, ErrPerrNotExist
|
||||
}
|
||||
|
||||
// Close 关闭到 peer 的连接
|
||||
|
||||
@@ -5,28 +5,29 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
// 监听本机 UDP endpoint
|
||||
func (m *Me) listenudp() (conn *net.UDPConn, err error) {
|
||||
conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.MustParseAddrPort(m.udpep.String())))
|
||||
const lstnbufgragsz = 65536
|
||||
|
||||
// 监听本机 endpoint
|
||||
func (m *Me) listen() (conn p2p.Conn, err error) {
|
||||
conn, err = m.ep.Listen()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.udpep = conn.LocalAddr()
|
||||
logrus.Infoln("[listen] at", m.udpep)
|
||||
m.ep = conn.LocalAddr()
|
||||
logrus.Infoln("[listen] at", m.ep)
|
||||
go func() {
|
||||
recvtotlcnt := uint64(0)
|
||||
recvloopcnt := uint16(0)
|
||||
@@ -36,7 +37,7 @@ func (m *Me) listenudp() (conn *net.UDPConn, err error) {
|
||||
n = 64 // 只用最多 64 核
|
||||
}
|
||||
logrus.Infoln("[listen] use cpu num:", n)
|
||||
listenbuff := make([]byte, 65536*n)
|
||||
listenbuff := make([]byte, lstnbufgragsz*n)
|
||||
hasntfinished := make([]sync.Mutex, n)
|
||||
for i := 0; err == nil; i++ {
|
||||
i %= n
|
||||
@@ -48,15 +49,15 @@ func (m *Me) listenudp() (conn *net.UDPConn, err error) {
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[listen] lock index", i)
|
||||
lbf := listenbuff[i*65536 : (i+1)*65536]
|
||||
n, addr, err := conn.ReadFromUDP(lbf)
|
||||
if m.loop == nil || errors.Is(err, net.ErrClosed) {
|
||||
lbf := listenbuff[i*lstnbufgragsz : (i+1)*lstnbufgragsz]
|
||||
n, addr, err := conn.ReadFromPeer(lbf)
|
||||
if m.connections == nil || errors.Is(err, net.ErrClosed) {
|
||||
logrus.Warnln("[listen] quit listening")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warnln("[listen] read from udp err, reconnect:", err)
|
||||
conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.MustParseAddrPort(m.udpep.String())))
|
||||
logrus.Warnln("[listen] read from conn err, reconnect:", err)
|
||||
conn, err = m.ep.Listen()
|
||||
if err != nil {
|
||||
logrus.Errorln("[listen] reconnect udp err:", err)
|
||||
return
|
||||
@@ -74,25 +75,26 @@ func (m *Me) listenudp() (conn *net.UDPConn, err error) {
|
||||
recvtotlcnt = 0
|
||||
recvlooptime = now
|
||||
}
|
||||
packet := m.wait(lbf[:n])
|
||||
packet := m.wait(lbf[:n:lstnbufgragsz])
|
||||
if packet == nil {
|
||||
logrus.Debugln("[listen] unlock index", i)
|
||||
logrus.Debugln("[listen] waiting, unlock index", i)
|
||||
hasntfinished[i].Unlock()
|
||||
i--
|
||||
continue
|
||||
}
|
||||
go m.listenthread(packet, addr, i, hasntfinished[i].Unlock)
|
||||
go m.dispatch(packet, addr, i, hasntfinished[i].Unlock)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Me) listenthread(packet *head.Packet, addr *net.UDPAddr, index int, finish func()) {
|
||||
func (m *Me) dispatch(packet *head.Packet, addr p2p.EndPoint, index int, finish func()) {
|
||||
defer finish()
|
||||
defer logrus.Debugln("[listen] unlock index", index)
|
||||
r := packet.Len() - len(packet.Data)
|
||||
defer logrus.Debugln("[listen] dispatched, unlock index", index)
|
||||
logrus.Debugln("[listen] start dispatching index", index)
|
||||
r := packet.Len() - packet.BodyLen()
|
||||
if r > 0 {
|
||||
logrus.Warnln("[listen] @", index, "packet from endpoint", addr, "is smaller than it declared: drop it")
|
||||
logrus.Warnln("[listen] @", index, "packet from endpoint", addr, "len", packet.BodyLen(), "is smaller than it declared len", packet.Len(), ", drop it")
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
@@ -103,9 +105,14 @@ func (m *Me) listenthread(packet *head.Packet, addr *net.UDPAddr, index int, fin
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
if p.endpoint == nil || p.endpoint.String() != addr.String() {
|
||||
logrus.Infoln("[listen] @", index, "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.endpoint)), unsafe.Pointer(addr))
|
||||
if p.endpoint == nil || !p.endpoint.Euqal(addr) {
|
||||
if m.ep.Network() == "udp" {
|
||||
logrus.Infoln("[listen] @", index, "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
p.endpoint = addr
|
||||
} else if !addr.Euqal(p.endpoint) && p.rawep == "" { // tcp/ws, ep not registered
|
||||
logrus.Infoln("[listen] @", index, "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
p.endpoint = addr
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case p.IsToMe(packet.Dst):
|
||||
@@ -116,22 +123,25 @@ func (m *Me) listenthread(packet *head.Packet, addr *net.UDPAddr, index int, fin
|
||||
}
|
||||
addt := packet.AdditionalData()
|
||||
var err error
|
||||
packet.Data, err = p.Decode(packet.CipherIndex(), addt, packet.Data)
|
||||
data, err := p.Decode(packet.CipherIndex(), addt, packet.Body())
|
||||
if err != nil {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid packet", ", key idx:", packet.CipherIndex(), "addt:", addt, "err:", err)
|
||||
logrus.Debugln("[listen] @", index, "drop invalid packet key idx:", packet.CipherIndex(), "addt:", addt, "err:", err)
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
packet.SetBody(data, true)
|
||||
if p.usezstd {
|
||||
dec, _ := zstd.NewReader(bytes.NewReader(packet.Data))
|
||||
dec, _ := zstd.NewReader(bytes.NewReader(packet.Body()))
|
||||
var err error
|
||||
packet.Data, err = io.ReadAll(dec)
|
||||
w := helper.SelectWriter()
|
||||
_, err = io.Copy(w, dec)
|
||||
dec.Close()
|
||||
if err != nil {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid zstd packet:", err)
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
packet.SetBody(w.Bytes(), true)
|
||||
}
|
||||
if !packet.IsVaildHash() {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid hash packet")
|
||||
@@ -156,22 +166,22 @@ func (m *Me) listenthread(packet *head.Packet, addr *net.UDPAddr, index int, fin
|
||||
packet.Put()
|
||||
case head.ProtoNotify:
|
||||
logrus.Infoln("[listen] @", index, "recv notify from", packet.Src)
|
||||
go p.onNotify(packet.Data)
|
||||
go p.onNotify(packet.Body())
|
||||
packet.Put()
|
||||
case head.ProtoQuery:
|
||||
logrus.Infoln("[listen] @", index, "recv query from", packet.Src)
|
||||
go p.onQuery(packet.Data)
|
||||
go p.onQuery(packet.Body())
|
||||
packet.Put()
|
||||
case head.ProtoData:
|
||||
if p.pipe != nil {
|
||||
p.pipe <- packet
|
||||
logrus.Debugln("[listen] @", index, "deliver to pipe of", p.peerip)
|
||||
} else {
|
||||
_, err := m.nic.Write(packet.Data)
|
||||
_, err := m.nic.Write(packet.Body())
|
||||
if err != nil {
|
||||
logrus.Errorln("[listen] @", index, "deliver", len(packet.Data), "bytes data to nic err:", err)
|
||||
logrus.Errorln("[listen] @", index, "deliver", packet.BodyLen(), "bytes data to nic err:", err)
|
||||
} else {
|
||||
logrus.Debugln("[listen] @", index, "deliver", len(packet.Data), "bytes data to nic")
|
||||
logrus.Debugln("[listen] @", index, "deliver", packet.BodyLen(), "bytes data to nic")
|
||||
}
|
||||
packet.Put()
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/lower"
|
||||
"github.com/fumiama/water/waterutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/lower"
|
||||
)
|
||||
|
||||
// Me 是本机的抽象
|
||||
@@ -27,16 +29,14 @@ type Me struct {
|
||||
me net.IP
|
||||
// 本机子网
|
||||
subnet net.IPNet
|
||||
// 本机 UDP endpoint
|
||||
udpep net.Addr
|
||||
// 本机环回 link
|
||||
loop *Link
|
||||
// 本机 endpoint
|
||||
ep p2p.EndPoint
|
||||
// 本机活跃的所有连接
|
||||
connections map[string]*Link
|
||||
// 读写同步锁
|
||||
connmapmu sync.RWMutex
|
||||
// 本机监听的 udp 连接, 用于向对端直接发送报文
|
||||
udpconn *net.UDPConn
|
||||
// 本机监听的连接端点, 也用于向对端直接发送报文
|
||||
conn p2p.Conn
|
||||
// 本机网卡
|
||||
nic lower.NICIO
|
||||
// 本机路由表
|
||||
@@ -49,11 +49,15 @@ type Me struct {
|
||||
srcport, dstport, mtu, speedloop uint16
|
||||
// 报头掩码
|
||||
mask uint64
|
||||
// 本机网络端点初始化配置
|
||||
networkconfigs []any
|
||||
}
|
||||
|
||||
type MyConfig struct {
|
||||
MyIPwithMask string
|
||||
MyEndpoint string
|
||||
Network string
|
||||
NetworkConfigs []any
|
||||
PrivateKey *[32]byte
|
||||
NIC lower.NICIO
|
||||
SrcPort, DstPort, MTU, SpeedLoop uint16
|
||||
@@ -64,7 +68,12 @@ type MyConfig struct {
|
||||
func NewMe(cfg *MyConfig) (m Me) {
|
||||
m.privKey = *cfg.PrivateKey
|
||||
var err error
|
||||
m.udpep, err = net.ResolveUDPAddr("udp", cfg.MyEndpoint)
|
||||
nw := cfg.Network
|
||||
if nw == "" {
|
||||
nw = "udp"
|
||||
}
|
||||
m.networkconfigs = cfg.NetworkConfigs
|
||||
m.ep, err = p2p.NewEndPoint(nw, cfg.MyEndpoint, m.networkconfigs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -74,7 +83,7 @@ func NewMe(cfg *MyConfig) (m Me) {
|
||||
}
|
||||
m.me = ip
|
||||
m.subnet = *cidr
|
||||
m.udpconn, err = m.listenudp()
|
||||
m.conn, err = m.listen()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -86,17 +95,6 @@ func NewMe(cfg *MyConfig) (m Me) {
|
||||
cache: ttl.NewCache[string, *Link](time.Minute),
|
||||
}
|
||||
m.router.SetDefault(nil)
|
||||
_, localp, err := net.SplitHostPort(m.EndPoint().String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.loop = m.AddPeer(&PeerConfig{
|
||||
PeerIP: m.me.String(),
|
||||
EndPoint: "127.0.0.1:" + localp,
|
||||
AllowedIPs: []string{cfg.MyIPwithMask},
|
||||
NoPipe: cfg.NIC != nil,
|
||||
MTU: cfg.MTU,
|
||||
})
|
||||
m.srcport = cfg.SrcPort
|
||||
m.dstport = cfg.DstPort
|
||||
m.mtu = cfg.MTU & 0xfff8
|
||||
@@ -125,16 +123,15 @@ func (m *Me) MTU() uint16 {
|
||||
return m.mtu
|
||||
}
|
||||
|
||||
func (m *Me) EndPoint() net.Addr {
|
||||
return m.udpep
|
||||
func (m *Me) EndPoint() p2p.EndPoint {
|
||||
return m.ep
|
||||
}
|
||||
|
||||
func (m *Me) Close() error {
|
||||
m.loop = nil
|
||||
m.connections = nil
|
||||
if m.udpconn != nil {
|
||||
_ = m.udpconn.Close()
|
||||
m.udpconn = nil
|
||||
if m.conn != nil {
|
||||
_ = m.conn.Close()
|
||||
m.conn = nil
|
||||
}
|
||||
m.router = nil
|
||||
if m.recving != nil {
|
||||
@@ -211,6 +208,14 @@ func (m *Me) sendAllSameDst(packet []byte) (n int) {
|
||||
rem = rem[i:]
|
||||
dst := waterutil.IPv4Destination(packet)
|
||||
logrus.Debugln("[me] sending", len(packet), "bytes packet from :"+strconv.Itoa(int(m.SrcPort())), "to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), "remain:", len(rem), "bytes")
|
||||
if m.me.Equal(dst) { // is to myself, write to nic (pipe not allow loopback)
|
||||
logrus.Debugln("[me] loopback packet")
|
||||
_, err := m.nic.Write(packet)
|
||||
if err != nil {
|
||||
logrus.Warnln("[me] write to loopback err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
lnk := m.router.NextHop(dst.String())
|
||||
if lnk == nil {
|
||||
logrus.Warnln("[me] drop packet to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), ": nil nexthop")
|
||||
|
||||
@@ -2,12 +2,12 @@ package link
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
@@ -19,6 +19,9 @@ func (l *Link) keepAlive(dur int64) {
|
||||
logrus.Infoln("[nat] start to keep alive")
|
||||
t := time.NewTicker(time.Second * time.Duration(dur))
|
||||
for range t.C {
|
||||
if l.me.connections == nil {
|
||||
return
|
||||
}
|
||||
n, err := l.WriteAndPut(head.NewPacket(head.ProtoHello, l.me.srcport, l.peerip, l.me.dstport, nil), false)
|
||||
if err == nil {
|
||||
logrus.Infoln("[nat] send", n, "bytes keep alive packet")
|
||||
@@ -44,11 +47,16 @@ func (l *Link) onNotify(packet []byte) {
|
||||
// ---- 遍历 Notify,注册对方的 endpoint 到
|
||||
// ---- connections,注意使用读写锁connmapmu
|
||||
for peer, ep := range notify {
|
||||
addr, err := net.ResolveUDPAddr("udp", ep)
|
||||
nw, epstr := ep[0], ep[1]
|
||||
if nw != l.me.ep.Network() {
|
||||
logrus.Warnln("[nat] ignore different network notify", nw, "addr", epstr)
|
||||
continue
|
||||
}
|
||||
addr, err := p2p.NewEndPoint(nw, epstr, l.me.networkconfigs...)
|
||||
if err == nil {
|
||||
p, ok := l.me.IsInPeer(peer)
|
||||
if ok {
|
||||
if p.endpoint.String() != ep {
|
||||
if !p.endpoint.Euqal(addr) {
|
||||
p.endpoint = addr
|
||||
logrus.Infoln("[nat] notify set ep of peer", peer, "to", ep)
|
||||
}
|
||||
@@ -73,14 +81,32 @@ func (l *Link) onQuery(packet []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
if l == nil || l.me == nil {
|
||||
logrus.Errorln("[nat] nil link/me")
|
||||
return
|
||||
}
|
||||
|
||||
// 2. notify分发
|
||||
// ---- 封装 Notify 到 新的 packet
|
||||
// ---- 调用 l.Send 发送到对方
|
||||
notify := make(head.Notify, len(peers))
|
||||
for _, p := range peers {
|
||||
lnk, ok := l.me.IsInPeer(p)
|
||||
eps := ""
|
||||
if l.me.ep.Network() == "udp" { // udp has real p2p
|
||||
eps = lnk.endpoint.String()
|
||||
}
|
||||
if eps == "" {
|
||||
eps = l.rawep // use registered ep only
|
||||
}
|
||||
if eps == "" {
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
notify[p] = lnk.endpoint.String()
|
||||
notify[p] = [2]string{
|
||||
lnk.endpoint.Network(),
|
||||
eps,
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(notify) > 0 {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
curve "github.com/fumiama/go-x25519"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
@@ -37,6 +38,7 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
l = &Link{
|
||||
pubk: cfg.PubicKey,
|
||||
peerip: net.ParseIP(cfg.PeerIP),
|
||||
rawep: cfg.EndPoint,
|
||||
allowtrans: cfg.AllowTrans,
|
||||
usezstd: cfg.UseZstd,
|
||||
me: m,
|
||||
@@ -72,7 +74,7 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
}
|
||||
}
|
||||
if cfg.EndPoint != "" {
|
||||
e, err := net.ResolveUDPAddr("udp", cfg.EndPoint)
|
||||
e, err := p2p.NewEndPoint(m.ep.Network(), cfg.EndPoint, m.networkconfigs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (l *Link) Read() *head.Packet {
|
||||
}
|
||||
|
||||
func (m *Me) wait(data []byte) *head.Packet {
|
||||
if len(data) < 60 { // not a valid packet
|
||||
if len(data) < head.PacketHeadLen { // not a valid packet
|
||||
return nil
|
||||
}
|
||||
bound := 64
|
||||
@@ -33,7 +33,7 @@ func (m *Me) wait(data []byte) *head.Packet {
|
||||
logrus.Debugln("[recv] drop invalid flags packet:", hex.EncodeToString(data[11:12]), hex.EncodeToString(data[10:11]))
|
||||
return nil
|
||||
}
|
||||
crc := binary.LittleEndian.Uint64(data[52:60])
|
||||
crc := binary.LittleEndian.Uint64(data[52:head.PacketHeadLen])
|
||||
if m.recved.Get(crc) { // 是重放攻击
|
||||
logrus.Warnln("[recv] ignore duplicated crc packet", strconv.FormatUint(crc, 16))
|
||||
return nil
|
||||
|
||||
@@ -14,6 +14,11 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDropBigDontFragPkt = errors.New("drop big don't fragmnet packet")
|
||||
ErrTTL = errors.New("ttl exceeded")
|
||||
)
|
||||
|
||||
// WriteAndPut 向 peer 发包并将包放回缓存池
|
||||
func (l *Link) WriteAndPut(p *head.Packet, istransfer bool) (n int, err error) {
|
||||
defer p.Put()
|
||||
@@ -27,37 +32,38 @@ func (l *Link) WriteAndPut(p *head.Packet, istransfer bool) (n int, err error) {
|
||||
if !istransfer {
|
||||
l.encrypt(p, sndcnt, teatype)
|
||||
}
|
||||
delta := (int(mtu) - 60) & 0x0000fff8
|
||||
delta := (int(mtu) - head.PacketHeadLen) & 0x0000fff8
|
||||
if delta <= 0 {
|
||||
logrus.Warnln("[send] reset invalid data frag len", delta, "to 8")
|
||||
delta = 8
|
||||
}
|
||||
if len(p.Data) <= delta {
|
||||
return l.write(p, teatype, sndcnt, uint32(len(p.Data)), 0, istransfer, false)
|
||||
remlen := p.BodyLen()
|
||||
if remlen <= delta {
|
||||
return l.write(p, teatype, sndcnt, uint32(remlen), 0, istransfer, false)
|
||||
}
|
||||
if istransfer && p.Flags.DontFrag() && len(p.Data) > delta {
|
||||
return 0, errors.New("drop don't fragmnet big trans packet")
|
||||
if istransfer && p.Flags.DontFrag() && remlen > delta {
|
||||
return 0, ErrDropBigDontFragPkt
|
||||
}
|
||||
data := p.Data
|
||||
ttl := p.TTL
|
||||
totl := uint32(len(data))
|
||||
totl := uint32(remlen)
|
||||
pos := 0
|
||||
packet := head.SelectPacket()
|
||||
*packet = *p
|
||||
for ; int(totl)-pos > delta; pos += delta {
|
||||
logrus.Debugln("[send] split frag [", pos, "~", pos+delta, "], remain:", int(totl)-pos-delta)
|
||||
packet.Data = data[:delta]
|
||||
packet := p.Copy()
|
||||
for remlen > delta {
|
||||
remlen -= delta
|
||||
logrus.Debugln("[send] split frag [", pos, "~", pos+delta, "], remain:", remlen)
|
||||
packet.CropBody(pos, pos+delta)
|
||||
cnt, err := l.write(packet, teatype, sndcnt, totl, uint16(pos>>3), istransfer, true)
|
||||
n += cnt
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
data = data[delta:]
|
||||
packet.TTL = ttl
|
||||
pos += delta
|
||||
}
|
||||
packet.Put()
|
||||
if len(data) > 0 {
|
||||
p.Data = data
|
||||
if remlen > 0 {
|
||||
logrus.Debugln("[send] last frag [", pos, "~", pos+remlen, "]")
|
||||
p.CropBody(pos, pos+remlen)
|
||||
cnt := 0
|
||||
cnt, err = l.write(p, teatype, sndcnt, totl, uint16(pos>>3), istransfer, false)
|
||||
n += cnt
|
||||
@@ -67,47 +73,50 @@ func (l *Link) WriteAndPut(p *head.Packet, istransfer bool) (n int, err error) {
|
||||
|
||||
func (l *Link) encrypt(p *head.Packet, sndcnt uint16, teatype uint8) {
|
||||
p.FillHash()
|
||||
logrus.Debugln("[send] data len before encrypt:", len(p.Data))
|
||||
logrus.Debugln("[send] data len before encrypt:", p.BodyLen())
|
||||
data := p.Body()
|
||||
if l.usezstd {
|
||||
w := helper.SelectWriter()
|
||||
defer helper.PutWriter(w)
|
||||
enc, _ := zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedFastest))
|
||||
_, _ = io.Copy(enc, bytes.NewReader(p.Data))
|
||||
_, _ = io.Copy(enc, bytes.NewReader(data))
|
||||
enc.Close()
|
||||
p.Data = w.Bytes()
|
||||
logrus.Debugln("[send] data len after zstd:", len(p.Data))
|
||||
data = w.Bytes()
|
||||
logrus.Debugln("[send] data len after zstd:", len(data))
|
||||
}
|
||||
p.Data = l.Encode(teatype, sndcnt&0x07ff, p.Data)
|
||||
logrus.Debugln("[send] data len after xchacha20:", len(p.Data), "addt:", sndcnt)
|
||||
p.SetBody(l.Encode(teatype, sndcnt&0x07ff, data), true)
|
||||
logrus.Debugln("[send] data len after xchacha20:", p.BodyLen(), "addt:", sndcnt)
|
||||
}
|
||||
|
||||
// write 向 peer 发一个包
|
||||
func (l *Link) write(p *head.Packet, teatype uint8, additional uint16, datasz uint32, offset uint16, istransfer, hasmore bool) (n int, err error) {
|
||||
func (l *Link) write(p *head.Packet, teatype uint8, additional uint16, datasz uint32, offset uint16, istransfer, hasmore bool) (int, error) {
|
||||
peerep := l.endpoint
|
||||
if peerep == nil {
|
||||
return 0, errors.New("nil endpoint of " + p.Dst.String())
|
||||
}
|
||||
|
||||
var d []byte
|
||||
var cl func()
|
||||
// TODO: now all packet allow frag, adapt to DF
|
||||
if istransfer {
|
||||
d, cl = p.Marshal(nil, teatype, additional, 0, 0, false, false)
|
||||
d, cl = p.Marshal(nil, 0, 0, 0, offset, false, hasmore)
|
||||
} else {
|
||||
d, cl = p.Marshal(l.me.me, teatype, additional, datasz, offset, false, hasmore)
|
||||
}
|
||||
if d == nil {
|
||||
return 0, errors.New("[send] ttl exceeded")
|
||||
}
|
||||
peerep := l.endpoint
|
||||
if peerep == nil {
|
||||
return 0, errors.New("[send] nil endpoint of " + p.Dst.String())
|
||||
return 0, ErrTTL
|
||||
}
|
||||
defer cl()
|
||||
|
||||
bound := 64
|
||||
endl := "..."
|
||||
if len(d) < bound {
|
||||
bound = len(d)
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[send] write", len(d), "bytes data from ep", l.me.udpconn.LocalAddr(), "to", peerep, "offset:", fmt.Sprintf("%04x", offset))
|
||||
logrus.Debugln("[send] write", len(d), "bytes data from ep", l.me.conn.LocalAddr(), "to", peerep, "offset:", fmt.Sprintf("%04x", offset))
|
||||
logrus.Debugln("[send] data bytes", hex.EncodeToString(d[:bound]), endl)
|
||||
d = l.me.xorenc(d)
|
||||
logrus.Debugln("[send] data xored", hex.EncodeToString(d[:bound]), endl)
|
||||
n, err = l.me.udpconn.WriteToUDP(d, peerep)
|
||||
cl()
|
||||
return
|
||||
return l.me.conn.WriteToPeer(d, peerep)
|
||||
}
|
||||
|
||||
44
gold/p2p/define.go
Normal file
44
gold/p2p/define.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEndpointTypeMistatch = errors.New("endpoint type mismatch")
|
||||
)
|
||||
|
||||
type Initializer func(endpoint string, configs ...any) (EndPoint, error)
|
||||
|
||||
var factory syncx.Map[string, Initializer]
|
||||
|
||||
func Register(network string, initializer Initializer) (actual Initializer, hasexist bool) {
|
||||
return factory.LoadOrStore(network, initializer)
|
||||
}
|
||||
|
||||
type EndPoint interface {
|
||||
fmt.Stringer
|
||||
Network() string
|
||||
Euqal(EndPoint) bool
|
||||
Listen() (Conn, error)
|
||||
}
|
||||
|
||||
func NewEndPoint(network, endpoint string, configs ...any) (EndPoint, error) {
|
||||
initializer, ok := factory.Load(network)
|
||||
if !ok {
|
||||
return nil, errors.New("network " + network + " not found")
|
||||
}
|
||||
return initializer(endpoint, configs...)
|
||||
}
|
||||
|
||||
type Conn interface {
|
||||
io.Closer
|
||||
fmt.Stringer // basically, the local address string
|
||||
LocalAddr() EndPoint
|
||||
ReadFromPeer([]byte) (int, EndPoint, error)
|
||||
WriteToPeer([]byte, EndPoint) (int, error)
|
||||
}
|
||||
35
gold/p2p/ip/init.go
Normal file
35
gold/p2p/ip/init.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, configs ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddr(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptcl := uint(0x04) // IPIP
|
||||
if len(configs) > 0 {
|
||||
ptcl = configs[0].(uint)
|
||||
}
|
||||
return &EndPoint{
|
||||
addr: &net.IPAddr{
|
||||
IP: addr.AsSlice(),
|
||||
Zone: addr.Zone(),
|
||||
},
|
||||
ptcl: ptcl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := helper.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
79
gold/p2p/ip/ip.go
Normal file
79
gold/p2p/ip/ip.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint struct {
|
||||
addr *net.IPAddr
|
||||
ptcl uint
|
||||
}
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return ep.addr.String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(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 ipep1.addr.IP.Equal(ipep2.addr.IP) &&
|
||||
ipep1.addr.Zone == ipep2.addr.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := net.ListenIP(
|
||||
"ip:"+strconv.Itoa(int(ep.ptcl)),
|
||||
ep.addr,
|
||||
)
|
||||
return &Conn{
|
||||
ep: ep,
|
||||
conn: conn,
|
||||
}, err
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
ep *EndPoint
|
||||
conn *net.IPConn
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return conn.conn.Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.conn.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint(conn.conn.LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := conn.conn.ReadFromIP(b)
|
||||
return n, &EndPoint{
|
||||
addr: addr,
|
||||
ptcl: conn.ep.ptcl,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
ipep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return conn.conn.WriteToIP(b, ipep.addr)
|
||||
}
|
||||
47
gold/p2p/tcp/init.go
Normal file
47
gold/p2p/tcp/init.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DialTimeout time.Duration
|
||||
PeersTimeout time.Duration
|
||||
ReceiveChannelSize int
|
||||
}
|
||||
|
||||
func NewEndpoint(endpoint string, configs ...any) (p2p.EndPoint, error) {
|
||||
return newEndpoint(endpoint, configs...)
|
||||
}
|
||||
|
||||
func newEndpoint(endpoint string, configs ...any) (*EndPoint, error) {
|
||||
var cfg *Config
|
||||
if len(configs) == 0 || configs[0] == nil {
|
||||
cfg = &Config{}
|
||||
} else {
|
||||
cfg = configs[0].(*Config)
|
||||
}
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EndPoint{
|
||||
addr: net.TCPAddrFromAddrPort(addr),
|
||||
dialtimeout: cfg.DialTimeout,
|
||||
peerstimeout: cfg.PeersTimeout,
|
||||
recvchansize: cfg.ReceiveChannelSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := helper.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
126
gold/p2p/tcp/pdu.go
Normal file
126
gold/p2p/tcp/pdu.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMagic = errors.New("invalid magic")
|
||||
)
|
||||
|
||||
type packetType uint8
|
||||
|
||||
const (
|
||||
packetTypeKeepAlive packetType = iota
|
||||
packetTypeNormal
|
||||
packetTypeTop
|
||||
)
|
||||
|
||||
const magic = 0x12d3fde9
|
||||
|
||||
var magicbuf [4]byte
|
||||
|
||||
func init() {
|
||||
binary.LittleEndian.PutUint32(magicbuf[:], magic)
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
typ packetType
|
||||
len uint16
|
||||
dat []byte
|
||||
io.ReaderFrom
|
||||
io.WriterTo
|
||||
}
|
||||
|
||||
func (p *packet) pack() (net.Buffers, func()) {
|
||||
d, cl := helper.OpenWriterF(func(w *helper.Writer) {
|
||||
w.WriteByte(byte(p.typ))
|
||||
w.WriteUInt16(p.len)
|
||||
})
|
||||
return net.Buffers{magicbuf[:], d, p.dat}, cl
|
||||
}
|
||||
|
||||
func (p *packet) Read(_ []byte) (int, error) {
|
||||
panic("stub")
|
||||
}
|
||||
|
||||
func (p *packet) Write(_ []byte) (int, error) {
|
||||
panic("stub")
|
||||
}
|
||||
|
||||
func (p *packet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var buf [4]byte
|
||||
cnt, err := io.ReadFull(r, buf[:])
|
||||
n = int64(cnt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if binary.LittleEndian.Uint32(buf[:]) != magic {
|
||||
err = ErrInvalidMagic
|
||||
return
|
||||
}
|
||||
cnt, err = io.ReadFull(r, buf[:3])
|
||||
n += int64(cnt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.typ = packetType(buf[0])
|
||||
p.len = binary.LittleEndian.Uint16(buf[1:3])
|
||||
w := helper.SelectWriter()
|
||||
copied, err := io.CopyN(w, r, int64(p.len))
|
||||
n += copied
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.dat = w.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func (p *packet) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf, cl := p.pack()
|
||||
defer cl()
|
||||
return io.Copy(w, &buf)
|
||||
}
|
||||
|
||||
func isvalid(tcpconn *net.TCPConn) bool {
|
||||
pckt := packet{}
|
||||
|
||||
stopch := make(chan struct{})
|
||||
t := time.AfterFunc(time.Second, func() {
|
||||
stopch <- struct{}{}
|
||||
})
|
||||
|
||||
var err error
|
||||
copych := make(chan struct{})
|
||||
go func() {
|
||||
_, err = io.Copy(&pckt, tcpconn)
|
||||
copych <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-stopch:
|
||||
logrus.Debugln("[tcp] validate recv from", tcpconn.RemoteAddr(), "timeout")
|
||||
return false
|
||||
case <-copych:
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] validate recv from", tcpconn.RemoteAddr(), "err:", err)
|
||||
return false
|
||||
}
|
||||
if pckt.typ != packetTypeKeepAlive {
|
||||
logrus.Debugln("[tcp] validate got invalid typ", pckt.typ, "from", tcpconn.RemoteAddr())
|
||||
return false
|
||||
}
|
||||
|
||||
logrus.Debugln("[tcp] passed validate recv from", tcpconn.RemoteAddr())
|
||||
return true
|
||||
}
|
||||
289
gold/p2p/tcp/tcp.go
Normal file
289
gold/p2p/tcp/tcp.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type EndPoint struct {
|
||||
addr *net.TCPAddr
|
||||
dialtimeout time.Duration
|
||||
peerstimeout time.Duration
|
||||
recvchansize int
|
||||
}
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return ep.addr.String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
tcpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
tcpep1 := ep
|
||||
return tcpep1.addr.IP.Equal(tcpep2.addr.IP) &&
|
||||
tcpep1.addr.Port == tcpep2.addr.Port &&
|
||||
tcpep1.addr.Zone == tcpep2.addr.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
lstn, err := net.ListenTCP(ep.addr.Network(), ep.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ep.addr = lstn.Addr().(*net.TCPAddr)
|
||||
peerstimeout := ep.peerstimeout
|
||||
if peerstimeout < time.Second*30 {
|
||||
peerstimeout = time.Second * 30
|
||||
}
|
||||
chansz := ep.recvchansize
|
||||
if chansz < 32 {
|
||||
chansz = 32
|
||||
}
|
||||
conn := &Conn{
|
||||
addr: ep,
|
||||
lstn: lstn,
|
||||
peers: ttl.NewCacheOn(peerstimeout, [4]func(string, *net.TCPConn){
|
||||
nil, nil, func(_ string, t *net.TCPConn) {
|
||||
err := t.CloseWrite()
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] close write from", t.LocalAddr(), "to", t.RemoteAddr(), "err:", err)
|
||||
} else {
|
||||
logrus.Debugln("[tcp] close write from", t.LocalAddr(), "to", t.RemoteAddr())
|
||||
}
|
||||
}, nil,
|
||||
}),
|
||||
recv: make(chan *connrecv, chansz),
|
||||
cplk: &sync.Mutex{},
|
||||
}
|
||||
go conn.accept()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type connrecv struct {
|
||||
addr *EndPoint // cast from tcpconn.RemoteAddr()
|
||||
conn *net.TCPConn
|
||||
pckt packet
|
||||
}
|
||||
|
||||
// Conn 伪装成无状态的有状态连接
|
||||
type Conn struct {
|
||||
addr *EndPoint
|
||||
lstn *net.TCPListener
|
||||
peers *ttl.Cache[string, *net.TCPConn]
|
||||
recv chan *connrecv
|
||||
cplk *sync.Mutex
|
||||
}
|
||||
|
||||
func (conn *Conn) accept() {
|
||||
for {
|
||||
tcpconn, err := conn.lstn.AcceptTCP()
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) { // normal close
|
||||
logrus.Infoln("[tcp] accept of", conn.addr, "got closed")
|
||||
return
|
||||
}
|
||||
if conn.addr == nil || conn.lstn == nil || conn.peers == nil || conn.recv == nil {
|
||||
return
|
||||
}
|
||||
logrus.Warnln("[tcp] accept on", conn.addr, "err:", err)
|
||||
_ = conn.Close()
|
||||
newc, err := conn.addr.Listen()
|
||||
if err != nil {
|
||||
logrus.Warn("[tcp] re-listen on", conn.addr, "err:", err)
|
||||
return
|
||||
}
|
||||
*conn = *newc.(*Conn)
|
||||
logrus.Info("[tcp] re-listen on", conn.addr)
|
||||
continue
|
||||
}
|
||||
go conn.receive(tcpconn, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) receive(tcpconn *net.TCPConn, hasvalidated bool) {
|
||||
ep, _ := newEndpoint(tcpconn.RemoteAddr().String(), &Config{
|
||||
DialTimeout: conn.addr.dialtimeout,
|
||||
PeersTimeout: conn.addr.peerstimeout,
|
||||
ReceiveChannelSize: conn.addr.recvchansize,
|
||||
})
|
||||
|
||||
if !hasvalidated {
|
||||
if !isvalid(tcpconn) {
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[tcp] accept from", ep)
|
||||
conn.peers.Set(ep.String(), tcpconn)
|
||||
}
|
||||
|
||||
peerstimeout := conn.addr.peerstimeout
|
||||
if peerstimeout < time.Second*30 {
|
||||
peerstimeout = time.Second * 30
|
||||
}
|
||||
peerstimeout *= 2
|
||||
defer conn.peers.Delete(ep.String())
|
||||
for {
|
||||
r := &connrecv{addr: ep}
|
||||
if conn.addr == nil || conn.lstn == nil || conn.peers == nil || conn.recv == nil {
|
||||
return
|
||||
}
|
||||
tcpconn := conn.peers.Get(ep.String())
|
||||
if tcpconn == nil {
|
||||
return
|
||||
}
|
||||
r.conn = tcpconn
|
||||
|
||||
stopch := make(chan struct{})
|
||||
t := time.AfterFunc(peerstimeout, func() {
|
||||
stopch <- struct{}{}
|
||||
})
|
||||
|
||||
var err error
|
||||
copych := make(chan struct{})
|
||||
go func() {
|
||||
_, err = io.Copy(&r.pckt, tcpconn)
|
||||
copych <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-stopch:
|
||||
logrus.Debugln("[tcp] recv from", ep, "timeout")
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
case <-copych:
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] recv from", ep, "err:", err)
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
}
|
||||
if r.pckt.typ >= packetTypeTop {
|
||||
logrus.Debugln("[tcp] close reading invalid conn from", ep, "typ", r.pckt.typ, "len", r.pckt.len)
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[tcp] dispatch packet from", ep, "typ", r.pckt.typ, "len", r.pckt.len)
|
||||
conn.recv <- r
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
if conn.lstn != nil {
|
||||
_ = conn.lstn.Close()
|
||||
}
|
||||
if conn.peers != nil {
|
||||
conn.peers.Destroy()
|
||||
}
|
||||
if conn.recv != nil {
|
||||
close(conn.recv)
|
||||
}
|
||||
conn.addr = nil
|
||||
conn.lstn = nil
|
||||
conn.peers = nil
|
||||
conn.recv = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.addr.String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
return conn.addr
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
var p *connrecv
|
||||
for {
|
||||
p = <-conn.recv
|
||||
if p == nil {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
conn.peers.Set(p.addr.String(), p.conn)
|
||||
if p.pckt.typ == packetTypeNormal {
|
||||
break
|
||||
}
|
||||
defer helper.PutBytes(p.pckt.dat)
|
||||
}
|
||||
n := copy(b, p.pckt.dat)
|
||||
return n, p.addr, nil
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (n int, err error) {
|
||||
tcpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
blen := len(b)
|
||||
if blen >= 65536 {
|
||||
return 0, errors.New("data size " + strconv.Itoa(blen) + " is too large")
|
||||
}
|
||||
retried := false
|
||||
conn.cplk.Lock()
|
||||
defer conn.cplk.Unlock()
|
||||
tcpconn := conn.peers.Get(tcpep.String())
|
||||
RECONNECT:
|
||||
if tcpconn == nil {
|
||||
dialtimeout := tcpep.dialtimeout
|
||||
if dialtimeout < time.Second {
|
||||
dialtimeout = time.Second
|
||||
}
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "timeout", dialtimeout)
|
||||
var cn net.Conn
|
||||
// must use another port to send because there's no exsiting conn
|
||||
cn, err = net.DialTimeout(tcpep.Network(), tcpep.addr.String(), dialtimeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tcpconn, ok = cn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return 0, errors.New("expect *net.TCPConn but got " + reflect.ValueOf(cn).Type().String())
|
||||
}
|
||||
_, err = io.Copy(tcpconn, &packet{
|
||||
typ: packetTypeKeepAlive,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "success, but write err:", err)
|
||||
return 0, err
|
||||
}
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "success, local:", tcpconn.LocalAddr())
|
||||
conn.peers.Set(tcpep.String(), tcpconn)
|
||||
go conn.receive(tcpconn, true)
|
||||
} else {
|
||||
logrus.Debugln("[tcp] reuse tcpconn from", tcpconn.LocalAddr(), "to", tcpconn.RemoteAddr())
|
||||
}
|
||||
cnt, err := io.Copy(tcpconn, &packet{
|
||||
typ: packetTypeNormal,
|
||||
len: uint16(blen),
|
||||
dat: b,
|
||||
})
|
||||
if err != nil {
|
||||
conn.peers.Delete(tcpep.String())
|
||||
if !retried {
|
||||
retried = true
|
||||
tcpconn = nil
|
||||
goto RECONNECT
|
||||
}
|
||||
}
|
||||
return int(cnt) - 3, err
|
||||
}
|
||||
25
gold/p2p/udp/init.go
Normal file
25
gold/p2p/udp/init.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(net.UDPAddrFromAddrPort(addr)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := helper.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
62
gold/p2p/udp/udp.go
Normal file
62
gold/p2p/udp/udp.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint net.UDPAddr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*net.UDPAddr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return (*net.UDPAddr)(ep).Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
udpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
udpep1 := ep
|
||||
return udpep1.IP.Equal(udpep2.IP) && udpep1.Port == udpep2.Port && udpep1.Zone == udpep2.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := net.ListenUDP((*net.UDPAddr)(ep).Network(), (*net.UDPAddr)(ep))
|
||||
return (*Conn)(conn), err
|
||||
}
|
||||
|
||||
type Conn net.UDPConn
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return (*net.UDPConn)(conn).Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return (*net.UDPConn)(conn).LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint((*net.UDPConn)(conn).LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
udpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return (*net.UDPConn)(conn).WriteTo(b, (*net.UDPAddr)(udpep))
|
||||
}
|
||||
27
gold/p2p/udplite/init.go
Normal file
27
gold/p2p/udplite/init.go
Normal file
@@ -0,0 +1,27 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(net.UDPAddrFromAddrPort(addr)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := helper.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
93
gold/p2p/udplite/lite.go
Normal file
93
gold/p2p/udplite/lite.go
Normal file
@@ -0,0 +1,93 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
)
|
||||
|
||||
// https://www.kernel.org/doc/Documentation/networking/udplite.txt
|
||||
const (
|
||||
IPPROTO_UDPLITE = 136
|
||||
SOL_UDPLITE = 136
|
||||
UDPLITE_SEND_CSCOV = 10
|
||||
UDPLITE_RECV_CSCOV = 11
|
||||
)
|
||||
|
||||
type sysListener struct {
|
||||
net.ListenConfig
|
||||
network, address string
|
||||
}
|
||||
|
||||
type sockaddr interface {
|
||||
net.Addr
|
||||
}
|
||||
|
||||
//go:linkname toLocal net.(*UDPAddr).toLocal
|
||||
func toLocal(a *net.UDPAddr, net string) sockaddr
|
||||
|
||||
//go:linkname internetSocket net.internetSocket
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
|
||||
//go:linkname newUDPConn net.newUDPConn
|
||||
func newUDPConn(fd unsafe.Pointer) *net.UDPConn
|
||||
|
||||
var sockaddrinterfaceinstance = toLocal(&net.UDPAddr{}, "")
|
||||
|
||||
func (sl *sysListener) listenUDP(ctx context.Context, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
var ctrlCtxFn func(cxt context.Context, network, address string, c syscall.RawConn) error
|
||||
if sl.ListenConfig.Control != nil {
|
||||
ctrlCtxFn = func(cxt context.Context, network, address string, c syscall.RawConn) error {
|
||||
return sl.ListenConfig.Control(network, address, c)
|
||||
}
|
||||
}
|
||||
sockladdr := sockaddrinterfaceinstance
|
||||
*(**net.UDPAddr)(unsafe.Add(unsafe.Pointer(&sockladdr), unsafe.Sizeof(uintptr(0)))) = laddr
|
||||
fd, err := internetSocket(ctx, sl.network, sockladdr, nil, syscall.SOCK_DGRAM, IPPROTO_UDPLITE, "listen", ctrlCtxFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newUDPConn(fd), nil
|
||||
}
|
||||
|
||||
func listenUDPLite(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
if laddr == nil {
|
||||
laddr = &net.UDPAddr{}
|
||||
}
|
||||
sl := &sysListener{network: network, address: laddr.String()}
|
||||
conn, err := sl.listenUDP(context.Background(), laddr)
|
||||
if err != nil {
|
||||
var laddrgeneral net.Addr
|
||||
if laddr != nil {
|
||||
laddrgeneral = laddr
|
||||
}
|
||||
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddrgeneral, Err: err}
|
||||
}
|
||||
rc, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
var errsys error
|
||||
err = rc.Control(func(fd uintptr) {
|
||||
errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_SEND_CSCOV, head.PacketHeadLen)
|
||||
if errsys != nil {
|
||||
return
|
||||
}
|
||||
errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_RECV_CSCOV, head.PacketHeadLen)
|
||||
})
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if errsys != nil {
|
||||
_ = conn.Close()
|
||||
return nil, errsys
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
1
gold/p2p/udplite/stub.go
Normal file
1
gold/p2p/udplite/stub.go
Normal file
@@ -0,0 +1 @@
|
||||
package udplite
|
||||
64
gold/p2p/udplite/udp.go
Normal file
64
gold/p2p/udplite/udp.go
Normal file
@@ -0,0 +1,64 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint net.UDPAddr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*net.UDPAddr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return "udplite"
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
udpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
udpep1 := ep
|
||||
return udpep1.IP.Equal(udpep2.IP) && udpep1.Port == udpep2.Port && udpep1.Zone == udpep2.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := listenUDPLite((*net.UDPAddr)(ep).Network(), (*net.UDPAddr)(ep))
|
||||
return (*Conn)(conn), err
|
||||
}
|
||||
|
||||
type Conn net.UDPConn
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return (*net.UDPConn)(conn).Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return (*net.UDPConn)(conn).LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint((*net.UDPConn)(conn).LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
udpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return (*net.UDPConn)(conn).WriteTo(b, (*net.UDPAddr)(udpep))
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package helper
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsExist 文件/路径存在
|
||||
func IsExist(path string) bool {
|
||||
@@ -13,3 +17,21 @@ func IsNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err != nil && os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// FolderName 本文件所在最下级文件夹名
|
||||
func FolderName() string {
|
||||
_, file, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
return "<unk>"
|
||||
}
|
||||
i := strings.LastIndex(file, "/")
|
||||
if i <= 0 {
|
||||
return file
|
||||
}
|
||||
file = file[:i]
|
||||
i = strings.LastIndex(file, "/")
|
||||
if i <= 0 || i+1 >= len(file) {
|
||||
return file
|
||||
}
|
||||
return file[i+1:]
|
||||
}
|
||||
|
||||
@@ -13,6 +13,19 @@ var bufferPool = sync.Pool{
|
||||
},
|
||||
}
|
||||
|
||||
func MakeBytes(sz int) []byte {
|
||||
w := SelectWriter()
|
||||
b := w.Bytes()
|
||||
if cap(b) >= sz {
|
||||
return b[:sz]
|
||||
}
|
||||
return make([]byte, sz)
|
||||
}
|
||||
|
||||
func PutBytes(b []byte) {
|
||||
PutWriter((*Writer)(bytes.NewBuffer(b)))
|
||||
}
|
||||
|
||||
// SelectWriter 从池中取出一个 Writer
|
||||
func SelectWriter() *Writer {
|
||||
// 因为 bufferPool 定义有 New 函数
|
||||
|
||||
30
lower/nic.go
30
lower/nic.go
@@ -2,8 +2,10 @@ package lower
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/fumiama/water"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -17,29 +19,35 @@ type NICIO interface {
|
||||
|
||||
// NIC 虚拟网卡
|
||||
type NIC struct {
|
||||
ifce *water.Interface
|
||||
ip string
|
||||
subnet string
|
||||
mtu string
|
||||
cidrs []string
|
||||
ifce *water.Interface
|
||||
ip net.IP
|
||||
subnet *net.IPNet
|
||||
rawipnet string
|
||||
mtu string
|
||||
cidrs []string
|
||||
}
|
||||
|
||||
// NewNIC 新建 TUN 网络接口卡
|
||||
// 网卡地址为 ip, 所属子网为 subnet
|
||||
// 以本网卡为下一跳的所有子网为 cidrs
|
||||
// cidrs 不包括本网卡 subnet
|
||||
func NewNIC(ip, subnet, mtu string, cidrs ...string) NICIO {
|
||||
func NewNIC(ip net.IP, subnet *net.IPNet, mtu string, cidrs ...string) NICIO {
|
||||
ifce, err := water.New(water.Config{DeviceType: water.TUN})
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
subn, bitsn := subnet.Mask.Size()
|
||||
if bitsn != 32 {
|
||||
panic("mask len " + strconv.Itoa(bitsn) + " is not supported")
|
||||
}
|
||||
n := &NIC{
|
||||
ifce: ifce,
|
||||
ip: ip,
|
||||
subnet: subnet,
|
||||
mtu: mtu,
|
||||
cidrs: cidrs,
|
||||
ifce: ifce,
|
||||
ip: ip,
|
||||
subnet: subnet,
|
||||
rawipnet: ip.String() + "/" + strconv.Itoa(subn),
|
||||
mtu: mtu,
|
||||
cidrs: cidrs,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -3,17 +3,24 @@
|
||||
|
||||
package lower
|
||||
|
||||
import "net"
|
||||
|
||||
func (n *NIC) Up() {
|
||||
execute("ifconfig", n.ifce.Name(), "mtu", n.mtu) // max: 9159
|
||||
execute("ifconfig", n.ifce.Name(), "inet", n.ip, n.ip, "up")
|
||||
execute("route", "add", n.subnet, "-interface", n.ifce.Name())
|
||||
execute(
|
||||
"ifconfig", n.ifce.Name(),
|
||||
"inet", n.ip.String(), n.ip.String(),
|
||||
"netmask", (net.IP)(n.subnet.Mask).String(),
|
||||
"up",
|
||||
)
|
||||
execute("route", "add", n.subnet.String(), "-interface", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("route", "add", c, "-interface", n.ifce.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
execute("route", "delete", n.subnet, "-interface", n.ifce.Name())
|
||||
execute("route", "delete", n.subnet.String(), "-interface", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("route", "delete", c, "-interface", n.ifce.Name())
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@ package lower
|
||||
|
||||
func (n *NIC) Up() {
|
||||
execute("/sbin/ip", "link", "set", "dev", n.ifce.Name(), "mtu", n.mtu)
|
||||
execute("/sbin/ip", "addr", "add", n.ip, "dev", n.ifce.Name())
|
||||
execute("/sbin/ip", "addr", "add", n.rawipnet, "dev", n.ifce.Name())
|
||||
execute("/sbin/ip", "link", "set", "dev", n.ifce.Name(), "up")
|
||||
execute("/sbin/ip", "route", "add", n.subnet, "dev", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("/sbin/ip", "route", "add", c, "dev", n.ifce.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
execute("/sbin/ip", "route", "del", n.subnet, "dev", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("/sbin/ip", "route", "del", c, "dev", n.ifce.Name())
|
||||
}
|
||||
|
||||
@@ -6,19 +6,14 @@ package lower
|
||||
import "net"
|
||||
|
||||
func (n *NIC) Up() {
|
||||
// execute("netsh", "interface", "set", "interface", n.ifce.Name(), "enabled")
|
||||
_, ipn, err := net.ParseCIDR(n.subnet)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
execute("cmd", "/c", "netsh interface ip set address name=\""+n.ifce.Name()+"\" source=static addr=\""+n.ip+"\" mask=\""+(net.IP)(ipn.Mask).String()+"\" gateway=none")
|
||||
execute("cmd", "/c", "netsh interface ip set address name=\""+n.ifce.Name()+"\" source=static addr=\""+n.ip.String()+"\" mask=\""+(net.IP)(n.subnet.Mask).String()+"\" gateway=none")
|
||||
execute("cmd", "/c", "netsh interface ipv4 set subinterface \""+n.ifce.Name()+"\" mtu="+n.mtu)
|
||||
for _, c := range n.cidrs {
|
||||
ip, cidr, err := net.ParseCIDR(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
execute("cmd", "/c", "route ADD "+ip.String()+" MASK "+(net.IP)(cidr.Mask).String()+" "+n.ip)
|
||||
execute("cmd", "/c", "route ADD "+ip.String()+" MASK "+(net.IP)(cidr.Mask).String()+" "+n.ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
_ "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/udp" // support udp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
)
|
||||
@@ -68,11 +73,11 @@ func (s *Tunnel) Read(p []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
defer pkt.Put()
|
||||
if len(pkt.Data) < 4 {
|
||||
logrus.Warnln("[tunnel] unexpected packet data len", len(pkt.Data), "content", pkt.Data)
|
||||
if pkt.BodyLen() < 4 {
|
||||
logrus.Warnln("[tunnel] unexpected packet data len", pkt.BodyLen(), "content", hex.EncodeToString(pkt.Body()))
|
||||
return 0, io.EOF
|
||||
}
|
||||
d = pkt.Data[4:]
|
||||
d = pkt.Body()[4:]
|
||||
}
|
||||
if d != nil {
|
||||
if len(p) >= len(d) {
|
||||
@@ -109,7 +114,7 @@ func (s *Tunnel) handleWrite() {
|
||||
}
|
||||
logrus.Debugln("[tunnel] writing", len(b), "bytes...")
|
||||
for len(b) > int(s.mtu)-4 {
|
||||
logrus.Infoln("[tunnel] seq", seq, "split buffer")
|
||||
logrus.Debugln("[tunnel] seq", seq, "split buffer")
|
||||
binary.LittleEndian.PutUint32(buf[:4], seq)
|
||||
seq++
|
||||
copy(buf[4:], b[:s.mtu-4])
|
||||
@@ -155,12 +160,12 @@ func (s *Tunnel) handleRead() {
|
||||
}
|
||||
end := 64
|
||||
endl := "..."
|
||||
if len(p.Data) < 64 {
|
||||
end = len(p.Data)
|
||||
if p.BodyLen() < 64 {
|
||||
end = p.BodyLen()
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[tunnel] read recv", hex.EncodeToString(p.Data[:end]), endl)
|
||||
recvseq := binary.LittleEndian.Uint32(p.Data[:4])
|
||||
logrus.Debugln("[tunnel] read recv", hex.EncodeToString(p.Body()[:end]), endl)
|
||||
recvseq := binary.LittleEndian.Uint32(p.Body()[:4])
|
||||
if recvseq == seq {
|
||||
logrus.Debugln("[tunnel] dispatch seq", seq)
|
||||
seq++
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
)
|
||||
|
||||
func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
func testTunnel(t *testing.T, nw string, isplain bool, pshk *[32]byte, mtu uint16) {
|
||||
selfpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -30,23 +31,37 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
||||
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||
|
||||
epm := "127.0.0.1"
|
||||
if nw != "ip" {
|
||||
epm += ":0"
|
||||
}
|
||||
// under macos you need to run
|
||||
//
|
||||
// sudo ifconfig lo0 alias 127.0.0.2
|
||||
epp := "127.0.0.2"
|
||||
if nw != "ip" {
|
||||
epp += ":0"
|
||||
}
|
||||
|
||||
m := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.2/32",
|
||||
MyEndpoint: "127.0.0.1:0",
|
||||
MyEndpoint: epm,
|
||||
Network: nw,
|
||||
PrivateKey: selfpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: 4096,
|
||||
MTU: mtu,
|
||||
})
|
||||
defer m.Close()
|
||||
|
||||
p := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.3/32",
|
||||
MyEndpoint: "127.0.0.1:0",
|
||||
MyEndpoint: epp,
|
||||
Network: nw,
|
||||
PrivateKey: peerpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: 4096,
|
||||
MTU: mtu,
|
||||
})
|
||||
defer p.Close()
|
||||
|
||||
@@ -63,8 +78,8 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
AllowedIPs: []string{"192.168.1.3/32"},
|
||||
PubicKey: ppp,
|
||||
PresharedKey: pshk,
|
||||
MTU: 4096,
|
||||
MTURandomRange: 1024,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
p.AddPeer(&link.PeerConfig{
|
||||
@@ -73,8 +88,8 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
AllowedIPs: []string{"192.168.1.2/32"},
|
||||
PubicKey: spp,
|
||||
PresharedKey: pshk,
|
||||
MTU: 4096,
|
||||
MTURandomRange: 1024,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
tunnme, err := Create(&m, "192.168.1.3")
|
||||
@@ -146,20 +161,138 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnel(t *testing.T) {
|
||||
func TestTunnelUDP(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, true, nil) // test plain text
|
||||
testTunnel(t, "udp", true, nil, 4096) // test plain text
|
||||
|
||||
testTunnel(t, false, nil) // test normal
|
||||
testTunnel(t, "udp", false, nil, 4096) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, false, &buf) // test preshared
|
||||
testTunnel(t, "udp", false, &buf, 4096) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelUDPSmallMTU(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "udp", true, nil, 1024) // test plain text
|
||||
|
||||
testTunnel(t, "udp", false, nil, 1024) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "udp", false, &buf, 1024) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelUDPLite(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return
|
||||
}
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "udplite", true, nil, 4096) // test plain text
|
||||
|
||||
testTunnel(t, "udplite", false, nil, 4096) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "udplite", false, &buf, 4096) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelUDPLiteSmallMTU(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return
|
||||
}
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "udplite", true, nil, 1024) // test plain text
|
||||
|
||||
testTunnel(t, "udplite", false, nil, 1024) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "udplite", false, &buf, 1024) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelTCP(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "tcp", true, nil, 4096) // test plain text
|
||||
|
||||
testTunnel(t, "tcp", false, nil, 4096) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "tcp", false, &buf, 4096) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelTCPSmallMTU(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "tcp", true, nil, 1024) // test plain text
|
||||
|
||||
testTunnel(t, "tcp", false, nil, 1024) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "tcp", false, &buf, 1024) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelIP(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "ip", true, nil, 4096) // test plain text
|
||||
|
||||
testTunnel(t, "ip", false, nil, 4096) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "ip", false, &buf, 4096) // test preshared
|
||||
}
|
||||
|
||||
func TestTunnelIPSmallMTU(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, "ip", true, nil, 1024) // test plain text
|
||||
|
||||
testTunnel(t, "ip", false, nil, 1024) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, "ip", false, &buf, 1024) // test preshared
|
||||
}
|
||||
|
||||
// logFormat specialize for go-cqhttp
|
||||
@@ -169,8 +302,7 @@ type logFormat struct {
|
||||
|
||||
// Format implements logrus.Formatter
|
||||
func (f logFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf := helper.SelectWriter()
|
||||
defer helper.PutWriter(buf)
|
||||
buf := helper.SelectWriter() // this writer will not be put back
|
||||
|
||||
buf.WriteByte('[')
|
||||
if f.enableColor {
|
||||
@@ -184,9 +316,7 @@ func (f logFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf.WriteString(entry.Message)
|
||||
buf.WriteString("\n")
|
||||
|
||||
ret := make([]byte, len(buf.Bytes()))
|
||||
copy(ret, buf.Bytes()) // copy buffer
|
||||
return ret, nil
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -9,6 +9,11 @@ import (
|
||||
curve "github.com/fumiama/go-x25519"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
_ "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/udp" // support udp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
@@ -34,7 +39,7 @@ func NewWireGold(c *config.Config) (wg WG, err error) {
|
||||
}
|
||||
n := copy(wg.key[:], base14.Decode(k))
|
||||
if n != 32 {
|
||||
err = errors.New("private key length is not 32")
|
||||
err = errors.New("private key length != 32, got " + strconv.Itoa(n))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,6 +74,10 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
myip := net.ParseIP(wg.c.IP)
|
||||
if myip == nil {
|
||||
panic("invalid ip " + wg.c.IP)
|
||||
}
|
||||
for _, p := range wg.c.Peers {
|
||||
for _, ip := range p.AllowedIPs {
|
||||
if len(ip) == 0 || ip[0] == 'x' {
|
||||
@@ -91,10 +100,11 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
}
|
||||
|
||||
wg.me = link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: wg.c.IP + "/32",
|
||||
MyIPwithMask: myip.String() + "/32",
|
||||
MyEndpoint: wg.c.EndPoint,
|
||||
Network: wg.c.Network,
|
||||
PrivateKey: &wg.key,
|
||||
NIC: lower.NewNIC(wg.c.IP, wg.c.SubNet, strconv.FormatInt(wg.c.MTU, 10), cidrs...),
|
||||
NIC: lower.NewNIC(myip, mysubnet, strconv.FormatInt(wg.c.MTU, 10), cidrs...),
|
||||
SrcPort: srcport,
|
||||
DstPort: dstport,
|
||||
MTU: uint16(wg.c.MTU),
|
||||
|
||||
Reference in New Issue
Block a user