Implement WireGuard protocol as outbound (client) (#1344)

* implement WireGuard protocol for Outbound

* upload license

* fix build for openbsd & dragonfly os

* updated wireguard-go

* fix up

* switch to another wireguard fork

* fix

* switch to upstream

* open connection through internet.Dialer (#1)

* use internet.Dialer

* maybe better code

* fix

* real fix

Co-authored-by: nanoda0523 <nanoda0523@users.noreply.github.com>

* fix bugs & add ability to recover during connection reset on UDP over TCP parent protocols

* improve performance

improve performance

* dns lookup endpoint && remove unused code

* interface address fallback

* better code && add config test case

Co-authored-by: nanoda0523 <nanoda0523@users.noreply.github.com>
This commit is contained in:
nanoda0523 2022-11-22 09:05:54 +08:00 committed by GitHub
parent 691b2b1c73
commit e18b52a5df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1326 additions and 1 deletions

111
infra/conf/wireguard.go Normal file
View file

@ -0,0 +1,111 @@
package conf
import (
"encoding/base64"
"encoding/hex"
"github.com/golang/protobuf/proto"
"github.com/xtls/xray-core/proxy/wireguard"
)
type WireGuardPeerConfig struct {
PublicKey string `json:"publicKey"`
PreSharedKey string `json:"preSharedKey"`
Endpoint string `json:"endpoint"`
KeepAlive int `json:"keepAlive"`
AllowedIPs []string `json:"allowedIPs,omitempty"`
}
func (c *WireGuardPeerConfig) Build() (proto.Message, error) {
var err error
config := new(wireguard.PeerConfig)
config.PublicKey, err = parseWireGuardKey(c.PublicKey)
if err != nil {
return nil, err
}
if c.PreSharedKey != "" {
config.PreSharedKey, err = parseWireGuardKey(c.PreSharedKey)
if err != nil {
return nil, err
}
} else {
config.PreSharedKey = "0000000000000000000000000000000000000000000000000000000000000000"
}
config.Endpoint = c.Endpoint
// default 0
config.KeepAlive = int32(c.KeepAlive)
if c.AllowedIPs == nil {
config.AllowedIps = []string{"0.0.0.0/0", "::0/0"}
} else {
config.AllowedIps = c.AllowedIPs
}
return config, nil
}
type WireGuardConfig struct {
SecretKey string `json:"secretKey"`
Address []string `json:"address"`
Peers []*WireGuardPeerConfig `json:"peers"`
MTU int `json:"mtu"`
NumWorkers int `json:"workers"`
}
func (c *WireGuardConfig) Build() (proto.Message, error) {
config := new(wireguard.DeviceConfig)
var err error
config.SecretKey, err = parseWireGuardKey(c.SecretKey)
if err != nil {
return nil, err
}
if c.Address == nil {
// bogon ips
config.Endpoint = []string{"10.0.0.1", "fd59:7153:2388:b5fd:0000:0000:0000:0001"}
} else {
config.Endpoint = c.Address
}
if c.Peers != nil {
config.Peers = make([]*wireguard.PeerConfig, len(c.Peers))
for i, p := range c.Peers {
msg, err := p.Build()
if err != nil {
return nil, err
}
config.Peers[i] = msg.(*wireguard.PeerConfig)
}
}
if c.MTU == 0 {
config.Mtu = 1420
} else {
config.Mtu = int32(c.MTU)
}
// these a fallback code exists in github.com/nanoda0523/wireguard-go code,
// we don't need to process fallback manually
config.NumWorkers = int32(c.NumWorkers)
return config, nil
}
func parseWireGuardKey(str string) (string, error) {
if len(str) != 64 {
// may in base64 form
dat, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return "", err
}
if len(dat) != 32 {
return "", newError("key should be 32 bytes: " + str)
}
return hex.EncodeToString(dat), err
} else {
// already hex form
return str, nil
}
}

View file

@ -0,0 +1,49 @@
package conf_test
import (
"testing"
. "github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/wireguard"
)
func TestWireGuardOutbound(t *testing.T) {
creator := func() Buildable {
return new(WireGuardConfig)
}
runMultiTestCase(t, []TestCase{
{
Input: `{
"secretKey": "uJv5tZMDltsiYEn+kUwb0Ll/CXWhMkaSCWWhfPEZM3A=",
"address": ["10.1.1.1", "fd59:7153:2388:b5fd:0000:0000:1234:0001"],
"peers": [
{
"publicKey": "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
"endpoint": "127.0.0.1:1234"
}
],
"mtu": 1300,
"workers": 2
}`,
Parser: loadJSON(creator),
Output: &wireguard.DeviceConfig{
// key converted into hex form
SecretKey: "b89bf9b5930396db226049fe914c1bd0b97f0975a13246920965a17cf1193370",
Endpoint: []string{"10.1.1.1", "fd59:7153:2388:b5fd:0000:0000:1234:0001"},
Peers: []*wireguard.PeerConfig{
{
// also can read from hex form directly
PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
Endpoint: "127.0.0.1:1234",
KeepAlive: 0,
AllowedIps: []string{"0.0.0.0/0", "::0/0"},
},
},
Mtu: 1300,
NumWorkers: 2,
},
},
})
}

View file

@ -40,6 +40,7 @@ var (
"trojan": func() interface{} { return new(TrojanClientConfig) },
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
"dns": func() interface{} { return new(DNSOutboundConfig) },
"wireguard": func() interface{} { return new(WireGuardConfig) },
}, "protocol", "settings")
ctllog = log.New(os.Stderr, "xctl> ", 0)