Skip to content

Commit

Permalink
fix: Reimplement wrtcip lifecycle to only start the TUN device once…
Browse files Browse the repository at this point in the history
… an IP is available to support Windows TUN drivers
  • Loading branch information
pojntfx committed Aug 27, 2023
1 parent f02ffc0 commit e1ed0c4
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 168 deletions.
2 changes: 1 addition & 1 deletion Hydrunfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ if [ "$1" = "go" ]; then
make depend

# Build
CGO_ENABLED=0 bagop -j "$(nproc)" -b "$2" -x '(android/*|ios/*|plan9/*|aix/*|linux/loong64|freebsd/riscv64)' -p "make build/$2 DST=\$DST" -d out
CGO_ENABLED=0 bagop -j "$(nproc)" -b "$2" -x '(android/*|ios/*|plan9/*|aix/*|linux/loong64|freebsd/riscv64|wasip1/wasm)' -p "make build/$2 DST=\$DST" -d out

exit 0
fi
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ For more information, see the [throughput measurement utility reference](#throug

### 6. Create a Layer 3 (IP) Overlay Network with `weron vpn ip`

If you want to join multiple nodes into an overlay network, the IP VPN is the best choice. It works similarly to i.e. Tailscale/WireGuard and can either dynamically allocate an IP address from a CIDR notation or statically assign one for you. On Windows, make sure to install [TAP-Windows](https://duckduckgo.com/?q=TAP-Windows&t=h_&ia=web) first. To get started, launch the VPN on the first peer:
If you want to join multiple nodes into an overlay network, the IP VPN is the best choice. It works similarly to i.e. Tailscale/WireGuard and can either dynamically allocate an IP address from a CIDR notation or statically assign one for you. On Windows, make sure to install [TAP-Windows](https://build.openvpn.net/downloads/releases/) first. Also note that due to technical limitations, only one IPv4 or IPv6 network and only one VPN instance at a time is supported on Windows; on macOS, only IPv6 networks are supported and IPv4 networks are ignored. To get started, launch the VPN on the first peer:

```shell
$ sudo weron vpn ip --community mycommunity --password mypassword --key mykey --ips 2001:db8::1/64,192.0.2.1/24
Expand Down Expand Up @@ -580,16 +580,16 @@ Aliases:

Flags:
--community string ID of community to join
--dev string Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux, macOS and Windows)
--dev string Name to give to the TUN device (i.e. weron0) (default is auto-generated; only supported on Linux)
--force-relay Force usage of TURN servers
-h, --help help for ip
--ice strings Comma-separated list of STUN servers (in format stun:host:port) and TURN servers to use (in format username:credential@turn:host:port) (i.e. username:credential@turn:global.turn.twilio.com:3478?transport=tcp) (default [stun:stun.l.google.com:19302])
--id-channel string Channel to use to negotiate names (default "weron/ip/id")
--ips strings Comma-separated list of IP networks to claim an IP address from and and give to the TUN device (i.e. 2001:db8::1/32,192.0.2.1/24) (on Windows, only one IPv4 and one IPv6 address are supported; on macOS, IPv4 addresses are ignored)
--ips strings Comma-separated list of IP networks to claim an IP address from and and give to the TUN device (i.e. 2001:db8::1/32,192.0.2.1/24) (on Windows, only one IP network (either IPv4 or IPv6) is supported; on macOS, IPv4 networks are ignored)
--key string Encryption key for community
--kicks duration Time to wait for kicks (default 5s)
--max-retries int Maximum amount of times to try and claim an IP address (default 200)
--parallel int Amount of threads to use to decode frames (default 8)
--parallel int Amount of threads to use to decode frames (default 20)
--password string Password for community
--raddr string Remote address (default "wss://weron.up.railway.app/")
--static Try to claim the exact IPs specified in the --ips flag statically instead of selecting a random one from the specified network
Expand All @@ -613,13 +613,13 @@ Aliases:

Flags:
--community string ID of community to join
--dev string Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux, macOS and Windows)
--dev string Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux and macOS)
--force-relay Force usage of TURN servers
-h, --help help for ethernet
--ice strings Comma-separated list of STUN servers (in format stun:host:port) and TURN servers to use (in format username:credential@turn:host:port) (i.e. username:credential@turn:global.turn.twilio.com:3478?transport=tcp) (default [stun:stun.l.google.com:19302])
--key string Encryption key for community
--mac string MAC address to give to the TAP device (i.e. 3a:f8:de:7b:ef:52) (default is auto-generated; only supported on Linux)
--parallel int Amount of threads to use to decode frames (default 8)
--parallel int Amount of threads to use to decode frames (default 20)
--password string Password for community
--raddr string Remote address (default "wss://weron.up.railway.app/")
--timeout duration Time to wait for connections (default 10s)
Expand Down
2 changes: 1 addition & 1 deletion cmd/weron/cmd/vpn_ethernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func init() {
vpnEthernetCmd.PersistentFlags().String(keyFlag, "", "Encryption key for community")
vpnEthernetCmd.PersistentFlags().StringSlice(iceFlag, []string{"stun:stun.l.google.com:19302"}, "Comma-separated list of STUN servers (in format stun:host:port) and TURN servers to use (in format username:credential@turn:host:port) (i.e. username:credential@turn:global.turn.twilio.com:3478?transport=tcp)")
vpnEthernetCmd.PersistentFlags().Bool(forceRelayFlag, false, "Force usage of TURN servers")
vpnEthernetCmd.PersistentFlags().String(devFlag, "", "Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux, macOS and Windows)")
vpnEthernetCmd.PersistentFlags().String(devFlag, "", "Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux and macOS)")
vpnEthernetCmd.PersistentFlags().String(macFlag, "", "MAC address to give to the TAP device (i.e. 3a:f8:de:7b:ef:52) (default is auto-generated; only supported on Linux)")
vpnEthernetCmd.PersistentFlags().Int(parallelFlag, runtime.NumCPU(), "Amount of threads to use to decode frames")

Expand Down
7 changes: 3 additions & 4 deletions cmd/weron/cmd/vpn_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
)

var (
errMissingIPs = errors.New("no IP(s) provided")
errInvalidCIDR = errors.New("invalid CIDR notation for IPs")
)

Expand Down Expand Up @@ -54,7 +53,7 @@ var vpnIPCmd = &cobra.Command{
}

if len(viper.GetStringSlice(ipsFlag)) <= 0 {
return errMissingIPs
return wrtcip.ErrMissingIPs
}

for _, ip := range viper.GetStringSlice(ipsFlag) {
Expand Down Expand Up @@ -131,8 +130,8 @@ func init() {
vpnIPCmd.PersistentFlags().String(keyFlag, "", "Encryption key for community")
vpnIPCmd.PersistentFlags().StringSlice(iceFlag, []string{"stun:stun.l.google.com:19302"}, "Comma-separated list of STUN servers (in format stun:host:port) and TURN servers to use (in format username:credential@turn:host:port) (i.e. username:credential@turn:global.turn.twilio.com:3478?transport=tcp)")
vpnIPCmd.PersistentFlags().Bool(forceRelayFlag, false, "Force usage of TURN servers")
vpnIPCmd.PersistentFlags().String(devFlag, "", "Name to give to the TAP device (i.e. weron0) (default is auto-generated; only supported on Linux, macOS and Windows)")
vpnIPCmd.PersistentFlags().StringSlice(ipsFlag, []string{""}, "Comma-separated list of IP networks to claim an IP address from and and give to the TUN device (i.e. 2001:db8::1/32,192.0.2.1/24) (on Windows, only one IPv4 and one IPv6 address are supported; on macOS, IPv4 addresses are ignored)")
vpnIPCmd.PersistentFlags().String(devFlag, "", "Name to give to the TUN device (i.e. weron0) (default is auto-generated; only supported on Linux)")
vpnIPCmd.PersistentFlags().StringSlice(ipsFlag, []string{""}, "Comma-separated list of IP networks to claim an IP address from and and give to the TUN device (i.e. 2001:db8::1/32,192.0.2.1/24) (on Windows, only one IP network (either IPv4 or IPv6) is supported; on macOS, IPv4 networks are ignored)")
vpnIPCmd.PersistentFlags().Bool(staticFlag, false, "Try to claim the exact IPs specified in the --"+ipsFlag+" flag statically instead of selecting a random one from the specified network")
vpnIPCmd.PersistentFlags().Int(parallelFlag, runtime.NumCPU(), "Amount of threads to use to decode frames")
vpnIPCmd.PersistentFlags().String(idChannelFlag, services.IPID, "Channel to use to negotiate names")
Expand Down
48 changes: 21 additions & 27 deletions pkg/wrtcip/netns_linux.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
package wrtcip

import (
"net"

"github.com/songgao/water"
"github.com/vishvananda/netlink"
)

func getPlatformSpecificParams(name string) water.PlatformSpecificParams {
return water.PlatformSpecificParams{
Name: name,
}
}

func setIPAddress(linkName string, ipaddr string, ipv4 bool) error {
link, err := netlink.LinkByName(linkName)
func setupTUN(name string, ips []string) (*water.Interface, int, error) {
tun, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{
Name: name,
},
})
if err != nil {
return err
return nil, 0, err
}

ip, err := netlink.ParseAddr(ipaddr)
link, err := netlink.LinkByName(tun.Name())
if err != nil {
return err
return tun, 0, err
}

return netlink.AddrAdd(link, ip)
}
for _, rawIP := range ips {
ip, err := netlink.ParseAddr(rawIP)
if err != nil {
return tun, 0, err
}

func getMTU(linkName string) (int, error) {
iface, err := net.InterfaceByName(linkName)
if err != nil {
return -1, err
if err := netlink.AddrAdd(link, ip); err != nil {
return tun, 0, err
}
}

return iface.MTU, nil
}

func setLinkUp(linkName string) error {
link, err := netlink.LinkByName(linkName)
if err != nil {
return err
if err := netlink.LinkSetUp(link); err != nil {
return tun, 0, err
}

return netlink.LinkSetUp(link)
return tun, link.Attrs().MTU, nil
}
53 changes: 31 additions & 22 deletions pkg/wrtcip/netns_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,48 @@ import (
"fmt"
"net"
"os/exec"
"runtime"

"github.com/songgao/water"
)

func getPlatformSpecificParams(name string) water.PlatformSpecificParams {
return water.PlatformSpecificParams{}
}
func setupTUN(name string, ips []string) (*water.Interface, int, error) {
tun, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{},
})
if err != nil {
return nil, 0, err
}

func setIPAddress(linkName string, ipaddr string, ipv4 bool) error {
if ipv4 {
output, err := exec.Command("ifconfig", linkName, "inet", ipaddr).CombinedOutput()
for _, rawIP := range ips {
ip, _, err := net.ParseCIDR(rawIP)
if err != nil {
return fmt.Errorf("could not add IPv4 address to interface: %v: %v", string(output), err)
return tun, 0, err
}
} else {
output, err := exec.Command("ifconfig", linkName, "inet6", "add", ipaddr).CombinedOutput()
if err != nil {
return fmt.Errorf("could not add IPv6 address to interface: %v: %v", string(output), err)

if ip.To4() != nil {
// macOS does not support IPv4 TUN
if runtime.GOOS == "darwin" && ip.To4() != nil {
continue
}

output, err := exec.Command("ifconfig", tun.Name(), "inet", rawIP).CombinedOutput()
if err != nil {
return tun, 0, fmt.Errorf("could not add IPv4 address to interface: %v: %v", string(output), err)
}
} else {
output, err := exec.Command("ifconfig", tun.Name(), "inet6", "add", rawIP).CombinedOutput()
if err != nil {
return tun, 0, fmt.Errorf("could not add IPv6 address to interface: %v: %v", string(output), err)
}
}
}

return nil
}

func getMTU(linkName string) (int, error) {
iface, err := net.InterfaceByName(linkName)
iface, err := net.InterfaceByName(tun.Name())
if err != nil {
return -1, err
return tun, 0, err
}

return iface.MTU, nil
}

func setLinkUp(linkName string) error {
return nil
return tun, iface.MTU, nil
}
46 changes: 25 additions & 21 deletions pkg/wrtcip/netns_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,44 @@ package wrtcip
import (
"fmt"
"net"
"os/exec"

"github.com/songgao/water"
"os/exec"
)

func getPlatformSpecificParams(name string) water.PlatformSpecificParams {
return water.PlatformSpecificParams{}
}
func setupTUN(name string, ips []string) (*water.Interface, int, error) {
tun, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{
ComponentID: "tap0901",
Network: ips[0],
},
})
if err != nil {
return nil, 0, err
}

ip, _, err := net.ParseCIDR(ips[0])
if err != nil {
return tun, 0, err
}

func setIPAddress(linkName string, ipaddr string, ipv4 bool) error {
if ipv4 {
output, err := exec.Command("netsh", "interface", "ipv4", "set", "address", linkName, "static", ipaddr).CombinedOutput()
if ip.To4() != nil {
output, err := exec.Command("netsh", "interface", "ipv4", "set", "address", tun.Name(), "static", ips[0]).CombinedOutput()
if err != nil {
return fmt.Errorf("could not add IPv4 address to interface: %v: %v", string(output), err)
return tun, 0, fmt.Errorf("could not add IPv4 address to interface: %v: %v", string(output), err)
}
} else {
output, err := exec.Command("netsh", "interface", "ipv6", "set", "address", linkName, ipaddr).CombinedOutput()
output, err := exec.Command("netsh", "interface", "ipv6", "set", "address", tun.Name(), ips[0]).CombinedOutput()
if err != nil {
return fmt.Errorf("could not add IPv6 address to interface: %v: %v", string(output), err)
return tun, 0, fmt.Errorf("could not add IPv6 address to interface: %v: %v", string(output), err)
}
}

return nil
}

func getMTU(linkName string) (int, error) {
iface, err := net.InterfaceByName(linkName)
iface, err := net.InterfaceByName(tun.Name())
if err != nil {
return -1, err
return tun, 0, err
}

return iface.MTU, nil
}

func setLinkUp(linkName string) error {
return nil
return tun, iface.MTU, nil
}
Loading

0 comments on commit e1ed0c4

Please sign in to comment.