From 6ae8e88bd12d08c591bad966089d6588bfcb3451 Mon Sep 17 00:00:00 2001 From: fumiama Date: Tue, 28 Dec 2021 21:50:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=BB=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- config/cfg.go | 41 ++++++++ go.mod | 3 + go.sum | 39 ++++++- gold/link/crypto.go | 10 +- gold/link/link.go | 6 +- gold/link/listen.go | 12 ++- gold/link/peer.go | 11 +- helper/data.go | 32 ++++++ helper/file.go | 15 +++ lower/nic.go | 109 ++++++++++++++++++++ lower/tun_darwin.go | 17 ++++ lower/tun_linux.go | 18 ++++ lower/tun_stub.go | 16 +++ lower/tun_windows.go | 18 ++++ main.go | 145 +++++++++++++++++++++++++++ upper/data.go | 10 +- upper/services/tunnel/tunnel_test.go | 8 +- 18 files changed, 499 insertions(+), 14 deletions(-) create mode 100644 config/cfg.go create mode 100644 helper/data.go create mode 100644 helper/file.go create mode 100644 lower/nic.go create mode 100644 lower/tun_darwin.go create mode 100644 lower/tun_linux.go create mode 100644 lower/tun_stub.go create mode 100644 lower/tun_windows.go diff --git a/.gitignore b/.gitignore index 25eef8e..3f4dd52 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ -.DS_Store \ No newline at end of file +.DS_Store +config.yaml diff --git a/config/cfg.go b/config/cfg.go new file mode 100644 index 0000000..117714e --- /dev/null +++ b/config/cfg.go @@ -0,0 +1,41 @@ +package config + +import ( + "bytes" + "log" + "os" + + "gopkg.in/yaml.v3" +) + +// Config WireGold 配置文件 +type Config struct { + IP string `yaml:"IP"` + SubNet string `yaml:"SubNet"` + PrivateKey string `yaml:"PrivateKey"` + EndPoint string `yaml:"EndPoint"` + Peers []Peer `yaml:"Peers"` +} + +// Peer 对端信息 +type Peer struct { + IP string `yaml:"IP"` + SubNet string `yaml:"SubNet"` + PublicKey string `yaml:"PublicKey"` + EndPoint string `yaml:"EndPoint"` + AllowedIPs []string `yaml:"AllowedIPs"` + KeepAliveSeconds int64 `yaml:"KeepAliveSeconds"` + AllowTrans bool `yaml:"AllowTrans"` +} + +func Parse(path string) (c Config) { + file, err := os.ReadFile(path) + if err != nil { + log.Fatal("open config file failed:", err) + } + err = yaml.NewDecoder(bytes.NewReader(file)).Decode(&c) + if err != nil { + log.Fatal("invalid config file:", err) + } + return +} diff --git a/go.mod b/go.mod index b579011..1d8c895 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module github.com/fumiama/WireGold go 1.16 require ( + github.com/fumiama/go-base16384 v1.2.1 github.com/fumiama/go-x25519 v1.0.0 github.com/fumiama/gofastTEA v0.0.6 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/sirupsen/logrus v1.8.1 + github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index c8ecc99..8b67e9d 100644 --- a/go.sum +++ b/go.sum @@ -3,43 +3,80 @@ github.com/Mrs4s/MiraiGo v0.0.0-20211120033824-43b23f4e6fcb/go.mod h1:imVKbfKqqe 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= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fumiama/go-base16384 v1.2.1 h1:6OGprW8g/95m2ocmryHi8mipZ7bx9StFMZDKEqLvMiA= +github.com/fumiama/go-base16384 v1.2.1/go.mod h1:1HTC0QFL7BjS0DuO5Qm+fBYKQkHqmAapLbRpCxrhPXQ= github.com/fumiama/go-x25519 v1.0.0 h1:hiGg9EhseVmGCc8T1jECVkj8Keu/aJ1ZK05RM8Vuavo= github.com/fumiama/go-x25519 v1.0.0/go.mod h1:8VOhfyGZzw4IUs4nCjQFqW9cA3V/QpSCtP3fo2dLNg4= github.com/fumiama/gofastTEA v0.0.6 h1:Yni3MXDbJVa/c4CecgdZDgCJK+fLdvGph+OBqY2mtiI= github.com/fumiama/gofastTEA v0.0.6/go.mod h1:+sBZ05nCA2skZkursHNvyr8kULlEetrYTM2y5kA4rQc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= +github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/wdvxdr1123/ZeroBot v1.3.2 h1:EFZNb3awNbwxRtmDkWv3PH6Z9rUV6ZLFa3hBmRMRRCA= +github.com/wdvxdr1123/ZeroBot v1.3.2/go.mod h1:i2DIqQjtjE+3gvVi9r9sc+QpNaUuyTXx/HNXXayIpwI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gold/link/crypto.go b/gold/link/crypto.go index 6394d71..614626f 100644 --- a/gold/link/crypto.go +++ b/gold/link/crypto.go @@ -6,6 +6,8 @@ import ( "unsafe" tea "github.com/fumiama/gofastTEA" + + "github.com/fumiama/WireGold/gold/head" ) // Me 是本机的抽象 @@ -29,10 +31,12 @@ type Me struct { myconn *net.UDPConn // 本机路由表 router *Router + // 不分目的 link 的接收队列 + pipe chan *head.Packet } // NewMe 设置本机参数 -func NewMe(privateKey *[32]byte, myipwithmask string, myEndpoint string) (m Me) { +func NewMe(privateKey *[32]byte, myipwithmask string, myEndpoint string, nopipeinlink bool) (m Me) { m.privKey = *privateKey var err error m.myend, err = net.ResolveUDPAddr("udp", myEndpoint) @@ -55,6 +59,10 @@ func NewMe(privateKey *[32]byte, myipwithmask string, myEndpoint string) (m Me) table: make(map[string]*Link, 16), } m.router.SetDefault(nil) + if nopipeinlink { + m.pipe = make(chan *head.Packet, 32) + } + m.AddPeer(m.me.String(), nil, "127.0.0.1:56789", []string{myipwithmask, "127.0.0.0/8"}, 0, false, nopipeinlink) return } diff --git a/gold/link/link.go b/gold/link/link.go index 650dd99..1dc5648 100644 --- a/gold/link/link.go +++ b/gold/link/link.go @@ -56,10 +56,14 @@ func (m *Me) Connect(peer string) (*Link, error) { // Close 关闭到 peer 的连接 func (l *Link) Close() { + l.status = LINK_STATUS_DOWN +} + +// Destroy 从 connections 移除 peer +func (l *Link) Destroy() { l.me.connmapmu.Lock() delete(l.me.connections, l.peerip.String()) l.me.connmapmu.Unlock() - l.status = LINK_STATUS_DOWN } // Read 从 peer 收包 diff --git a/gold/link/listen.go b/gold/link/listen.go index a4290f6..58e096f 100644 --- a/gold/link/listen.go +++ b/gold/link/listen.go @@ -61,7 +61,11 @@ func (m *Me) listen() (conn *net.UDPConn, err error) { p.onQuery(&packet) case head.ProtoData: logrus.Infoln("[link] deliver to", p.peerip) - p.pipe <- &packet + if p.pipe != nil { + p.pipe <- &packet + } else { + m.pipe <- &packet + } default: break } @@ -84,6 +88,12 @@ func (m *Me) listen() (conn *net.UDPConn, err error) { return } +// Read 接收所有发送给本机的报文 +// 需要开启 nopipe +func (m *Me) Read() *head.Packet { + return <-m.pipe +} + // 从 conn 读取 sz 字节数据 func readAll(conn *net.UDPConn, sz int) ([]byte, error) { i := 0 diff --git a/gold/link/peer.go b/gold/link/peer.go index cb6c8e9..6eafa12 100644 --- a/gold/link/peer.go +++ b/gold/link/peer.go @@ -4,13 +4,13 @@ import ( "net" "unsafe" - curve "github.com/fumiama/go-x25519" - "github.com/fumiama/WireGold/gold/head" + curve "github.com/fumiama/go-x25519" + "github.com/sirupsen/logrus" ) // AddPeer 添加一个 peer -func (m *Me) AddPeer(peerip string, pubicKey *[32]byte, endPoint string, allowedIPs []string, keepAlive int64, allowTrans bool) (l *Link) { +func (m *Me) AddPeer(peerip string, pubicKey *[32]byte, endPoint string, allowedIPs []string, keepAlive int64, allowTrans, nopipe bool) (l *Link) { peerip = net.ParseIP(peerip).String() var ok bool l, ok = m.IsInPeer(peerip) @@ -20,11 +20,13 @@ func (m *Me) AddPeer(peerip string, pubicKey *[32]byte, endPoint string, allowed l = &Link{ pubk: pubicKey, keepalive: keepAlive, - pipe: make(chan *head.Packet, 32), peerip: net.ParseIP(peerip), allowtrans: allowTrans, me: m, } + if !nopipe { + l.pipe = make(chan *head.Packet, 32) + } if pubicKey != nil { c := curve.Get(m.privKey[:]) k, err := c.Shared(pubicKey) @@ -53,6 +55,7 @@ func (m *Me) AddPeer(peerip string, pubicKey *[32]byte, endPoint string, allowed l.me.connmapmu.Lock() l.me.connections[peerip] = l l.me.connmapmu.Unlock() + logrus.Infoln("[peer] add peer:", peerip, "allow:", allowedIPs) return } diff --git a/helper/data.go b/helper/data.go new file mode 100644 index 0000000..874b10a --- /dev/null +++ b/helper/data.go @@ -0,0 +1,32 @@ +package helper + +import ( + "unsafe" +) + +// slice is the runtime representation of a slice. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type slice struct { + data unsafe.Pointer + len int + cap int +} + +// BytesToString 没有内存开销的转换 +func BytesToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// StringToBytes 没有内存开销的转换 +func StringToBytes(s string) (b []byte) { + bh := (*slice)(unsafe.Pointer(&b)) + sh := (*slice)(unsafe.Pointer(&s)) // 不要访问 sh.cap + bh.data = sh.data + bh.len = sh.len + bh.cap = sh.len + return b +} diff --git a/helper/file.go b/helper/file.go new file mode 100644 index 0000000..a7731b2 --- /dev/null +++ b/helper/file.go @@ -0,0 +1,15 @@ +package helper + +import "os" + +// IsExist 文件/路径存在 +func IsExist(path string) bool { + _, err := os.Stat(path) + return err == nil || os.IsExist(err) +} + +// IsNotExist 文件/路径不存在 +func IsNotExist(path string) bool { + _, err := os.Stat(path) + return err != nil && os.IsNotExist(err) +} diff --git a/lower/nic.go b/lower/nic.go new file mode 100644 index 0000000..3d713d5 --- /dev/null +++ b/lower/nic.go @@ -0,0 +1,109 @@ +package lower + +import ( + "os" + "os/exec" + + "github.com/sirupsen/logrus" + "github.com/songgao/water" + "github.com/songgao/water/waterutil" + + "github.com/fumiama/WireGold/gold/head" + "github.com/fumiama/WireGold/gold/link" +) + +// NIC 虚拟网卡 +type NIC struct { + ifce *water.Interface + ip string + subnet string + hasstart bool +} + +// NewNIC 新建 TUN 网络接口卡 +// 网卡地址为 ip, 所属子网为 subnet +func NewNIC(ip, subnet string) (n *NIC) { + ifce, err := water.New(water.Config{DeviceType: water.TUN}) + if err != nil { + panic(err) + } + n = &NIC{ + ifce: ifce, + ip: ip, + subnet: subnet, + } + n.prepare() + return +} + +// Start 开始处理网卡消息,阻塞 +func (nc *NIC) Start(m *link.Me) { + if nc.hasstart { + return + } + nc.hasstart = true + go func() { // 接收到 NIC + for nc.hasstart { + packet := m.Read() + logrus.Infoln("[lower] recv", len(packet.Data), "bytes packet") + if !waterutil.IsIPv4(packet.Data) { + logrus.Warnln("[lower] recv recv non-ipv4 packet") + continue + } + _, err := nc.ifce.Write(packet.Data) + if err != nil { + logrus.Errorln("[lower] recv write to nic err:", err) + break + } + } + }() + buf := make([]byte, 4096) + for nc.hasstart { // 从 NIC 发送 + packet := buf + n, err := nc.ifce.Read(packet) + if err != nil { + logrus.Errorln("[lower] send read from nic err:", err) + break + } + if n == 0 { + continue + } + packet = packet[:n] + if !waterutil.IsIPv4(packet) { + logrus.Warnln("[lower] send recv non-ipv4 packet") + continue + } + logrus.Infoln("[lower] send", n, "bytes packet") + dst := waterutil.IPv4Destination(packet) + srcport := waterutil.IPv4SourcePort(packet) + dstport := waterutil.IPv4DestinationPort(packet) + lnk, err := m.Connect(dst.String()) + if err != nil { + logrus.Errorln("[lower] connect to peer err:", err) + continue + } + lnk.Write(head.NewPacket(head.ProtoData, srcport, dstport, packet)) + } +} + +// Stop 停止处理 +func (n *NIC) Stop() { + n.hasstart = false +} + +// Destroy 关闭网卡 +func (n *NIC) Destroy() error { + return n.ifce.Close() +} + +func execute(c string, args ...string) { + logrus.Printf("[lower] exec cmd: %v %v:", c, args) + cmd := exec.Command(c, args...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + err := cmd.Run() + if err != nil { + logrus.Panicln("[lower] failed to exec cmd:", err) + } +} diff --git a/lower/tun_darwin.go b/lower/tun_darwin.go new file mode 100644 index 0000000..1d2e3fd --- /dev/null +++ b/lower/tun_darwin.go @@ -0,0 +1,17 @@ +//go:build darwin +// +build darwin + +package lower + +func (n *NIC) prepare() { + execute("ifconfig", n.ifce.Name(), "inet", n.ip, n.ip, "up") + execute("route", "add", n.subnet, "-interface", n.ifce.Name()) +} + +func (n *NIC) Up() { + execute("ifconfig", n.ifce.Name(), "inet", n.ip, n.ip, "up") +} + +func (n *NIC) Down() { + execute("ifconfig", n.ifce.Name(), "down") +} diff --git a/lower/tun_linux.go b/lower/tun_linux.go new file mode 100644 index 0000000..a32f9b2 --- /dev/null +++ b/lower/tun_linux.go @@ -0,0 +1,18 @@ +//go:build linux +// +build linux + +package lower + +func (n *NIC) prepare() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "mtu", "1500") + execute("/sbin/ip", "addr", "add", ip, "dev", ifcename) + execute("/sbin/ip", "route", "add", subnet, "dev", ifcename) +} + +func (n *NIC) Up() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "up") +} + +func (n *NIC) Down() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "down") +} diff --git a/lower/tun_stub.go b/lower/tun_stub.go new file mode 100644 index 0000000..4f523da --- /dev/null +++ b/lower/tun_stub.go @@ -0,0 +1,16 @@ +//go:build !darwin && !linux && !windows +// +build !darwin,!linux,!windows + +package lower + +func (n *NIC) prepare() { + panic("not support this os now") +} + +func (n *NIC) Up() { + panic("not support this os now") +} + +func (n *NIC) Down() { + panic("not support this os now") +} diff --git a/lower/tun_windows.go b/lower/tun_windows.go new file mode 100644 index 0000000..d90efa2 --- /dev/null +++ b/lower/tun_windows.go @@ -0,0 +1,18 @@ +//go:build windows +// +build windows + +package lower + +func (n *NIC) prepare() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "mtu", "1500") + execute("/sbin/ip", "addr", "add", ip, "dev", ifcename) + execute("/sbin/ip", "route", "add", subnet, "dev", ifcename) +} + +func (n *NIC) Up() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "up") +} + +func (n *NIC) Down() { + execute("/sbin/ip", "link", "set", "dev", ifcename, "down") +} diff --git a/main.go b/main.go index 06ab7d0..9341ef5 100644 --- a/main.go +++ b/main.go @@ -1 +1,146 @@ package main + +import ( + "flag" + "fmt" + "os" + + base14 "github.com/fumiama/go-base16384" + curve "github.com/fumiama/go-x25519" + + "github.com/fumiama/WireGold/config" + "github.com/fumiama/WireGold/gold/link" + "github.com/fumiama/WireGold/helper" + "github.com/fumiama/WireGold/lower" +) + +const suffix32 = "㴄" + +func main() { + help := flag.Bool("h", false, "display this help") + gen := flag.Bool("g", false, "generate key pair") + showp := flag.Bool("p", false, "show my publickey") + file := flag.String("c", "config.yaml", "specify conf file") + flag.Parse() + if *help { + displayHelp("") + } + if *gen { + k, err := curve.New(nil) + if err != nil { + panic(err) + } + pubk, err := base14.UTF16be2utf8(base14.Encode((*k.Public())[:])) + if err != nil { + panic(err) + } + prvk, err := base14.UTF16be2utf8(base14.Encode((*k.Private())[:])) + if err != nil { + panic(err) + } + fmt.Println("PublicKey:", helper.BytesToString(pubk[:57])) + fmt.Println("PrivateKey:", helper.BytesToString(prvk[:57])) + os.Exit(0) + } + if helper.IsNotExist(*file) { + f, err := os.Create(*file) + if err != nil { + panic(err) + } + var r string + fmt.Print("IP: ") + fmt.Scanln(&r) + if r == "" { + f.Close() + os.Remove(*file) + fmt.Println("nil ip") + return + } + f.WriteString("IP: " + r + "\n") + r = "" + + fmt.Print("SubNet: ") + fmt.Scanln(&r) + if r == "" { + f.Close() + os.Remove(*file) + fmt.Println("nil subnet") + return + } + f.WriteString("SubNet: " + r + "\n") + r = "" + + fmt.Print("PrivateKey: ") + fmt.Scanln(&r) + if r == "" { + f.Close() + os.Remove(*file) + fmt.Println("nil private key") + return + } + f.WriteString("PrivateKey: " + r + "\n") + r = "" + + fmt.Print("EndPoint: ") + fmt.Scanln(&r) + if r == "" { + f.Close() + os.Remove(*file) + fmt.Println("nil endpoint") + return + } + f.WriteString("EndPoint: " + r + "\n") + r = "" + + f.Close() + } + c := config.Parse(*file) + if c.IP == "" { + displayHelp("nil ip") + } + if c.SubNet == "" { + displayHelp("nil subnet") + } + if c.PrivateKey == "" { + displayHelp("nil private key") + } + if c.EndPoint == "" { + displayHelp("nil endpoint") + } + var key [32]byte + k, err := base14.UTF82utf16be(helper.StringToBytes(c.PrivateKey + suffix32)) + if err != nil { + panic(err) + } + n := copy(key[:], base14.Decode(k)) + if n != 32 { + displayHelp("private key length is not 32") + } + + if *showp { + c := curve.Get(key[:]) + pubk, err := base14.UTF16be2utf8(base14.Encode((*c.Public())[:])) + if err != nil { + panic(err) + } + fmt.Println("PublicKey:", helper.BytesToString(pubk[:57])) + os.Exit(0) + } + + nic := lower.NewNIC(c.IP, c.SubNet) + me := link.NewMe(&key, c.IP+"/32", c.EndPoint, true) + nic.Up() + defer func() { + nic.Stop() + nic.Down() + nic.Destroy() + }() + + nic.Start(&me) +} + +func displayHelp(hint string) { + fmt.Println(hint) + flag.Usage() + os.Exit(0) +} diff --git a/upper/data.go b/upper/data.go index 2d55ba6..27656cd 100644 --- a/upper/data.go +++ b/upper/data.go @@ -2,7 +2,15 @@ package upper import "io" +// 常用服务端口 +const ( + // ServiceNull 不在意端口号的服务 + ServiceNull = iota + // ServiceTunnel 管道通信服务 + ServiceTunnel +) + type Service interface { - Create(peer string, srcport uint16, destport uint16) (Service, error) + Create(peer string, srcport, destport, mtu uint16) (Service, error) io.ReadWriteCloser } diff --git a/upper/services/tunnel/tunnel_test.go b/upper/services/tunnel/tunnel_test.go index 223accf..b7b2536 100644 --- a/upper/services/tunnel/tunnel_test.go +++ b/upper/services/tunnel/tunnel_test.go @@ -27,10 +27,10 @@ func TestTunnel(t *testing.T) { t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:])) t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:])) - m := link.NewMe(selfpk.Private(), "192.168.1.2", "127.0.0.1:1236") - m.AddPeer("192.168.1.3", peerpk.Public(), "127.0.0.1:1237", []string{"192.168.1.3/32"}, 0, false) - p := link.NewMe(peerpk.Private(), "192.168.1.3", "127.0.0.1:1237") - p.AddPeer("192.168.1.2", selfpk.Public(), "127.0.0.1:1236", []string{"192.168.1.2/32"}, 0, false) + m := link.NewMe(selfpk.Private(), "192.168.1.2/32", "127.0.0.1:1236", false) + m.AddPeer("192.168.1.3", peerpk.Public(), "127.0.0.1:1237", []string{"192.168.1.3/32"}, 0, false, false) + p := link.NewMe(peerpk.Private(), "192.168.1.3/32", "127.0.0.1:1237", false) + p.AddPeer("192.168.1.2", selfpk.Public(), "127.0.0.1:1236", []string{"192.168.1.2/32"}, 0, false, false) tunnme, err := Create(&m, "192.168.1.3", 1, 1, 4096) if err != nil { t.Fatal(err)