Merge pull request #1 from yggdrasil-network/develop

Branch Develop: Base to Fork
This commit is contained in:
Christer Warén 2018-12-12 04:08:34 +02:00 committed by GitHub
commit e5218335c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 542 additions and 125 deletions

View File

@ -7,8 +7,6 @@ jobs:
docker:
- image: circleci/golang:1.11
working_directory: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}}
steps:
- checkout
@ -17,7 +15,9 @@ jobs:
command: |
mkdir /tmp/upload
echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV
echo 'export CIVERSION=$(sh contrib/semver/version.sh | cut -c 2-)' >> $BASH_ENV
echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV
git config --global user.email "$(git log --format='%ae' HEAD -1)";
git config --global user.name "$(git log --format='%an' HEAD -1)";
- run:
name: Install alien
@ -98,3 +98,13 @@ jobs:
- store_artifacts:
path: /tmp/upload
destination: /
- run:
name: Create tags (master branch only)
command: >
if [ "${CIRCLE_BRANCH}" == "master" ]; then
git tag -f -a $(sh contrib/semver/version.sh) -m "Created by CircleCI" && git push -f --tags;
else
echo "Only runs for master branch (this is ${CIRCLE_BRANCH})";
fi;
when: on_success

View File

@ -25,6 +25,33 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- in case of vulnerabilities.
-->
## [0.3.0] - 2018-12-12
### Added
- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil
- Add advanced `SwitchOptions` in configuration file for tuning the switch
- Add `dhtPing` to the admin socket to aid in crawling the network
- New macOS .pkgs built automatically by CircleCI
- Add Dockerfile to repository for Docker support
- Add `-json` command line flag for generating and normalising configuration in plain JSON instead of HJSON
- Build name and version numbers are now imprinted onto the build, accessible through `yggdrasil -version` and `yggdrasilctl getSelf`
- Add ability to disable admin socket by setting `AdminListen` to `"none"`
- `yggdrasilctl` now tries to look for the default configuration file to find `AdminListen` if `-endpoint` is not specified
- `yggdrasilctl` now returns more useful logging in the event of a fatal error
### Changed
- Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level)
- The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux
- Cleaned up some of the parameter naming in the admin socket
- Latency-based parent selection for the switch instead of uptime-based (should help to avoid high latency links somewhat)
- Real peering endpoints now shown in the admin socket `getPeers` call to help identify peerings
- Reuse the multicast port on supported platforms so that multiple Yggdrasil processes can run
- `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed)
### Fixed
- Memory leaks in the DHT fixed
- Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode
- Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive
## [0.2.7] - 2018-10-13
### Added
- Session firewall, which makes it possible to control who can open sessions with your node

View File

@ -15,7 +15,7 @@ You're encouraged to play with it, but it is strongly advised not to use it for
## Building
1. Install Go (tested on 1.9+, [godeb](https://github.com/niemeyer/godeb) is recommended for debian-based linux distributions).
1. Install Go (requires 1.11 or later, [godeb](https://github.com/niemeyer/godeb) is recommended for Debian-based Linux distributions).
2. Clone this repository.
2. `./build`

View File

@ -1 +0,0 @@
0.2

33
build
View File

@ -1,25 +1,36 @@
#!/bin/sh
while getopts ud option
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil}
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
while getopts "udtc:l:" option
do
case "${option}"
in
u) UPX=true;;
d) DEBUG=true;;
t) TABLES=true;;
c) GCFLAGS="$GCFLAGS $OPTARG";;
l) LDFLAGS="$LDFLAGS $OPTARG";;
esac
done
export GOPATH=$PWD
echo "Downloading..."
go get -d -v
go get -d -v yggdrasil
for file in *.go ; do
echo "Building: $file"
#go build $@ $file
if [ -z $TABLES ]; then
STRIP="-s -w"
fi
for CMD in `ls cmd/` ; do
echo "Building: $CMD"
if [ $DEBUG ]; then
go build -tags debug -v $file
go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD
else
go build -ldflags="-s -w" -v $file
go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD
fi
if [ $UPX ]; then
upx --brute ${file%.go}
upx --brute $CMD
fi
done

View File

@ -21,9 +21,9 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/neilalexander/hjson-go"
"yggdrasil"
"yggdrasil/config"
"yggdrasil/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
)
type nodeConfig = config.NodeConfig
@ -100,10 +100,15 @@ func main() {
normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised")
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)")
version := flag.Bool("version", false, "prints the version of this build")
flag.Parse()
var cfg *nodeConfig
switch {
case *version:
fmt.Println("Build name:", yggdrasil.GetBuildName())
fmt.Println("Build version:", yggdrasil.GetBuildVersion())
os.Exit(0)
case *autoconf:
// Use an autoconf-generated config, this will give us random keys and
// port numbers, and will use an automatically selected TUN/TAP interface.

View File

@ -1,53 +1,117 @@
package main
import "errors"
import "flag"
import "fmt"
import "strings"
import "net"
import "net/url"
import "sort"
import "encoding/json"
import "strconv"
import "os"
import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
"os"
"sort"
"strconv"
"strings"
import "yggdrasil/defaults"
"golang.org/x/text/encoding/unicode"
"github.com/neilalexander/hjson-go"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
)
type admin_info map[string]interface{}
func main() {
server := flag.String("endpoint", defaults.GetDefaults().DefaultAdminListen, "Admin socket endpoint")
injson := flag.Bool("json", false, "Output in JSON format")
logbuffer := &bytes.Buffer{}
logger := log.New(logbuffer, "", log.Flags())
defer func() {
if r := recover(); r != nil {
logger.Println("Fatal error:", r)
fmt.Print(logbuffer)
os.Exit(1)
}
}()
endpoint := defaults.GetDefaults().DefaultAdminListen
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n", os.Args[0])
fmt.Println("Options:")
flag.PrintDefaults()
fmt.Println("Commands:\n - Use \"list\" for a list of available commands")
fmt.Println("Examples:")
fmt.Println(" - ", os.Args[0], "list")
fmt.Println(" - ", os.Args[0], "getPeers")
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
}
server := flag.String("endpoint", endpoint, "Admin socket endpoint")
injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)")
verbose := flag.Bool("v", false, "Verbose output (includes public keys)")
flag.Parse()
args := flag.Args()
if len(args) == 0 {
fmt.Println("usage:", os.Args[0], "[-endpoint=proto://server] [-v] [-json] command [key=value] [...]")
fmt.Println("example:", os.Args[0], "getPeers")
fmt.Println("example:", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
fmt.Println("example:", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
fmt.Println("example:", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
flag.Usage()
return
}
if *server == endpoint {
if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 ||
bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
config, err = decoder.Bytes(config)
if err != nil {
panic(err)
}
}
var dat map[string]interface{}
if err := hjson.Unmarshal(config, &dat); err != nil {
panic(err)
}
if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") {
endpoint = ep
logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile)
logger.Println("Using endpoint", endpoint, "from AdminListen")
} else {
logger.Println("Configuration file doesn't contain appropriate AdminListen option")
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
}
} else {
logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile)
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
}
} else {
logger.Println("Using endpoint", endpoint, "from command line")
}
var conn net.Conn
u, err := url.Parse(*server)
u, err := url.Parse(endpoint)
if err == nil {
switch strings.ToLower(u.Scheme) {
case "unix":
conn, err = net.Dial("unix", (*server)[7:])
logger.Println("Connecting to UNIX socket", endpoint[7:])
conn, err = net.Dial("unix", endpoint[7:])
case "tcp":
logger.Println("Connecting to TCP socket", u.Host)
conn, err = net.Dial("tcp", u.Host)
default:
logger.Println("Unknown protocol or malformed address - check your endpoint")
err = errors.New("protocol not supported")
}
} else {
conn, err = net.Dial("tcp", *server)
logger.Println("Connecting to TCP socket", u.Host)
conn, err = net.Dial("tcp", endpoint)
}
if err != nil {
panic(err)
}
logger.Println("Connected")
defer conn.Close()
decoder := json.NewDecoder(conn)
@ -57,11 +121,13 @@ func main() {
for c, a := range args {
if c == 0 {
logger.Printf("Sending request: %v\n", a)
send["request"] = a
continue
}
tokens := strings.Split(a, "=")
if i, err := strconv.Atoi(tokens[1]); err == nil {
logger.Printf("Sending parameter %s: %d\n", tokens[0], i)
send[tokens[0]] = i
} else {
switch strings.ToLower(tokens[1]) {
@ -72,28 +138,31 @@ func main() {
default:
send[tokens[0]] = tokens[1]
}
logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]])
}
}
if err := encoder.Encode(&send); err != nil {
panic(err)
}
logger.Printf("Request sent")
if err := decoder.Decode(&recv); err == nil {
logger.Printf("Response received")
if recv["status"] == "error" {
if err, ok := recv["error"]; ok {
fmt.Println("Error:", err)
fmt.Println("Admin socket returned an error:", err)
} else {
fmt.Println("Unspecified error occured")
fmt.Println("Admin socket returned an error but didn't specify any error text")
}
os.Exit(1)
}
if _, ok := recv["request"]; !ok {
fmt.Println("Missing request in response (malformed response?)")
return
os.Exit(1)
}
if _, ok := recv["response"]; !ok {
fmt.Println("Missing response body (malformed response?)")
return
os.Exit(1)
}
req := recv["request"].(map[string]interface{})
res := recv["response"].(map[string]interface{})
@ -181,6 +250,12 @@ func main() {
}
case "getself":
for k, v := range res["self"].(map[string]interface{}) {
if buildname, ok := v.(map[string]interface{})["build_name"].(string); ok && buildname != "unknown" {
fmt.Println("Build name:", buildname)
}
if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" {
fmt.Println("Build version:", buildversion)
}
fmt.Println("IPv6 address:", k)
if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok {
fmt.Println("IPv6 subnet:", subnet)
@ -227,7 +302,7 @@ func main() {
queuesize := v.(map[string]interface{})["queue_size"].(float64)
queuepackets := v.(map[string]interface{})["queue_packets"].(float64)
queueid := v.(map[string]interface{})["queue_id"].(string)
portqueues[queueport] += 1
portqueues[queueport]++
portqueuesize[queueport] += queuesize
portqueuepackets[queueport] += queuepackets
queuesizepercent := (100 / maximumqueuesize) * queuesize
@ -315,9 +390,11 @@ func main() {
fmt.Println(string(json))
}
}
} else {
logger.Println("Error receiving response:", err)
}
if v, ok := recv["status"]; ok && v == "error" {
if v, ok := recv["status"]; ok && v != "success" {
os.Exit(1)
}
os.Exit(0)

View File

@ -17,7 +17,7 @@ import (
"github.com/neilalexander/hjson-go"
"golang.org/x/text/encoding/unicode"
"yggdrasil/config"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
)
type nodeConfig = config.NodeConfig

View File

@ -7,12 +7,12 @@
if [ `pwd` != `git rev-parse --show-toplevel` ]
then
echo "You should run this script from the top-level directory of the git repo"
exit -1
exit 1
fi
PKGBRANCH=$(basename `git name-rev --name-only HEAD`)
PKGNAME=$(sh contrib/semver/name.sh)
PKGVERSION=$(sh contrib/semver/version.sh | cut -c 2-)
PKGVERSION=$(sh contrib/semver/version.sh --bare)
PKGARCH=${PKGARCH-amd64}
PKGFILE=$PKGNAME-$PKGVERSION-$PKGARCH.deb
PKGREPLACES=yggdrasil
@ -29,7 +29,7 @@ elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=7 ./build
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build
else
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64"
exit -1
exit 1
fi
echo "Building $PKGFILE"

View File

@ -1,18 +1,22 @@
FROM golang:stretch
MAINTAINER Christer Waren/CWINFO "christer.waren@cwinfo.org"
RUN apt-get update \
&& apt-get upgrade -y
ADD . /src
FROM docker.io/golang:alpine as builder
COPY . /src
WORKDIR /src
RUN apk add git && ./build
RUN adduser --system --home /etc/yggdrasil-network --uid 1000 yggdrasil-network \
&& rm -rf build_* && ./build \
&& cp yggdrasil /usr/bin \
&& cp contrib/docker/entrypoint.sh /
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 contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh
# RUN addgroup -g 1000 -S yggdrasil-network \
# && adduser -u 1000 -S -g 1000 --home /etc/yggdrasil-network yggdrasil-network
#
# USER yggdrasil-network
# TODO: Make running unprivileged work
VOLUME [ "/etc/yggdrasil-network" ]
ENTRYPOINT [ "/entrypoint.sh" ]
ENTRYPOINT [ "/usr/bin/entrypoint.sh" ]

2
contrib/docker/entrypoint.sh Normal file → Executable file
View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
set -e

View File

@ -72,7 +72,7 @@ chmod +x pkgbuild/root/usr/local/bin/yggdrasilctl
# Work out metadata for the package info
PKGNAME=$(sh contrib/semver/name.sh)
PKGVERSION=$(sh contrib/semver/version.sh | cut -c 2-)
PKGVERSION=$(sh contrib/semver/version.sh --bare)
PKGARCH=${PKGARCH-amd64}
PAYLOADSIZE=$(( $(wc -c pkgbuild/flat/base.pkg/Payload | awk '{ print $1 }') / 1024 ))

View File

@ -0,0 +1,47 @@
Name: yggdrasil
Version: 0.3.0
Release: 1%{?dist}
Summary: End-to-end encrypted IPv6 networking
License: GPLv3
URL: https://yggdrasil-network.github.io
Source0: https://codeload.github.com/yggdrasil-network/yggdrasil-go/tar.gz/v0.3.0
%{?systemd_requires}
BuildRequires: systemd golang >= 1.11
%description
Yggdrasil is a proof-of-concept to explore a wholly different approach to
network routing. Whereas current computer networks depend heavily on very
centralised design and configuration, Yggdrasil breaks this mould by making
use of a global spanning tree to form a scalable IPv6 encrypted mesh network.
%prep
%setup -qn yggdrasil-go-%{version}
%build
./build -t -l "-linkmode=external"
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/%{_bindir}
mkdir -p %{buildroot}/%{_sysconfdir}/systemd/system
install -m 0755 yggdrasil %{buildroot}/%{_bindir}/yggdrasil
install -m 0755 yggdrasilctl %{buildroot}/%{_bindir}/yggdrasilctl
install -m 0755 contrib/systemd/yggdrasil.service %{buildroot}/%{_sysconfdir}/systemd/system/yggdrasil.service
install -m 0755 contrib/systemd/yggdrasil-resume.service %{buildroot}/%{_sysconfdir}/systemd/system/yggdrasil-resume.service
%files
%{_bindir}/yggdrasil
%{_bindir}/yggdrasilctl
%{_sysconfdir}/systemd/system/yggdrasil.service
%{_sysconfdir}/systemd/system/yggdrasil-resume.service
%post
%systemd_post yggdrasil.service
%preun
%systemd_preun yggdrasil.service
%postun
%systemd_postun_with_restart yggdrasil.service

View File

@ -1,7 +1,16 @@
#!/bin/sh
# Get the branch name, removing any "/" characters from pull requests
BRANCH=$(git symbolic-ref --short HEAD | tr -d "/" 2>/dev/null)
# Get the current branch name
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
# Complain if the git history is not available
if [ $? != 0 ]; then
printf "unknown"
exit 1
fi
# Remove "/" characters from the branch name if present
BRANCH=$(echo $BRANCH | tr -d "/")
# Check if the branch name is not master
if [ "$BRANCH" = "master" ]; then

View File

@ -4,7 +4,7 @@
DEVELOPBRANCH="yggdrasil-network/develop"
# Get the last tag
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*" 2>/dev/null)
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.0" 2>/dev/null)
# Get last merge to master
MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | head -n 1)
@ -12,12 +12,25 @@ MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | hea
# Get the number of merges since the last merge to master
PATCH=$(git rev-list $TAG..master --count --merges --grep="from $DEVELOPBRANCH" 2>/dev/null)
# Decide whether we should prepend the version with "v" - the default is that
# we do because we use it in git tags, but we might not always need it
PREPEND="v"
if [ "$1" = "--bare" ]; then
PREPEND=""
fi
# If it fails then there's no last tag - go from the first commit
if [ $? != 0 ]; then
PATCH=$(git rev-list HEAD --count 2>/dev/null)
printf 'v0.0.%d' "$PATCH"
exit -1
# Complain if the git history is not available
if [ $? != 0 ]; then
printf 'unknown'
exit 1
fi
printf '%s0.0.%d' "$PREPEND" "$PATCH"
exit 1
fi
# Get the number of merges on the current branch since the last tag
@ -32,9 +45,13 @@ BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Output in the desired format
if [ $PATCH = 0 ]; then
printf 'v%d.%d' "$MAJOR" "$MINOR"
if [ ! -z $FULL ]; then
printf '%s%d.%d.0' "$PREPEND" "$MAJOR" "$MINOR"
else
printf '%s%d.%d' "$PREPEND" "$MAJOR" "$MINOR"
fi
else
printf 'v%d.%d.%d' "$MAJOR" "$MINOR" "$PATCH"
printf '%s%d.%d.%d' "$PREPEND" "$MAJOR" "$MINOR" "$PATCH"
fi
# Add the build tag on non-master branches
@ -43,4 +60,3 @@ if [ $BRANCH != "master" ]; then
printf -- "-%04d" "$BUILD"
fi
fi

14
go.mod Normal file
View File

@ -0,0 +1,14 @@
module github.com/yggdrasil-network/yggdrasil-go
require (
github.com/docker/libcontainer v2.2.1+incompatible
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
github.com/mitchellh/mapstructure v1.1.2
github.com/neilalexander/hjson-go v0.0.0-20180509131856-23267a251165
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
golang.org/x/net v0.0.0-20181207154023-610586996380
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e
golang.org/x/text v0.3.0
)

20
go.sum Normal file
View File

@ -0,0 +1,20 @@
github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0=
github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw=
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/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/neilalexander/hjson-go v0.0.0-20180509131856-23267a251165 h1:Oo7Yfu5lEQLGvvh2p9Z8FRHJIsl7fdOCK9xXFNBkqmQ=
github.com/neilalexander/hjson-go v0.0.0-20180509131856-23267a251165/go.mod h1:l+Zao6IpQ+6d/y7LnYnOfbfOeU/9xRiTi4HLVpnkcTg=
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/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae h1:MYCANF1kehCG6x6G+/9txLfq6n3lS5Vp0Mxn1hdiBAc=
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae/go.mod h1:R0SBCsugm+Sf1katgTb2t7GXMm+nRIv43tM4VDZbaOs=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20181207154023-610586996380 h1:zPQexyRtNYBc7bcHmehl1dH6TB3qn8zytv8cBGLDNY0=
golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dzTt5OQ3vMQo9mkOIKIo=
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -16,7 +16,7 @@ import "encoding/hex"
import "flag"
import "fmt"
import "runtime"
import . "yggdrasil"
import . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
var doSig = flag.Bool("sig", false, "generate new signing keys instead")

View File

@ -1,4 +1,2 @@
#!/bin/bash
export GOPATH=$PWD
go get -d yggdrasil
go run -tags debug misc/sim/treesim.go "$@"

View File

@ -12,7 +12,7 @@ import "runtime"
import "runtime/pprof"
import "flag"
import . "yggdrasil"
import . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
////////////////////////////////////////////////////////////////////////////////

View File

@ -3,7 +3,7 @@ package config
// NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct {
Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
AdminListen string `comment:"Listen address for admin connections Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X."`
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."`
ReadTimeout int32 `comment:"Read timeout for connections, specified in milliseconds. If less\nthan 6000 and not negative, 6000 (the default) is used. If negative,\nreads won't time out."`

View File

@ -7,6 +7,9 @@ type platformDefaultParameters struct {
// Admin socket
DefaultAdminListen string
// Configuration (used for yggdrasilctl)
DefaultConfigFile string
// TUN/TAP
MaximumIfMTU int
DefaultIfMTU int

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{
// Admin
DefaultAdminListen: "tcp://localhost:9001",
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 65535,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{
// Admin
DefaultAdminListen: "tcp://localhost:9001",
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 32767,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{
// Admin
DefaultAdminListen: "tcp://localhost:9001",
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 65535,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{
// Admin
DefaultAdminListen: "tcp://localhost:9001",
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 9000,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{
// Admin
DefaultAdminListen: "tcp://localhost:9001",
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 16384,

View File

@ -9,6 +9,9 @@ func GetDefaults() platformDefaultParameters {
// Admin
DefaultAdminListen: "tcp://localhost:9001",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 65535,
DefaultIfMTU: 65535,

View File

@ -9,6 +9,9 @@ func GetDefaults() platformDefaultParameters {
// Admin
DefaultAdminListen: "tcp://localhost:9001",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "C:\\Program Files\\Yggdrasil\\yggdrasil.conf",
// TUN/TAP
MaximumIfMTU: 65535,
DefaultIfMTU: 65535,

View File

@ -14,7 +14,7 @@ import (
"sync/atomic"
"time"
"yggdrasil/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
)
// TODO: Add authentication
@ -268,11 +268,11 @@ func (a *admin) init(c *Core, listenaddr string) {
return admin_info{"source_subnets": subnets}, nil
})
a.addHandler("getRoutes", []string{}, func(in admin_info) (admin_info, error) {
var routes []string
routes := make(admin_info)
a.core.router.doAdmin(func() {
getRoutes := func(ckrs []cryptokey_route) {
for _, ckr := range ckrs {
routes = append(routes, fmt.Sprintf("%s via %s", ckr.subnet.String(), hex.EncodeToString(ckr.destination[:])))
routes[ckr.subnet.String()] = hex.EncodeToString(ckr.destination[:])
}
}
getRoutes(a.core.router.cryptokey.ipv4routes)
@ -326,7 +326,9 @@ func (a *admin) init(c *Core, listenaddr string) {
// start runs the admin API socket to listen for / respond to admin API calls.
func (a *admin) start() error {
go a.listen()
if a.listenaddr != "none" && a.listenaddr != "" {
go a.listen()
}
return nil
}
@ -341,7 +343,19 @@ func (a *admin) listen() {
if err == nil {
switch strings.ToLower(u.Scheme) {
case "unix":
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
}
a.listener, err = net.Listen("unix", a.listenaddr[7:])
if err == nil {
switch a.listenaddr[7:8] {
case "@": // maybe abstract namespace
default:
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
}
}
}
case "tcp":
a.listener, err = net.Listen("tcp", u.Host)
default:
@ -561,6 +575,13 @@ func (a *admin) getData_getSelf() *admin_nodeInfo {
{"subnet", a.core.GetSubnet().String()},
{"coords", fmt.Sprint(coords)},
}
if name := GetBuildName(); name != "unknown" {
self = append(self, admin_pair{"build_name", name})
}
if version := GetBuildVersion(); version != "unknown" {
self = append(self, admin_pair{"build_version", version})
}
return &self
}
@ -737,6 +758,10 @@ func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtR
}
var coords []byte
for _, cstr := range strings.Split(strings.Trim(coordString, "[]"), " ") {
if cstr == "" {
// Special case, happens if trimmed is the empty string, e.g. this is the root
continue
}
if u64, err := strconv.ParseUint(cstr, 10, 8); err != nil {
return dhtRes{}, err
} else {

View File

@ -241,6 +241,16 @@ func (c *cryptokey) getPublicKeyForAddress(addr address, addrlen int) (boxPubKey
for _, route := range *routingtable {
// Does this subnet match the given IP?
if route.subnet.Contains(ip) {
// Check if the routing cache is above a certain size, if it is evict
// a random entry so we can make room for this one. We take advantage
// of the fact that the iteration order is random here
for k := range *routingcache {
if len(*routingcache) < 1024 {
break
}
delete(*routingcache, k)
}
// Cache the entry for future packets to get a faster lookup
(*routingcache)[addr] = route

View File

@ -8,10 +8,13 @@ import (
"net"
"regexp"
"yggdrasil/config"
"yggdrasil/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
)
var buildName string
var buildVersion string
// The Core object represents the Yggdrasil node. You should create a Core
// object for each Yggdrasil node you plan to run.
type Core struct {
@ -59,12 +62,38 @@ func (c *Core) init(bpub *boxPubKey,
c.tun.init(c)
}
// Get the current build name. This is usually injected if built from git,
// or returns "unknown" otherwise.
func GetBuildName() string {
if buildName == "" {
return "unknown"
}
return buildName
}
// Get the current build version. This is usually injected if built from git,
// or returns "unknown" otherwise.
func GetBuildVersion() string {
if buildVersion == "" {
return "unknown"
}
return buildVersion
}
// Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging
// through the provided log.Logger. The started stack will include TCP and UDP
// sockets, a multicast discovery socket, an admin socket, router, switch and
// DHT node.
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
c.log = log
if name := GetBuildName(); name != "unknown" {
c.log.Println("Build name:", name)
}
if version := GetBuildVersion(); version != "unknown" {
c.log.Println("Build version:", version)
}
c.log.Println("Starting up...")
var boxPub boxPubKey

View File

@ -22,7 +22,7 @@ import "net/http"
import "runtime"
import "os"
import "yggdrasil/defaults"
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
// Start the profiler in debug builds, if the required environment variable is set.
func init() {
@ -504,27 +504,43 @@ func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) {
func DEBUG_simLinkPeers(p, q *peer) {
// Sets q.out() to point to p and starts p.linkLoop()
p.linkOut, q.linkOut = make(chan []byte, 1), make(chan []byte, 1)
go func() {
for bs := range p.linkOut {
q.handlePacket(bs)
goWorkers := func(source, dest *peer) {
source.linkOut = make(chan []byte, 1)
send := make(chan []byte, 1)
source.out = func(bs []byte) {
send <- bs
}
}()
go func() {
for bs := range q.linkOut {
p.handlePacket(bs)
}
}()
p.out = func(bs []byte) {
p.core.switchTable.idleIn <- p.port
go q.handlePacket(bs)
go source.linkLoop()
go func() {
var packets [][]byte
for {
select {
case packet := <-source.linkOut:
packets = append(packets, packet)
continue
case packet := <-send:
packets = append(packets, packet)
source.core.switchTable.idleIn <- source.port
continue
default:
}
if len(packets) > 0 {
dest.handlePacket(packets[0])
packets = packets[1:]
continue
}
select {
case packet := <-source.linkOut:
packets = append(packets, packet)
case packet := <-send:
packets = append(packets, packet)
source.core.switchTable.idleIn <- source.port
}
}
}()
}
q.out = func(bs []byte) {
q.core.switchTable.idleIn <- q.port
go p.handlePacket(bs)
}
go p.linkLoop()
go q.linkLoop()
goWorkers(p, q)
goWorkers(q, p)
p.core.switchTable.idleIn <- p.port
q.core.switchTable.idleIn <- q.port
}

View File

@ -1,6 +1,7 @@
package yggdrasil
import (
"context"
"fmt"
"net"
"time"
@ -35,7 +36,10 @@ func (m *multicast) start() error {
return err
}
listenString := fmt.Sprintf("[::]:%v", addr.Port)
conn, err := net.ListenPacket("udp6", listenString)
lc := net.ListenConfig{
Control: multicastReuse,
}
conn, err := lc.ListenPacket(context.Background(), "udp6", listenString)
if err != nil {
return err
}

View File

@ -0,0 +1,9 @@
// +build !linux,!darwin,!netbsd,!freebsd,!openbsd,!dragonflybsd,!windows
package yggdrasil
import "syscall"
func multicastReuse(network string, address string, c syscall.RawConn) error {
return nil
}

View File

@ -0,0 +1,22 @@
// +build linux darwin netbsd freebsd openbsd dragonflybsd
package yggdrasil
import "syscall"
import "golang.org/x/sys/unix"
func multicastReuse(network string, address string, c syscall.RawConn) error {
var control error
var reuseport error
control = c.Control(func(fd uintptr) {
reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
})
switch {
case reuseport != nil:
return reuseport
default:
return control
}
}

View File

@ -0,0 +1,22 @@
// +build windows
package yggdrasil
import "syscall"
import "golang.org/x/sys/windows"
func multicastReuse(network string, address string, c syscall.RawConn) error {
var control error
var reuseaddr error
control = c.Control(func(fd uintptr) {
reuseaddr = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)
})
switch {
case reuseaddr != nil:
return reuseaddr
default:
return control
}
}

View File

@ -208,6 +208,7 @@ func (t *switchTable) doMaintenance() {
defer t.mutex.Unlock() // Release lock when we're done
t.cleanRoot()
t.cleanDropped()
t.cleanPeers()
}
// Updates the root periodically if it is ourself, or promotes ourself to root if we're better than the current root or if the current root has timed out.
@ -258,11 +259,33 @@ func (t *switchTable) forgetPeer(port switchPort) {
if port != t.parent {
return
}
t.parent = 0
for _, info := range t.data.peers {
t.unlockedHandleMsg(&info.msg, info.port, true)
}
}
// Clean all unresponsive peers from the table, needed in case a peer stops updating.
// Needed in case a non-parent peer keeps the connection open but stops sending updates.
// Also reclaims space from deleted peers by copying the map.
func (t *switchTable) cleanPeers() {
now := time.Now()
for port, peer := range t.data.peers {
if now.Sub(peer.time) > switch_timeout+switch_throttle {
// Longer than switch_timeout to make sure we don't remove a working peer because the root stopped responding.
delete(t.data.peers, port)
}
}
if _, isIn := t.data.peers[t.parent]; !isIn {
// The root timestamp would probably time out before this happens, but better safe than sorry.
// We removed the current parent, so find a new one.
t.parent = 0
for _, peer := range t.data.peers {
t.unlockedHandleMsg(&peer.msg, peer.port, true)
}
}
}
// Dropped is a list of roots that are better than the current root, but stopped sending new timestamps.
// If we switch to a new root, and that root is better than an old root that previously timed out, then we can clean up the old dropped root infos.
// This function is called periodically to do that cleanup.
@ -377,6 +400,8 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep
doUpdate := false
oldSender := t.data.peers[fromPort]
if !equiv(&sender.locator, &oldSender.locator) {
// Reset faster info, we'll start refilling it right after this
sender.faster = nil
doUpdate = true
}
// Update the matrix of peer "faster" thresholds
@ -387,25 +412,20 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep
for port, peer := range t.data.peers {
if port == fromPort {
continue
}
switch {
case msg.Root != peer.locator.root:
// Different roots, blindly guess that the relationships will stay the same?
sender.faster[port] = oldSender.faster[peer.port]
case sender.locator.tstamp <= peer.locator.tstamp:
// Slower than this node, penalize (more than the reward amount)
if oldSender.faster[port] > 1 {
sender.faster[port] = oldSender.faster[peer.port] - 2
} else {
sender.faster[port] = 0
}
default:
} else if sender.locator.root != peer.locator.root || sender.locator.tstamp > peer.locator.tstamp {
// We were faster than this node, so increment, as long as we don't overflow because of it
if oldSender.faster[peer.port] < switch_faster_threshold {
sender.faster[port] = oldSender.faster[peer.port] + 1
} else {
sender.faster[port] = switch_faster_threshold
}
} else {
// Slower than this node, penalize (more than the reward amount)
if oldSender.faster[port] > 1 {
sender.faster[port] = oldSender.faster[peer.port] - 2
} else {
sender.faster[port] = 0
}
}
}
}
@ -457,12 +477,10 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep
// First, reset all faster-related info to 0.
// Then, de-parent the node and reprocess all messages to find a new parent.
t.parent = 0
sender.faster = nil
for _, peer := range t.data.peers {
if peer.port == sender.port {
continue
}
delete(peer.faster, sender.port)
t.unlockedHandleMsg(&peer.msg, peer.port, true)
}
// Process the sender last, to avoid keeping them as a parent if at all possible.

View File

@ -6,10 +6,11 @@ import (
"bytes"
"errors"
"time"
"yggdrasil/defaults"
"github.com/songgao/packets/ethernet"
"github.com/yggdrasil-network/water"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
)
const tun_IPv6_HEADER_LENGTH = 40