Merge pull request #618 from yggdrasil-network/goodbyewater

Replace Water library with Wireguard's tun package (inc. Wintun support on Windows)
This commit is contained in:
Arceliar 2019-11-28 12:03:05 -06:00 committed by GitHub
commit 9967541627
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 521 additions and 708 deletions

View File

@ -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=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; 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: - run:
name: Build for Windows name: Build for Windows
command: | command: |

20
appveyor.yml Normal file
View File

@ -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'

205
contrib/msi/build-msi.sh Normal file
View File

@ -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 <neilalexander@users.noreply.github.com>
# 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
<?xml version="1.0" encoding="windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product
Name="Yggdrasil (${PKGNAME} branch)"
Id="*"
UpgradeCode="${PKGGUID}"
Language="1033"
Codepage="1252"
Version="${PKGVERSIONMS}"
Manufacturer="github.com/yggdrasil-network">
<Package
Id="*"
Keywords="Installer"
Description="Yggdrasil Network Installer"
Comments="This is the Yggdrasil Network binary."
Manufacturer="github.com/yggdrasil-network"
InstallerVersion="200"
Languages="1033"
Compressed="yes"
Platform="${PKGARCH}"
SummaryCodepage="1252" />
<MajorUpgrade
AllowDowngrades="yes" />
<Media
Id="1"
Cabinet="Media.cab"
EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="${PKGINSTFOLDER}" Name="PFiles">
<Directory Id="YggdrasilInstallFolder" Name="Yggdrasil">
<Component Id="MainExecutable" Guid="c2119231-2aa3-4962-867a-9759c87beb24">
<File
Id="Yggdrasil"
Name="yggdrasil.exe"
DiskId="1"
Source="yggdrasil.exe"
KeyPath="yes" />
<ServiceInstall
Id="ServiceInstaller"
Account="LocalSystem"
Description="Yggdrasil Network router process"
DisplayName="Yggdrasil Service"
ErrorControl="normal"
LoadOrderGroup="NetworkProvider"
Name="yggdrasil"
Start="auto"
Type="ownProcess"
Arguments='-useconffile "[YggdrasilInstallFolder]yggdrasil.conf" -logto "[YggdrasilInstallFolder]yggdrasil.log"'
Vital="yes" />
<ServiceControl
Id="ServiceControl"
Name="yggdrasil"
Start="install"
Stop="both"
Remove="uninstall" />
</Component>
<Component Id="CtrlExecutable" Guid="a916b730-974d-42a1-b687-d9d504cbb86a">
<File
Id="Yggdrasilctl"
Name="yggdrasilctl.exe"
DiskId="1"
Source="yggdrasilctl.exe"
KeyPath="yes"/>
</Component>
<Component Id="ConfigScript" Guid="64a3733b-c98a-4732-85f3-20cd7da1a785">
<File
Id="Configbat"
Name="updateconfig.bat"
DiskId="1"
Source="config.bat"
KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<Merge
Id="Wintun"
Language="0"
DiskId="1"
SourceFile="${PKGMSMNAME}" />
</Directory>
<Feature Id="Complete" Level="1">
<MergeRef Id="Wintun" />
<ComponentRef Id="MainExecutable" />
<ComponentRef Id="CtrlExecutable" />
<ComponentRef Id="ConfigScript" />
</Feature>
<CustomAction
Id="UpdateGenerateConfig"
Directory="YggdrasilInstallFolder"
ExeCommand="cmd.exe /c updateconfig.bat"
Execute="deferred"
Impersonate="no"
Return="check" />
<InstallExecuteSequence>
<Custom
Action="UpdateGenerateConfig"
Before="InstallServices">
NOT Installed AND NOT REMOVE
</Custom>
</InstallExecuteSequence>
</Product>
</Wix>
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

11
go.mod
View File

@ -9,13 +9,12 @@ require (
github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
github.com/mitchellh/mapstructure v1.1.2 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/netlink v1.0.0
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect 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-20191122220453-ac88ee75c92c
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914
golang.org/x/net v0.0.0-20191021144547-ec77196f6094 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e
golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb
golang.org/x/text v0.3.2 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
) )

32
go.sum
View File

@ -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/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 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g=
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= 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 h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I=
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= 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-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-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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-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-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/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-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-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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 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/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.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=

View File

@ -5,7 +5,7 @@ Yggdrasil node.
The configuration contains, amongst other things, encryption keys which are used The configuration contains, amongst other things, encryption keys which are used
to derive a node's identity, information about peerings and node information 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 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 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 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."` 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!"` 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."` 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."` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
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 interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
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."`
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."` 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."` 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."` 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.MulticastInterfaces = defaults.GetDefaults().DefaultMulticastInterfaces
cfg.IfName = defaults.GetDefaults().DefaultIfName cfg.IfName = defaults.GetDefaults().DefaultIfName
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
cfg.SessionFirewall.Enable = false cfg.SessionFirewall.Enable = false
cfg.SessionFirewall.AllowFromDirect = true cfg.SessionFirewall.AllowFromDirect = true
cfg.SessionFirewall.AllowFromRemote = true cfg.SessionFirewall.AllowFromRemote = true

View File

@ -14,8 +14,7 @@ type platformDefaultParameters struct {
DefaultMulticastInterfaces []string DefaultMulticastInterfaces []string
// TUN/TAP // TUN/TAP
MaximumIfMTU int MaximumIfMTU int
DefaultIfMTU int DefaultIfMTU int
DefaultIfName string DefaultIfName string
DefaultIfTAPMode bool
} }

View File

@ -19,9 +19,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,
DefaultIfName: "auto", DefaultIfName: "auto",
DefaultIfTAPMode: false,
} }
} }

View File

@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 32767, MaximumIfMTU: 32767,
DefaultIfMTU: 32767, DefaultIfMTU: 32767,
DefaultIfName: "/dev/tap0", DefaultIfName: "/dev/tun0",
DefaultIfTAPMode: true,
} }
} }

View File

@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,
DefaultIfName: "auto", DefaultIfName: "auto",
DefaultIfTAPMode: false,
} }
} }

View File

@ -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,
}
}

View File

@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 16384, MaximumIfMTU: 16384,
DefaultIfMTU: 16384, DefaultIfMTU: 16384,
DefaultIfName: "/dev/tap0", DefaultIfName: "/dev/tun0",
DefaultIfTAPMode: true,
} }
} }

View File

@ -1,4 +1,4 @@
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd // +build !linux,!darwin,!windows,!openbsd,!freebsd
package defaults package defaults
@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,
DefaultIfName: "none", DefaultIfName: "none",
DefaultIfTAPMode: false,
} }
} }

View File

@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
}, },
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,
DefaultIfName: "auto", DefaultIfName: "Yggdrasil",
DefaultIfTAPMode: true,
} }
} }

View File

@ -19,9 +19,8 @@ func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
}() }()
return admin.Info{ return admin.Info{
t.iface.Name(): admin.Info{ t.Name(): admin.Info{
"tap_mode": t.iface.IsTAP(), "mtu": t.mtu,
"mtu": t.mtu,
}, },
}, nil }, nil
}) })

View File

@ -11,32 +11,16 @@ package tuntap
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"net" "net"
"sync"
"time"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
) )
const len_ETHER = 14 const len_ETHER = 14
type ICMPv6 struct { type ICMPv6 struct {
tun *TunAdapter 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
} }
// Marshal returns the binary encoding of h. // Marshal returns the binary encoding of h.
@ -61,182 +45,6 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
// addresses. // addresses.
func (i *ICMPv6) Init(t *TunAdapter) { func (i *ICMPv6) Init(t *TunAdapter) {
i.tun = t 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 // 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 // Send it back
return responsePacket, nil 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
}

View File

@ -1,11 +1,6 @@
package tuntap package tuntap
import ( import (
"bytes"
"net"
"time"
"github.com/songgao/packets/ethernet"
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -14,6 +9,8 @@ import (
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
) )
const TUN_OFFSET_BYTES = 4
type tunWriter struct { type tunWriter struct {
phony.Inbox phony.Inbox
tun *TunAdapter 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) { func (w *tunWriter) _write(b []byte) {
defer util.PutBytes(b)
var written int var written int
var err error var err error
n := len(b) n := len(b)
if n == 0 { if n == 0 {
return return
} }
if w.tun.iface.IsTAP() { temp := append(util.ResizeBytes(util.GetBytes(), TUN_OFFSET_BYTES), b...)
sendndp := func(dstAddr address.Address) { defer util.PutBytes(temp)
neigh, known := w.tun.icmpv6.getNeighbor(dstAddr) written, err = w.tun.iface.Write(temp, TUN_OFFSET_BYTES)
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)
}
if err != nil { if err != nil {
w.tun.Act(w, func() { w.tun.Act(w, func() {
if !w.tun.isOpen { 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 { if written != n+TUN_OFFSET_BYTES {
w.tun.log.Errorln("TUN/TAP iface write mismatch:", written, "bytes written vs", n, "bytes given") // 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() { func (r *tunReader) _read() {
// Get a slice to store the packet in // Get a slice to store the packet in
recvd := util.ResizeBytes(util.GetBytes(), 65535+tun_ETHER_HEADER_LENGTH) recvd := util.ResizeBytes(util.GetBytes(), r.tun.mtu+TUN_OFFSET_BYTES)
// Wait for a packet to be delivered to us through the TUN/TAP adapter // Wait for a packet to be delivered to us through the TUN adapter
n, err := r.tun.iface.Read(recvd) n, err := r.tun.iface.Read(recvd, TUN_OFFSET_BYTES)
if n <= 0 { 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) util.PutBytes(recvd)
} else { } else {
r.tun.handlePacketFrom(r, recvd[:n], err) r.tun.handlePacketFrom(r, recvd[TUN_OFFSET_BYTES:n+TUN_OFFSET_BYTES], err)
} }
if err == nil { if err == nil {
// Now read again // 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 // does the work of reading a packet and sending it to the correct tunConn
func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
if err != nil { if err != nil {
tun.log.Errorln("TUN/TAP iface read error:", err) tun.log.Errorln("TUN iface read error:", err)
return 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 // Offset the buffer from now on so that we can ignore ethernet frames if
// they are present // 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 // Check if the packet is long enough to detect if it's an ICMP packet or not
if len(bs) < 7 { 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 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 // 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 // and node IDs are. We will need these in order to work out where to send
// the packet // the packet
@ -181,7 +106,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
if bs[0]&0xf0 == 0x60 { if bs[0]&0xf0 == 0x60 {
// Check if we have a fully-sized IPv6 header // Check if we have a fully-sized IPv6 header
if len(bs) < 40 { 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 return
} }
// Check the packet size // Check the packet size
@ -195,7 +120,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
} else if bs[0]&0xf0 == 0x40 { } else if bs[0]&0xf0 == 0x40 {
// Check if we have a fully-sized IPv4 header // Check if we have a fully-sized IPv4 header
if len(bs) < 20 { 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 return
} }
// Check the packet size // 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 { if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil {
// Something went wrong when storing the connection, typically that // Something went wrong when storing the connection, typically that
// something already exists for this address or subnet // something already exists for this address or subnet
tun.log.Debugln("TUN/TAP iface wrap:", err) tun.log.Debugln("TUN iface wrap:", err)
return return
} }
for _, packet := range packets { for _, packet := range packets {

View File

@ -18,7 +18,7 @@ import (
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
"github.com/gologme/log" "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/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/config"
@ -28,12 +28,11 @@ import (
) )
const tun_IPv6_HEADER_LENGTH = 40 const tun_IPv6_HEADER_LENGTH = 40
const tun_ETHER_HEADER_LENGTH = 14
// TunAdapter represents a running TUN/TAP interface and extends the // TunAdapter represents a running TUN interface and extends the
// yggdrasil.Adapter type. In order to use the TUN/TAP adapter with Yggdrasil, // yggdrasil.Adapter type. In order to use the TUN adapter with Yggdrasil, you
// you should pass this object to the yggdrasil.SetRouterAdapter() function // should pass this object to the yggdrasil.SetRouterAdapter() function before
// before calling yggdrasil.Start(). // calling yggdrasil.Start().
type TunAdapter struct { type TunAdapter struct {
core *yggdrasil.Core core *yggdrasil.Core
writer tunWriter writer tunWriter
@ -48,7 +47,7 @@ type TunAdapter struct {
ckr cryptokey ckr cryptokey
icmpv6 ICMPv6 icmpv6 ICMPv6
mtu int 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 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 //mutex sync.RWMutex // Protects the below
addrToConn map[address.Address]*tunConn 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 // Gets the maximum supported MTU for the platform based on the defaults in
// defaults.GetDefaults(). // defaults.GetDefaults().
func getSupportedMTU(mtu int, istapmode bool) int { func getSupportedMTU(mtu int) int {
if mtu < 1280 { if mtu < 1280 {
return 1280 return 1280
} }
if mtu > MaximumMTU(istapmode) { if mtu > MaximumMTU() {
return MaximumMTU(istapmode) return MaximumMTU()
} }
return mtu 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 // Name returns the name of the adapter, e.g. "tun0". On Windows, this may
// return a canonical adapter name instead. // return a canonical adapter name instead.
func (tun *TunAdapter) Name() string { 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 // 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 // the maximum value is determined by your platform. The returned value will
// never exceed that of MaximumMTU(). // never exceed that of MaximumMTU().
func (tun *TunAdapter) MTU() int { 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 // DefaultName gets the default TUN interface name for your platform.
// 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.
func DefaultName() string { func DefaultName() string {
return defaults.GetDefaults().DefaultIfName 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. // be as high as MaximumMTU(), depending on platform, but is never lower than 1280.
func DefaultMTU() int { func DefaultMTU() int {
ehbytes := 0 return defaults.GetDefaults().DefaultIfMTU
if DefaultIsTAP() {
ehbytes = tun_ETHER_HEADER_LENGTH
}
return defaults.GetDefaults().DefaultIfMTU - ehbytes
} }
// DefaultIsTAP returns true if the default adapter mode for the current // MaximumMTU returns the maximum supported TUN interface MTU for your
// 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
// platform. This can be as high as 65535, depending on platform, but is never // platform. This can be as high as 65535, depending on platform, but is never
// lower than 1280. // lower than 1280.
func MaximumMTU(iftapmode bool) int { func MaximumMTU() int {
ehbytes := 0 return defaults.GetDefaults().MaximumIfMTU
if iftapmode {
ehbytes = tun_ETHER_HEADER_LENGTH
}
return defaults.GetDefaults().MaximumIfMTU - ehbytes
} }
// 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. // 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 { func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error {
tunoptions, ok := options.(TunOptions) tunoptions, ok := options.(TunOptions)
@ -145,7 +127,7 @@ func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log
return nil 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. // reader actor to handle packets on that interface.
func (tun *TunAdapter) Start() error { func (tun *TunAdapter) Start() error {
var err error var err error
@ -157,11 +139,11 @@ func (tun *TunAdapter) Start() error {
func (tun *TunAdapter) _start() error { func (tun *TunAdapter) _start() error {
if tun.isOpen { if tun.isOpen {
return errors.New("TUN/TAP module is already started") return errors.New("TUN module is already started")
} }
current := tun.config.GetCurrent() current := tun.config.GetCurrent()
if tun.config == nil || tun.listener == nil || tun.dialer == nil { 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 var boxPub crypto.BoxPubKey
boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey) boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey)
@ -174,23 +156,19 @@ func (tun *TunAdapter) _start() error {
tun.subnet = *address.SubnetForNodeID(nodeID) tun.subnet = *address.SubnetForNodeID(nodeID)
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1)
if current.IfName == "none" || current.IfName == "dummy" { 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 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 return err
} }
if tun.MTU() != current.IfMTU { 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.core.SetMaximumSessionMTU(uint16(tun.MTU()))
tun.isOpen = true tun.isOpen = true
go tun.handler() go tun.handler()
tun.reader.Act(nil, tun.reader._read) // Start the reader 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) tun.ckr.init(tun)
return nil return nil
} }
@ -204,7 +182,7 @@ func (tun *TunAdapter) IsStarted() bool {
return isOpen 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. // read/write goroutines to handle packets on that interface.
func (tun *TunAdapter) Stop() error { func (tun *TunAdapter) Stop() error {
var err error var err error
@ -216,7 +194,7 @@ func (tun *TunAdapter) Stop() error {
func (tun *TunAdapter) _stop() error { func (tun *TunAdapter) _stop() error {
tun.isOpen = false tun.isOpen = false
// by TUN/TAP, e.g. readers/writers, sessions // by TUN, e.g. readers/writers, sessions
if tun.iface != nil { if tun.iface != nil {
// Just in case we failed to start up the iface for some reason, this can apparently happen on Windows // Just in case we failed to start up the iface for some reason, this can apparently happen on Windows
tun.iface.Close() tun.iface.Close()
@ -224,16 +202,16 @@ func (tun *TunAdapter) _stop() error {
return nil 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 // and then signals the various module goroutines to reconfigure themselves if
// needed. // needed.
func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) { 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 // Replace the active configuration with the supplied one
tun.config.Replace(*config) 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 // 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 // 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. // this doesn't actually happen in the real world yet.
@ -248,14 +226,14 @@ func (tun *TunAdapter) handler() error {
// Accept the incoming connection // Accept the incoming connection
conn, err := tun.listener.Accept() conn, err := tun.listener.Accept()
if err != nil { if err != nil {
tun.log.Errorln("TUN/TAP connection accept error:", err) tun.log.Errorln("TUN connection accept error:", err)
return err return err
} }
phony.Block(tun, func() { phony.Block(tun, func() {
if _, err := tun._wrap(conn.(*yggdrasil.Conn)); err != nil { if _, err := tun._wrap(conn.(*yggdrasil.Conn)); err != nil {
// Something went wrong when storing the connection, typically that // Something went wrong when storing the connection, typically that
// something already exists for this address or subnet // something already exists for this address or subnet
tun.log.Debugln("TUN/TAP handler wrap:", err) tun.log.Debugln("TUN handler wrap:", err)
} }
}) })
} }

View File

@ -1,4 +1,4 @@
// +build openbsd freebsd netbsd // +build openbsd freebsd
package tuntap package tuntap
@ -12,7 +12,7 @@ import (
"golang.org/x/sys/unix" "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 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 ifru_addrlifetime in6_addrlifetime
} }
// Sets the IPv6 address of the utun adapter. On all BSD platforms (FreeBSD, // Configures the TUN adapter with the correct IPv6 address and MTU.
// OpenBSD, NetBSD) an attempt is made to set the adapter properties by using func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error {
// a system socket and making syscalls to the kernel. This is not refined though iface, err := wgtun.CreateTUN(ifname, mtu)
// 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)
if err != nil { if err != nil {
panic(err) panic(err)
} }
tun.iface = iface 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) return tun.setupAddress(addr)
} }
@ -114,13 +98,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
} }
// Friendly output // 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 IPv6: %s", addr)
tun.log.Infof("Interface MTU: %d", tun.mtu) tun.log.Infof("Interface MTU: %d", tun.mtu)
// Create the MTU request // Create the MTU request
var ir in6_ifreq_mtu var ir in6_ifreq_mtu
copy(ir.ifr_name[:], tun.iface.Name()) copy(ir.ifr_name[:], tun.Name())
ir.ifru_mtu = int(tun.mtu) ir.ifru_mtu = int(tun.mtu)
// Set the MTU // Set the MTU
@ -129,7 +113,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
// Fall back to ifconfig to set the MTU // 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, " ")) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {
@ -141,7 +125,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
// Create the address request // Create the address request
// FIXME: I don't work! // FIXME: I don't work!
var ar in6_ifreq_addr 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_len = uint8(unsafe.Sizeof(ar.ifru_addr))
ar.ifru_addr.sin6_family = unix.AF_INET6 ar.ifru_addr.sin6_family = unix.AF_INET6
parts := strings.Split(strings.Split(addr, "/")[0], ":") 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) tun.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno)
// Fall back to ifconfig to set the address // 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, " ")) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {

View File

@ -12,22 +12,24 @@ import (
"golang.org/x/sys/unix" "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. // Configures the "utun" adapter with the correct IPv6 address and MTU.
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error {
if iftapmode { if ifname == "auto" {
tun.log.Warnln("Warning: TAP mode is not supported on this platform, defaulting to TUN") ifname = "utun"
iftapmode = false
} }
config := water.Config{DeviceType: water.TUN} iface, err := wgtun.CreateTUN(ifname, mtu)
iface, err := water.New(config)
if err != nil { if err != nil {
panic(err) panic(err)
} }
tun.iface = iface 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) return tun.setupAddress(addr)
} }
@ -80,7 +82,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
} }
var ar in6_aliasreq 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)) ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask))
b := make([]byte, 16) b := make([]byte, 16)
@ -104,7 +106,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
ar.ifra_lifetime.ia6t_pltime = darwin_ND6_INFINITE_LIFETIME ar.ifra_lifetime.ia6t_pltime = darwin_ND6_INFINITE_LIFETIME
var ir ifreq var ir ifreq
copy(ir.ifr_name[:], tun.iface.Name()) copy(ir.ifr_name[:], tun.Name())
ir.ifru_mtu = uint32(tun.mtu) ir.ifru_mtu = uint32(tun.mtu)
tun.log.Infof("Interface name: %s", ar.ifra_name) tun.log.Infof("Interface name: %s", ar.ifra_name)

View File

@ -6,31 +6,24 @@ package tuntap
import ( import (
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
wgtun "golang.zx2c4.com/wireguard/tun"
water "github.com/yggdrasil-network/water"
) )
// Configures the TAP adapter with the correct IPv6 address and MTU. // Configures the TUN adapter with the correct IPv6 address and MTU.
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error {
var config water.Config if ifname == "auto" {
if iftapmode { ifname = "\000"
config = water.Config{DeviceType: water.TAP}
} else {
config = water.Config{DeviceType: water.TUN}
} }
if ifname != "" && ifname != "auto" { iface, err := wgtun.CreateTUN(ifname, mtu)
config.Name = ifname
}
iface, err := water.New(config)
if err != nil { if err != nil {
panic(err) panic(err)
} }
tun.iface = iface tun.iface = iface
tun.mtu = getSupportedMTU(mtu, iftapmode) if mtu, err := iface.MTU(); err == nil {
// Friendly output tun.mtu = getSupportedMTU(mtu)
tun.log.Infof("Interface name: %s", tun.iface.Name()) } else {
tun.log.Infof("Interface IPv6: %s", addr) tun.mtu = 0
tun.log.Infof("Interface MTU: %d", tun.mtu) }
return tun.setupAddress(addr) return tun.setupAddress(addr)
} }
@ -43,7 +36,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
if err != nil { if err != nil {
return err return err
} }
nlintf, err := netlink.LinkByName(tun.iface.Name()) nlintf, err := netlink.LinkByName(tun.Name())
if err != nil { if err != nil {
return err return err
} }
@ -56,5 +49,9 @@ func (tun *TunAdapter) setupAddress(addr string) error {
if err := netlink.LinkSetUp(nlintf); err != nil { if err := netlink.LinkSetUp(nlintf); err != nil {
return err 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 return nil
} }

View File

@ -1,33 +1,32 @@
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile // +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile
package tuntap package tuntap
import water "github.com/yggdrasil-network/water"
// This is to catch unsupported platforms // This is to catch unsupported platforms
// If your platform supports tun devices, you could try configuring it manually // 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 import (
// no guarantees are made at this point on an unsupported platform. wgtun "golang.zx2c4.com/wireguard/tun"
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { )
var config water.Config
if iftapmode { // Configures the TUN adapter with the correct IPv6 address and MTU.
config = water.Config{DeviceType: water.TAP} func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error {
} else { iface, err := wgtun.CreateTUN(ifname, mtu)
config = water.Config{DeviceType: water.TUN}
}
iface, err := water.New(config)
if err != nil { if err != nil {
panic(err) panic(err)
} }
tun.iface = iface 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) return tun.setupAddress(addr)
} }
// We don't know how to set the IPv6 address on an unknown platform, therefore // 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. // write about it to stdout and don't try to do anything further.
func (tun *TunAdapter) setupAddress(addr string) error { 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 return nil
} }

View File

@ -1,116 +1,150 @@
// +build windows
package tuntap package tuntap
import ( import (
"bytes"
"errors" "errors"
"fmt" "log"
"os/exec" "net"
"strings"
"time"
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 // This is to catch Windows platforms
// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows // Configures the TUN adapter with the correct IPv6 address and MTU.
// we don't make use of a direct operating system API to do this - we instead func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error {
// 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"
if ifname == "auto" { if ifname == "auto" {
config.PlatformSpecificParams.InterfaceName = "" ifname = defaults.GetDefaults().DefaultIfName
} else {
config.PlatformSpecificParams.InterfaceName = ifname
} }
iface, err := water.New(config) return elevate.DoAsSystem(func() error {
if err != nil { var err error
return err var iface wgtun.Device
} var guid windows.GUID
if iface.Name() == "" { if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil {
return errors.New("unable to find TAP adapter with component ID " + config.PlatformSpecificParams.ComponentID) return err
} }
// Reset the adapter - this invalidates iface so we'll need to get a new one if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu); err != nil {
cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED") return err
tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) }
output, err := cmd.CombinedOutput() tun.iface = iface
if err != nil { if err = tun.setupAddress(addr); err != nil {
tun.log.Errorln("Windows netsh failed:", err) tun.log.Errorln("Failed to set up TUN address:", err)
tun.log.Traceln(string(output)) return err
return err }
} if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil {
time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect tun.log.Errorln("Failed to set up TUN MTU:", err)
// Bring the interface back up return err
cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") }
tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) if mtu, err = iface.MTU(); err == nil {
output, err = cmd.CombinedOutput() tun.mtu = mtu
if err != nil { }
tun.log.Errorln("Windows netsh failed:", err) return nil
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)
} }
// Sets the MTU of the TAP adapter. // Sets the MTU of the TAP adapter.
func (tun *TunAdapter) setupMTU(mtu int) error { func (tun *TunAdapter) setupMTU(mtu int) error {
if tun.iface == nil || tun.iface.Name() == "" { if tun.iface == nil || tun.Name() == "" {
return errors.New("Can't configure MTU as TAP adapter is not present") return errors.New("Can't configure MTU as TUN adapter is not present")
} }
// Set MTU if intf, ok := tun.iface.(*wgtun.NativeTun); ok {
cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface", luid := winipcfg.LUID(intf.LUID())
fmt.Sprintf("interface=%s", tun.iface.Name()), ipfamily, err := luid.IPInterface(windows.AF_INET6)
fmt.Sprintf("mtu=%d", mtu), if err != nil {
"store=active") return err
tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) }
output, err := cmd.CombinedOutput()
if err != nil { ipfamily.NLMTU = uint32(mtu)
tun.log.Errorln("Windows netsh failed:", err) intf.ForceMTU(int(ipfamily.NLMTU))
tun.log.Traceln(string(output)) ipfamily.UseAutomaticMetric = false
return err 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 return nil
} }
// Sets the IPv6 address of the TAP adapter. // Sets the IPv6 address of the TAP adapter.
func (tun *TunAdapter) setupAddress(addr string) error { func (tun *TunAdapter) setupAddress(addr string) error {
if tun.iface == nil || tun.iface.Name() == "" { if tun.iface == nil || tun.Name() == "" {
return errors.New("Can't configure IPv6 address as TAP adapter is not present") return errors.New("Can't configure IPv6 address as TUN adapter is not present")
} }
// Set address if intf, ok := tun.iface.(*wgtun.NativeTun); ok {
cmd := exec.Command("netsh", "interface", "ipv6", "add", "address", if ipaddr, ipnet, err := net.ParseCIDR(addr); err == nil {
fmt.Sprintf("interface=%s", tun.iface.Name()), luid := winipcfg.LUID(intf.LUID())
fmt.Sprintf("addr=%s", addr), addresses := append([]net.IPNet{}, net.IPNet{
"store=active") IP: ipaddr,
tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) Mask: ipnet.Mask,
output, err := cmd.CombinedOutput() })
if err != nil {
tun.log.Errorln("Windows netsh failed:", err) err := luid.SetIPAddressesForFamily(windows.AF_INET6, addresses)
tun.log.Traceln(string(output)) if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
return err 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 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)
}
}
}
}