diff --git a/.circleci/config.yml b/.circleci/config.yml index 17cbfade..3e223e55 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,13 +174,6 @@ jobs: GOOS=freebsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-amd64; GOOS=freebsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-i386; - - run: - name: Build for NetBSD - command: | - rm -f {yggdrasil,yggdrasilctl} - GOOS=netbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-netbsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-netbsd-amd64; - GOOS=netbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-netbsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-netbsd-i386; - - run: name: Build for Windows command: | diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..58724a24 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,20 @@ +version: '{build}' +pull_requests: + do_not_increment_build_number: true +os: Visual Studio 2017 +shallow_clone: false + +environment: + MSYS2_PATH_TYPE: inherit + CHERE_INVOKING: enabled_from_arguments + +build_script: +- cmd: >- + cd %APPVEYOR_BUILD_FOLDER% +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64" +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86" + +test: off + +artifacts: +- path: '*.msi' diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh new file mode 100644 index 00000000..5d8eb7db --- /dev/null +++ b/contrib/msi/build-msi.sh @@ -0,0 +1,205 @@ +#!/bin/sh + +# This script generates an MSI file for Yggdrasil for a given architecture. It +# needs to run on Windows within MSYS2 and Go 1.13 or later must be installed on +# the system and within the PATH. This is ran currently by Appveyor (see +# appveyor.yml in the repository root) for both x86 and x64. +# +# Author: Neil Alexander + +# Get arch from command line if given +PKGARCH=$1 +if [ "${PKGARCH}" == "" ]; +then + echo "tell me the architecture: x86 or x64" + exit 1 +fi + +# Get the rest of the repository history. This is needed within Appveyor because +# otherwise we don't get all of the branch histories and therefore the semver +# scripts don't work properly. +if [ "${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}" != "" ]; +then + git fetch --all + git checkout ${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH} +elif [ "${APPVEYOR_REPO_BRANCH}" != "" ]; +then + git fetch --all + git checkout ${APPVEYOR_REPO_BRANCH} +fi + +# Install prerequisites within MSYS2 +pacman -S --needed --noconfirm unzip git curl + +# Download the wix tools! +if [ ! -d wixbin ]; +then + curl -LO https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip + if [ `md5sum wix311-binaries.zip | cut -f 1 -d " "` != "47a506f8ab6666ee3cc502fb07d0ee2a" ]; + then + echo "wix package didn't match expected checksum" + exit 1 + fi + mkdir -p wixbin + unzip -o wix311-binaries.zip -d wixbin || ( + echo "failed to unzip WiX" + exit 1 + ) +fi + +# Build Yggdrasil! +[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build +[ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build + +# Create the postinstall script +cat > config.bat << EOF +if not exist yggdrasil.conf ( + yggdrasil.exe -genconf > yggdrasil.conf +) +EOF + +# Work out metadata for the package info +PKGNAME=$(sh contrib/semver/name.sh) +PKGVERSION=$(sh contrib/semver/version.sh --bare) +PKGVERSIONMS=$(echo $PKGVERSION | tr - .) +[ "${PKGARCH}" == "x64" ] && \ + PKGGUID="77757838-1a23-40a5-a720-c3b43e0260cc" PKGINSTFOLDER="ProgramFiles64Folder" || \ + PKGGUID="54a3294e-a441-4322-aefb-3bb40dd022bb" PKGINSTFOLDER="ProgramFilesFolder" + +# Download the Wintun driver +if [ $PKGARCH = "x64" ]; then + PKGMSMNAME=wintun-x64.msm + curl -o ${PKGMSMNAME} https://www.wintun.net/builds/wintun-amd64-0.7.msm || (echo "couldn't get wintun"; exit 1) +elif [ $PKGARCH = "x86" ]; then + PKGMSMNAME=wintun-x86.msm + curl -o ${PKGMSMNAME} https://www.wintun.net/builds/wintun-x86-0.7.msm || (echo "couldn't get wintun"; exit 1) +else + echo "wasn't sure which architecture to get wintun for" + exit 1 +fi + +# Generate the wix.xml file +cat > wix.xml << EOF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT Installed AND NOT REMOVE + + + + + +EOF + +# Generate the MSI +CANDLEFLAGS="-nologo" +LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61" +wixbin/candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \ +wixbin/light $LIGHTFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj diff --git a/go.mod b/go.mod index a45600a3..c11ccdf7 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,12 @@ require ( github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 - github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 - github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b // indirect github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect - github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8 - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/net v0.0.0-20191021144547-ec77196f6094 - golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb + golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c + golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 + golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e golang.org/x/text v0.3.2 + golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 + golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 ) diff --git a/go.sum b/go.sum index ecfa4626..9a61fd2c 100644 --- a/go.sum +++ b/go.sum @@ -8,29 +8,37 @@ github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:b github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= +github.com/lxn/walk v0.0.0-20191031081659-c0bb82ae46cb/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1 h1:h0wbuSK8xUNmMwDdCxZx2OLdkVck6Bb31zj4CxCN5I4= +github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w= -github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8oye9a7HmytmZ+tr8Q2vlP0tAHP13kTHzwvQY= -github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c= -github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8 h1:YY9Pg2BEp0jeUVU60svTOaDr+fs1ySC9RbdC1Qc6wOw= -github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8/go.mod h1:R0SBCsugm+Sf1katgTb2t7GXMm+nRIv43tM4VDZbaOs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb h1:ZxSglHghKPYD8WDeRUzRJrUJtDF0PxsTUSxyqr9/5BI= -golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 h1:KxtBKNgJUQG8vwZzJKkwBGOcqp95xLu6A6KIMde1kl0= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 h1:TreP+furSwdqoSToFrwb1S5cwxb7jhOsnwj2MsDeT+4= +golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25/go.mod h1:EO8KCpT944a9CnwHJLZ1sl84FfIrY42fP/fcXUuYhKM= diff --git a/src/config/config.go b/src/config/config.go index ac88bfc5..3e1438b8 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -5,7 +5,7 @@ Yggdrasil node. The configuration contains, amongst other things, encryption keys which are used to derive a node's identity, information about peerings and node information that is shared with the network. There are also some module-specific options -related to TUN/TAP, multicast and the admin socket. +related to TUN, multicast and the admin socket. In order for a node to maintain the same identity across restarts, you should persist the configuration onto the filesystem or into some configuration storage @@ -70,9 +70,8 @@ type NodeConfig struct { SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` - IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` - IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."` - IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` + IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."` TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."` SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` @@ -127,7 +126,6 @@ func GenerateConfig() *NodeConfig { cfg.MulticastInterfaces = defaults.GetDefaults().DefaultMulticastInterfaces cfg.IfName = defaults.GetDefaults().DefaultIfName cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU - cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode cfg.SessionFirewall.Enable = false cfg.SessionFirewall.AllowFromDirect = true cfg.SessionFirewall.AllowFromRemote = true diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index a5784198..ceb58ba8 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -14,8 +14,7 @@ type platformDefaultParameters struct { DefaultMulticastInterfaces []string // TUN/TAP - MaximumIfMTU int - DefaultIfMTU int - DefaultIfName string - DefaultIfTAPMode bool + MaximumIfMTU int + DefaultIfMTU int + DefaultIfName string } diff --git a/src/defaults/defaults_darwin.go b/src/defaults/defaults_darwin.go index 47683bf9..9fbe6d8e 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/defaults/defaults_darwin.go @@ -19,9 +19,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "auto", } } diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index 0e523483..b08d80d0 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 32767, - DefaultIfMTU: 32767, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, + MaximumIfMTU: 32767, + DefaultIfMTU: 32767, + DefaultIfName: "/dev/tun0", } } diff --git a/src/defaults/defaults_linux.go b/src/defaults/defaults_linux.go index b0aaf855..5f3f12a8 100644 --- a/src/defaults/defaults_linux.go +++ b/src/defaults/defaults_linux.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "auto", } } diff --git a/src/defaults/defaults_netbsd.go b/src/defaults/defaults_netbsd.go deleted file mode 100644 index 52a487b7..00000000 --- a/src/defaults/defaults_netbsd.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build netbsd - -package defaults - -// Sane defaults for the BSD platforms. The "default" options may be -// may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { - return platformDefaultParameters{ - // Admin - DefaultAdminListen: "unix:///var/run/yggdrasil.sock", - - // Configuration (used for yggdrasilctl) - DefaultConfigFile: "/etc/yggdrasil.conf", - - // Multicast interfaces - DefaultMulticastInterfaces: []string{ - ".*", - }, - - // TUN/TAP - MaximumIfMTU: 9000, - DefaultIfMTU: 9000, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, - } -} diff --git a/src/defaults/defaults_openbsd.go b/src/defaults/defaults_openbsd.go index d44e5714..20741ee8 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/defaults/defaults_openbsd.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 16384, - DefaultIfMTU: 16384, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, + MaximumIfMTU: 16384, + DefaultIfMTU: 16384, + DefaultIfName: "/dev/tun0", } } diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index 0ba825c5..3b035537 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd +// +build !linux,!darwin,!windows,!openbsd,!freebsd package defaults @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "none", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "none", } } diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index 6d53225a..305a2ffe 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: true, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "Yggdrasil", } } diff --git a/src/tuntap/admin.go b/src/tuntap/admin.go index 778c03ae..c7fc20b0 100644 --- a/src/tuntap/admin.go +++ b/src/tuntap/admin.go @@ -19,9 +19,8 @@ func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) { }() return admin.Info{ - t.iface.Name(): admin.Info{ - "tap_mode": t.iface.IsTAP(), - "mtu": t.mtu, + t.Name(): admin.Info{ + "mtu": t.mtu, }, }, nil }) diff --git a/src/tuntap/icmpv6.go b/src/tuntap/icmpv6.go index e601acb5..67c10e54 100644 --- a/src/tuntap/icmpv6.go +++ b/src/tuntap/icmpv6.go @@ -11,32 +11,16 @@ package tuntap import ( "encoding/binary" - "errors" "net" - "sync" - "time" "golang.org/x/net/icmp" "golang.org/x/net/ipv6" - - "github.com/yggdrasil-network/yggdrasil-go/src/address" ) const len_ETHER = 14 type ICMPv6 struct { - tun *TunAdapter - mylladdr net.IP - mymac net.HardwareAddr - peermacs map[address.Address]neighbor - peermacsmutex sync.RWMutex -} - -type neighbor struct { - mac net.HardwareAddr - learned bool - lastadvertisement time.Time - lastsolicitation time.Time + tun *TunAdapter } // Marshal returns the binary encoding of h. @@ -61,182 +45,6 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { // addresses. func (i *ICMPv6) Init(t *TunAdapter) { i.tun = t - i.peermacsmutex.Lock() - i.peermacs = make(map[address.Address]neighbor) - i.peermacsmutex.Unlock() - - // Our MAC address and link-local address - i.mymac = net.HardwareAddr{ - 0x02, 0x00, 0x00, 0x00, 0x00, 0x02} - i.mylladdr = net.IP{ - 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE} - copy(i.mymac[:], i.tun.addr[:]) - copy(i.mylladdr[9:], i.tun.addr[1:]) -} - -// Parses an incoming ICMPv6 packet. The packet provided may be either an -// ethernet frame containing an IP packet, or the IP packet alone. This is -// determined by whether the TUN/TAP adapter is running in TUN (layer 3) or -// TAP (layer 2) mode. Returns an error condition which is nil if the ICMPv6 -// module handled the packet or contains the error if not. -func (i *ICMPv6) ParsePacket(datain []byte) error { - var response []byte - var err error - - // Parse the frame/packet - if i.tun.IsTAP() { - response, err = i.UnmarshalPacketL2(datain) - } else { - response, err = i.UnmarshalPacket(datain, nil) - } - - if err != nil { - return err - } - - // Write the packet to TUN/TAP - i.tun.iface.Write(response) - return nil -} - -// Unwraps the ethernet headers of an incoming ICMPv6 packet and hands off -// the IP packet to the ParsePacket function for further processing. -// A response buffer is also created for the response message, also complete -// with ethernet headers. -func (i *ICMPv6) UnmarshalPacketL2(datain []byte) ([]byte, error) { - // Ignore non-IPv6 frames - if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) { - return nil, errors.New("Ignoring non-IPv6 frame") - } - - // Hand over to ParsePacket to interpret the IPv6 packet - mac := datain[6:12] - ipv6packet, err := i.UnmarshalPacket(datain[len_ETHER:], &mac) - if err != nil { - return nil, err - } - - // Create the response buffer - dataout := make([]byte, len_ETHER+ipv6.HeaderLen+32) - - // Populate the response ethernet headers - copy(dataout[:6], datain[6:12]) - copy(dataout[6:12], i.mymac[:]) - binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) - - // Copy the returned packet to our response ethernet frame - copy(dataout[len_ETHER:], ipv6packet) - return dataout, nil -} - -// Unwraps the IP headers of an incoming IPv6 packet and performs various -// sanity checks on the packet - i.e. is the packet an ICMPv6 packet, does the -// ICMPv6 message match a known expected type. The relevant handler function -// is then called and a response packet may be returned. -func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) { - // Parse the IPv6 packet headers - ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen]) - if err != nil { - return nil, err - } - - // Check if the packet is IPv6 - if ipv6Header.Version != ipv6.Version { - return nil, errors.New("Ignoring non-IPv6 packet") - } - - // Check if the packet is ICMPv6 - if ipv6Header.NextHeader != 58 { - return nil, errors.New("Ignoring non-ICMPv6 packet") - } - - // Parse the ICMPv6 message contents - icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:]) - if err != nil { - return nil, err - } - - // Check for a supported message type - switch icmpv6Header.Type { - case ipv6.ICMPTypeNeighborSolicitation: - if !i.tun.IsTAP() { - return nil, errors.New("Ignoring Neighbor Solicitation in TUN mode") - } - response, err := i.HandleNDP(datain[ipv6.HeaderLen:]) - if err == nil { - // Create our ICMPv6 response - responsePacket, err := CreateICMPv6( - ipv6Header.Src, i.mylladdr, - ipv6.ICMPTypeNeighborAdvertisement, 0, - &icmp.DefaultMessageBody{Data: response}) - if err != nil { - return nil, err - } - // Send it back - return responsePacket, nil - } else { - return nil, err - } - case ipv6.ICMPTypeNeighborAdvertisement: - if !i.tun.IsTAP() { - return nil, errors.New("Ignoring Neighbor Advertisement in TUN mode") - } - if datamac != nil { - var addr address.Address - var target address.Address - mac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - copy(addr[:], ipv6Header.Src[:]) - copy(target[:], datain[48:64]) - copy(mac[:], (*datamac)[:]) - i.peermacsmutex.Lock() - neighbor := i.peermacs[target] - neighbor.mac = mac - neighbor.learned = true - neighbor.lastadvertisement = time.Now() - i.peermacs[target] = neighbor - i.peermacsmutex.Unlock() - i.tun.log.Debugln("Learned peer MAC", mac.String(), "for", net.IP(target[:]).String()) - /* - i.tun.log.Debugln("Peer MAC table:") - i.peermacsmutex.RLock() - for t, n := range i.peermacs { - if n.learned { - i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "has MAC", n.mac.String()) - } else { - i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "is not learned yet") - } - } - i.peermacsmutex.RUnlock() - */ - } - return nil, errors.New("No response needed") - } - - return nil, errors.New("ICMPv6 type not matched") -} - -// Creates an ICMPv6 packet based on the given icmp.MessageBody and other -// parameters, complete with ethernet and IP headers, which can be written -// directly to a TAP adapter. -func (i *ICMPv6) CreateICMPv6L2(dstmac net.HardwareAddr, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { - // Pass through to CreateICMPv6 - ipv6packet, err := CreateICMPv6(dst, src, mtype, mcode, mbody) - if err != nil { - return nil, err - } - - // Create the response buffer - dataout := make([]byte, len_ETHER+len(ipv6packet)) - - // Populate the response ethernet headers - copy(dataout[:6], dstmac[:6]) - copy(dataout[6:12], i.mymac[:]) - binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) - - // Copy the returned packet to our response ethernet frame - copy(dataout[len_ETHER:], ipv6packet) - return dataout, nil } // Creates an ICMPv6 packet based on the given icmp.MessageBody and other @@ -281,106 +89,3 @@ func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody // Send it back return responsePacket, nil } - -func (i *ICMPv6) Solicit(addr address.Address) { - retries := 5 - for retries > 0 { - retries-- - i.peermacsmutex.RLock() - if n, ok := i.peermacs[addr]; ok && n.learned { - i.tun.log.Debugln("MAC learned for", net.IP(addr[:]).String()) - i.peermacsmutex.RUnlock() - return - } - i.peermacsmutex.RUnlock() - i.tun.log.Debugln("Sending neighbor solicitation for", net.IP(addr[:]).String()) - i.peermacsmutex.Lock() - if n, ok := i.peermacs[addr]; !ok { - i.peermacs[addr] = neighbor{ - lastsolicitation: time.Now(), - } - } else { - n.lastsolicitation = time.Now() - } - i.peermacsmutex.Unlock() - request, err := i.createNDPL2(addr) - if err != nil { - panic(err) - } - if _, err := i.tun.iface.Write(request); err != nil { - panic(err) - } - i.tun.log.Debugln("Sent neighbor solicitation for", net.IP(addr[:]).String()) - time.Sleep(time.Second) - } -} - -func (i *ICMPv6) getNeighbor(addr address.Address) (neighbor, bool) { - i.peermacsmutex.RLock() - defer i.peermacsmutex.RUnlock() - - n, ok := i.peermacs[addr] - return n, ok -} - -func (i *ICMPv6) createNDPL2(dst address.Address) ([]byte, error) { - // Create the ND payload - var payload [28]byte - copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00}) // Flags - copy(payload[4:20], dst[:]) // Destination - copy(payload[20:22], []byte{0x01, 0x01}) // Type & length - copy(payload[22:28], i.mymac[:6]) // Link layer address - - // Create the ICMPv6 solicited-node address - var dstaddr address.Address - copy(dstaddr[:13], []byte{ - 0xFF, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xFF}) - copy(dstaddr[13:], dst[13:16]) - - // Create the multicast MAC - dstmac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - copy(dstmac[:2], []byte{0x33, 0x33}) - copy(dstmac[2:6], dstaddr[12:16]) - - // Create the ND request - requestPacket, err := i.CreateICMPv6L2( - dstmac, dstaddr[:], i.mylladdr, - ipv6.ICMPTypeNeighborSolicitation, 0, - &icmp.DefaultMessageBody{Data: payload[:]}) - if err != nil { - return nil, err - } - - return requestPacket, nil -} - -// Generates a response to an NDP discovery packet. This is effectively called -// when the host operating system generates an NDP request for any address in -// the fd00::/8 range, so that the operating system knows to route that traffic -// to the Yggdrasil TAP adapter. -func (i *ICMPv6) HandleNDP(in []byte) ([]byte, error) { - // Ignore NDP requests for anything outside of fd00::/8 - var source address.Address - copy(source[:], in[8:]) - var snet address.Subnet - copy(snet[:], in[8:]) - switch { - case source.IsValid(): - case snet.IsValid(): - default: - return nil, errors.New("Not an NDP for 0200::/7") - } - - // Create our NDP message body response - body := make([]byte, 28) - binary.BigEndian.PutUint32(body[:4], uint32(0x40000000)) // Flags - copy(body[4:20], in[8:24]) // Target address - body[20] = uint8(2) // Type: Target link-layer address - body[21] = uint8(1) // Length: 1x address (8 bytes) - copy(body[22:28], i.mymac[:6]) - - // Send it back - return body, nil -} diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 3d788b1a..5efbc8a7 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -1,11 +1,6 @@ package tuntap import ( - "bytes" - "net" - "time" - - "github.com/songgao/packets/ethernet" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" @@ -14,6 +9,8 @@ import ( "github.com/Arceliar/phony" ) +const TUN_OFFSET_BYTES = 4 + type tunWriter struct { phony.Inbox tun *TunAdapter @@ -25,80 +22,29 @@ func (w *tunWriter) writeFrom(from phony.Actor, b []byte) { }) } -// write is pretty loose with the memory safety rules, e.g. it assumes it can read w.tun.iface.IsTap() safely +// write is pretty loose with the memory safety rules, e.g. it assumes it can +// read w.tun.iface.IsTap() safely func (w *tunWriter) _write(b []byte) { + defer util.PutBytes(b) var written int var err error n := len(b) if n == 0 { return } - if w.tun.iface.IsTAP() { - sendndp := func(dstAddr address.Address) { - neigh, known := w.tun.icmpv6.getNeighbor(dstAddr) - known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) - if !known { - w.tun.icmpv6.Solicit(dstAddr) - } - } - peermac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - var dstAddr address.Address - var peerknown bool - if b[0]&0xf0 == 0x40 { - dstAddr = w.tun.addr - } else if b[0]&0xf0 == 0x60 { - if !bytes.Equal(w.tun.addr[:16], dstAddr[:16]) && !bytes.Equal(w.tun.subnet[:8], dstAddr[:8]) { - dstAddr = w.tun.addr - } - } - if neighbor, ok := w.tun.icmpv6.getNeighbor(dstAddr); ok && neighbor.learned { - // If we've learned the MAC of a 300::/7 address, for example, or a CKR - // address, use the MAC address of that - peermac = neighbor.mac - peerknown = true - } else if neighbor, ok := w.tun.icmpv6.getNeighbor(w.tun.addr); ok && neighbor.learned { - // Otherwise send directly to the MAC address of the host if that's - // known instead - peermac = neighbor.mac - peerknown = true - } else { - // Nothing has been discovered, try to discover the destination - sendndp(w.tun.addr) - } - if peerknown { - var proto ethernet.Ethertype - switch { - case b[0]&0xf0 == 0x60: - proto = ethernet.IPv6 - case b[0]&0xf0 == 0x40: - proto = ethernet.IPv4 - } - var frame ethernet.Frame - frame.Prepare( - peermac[:6], // Destination MAC address - w.tun.icmpv6.mymac[:6], // Source MAC address - ethernet.NotTagged, // VLAN tagging - proto, // Ethertype - len(b)) // Payload length - copy(frame[tun_ETHER_HEADER_LENGTH:], b[:n]) - n += tun_ETHER_HEADER_LENGTH - written, err = w.tun.iface.Write(frame[:n]) - } else { - w.tun.log.Errorln("TUN/TAP iface write error: no peer MAC known for", net.IP(dstAddr[:]).String(), "- dropping packet") - } - } else { - written, err = w.tun.iface.Write(b[:n]) - util.PutBytes(b) - } + temp := append(util.ResizeBytes(util.GetBytes(), TUN_OFFSET_BYTES), b...) + defer util.PutBytes(temp) + written, err = w.tun.iface.Write(temp, TUN_OFFSET_BYTES) if err != nil { w.tun.Act(w, func() { if !w.tun.isOpen { - w.tun.log.Errorln("TUN/TAP iface write error:", err) + w.tun.log.Errorln("TUN iface write error:", err) } }) } - if written != n { - w.tun.log.Errorln("TUN/TAP iface write mismatch:", written, "bytes written vs", n, "bytes given") + if written != n+TUN_OFFSET_BYTES { + // FIXME some platforms return the wrong number of bytes written, causing error spam + //w.tun.log.Errorln("TUN iface write mismatch:", written, "bytes written vs", n+TUN_OFFSET_BYTES, "bytes given") } } @@ -109,13 +55,18 @@ type tunReader struct { func (r *tunReader) _read() { // Get a slice to store the packet in - recvd := util.ResizeBytes(util.GetBytes(), 65535+tun_ETHER_HEADER_LENGTH) - // Wait for a packet to be delivered to us through the TUN/TAP adapter - n, err := r.tun.iface.Read(recvd) - if n <= 0 { + recvd := util.ResizeBytes(util.GetBytes(), r.tun.mtu+TUN_OFFSET_BYTES) + // Wait for a packet to be delivered to us through the TUN adapter + n, err := r.tun.iface.Read(recvd, TUN_OFFSET_BYTES) + if n <= TUN_OFFSET_BYTES || err != nil { + r.tun.log.Errorln("Error reading TUN:", err) + ferr := r.tun.iface.Flush() + if ferr != nil { + r.tun.log.Errorln("Unable to flush packets:", ferr) + } util.PutBytes(recvd) } else { - r.tun.handlePacketFrom(r, recvd[:n], err) + r.tun.handlePacketFrom(r, recvd[TUN_OFFSET_BYTES:n+TUN_OFFSET_BYTES], err) } if err == nil { // Now read again @@ -132,43 +83,17 @@ func (tun *TunAdapter) handlePacketFrom(from phony.Actor, packet []byte, err err // does the work of reading a packet and sending it to the correct tunConn func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if err != nil { - tun.log.Errorln("TUN/TAP iface read error:", err) + tun.log.Errorln("TUN iface read error:", err) return } - // If it's a TAP adapter, update the buffer slice so that we no longer - // include the ethernet headers - offset := 0 - if tun.iface.IsTAP() { - // Set our offset to beyond the ethernet headers - offset = tun_ETHER_HEADER_LENGTH - // Check first of all that we can go beyond the ethernet headers - if len(recvd) <= offset { - return - } - } // Offset the buffer from now on so that we can ignore ethernet frames if // they are present - bs := recvd[offset:] + bs := recvd[:] // Check if the packet is long enough to detect if it's an ICMP packet or not if len(bs) < 7 { - tun.log.Traceln("TUN/TAP iface read undersized unknown packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized unknown packet, length:", len(bs)) return } - // If we detect an ICMP packet then hand it to the ICMPv6 module - if bs[6] == 58 { - // Found an ICMPv6 packet - we need to make sure to give ICMPv6 the full - // Ethernet frame rather than just the IPv6 packet as this is needed for - // NDP to work correctly - if err := tun.icmpv6.ParsePacket(recvd); err == nil { - // We acted on the packet in the ICMPv6 module so don't forward or do - // anything else with it - return - } - } - if offset != 0 { - // Shift forward to avoid leaking bytes off the front of the slice when we eventually store it - bs = append(recvd[:0], bs...) - } // From the IP header, work out what our source and destination addresses // and node IDs are. We will need these in order to work out where to send // the packet @@ -181,7 +106,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if bs[0]&0xf0 == 0x60 { // Check if we have a fully-sized IPv6 header if len(bs) < 40 { - tun.log.Traceln("TUN/TAP iface read undersized ipv6 packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized ipv6 packet, length:", len(bs)) return } // Check the packet size @@ -195,7 +120,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { } else if bs[0]&0xf0 == 0x40 { // Check if we have a fully-sized IPv4 header if len(bs) < 20 { - tun.log.Traceln("TUN/TAP iface read undersized ipv4 packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized ipv4 packet, length:", len(bs)) return } // Check the packet size @@ -267,7 +192,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil { // Something went wrong when storing the connection, typically that // something already exists for this address or subnet - tun.log.Debugln("TUN/TAP iface wrap:", err) + tun.log.Debugln("TUN iface wrap:", err) return } for _, packet := range packets { diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 1e994ea5..807ce7fb 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -18,7 +18,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/water" + "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -28,12 +28,11 @@ import ( ) const tun_IPv6_HEADER_LENGTH = 40 -const tun_ETHER_HEADER_LENGTH = 14 -// TunAdapter represents a running TUN/TAP interface and extends the -// yggdrasil.Adapter type. In order to use the TUN/TAP adapter with Yggdrasil, -// you should pass this object to the yggdrasil.SetRouterAdapter() function -// before calling yggdrasil.Start(). +// TunAdapter represents a running TUN interface and extends the +// yggdrasil.Adapter type. In order to use the TUN adapter with Yggdrasil, you +// should pass this object to the yggdrasil.SetRouterAdapter() function before +// calling yggdrasil.Start(). type TunAdapter struct { core *yggdrasil.Core writer tunWriter @@ -48,7 +47,7 @@ type TunAdapter struct { ckr cryptokey icmpv6 ICMPv6 mtu int - iface *water.Interface + iface tun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below //mutex sync.RWMutex // Protects the below addrToConn map[address.Address]*tunConn @@ -64,12 +63,12 @@ type TunOptions struct { // Gets the maximum supported MTU for the platform based on the defaults in // defaults.GetDefaults(). -func getSupportedMTU(mtu int, istapmode bool) int { +func getSupportedMTU(mtu int) int { if mtu < 1280 { return 1280 } - if mtu > MaximumMTU(istapmode) { - return MaximumMTU(istapmode) + if mtu > MaximumMTU() { + return MaximumMTU() } return mtu } @@ -77,55 +76,38 @@ func getSupportedMTU(mtu int, istapmode bool) int { // Name returns the name of the adapter, e.g. "tun0". On Windows, this may // return a canonical adapter name instead. func (tun *TunAdapter) Name() string { - return tun.iface.Name() + if name, err := tun.iface.Name(); err == nil { + return name + } + return "" } // MTU gets the adapter's MTU. This can range between 1280 and 65535, although // the maximum value is determined by your platform. The returned value will // never exceed that of MaximumMTU(). func (tun *TunAdapter) MTU() int { - return getSupportedMTU(tun.mtu, tun.IsTAP()) + return getSupportedMTU(tun.mtu) } -// IsTAP returns true if the adapter is a TAP adapter (Layer 2) or false if it -// is a TUN adapter (Layer 3). -func (tun *TunAdapter) IsTAP() bool { - return tun.iface.IsTAP() -} - -// DefaultName gets the default TUN/TAP interface name for your platform. +// DefaultName gets the default TUN interface name for your platform. func DefaultName() string { return defaults.GetDefaults().DefaultIfName } -// DefaultMTU gets the default TUN/TAP interface MTU for your platform. This can +// DefaultMTU gets the default TUN interface MTU for your platform. This can // be as high as MaximumMTU(), depending on platform, but is never lower than 1280. func DefaultMTU() int { - ehbytes := 0 - if DefaultIsTAP() { - ehbytes = tun_ETHER_HEADER_LENGTH - } - return defaults.GetDefaults().DefaultIfMTU - ehbytes + return defaults.GetDefaults().DefaultIfMTU } -// DefaultIsTAP returns true if the default adapter mode for the current -// platform is TAP (Layer 2) and returns false for TUN (Layer 3). -func DefaultIsTAP() bool { - return defaults.GetDefaults().DefaultIfTAPMode -} - -// MaximumMTU returns the maximum supported TUN/TAP interface MTU for your +// MaximumMTU returns the maximum supported TUN interface MTU for your // platform. This can be as high as 65535, depending on platform, but is never // lower than 1280. -func MaximumMTU(iftapmode bool) int { - ehbytes := 0 - if iftapmode { - ehbytes = tun_ETHER_HEADER_LENGTH - } - return defaults.GetDefaults().MaximumIfMTU - ehbytes +func MaximumMTU() int { + return defaults.GetDefaults().MaximumIfMTU } -// Init initialises the TUN/TAP module. You must have acquired a Listener from +// Init initialises the TUN module. You must have acquired a Listener from // the Yggdrasil core before this point and it must not be in use elsewhere. func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error { tunoptions, ok := options.(TunOptions) @@ -145,7 +127,7 @@ func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log return nil } -// Start the setup process for the TUN/TAP adapter. If successful, starts the +// Start the setup process for the TUN adapter. If successful, starts the // reader actor to handle packets on that interface. func (tun *TunAdapter) Start() error { var err error @@ -157,11 +139,11 @@ func (tun *TunAdapter) Start() error { func (tun *TunAdapter) _start() error { if tun.isOpen { - return errors.New("TUN/TAP module is already started") + return errors.New("TUN module is already started") } current := tun.config.GetCurrent() if tun.config == nil || tun.listener == nil || tun.dialer == nil { - return errors.New("no configuration available to TUN/TAP") + return errors.New("no configuration available to TUN") } var boxPub crypto.BoxPubKey boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey) @@ -174,23 +156,19 @@ func (tun *TunAdapter) _start() error { tun.subnet = *address.SubnetForNodeID(nodeID) addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) if current.IfName == "none" || current.IfName == "dummy" { - tun.log.Debugln("Not starting TUN/TAP as ifname is none or dummy") + tun.log.Debugln("Not starting TUN as ifname is none or dummy") return nil } - if err := tun.setup(current.IfName, current.IfTAPMode, addr, current.IfMTU); err != nil { + if err := tun.setup(current.IfName, addr, current.IfMTU); err != nil { return err } if tun.MTU() != current.IfMTU { - tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) + tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU()) } tun.core.SetMaximumSessionMTU(uint16(tun.MTU())) tun.isOpen = true go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader - tun.icmpv6.Init(tun) - if tun.IsTAP() { - go tun.icmpv6.Solicit(tun.addr) - } tun.ckr.init(tun) return nil } @@ -204,7 +182,7 @@ func (tun *TunAdapter) IsStarted() bool { return isOpen } -// Start the setup process for the TUN/TAP adapter. If successful, starts the +// Start the setup process for the TUN adapter. If successful, starts the // read/write goroutines to handle packets on that interface. func (tun *TunAdapter) Stop() error { var err error @@ -216,7 +194,7 @@ func (tun *TunAdapter) Stop() error { func (tun *TunAdapter) _stop() error { tun.isOpen = false - // by TUN/TAP, e.g. readers/writers, sessions + // by TUN, e.g. readers/writers, sessions if tun.iface != nil { // Just in case we failed to start up the iface for some reason, this can apparently happen on Windows tun.iface.Close() @@ -224,16 +202,16 @@ func (tun *TunAdapter) _stop() error { return nil } -// UpdateConfig updates the TUN/TAP module with the provided config.NodeConfig +// UpdateConfig updates the TUN module with the provided config.NodeConfig // and then signals the various module goroutines to reconfigure themselves if // needed. func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) { - tun.log.Debugln("Reloading TUN/TAP configuration...") + tun.log.Debugln("Reloading TUN configuration...") // Replace the active configuration with the supplied one tun.config.Replace(*config) - // If the MTU has changed in the TUN/TAP module then this is where we would + // If the MTU has changed in the TUN module then this is where we would // tell the router so that updated session pings can be sent. However, we // don't currently update the MTU of the adapter once it has been created so // this doesn't actually happen in the real world yet. @@ -248,14 +226,14 @@ func (tun *TunAdapter) handler() error { // Accept the incoming connection conn, err := tun.listener.Accept() if err != nil { - tun.log.Errorln("TUN/TAP connection accept error:", err) + tun.log.Errorln("TUN connection accept error:", err) return err } phony.Block(tun, func() { if _, err := tun._wrap(conn.(*yggdrasil.Conn)); err != nil { // Something went wrong when storing the connection, typically that // something already exists for this address or subnet - tun.log.Debugln("TUN/TAP handler wrap:", err) + tun.log.Debugln("TUN handler wrap:", err) } }) } diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 78a4adab..219e3485 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -1,4 +1,4 @@ -// +build openbsd freebsd netbsd +// +build openbsd freebsd package tuntap @@ -12,7 +12,7 @@ import ( "golang.org/x/sys/unix" - "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 @@ -72,34 +72,18 @@ type in6_ifreq_lifetime struct { ifru_addrlifetime in6_addrlifetime } -// Sets the IPv6 address of the utun adapter. On all BSD platforms (FreeBSD, -// OpenBSD, NetBSD) an attempt is made to set the adapter properties by using -// a system socket and making syscalls to the kernel. This is not refined though -// and often doesn't work (if at all), therefore if a call fails, it resorts -// to calling "ifconfig" instead. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if ifname[:4] == "auto" { - ifname = "/dev/tap0" - } - if len(ifname) < 9 { - panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX") - } - switch { - case iftapmode || ifname[:8] == "/dev/tap": - config = water.Config{DeviceType: water.TAP} - case !iftapmode || ifname[:8] == "/dev/tun": - panic("TUN mode is not currently supported on this platform, please use TAP instead") - default: - panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX") - } - config.Name = ifname - iface, err := water.New(config) +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } @@ -114,13 +98,13 @@ func (tun *TunAdapter) setupAddress(addr string) error { } // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface name: %s", tun.Name()) tun.log.Infof("Interface IPv6: %s", addr) tun.log.Infof("Interface MTU: %d", tun.mtu) // Create the MTU request var ir in6_ifreq_mtu - copy(ir.ifr_name[:], tun.iface.Name()) + copy(ir.ifr_name[:], tun.Name()) ir.ifru_mtu = int(tun.mtu) // Set the MTU @@ -129,7 +113,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) // Fall back to ifconfig to set the MTU - cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) + cmd := exec.Command("ifconfig", tun.Name(), "mtu", string(tun.mtu)) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { @@ -141,7 +125,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { // Create the address request // FIXME: I don't work! var ar in6_ifreq_addr - copy(ar.ifr_name[:], tun.iface.Name()) + copy(ar.ifr_name[:], tun.Name()) ar.ifru_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifru_addr)) ar.ifru_addr.sin6_family = unix.AF_INET6 parts := strings.Split(strings.Split(addr, "/")[0], ":") @@ -158,7 +142,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) // Fall back to ifconfig to set the address - cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) + cmd := exec.Command("ifconfig", tun.Name(), "inet6", addr) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index ab6f34e3..cf2fbfb4 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -12,22 +12,24 @@ import ( "golang.org/x/sys/unix" - water "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) // Configures the "utun" adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - if iftapmode { - tun.log.Warnln("Warning: TAP mode is not supported on this platform, defaulting to TUN") - iftapmode = false +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + if ifname == "auto" { + ifname = "utun" } - config := water.Config{DeviceType: water.TUN} - iface, err := water.New(config) + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } @@ -80,7 +82,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { } var ar in6_aliasreq - copy(ar.ifra_name[:], tun.iface.Name()) + copy(ar.ifra_name[:], tun.Name()) ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask)) b := make([]byte, 16) @@ -104,7 +106,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { ar.ifra_lifetime.ia6t_pltime = darwin_ND6_INFINITE_LIFETIME var ir ifreq - copy(ir.ifr_name[:], tun.iface.Name()) + copy(ir.ifr_name[:], tun.Name()) ir.ifru_mtu = uint32(tun.mtu) tun.log.Infof("Interface name: %s", ar.ifra_name) diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index b5918328..4206b26a 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -6,31 +6,24 @@ package tuntap import ( "github.com/vishvananda/netlink" - - water "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) -// Configures the TAP adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if iftapmode { - config = water.Config{DeviceType: water.TAP} - } else { - config = water.Config{DeviceType: water.TUN} +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + if ifname == "auto" { + ifname = "\000" } - if ifname != "" && ifname != "auto" { - config.Name = ifname - } - iface, err := water.New(config) + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) - // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) - tun.log.Infof("Interface IPv6: %s", addr) - tun.log.Infof("Interface MTU: %d", tun.mtu) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } @@ -43,7 +36,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { if err != nil { return err } - nlintf, err := netlink.LinkByName(tun.iface.Name()) + nlintf, err := netlink.LinkByName(tun.Name()) if err != nil { return err } @@ -56,5 +49,9 @@ func (tun *TunAdapter) setupAddress(addr string) error { if err := netlink.LinkSetUp(nlintf); err != nil { return err } + // Friendly output + tun.log.Infof("Interface name: %s", tun.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) return nil } diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index 7d4f0643..8a27f57b 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -1,33 +1,32 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile +// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile package tuntap -import water "github.com/yggdrasil-network/water" - // This is to catch unsupported platforms // If your platform supports tun devices, you could try configuring it manually -// Creates the TUN/TAP adapter, if supported by the Water library. Note that -// no guarantees are made at this point on an unsupported platform. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if iftapmode { - config = water.Config{DeviceType: water.TAP} - } else { - config = water.Config{DeviceType: water.TUN} - } - iface, err := water.New(config) +import ( + wgtun "golang.zx2c4.com/wireguard/tun" +) + +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } // We don't know how to set the IPv6 address on an unknown platform, therefore // write about it to stdout and don't try to do anything further. func (tun *TunAdapter) setupAddress(addr string) error { - tun.log.Warnln("Warning: Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.log.Warnln("Warning: Platform not supported, you must set the address of", tun.Name(), "to", addr) return nil } diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index d4fd1c3d..9b5f7b0c 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -1,116 +1,150 @@ +// +build windows + package tuntap import ( + "bytes" "errors" - "fmt" - "os/exec" - "strings" - "time" + "log" + "net" - water "github.com/yggdrasil-network/water" + "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "golang.org/x/sys/windows" + + wgtun "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/windows/elevate" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) // This is to catch Windows platforms -// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows -// we don't make use of a direct operating system API to do this - we instead -// delegate the hard work to "netsh". -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - if !iftapmode { - tun.log.Warnln("Warning: TUN mode is not supported on this platform, defaulting to TAP") - iftapmode = true - } - config := water.Config{DeviceType: water.TAP} - config.PlatformSpecificParams.ComponentID = "tap0901" - config.PlatformSpecificParams.Network = "169.254.0.1/32" +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { - config.PlatformSpecificParams.InterfaceName = "" - } else { - config.PlatformSpecificParams.InterfaceName = ifname + ifname = defaults.GetDefaults().DefaultIfName } - iface, err := water.New(config) - if err != nil { - return err - } - if iface.Name() == "" { - return errors.New("unable to find TAP adapter with component ID " + config.PlatformSpecificParams.ComponentID) - } - // Reset the adapter - this invalidates iface so we'll need to get a new one - cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err - } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect - // Bring the interface back up - cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err = cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err - } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect - // Get a new iface - iface, err = water.New(config) - if err != nil { - panic(err) - } - tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) - err = tun.setupMTU(tun.mtu) - if err != nil { - panic(err) - } - // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) - tun.log.Infof("Interface IPv6: %s", addr) - tun.log.Infof("Interface MTU: %d", tun.mtu) - return tun.setupAddress(addr) + return elevate.DoAsSystem(func() error { + var err error + var iface wgtun.Device + var guid windows.GUID + if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil { + return err + } + if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu); err != nil { + return err + } + tun.iface = iface + if err = tun.setupAddress(addr); err != nil { + tun.log.Errorln("Failed to set up TUN address:", err) + return err + } + if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil { + tun.log.Errorln("Failed to set up TUN MTU:", err) + return err + } + if mtu, err = iface.MTU(); err == nil { + tun.mtu = mtu + } + return nil + }) } // Sets the MTU of the TAP adapter. func (tun *TunAdapter) setupMTU(mtu int) error { - if tun.iface == nil || tun.iface.Name() == "" { - return errors.New("Can't configure MTU as TAP adapter is not present") + if tun.iface == nil || tun.Name() == "" { + return errors.New("Can't configure MTU as TUN adapter is not present") } - // Set MTU - cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface", - fmt.Sprintf("interface=%s", tun.iface.Name()), - fmt.Sprintf("mtu=%d", mtu), - "store=active") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err + if intf, ok := tun.iface.(*wgtun.NativeTun); ok { + luid := winipcfg.LUID(intf.LUID()) + ipfamily, err := luid.IPInterface(windows.AF_INET6) + if err != nil { + return err + } + + ipfamily.NLMTU = uint32(mtu) + intf.ForceMTU(int(ipfamily.NLMTU)) + ipfamily.UseAutomaticMetric = false + ipfamily.Metric = 0 + ipfamily.DadTransmits = 0 + ipfamily.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled + + if err := ipfamily.Set(); err != nil { + return err + } } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect + return nil } // Sets the IPv6 address of the TAP adapter. func (tun *TunAdapter) setupAddress(addr string) error { - if tun.iface == nil || tun.iface.Name() == "" { - return errors.New("Can't configure IPv6 address as TAP adapter is not present") + if tun.iface == nil || tun.Name() == "" { + return errors.New("Can't configure IPv6 address as TUN adapter is not present") } - // Set address - cmd := exec.Command("netsh", "interface", "ipv6", "add", "address", - fmt.Sprintf("interface=%s", tun.iface.Name()), - fmt.Sprintf("addr=%s", addr), - "store=active") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err + if intf, ok := tun.iface.(*wgtun.NativeTun); ok { + if ipaddr, ipnet, err := net.ParseCIDR(addr); err == nil { + luid := winipcfg.LUID(intf.LUID()) + addresses := append([]net.IPNet{}, net.IPNet{ + IP: ipaddr, + Mask: ipnet.Mask, + }) + + err := luid.SetIPAddressesForFamily(windows.AF_INET6, addresses) + if err == windows.ERROR_OBJECT_ALREADY_EXISTS { + cleanupAddressesOnDisconnectedInterfaces(windows.AF_INET6, addresses) + err = luid.SetIPAddressesForFamily(windows.AF_INET6, addresses) + } + if err != nil { + return err + } + } else { + return err + } + } else { + return errors.New("unable to get NativeTUN") } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect return nil } + +/* + * cleanupAddressesOnDisconnectedInterfaces + * SPDX-License-Identifier: MIT + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ +func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) { + if len(addresses) == 0 { + return + } + includedInAddresses := func(a net.IPNet) bool { + // TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! + for _, addr := range addresses { + ip := addr.IP + if ip4 := ip.To4(); ip4 != nil { + ip = ip4 + } + mA, _ := addr.Mask.Size() + mB, _ := a.Mask.Size() + if bytes.Equal(ip, a.IP) && mA == mB { + return true + } + } + return false + } + interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault) + if err != nil { + return + } + for _, iface := range interfaces { + if iface.OperStatus == winipcfg.IfOperStatusUp { + continue + } + for address := iface.FirstUnicastAddress; address != nil; address = address.Next { + ip := address.Address.IP() + ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} + if includedInAddresses(ipnet) { + log.Printf("Cleaning up stale address %s from interface ā€˜%sā€™", ipnet.String(), iface.FriendlyName()) + iface.LUID.DeleteIPAddress(ipnet) + } + } + } +}