feat: Add multi-peer support for wireguard outbound
This commit is contained in:
parent
ae5fafa885
commit
2c83e7297c
2 changed files with 162 additions and 71 deletions
|
@ -41,19 +41,26 @@ type WireGuard struct {
|
|||
|
||||
type WireGuardOption struct {
|
||||
BasicOption
|
||||
WireGuardPeerOption
|
||||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port"`
|
||||
Ip string `proxy:"ip,omitempty"`
|
||||
Ipv6 string `proxy:"ipv6,omitempty"`
|
||||
PrivateKey string `proxy:"private-key"`
|
||||
PublicKey string `proxy:"public-key"`
|
||||
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
|
||||
Reserved []uint8 `proxy:"reserved,omitempty"`
|
||||
Workers int `proxy:"workers,omitempty"`
|
||||
MTU int `proxy:"mtu,omitempty"`
|
||||
UDP bool `proxy:"udp,omitempty"`
|
||||
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
|
||||
|
||||
Peers []WireGuardPeerOption `proxy:"peers,omitempty"`
|
||||
}
|
||||
|
||||
type WireGuardPeerOption struct {
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port"`
|
||||
Ip string `proxy:"ip,omitempty"`
|
||||
Ipv6 string `proxy:"ipv6,omitempty"`
|
||||
PublicKey string `proxy:"public-key,omitempty"`
|
||||
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
|
||||
Reserved []uint8 `proxy:"reserved,omitempty"`
|
||||
AllowedIPs []string `proxy:"allowed_ips,omitempty"`
|
||||
}
|
||||
|
||||
type wgSingDialer struct {
|
||||
|
@ -80,32 +87,11 @@ func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (
|
|||
return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap())
|
||||
}
|
||||
|
||||
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
||||
outbound := &WireGuard{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.WireGuard,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
dialer: &wgSingDialer{dialer: dialer.NewDialer()},
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeWireGuard)
|
||||
func (option WireGuardPeerOption) Addr() M.Socksaddr {
|
||||
return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))
|
||||
}
|
||||
|
||||
var reserved [3]uint8
|
||||
if len(option.Reserved) > 0 {
|
||||
if len(option.Reserved) != 3 {
|
||||
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved))
|
||||
}
|
||||
reserved[0] = uint8(option.Reserved[0])
|
||||
reserved[1] = uint8(option.Reserved[1])
|
||||
reserved[2] = uint8(option.Reserved[2])
|
||||
}
|
||||
peerAddr := M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))
|
||||
outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, true, peerAddr, reserved)
|
||||
func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
|
||||
localPrefixes := make([]netip.Prefix, 0, 2)
|
||||
if len(option.Ip) > 0 {
|
||||
if !strings.Contains(option.Ip, "/") {
|
||||
|
@ -130,7 +116,46 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||
if len(localPrefixes) == 0 {
|
||||
return nil, E.New("missing local address")
|
||||
}
|
||||
var privateKey, peerPublicKey, preSharedKey string
|
||||
return localPrefixes, nil
|
||||
}
|
||||
|
||||
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
||||
outbound := &WireGuard{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.WireGuard,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
dialer: &wgSingDialer{dialer: dialer.NewDialer()},
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeWireGuard)
|
||||
|
||||
var reserved [3]uint8
|
||||
if len(option.Reserved) > 0 {
|
||||
if len(option.Reserved) != 3 {
|
||||
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved))
|
||||
}
|
||||
copy(reserved[:], option.Reserved)
|
||||
}
|
||||
var isConnect bool
|
||||
var connectAddr M.Socksaddr
|
||||
if len(option.Peers) < 2 {
|
||||
isConnect = true
|
||||
if len(option.Peers) == 1 {
|
||||
connectAddr = option.Peers[0].Addr()
|
||||
} else {
|
||||
connectAddr = option.Addr()
|
||||
}
|
||||
}
|
||||
outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, isConnect, connectAddr, reserved)
|
||||
|
||||
var localPrefixes []netip.Prefix
|
||||
|
||||
var privateKey string
|
||||
{
|
||||
bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey)
|
||||
if err != nil {
|
||||
|
@ -138,6 +163,52 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||
}
|
||||
privateKey = hex.EncodeToString(bytes)
|
||||
}
|
||||
ipcConf := "private_key=" + privateKey
|
||||
if peersLen := len(option.Peers); peersLen > 0 {
|
||||
localPrefixes = make([]netip.Prefix, 0, peersLen*2)
|
||||
for i, peer := range option.Peers {
|
||||
var peerPublicKey, preSharedKey string
|
||||
{
|
||||
bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode public key for peer ", i)
|
||||
}
|
||||
peerPublicKey = hex.EncodeToString(bytes)
|
||||
}
|
||||
if peer.PreSharedKey != "" {
|
||||
bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode pre shared key for peer ", i)
|
||||
}
|
||||
preSharedKey = hex.EncodeToString(bytes)
|
||||
}
|
||||
destination := peer.Addr()
|
||||
ipcConf += "\npublic_key=" + peerPublicKey
|
||||
ipcConf += "\nendpoint=" + destination.String()
|
||||
if preSharedKey != "" {
|
||||
ipcConf += "\npreshared_key=" + preSharedKey
|
||||
}
|
||||
if len(peer.AllowedIPs) == 0 {
|
||||
return nil, E.New("missing allowed_ips for peer ", i)
|
||||
}
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
ipcConf += "\nallowed_ip=" + allowedIP
|
||||
}
|
||||
if len(peer.Reserved) > 0 {
|
||||
if len(peer.Reserved) != 3 {
|
||||
return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved))
|
||||
}
|
||||
copy(reserved[:], option.Reserved)
|
||||
outbound.bind.SetReservedForEndpoint(destination, reserved)
|
||||
}
|
||||
prefixes, err := peer.Prefixes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localPrefixes = append(localPrefixes, prefixes...)
|
||||
}
|
||||
} else {
|
||||
var peerPublicKey, preSharedKey string
|
||||
{
|
||||
bytes, err := base64.StdEncoding.DecodeString(option.PublicKey)
|
||||
if err != nil {
|
||||
|
@ -152,12 +223,16 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||
}
|
||||
preSharedKey = hex.EncodeToString(bytes)
|
||||
}
|
||||
ipcConf := "private_key=" + privateKey
|
||||
ipcConf += "\npublic_key=" + peerPublicKey
|
||||
ipcConf += "\nendpoint=" + peerAddr.String()
|
||||
ipcConf += "\nendpoint=" + connectAddr.String()
|
||||
if preSharedKey != "" {
|
||||
ipcConf += "\npreshared_key=" + preSharedKey
|
||||
}
|
||||
var err error
|
||||
localPrefixes, err = option.Prefixes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var has4, has6 bool
|
||||
for _, address := range localPrefixes {
|
||||
if address.Addr().Is4() {
|
||||
|
@ -172,6 +247,8 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||
if has6 {
|
||||
ipcConf += "\nallowed_ip=::/0"
|
||||
}
|
||||
}
|
||||
|
||||
if option.PersistentKeepalive != 0 {
|
||||
ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive)
|
||||
}
|
||||
|
@ -179,6 +256,9 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||
if mtu == 0 {
|
||||
mtu = 1408
|
||||
}
|
||||
if len(localPrefixes) == 0 {
|
||||
return nil, E.New("missing local address")
|
||||
}
|
||||
var err error
|
||||
outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu))
|
||||
if err != nil {
|
||||
|
|
|
@ -620,12 +620,23 @@ proxies: # socks5
|
|||
port: 2480
|
||||
ip: 172.16.0.2
|
||||
ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
||||
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
|
||||
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
||||
# pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=
|
||||
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
|
||||
udp: true
|
||||
reserved: "U4An"
|
||||
# 数组格式也是合法的
|
||||
# reserved: [209,98,59]
|
||||
# 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定
|
||||
# peers:
|
||||
# - server: 162.159.192.1
|
||||
# port: 2480
|
||||
# ip: 172.16.0.2
|
||||
# ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
||||
# public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
||||
# # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=
|
||||
# allowed_ips: ['0.0.0.0/0']
|
||||
# reserved: [209,98,59]
|
||||
|
||||
# tuic
|
||||
- name: tuic
|
||||
|
|
Loading…
Reference in a new issue