diff --git a/gold/head/nat.go b/gold/head/nat.go index b3b608a..9f10424 100644 --- a/gold/head/nat.go +++ b/gold/head/nat.go @@ -1,7 +1,7 @@ package head -// map[peerip]endpoint +// Notify 是 map[peerip]endpoint type Notify map[string]string -// peerips array +// Query 是 peerips 组成的数组 type Query []string diff --git a/gold/head/packet.go b/gold/head/packet.go index 2ff906b..37e308b 100644 --- a/gold/head/packet.go +++ b/gold/head/packet.go @@ -2,17 +2,31 @@ package head import "encoding/json" +// Packet 是发送和接收的最小单位 type Packet struct { - DataSZ uint32 - Proto uint8 - TTL uint8 + // DataSZ len(Data) + DataSZ uint32 + // Proto 详见 head + Proto uint8 + // TTL is time to live + TTL uint8 + // SrcPort 源端口 SrcPort uint16 + // DstPort 目的端口 DstPort uint16 - Src string - Dst string - Data []byte + // Src 源 ip + Src string + // Dst 目的 ip + Dst string + // Hash 使用 BLAKE2 生成加密前 Packet 的摘要 + // 生成时 Hash 全 0 + // https://github.com/minio/blake2b-simd + Hash [32]byte + // Data 承载的数据 + Data []byte } +// NewPacket 生成一个新包 func NewPacket(proto uint8, srcPort uint16, dstPort uint16, data []byte) *Packet { return &Packet{ Proto: proto, @@ -23,10 +37,14 @@ func NewPacket(proto uint8, srcPort uint16, dstPort uint16, data []byte) *Packet } } +// UnMashal 将 data 的数据解码到自身 +// 同时通过 Hash 验证数据完整性 func (p *Packet) UnMashal(data []byte) error { return json.Unmarshal(data, p) } +// Mashal 将自身数据编码为 []byte +// 同时生成 Hash func (p *Packet) Mashal(src string, dst string) ([]byte, error) { p.DataSZ = uint32(len(p.Data)) p.Src = src diff --git a/gold/head/protos.go b/gold/head/protos.go index a9e121f..ce5d257 100644 --- a/gold/head/protos.go +++ b/gold/head/protos.go @@ -1,5 +1,6 @@ package head +// Proto 类型定义 const ( ProtoHello uint8 = iota ProtoNotify diff --git a/gold/link/crypto.go b/gold/link/crypto.go index 473367c..54a4575 100644 --- a/gold/link/crypto.go +++ b/gold/link/crypto.go @@ -3,11 +3,18 @@ package link import "net" var ( + // 本机私钥 + // 利用 Curve25519 生成 + // https://pkg.go.dev/golang.org/x/crypto/curve25519 + // https://www.zhihu.com/question/266758647 privKey [32]byte - me net.IP - myend *net.UDPAddr + // 本机虚拟 ip + me net.IP + // 本机 endpoint + myend *net.UDPAddr ) +// SetMyself 设置本机参数 func SetMyself(privateKey [32]byte, myIP string, myEndpoint string) { privKey = privateKey var err error @@ -22,10 +29,14 @@ func SetMyself(privateKey [32]byte, myIP string, myEndpoint string) { } } +// Encode 使用 ChaCha20-Poly1305 AEAD 对称加密加密 +// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 func (l *Link) Encode(b []byte) (eb []byte, err error) { return b, nil } +// Encode 使用 ChaCha20-Poly1305 AEAD 对称加密解密 +// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 func (l *Link) Decode(b []byte) (db []byte, err error) { return b, nil } diff --git a/gold/link/link.go b/gold/link/link.go index 1d1d404..3ad8200 100644 --- a/gold/link/link.go +++ b/gold/link/link.go @@ -9,16 +9,29 @@ import ( "github.com/sirupsen/logrus" ) +// Link 是本机到 peer 的连接抽象 type Link struct { - PubicKey [32]byte - EndPoint string - KeepAlive int64 - pipe chan *head.Packet - peerip net.IP - endpoint *net.UDPAddr - allowedips []*net.IPNet - hasKeepRuning bool - status int + // peer 的公钥 + pubk [32]byte + // peer 的公网 ip:port + pep string + // 决定本机是否定时向 peer 发送 hello 保持 NAT。 + // 以秒为单位,小于等于 0 不发送 + keepalive int64 + // 收到的包的队列 + pipe chan *head.Packet + // peer 的虚拟 ip + peerip net.IP + // peer 的公网 endpoint + endpoint *net.UDPAddr + // 本机允许接收/发送的 ip 网段 + allowedips []*net.IPNet + // 是否已经调用过 keepAlive + haskeepruning bool + // 是否允许转发 + allowtrans bool + // 连接的状态,详见下方 const + status int } const ( @@ -28,11 +41,15 @@ const ( ) var ( + // 本机活跃的所有连接 connections = make(map[string]*Link) - connmapmu sync.RWMutex - myconn *net.UDPConn + // 读写同步锁 + connmapmu sync.RWMutex + // 本机监听的 endpoint + myconn *net.UDPConn ) +// Connect 初始化与 peer 的连接 func Connect(peer string) (*Link, error) { p, ok := IsInPeer(net.ParseIP(peer).String()) if ok { @@ -42,6 +59,7 @@ func Connect(peer string) (*Link, error) { return nil, errors.New("peer not exist") } +// Close 关闭到 peer 的连接 func (l *Link) Close() { connmapmu.Lock() delete(connections, l.peerip.String()) @@ -49,10 +67,12 @@ func (l *Link) Close() { l.status = LINK_STATUS_DOWN } +// Read 从 peer 收包 func (l *Link) Read() *head.Packet { return <-l.pipe } +// Write 向 peer 发包 func (l *Link) Write(p *head.Packet) (n int, err error) { p.Data, err = l.Encode(p.Data) if err == nil { @@ -60,7 +80,7 @@ func (l *Link) Write(p *head.Packet) (n int, err error) { d, err = p.Mashal(me.String(), l.peerip.String()) logrus.Debugln("[link] write data", string(d)) if err == nil { - n, err = myconn.WriteToUDP(d, l.endpoint) + n, err = myconn.WriteToUDP(d, l.NextHop(l.peerip).endpoint) } } return diff --git a/gold/link/listen.go b/gold/link/listen.go index fe3aff2..34c3554 100644 --- a/gold/link/listen.go +++ b/gold/link/listen.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" ) +// 监听本机 endpoint func listen() (conn *net.UDPConn, err error) { conn, err = net.ListenUDP("udp", myend) if err == nil { @@ -30,41 +31,47 @@ func listen() (conn *net.UDPConn, err error) { p, ok := IsInPeer(packet.Src) logrus.Infoln("[link] recv from endpoint", addr, "src", packet.Src, "dst", packet.Dst) logrus.Debugln("[link] recv:", string(lbf)) - if p.EndPoint == "" || p.EndPoint != addr.String() { + if p.pep == "" || p.pep != addr.String() { logrus.Infoln("[link] set endpoint of peer", p.peerip, "to", addr.String()) p.endpoint = addr - p.EndPoint = addr.String() + p.pep = addr.String() } - if ok && p.Accept(net.IP(packet.Dst)) { - packet.Data, err = p.Decode(packet.Data) - if err == nil { - switch packet.Proto { - case head.ProtoHello: - switch p.status { - case LINK_STATUS_DOWN: - _, _ = p.Write(head.NewPacket(head.ProtoHello, 0, 0, nil)) - logrus.Infoln("[link] send hello ack packet") - p.status = LINK_STATUS_HALFUP - case LINK_STATUS_HALFUP: - p.status = LINK_STATUS_UP - case LINK_STATUS_UP: + if ok { + if p.IsToMe(net.ParseIP(packet.Dst)) { + packet.Data, err = p.Decode(packet.Data) + if err == nil { + switch packet.Proto { + case head.ProtoHello: + switch p.status { + case LINK_STATUS_DOWN: + _, _ = p.Write(head.NewPacket(head.ProtoHello, 0, 0, nil)) + logrus.Infoln("[link] send hello ack packet") + p.status = LINK_STATUS_HALFUP + case LINK_STATUS_HALFUP: + p.status = LINK_STATUS_UP + case LINK_STATUS_UP: + break + } + case head.ProtoNotify: + logrus.Infoln("[link] recv notify") + onNotify(&packet) + case head.ProtoQuery: + logrus.Infoln("[link] recv query") + onQuery(&packet) + case head.ProtoData: + logrus.Infoln("[link] deliver to", p.peerip) + p.pipe <- &packet + default: break } - case head.ProtoNotify: - logrus.Infoln("[link] recv notify") - onNotify(&packet) - case head.ProtoQuery: - logrus.Infoln("[link] recv query") - onQuery(&packet) - case head.ProtoData: - logrus.Infoln("[link] deliver to", p.peerip) - p.pipe <- &packet - default: - break } + } else if p.Accept(net.ParseIP(packet.Dst)) && p.allowtrans { + // 转发 + p.Write(&packet) + logrus.Infoln("[link] trans") } } else { - logrus.Infoln("[link] packet to", packet.Dst, "is refused") + logrus.Infoln("[link] packet to", packet.Dst, "is refused", "(me:", me, ")") } } } @@ -74,6 +81,7 @@ func listen() (conn *net.UDPConn, err error) { return } +// 从 conn 读取 sz 字节数据 func readAll(conn *net.UDPConn, sz int) ([]byte, error) { i := 0 n := 0 diff --git a/gold/link/nat.go b/gold/link/nat.go index aa72b83..590d15a 100644 --- a/gold/link/nat.go +++ b/gold/link/nat.go @@ -8,11 +8,12 @@ import ( "github.com/fumiama/WireGold/gold/head" ) +// 保持 NAT func (l *Link) keepAlive() { - if l.KeepAlive > 0 && !l.hasKeepRuning { - l.hasKeepRuning = true + if l.keepalive > 0 && !l.haskeepruning { + l.haskeepruning = true go func() { - t := time.NewTicker(time.Second * time.Duration(l.KeepAlive)) + t := time.NewTicker(time.Second * time.Duration(l.keepalive)) for range t.C { _, _ = l.Write(head.NewPacket(head.ProtoHello, 0, 0, nil)) logrus.Infoln("[link.nat] send keep alive packet") @@ -22,10 +23,12 @@ func (l *Link) keepAlive() { } } +// 收到询问包的处理函数 func onQuery(packet *head.Packet) { // TODO: 完成data解包与notify分发 } +// 收到通告包的处理函数 func onNotify(packet *head.Packet) { // TODO: 完成data解包与endpoint注册 } diff --git a/gold/link/peer.go b/gold/link/peer.go index 2a86686..7b59960 100644 --- a/gold/link/peer.go +++ b/gold/link/peer.go @@ -6,7 +6,8 @@ import ( "github.com/fumiama/WireGold/gold/head" ) -func AddPeer(peerip string, pubicKey [32]byte, endPoint string, allowedIPs []string, keepAlive int64) (l *Link) { +// AddPeer 添加一个 peer +func AddPeer(peerip string, pubicKey [32]byte, endPoint string, allowedIPs []string, keepAlive int64, allowTrans bool) (l *Link) { peerip = net.ParseIP(peerip).String() var ok bool l, ok = IsInPeer(peerip) @@ -14,17 +15,18 @@ func AddPeer(peerip string, pubicKey [32]byte, endPoint string, allowedIPs []str return } l = &Link{ - PubicKey: pubicKey, - KeepAlive: keepAlive, - pipe: make(chan *head.Packet, 32), - peerip: net.ParseIP(peerip), + pubk: pubicKey, + keepalive: keepAlive, + pipe: make(chan *head.Packet, 32), + peerip: net.ParseIP(peerip), + allowtrans: allowTrans, } if endPoint != "" { e, err := net.ResolveUDPAddr("udp", endPoint) if err != nil { panic(err) } - l.EndPoint = endPoint + l.pep = endPoint l.endpoint = e } if allowedIPs != nil { @@ -42,6 +44,7 @@ func AddPeer(peerip string, pubicKey [32]byte, endPoint string, allowedIPs []str return } +// IsInPeer 查找 peer 是否已经在册 func IsInPeer(peer string) (p *Link, ok bool) { connmapmu.RLock() p, ok = connections[peer] diff --git a/gold/link/router.go b/gold/link/router.go index 8de4e58..6328fbc 100644 --- a/gold/link/router.go +++ b/gold/link/router.go @@ -2,6 +2,7 @@ package link import "net" +// Accept 判断是否应当接受 ip 发来的包 func (l *Link) Accept(ip net.IP) bool { for _, cidr := range l.allowedips { if cidr.Contains(ip) { @@ -11,6 +12,12 @@ func (l *Link) Accept(ip net.IP) bool { return false } -func NextHop(ip net.IP) *Link { - return nil +// IsToMe 判断是否是发给自己的包 +func (l *Link) IsToMe(ip net.IP) bool { + return ip.Equal(me) +} + +// NextHop 得到前往 ip 的下一跳的 link +func (l *Link) NextHop(ip net.IP) *Link { + return l } diff --git a/upper/services/tunnel/tunnel_test.go b/upper/services/tunnel/tunnel_test.go index 540a796..8319559 100644 --- a/upper/services/tunnel/tunnel_test.go +++ b/upper/services/tunnel/tunnel_test.go @@ -10,7 +10,7 @@ import ( func TestTunnel(t *testing.T) { logrus.SetLevel(logrus.DebugLevel) link.SetMyself([32]byte{}, "192.168.1.2", "127.0.0.1:1236") - link.AddPeer("192.168.1.2", [32]byte{}, "127.0.0.1:1236", nil, 0) + link.AddPeer("192.168.1.2", [32]byte{}, "127.0.0.1:1236", nil, 0, false) tunn, err := Create("192.168.1.2", 1, 1) if err != nil { t.Error(err)