mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-25 09:31:34 +00:00
commit
c3f8db6991
@ -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: |
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -25,6 +25,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- in case of vulnerabilities.
|
||||
-->
|
||||
|
||||
## [0.3.13] - 2020-02-21
|
||||
### Added
|
||||
- Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows
|
||||
- Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver)
|
||||
- NodeInfo code is now actorised, should be more reliable
|
||||
- The DHT now tries to store the two closest nodes in either direction instead of one, such that if a node goes offline, the replacement is already known
|
||||
- The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID
|
||||
|
||||
### Changed
|
||||
- The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified
|
||||
- DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time
|
||||
- DHT search results are now sorted
|
||||
- The systemd service now handles configuration generation in a different unit
|
||||
- The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses
|
||||
|
||||
### Fixed
|
||||
- The multicast code no longer panics when shutting down the node
|
||||
- A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed
|
||||
- A bug resulting in incorrect idle notifications in the switch should now be fixed
|
||||
- MTUs are now using a common datatype throughout the codebase
|
||||
|
||||
### Removed
|
||||
- TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config!
|
||||
- NetBSD support has been removed until the Wireguard TUN package supports NetBSD
|
||||
|
||||
## [0.3.12] - 2019-11-24
|
||||
### Added
|
||||
- New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU`
|
||||
@ -66,7 +91,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
### Changed
|
||||
- On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load
|
||||
- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attemps to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do
|
||||
- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do
|
||||
|
||||
### Fixed
|
||||
- The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load
|
||||
@ -90,7 +115,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds
|
||||
- A memory leak in the add-peer loop has been fixed
|
||||
- The admin socket now reports the correct URI strings for SOCKS peers in `getPeers`
|
||||
- A race condition when dialling a remote node by both the node address and routed prefix simultaneously has been fixed
|
||||
- A race condition when dialing a remote node by both the node address and routed prefix simultaneously has been fixed
|
||||
- A race condition between the router and the dial code resulting in a panic has been fixed
|
||||
- A panic which could occur when the TUN/TAP interface disappears (e.g. during soft-shutdown) has been fixed
|
||||
- A bug in the semantic versioning script which accompanies Yggdrasil for builds has been fixed
|
||||
@ -180,7 +205,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
## [0.3.4] - 2019-03-12
|
||||
### Added
|
||||
- Support for multiple listeners (although currently only TCP listeners are supported)
|
||||
- New multicast behaviour where each multicast interface is given it's own link-local listener and does not depend on the `Listen` configuration
|
||||
- New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration
|
||||
- Blocking detection in the switch to avoid parenting a blocked peer
|
||||
- Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime
|
||||
- Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash
|
||||
@ -374,7 +399,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- Wire format changes (backwards incompatible).
|
||||
- Less maintenance traffic per peer.
|
||||
- Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers).
|
||||
- Iterative DHT (added some time between v0.1.0 and here).
|
||||
- Iterative DHT (added sometime between v0.1.0 and here).
|
||||
- Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet.
|
||||
|
||||
### Removed
|
||||
|
@ -34,7 +34,6 @@ some of the below:
|
||||
- Windows
|
||||
- FreeBSD
|
||||
- OpenBSD
|
||||
- NetBSD
|
||||
- OpenWrt
|
||||
|
||||
Please see our [Platforms](https://yggdrasil-network.github.io/platforms.html) pages for more
|
||||
|
20
appveyor.yml
Normal file
20
appveyor.yml
Normal 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'
|
3
build
3
build
@ -9,7 +9,7 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||
ARGS="-v"
|
||||
|
||||
while getopts "uaitc:l:dro:" option
|
||||
while getopts "uaitc:l:dro:p" option
|
||||
do
|
||||
case "$option"
|
||||
in
|
||||
@ -22,6 +22,7 @@ do
|
||||
d) ARGS="$ARGS -tags debug" DEBUG=true;;
|
||||
r) ARGS="$ARGS -race";;
|
||||
o) ARGS="$ARGS -o $OPTARG";;
|
||||
p) ARGS="$ARGS -buildmode=pie";;
|
||||
esac
|
||||
done
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
This file generates crypto keys.
|
||||
It prints out a new set of keys each time if finds a "better" one.
|
||||
By default, "better" means a higher NodeID (-> higher IP address).
|
||||
This is because the IP address format can compress leading 1s in the address, to incrase the number of ID bits in the address.
|
||||
This is because the IP address format can compress leading 1s in the address, to increase the number of ID bits in the address.
|
||||
|
||||
If run with the "-sig" flag, it generates signing keys instead.
|
||||
A "better" signing key means one with a higher TreeID.
|
||||
|
@ -132,6 +132,32 @@ func doGenconf(isjson bool) string {
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
func setLogLevel(loglevel string, logger *log.Logger) {
|
||||
levels := [...]string{"error", "warn", "info", "debug", "trace"}
|
||||
loglevel = strings.ToLower(loglevel)
|
||||
|
||||
contains := func() bool {
|
||||
for _, l := range levels {
|
||||
if l == loglevel {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !contains() { // set default log level
|
||||
logger.Infoln("Loglevel parse failed. Set default level(info)")
|
||||
loglevel = "info"
|
||||
}
|
||||
|
||||
for _, l := range levels {
|
||||
logger.EnableLevel(l)
|
||||
if l == loglevel {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The main function is responsible for configuring and starting Yggdrasil.
|
||||
func main() {
|
||||
// Configure the command line parameters.
|
||||
@ -142,10 +168,10 @@ func main() {
|
||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||
ver := flag.Bool("version", false, "prints the version of this build")
|
||||
logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable")
|
||||
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
||||
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
|
||||
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
|
||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||
flag.Parse()
|
||||
|
||||
var cfg *config.NodeConfig
|
||||
@ -239,20 +265,9 @@ func main() {
|
||||
logger = log.New(os.Stdout, "", log.Flags())
|
||||
logger.Warnln("Logging defaulting to stdout")
|
||||
}
|
||||
//logger.EnableLevel("error")
|
||||
//logger.EnableLevel("warn")
|
||||
//logger.EnableLevel("info")
|
||||
if levels := strings.Split(*logging, ","); len(levels) > 0 {
|
||||
for _, level := range levels {
|
||||
l := strings.TrimSpace(level)
|
||||
switch l {
|
||||
case "error", "warn", "info", "trace", "debug":
|
||||
logger.EnableLevel(l)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLogLevel(*loglevel, logger)
|
||||
|
||||
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
||||
// don't need to create this manually.
|
||||
n := node{}
|
||||
|
@ -25,14 +25,20 @@ import (
|
||||
type admin_info map[string]interface{}
|
||||
|
||||
func main() {
|
||||
// makes sure we can use defer and still return an error code to the OS
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
func run() int {
|
||||
logbuffer := &bytes.Buffer{}
|
||||
logger := log.New(logbuffer, "", log.Flags())
|
||||
defer func() {
|
||||
defer func() int {
|
||||
if r := recover(); r != nil {
|
||||
logger.Println("Fatal error:", r)
|
||||
fmt.Print(logbuffer)
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}()
|
||||
|
||||
endpoint := defaults.GetDefaults().DefaultAdminListen
|
||||
@ -62,12 +68,12 @@ func main() {
|
||||
fmt.Println("Build name:", version.BuildName())
|
||||
fmt.Println("Build version:", version.BuildVersion())
|
||||
fmt.Println("To get the version number of the running Yggdrasil node, run", os.Args[0], "getSelf")
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
flag.Usage()
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
if *server == endpoint {
|
||||
@ -176,15 +182,15 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("Admin socket returned an error but didn't specify any error text")
|
||||
}
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
if _, ok := recv["request"]; !ok {
|
||||
fmt.Println("Missing request in response (malformed response?)")
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
if _, ok := recv["response"]; !ok {
|
||||
fmt.Println("Missing response body (malformed response?)")
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
req := recv["request"].(map[string]interface{})
|
||||
res := recv["response"].(map[string]interface{})
|
||||
@ -193,7 +199,7 @@ func main() {
|
||||
if json, err := json.MarshalIndent(res, "", " "); err == nil {
|
||||
fmt.Println(string(json))
|
||||
}
|
||||
os.Exit(0)
|
||||
return 0
|
||||
}
|
||||
|
||||
switch strings.ToLower(req["request"].(string)) {
|
||||
@ -434,7 +440,7 @@ func main() {
|
||||
}
|
||||
|
||||
if v, ok := recv["status"]; ok && v != "success" {
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
os.Exit(0)
|
||||
return 0
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ WORKDIR /src
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
RUN apk add git && ./build
|
||||
RUN apk add git && ./build && go build -o /src/genkeys cmd/genkeys/main.go
|
||||
|
||||
FROM docker.io/alpine
|
||||
LABEL maintainer="Christer Waren/CWINFO <christer.waren@cwinfo.org>"
|
||||
|
||||
COPY --from=builder /src/yggdrasil /usr/bin/yggdrasil
|
||||
COPY --from=builder /src/yggdrasilctl /usr/bin/yggdrasilctl
|
||||
COPY --from=builder /src/genkeys /usr/bin/genkeys
|
||||
COPY contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh
|
||||
|
||||
# RUN addgroup -g 1000 -S yggdrasil-network \
|
||||
|
224
contrib/msi/build-msi.sh
Normal file
224
contrib/msi/build-msi.sh
Normal file
@ -0,0 +1,224 @@
|
||||
#!/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 > updateconfig.bat << EOF
|
||||
if not exist %ALLUSERSPROFILE%\\Yggdrasil (
|
||||
mkdir %ALLUSERSPROFILE%\\Yggdrasil
|
||||
)
|
||||
if not exist %ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf (
|
||||
if exist yggdrasil.exe (
|
||||
yggdrasil.exe -genconf > %ALLUSERSPROFILE%\\Yggdrasil\\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
|
||||
|
||||
if [ $PKGNAME != "master" ]; then
|
||||
PKGDISPLAYNAME="Yggdrasil Network (${PKGNAME} branch)"
|
||||
else
|
||||
PKGDISPLAYNAME="Yggdrasil Network"
|
||||
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="${PKGDISPLAYNAME}"
|
||||
Id="*"
|
||||
UpgradeCode="${PKGGUID}"
|
||||
Language="1033"
|
||||
Codepage="1252"
|
||||
Version="${PKGVERSIONMS}"
|
||||
Manufacturer="github.com/yggdrasil-network">
|
||||
|
||||
<Package
|
||||
Id="*"
|
||||
Keywords="Installer"
|
||||
Description="Yggdrasil Network Installer"
|
||||
Comments="Yggdrasil Network standalone router for Windows."
|
||||
Manufacturer="github.com/yggdrasil-network"
|
||||
InstallerVersion="200"
|
||||
InstallScope="perMachine"
|
||||
Languages="1033"
|
||||
Compressed="yes"
|
||||
Platform="${PKGARCH}"
|
||||
SummaryCodepage="1252" />
|
||||
|
||||
<MajorUpgrade
|
||||
AllowDowngrades="yes" />
|
||||
|
||||
<Media
|
||||
Id="1"
|
||||
Cabinet="Media.cab"
|
||||
EmbedCab="yes"
|
||||
CompressionLevel="high" />
|
||||
|
||||
<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 "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf" -logto "%ALLUSERSPROFILE%\\Yggdrasil\\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="updateconfig.bat"
|
||||
KeyPath="yes"/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<Merge
|
||||
Id="Wintun"
|
||||
Language="0"
|
||||
DiskId="1"
|
||||
SourceFile="${PKGMSMNAME}" />
|
||||
</Directory>
|
||||
|
||||
<Feature Id="YggdrasilFeature" Title="Yggdrasil" Level="1">
|
||||
<ComponentRef Id="MainExecutable" />
|
||||
<ComponentRef Id="CtrlExecutable" />
|
||||
<ComponentRef Id="ConfigScript" />
|
||||
</Feature>
|
||||
|
||||
<Feature Id="WintunFeature" Title="Wintun" Level="1">
|
||||
<Condition Level="0">
|
||||
UPGRADINGPRODUCTCODE
|
||||
</Condition>
|
||||
<MergeRef Id="Wintun" />
|
||||
</Feature>
|
||||
|
||||
<CustomAction
|
||||
Id="UpdateGenerateConfig"
|
||||
Directory="YggdrasilInstallFolder"
|
||||
ExeCommand="cmd.exe /c updateconfig.bat"
|
||||
Execute="deferred"
|
||||
Return="check"
|
||||
Impersonate="yes" />
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom
|
||||
Action="UpdateGenerateConfig"
|
||||
Before="StartServices">
|
||||
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 -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj
|
13
contrib/systemd/yggdrasil-default-config.service
Normal file
13
contrib/systemd/yggdrasil-default-config.service
Normal file
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=yggdrasil default config generator
|
||||
ConditionPathExists=|!/etc/yggdrasil.conf
|
||||
ConditionFileNotEmpty=|!/etc/yggdrasil.conf
|
||||
Wants=local-fs.target
|
||||
After=local-fs.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
Group=yggdrasil
|
||||
StandardOutput=file:/etc/yggdrasil.conf
|
||||
ExecStart=/usr/bin/yggdrasil -genconf
|
||||
ExecStartPost=/usr/bin/chmod 0640 /etc/yggdrasil.conf
|
@ -1,20 +1,17 @@
|
||||
[Unit]
|
||||
Description=yggdrasil
|
||||
Wants=network.target
|
||||
Wants=yggdrasil-default-config.service
|
||||
After=network.target
|
||||
After=yggdrasil-default-config.service
|
||||
|
||||
[Service]
|
||||
Group=yggdrasil
|
||||
ProtectHome=true
|
||||
ProtectSystem=true
|
||||
SyslogIdentifier=yggdrasil
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
|
||||
ExecStartPre=+-/sbin/modprobe tun
|
||||
ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \
|
||||
then umask 077; \
|
||||
yggdrasil -genconf > /etc/yggdrasil.conf; \
|
||||
echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \
|
||||
fi"
|
||||
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
|
@ -65,12 +65,12 @@ Given the coordinates of any two nodes, it is possible to calculate the length o
|
||||
Traffic is forwarded using a [greedy routing](https://en.wikipedia.org/wiki/Small-world_routing#Greedy_routing) scheme, where each node forwards the packet to a one-hop neighbor that is closer to the destination (according to this distance metric) than the current node.
|
||||
In particular, when a packet needs to be forwarded, a node will forward it to whatever peer is closest to the destination in the greedy [metric space](https://en.wikipedia.org/wiki/Metric_space) used by the network, provided that the peer is closer to the destination than the current node.
|
||||
|
||||
If no closer peers are idle, then the packet is queued in FIFO order, with separate queues per destination coords (currently, as a bit of a hack, IPv6 flow labels are embedeed after the end of the significant part of the coords, so queues distinguish between different traffic streams with the same destination).
|
||||
If no closer peers are idle, then the packet is queued in FIFO order, with separate queues per destination coords (currently, as a bit of a hack, IPv6 flow labels are embedded after the end of the significant part of the coords, so queues distinguish between different traffic streams with the same destination).
|
||||
Whenever the node finishes forwarding a packet to a peer, it checks the queues, and will forward the first packet from the queue with the maximum `<age of first packet>/<queue size in bytes>`, i.e. the bandwidth the queue is attempting to use, subject to the constraint that the peer is a valid next hop (i.e. closer to the destination than the current node).
|
||||
If no non-empty queue is available, then the peer is added to the idle set, forward packets when the need arises.
|
||||
|
||||
This acts as a crude approximation of backpressure routing, where the remote queue sizes are assumed to be equal to the distance of a node from a destination (rather than communicating queue size information), and packets are never forwarded "backwards" through the network, but congestion on a local link is routed around when possible.
|
||||
The queue selection strategy behaves similar to shortest-queue-first, in that a larger fration of available bandwith to sessions that attempt to use less bandwidth, and is loosely based on the rationale behind some proposed solutions to the [cake-cutting](https://en.wikipedia.org/wiki/Fair_cake-cutting) problem.
|
||||
The queue selection strategy behaves similar to shortest-queue-first, in that a larger fraction of available bandwidth to sessions that attempt to use less bandwidth, and is loosely based on the rationale behind some proposed solutions to the [cake-cutting](https://en.wikipedia.org/wiki/Fair_cake-cutting) problem.
|
||||
|
||||
The queue size is limited to 4 MB. If a packet is added to a queue and the total size of all queues is larger than this threshold, then a random queue is selected (with odds proportional to relative queue sizes), and the first packet from that queue is dropped, with the process repeated until the total queue size drops below the allowed threshold.
|
||||
|
||||
|
11
go.mod
11
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-20191227163750-53104e6ec876
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
|
||||
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7
|
||||
golang.org/x/text v0.3.2
|
||||
golang.zx2c4.com/wireguard v0.0.20200121
|
||||
golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25
|
||||
)
|
||||
|
34
go.sum
34
go.sum
@ -8,29 +8,39 @@ 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-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/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-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/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-20200103143344-a1369afcdac7 h1:/W9OPMnnpmFXHYkcp2rQsbFUbRlRzfECQjmAFiOyHE8=
|
||||
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/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 v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
|
||||
golang.zx2c4.com/wireguard v0.0.20200121/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=
|
||||
|
@ -793,7 +793,7 @@ def timelineDimesTest():
|
||||
store = makeStoreDimesEdges(path, bestRoot)
|
||||
rootID = "R" + bestRoot[1:]
|
||||
assert rootID in store
|
||||
# Don't forget to set random seed before setitng times
|
||||
# Don't forget to set random seed before setting times
|
||||
# To make results reproducible
|
||||
nodeIDs = sorted(store.keys())
|
||||
random.seed(12345)
|
||||
|
@ -424,7 +424,7 @@ func main() {
|
||||
//*/
|
||||
startNetwork(kstore)
|
||||
//time.Sleep(10*time.Second)
|
||||
// Note that testPaths only works if pressure is turend off
|
||||
// Note that testPaths only works if pressure is turned off
|
||||
// Otherwise congestion can lead to routing loops?
|
||||
for finished := false; !finished; {
|
||||
finished = testPaths(kstore)
|
||||
|
@ -15,9 +15,9 @@ type Address [16]byte
|
||||
type Subnet [8]byte
|
||||
|
||||
// GetPrefix returns the address prefix used by yggdrasil.
|
||||
// The current implementation requires this to be a muliple of 8 bits + 7 bits.
|
||||
// The current implementation requires this to be a multiple of 8 bits + 7 bits.
|
||||
// The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1).
|
||||
// Nodes that configure this differently will be unable to communicate with eachother using IP packets, though routing and the DHT machinery *should* still work.
|
||||
// Nodes that configure this differently will be unable to communicate with each other using IP packets, though routing and the DHT machinery *should* still work.
|
||||
func GetPrefix() [1]byte {
|
||||
return [...]byte{0x02}
|
||||
}
|
||||
|
@ -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
|
||||
@ -22,8 +22,11 @@ import (
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/types"
|
||||
)
|
||||
|
||||
type MTU = types.MTU
|
||||
|
||||
// NodeState represents the active and previous configuration of an Yggdrasil
|
||||
// node. A NodeState object is returned when starting an Yggdrasil node. Note
|
||||
// that this structure and related functions are likely to disappear soon.
|
||||
@ -70,9 +73,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 MTU `comment:"Maximum 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 +129,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
|
||||
|
@ -194,6 +194,16 @@ type BoxSharedKey [BoxSharedKeyLen]byte
|
||||
// BoxNonce is the nonce used in NaCl-like crypto "box" operations (curve25519+xsalsa20+poly1305), and must not be reused for different messages encrypted using the same BoxSharedKey.
|
||||
type BoxNonce [BoxNonceLen]byte
|
||||
|
||||
// String returns a string representation of the "box" key.
|
||||
func (k BoxPubKey) String() string {
|
||||
return hex.EncodeToString(k[:])
|
||||
}
|
||||
|
||||
// Network returns "curve25519" for "box" keys.
|
||||
func (n BoxPubKey) Network() string {
|
||||
return "curve25519"
|
||||
}
|
||||
|
||||
// NewBoxKeys generates a new pair of public/private crypto box keys.
|
||||
func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) {
|
||||
pubBytes, privBytes, err := box.GenerateKey(rand.Reader)
|
||||
@ -215,7 +225,7 @@ func GetSharedKey(myPrivKey *BoxPrivKey,
|
||||
return (*BoxSharedKey)(&shared)
|
||||
}
|
||||
|
||||
// BoxOpen returns a message and true if it successfull opens a crypto box using the provided shared key and nonce.
|
||||
// BoxOpen returns a message and true if it successfully opens a crypto box using the provided shared key and nonce.
|
||||
func BoxOpen(shared *BoxSharedKey,
|
||||
boxed []byte,
|
||||
nonce *BoxNonce) ([]byte, bool) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package defaults
|
||||
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/types"
|
||||
|
||||
// Defines which parameters are expected by default for configuration on a
|
||||
// specific platform. These values are populated in the relevant defaults_*.go
|
||||
// for the platform being targeted. They must be set.
|
||||
@ -14,8 +16,7 @@ type platformDefaultParameters struct {
|
||||
DefaultMulticastInterfaces []string
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU int
|
||||
DefaultIfMTU int
|
||||
DefaultIfName string
|
||||
DefaultIfTAPMode bool
|
||||
MaximumIfMTU types.MTU
|
||||
DefaultIfMTU types.MTU
|
||||
DefaultIfName string
|
||||
}
|
||||
|
@ -19,9 +19,8 @@ func GetDefaults() platformDefaultParameters {
|
||||
},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "auto",
|
||||
DefaultIfTAPMode: false,
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "auto",
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
|
||||
},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "auto",
|
||||
DefaultIfTAPMode: false,
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "auto",
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters {
|
||||
},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "auto",
|
||||
DefaultIfTAPMode: true,
|
||||
MaximumIfMTU: 65535,
|
||||
DefaultIfMTU: 65535,
|
||||
DefaultIfName: "Yggdrasil",
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ func (m *Multicast) Stop() error {
|
||||
err = m._stop()
|
||||
})
|
||||
m.log.Debugln("Stopped multicast module")
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Multicast) _stop() error {
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -63,7 +63,7 @@ func (s *tunConn) _read(bs []byte) (err error) {
|
||||
default:
|
||||
isCGA = false
|
||||
}
|
||||
// Check destiantion addresses
|
||||
// Check destination addresses
|
||||
switch {
|
||||
case ipv6 && bs[24] == 0x02 && bytes.Equal(s.tun.addr[:16], bs[24:40]): // destination
|
||||
case ipv6 && bs[24] == 0x03 && bytes.Equal(s.tun.subnet[:8], bs[24:32]): // destination
|
||||
@ -92,8 +92,7 @@ func (s *tunConn) _read(bs []byte) (err error) {
|
||||
// The destination address isn't in our CKR allowed range
|
||||
skip = true
|
||||
} else if key, err := s.tun.ckr.getPublicKeyForAddress(srcAddr, addrlen); err == nil {
|
||||
srcNodeID := crypto.GetNodeID(&key)
|
||||
if *s.conn.RemoteAddr().(*crypto.NodeID) == *srcNodeID {
|
||||
if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key {
|
||||
// This is the one allowed CKR case, where source and destination addresses are both good
|
||||
} else {
|
||||
// The CKR key associated with this address doesn't match the sender's NodeID
|
||||
@ -169,8 +168,7 @@ func (s *tunConn) _write(bs []byte) (err error) {
|
||||
// The source address isn't in our CKR allowed range
|
||||
skip = true
|
||||
} else if key, err := s.tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil {
|
||||
dstNodeID := crypto.GetNodeID(&key)
|
||||
if *s.conn.RemoteAddr().(*crypto.NodeID) == *dstNodeID {
|
||||
if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key {
|
||||
// This is the one allowed CKR case, where source and destination addresses are both good
|
||||
} else {
|
||||
// The CKR key associated with this address doesn't match the sender's NodeID
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(), int(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 {
|
||||
|
@ -18,22 +18,24 @@ 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"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/types"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
||||
)
|
||||
|
||||
const tun_IPv6_HEADER_LENGTH = 40
|
||||
const tun_ETHER_HEADER_LENGTH = 14
|
||||
type MTU = types.MTU
|
||||
|
||||
// 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().
|
||||
const tun_IPv6_HEADER_LENGTH = 40
|
||||
|
||||
// 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
|
||||
@ -47,8 +49,8 @@ type TunAdapter struct {
|
||||
subnet address.Subnet
|
||||
ckr cryptokey
|
||||
icmpv6 ICMPv6
|
||||
mtu int
|
||||
iface *water.Interface
|
||||
mtu MTU
|
||||
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 +66,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 MTU) MTU {
|
||||
if mtu < 1280 {
|
||||
return 1280
|
||||
}
|
||||
if mtu > MaximumMTU(istapmode) {
|
||||
return MaximumMTU(istapmode)
|
||||
if mtu > MaximumMTU() {
|
||||
return MaximumMTU()
|
||||
}
|
||||
return mtu
|
||||
}
|
||||
@ -77,55 +79,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())
|
||||
func (tun *TunAdapter) MTU() MTU {
|
||||
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
|
||||
func DefaultMTU() MTU {
|
||||
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() MTU {
|
||||
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 +130,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 +142,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 +159,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.core.SetMaximumSessionMTU(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 +185,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 +197,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 +205,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 +229,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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -270,7 +251,8 @@ func (tun *TunAdapter) _wrap(conn *yggdrasil.Conn) (c *tunConn, err error) {
|
||||
}
|
||||
c = &s
|
||||
// Get the remote address and subnet of the other side
|
||||
remoteNodeID := conn.RemoteAddr().(*crypto.NodeID)
|
||||
remotePubKey := conn.RemoteAddr().(*crypto.BoxPubKey)
|
||||
remoteNodeID := crypto.GetNodeID(remotePubKey)
|
||||
s.addr = *address.AddrForNodeID(remoteNodeID)
|
||||
s.snet = *address.SubnetForNodeID(remoteNodeID)
|
||||
// Work out if this is already a destination we already know about
|
||||
|
@ -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 MTU) error {
|
||||
iface, err := wgtun.CreateTUN(ifname, int(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(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 {
|
||||
|
@ -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 MTU) error {
|
||||
if ifname == "auto" {
|
||||
ifname = "utun"
|
||||
}
|
||||
config := water.Config{DeviceType: water.TUN}
|
||||
iface, err := water.New(config)
|
||||
iface, err := wgtun.CreateTUN(ifname, int(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(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)
|
||||
|
@ -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 MTU) error {
|
||||
if ifname == "auto" {
|
||||
ifname = "\000"
|
||||
}
|
||||
if ifname != "" && ifname != "auto" {
|
||||
config.Name = ifname
|
||||
}
|
||||
iface, err := water.New(config)
|
||||
iface, err := wgtun.CreateTUN(ifname, int(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(mtu))
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
@ -43,18 +36,22 @@ 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
|
||||
}
|
||||
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetMTU(nlintf, tun.mtu); err != nil {
|
||||
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 MTU) 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, int(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(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")
|
||||
func (tun *TunAdapter) setupMTU(mtu MTU) error {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
src/types/types.go
Normal file
3
src/types/types.go
Normal file
@ -0,0 +1,3 @@
|
||||
package types
|
||||
|
||||
type MTU uint16
|
@ -103,11 +103,11 @@ func GetFlowKey(bs []byte) uint64 {
|
||||
// Get the IP protocol version from the packet
|
||||
switch bs[0] & 0xf0 {
|
||||
case 0x40: // IPv4 packet
|
||||
// Check the packet meets minimum UDP packet length
|
||||
if len(bs) >= 24 {
|
||||
// Is the protocol TCP, UDP or SCTP?
|
||||
if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 {
|
||||
ihl := bs[0] & 0x0f * 4 // Header length
|
||||
ihl := (bs[0] & 0x0f) * 4 // whole IPv4 header length (min 20)
|
||||
// 8 is minimum UDP packet length
|
||||
if ihl >= 20 && len(bs)-int(ihl) >= 8 {
|
||||
switch bs[9] /* protocol */ {
|
||||
case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ :
|
||||
flowkey = uint64(bs[9])<<32 /* proto */ |
|
||||
uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ |
|
||||
uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */
|
||||
@ -119,8 +119,8 @@ func GetFlowKey(bs []byte) uint64 {
|
||||
// If the flowlabel isn't present, make protokey from proto | sport | dport
|
||||
// if the packet meets minimum UDP packet length
|
||||
if flowkey == 0 && len(bs) >= 48 {
|
||||
// Is the protocol TCP, UDP or SCTP?
|
||||
if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 {
|
||||
switch bs[9] /* protocol */ {
|
||||
case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ :
|
||||
flowkey = uint64(bs[6])<<32 /* proto */ |
|
||||
uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ |
|
||||
uint64(bs[42])<<8 | uint64(bs[43]) /* dport */
|
||||
|
@ -99,7 +99,7 @@ type Session struct {
|
||||
Coords []uint64 // The coordinates of the remote node
|
||||
BytesSent uint64 // Bytes sent to the session
|
||||
BytesRecvd uint64 // Bytes received from the session
|
||||
MTU uint16 // The maximum supported message size of the session
|
||||
MTU MTU // The maximum supported message size of the session
|
||||
Uptime time.Duration // How long this session has been active for
|
||||
WasMTUFixed bool // This field is no longer used
|
||||
}
|
||||
@ -364,8 +364,8 @@ func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
|
||||
}
|
||||
|
||||
// GetMaximumSessionMTU returns the maximum allowed session MTU size.
|
||||
func (c *Core) GetMaximumSessionMTU() uint16 {
|
||||
var mtu uint16
|
||||
func (c *Core) GetMaximumSessionMTU() MTU {
|
||||
var mtu MTU
|
||||
phony.Block(&c.router, func() {
|
||||
mtu = c.router.sessions.myMaximumMTU
|
||||
})
|
||||
@ -375,7 +375,7 @@ func (c *Core) GetMaximumSessionMTU() uint16 {
|
||||
// SetMaximumSessionMTU sets the maximum allowed session MTU size. The default
|
||||
// value is 65535 bytes. Session pings will be sent to update all open sessions
|
||||
// if the MTU has changed.
|
||||
func (c *Core) SetMaximumSessionMTU(mtu uint16) {
|
||||
func (c *Core) SetMaximumSessionMTU(mtu MTU) {
|
||||
phony.Block(&c.router, func() {
|
||||
if c.router.sessions.myMaximumMTU != mtu {
|
||||
c.router.sessions.myMaximumMTU = mtu
|
||||
@ -390,17 +390,15 @@ func (c *Core) SetMaximumSessionMTU(mtu uint16) {
|
||||
// necessary when, e.g. crawling the network.
|
||||
func (c *Core) GetNodeInfo(key crypto.BoxPubKey, coords []uint64, nocache bool) (NodeInfoPayload, error) {
|
||||
response := make(chan *NodeInfoPayload, 1)
|
||||
sendNodeInfoRequest := func() {
|
||||
c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case response <- nodeinfo:
|
||||
default:
|
||||
}
|
||||
})
|
||||
c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false)
|
||||
}
|
||||
phony.Block(&c.router, sendNodeInfoRequest)
|
||||
c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case response <- nodeinfo:
|
||||
default:
|
||||
}
|
||||
})
|
||||
c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false)
|
||||
phony.Block(&c.router.nodeinfo, func() {}) // Wait for sendNodeInfo before starting timer
|
||||
timer := time.AfterFunc(6*time.Second, func() { close(response) })
|
||||
defer timer.Stop()
|
||||
for res := range response {
|
||||
|
@ -7,11 +7,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/types"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
)
|
||||
|
||||
type MTU = types.MTU
|
||||
|
||||
// ConnError implements the net.Error interface
|
||||
type ConnError struct {
|
||||
error
|
||||
@ -65,7 +68,7 @@ type Conn struct {
|
||||
nodeID *crypto.NodeID
|
||||
nodeMask *crypto.NodeID
|
||||
session *sessionInfo
|
||||
mtu uint16
|
||||
mtu MTU
|
||||
readCallback func([]byte)
|
||||
readBuffer chan []byte
|
||||
}
|
||||
@ -93,7 +96,7 @@ func (c *Conn) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *Conn) setMTU(from phony.Actor, mtu uint16) {
|
||||
func (c *Conn) setMTU(from phony.Actor, mtu MTU) {
|
||||
c.Act(from, func() { c.mtu = mtu })
|
||||
}
|
||||
|
||||
@ -128,7 +131,7 @@ func (c *Conn) search() error {
|
||||
}
|
||||
}
|
||||
sinfo := c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted)
|
||||
sinfo.continueSearch()
|
||||
sinfo.startSearch()
|
||||
} else {
|
||||
err = errors.New("search already exists")
|
||||
close(done)
|
||||
@ -152,7 +155,7 @@ func (c *Conn) doSearch() {
|
||||
sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted)
|
||||
c.core.log.Debugf("%s DHT search started: %p", c.String(), sinfo)
|
||||
// Start the search
|
||||
sinfo.continueSearch()
|
||||
sinfo.startSearch()
|
||||
}
|
||||
}
|
||||
c.core.router.Act(c.session, routerWork)
|
||||
@ -164,7 +167,7 @@ func (c *Conn) _getDeadlineCancellation(t *time.Time) (util.Cancellation, bool)
|
||||
c := util.CancellationWithDeadline(c.session.cancel, *t)
|
||||
return c, true
|
||||
} else {
|
||||
// No deadline was set, so just return the existinc cancellation and a dummy value
|
||||
// No deadline was set, so just return the existing cancellation and a dummy value
|
||||
return c.session.cancel, false
|
||||
}
|
||||
}
|
||||
@ -279,7 +282,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error {
|
||||
}
|
||||
|
||||
// WriteFrom should be called by a phony.Actor, and tells the Conn to send a
|
||||
// message. This is used internaly by Write. If the callback is called with a
|
||||
// message. This is used internally by Write. If the callback is called with a
|
||||
// non-nil value, then it is safe to reuse the argument FlowKeyMessage.
|
||||
func (c *Conn) WriteFrom(from phony.Actor, msg FlowKeyMessage, callback func(error)) {
|
||||
c.Act(from, func() {
|
||||
@ -347,16 +350,19 @@ func (c *Conn) Close() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// LocalAddr returns the complete node ID of the local side of the connection.
|
||||
// This is always going to return your own node's node ID.
|
||||
// LocalAddr returns the complete public key of the local side of the
|
||||
// connection. This is always going to return your own node's public key.
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return crypto.GetNodeID(&c.core.boxPub)
|
||||
return &c.core.boxPub
|
||||
}
|
||||
|
||||
// RemoteAddr returns the complete node ID of the remote side of the connection.
|
||||
// RemoteAddr returns the complete public key of the remote side of the
|
||||
// connection.
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
// RemoteAddr is set during the dial or accept, and isn't changed, so it's safe to access directly
|
||||
return c.nodeID
|
||||
if c.session != nil {
|
||||
return &c.session.theirPermPub
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDeadline is equivalent to calling both SetReadDeadline and
|
||||
|
@ -22,7 +22,7 @@ func GenerateConfig() *config.NodeConfig {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// GetLoggerWithPrefix creates a new logger instance wih prefix.
|
||||
// GetLoggerWithPrefix creates a new logger instance with prefix.
|
||||
// If verbose is set to true, three log levels are enabled: "info", "warn", "error".
|
||||
func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
||||
l := log.New(os.Stderr, prefix, log.Flags())
|
||||
|
@ -20,7 +20,7 @@ const (
|
||||
)
|
||||
|
||||
// dhtInfo represents everything we know about a node in the DHT.
|
||||
// This includes its key, a cache of it's NodeID, coords, and timing/ping related info for deciding who/when to ping nodes for maintenance.
|
||||
// This includes its key, a cache of its NodeID, coords, and timing/ping related info for deciding who/when to ping nodes for maintenance.
|
||||
type dhtInfo struct {
|
||||
nodeID_hidden *crypto.NodeID
|
||||
key crypto.BoxPubKey
|
||||
@ -387,6 +387,8 @@ func (t *dht) getImportant() []*dhtInfo {
|
||||
if dist < minDist {
|
||||
minDist = dist
|
||||
important = append(important, info)
|
||||
} else if len(important) < 2 {
|
||||
important = append(important, info)
|
||||
}
|
||||
}
|
||||
var temp []*dhtInfo
|
||||
@ -397,6 +399,8 @@ func (t *dht) getImportant() []*dhtInfo {
|
||||
if dist < minDist {
|
||||
minDist = dist
|
||||
temp = append(temp, info)
|
||||
} else if len(temp) < 2 {
|
||||
temp = append(temp, info)
|
||||
}
|
||||
}
|
||||
for idx := len(temp) - 1; idx >= 0; idx-- {
|
||||
|
@ -17,19 +17,32 @@ type Dialer struct {
|
||||
core *Core
|
||||
}
|
||||
|
||||
// Dial opens a session to the given node. The first paramter should be "nodeid"
|
||||
// and the second parameter should contain a hexadecimal representation of the
|
||||
// target node ID. It uses DialContext internally.
|
||||
// Dial opens a session to the given node. The first parameter should be
|
||||
// "curve25519" or "nodeid" and the second parameter should contain a
|
||||
// hexadecimal representation of the target. It uses DialContext internally.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||
return d.DialContext(nil, network, address)
|
||||
}
|
||||
|
||||
// DialContext is used internally by Dial, and should only be used with a context that includes a timeout. It uses DialByNodeIDandMask internally.
|
||||
// DialContext is used internally by Dial, and should only be used with a
|
||||
// context that includes a timeout. It uses DialByNodeIDandMask internally when
|
||||
// the network is "nodeid", or DialByPublicKey when the network is "curve25519".
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
var nodeID crypto.NodeID
|
||||
var nodeMask crypto.NodeID
|
||||
// Process
|
||||
switch network {
|
||||
case "curve25519":
|
||||
dest, err := hex.DecodeString(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(dest) != crypto.BoxPubKeyLen {
|
||||
return nil, errors.New("invalid key length supplied")
|
||||
}
|
||||
var pubKey crypto.BoxPubKey
|
||||
copy(pubKey[:], dest)
|
||||
return d.DialByPublicKey(ctx, &pubKey)
|
||||
case "nodeid":
|
||||
// A node ID was provided - we don't need to do anything special with it
|
||||
if tokens := strings.Split(address, "/"); len(tokens) == 2 {
|
||||
@ -62,15 +75,19 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.
|
||||
}
|
||||
}
|
||||
|
||||
// DialByNodeIDandMask opens a session to the given node based on raw
|
||||
// NodeID parameters. If ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, beginning *after* the search finishes.
|
||||
// DialByNodeIDandMask opens a session to the given node based on raw NodeID
|
||||
// parameters. If ctx is nil or has no timeout, then a default timeout of 6
|
||||
// seconds will apply, beginning *after* the search finishes.
|
||||
func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *crypto.NodeID) (net.Conn, error) {
|
||||
startDial := time.Now()
|
||||
conn := newConn(d.core, nodeID, nodeMask, nil)
|
||||
if err := conn.search(); err != nil {
|
||||
// TODO: make searches take a context, so they can be cancelled early
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
endSearch := time.Now()
|
||||
d.core.log.Debugln("Dial searched for:", nodeID, "in time:", endSearch.Sub(startDial))
|
||||
conn.session.setConn(nil, conn)
|
||||
var cancel context.CancelFunc
|
||||
if ctx == nil {
|
||||
@ -80,9 +97,24 @@ func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *cryp
|
||||
defer cancel()
|
||||
select {
|
||||
case <-conn.session.init:
|
||||
endInit := time.Now()
|
||||
d.core.log.Debugln("Dial initialized session for:", nodeID, "in time:", endInit.Sub(endSearch))
|
||||
d.core.log.Debugln("Finished dial for:", nodeID, "in time:", endInit.Sub(startDial))
|
||||
return conn, nil
|
||||
case <-ctx.Done():
|
||||
conn.Close()
|
||||
return nil, errors.New("session handshake timeout")
|
||||
}
|
||||
}
|
||||
|
||||
// DialByPublicKey opens a session to the given node based on the public key. If
|
||||
// ctx is nil or has no timeout, then a default timeout of 6 seconds will apply,
|
||||
// beginning *after* the search finishes.
|
||||
func (d *Dialer) DialByPublicKey(ctx context.Context, pubKey *crypto.BoxPubKey) (net.Conn, error) {
|
||||
nodeID := crypto.GetNodeID(pubKey)
|
||||
var nodeMask crypto.NodeID
|
||||
for i := range nodeMask {
|
||||
nodeMask[i] = 0xFF
|
||||
}
|
||||
return d.DialByNodeIDandMask(ctx, nodeID, &nodeMask)
|
||||
}
|
||||
|
@ -113,9 +113,9 @@ a Dialer:
|
||||
// ...
|
||||
}
|
||||
|
||||
You can then dial using the 16-byte node ID in hexadecimal format, for example:
|
||||
You can then dial using the node's public key in hexadecimal format, for example:
|
||||
|
||||
conn, err := dialer.Dial("nodeid", "24a58cfce691ec016b0f698f7be1bee983cea263781017e99ad3ef62b4ef710a45d6c1a072c5ce46131bd574b78818c9957042cafeeed13966f349e94eb771bf")
|
||||
conn, err := dialer.Dial("curve25519", "55071be281f50d0abbda63aadc59755624280c44b2f1f47684317aa4e0325604")
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ type linkInterface struct {
|
||||
closeTimer *time.Timer // Fires when the link has been idle so long we need to close it
|
||||
inSwitch bool // True if the switch is tracking this link
|
||||
stalled bool // True if we haven't been receiving any response traffic
|
||||
unstalled bool // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up)
|
||||
}
|
||||
|
||||
func (l *link) init(c *Core) error {
|
||||
@ -122,7 +123,7 @@ func (l *link) listen(uri string) error {
|
||||
}
|
||||
|
||||
func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) {
|
||||
// Technically anything unique would work for names, but lets pick something human readable, just for debugging
|
||||
// Technically anything unique would work for names, but let's pick something human readable, just for debugging
|
||||
intf := linkInterface{
|
||||
name: name,
|
||||
link: l,
|
||||
@ -324,11 +325,15 @@ func (intf *linkInterface) notifySent(size int, isLinkTraffic bool) {
|
||||
|
||||
// Notify the switch that we're ready for more traffic, assuming we're not in a stalled state
|
||||
func (intf *linkInterface) _notifySwitch() {
|
||||
if !intf.inSwitch && !intf.stalled {
|
||||
intf.inSwitch = true
|
||||
intf.link.core.switchTable.Act(intf, func() {
|
||||
intf.link.core.switchTable._idleIn(intf.peer.port)
|
||||
})
|
||||
if !intf.inSwitch {
|
||||
if intf.stalled {
|
||||
intf.unstalled = false
|
||||
} else {
|
||||
intf.inSwitch = true
|
||||
intf.link.core.switchTable.Act(intf, func() {
|
||||
intf.link.core.switchTable._idleIn(intf.peer.port)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,7 +367,10 @@ func (intf *linkInterface) notifyRead(size int) {
|
||||
intf.stallTimer = nil
|
||||
}
|
||||
intf.stalled = false
|
||||
intf._notifySwitch()
|
||||
if !intf.unstalled {
|
||||
intf._notifySwitch()
|
||||
intf.unstalled = true
|
||||
}
|
||||
if size > 0 && intf.stallTimer == nil {
|
||||
intf.stallTimer = time.AfterFunc(keepAliveTime, intf.notifyDoKeepAlive)
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func (l *Listener) Close() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr is not implemented for this type yet
|
||||
// Addr returns the address of the listener
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return nil
|
||||
return &l.core.boxPub
|
||||
}
|
||||
|
@ -5,21 +5,19 @@ import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||
)
|
||||
|
||||
type nodeinfo struct {
|
||||
core *Core
|
||||
myNodeInfo NodeInfoPayload
|
||||
myNodeInfoMutex sync.RWMutex
|
||||
callbacks map[crypto.BoxPubKey]nodeinfoCallback
|
||||
callbacksMutex sync.Mutex
|
||||
cache map[crypto.BoxPubKey]nodeinfoCached
|
||||
cacheMutex sync.RWMutex
|
||||
phony.Inbox
|
||||
core *Core
|
||||
myNodeInfo NodeInfoPayload
|
||||
callbacks map[crypto.BoxPubKey]nodeinfoCallback
|
||||
cache map[crypto.BoxPubKey]nodeinfoCached
|
||||
}
|
||||
|
||||
type nodeinfoCached struct {
|
||||
@ -43,35 +41,43 @@ type nodeinfoReqRes struct {
|
||||
// Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep
|
||||
// the cache/callback maps clean of stale entries
|
||||
func (m *nodeinfo) init(core *Core) {
|
||||
m.Act(nil, func() {
|
||||
m._init(core)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _init(core *Core) {
|
||||
m.core = core
|
||||
m.callbacks = make(map[crypto.BoxPubKey]nodeinfoCallback)
|
||||
m.cache = make(map[crypto.BoxPubKey]nodeinfoCached)
|
||||
|
||||
var f func()
|
||||
f = func() {
|
||||
m.callbacksMutex.Lock()
|
||||
for boxPubKey, callback := range m.callbacks {
|
||||
if time.Since(callback.created) > time.Minute {
|
||||
delete(m.callbacks, boxPubKey)
|
||||
}
|
||||
m._cleanup()
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _cleanup() {
|
||||
for boxPubKey, callback := range m.callbacks {
|
||||
if time.Since(callback.created) > time.Minute {
|
||||
delete(m.callbacks, boxPubKey)
|
||||
}
|
||||
m.callbacksMutex.Unlock()
|
||||
m.cacheMutex.Lock()
|
||||
for boxPubKey, cache := range m.cache {
|
||||
if time.Since(cache.created) > time.Hour {
|
||||
delete(m.cache, boxPubKey)
|
||||
}
|
||||
}
|
||||
m.cacheMutex.Unlock()
|
||||
time.AfterFunc(time.Second*30, f)
|
||||
}
|
||||
go f()
|
||||
for boxPubKey, cache := range m.cache {
|
||||
if time.Since(cache.created) > time.Hour {
|
||||
delete(m.cache, boxPubKey)
|
||||
}
|
||||
}
|
||||
time.AfterFunc(time.Second*30, func() {
|
||||
m.Act(nil, m._cleanup)
|
||||
})
|
||||
}
|
||||
|
||||
// Add a callback for a nodeinfo lookup
|
||||
func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) {
|
||||
m.callbacksMutex.Lock()
|
||||
defer m.callbacksMutex.Unlock()
|
||||
m.Act(nil, func() {
|
||||
m._addCallback(sender, call)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) {
|
||||
m.callbacks[sender] = nodeinfoCallback{
|
||||
created: time.Now(),
|
||||
call: call,
|
||||
@ -79,9 +85,7 @@ func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *Node
|
||||
}
|
||||
|
||||
// Handles the callback, if there is one
|
||||
func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) {
|
||||
m.callbacksMutex.Lock()
|
||||
defer m.callbacksMutex.Unlock()
|
||||
func (m *nodeinfo) _callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) {
|
||||
if callback, ok := m.callbacks[sender]; ok {
|
||||
callback.call(&nodeinfo)
|
||||
delete(m.callbacks, sender)
|
||||
@ -89,16 +93,26 @@ func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) {
|
||||
}
|
||||
|
||||
// Get the current node's nodeinfo
|
||||
func (m *nodeinfo) getNodeInfo() NodeInfoPayload {
|
||||
m.myNodeInfoMutex.RLock()
|
||||
defer m.myNodeInfoMutex.RUnlock()
|
||||
func (m *nodeinfo) getNodeInfo() (p NodeInfoPayload) {
|
||||
phony.Block(m, func() {
|
||||
p = m._getNodeInfo()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _getNodeInfo() NodeInfoPayload {
|
||||
return m.myNodeInfo
|
||||
}
|
||||
|
||||
// Set the current node's nodeinfo
|
||||
func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error {
|
||||
m.myNodeInfoMutex.Lock()
|
||||
defer m.myNodeInfoMutex.Unlock()
|
||||
func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) (err error) {
|
||||
phony.Block(m, func() {
|
||||
err = m._setNodeInfo(given, privacy)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _setNodeInfo(given interface{}, privacy bool) error {
|
||||
defaults := map[string]interface{}{
|
||||
"buildname": version.BuildName(),
|
||||
"buildversion": version.BuildVersion(),
|
||||
@ -134,9 +148,7 @@ func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error {
|
||||
}
|
||||
|
||||
// Add nodeinfo into the cache for a node
|
||||
func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) {
|
||||
m.cacheMutex.Lock()
|
||||
defer m.cacheMutex.Unlock()
|
||||
func (m *nodeinfo) _addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) {
|
||||
m.cache[key] = nodeinfoCached{
|
||||
created: time.Now(),
|
||||
payload: payload,
|
||||
@ -144,9 +156,7 @@ func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPaylo
|
||||
}
|
||||
|
||||
// Get a nodeinfo entry from the cache
|
||||
func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) {
|
||||
m.cacheMutex.RLock()
|
||||
defer m.cacheMutex.RUnlock()
|
||||
func (m *nodeinfo) _getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) {
|
||||
if nodeinfo, ok := m.cache[key]; ok {
|
||||
return nodeinfo.payload, nil
|
||||
}
|
||||
@ -154,22 +164,34 @@ func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, err
|
||||
}
|
||||
|
||||
// Handles a nodeinfo request/response - called from the router
|
||||
func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) {
|
||||
func (m *nodeinfo) handleNodeInfo(from phony.Actor, nodeinfo *nodeinfoReqRes) {
|
||||
m.Act(from, func() {
|
||||
m._handleNodeInfo(nodeinfo)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _handleNodeInfo(nodeinfo *nodeinfoReqRes) {
|
||||
if nodeinfo.IsResponse {
|
||||
m.callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||
m.addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||
m._callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||
m._addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||
} else {
|
||||
m.sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true)
|
||||
m._sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Send nodeinfo request or response - called from the router
|
||||
func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) {
|
||||
m.Act(nil, func() {
|
||||
m._sendNodeInfo(key, coords, isResponse)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *nodeinfo) _sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) {
|
||||
table := m.core.switchTable.table.Load().(lookupTable)
|
||||
nodeinfo := nodeinfoReqRes{
|
||||
SendCoords: table.self.getCoords(),
|
||||
IsResponse: isResponse,
|
||||
NodeInfo: m.getNodeInfo(),
|
||||
NodeInfo: m._getNodeInfo(),
|
||||
}
|
||||
bs := nodeinfo.encode()
|
||||
shared := m.core.router.sessions.getSharedKey(&m.core.boxPriv, &key)
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
// The peers struct represents peers with an active connection.
|
||||
// Incoming packets are passed to the corresponding peer, which handles them somehow.
|
||||
// In most cases, this involves passing the packet to the handler for outgoing traffic to another peer.
|
||||
// In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch.
|
||||
// In other cases, its link protocol traffic is used to build the spanning tree, in which case this checks signatures and passes the message along to the switch.
|
||||
type peers struct {
|
||||
core *Core
|
||||
mutex sync.Mutex // Synchronize writes to atomic
|
||||
@ -90,7 +90,7 @@ func (ps *peers) putPorts(ports map[switchPort]*peer) {
|
||||
ps.ports.Store(ports)
|
||||
}
|
||||
|
||||
// Information known about a peer, including thier box/sig keys, precomputed shared keys (static and ephemeral) and a handler for their outgoing traffic
|
||||
// Information known about a peer, including their box/sig keys, precomputed shared keys (static and ephemeral) and a handler for their outgoing traffic
|
||||
type peer struct {
|
||||
phony.Inbox
|
||||
core *Core
|
||||
@ -356,7 +356,7 @@ func (p *peer) _handleSwitchMsg(packet []byte) {
|
||||
p.dinfo = nil
|
||||
return
|
||||
}
|
||||
// Pass a mesage to the dht informing it that this peer (still) exists
|
||||
// Pass a message to the dht informing it that this peer (still) exists
|
||||
loc.coords = loc.coords[:len(loc.coords)-1]
|
||||
p.dinfo = &dhtInfo{
|
||||
key: p.box,
|
||||
|
@ -78,6 +78,7 @@ func (r *router) init(core *Core) {
|
||||
func (r *router) reconfigure() {
|
||||
// Reconfigure the router
|
||||
current := r.core.config.GetCurrent()
|
||||
r.core.log.Println("Reloading NodeInfo...")
|
||||
if err := r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy); err != nil {
|
||||
r.core.log.Errorln("Error reloading NodeInfo:", err)
|
||||
} else {
|
||||
@ -249,5 +250,5 @@ func (r *router) _handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||
return
|
||||
}
|
||||
req.SendPermPub = *fromKey
|
||||
r.nodeinfo.handleNodeInfo(&req)
|
||||
r.nodeinfo.handleNodeInfo(r, &req)
|
||||
}
|
||||
|
@ -22,13 +22,9 @@ import (
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
)
|
||||
|
||||
// This defines the maximum number of dhtInfo that we keep track of for nodes to query in an ongoing search.
|
||||
const search_MAX_SEARCH_SIZE = 16
|
||||
|
||||
// This defines the time after which we send a new search packet.
|
||||
// Search packets are sent automatically immediately after a response is received.
|
||||
// So this allows for timeouts and for long searches to become increasingly parallel.
|
||||
const search_RETRY_TIME = time.Second
|
||||
// This defines the time after which we time out a search (so it can restart).
|
||||
const search_RETRY_TIME = 3 * time.Second
|
||||
const search_STEP_TIME = 100 * time.Millisecond
|
||||
|
||||
// Information about an ongoing search.
|
||||
// Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
||||
@ -37,10 +33,11 @@ type searchInfo struct {
|
||||
dest crypto.NodeID
|
||||
mask crypto.NodeID
|
||||
time time.Time
|
||||
toVisit []*dhtInfo
|
||||
visited map[crypto.NodeID]bool
|
||||
visited crypto.NodeID // Closest address visited so far
|
||||
callback func(*sessionInfo, error)
|
||||
// TODO context.Context for timeout and cancellation
|
||||
send uint64 // log number of requests sent
|
||||
recv uint64 // log number of responses received
|
||||
}
|
||||
|
||||
// This stores a map of active searches.
|
||||
@ -78,100 +75,108 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba
|
||||
// If there is, it adds the response info to the search and triggers a new search step.
|
||||
// If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more.
|
||||
func (sinfo *searchInfo) handleDHTRes(res *dhtRes) {
|
||||
if res == nil || sinfo.checkDHTRes(res) {
|
||||
// Either we don't recognize this search, or we just finished it
|
||||
return
|
||||
}
|
||||
// Add to the search and continue
|
||||
sinfo.addToSearch(res)
|
||||
sinfo.doSearchStep()
|
||||
}
|
||||
|
||||
// Adds the information from a dhtRes to an ongoing search.
|
||||
// Info about a node that has already been visited is not re-added to the search.
|
||||
// Duplicate information about nodes toVisit is deduplicated (the newest information is kept).
|
||||
// The toVisit list is sorted in ascending order of keyspace distance from the destination.
|
||||
func (sinfo *searchInfo) addToSearch(res *dhtRes) {
|
||||
// Add responses to toVisit if closer to dest than the res node
|
||||
from := dhtInfo{key: res.Key, coords: res.Coords}
|
||||
sinfo.visited[*from.getNodeID()] = true
|
||||
for _, info := range res.Infos {
|
||||
if *info.getNodeID() == sinfo.searches.router.dht.nodeID || sinfo.visited[*info.getNodeID()] {
|
||||
continue
|
||||
if res != nil {
|
||||
sinfo.recv++
|
||||
if sinfo.checkDHTRes(res) {
|
||||
return // Search finished successfully
|
||||
}
|
||||
if dht_ordered(&sinfo.dest, info.getNodeID(), from.getNodeID()) {
|
||||
// Response is closer to the destination
|
||||
sinfo.toVisit = append(sinfo.toVisit, info)
|
||||
// Use results to start an additional search thread
|
||||
infos := append([]*dhtInfo(nil), res.Infos...)
|
||||
infos = sinfo.getAllowedInfos(infos)
|
||||
if len(infos) > 0 {
|
||||
sinfo.continueSearch(infos)
|
||||
}
|
||||
}
|
||||
// Deduplicate
|
||||
vMap := make(map[crypto.NodeID]*dhtInfo)
|
||||
for _, info := range sinfo.toVisit {
|
||||
vMap[*info.getNodeID()] = info
|
||||
}
|
||||
sinfo.toVisit = sinfo.toVisit[:0]
|
||||
for _, info := range vMap {
|
||||
sinfo.toVisit = append(sinfo.toVisit, info)
|
||||
}
|
||||
// Sort
|
||||
sort.SliceStable(sinfo.toVisit, func(i, j int) bool {
|
||||
// Should return true if i is closer to the destination than j
|
||||
return dht_ordered(&res.Dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID())
|
||||
})
|
||||
// Truncate to some maximum size
|
||||
if len(sinfo.toVisit) > search_MAX_SEARCH_SIZE {
|
||||
sinfo.toVisit = sinfo.toVisit[:search_MAX_SEARCH_SIZE]
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no nodes left toVisit, then this cleans up the search.
|
||||
// If there has been no response in too long, then this cleans up the search.
|
||||
// Otherwise, it pops the closest node to the destination (in keyspace) off of the toVisit list and sends a dht ping.
|
||||
func (sinfo *searchInfo) doSearchStep() {
|
||||
if len(sinfo.toVisit) == 0 {
|
||||
if time.Since(sinfo.time) > search_RETRY_TIME {
|
||||
// Dead end and no response in too long, do cleanup
|
||||
delete(sinfo.searches.searches, sinfo.dest)
|
||||
sinfo.callback(nil, errors.New("search reached dead end"))
|
||||
}
|
||||
return
|
||||
func (sinfo *searchInfo) doSearchStep(infos []*dhtInfo) {
|
||||
if len(infos) > 0 {
|
||||
// Send to the next search target
|
||||
next := infos[0]
|
||||
rq := dhtReqKey{next.key, sinfo.dest}
|
||||
sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes)
|
||||
sinfo.searches.router.dht.ping(next, &sinfo.dest)
|
||||
sinfo.send++
|
||||
}
|
||||
// Send to the next search target
|
||||
var next *dhtInfo
|
||||
next, sinfo.toVisit = sinfo.toVisit[0], sinfo.toVisit[1:]
|
||||
rq := dhtReqKey{next.key, sinfo.dest}
|
||||
sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes)
|
||||
sinfo.searches.router.dht.ping(next, &sinfo.dest)
|
||||
sinfo.time = time.Now()
|
||||
}
|
||||
|
||||
// If we've recenty sent a ping for this search, do nothing.
|
||||
// Otherwise, doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME.
|
||||
func (sinfo *searchInfo) continueSearch() {
|
||||
sinfo.doSearchStep()
|
||||
// In case the search dies, try to spawn another thread later
|
||||
// Note that this will spawn multiple parallel searches as time passes
|
||||
// Any that die aren't restarted, but a new one will start later
|
||||
time.AfterFunc(search_RETRY_TIME, func() {
|
||||
// Get a list of search targets that are close enough to the destination to try
|
||||
// Requires an initial list as input
|
||||
func (sinfo *searchInfo) getAllowedInfos(infos []*dhtInfo) []*dhtInfo {
|
||||
sort.SliceStable(infos, func(i, j int) bool {
|
||||
// Should return true if i is closer to the destination than j
|
||||
return dht_ordered(&sinfo.dest, infos[i].getNodeID(), infos[j].getNodeID())
|
||||
})
|
||||
// Remove anything too far away to be useful
|
||||
for idx, info := range infos {
|
||||
if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) {
|
||||
infos = infos[:idx]
|
||||
break
|
||||
}
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// Run doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME.
|
||||
// Must not be called with an empty list of infos
|
||||
func (sinfo *searchInfo) continueSearch(infos []*dhtInfo) {
|
||||
sinfo.doSearchStep(infos)
|
||||
infos = infos[1:] // Remove the node we just tried
|
||||
// In case there's no response, try the next node in infos later
|
||||
time.AfterFunc(search_STEP_TIME, func() {
|
||||
sinfo.searches.router.Act(nil, func() {
|
||||
// FIXME this keeps the search alive forever if not for the searches map, fix that
|
||||
newSearchInfo := sinfo.searches.searches[sinfo.dest]
|
||||
if newSearchInfo != sinfo {
|
||||
return
|
||||
}
|
||||
sinfo.continueSearch()
|
||||
// Get good infos here instead of at the top, to make sure we can always start things off with a continueSearch call to ourself
|
||||
infos = sinfo.getAllowedInfos(infos)
|
||||
if len(infos) > 0 {
|
||||
sinfo.continueSearch(infos)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Initially start a search
|
||||
func (sinfo *searchInfo) startSearch() {
|
||||
loc := sinfo.searches.router.core.switchTable.getLocator()
|
||||
var infos []*dhtInfo
|
||||
infos = append(infos, &dhtInfo{
|
||||
key: sinfo.searches.router.core.boxPub,
|
||||
coords: loc.getCoords(),
|
||||
})
|
||||
// Start the search by asking ourself, useful if we're the destination
|
||||
sinfo.continueSearch(infos)
|
||||
// Start a timer to clean up the search if everything times out
|
||||
var cleanupFunc func()
|
||||
cleanupFunc = func() {
|
||||
sinfo.searches.router.Act(nil, func() {
|
||||
// FIXME this keeps the search alive forever if not for the searches map, fix that
|
||||
newSearchInfo := sinfo.searches.searches[sinfo.dest]
|
||||
if newSearchInfo != sinfo {
|
||||
return
|
||||
}
|
||||
elapsed := time.Since(sinfo.time)
|
||||
if elapsed > search_RETRY_TIME {
|
||||
// cleanup
|
||||
delete(sinfo.searches.searches, sinfo.dest)
|
||||
sinfo.callback(nil, errors.New("search reached dead end"))
|
||||
return
|
||||
}
|
||||
time.AfterFunc(search_RETRY_TIME-elapsed, cleanupFunc)
|
||||
})
|
||||
}
|
||||
time.AfterFunc(search_RETRY_TIME, cleanupFunc)
|
||||
}
|
||||
|
||||
// Calls create search, and initializes the iterative search parts of the struct before returning it.
|
||||
func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo {
|
||||
sinfo := s.createSearch(dest, mask, callback)
|
||||
sinfo.visited = make(map[crypto.NodeID]bool)
|
||||
loc := s.router.core.switchTable.getLocator()
|
||||
sinfo.toVisit = append(sinfo.toVisit, &dhtInfo{
|
||||
key: s.router.core.boxPub,
|
||||
coords: loc.getCoords(),
|
||||
}) // Start the search by asking ourself, useful if we're the destination
|
||||
sinfo.visited = s.router.dht.nodeID
|
||||
return sinfo
|
||||
}
|
||||
|
||||
@ -179,7 +184,14 @@ func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callb
|
||||
// If the response is from the target, get/create a session, trigger a session ping, and return true.
|
||||
// Otherwise return false.
|
||||
func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool {
|
||||
them := crypto.GetNodeID(&res.Key)
|
||||
from := dhtInfo{key: res.Key, coords: res.Coords}
|
||||
if *from.getNodeID() != sinfo.visited && dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) {
|
||||
// Closer to the destination, so update visited
|
||||
sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, from.getNodeID(), sinfo.send, sinfo.recv)
|
||||
sinfo.visited = *from.getNodeID()
|
||||
sinfo.time = time.Now()
|
||||
}
|
||||
them := from.getNodeID()
|
||||
var destMasked crypto.NodeID
|
||||
var themMasked crypto.NodeID
|
||||
for idx := 0; idx < crypto.NodeIDLen; idx++ {
|
||||
@ -201,7 +213,10 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool {
|
||||
sinfo.callback(sess, nil)
|
||||
}
|
||||
// Cleanup
|
||||
delete(sinfo.searches.searches, res.Dest)
|
||||
if _, isIn := sinfo.searches.searches[sinfo.dest]; isIn {
|
||||
sinfo.searches.router.core.log.Debugln("Finished search:", &sinfo.dest, sinfo.send, sinfo.recv)
|
||||
delete(sinfo.searches.searches, res.Dest)
|
||||
}
|
||||
}
|
||||
// They match, so create a session and send a sessionRequest
|
||||
var err error
|
||||
|
@ -36,14 +36,13 @@ type sessionInfo struct {
|
||||
myHandle crypto.Handle //
|
||||
theirNonce crypto.BoxNonce //
|
||||
myNonce crypto.BoxNonce //
|
||||
theirMTU uint16 //
|
||||
myMTU uint16 //
|
||||
theirMTU MTU //
|
||||
myMTU MTU //
|
||||
wasMTUFixed bool // Was the MTU fixed by a receive error?
|
||||
timeOpened time.Time // Time the sessino was opened
|
||||
timeOpened time.Time // Time the session was opened
|
||||
time time.Time // Time we last received a packet
|
||||
mtuTime time.Time // time myMTU was last changed
|
||||
pingTime time.Time // time the first ping was sent since the last received packet
|
||||
pingSend time.Time // time the last ping was sent
|
||||
coords []byte // coords of destination
|
||||
reset bool // reset if coords change
|
||||
tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation
|
||||
@ -55,7 +54,7 @@ type sessionInfo struct {
|
||||
callbacks []chan func() // Finished work from crypto workers
|
||||
}
|
||||
|
||||
// Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
|
||||
// Represents a session ping/pong packet, and includes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
|
||||
type sessionPing struct {
|
||||
SendPermPub crypto.BoxPubKey // Sender's permanent key
|
||||
Handle crypto.Handle // Random number to ID session
|
||||
@ -63,7 +62,7 @@ type sessionPing struct {
|
||||
Coords []byte //
|
||||
Tstamp int64 // unix time, but the only real requirement is that it increases
|
||||
IsPong bool //
|
||||
MTU uint16 //
|
||||
MTU MTU //
|
||||
}
|
||||
|
||||
// Updates session info in response to a ping, after checking that the ping is OK.
|
||||
@ -117,7 +116,7 @@ type sessions struct {
|
||||
lastCleanup time.Time
|
||||
isAllowedHandler func(pubkey *crypto.BoxPubKey, initiator bool) bool // Returns true or false if session setup is allowed
|
||||
isAllowedMutex sync.RWMutex // Protects the above
|
||||
myMaximumMTU uint16 // Maximum allowed session MTU
|
||||
myMaximumMTU MTU // Maximum allowed session MTU
|
||||
permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey // Maps known permanent keys to their shared key, used by DHT a lot
|
||||
sinfos map[crypto.Handle]*sessionInfo // Maps handle onto session info
|
||||
byTheirPerm map[crypto.BoxPubKey]*crypto.Handle // Maps theirPermPub onto handle
|
||||
@ -175,7 +174,7 @@ func (ss *sessions) getByTheirPerm(key *crypto.BoxPubKey) (*sessionInfo, bool) {
|
||||
}
|
||||
|
||||
// Creates a new session and lazily cleans up old existing sessions. This
|
||||
// includse initializing session info to sane defaults (e.g. lowest supported
|
||||
// includes initializing session info to sane defaults (e.g. lowest supported
|
||||
// MTU).
|
||||
func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
||||
// TODO: this check definitely needs to be moved
|
||||
@ -197,7 +196,6 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
||||
sinfo.time = now
|
||||
sinfo.mtuTime = now
|
||||
sinfo.pingTime = now
|
||||
sinfo.pingSend = now
|
||||
sinfo.init = make(chan struct{})
|
||||
sinfo.cancel = util.NewCancellation()
|
||||
higher := false
|
||||
@ -385,7 +383,7 @@ func (ss *sessions) handlePing(ping *sessionPing) {
|
||||
// Get the MTU of the session.
|
||||
// Will be equal to the smaller of this node's MTU or the remote node's MTU.
|
||||
// If sending over links with a maximum message size (this was a thing with the old UDP code), it could be further lowered, to a minimum of 1280.
|
||||
func (sinfo *sessionInfo) _getMTU() uint16 {
|
||||
func (sinfo *sessionInfo) _getMTU() MTU {
|
||||
if sinfo.theirMTU == 0 || sinfo.myMTU == 0 {
|
||||
return 0
|
||||
}
|
||||
@ -415,7 +413,7 @@ func (sinfo *sessionInfo) _updateNonce(theirNonce *crypto.BoxNonce) {
|
||||
}
|
||||
|
||||
// Resets all sessions to an uninitialized state.
|
||||
// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change.
|
||||
// Called after coord changes, so attempts to use a session will trigger a new ping and notify the remote end of the coord change.
|
||||
// Only call this from the router actor.
|
||||
func (ss *sessions) reset() {
|
||||
for _, _sinfo := range ss.sinfos {
|
||||
|
@ -107,7 +107,7 @@ func (l *switchLocator) getCoords() []byte {
|
||||
return bs
|
||||
}
|
||||
|
||||
// Returns true if the this locator represents an ancestor of the locator given as an argument.
|
||||
// Returns true if this locator represents an ancestor of the locator given as an argument.
|
||||
// Ancestor means that it's the parent node, or the parent of parent, and so on...
|
||||
func (x *switchLocator) isAncestorOf(y *switchLocator) bool {
|
||||
if x.root != y.root {
|
||||
@ -381,7 +381,7 @@ func (t *switchTable) handleMsg(msg *switchMsg, fromPort switchPort) {
|
||||
// Then the tricky part, it decides if it should update our own locator as a result.
|
||||
// That happens if this node is already our parent, or is advertising a better root, or is advertising a better path to the same root, etc...
|
||||
// There are a lot of very delicate order sensitive checks here, so its' best to just read the code if you need to understand what it's doing.
|
||||
// It's very important to not change the order of the statements in the case function unless you're absolutely sure that it's safe, including safe if used along side nodes that used the previous order.
|
||||
// It's very important to not change the order of the statements in the case function unless you're absolutely sure that it's safe, including safe if used alongside nodes that used the previous order.
|
||||
// Set the third arg to true if you're reprocessing an old message, e.g. to find a new parent after one disconnects, to avoid updating some timing related things.
|
||||
func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, reprocessing bool) {
|
||||
// TODO directly use a switchMsg instead of switchMessage + sigs
|
||||
@ -808,12 +808,12 @@ func (t *switchTable) _handleIdle(port switchPort) bool {
|
||||
packet := buf.packets[0]
|
||||
coords := switch_getPacketCoords(packet.bytes)
|
||||
priority := float64(now.Sub(packet.time)) / float64(buf.size)
|
||||
if priority > bestPriority && t.portIsCloser(coords, port) {
|
||||
if priority >= bestPriority && t.portIsCloser(coords, port) {
|
||||
best = streamID
|
||||
bestPriority = priority
|
||||
}
|
||||
}
|
||||
if bestPriority != 0 {
|
||||
if best != "" {
|
||||
buf := t.queues.bufs[best]
|
||||
var packet switch_packetInfo
|
||||
// TODO decide if this should be LIFO or FIFO
|
||||
|
@ -1,14 +1,14 @@
|
||||
package yggdrasil
|
||||
|
||||
// This file contains the version metadata struct
|
||||
// Used in the inital connection setup and key exchange
|
||||
// Used in the initial connection setup and key exchange
|
||||
// Some of this could arguably go in wire.go instead
|
||||
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
|
||||
// This is the version-specific metadata exchanged at the start of a connection.
|
||||
// It must always beign with the 4 bytes "meta" and a wire formatted uint64 major version number.
|
||||
// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open an connection.
|
||||
// It must always begin with the 4 bytes "meta" and a wire formatted uint64 major version number.
|
||||
// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open a connection.
|
||||
type version_metadata struct {
|
||||
meta [4]byte
|
||||
ver uint64 // 1 byte in this version
|
||||
|
@ -380,7 +380,7 @@ func (p *sessionPing) decode(bs []byte) bool {
|
||||
if pType == wire_SessionPong {
|
||||
p.IsPong = true
|
||||
}
|
||||
p.MTU = uint16(mtu)
|
||||
p.MTU = MTU(mtu)
|
||||
return true
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user