mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-25 23:01:38 +00:00
commit
ccf0090540
21
CHANGELOG.md
21
CHANGELOG.md
@ -25,6 +25,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
- in case of vulnerabilities.
|
- in case of vulnerabilities.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## [0.3.1] - 2018-12-17
|
||||||
|
### Added
|
||||||
|
- Build name and version is now imprinted onto the binaries if available/specified during build
|
||||||
|
- Ability to disable admin socket with `AdminListen: none`
|
||||||
|
- `AF_UNIX` domain sockets for the admin socket
|
||||||
|
- Cache size restriction for crypto-key routes
|
||||||
|
- `NodeInfo` support for specifying node information, e.g. node name or contact, which can be used in network crawls or surveys
|
||||||
|
- `getNodeInfo` request added to admin socket
|
||||||
|
- Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock`
|
||||||
|
- Formatting of `getRoutes` in the admin socket has been improved
|
||||||
|
- Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions
|
||||||
|
- Crypto, address and other utility code refactored into separate Go packages
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established)
|
||||||
|
- `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly
|
||||||
|
- Panic fixed when `Peers` or `InterfacePeers` was commented out
|
||||||
|
|
||||||
## [0.3.0] - 2018-12-12
|
## [0.3.0] - 2018-12-12
|
||||||
### Added
|
### Added
|
||||||
- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil
|
- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil
|
||||||
|
@ -12,14 +12,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
|
||||||
|
"github.com/hjson/hjson-go"
|
||||||
"github.com/kardianos/minwinsvc"
|
"github.com/kardianos/minwinsvc"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/neilalexander/hjson-go"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
@ -188,6 +189,35 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check to see if the peers are in a parsable format, if not then default
|
||||||
|
// them to the TCP scheme
|
||||||
|
if peers, ok := dat["Peers"].([]interface{}); ok {
|
||||||
|
for index, peer := range peers {
|
||||||
|
uri := peer.(string)
|
||||||
|
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(uri, "tcp:") {
|
||||||
|
uri = uri[4:]
|
||||||
|
}
|
||||||
|
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now do the same with the interface peers
|
||||||
|
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
|
||||||
|
for intf, peers := range interfacepeers {
|
||||||
|
for index, peer := range peers.([]interface{}) {
|
||||||
|
uri := peer.(string)
|
||||||
|
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(uri, "tcp:") {
|
||||||
|
uri = uri[4:]
|
||||||
|
}
|
||||||
|
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||||
// we generated above.
|
// we generated above.
|
||||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
|
||||||
"github.com/neilalexander/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,13 +37,15 @@ func main() {
|
|||||||
endpoint := defaults.GetDefaults().DefaultAdminListen
|
endpoint := defaults.GetDefaults().DefaultAdminListen
|
||||||
|
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n", os.Args[0])
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n\n", os.Args[0])
|
||||||
fmt.Println("Options:")
|
fmt.Println("Options:")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
fmt.Println("Commands:\n - Use \"list\" for a list of available commands")
|
fmt.Println("\nPlease note that options must always specified BEFORE the command\non the command line or they will be ignored.\n")
|
||||||
|
fmt.Println("Commands:\n - Use \"list\" for a list of available commands\n")
|
||||||
fmt.Println("Examples:")
|
fmt.Println("Examples:")
|
||||||
fmt.Println(" - ", os.Args[0], "list")
|
fmt.Println(" - ", os.Args[0], "list")
|
||||||
fmt.Println(" - ", os.Args[0], "getPeers")
|
fmt.Println(" - ", os.Args[0], "getPeers")
|
||||||
|
fmt.Println(" - ", os.Args[0], "-v getSelf")
|
||||||
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
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=tcp://localhost:9001 getDHT")
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
|
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
|
||||||
@ -87,6 +89,7 @@ func main() {
|
|||||||
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
endpoint = *server
|
||||||
logger.Println("Using endpoint", endpoint, "from command line")
|
logger.Println("Using endpoint", endpoint, "from command line")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,11 +124,20 @@ func main() {
|
|||||||
|
|
||||||
for c, a := range args {
|
for c, a := range args {
|
||||||
if c == 0 {
|
if c == 0 {
|
||||||
|
if strings.HasPrefix(a, "-") {
|
||||||
|
logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a)
|
||||||
|
continue
|
||||||
|
}
|
||||||
logger.Printf("Sending request: %v\n", a)
|
logger.Printf("Sending request: %v\n", a)
|
||||||
send["request"] = a
|
send["request"] = a
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tokens := strings.Split(a, "=")
|
tokens := strings.Split(a, "=")
|
||||||
|
if len(tokens) == 1 {
|
||||||
|
send[tokens[0]] = true
|
||||||
|
} else if len(tokens) > 2 {
|
||||||
|
send[tokens[0]] = strings.Join(tokens[1:], "=")
|
||||||
|
} else if len(tokens) == 2 {
|
||||||
if i, err := strconv.Atoi(tokens[1]); err == nil {
|
if i, err := strconv.Atoi(tokens[1]); err == nil {
|
||||||
logger.Printf("Sending parameter %s: %d\n", tokens[0], i)
|
logger.Printf("Sending parameter %s: %d\n", tokens[0], i)
|
||||||
send[tokens[0]] = i
|
send[tokens[0]] = i
|
||||||
@ -141,6 +153,7 @@ func main() {
|
|||||||
logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]])
|
logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := encoder.Encode(&send); err != nil {
|
if err := encoder.Encode(&send); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -187,7 +200,7 @@ func main() {
|
|||||||
if !keysOrdered {
|
if !keysOrdered {
|
||||||
for k := range slv.(map[string]interface{}) {
|
for k := range slv.(map[string]interface{}) {
|
||||||
if !*verbose {
|
if !*verbose {
|
||||||
if k == "box_pub_key" || k == "box_sig_key" {
|
if k == "box_pub_key" || k == "box_sig_key" || k == "nodeinfo" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/neilalexander/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
|
@ -52,8 +52,11 @@ Architecture: $PKGARCH
|
|||||||
Replaces: $PKGREPLACES
|
Replaces: $PKGREPLACES
|
||||||
Conflicts: $PKGREPLACES
|
Conflicts: $PKGREPLACES
|
||||||
Maintainer: Neil Alexander <neilalexander@users.noreply.github.com>
|
Maintainer: Neil Alexander <neilalexander@users.noreply.github.com>
|
||||||
Description: Debian yggdrasil package
|
Description: Yggdrasil Network
|
||||||
Binary yggdrasil package for Debian and Ubuntu
|
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6
|
||||||
|
network. It is lightweight, self-arranging, supported on multiple platforms and
|
||||||
|
allows pretty much any IPv6-capable application to communicate securely with
|
||||||
|
other Yggdrasil nodes.
|
||||||
EOF
|
EOF
|
||||||
cat > /tmp/$PKGNAME/debian/copyright << EOF
|
cat > /tmp/$PKGNAME/debian/copyright << EOF
|
||||||
Please see https://github.com/yggdrasil-network/yggdrasil-go/
|
Please see https://github.com/yggdrasil-network/yggdrasil-go/
|
||||||
@ -68,21 +71,40 @@ etc/systemd/system/*.service etc/systemd/system
|
|||||||
EOF
|
EOF
|
||||||
cat > /tmp/$PKGNAME/debian/postinst << EOF
|
cat > /tmp/$PKGNAME/debian/postinst << EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! getent group yggdrasil 2>&1 > /dev/null; then
|
||||||
|
addgroup --system --quiet yggdrasil
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f /etc/yggdrasil.conf ];
|
if [ -f /etc/yggdrasil.conf ];
|
||||||
then
|
then
|
||||||
mkdir -p /var/backups
|
mkdir -p /var/backups
|
||||||
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
|
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
|
||||||
cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
|
cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
|
||||||
echo "Normalising /etc/yggdrasil.conf"
|
echo "Normalising and updating /etc/yggdrasil.conf"
|
||||||
/usr/bin/yggdrasil -useconffile /var/backups/yggdrasil.conf.`date +%Y%m%d` -normaliseconf > /etc/yggdrasil.conf
|
/usr/bin/yggdrasil -useconffile /var/backups/yggdrasil.conf.`date +%Y%m%d` -normaliseconf > /etc/yggdrasil.conf
|
||||||
|
chgrp yggdrasil /etc/yggdrasil.conf
|
||||||
|
|
||||||
|
if command -v systemctl >/dev/null; then
|
||||||
|
systemctl daemon-reload >/dev/null || true
|
||||||
|
systemctl enable yggdrasil || true
|
||||||
|
systemctl start yggdrasil || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Generating initial configuration file /etc/yggdrasil.conf"
|
||||||
|
echo "Please familiarise yourself with this file before starting Yggdrasil"
|
||||||
|
/usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf
|
||||||
|
chgrp yggdrasil /etc/yggdrasil.conf
|
||||||
fi
|
fi
|
||||||
systemctl enable yggdrasil
|
|
||||||
systemctl start yggdrasil
|
|
||||||
EOF
|
EOF
|
||||||
cat > /tmp/$PKGNAME/debian/prerm << EOF
|
cat > /tmp/$PKGNAME/debian/prerm << EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
systemctl disable yggdrasil
|
if command -v systemctl >/dev/null; then
|
||||||
systemctl stop yggdrasil
|
if systemctl is-active --quiet yggdrasil; then
|
||||||
|
systemctl stop yggdrasil || true
|
||||||
|
fi
|
||||||
|
systemctl disable yggdrasil || true
|
||||||
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cp yggdrasil /tmp/$PKGNAME/usr/bin/
|
cp yggdrasil /tmp/$PKGNAME/usr/bin/
|
||||||
|
@ -2,6 +2,9 @@ FROM docker.io/golang:alpine as builder
|
|||||||
|
|
||||||
COPY . /src
|
COPY . /src
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
RUN apk add git && ./build
|
RUN apk add git && ./build
|
||||||
|
|
||||||
FROM docker.io/alpine
|
FROM docker.io/alpine
|
||||||
|
@ -10,7 +10,7 @@ TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.0" 2>/dev/null)
|
|||||||
MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | head -n 1)
|
MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | head -n 1)
|
||||||
|
|
||||||
# Get the number of merges since the last merge to master
|
# 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)
|
PATCH=$(git rev-list $TAG..master --count --merges --grep="from $DEVELOPBRANCH" --first-parent master 2>/dev/null)
|
||||||
|
|
||||||
# Decide whether we should prepend the version with "v" - the default is that
|
# 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
|
# we do because we use it in git tags, but we might not always need it
|
||||||
|
@ -4,6 +4,7 @@ Wants=network.target
|
|||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
Group=yggdrasil
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ProtectSystem=true
|
ProtectSystem=true
|
||||||
SyslogIdentifier=yggdrasil
|
SyslogIdentifier=yggdrasil
|
||||||
@ -12,7 +13,7 @@ ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \
|
|||||||
yggdrasil -genconf > /etc/yggdrasil.conf; \
|
yggdrasil -genconf > /etc/yggdrasil.conf; \
|
||||||
echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \
|
echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \
|
||||||
fi"
|
fi"
|
||||||
ExecStart=/bin/sh -c "exec yggdrasil -useconf < /etc/yggdrasil.conf"
|
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
2
go.mod
2
go.mod
@ -2,9 +2,9 @@ module github.com/yggdrasil-network/yggdrasil-go
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/libcontainer v2.2.1+incompatible
|
github.com/docker/libcontainer v2.2.1+incompatible
|
||||||
|
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222
|
||||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
|
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/neilalexander/hjson-go v0.0.0-20180509131856-23267a251165
|
|
||||||
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
|
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
|
||||||
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae
|
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
||||||
|
4
go.sum
4
go.sum
@ -1,11 +1,11 @@
|
|||||||
github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0=
|
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/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw=
|
||||||
|
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 h1:xmvkbxXDeN1ffWq8kvrhyqVYAO2aXuRBsbpxVTR+JyU=
|
||||||
|
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g=
|
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g=
|
||||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo=
|
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/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 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w=
|
||||||
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8oye9a7HmytmZ+tr8Q2vlP0tAHP13kTHzwvQY=
|
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 h1:MYCANF1kehCG6x6G+/9txLfq6n3lS5Vp0Mxn1hdiBAc=
|
||||||
|
@ -12,11 +12,16 @@ This only matters if it's high enough to make you the root of the tree.
|
|||||||
*/
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "encoding/hex"
|
import (
|
||||||
import "flag"
|
"encoding/hex"
|
||||||
import "fmt"
|
"flag"
|
||||||
import "runtime"
|
"fmt"
|
||||||
import . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
"net"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
var doSig = flag.Bool("sig", false, "generate new signing keys instead")
|
var doSig = flag.Bool("sig", false, "generate new signing keys instead")
|
||||||
|
|
||||||
@ -82,12 +87,7 @@ func isBetter(oldID, newID []byte) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doBoxKeys(out chan<- keySet, in <-chan []byte) {
|
func doBoxKeys(out chan<- keySet, in <-chan []byte) {
|
||||||
c := Core{}
|
var bestID crypto.NodeID
|
||||||
pub, _ := c.DEBUG_newBoxKeys()
|
|
||||||
bestID := c.DEBUG_getNodeID(pub)
|
|
||||||
for idx := range bestID {
|
|
||||||
bestID[idx] = 0
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case newBestID := <-in:
|
case newBestID := <-in:
|
||||||
@ -95,22 +95,20 @@ func doBoxKeys(out chan<- keySet, in <-chan []byte) {
|
|||||||
copy(bestID[:], newBestID)
|
copy(bestID[:], newBestID)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
pub, priv := c.DEBUG_newBoxKeys()
|
pub, priv := crypto.NewBoxKeys()
|
||||||
id := c.DEBUG_getNodeID(pub)
|
id := crypto.GetNodeID(pub)
|
||||||
if !isBetter(bestID[:], id[:]) {
|
if !isBetter(bestID[:], id[:]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bestID = id
|
bestID = *id
|
||||||
ip := c.DEBUG_addrForNodeID(id)
|
ip := net.IP(address.AddrForNodeID(id)[:]).String()
|
||||||
out <- keySet{priv[:], pub[:], id[:], ip}
|
out <- keySet{priv[:], pub[:], id[:], ip}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSigKeys(out chan<- keySet, in <-chan []byte) {
|
func doSigKeys(out chan<- keySet, in <-chan []byte) {
|
||||||
c := Core{}
|
var bestID crypto.TreeID
|
||||||
pub, _ := c.DEBUG_newSigKeys()
|
|
||||||
bestID := c.DEBUG_getTreeID(pub)
|
|
||||||
for idx := range bestID {
|
for idx := range bestID {
|
||||||
bestID[idx] = 0
|
bestID[idx] = 0
|
||||||
}
|
}
|
||||||
@ -122,12 +120,12 @@ func doSigKeys(out chan<- keySet, in <-chan []byte) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
pub, priv := c.DEBUG_newSigKeys()
|
pub, priv := crypto.NewSigKeys()
|
||||||
id := c.DEBUG_getTreeID(pub)
|
id := crypto.GetTreeID(pub)
|
||||||
if !isBetter(bestID[:], id[:]) {
|
if !isBetter(bestID[:], id[:]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bestID = id
|
bestID = *id
|
||||||
out <- keySet{priv[:], pub[:], id[:], ""}
|
out <- keySet{priv[:], pub[:], id[:], ""}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
package yggdrasil
|
package address
|
||||||
|
|
||||||
|
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
|
||||||
// address represents an IPv6 address in the yggdrasil address range.
|
// address represents an IPv6 address in the yggdrasil address range.
|
||||||
type address [16]byte
|
type Address [16]byte
|
||||||
|
|
||||||
// subnet represents an IPv6 /64 subnet in the yggdrasil subnet range.
|
// subnet represents an IPv6 /64 subnet in the yggdrasil subnet range.
|
||||||
type subnet [8]byte
|
type Subnet [8]byte
|
||||||
|
|
||||||
// address_prefix is the prefix used for all addresses and subnets in the network.
|
// address_prefix is the prefix used for all addresses and subnets in the network.
|
||||||
// The current implementation requires this to be a muliple of 8 bits + 7 bits.
|
// The current implementation requires this to be a muliple of 8 bits + 7 bits.
|
||||||
// The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1).
|
// 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, though routing and the DHT machinery *should* still work.
|
// Nodes that configure this differently will be unable to communicate with eachother, though routing and the DHT machinery *should* still work.
|
||||||
var address_prefix = [...]byte{0x02}
|
func GetPrefix() [1]byte {
|
||||||
|
return [...]byte{0x02}
|
||||||
|
}
|
||||||
|
|
||||||
// isValid returns true if an address falls within the range used by nodes in the network.
|
// isValid returns true if an address falls within the range used by nodes in the network.
|
||||||
func (a *address) isValid() bool {
|
func (a *Address) IsValid() bool {
|
||||||
for idx := range address_prefix {
|
prefix := GetPrefix()
|
||||||
if (*a)[idx] != address_prefix[idx] {
|
for idx := range prefix {
|
||||||
|
if (*a)[idx] != prefix[idx] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,28 +28,29 @@ func (a *address) isValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isValid returns true if a prefix falls within the range usable by the network.
|
// isValid returns true if a prefix falls within the range usable by the network.
|
||||||
func (s *subnet) isValid() bool {
|
func (s *Subnet) IsValid() bool {
|
||||||
l := len(address_prefix)
|
prefix := GetPrefix()
|
||||||
for idx := range address_prefix[:l-1] {
|
l := len(prefix)
|
||||||
if (*s)[idx] != address_prefix[idx] {
|
for idx := range prefix[:l-1] {
|
||||||
|
if (*s)[idx] != prefix[idx] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (*s)[l-1] == address_prefix[l-1]|0x01
|
return (*s)[l-1] == prefix[l-1]|0x01
|
||||||
}
|
}
|
||||||
|
|
||||||
// address_addrForNodeID takes a *NodeID as an argument and returns an *address.
|
// address_addrForNodeID takes a *NodeID as an argument and returns an *address.
|
||||||
// This subnet begins with the address prefix, with the last bit set to 0 to indicate an address.
|
// This subnet begins with the address prefix, with the last bit set to 0 to indicate an address.
|
||||||
// The following 8 bits are set to the number of leading 1 bits in the NodeID.
|
// The following 8 bits are set to the number of leading 1 bits in the NodeID.
|
||||||
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the address.
|
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the address.
|
||||||
func address_addrForNodeID(nid *NodeID) *address {
|
func AddrForNodeID(nid *crypto.NodeID) *Address {
|
||||||
// 128 bit address
|
// 128 bit address
|
||||||
// Begins with prefix
|
// Begins with prefix
|
||||||
// Next bit is a 0
|
// Next bit is a 0
|
||||||
// Next 7 bits, interpreted as a uint, are # of leading 1s in the NodeID
|
// Next 7 bits, interpreted as a uint, are # of leading 1s in the NodeID
|
||||||
// Leading 1s and first leading 0 of the NodeID are truncated off
|
// Leading 1s and first leading 0 of the NodeID are truncated off
|
||||||
// The rest is appended to the IPv6 address (truncated to 128 bits total)
|
// The rest is appended to the IPv6 address (truncated to 128 bits total)
|
||||||
var addr address
|
var addr Address
|
||||||
var temp []byte
|
var temp []byte
|
||||||
done := false
|
done := false
|
||||||
ones := byte(0)
|
ones := byte(0)
|
||||||
@ -67,9 +73,10 @@ func address_addrForNodeID(nid *NodeID) *address {
|
|||||||
temp = append(temp, bits)
|
temp = append(temp, bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy(addr[:], address_prefix[:])
|
prefix := GetPrefix()
|
||||||
addr[len(address_prefix)] = ones
|
copy(addr[:], prefix[:])
|
||||||
copy(addr[len(address_prefix)+1:], temp)
|
addr[len(prefix)] = ones
|
||||||
|
copy(addr[len(prefix)+1:], temp)
|
||||||
return &addr
|
return &addr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,14 +84,15 @@ func address_addrForNodeID(nid *NodeID) *address {
|
|||||||
// This subnet begins with the address prefix, with the last bit set to 1 to indicate a prefix.
|
// This subnet begins with the address prefix, with the last bit set to 1 to indicate a prefix.
|
||||||
// The following 8 bits are set to the number of leading 1 bits in the NodeID.
|
// The following 8 bits are set to the number of leading 1 bits in the NodeID.
|
||||||
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the subnet.
|
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the subnet.
|
||||||
func address_subnetForNodeID(nid *NodeID) *subnet {
|
func SubnetForNodeID(nid *crypto.NodeID) *Subnet {
|
||||||
// Exactly as the address version, with two exceptions:
|
// Exactly as the address version, with two exceptions:
|
||||||
// 1) The first bit after the fixed prefix is a 1 instead of a 0
|
// 1) The first bit after the fixed prefix is a 1 instead of a 0
|
||||||
// 2) It's truncated to a subnet prefix length instead of 128 bits
|
// 2) It's truncated to a subnet prefix length instead of 128 bits
|
||||||
addr := *address_addrForNodeID(nid)
|
addr := *AddrForNodeID(nid)
|
||||||
var snet subnet
|
var snet Subnet
|
||||||
copy(snet[:], addr[:])
|
copy(snet[:], addr[:])
|
||||||
snet[len(address_prefix)-1] |= 0x01
|
prefix := GetPrefix()
|
||||||
|
snet[len(prefix)-1] |= 0x01
|
||||||
return &snet
|
return &snet
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,17 +100,18 @@ func address_subnetForNodeID(nid *NodeID) *subnet {
|
|||||||
// The first is a NodeID with all the bits known from the address set to their correct values.
|
// The first is a NodeID with all the bits known from the address set to their correct values.
|
||||||
// The second is a bitmask with 1 bit set for each bit that was known from the address.
|
// The second is a bitmask with 1 bit set for each bit that was known from the address.
|
||||||
// This is used to look up NodeIDs in the DHT and tell if they match an address.
|
// This is used to look up NodeIDs in the DHT and tell if they match an address.
|
||||||
func (a *address) getNodeIDandMask() (*NodeID, *NodeID) {
|
func (a *Address) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
|
||||||
// Mask is a bitmask to mark the bits visible from the address
|
// Mask is a bitmask to mark the bits visible from the address
|
||||||
// This means truncated leading 1s, first leading 0, and visible part of addr
|
// This means truncated leading 1s, first leading 0, and visible part of addr
|
||||||
var nid NodeID
|
var nid crypto.NodeID
|
||||||
var mask NodeID
|
var mask crypto.NodeID
|
||||||
ones := int(a[len(address_prefix)])
|
prefix := GetPrefix()
|
||||||
|
ones := int(a[len(prefix)])
|
||||||
for idx := 0; idx < ones; idx++ {
|
for idx := 0; idx < ones; idx++ {
|
||||||
nid[idx/8] |= 0x80 >> byte(idx%8)
|
nid[idx/8] |= 0x80 >> byte(idx%8)
|
||||||
}
|
}
|
||||||
nidOffset := ones + 1
|
nidOffset := ones + 1
|
||||||
addrOffset := 8*len(address_prefix) + 8
|
addrOffset := 8*len(prefix) + 8
|
||||||
for idx := addrOffset; idx < 8*len(a); idx++ {
|
for idx := addrOffset; idx < 8*len(a); idx++ {
|
||||||
bits := a[idx/8] & (0x80 >> byte(idx%8))
|
bits := a[idx/8] & (0x80 >> byte(idx%8))
|
||||||
bits <<= byte(idx % 8)
|
bits <<= byte(idx % 8)
|
||||||
@ -110,7 +119,7 @@ func (a *address) getNodeIDandMask() (*NodeID, *NodeID) {
|
|||||||
bits >>= byte(nidIdx % 8)
|
bits >>= byte(nidIdx % 8)
|
||||||
nid[nidIdx/8] |= bits
|
nid[nidIdx/8] |= bits
|
||||||
}
|
}
|
||||||
maxMask := 8*(len(a)-len(address_prefix)-1) + ones + 1
|
maxMask := 8*(len(a)-len(prefix)-1) + ones + 1
|
||||||
for idx := 0; idx < maxMask; idx++ {
|
for idx := 0; idx < maxMask; idx++ {
|
||||||
mask[idx/8] |= 0x80 >> byte(idx%8)
|
mask[idx/8] |= 0x80 >> byte(idx%8)
|
||||||
}
|
}
|
||||||
@ -121,16 +130,17 @@ func (a *address) getNodeIDandMask() (*NodeID, *NodeID) {
|
|||||||
// The first is a NodeID with all the bits known from the address set to their correct values.
|
// The first is a NodeID with all the bits known from the address set to their correct values.
|
||||||
// The second is a bitmask with 1 bit set for each bit that was known from the subnet.
|
// The second is a bitmask with 1 bit set for each bit that was known from the subnet.
|
||||||
// This is used to look up NodeIDs in the DHT and tell if they match a subnet.
|
// This is used to look up NodeIDs in the DHT and tell if they match a subnet.
|
||||||
func (s *subnet) getNodeIDandMask() (*NodeID, *NodeID) {
|
func (s *Subnet) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
|
||||||
// As with the address version, but visible parts of the subnet prefix instead
|
// As with the address version, but visible parts of the subnet prefix instead
|
||||||
var nid NodeID
|
var nid crypto.NodeID
|
||||||
var mask NodeID
|
var mask crypto.NodeID
|
||||||
ones := int(s[len(address_prefix)])
|
prefix := GetPrefix()
|
||||||
|
ones := int(s[len(prefix)])
|
||||||
for idx := 0; idx < ones; idx++ {
|
for idx := 0; idx < ones; idx++ {
|
||||||
nid[idx/8] |= 0x80 >> byte(idx%8)
|
nid[idx/8] |= 0x80 >> byte(idx%8)
|
||||||
}
|
}
|
||||||
nidOffset := ones + 1
|
nidOffset := ones + 1
|
||||||
addrOffset := 8*len(address_prefix) + 8
|
addrOffset := 8*len(prefix) + 8
|
||||||
for idx := addrOffset; idx < 8*len(s); idx++ {
|
for idx := addrOffset; idx < 8*len(s); idx++ {
|
||||||
bits := s[idx/8] & (0x80 >> byte(idx%8))
|
bits := s[idx/8] & (0x80 >> byte(idx%8))
|
||||||
bits <<= byte(idx % 8)
|
bits <<= byte(idx % 8)
|
||||||
@ -138,7 +148,7 @@ func (s *subnet) getNodeIDandMask() (*NodeID, *NodeID) {
|
|||||||
bits >>= byte(nidIdx % 8)
|
bits >>= byte(nidIdx % 8)
|
||||||
nid[nidIdx/8] |= bits
|
nid[nidIdx/8] |= bits
|
||||||
}
|
}
|
||||||
maxMask := 8*(len(s)-len(address_prefix)-1) + ones + 1
|
maxMask := 8*(len(s)-len(prefix)-1) + ones + 1
|
||||||
for idx := 0; idx < maxMask; idx++ {
|
for idx := 0; idx < maxMask; idx++ {
|
||||||
mask[idx/8] |= 0x80 >> byte(idx%8)
|
mask[idx/8] |= 0x80 >> byte(idx%8)
|
||||||
}
|
}
|
@ -19,6 +19,7 @@ type NodeConfig struct {
|
|||||||
SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."`
|
SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."`
|
||||||
TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."`
|
TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."`
|
||||||
SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."`
|
SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."`
|
||||||
|
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||||
//Net NetConfig `comment:"Extended options for connecting to peers over other networks."`
|
//Net NetConfig `comment:"Extended options for connecting to peers over other networks."`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
187
src/crypto/crypto.go
Normal file
187
src/crypto/crypto.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This part of the package wraps crypto operations needed elsewhere
|
||||||
|
|
||||||
|
In particular, it exposes key generation for ed25519 and nacl box
|
||||||
|
|
||||||
|
It also defines NodeID and TreeID as hashes of keys, and wraps hash functions
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha512"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ed25519"
|
||||||
|
"golang.org/x/crypto/nacl/box"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// NodeID and TreeID
|
||||||
|
|
||||||
|
const NodeIDLen = sha512.Size
|
||||||
|
const TreeIDLen = sha512.Size
|
||||||
|
const handleLen = 8
|
||||||
|
|
||||||
|
type NodeID [NodeIDLen]byte
|
||||||
|
type TreeID [TreeIDLen]byte
|
||||||
|
type Handle [handleLen]byte
|
||||||
|
|
||||||
|
func GetNodeID(pub *BoxPubKey) *NodeID {
|
||||||
|
h := sha512.Sum512(pub[:])
|
||||||
|
return (*NodeID)(&h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTreeID(pub *SigPubKey) *TreeID {
|
||||||
|
h := sha512.Sum512(pub[:])
|
||||||
|
return (*TreeID)(&h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandle() *Handle {
|
||||||
|
var h Handle
|
||||||
|
_, err := rand.Read(h[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &h
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Signatures
|
||||||
|
|
||||||
|
const SigPubKeyLen = ed25519.PublicKeySize
|
||||||
|
const SigPrivKeyLen = ed25519.PrivateKeySize
|
||||||
|
const SigLen = ed25519.SignatureSize
|
||||||
|
|
||||||
|
type SigPubKey [SigPubKeyLen]byte
|
||||||
|
type SigPrivKey [SigPrivKeyLen]byte
|
||||||
|
type SigBytes [SigLen]byte
|
||||||
|
|
||||||
|
func NewSigKeys() (*SigPubKey, *SigPrivKey) {
|
||||||
|
var pub SigPubKey
|
||||||
|
var priv SigPrivKey
|
||||||
|
pubSlice, privSlice, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
copy(pub[:], pubSlice)
|
||||||
|
copy(priv[:], privSlice)
|
||||||
|
return &pub, &priv
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sign(priv *SigPrivKey, msg []byte) *SigBytes {
|
||||||
|
var sig SigBytes
|
||||||
|
sigSlice := ed25519.Sign(priv[:], msg)
|
||||||
|
copy(sig[:], sigSlice)
|
||||||
|
return &sig
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verify(pub *SigPubKey, msg []byte, sig *SigBytes) bool {
|
||||||
|
// Should sig be an array instead of a slice?...
|
||||||
|
// It's fixed size, but
|
||||||
|
return ed25519.Verify(pub[:], msg, sig[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// NaCl-like crypto "box" (curve25519+xsalsa20+poly1305)
|
||||||
|
|
||||||
|
const BoxPubKeyLen = 32
|
||||||
|
const BoxPrivKeyLen = 32
|
||||||
|
const BoxSharedKeyLen = 32
|
||||||
|
const BoxNonceLen = 24
|
||||||
|
const BoxOverhead = box.Overhead
|
||||||
|
|
||||||
|
type BoxPubKey [BoxPubKeyLen]byte
|
||||||
|
type BoxPrivKey [BoxPrivKeyLen]byte
|
||||||
|
type BoxSharedKey [BoxSharedKeyLen]byte
|
||||||
|
type BoxNonce [BoxNonceLen]byte
|
||||||
|
|
||||||
|
func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) {
|
||||||
|
pubBytes, privBytes, err := box.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pub := (*BoxPubKey)(pubBytes)
|
||||||
|
priv := (*BoxPrivKey)(privBytes)
|
||||||
|
return pub, priv
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSharedKey(myPrivKey *BoxPrivKey,
|
||||||
|
othersPubKey *BoxPubKey) *BoxSharedKey {
|
||||||
|
var shared [BoxSharedKeyLen]byte
|
||||||
|
priv := (*[BoxPrivKeyLen]byte)(myPrivKey)
|
||||||
|
pub := (*[BoxPubKeyLen]byte)(othersPubKey)
|
||||||
|
box.Precompute(&shared, pub, priv)
|
||||||
|
return (*BoxSharedKey)(&shared)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BoxOpen(shared *BoxSharedKey,
|
||||||
|
boxed []byte,
|
||||||
|
nonce *BoxNonce) ([]byte, bool) {
|
||||||
|
out := util.GetBytes()
|
||||||
|
s := (*[BoxSharedKeyLen]byte)(shared)
|
||||||
|
n := (*[BoxNonceLen]byte)(nonce)
|
||||||
|
unboxed, success := box.OpenAfterPrecomputation(out, boxed, n, s)
|
||||||
|
return unboxed, success
|
||||||
|
}
|
||||||
|
|
||||||
|
func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *BoxNonce) {
|
||||||
|
if nonce == nil {
|
||||||
|
nonce = NewBoxNonce()
|
||||||
|
}
|
||||||
|
nonce.Increment()
|
||||||
|
out := util.GetBytes()
|
||||||
|
s := (*[BoxSharedKeyLen]byte)(shared)
|
||||||
|
n := (*[BoxNonceLen]byte)(nonce)
|
||||||
|
boxed := box.SealAfterPrecomputation(out, unboxed, n, s)
|
||||||
|
return boxed, nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoxNonce() *BoxNonce {
|
||||||
|
var nonce BoxNonce
|
||||||
|
_, err := rand.Read(nonce[:])
|
||||||
|
for ; err == nil && nonce[0] == 0xff; _, err = rand.Read(nonce[:]) {
|
||||||
|
// Make sure nonce isn't too high
|
||||||
|
// This is just to make rollover unlikely to happen
|
||||||
|
// Rollover is fine, but it may kill the session and force it to reopen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *BoxNonce) Increment() {
|
||||||
|
oldNonce := *n
|
||||||
|
n[len(n)-1] += 2
|
||||||
|
for i := len(n) - 2; i >= 0; i-- {
|
||||||
|
if n[i+1] < oldNonce[i+1] {
|
||||||
|
n[i] += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to subtract one nonce from another, staying in the range +- 64.
|
||||||
|
// This is used by the nonce progression machinery to advance the bitmask of recently received packets (indexed by nonce), or to check the appropriate bit of the bitmask.
|
||||||
|
// It's basically part of the machinery that prevents replays and duplicate packets.
|
||||||
|
func (n *BoxNonce) Minus(m *BoxNonce) int64 {
|
||||||
|
diff := int64(0)
|
||||||
|
for idx := range n {
|
||||||
|
diff *= 256
|
||||||
|
diff += int64(n[idx]) - int64(m[idx])
|
||||||
|
if diff > 64 {
|
||||||
|
diff = 64
|
||||||
|
}
|
||||||
|
if diff < -64 {
|
||||||
|
diff = -64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diff
|
||||||
|
}
|
@ -1,21 +1,21 @@
|
|||||||
package yggdrasil
|
package util
|
||||||
|
|
||||||
// These are misc. utility functions that didn't really fit anywhere else
|
// These are misc. utility functions that didn't really fit anywhere else
|
||||||
|
|
||||||
import "runtime"
|
import "runtime"
|
||||||
|
|
||||||
// A wrapper around runtime.Gosched() so it doesn't need to be imported elsewhere.
|
// A wrapper around runtime.Gosched() so it doesn't need to be imported elsewhere.
|
||||||
func util_yield() {
|
func Yield() {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A wrapper around runtime.LockOSThread() so it doesn't need to be imported elsewhere.
|
// A wrapper around runtime.LockOSThread() so it doesn't need to be imported elsewhere.
|
||||||
func util_lockthread() {
|
func LockThread() {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A wrapper around runtime.UnlockOSThread() so it doesn't need to be imported elsewhere.
|
// A wrapper around runtime.UnlockOSThread() so it doesn't need to be imported elsewhere.
|
||||||
func util_unlockthread() {
|
func UnlockThread() {
|
||||||
runtime.UnlockOSThread()
|
runtime.UnlockOSThread()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +23,12 @@ func util_unlockthread() {
|
|||||||
// It's used like a sync.Pool, but with a fixed size and typechecked without type casts to/from interface{} (which were making the profiles look ugly).
|
// It's used like a sync.Pool, but with a fixed size and typechecked without type casts to/from interface{} (which were making the profiles look ugly).
|
||||||
var byteStore chan []byte
|
var byteStore chan []byte
|
||||||
|
|
||||||
// Initializes the byteStore
|
func init() {
|
||||||
func util_initByteStore() {
|
|
||||||
if byteStore == nil {
|
|
||||||
byteStore = make(chan []byte, 32)
|
byteStore = make(chan []byte, 32)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Gets an empty slice from the byte store, if one is available, or else returns a new nil slice.
|
// Gets an empty slice from the byte store, if one is available, or else returns a new nil slice.
|
||||||
func util_getBytes() []byte {
|
func GetBytes() []byte {
|
||||||
select {
|
select {
|
||||||
case bs := <-byteStore:
|
case bs := <-byteStore:
|
||||||
return bs[:0]
|
return bs[:0]
|
||||||
@ -41,7 +38,7 @@ func util_getBytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Puts a slice in the store, if there's room, or else returns and lets the slice get collected.
|
// Puts a slice in the store, if there's room, or else returns and lets the slice get collected.
|
||||||
func util_putBytes(bs []byte) {
|
func PutBytes(bs []byte) {
|
||||||
select {
|
select {
|
||||||
case byteStore <- bs:
|
case byteStore <- bs:
|
||||||
default:
|
default:
|
25
src/yggdrasil/adapter.go
Normal file
25
src/yggdrasil/adapter.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package yggdrasil
|
||||||
|
|
||||||
|
// Defines the minimum required functions for an adapter type.
|
||||||
|
type AdapterInterface interface {
|
||||||
|
init(core *Core, send chan<- []byte, recv <-chan []byte)
|
||||||
|
read() error
|
||||||
|
write() error
|
||||||
|
close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines the minimum required struct members for an adapter type (this is
|
||||||
|
// now the base type for tunAdapter in tun.go)
|
||||||
|
type Adapter struct {
|
||||||
|
AdapterInterface
|
||||||
|
core *Core
|
||||||
|
send chan<- []byte
|
||||||
|
recv <-chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialises the adapter.
|
||||||
|
func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
|
||||||
|
adapter.core = core
|
||||||
|
adapter.send = send
|
||||||
|
adapter.recv = recv
|
||||||
|
}
|
@ -14,6 +14,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -160,9 +162,9 @@ func (a *admin) init(c *Core, listenaddr string) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
return admin_info{
|
return admin_info{
|
||||||
a.core.tun.iface.Name(): admin_info{
|
a.core.router.tun.iface.Name(): admin_info{
|
||||||
"tap_mode": a.core.tun.iface.IsTAP(),
|
"tap_mode": a.core.router.tun.iface.IsTAP(),
|
||||||
"mtu": a.core.tun.mtu,
|
"mtu": a.core.router.tun.mtu,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
@ -185,8 +187,8 @@ func (a *admin) init(c *Core, listenaddr string) {
|
|||||||
return admin_info{}, errors.New("Failed to configure adapter")
|
return admin_info{}, errors.New("Failed to configure adapter")
|
||||||
} else {
|
} else {
|
||||||
return admin_info{
|
return admin_info{
|
||||||
a.core.tun.iface.Name(): admin_info{
|
a.core.router.tun.iface.Name(): admin_info{
|
||||||
"tap_mode": a.core.tun.iface.IsTAP(),
|
"tap_mode": a.core.router.tun.iface.IsTAP(),
|
||||||
"mtu": ifmtu,
|
"mtu": ifmtu,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
@ -314,7 +316,7 @@ func (a *admin) init(c *Core, listenaddr string) {
|
|||||||
"box_pub_key": hex.EncodeToString(dinfo.key[:]),
|
"box_pub_key": hex.EncodeToString(dinfo.key[:]),
|
||||||
"coords": fmt.Sprintf("%v", dinfo.coords),
|
"coords": fmt.Sprintf("%v", dinfo.coords),
|
||||||
}
|
}
|
||||||
addr := net.IP(address_addrForNodeID(getNodeID(&dinfo.key))[:]).String()
|
addr := net.IP(address.AddrForNodeID(crypto.GetNodeID(&dinfo.key))[:]).String()
|
||||||
infos[addr] = info
|
infos[addr] = info
|
||||||
}
|
}
|
||||||
return admin_info{"nodes": infos}, nil
|
return admin_info{"nodes": infos}, nil
|
||||||
@ -322,6 +324,23 @@ func (a *admin) init(c *Core, listenaddr string) {
|
|||||||
return admin_info{}, err
|
return admin_info{}, err
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
a.addHandler("getNodeInfo", []string{"box_pub_key", "coords", "[nocache]"}, func(in admin_info) (admin_info, error) {
|
||||||
|
var nocache bool
|
||||||
|
if in["nocache"] != nil {
|
||||||
|
nocache = in["nocache"].(string) == "true"
|
||||||
|
}
|
||||||
|
result, err := a.admin_getNodeInfo(in["box_pub_key"].(string), in["coords"].(string), nocache)
|
||||||
|
if err == nil {
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err = json.Unmarshal(result, &m); err == nil {
|
||||||
|
return admin_info{"nodeinfo": m}, nil
|
||||||
|
} else {
|
||||||
|
return admin_info{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return admin_info{}, err
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// start runs the admin API socket to listen for / respond to admin API calls.
|
// start runs the admin API socket to listen for / respond to admin API calls.
|
||||||
@ -376,7 +395,7 @@ func (a *admin) listen() {
|
|||||||
for {
|
for {
|
||||||
conn, err := a.listener.Accept()
|
conn, err := a.listener.Accept()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
a.handleRequest(conn)
|
go a.handleRequest(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,13 +534,7 @@ func (a *admin) addPeer(addr string, sintf string) error {
|
|||||||
return errors.New("invalid peer: " + addr)
|
return errors.New("invalid peer: " + addr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no url scheme provided
|
return errors.New("invalid peer: " + addr)
|
||||||
addr = strings.ToLower(addr)
|
|
||||||
if strings.HasPrefix(addr, "tcp:") {
|
|
||||||
addr = addr[4:]
|
|
||||||
}
|
|
||||||
a.core.tcp.connect(addr, "")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -539,12 +552,12 @@ func (a *admin) removePeer(p string) error {
|
|||||||
// startTunWithMTU creates the tun/tap device, sets its address, and sets the MTU to the provided value.
|
// startTunWithMTU creates the tun/tap device, sets its address, and sets the MTU to the provided value.
|
||||||
func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error {
|
func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error {
|
||||||
// Close the TUN first if open
|
// Close the TUN first if open
|
||||||
_ = a.core.tun.close()
|
_ = a.core.router.tun.close()
|
||||||
// Then reconfigure and start it
|
// Then reconfigure and start it
|
||||||
addr := a.core.router.addr
|
addr := a.core.router.addr
|
||||||
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix)-1)
|
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address.GetPrefix())-1)
|
||||||
if ifname != "none" {
|
if ifname != "none" {
|
||||||
err := a.core.tun.setup(ifname, iftapmode, straddr, ifmtu)
|
err := a.core.router.tun.setup(ifname, iftapmode, straddr, ifmtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -559,9 +572,9 @@ func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error
|
|||||||
a.core.sessions.sendPingPong(sinfo, false)
|
a.core.sessions.sendPingPong(sinfo, false)
|
||||||
}
|
}
|
||||||
// Aaaaand... go!
|
// Aaaaand... go!
|
||||||
go a.core.tun.read()
|
go a.core.router.tun.read()
|
||||||
}
|
}
|
||||||
go a.core.tun.write()
|
go a.core.router.tun.write()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +609,7 @@ func (a *admin) getData_getPeers() []admin_nodeInfo {
|
|||||||
sort.Slice(ps, func(i, j int) bool { return ps[i] < ps[j] })
|
sort.Slice(ps, func(i, j int) bool { return ps[i] < ps[j] })
|
||||||
for _, port := range ps {
|
for _, port := range ps {
|
||||||
p := ports[port]
|
p := ports[port]
|
||||||
addr := *address_addrForNodeID(getNodeID(&p.box))
|
addr := *address.AddrForNodeID(crypto.GetNodeID(&p.box))
|
||||||
info := admin_nodeInfo{
|
info := admin_nodeInfo{
|
||||||
{"ip", net.IP(addr[:]).String()},
|
{"ip", net.IP(addr[:]).String()},
|
||||||
{"port", port},
|
{"port", port},
|
||||||
@ -621,7 +634,7 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo {
|
|||||||
if !isIn {
|
if !isIn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addr := *address_addrForNodeID(getNodeID(&peer.box))
|
addr := *address.AddrForNodeID(crypto.GetNodeID(&peer.box))
|
||||||
coords := elem.locator.getCoords()
|
coords := elem.locator.getCoords()
|
||||||
info := admin_nodeInfo{
|
info := admin_nodeInfo{
|
||||||
{"ip", net.IP(addr[:]).String()},
|
{"ip", net.IP(addr[:]).String()},
|
||||||
@ -679,7 +692,7 @@ func (a *admin) getData_getDHT() []admin_nodeInfo {
|
|||||||
return dht_ordered(&a.core.dht.nodeID, dhtInfos[i].getNodeID(), dhtInfos[j].getNodeID())
|
return dht_ordered(&a.core.dht.nodeID, dhtInfos[i].getNodeID(), dhtInfos[j].getNodeID())
|
||||||
})
|
})
|
||||||
for _, v := range dhtInfos {
|
for _, v := range dhtInfos {
|
||||||
addr := *address_addrForNodeID(v.getNodeID())
|
addr := *address.AddrForNodeID(v.getNodeID())
|
||||||
info := admin_nodeInfo{
|
info := admin_nodeInfo{
|
||||||
{"ip", net.IP(addr[:]).String()},
|
{"ip", net.IP(addr[:]).String()},
|
||||||
{"coords", fmt.Sprint(v.coords)},
|
{"coords", fmt.Sprint(v.coords)},
|
||||||
@ -729,7 +742,7 @@ func (a *admin) getAllowedEncryptionPublicKeys() []string {
|
|||||||
func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||||
boxBytes, err := hex.DecodeString(bstr)
|
boxBytes, err := hex.DecodeString(bstr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var box boxPubKey
|
var box crypto.BoxPubKey
|
||||||
copy(box[:], boxBytes)
|
copy(box[:], boxBytes)
|
||||||
a.core.peers.addAllowedEncryptionPublicKey(&box)
|
a.core.peers.addAllowedEncryptionPublicKey(&box)
|
||||||
}
|
}
|
||||||
@ -741,7 +754,7 @@ func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
|||||||
func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||||
boxBytes, err := hex.DecodeString(bstr)
|
boxBytes, err := hex.DecodeString(bstr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var box boxPubKey
|
var box crypto.BoxPubKey
|
||||||
copy(box[:], boxBytes)
|
copy(box[:], boxBytes)
|
||||||
a.core.peers.removeAllowedEncryptionPublicKey(&box)
|
a.core.peers.removeAllowedEncryptionPublicKey(&box)
|
||||||
}
|
}
|
||||||
@ -750,7 +763,7 @@ func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
|||||||
|
|
||||||
// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID.
|
// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID.
|
||||||
func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtRes, error) {
|
func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtRes, error) {
|
||||||
var key boxPubKey
|
var key crypto.BoxPubKey
|
||||||
if keyBytes, err := hex.DecodeString(keyString); err != nil {
|
if keyBytes, err := hex.DecodeString(keyString); err != nil {
|
||||||
return dhtRes{}, err
|
return dhtRes{}, err
|
||||||
} else {
|
} else {
|
||||||
@ -781,7 +794,7 @@ func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtR
|
|||||||
} else if len(targetBytes) != len(target) {
|
} else if len(targetBytes) != len(target) {
|
||||||
return dhtRes{}, errors.New("Incorrect target NodeID length")
|
return dhtRes{}, errors.New("Incorrect target NodeID length")
|
||||||
} else {
|
} else {
|
||||||
target = NodeID{}
|
var target crypto.NodeID
|
||||||
copy(target[:], targetBytes)
|
copy(target[:], targetBytes)
|
||||||
}
|
}
|
||||||
rq := dhtReqKey{info.key, target}
|
rq := dhtReqKey{info.key, target}
|
||||||
@ -806,6 +819,52 @@ func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtR
|
|||||||
return dhtRes{}, errors.New(fmt.Sprintf("DHT ping timeout: %s", keyString))
|
return dhtRes{}, errors.New(fmt.Sprintf("DHT ping timeout: %s", keyString))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (nodeinfoPayload, error) {
|
||||||
|
var key crypto.BoxPubKey
|
||||||
|
if keyBytes, err := hex.DecodeString(keyString); err != nil {
|
||||||
|
return nodeinfoPayload{}, err
|
||||||
|
} else {
|
||||||
|
copy(key[:], keyBytes)
|
||||||
|
}
|
||||||
|
if !nocache {
|
||||||
|
if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil {
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 nodeinfoPayload{}, err
|
||||||
|
} else {
|
||||||
|
coords = append(coords, uint8(u64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response := make(chan *nodeinfoPayload, 1)
|
||||||
|
sendNodeInfoRequest := func() {
|
||||||
|
a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
|
||||||
|
defer func() { recover() }()
|
||||||
|
select {
|
||||||
|
case response <- nodeinfo:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
a.core.nodeinfo.sendNodeInfo(key, coords, false)
|
||||||
|
}
|
||||||
|
a.core.router.doAdmin(sendNodeInfoRequest)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
close(response)
|
||||||
|
}()
|
||||||
|
for res := range response {
|
||||||
|
return *res, nil
|
||||||
|
}
|
||||||
|
return nodeinfoPayload{}, errors.New(fmt.Sprintf("getNodeInfo timeout: %s", keyString))
|
||||||
|
}
|
||||||
|
|
||||||
// getResponse_dot returns a response for a graphviz dot formatted representation of the known parts of the network.
|
// getResponse_dot returns a response for a graphviz dot formatted representation of the known parts of the network.
|
||||||
// This is color-coded and labeled, and includes the self node, switch peers, nodes known to the DHT, and nodes with open sessions.
|
// This is color-coded and labeled, and includes the self node, switch peers, nodes known to the DHT, and nodes with open sessions.
|
||||||
// The graph is structured as a tree with directed links leading away from the root.
|
// The graph is structured as a tree with directed links leading away from the root.
|
||||||
|
@ -7,6 +7,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This module implements crypto-key routing, similar to Wireguard, where we
|
// This module implements crypto-key routing, similar to Wireguard, where we
|
||||||
@ -17,15 +20,15 @@ type cryptokey struct {
|
|||||||
enabled bool
|
enabled bool
|
||||||
ipv4routes []cryptokey_route
|
ipv4routes []cryptokey_route
|
||||||
ipv6routes []cryptokey_route
|
ipv6routes []cryptokey_route
|
||||||
ipv4cache map[address]cryptokey_route
|
ipv4cache map[address.Address]cryptokey_route
|
||||||
ipv6cache map[address]cryptokey_route
|
ipv6cache map[address.Address]cryptokey_route
|
||||||
ipv4sources []net.IPNet
|
ipv4sources []net.IPNet
|
||||||
ipv6sources []net.IPNet
|
ipv6sources []net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
type cryptokey_route struct {
|
type cryptokey_route struct {
|
||||||
subnet net.IPNet
|
subnet net.IPNet
|
||||||
destination boxPubKey
|
destination crypto.BoxPubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise crypto-key routing. This must be done before any other CKR calls.
|
// Initialise crypto-key routing. This must be done before any other CKR calls.
|
||||||
@ -33,8 +36,8 @@ func (c *cryptokey) init(core *Core) {
|
|||||||
c.core = core
|
c.core = core
|
||||||
c.ipv4routes = make([]cryptokey_route, 0)
|
c.ipv4routes = make([]cryptokey_route, 0)
|
||||||
c.ipv6routes = make([]cryptokey_route, 0)
|
c.ipv6routes = make([]cryptokey_route, 0)
|
||||||
c.ipv4cache = make(map[address]cryptokey_route, 0)
|
c.ipv4cache = make(map[address.Address]cryptokey_route, 0)
|
||||||
c.ipv6cache = make(map[address]cryptokey_route, 0)
|
c.ipv6cache = make(map[address.Address]cryptokey_route, 0)
|
||||||
c.ipv4sources = make([]net.IPNet, 0)
|
c.ipv4sources = make([]net.IPNet, 0)
|
||||||
c.ipv6sources = make([]net.IPNet, 0)
|
c.ipv6sources = make([]net.IPNet, 0)
|
||||||
}
|
}
|
||||||
@ -52,7 +55,7 @@ func (c *cryptokey) isEnabled() bool {
|
|||||||
// Check whether the given address (with the address length specified in bytes)
|
// Check whether the given address (with the address length specified in bytes)
|
||||||
// matches either the current node's address, the node's routed subnet or the
|
// matches either the current node's address, the node's routed subnet or the
|
||||||
// list of subnets specified in IPv4Sources/IPv6Sources.
|
// list of subnets specified in IPv4Sources/IPv6Sources.
|
||||||
func (c *cryptokey) isValidSource(addr address, addrlen int) bool {
|
func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool {
|
||||||
ip := net.IP(addr[:addrlen])
|
ip := net.IP(addr[:addrlen])
|
||||||
|
|
||||||
if addrlen == net.IPv6len {
|
if addrlen == net.IPv6len {
|
||||||
@ -143,7 +146,7 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
|||||||
|
|
||||||
// Build our references to the routing table and cache
|
// Build our references to the routing table and cache
|
||||||
var routingtable *[]cryptokey_route
|
var routingtable *[]cryptokey_route
|
||||||
var routingcache *map[address]cryptokey_route
|
var routingcache *map[address.Address]cryptokey_route
|
||||||
|
|
||||||
// Check if the prefix is IPv4 or IPv6
|
// Check if the prefix is IPv4 or IPv6
|
||||||
if prefixsize == net.IPv6len*8 {
|
if prefixsize == net.IPv6len*8 {
|
||||||
@ -157,11 +160,11 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is the route an Yggdrasil destination?
|
// Is the route an Yggdrasil destination?
|
||||||
var addr address
|
var addr address.Address
|
||||||
var snet subnet
|
var snet address.Subnet
|
||||||
copy(addr[:], ipaddr)
|
copy(addr[:], ipaddr)
|
||||||
copy(snet[:], ipnet.IP)
|
copy(snet[:], ipnet.IP)
|
||||||
if addr.isValid() || snet.isValid() {
|
if addr.IsValid() || snet.IsValid() {
|
||||||
return errors.New("Can't specify Yggdrasil destination as crypto-key route")
|
return errors.New("Can't specify Yggdrasil destination as crypto-key route")
|
||||||
}
|
}
|
||||||
// Do we already have a route for this subnet?
|
// Do we already have a route for this subnet?
|
||||||
@ -173,11 +176,11 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
|||||||
// Decode the public key
|
// Decode the public key
|
||||||
if bpk, err := hex.DecodeString(dest); err != nil {
|
if bpk, err := hex.DecodeString(dest); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if len(bpk) != boxPubKeyLen {
|
} else if len(bpk) != crypto.BoxPubKeyLen {
|
||||||
return errors.New(fmt.Sprintf("Incorrect key length for %s", dest))
|
return errors.New(fmt.Sprintf("Incorrect key length for %s", dest))
|
||||||
} else {
|
} else {
|
||||||
// Add the new crypto-key route
|
// Add the new crypto-key route
|
||||||
var key boxPubKey
|
var key crypto.BoxPubKey
|
||||||
copy(key[:], bpk)
|
copy(key[:], bpk)
|
||||||
*routingtable = append(*routingtable, cryptokey_route{
|
*routingtable = append(*routingtable, cryptokey_route{
|
||||||
subnet: *ipnet,
|
subnet: *ipnet,
|
||||||
@ -205,16 +208,16 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
|||||||
// Looks up the most specific route for the given address (with the address
|
// Looks up the most specific route for the given address (with the address
|
||||||
// length specified in bytes) from the crypto-key routing table. An error is
|
// length specified in bytes) from the crypto-key routing table. An error is
|
||||||
// returned if the address is not suitable or no route was found.
|
// returned if the address is not suitable or no route was found.
|
||||||
func (c *cryptokey) getPublicKeyForAddress(addr address, addrlen int) (boxPubKey, error) {
|
func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (crypto.BoxPubKey, error) {
|
||||||
// Check if the address is a valid Yggdrasil address - if so it
|
// Check if the address is a valid Yggdrasil address - if so it
|
||||||
// is exempt from all CKR checking
|
// is exempt from all CKR checking
|
||||||
if addr.isValid() {
|
if addr.IsValid() {
|
||||||
return boxPubKey{}, errors.New("Cannot look up CKR for Yggdrasil addresses")
|
return crypto.BoxPubKey{}, errors.New("Cannot look up CKR for Yggdrasil addresses")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build our references to the routing table and cache
|
// Build our references to the routing table and cache
|
||||||
var routingtable *[]cryptokey_route
|
var routingtable *[]cryptokey_route
|
||||||
var routingcache *map[address]cryptokey_route
|
var routingcache *map[address.Address]cryptokey_route
|
||||||
|
|
||||||
// Check if the prefix is IPv4 or IPv6
|
// Check if the prefix is IPv4 or IPv6
|
||||||
if addrlen == net.IPv6len {
|
if addrlen == net.IPv6len {
|
||||||
@ -224,7 +227,7 @@ func (c *cryptokey) getPublicKeyForAddress(addr address, addrlen int) (boxPubKey
|
|||||||
routingtable = &c.ipv4routes
|
routingtable = &c.ipv4routes
|
||||||
routingcache = &c.ipv4cache
|
routingcache = &c.ipv4cache
|
||||||
} else {
|
} else {
|
||||||
return boxPubKey{}, errors.New("Unexpected prefix size")
|
return crypto.BoxPubKey{}, errors.New("Unexpected prefix size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there's a cache entry for this addr
|
// Check if there's a cache entry for this addr
|
||||||
@ -260,7 +263,7 @@ func (c *cryptokey) getPublicKeyForAddress(addr address, addrlen int) (boxPubKey
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No route was found if we got to this point
|
// No route was found if we got to this point
|
||||||
return boxPubKey{}, errors.New(fmt.Sprintf("No route to %s", ip.String()))
|
return crypto.BoxPubKey{}, errors.New(fmt.Sprintf("No route to %s", ip.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a source subnet, which allows traffic with these source addresses to
|
// Removes a source subnet, which allows traffic with these source addresses to
|
||||||
@ -312,7 +315,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error {
|
|||||||
|
|
||||||
// Build our references to the routing table and cache
|
// Build our references to the routing table and cache
|
||||||
var routingtable *[]cryptokey_route
|
var routingtable *[]cryptokey_route
|
||||||
var routingcache *map[address]cryptokey_route
|
var routingcache *map[address.Address]cryptokey_route
|
||||||
|
|
||||||
// Check if the prefix is IPv4 or IPv6
|
// Check if the prefix is IPv4 or IPv6
|
||||||
if prefixsize == net.IPv6len*8 {
|
if prefixsize == net.IPv6len*8 {
|
||||||
@ -329,7 +332,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error {
|
|||||||
bpk, err := hex.DecodeString(dest)
|
bpk, err := hex.DecodeString(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if len(bpk) != boxPubKeyLen {
|
} else if len(bpk) != crypto.BoxPubKeyLen {
|
||||||
return errors.New(fmt.Sprintf("Incorrect key length for %s", dest))
|
return errors.New(fmt.Sprintf("Incorrect key length for %s", dest))
|
||||||
}
|
}
|
||||||
netStr := ipnet.String()
|
netStr := ipnet.String()
|
||||||
|
@ -8,7 +8,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"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/defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,33 +21,32 @@ var buildVersion string
|
|||||||
// object for each Yggdrasil node you plan to run.
|
// object for each Yggdrasil node you plan to run.
|
||||||
type Core struct {
|
type Core struct {
|
||||||
// This is the main data structure that holds everything else for a node
|
// This is the main data structure that holds everything else for a node
|
||||||
boxPub boxPubKey
|
boxPub crypto.BoxPubKey
|
||||||
boxPriv boxPrivKey
|
boxPriv crypto.BoxPrivKey
|
||||||
sigPub sigPubKey
|
sigPub crypto.SigPubKey
|
||||||
sigPriv sigPrivKey
|
sigPriv crypto.SigPrivKey
|
||||||
switchTable switchTable
|
switchTable switchTable
|
||||||
peers peers
|
peers peers
|
||||||
sessions sessions
|
sessions sessions
|
||||||
router router
|
router router
|
||||||
dht dht
|
dht dht
|
||||||
tun tunDevice
|
|
||||||
admin admin
|
admin admin
|
||||||
searches searches
|
searches searches
|
||||||
multicast multicast
|
multicast multicast
|
||||||
|
nodeinfo nodeinfo
|
||||||
tcp tcpInterface
|
tcp tcpInterface
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
|
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) init(bpub *boxPubKey,
|
func (c *Core) init(bpub *crypto.BoxPubKey,
|
||||||
bpriv *boxPrivKey,
|
bpriv *crypto.BoxPrivKey,
|
||||||
spub *sigPubKey,
|
spub *crypto.SigPubKey,
|
||||||
spriv *sigPrivKey) {
|
spriv *crypto.SigPrivKey) {
|
||||||
// TODO separate init and start functions
|
// TODO separate init and start functions
|
||||||
// Init sets up structs
|
// Init sets up structs
|
||||||
// Start launches goroutines that depend on structs being set up
|
// Start launches goroutines that depend on structs being set up
|
||||||
// This is pretty much required to completely avoid race conditions
|
// This is pretty much required to completely avoid race conditions
|
||||||
util_initByteStore()
|
|
||||||
if c.log == nil {
|
if c.log == nil {
|
||||||
c.log = log.New(ioutil.Discard, "", 0)
|
c.log = log.New(ioutil.Discard, "", 0)
|
||||||
}
|
}
|
||||||
@ -59,7 +60,6 @@ func (c *Core) init(bpub *boxPubKey,
|
|||||||
c.peers.init(c)
|
c.peers.init(c)
|
||||||
c.router.init(c)
|
c.router.init(c)
|
||||||
c.switchTable.init(c, c.sigPub) // TODO move before peers? before router?
|
c.switchTable.init(c, c.sigPub) // TODO move before peers? before router?
|
||||||
c.tun.init(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current build name. This is usually injected if built from git,
|
// Get the current build name. This is usually injected if built from git,
|
||||||
@ -96,10 +96,10 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
|
|
||||||
c.log.Println("Starting up...")
|
c.log.Println("Starting up...")
|
||||||
|
|
||||||
var boxPub boxPubKey
|
var boxPub crypto.BoxPubKey
|
||||||
var boxPriv boxPrivKey
|
var boxPriv crypto.BoxPrivKey
|
||||||
var sigPub sigPubKey
|
var sigPub crypto.SigPubKey
|
||||||
var sigPriv sigPrivKey
|
var sigPriv crypto.SigPrivKey
|
||||||
boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey)
|
boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -124,6 +124,9 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
||||||
c.admin.init(c, nc.AdminListen)
|
c.admin.init(c, nc.AdminListen)
|
||||||
|
|
||||||
|
c.nodeinfo.init(c)
|
||||||
|
c.nodeinfo.setNodeInfo(nc.NodeInfo)
|
||||||
|
|
||||||
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {
|
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {
|
||||||
c.log.Println("Failed to start TCP interface")
|
c.log.Println("Failed to start TCP interface")
|
||||||
return err
|
return err
|
||||||
@ -188,7 +191,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ip := net.IP(c.router.addr[:]).String()
|
ip := net.IP(c.router.addr[:]).String()
|
||||||
if err := c.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address_prefix)-1), nc.IfMTU); err != nil {
|
if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address.GetPrefix())-1), nc.IfMTU); err != nil {
|
||||||
c.log.Println("Failed to start TUN/TAP")
|
c.log.Println("Failed to start TUN/TAP")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -200,45 +203,55 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
// Stops the Yggdrasil node.
|
// Stops the Yggdrasil node.
|
||||||
func (c *Core) Stop() {
|
func (c *Core) Stop() {
|
||||||
c.log.Println("Stopping...")
|
c.log.Println("Stopping...")
|
||||||
c.tun.close()
|
c.router.tun.close()
|
||||||
c.admin.close()
|
c.admin.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a new encryption keypair. The encryption keys are used to
|
// Generates a new encryption keypair. The encryption keys are used to
|
||||||
// encrypt traffic and to derive the IPv6 address/subnet of the node.
|
// encrypt traffic and to derive the IPv6 address/subnet of the node.
|
||||||
func (c *Core) NewEncryptionKeys() (*boxPubKey, *boxPrivKey) {
|
func (c *Core) NewEncryptionKeys() (*crypto.BoxPubKey, *crypto.BoxPrivKey) {
|
||||||
return newBoxKeys()
|
return crypto.NewBoxKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a new signing keypair. The signing keys are used to derive the
|
// Generates a new signing keypair. The signing keys are used to derive the
|
||||||
// structure of the spanning tree.
|
// structure of the spanning tree.
|
||||||
func (c *Core) NewSigningKeys() (*sigPubKey, *sigPrivKey) {
|
func (c *Core) NewSigningKeys() (*crypto.SigPubKey, *crypto.SigPrivKey) {
|
||||||
return newSigKeys()
|
return crypto.NewSigKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the node ID.
|
// Gets the node ID.
|
||||||
func (c *Core) GetNodeID() *NodeID {
|
func (c *Core) GetNodeID() *crypto.NodeID {
|
||||||
return getNodeID(&c.boxPub)
|
return crypto.GetNodeID(&c.boxPub)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the tree ID.
|
// Gets the tree ID.
|
||||||
func (c *Core) GetTreeID() *TreeID {
|
func (c *Core) GetTreeID() *crypto.TreeID {
|
||||||
return getTreeID(&c.sigPub)
|
return crypto.GetTreeID(&c.sigPub)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the IPv6 address of the Yggdrasil node. This is always a /128.
|
// Gets the IPv6 address of the Yggdrasil node. This is always a /128.
|
||||||
func (c *Core) GetAddress() *net.IP {
|
func (c *Core) GetAddress() *net.IP {
|
||||||
address := net.IP(address_addrForNodeID(c.GetNodeID())[:])
|
address := net.IP(address.AddrForNodeID(c.GetNodeID())[:])
|
||||||
return &address
|
return &address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the routed IPv6 subnet of the Yggdrasil node. This is always a /64.
|
// Gets the routed IPv6 subnet of the Yggdrasil node. This is always a /64.
|
||||||
func (c *Core) GetSubnet() *net.IPNet {
|
func (c *Core) GetSubnet() *net.IPNet {
|
||||||
subnet := address_subnetForNodeID(c.GetNodeID())[:]
|
subnet := address.SubnetForNodeID(c.GetNodeID())[:]
|
||||||
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
|
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
|
return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the nodeinfo.
|
||||||
|
func (c *Core) GetNodeInfo() nodeinfoPayload {
|
||||||
|
return c.nodeinfo.getNodeInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the nodeinfo.
|
||||||
|
func (c *Core) SetNodeInfo(nodeinfo interface{}) {
|
||||||
|
c.nodeinfo.setNodeInfo(nodeinfo)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the output logger of the Yggdrasil node after startup. This may be
|
// Sets the output logger of the Yggdrasil node after startup. This may be
|
||||||
// useful if you want to redirect the output later.
|
// useful if you want to redirect the output later.
|
||||||
func (c *Core) SetLogger(log *log.Logger) {
|
func (c *Core) SetLogger(log *log.Logger) {
|
||||||
@ -293,10 +306,10 @@ func (c *Core) GetTUNDefaultIfTAPMode() bool {
|
|||||||
|
|
||||||
// Gets the current TUN/TAP interface name.
|
// Gets the current TUN/TAP interface name.
|
||||||
func (c *Core) GetTUNIfName() string {
|
func (c *Core) GetTUNIfName() string {
|
||||||
return c.tun.iface.Name()
|
return c.router.tun.iface.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the current TUN/TAP interface MTU.
|
// Gets the current TUN/TAP interface MTU.
|
||||||
func (c *Core) GetTUNIfMTU() int {
|
func (c *Core) GetTUNIfMTU() int {
|
||||||
return c.tun.mtu
|
return c.router.tun.mtu
|
||||||
}
|
}
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
package yggdrasil
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
This part of the package wraps crypto operations needed elsewhere
|
|
||||||
|
|
||||||
In particular, it exposes key generation for ed25519 and nacl box
|
|
||||||
|
|
||||||
It also defines NodeID and TreeID as hashes of keys, and wraps hash functions
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha512"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/nacl/box"
|
|
||||||
)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// NodeID and TreeID
|
|
||||||
|
|
||||||
const NodeIDLen = sha512.Size
|
|
||||||
const TreeIDLen = sha512.Size
|
|
||||||
const handleLen = 8
|
|
||||||
|
|
||||||
type NodeID [NodeIDLen]byte
|
|
||||||
type TreeID [TreeIDLen]byte
|
|
||||||
type handle [handleLen]byte
|
|
||||||
|
|
||||||
func getNodeID(pub *boxPubKey) *NodeID {
|
|
||||||
h := sha512.Sum512(pub[:])
|
|
||||||
return (*NodeID)(&h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTreeID(pub *sigPubKey) *TreeID {
|
|
||||||
h := sha512.Sum512(pub[:])
|
|
||||||
return (*TreeID)(&h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHandle() *handle {
|
|
||||||
var h handle
|
|
||||||
_, err := rand.Read(h[:])
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &h
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Signatures
|
|
||||||
|
|
||||||
const sigPubKeyLen = ed25519.PublicKeySize
|
|
||||||
const sigPrivKeyLen = ed25519.PrivateKeySize
|
|
||||||
const sigLen = ed25519.SignatureSize
|
|
||||||
|
|
||||||
type sigPubKey [sigPubKeyLen]byte
|
|
||||||
type sigPrivKey [sigPrivKeyLen]byte
|
|
||||||
type sigBytes [sigLen]byte
|
|
||||||
|
|
||||||
func newSigKeys() (*sigPubKey, *sigPrivKey) {
|
|
||||||
var pub sigPubKey
|
|
||||||
var priv sigPrivKey
|
|
||||||
pubSlice, privSlice, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
copy(pub[:], pubSlice)
|
|
||||||
copy(priv[:], privSlice)
|
|
||||||
return &pub, &priv
|
|
||||||
}
|
|
||||||
|
|
||||||
func sign(priv *sigPrivKey, msg []byte) *sigBytes {
|
|
||||||
var sig sigBytes
|
|
||||||
sigSlice := ed25519.Sign(priv[:], msg)
|
|
||||||
copy(sig[:], sigSlice)
|
|
||||||
return &sig
|
|
||||||
}
|
|
||||||
|
|
||||||
func verify(pub *sigPubKey, msg []byte, sig *sigBytes) bool {
|
|
||||||
// Should sig be an array instead of a slice?...
|
|
||||||
// It's fixed size, but
|
|
||||||
return ed25519.Verify(pub[:], msg, sig[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// NaCl-like crypto "box" (curve25519+xsalsa20+poly1305)
|
|
||||||
|
|
||||||
const boxPubKeyLen = 32
|
|
||||||
const boxPrivKeyLen = 32
|
|
||||||
const boxSharedKeyLen = 32
|
|
||||||
const boxNonceLen = 24
|
|
||||||
const boxOverhead = box.Overhead
|
|
||||||
|
|
||||||
type boxPubKey [boxPubKeyLen]byte
|
|
||||||
type boxPrivKey [boxPrivKeyLen]byte
|
|
||||||
type boxSharedKey [boxSharedKeyLen]byte
|
|
||||||
type boxNonce [boxNonceLen]byte
|
|
||||||
|
|
||||||
func newBoxKeys() (*boxPubKey, *boxPrivKey) {
|
|
||||||
pubBytes, privBytes, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
pub := (*boxPubKey)(pubBytes)
|
|
||||||
priv := (*boxPrivKey)(privBytes)
|
|
||||||
return pub, priv
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSharedKey(myPrivKey *boxPrivKey,
|
|
||||||
othersPubKey *boxPubKey) *boxSharedKey {
|
|
||||||
var shared [boxSharedKeyLen]byte
|
|
||||||
priv := (*[boxPrivKeyLen]byte)(myPrivKey)
|
|
||||||
pub := (*[boxPubKeyLen]byte)(othersPubKey)
|
|
||||||
box.Precompute(&shared, pub, priv)
|
|
||||||
return (*boxSharedKey)(&shared)
|
|
||||||
}
|
|
||||||
|
|
||||||
func boxOpen(shared *boxSharedKey,
|
|
||||||
boxed []byte,
|
|
||||||
nonce *boxNonce) ([]byte, bool) {
|
|
||||||
out := util_getBytes()
|
|
||||||
s := (*[boxSharedKeyLen]byte)(shared)
|
|
||||||
n := (*[boxNonceLen]byte)(nonce)
|
|
||||||
unboxed, success := box.OpenAfterPrecomputation(out, boxed, n, s)
|
|
||||||
return unboxed, success
|
|
||||||
}
|
|
||||||
|
|
||||||
func boxSeal(shared *boxSharedKey, unboxed []byte, nonce *boxNonce) ([]byte, *boxNonce) {
|
|
||||||
if nonce == nil {
|
|
||||||
nonce = newBoxNonce()
|
|
||||||
}
|
|
||||||
nonce.update()
|
|
||||||
out := util_getBytes()
|
|
||||||
s := (*[boxSharedKeyLen]byte)(shared)
|
|
||||||
n := (*[boxNonceLen]byte)(nonce)
|
|
||||||
boxed := box.SealAfterPrecomputation(out, unboxed, n, s)
|
|
||||||
return boxed, nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBoxNonce() *boxNonce {
|
|
||||||
var nonce boxNonce
|
|
||||||
_, err := rand.Read(nonce[:])
|
|
||||||
for ; err == nil && nonce[0] == 0xff; _, err = rand.Read(nonce[:]) {
|
|
||||||
// Make sure nonce isn't too high
|
|
||||||
// This is just to make rollover unlikely to happen
|
|
||||||
// Rollover is fine, but it may kill the session and force it to reopen
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *boxNonce) update() {
|
|
||||||
oldNonce := *n
|
|
||||||
n[len(n)-1] += 2
|
|
||||||
for i := len(n) - 2; i >= 0; i-- {
|
|
||||||
if n[i+1] < oldNonce[i+1] {
|
|
||||||
n[i] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,6 +22,8 @@ import "net/http"
|
|||||||
import "runtime"
|
import "runtime"
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
|
import "github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
|
||||||
// Start the profiler in debug builds, if the required environment variable is set.
|
// Start the profiler in debug builds, if the required environment variable is set.
|
||||||
@ -48,8 +50,8 @@ func StartProfiler(log *log.Logger) error {
|
|||||||
// This function is only called by the simulator to set up a node with random
|
// This function is only called by the simulator to set up a node with random
|
||||||
// keys. It should not be used and may be removed in the future.
|
// keys. It should not be used and may be removed in the future.
|
||||||
func (c *Core) Init() {
|
func (c *Core) Init() {
|
||||||
bpub, bpriv := newBoxKeys()
|
bpub, bpriv := crypto.NewBoxKeys()
|
||||||
spub, spriv := newSigKeys()
|
spub, spriv := crypto.NewSigKeys()
|
||||||
c.init(bpub, bpriv, spub, spriv)
|
c.init(bpub, bpriv, spub, spriv)
|
||||||
c.switchTable.start()
|
c.switchTable.start()
|
||||||
c.router.start()
|
c.router.start()
|
||||||
@ -59,20 +61,20 @@ func (c *Core) Init() {
|
|||||||
|
|
||||||
// Core
|
// Core
|
||||||
|
|
||||||
func (c *Core) DEBUG_getSigningPublicKey() sigPubKey {
|
func (c *Core) DEBUG_getSigningPublicKey() crypto.SigPubKey {
|
||||||
return (sigPubKey)(c.sigPub)
|
return (crypto.SigPubKey)(c.sigPub)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getEncryptionPublicKey() boxPubKey {
|
func (c *Core) DEBUG_getEncryptionPublicKey() crypto.BoxPubKey {
|
||||||
return (boxPubKey)(c.boxPub)
|
return (crypto.BoxPubKey)(c.boxPub)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getSend() chan<- []byte {
|
func (c *Core) DEBUG_getSend() chan<- []byte {
|
||||||
return c.tun.send
|
return c.router.tun.send
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getRecv() <-chan []byte {
|
func (c *Core) DEBUG_getRecv() <-chan []byte {
|
||||||
return c.tun.recv
|
return c.router.tun.recv
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer
|
// Peer
|
||||||
@ -81,7 +83,7 @@ func (c *Core) DEBUG_getPeers() *peers {
|
|||||||
return &c.peers
|
return &c.peers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *peers) DEBUG_newPeer(box boxPubKey, sig sigPubKey, link boxSharedKey) *peer {
|
func (ps *peers) DEBUG_newPeer(box crypto.BoxPubKey, sig crypto.SigPubKey, link crypto.BoxSharedKey) *peer {
|
||||||
//in <-chan []byte,
|
//in <-chan []byte,
|
||||||
//out chan<- []byte) *peer {
|
//out chan<- []byte) *peer {
|
||||||
return ps.newPeer(&box, &sig, &link, "(simulator)") //, in, out)
|
return ps.newPeer(&box, &sig, &link, "(simulator)") //, in, out)
|
||||||
@ -98,7 +100,7 @@ func (ps *peers) DEBUG_startPeers() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (ps *peers) DEBUG_hasPeer(key sigPubKey) bool {
|
func (ps *peers) DEBUG_hasPeer(key crypto.SigPubKey) bool {
|
||||||
ports := ps.ports.Load().(map[switchPort]*peer)
|
ports := ps.ports.Load().(map[switchPort]*peer)
|
||||||
for _, p := range ports {
|
for _, p := range ports {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
@ -120,7 +122,7 @@ func (ps *peers) DEBUG_getPorts() map[switchPort]*peer {
|
|||||||
return newPeers
|
return newPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *peer) DEBUG_getSigKey() sigPubKey {
|
func (p *peer) DEBUG_getSigKey() crypto.SigPubKey {
|
||||||
return p.sig
|
return p.sig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,8 +294,8 @@ func (c *Core) DEBUG_startLoopbackUDPInterface() {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
func (c *Core) DEBUG_getAddr() *address {
|
func (c *Core) DEBUG_getAddr() *address.Address {
|
||||||
return address_addrForNodeID(&c.dht.nodeID)
|
return address.AddrForNodeID(&c.dht.nodeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_startTun(ifname string, iftapmode bool) {
|
func (c *Core) DEBUG_startTun(ifname string, iftapmode bool) {
|
||||||
@ -302,56 +304,56 @@ func (c *Core) DEBUG_startTun(ifname string, iftapmode bool) {
|
|||||||
|
|
||||||
func (c *Core) DEBUG_startTunWithMTU(ifname string, iftapmode bool, mtu int) {
|
func (c *Core) DEBUG_startTunWithMTU(ifname string, iftapmode bool, mtu int) {
|
||||||
addr := c.DEBUG_getAddr()
|
addr := c.DEBUG_getAddr()
|
||||||
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix))
|
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address.GetPrefix()))
|
||||||
if ifname != "none" {
|
if ifname != "none" {
|
||||||
err := c.tun.setup(ifname, iftapmode, straddr, mtu)
|
err := c.router.tun.setup(ifname, iftapmode, straddr, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
c.log.Println("Setup TUN/TAP:", c.tun.iface.Name(), straddr)
|
c.log.Println("Setup TUN/TAP:", c.router.tun.iface.Name(), straddr)
|
||||||
go func() { panic(c.tun.read()) }()
|
go func() { panic(c.router.tun.read()) }()
|
||||||
}
|
}
|
||||||
go func() { panic(c.tun.write()) }()
|
go func() { panic(c.router.tun.write()) }()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_stopTun() {
|
func (c *Core) DEBUG_stopTun() {
|
||||||
c.tun.close()
|
c.router.tun.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
func (c *Core) DEBUG_newBoxKeys() (*boxPubKey, *boxPrivKey) {
|
func (c *Core) DEBUG_newBoxKeys() (*crypto.BoxPubKey, *crypto.BoxPrivKey) {
|
||||||
return newBoxKeys()
|
return crypto.NewBoxKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getSharedKey(myPrivKey *boxPrivKey, othersPubKey *boxPubKey) *boxSharedKey {
|
func (c *Core) DEBUG_getSharedKey(myPrivKey *crypto.BoxPrivKey, othersPubKey *crypto.BoxPubKey) *crypto.BoxSharedKey {
|
||||||
return getSharedKey(myPrivKey, othersPubKey)
|
return crypto.GetSharedKey(myPrivKey, othersPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_newSigKeys() (*sigPubKey, *sigPrivKey) {
|
func (c *Core) DEBUG_newSigKeys() (*crypto.SigPubKey, *crypto.SigPrivKey) {
|
||||||
return newSigKeys()
|
return crypto.NewSigKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getNodeID(pub *boxPubKey) *NodeID {
|
func (c *Core) DEBUG_getNodeID(pub *crypto.BoxPubKey) *crypto.NodeID {
|
||||||
return getNodeID(pub)
|
return crypto.GetNodeID(pub)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_getTreeID(pub *sigPubKey) *TreeID {
|
func (c *Core) DEBUG_getTreeID(pub *crypto.SigPubKey) *crypto.TreeID {
|
||||||
return getTreeID(pub)
|
return crypto.GetTreeID(pub)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_addrForNodeID(nodeID *NodeID) string {
|
func (c *Core) DEBUG_addrForNodeID(nodeID *crypto.NodeID) string {
|
||||||
return net.IP(address_addrForNodeID(nodeID)[:]).String()
|
return net.IP(address.AddrForNodeID(nodeID)[:]).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_init(bpub []byte,
|
func (c *Core) DEBUG_init(bpub []byte,
|
||||||
bpriv []byte,
|
bpriv []byte,
|
||||||
spub []byte,
|
spub []byte,
|
||||||
spriv []byte) {
|
spriv []byte) {
|
||||||
var boxPub boxPubKey
|
var boxPub crypto.BoxPubKey
|
||||||
var boxPriv boxPrivKey
|
var boxPriv crypto.BoxPrivKey
|
||||||
var sigPub sigPubKey
|
var sigPub crypto.SigPubKey
|
||||||
var sigPriv sigPrivKey
|
var sigPriv crypto.SigPrivKey
|
||||||
copy(boxPub[:], bpub)
|
copy(boxPub[:], bpub)
|
||||||
copy(boxPriv[:], bpriv)
|
copy(boxPriv[:], bpriv)
|
||||||
copy(sigPub[:], spub)
|
copy(sigPub[:], spub)
|
||||||
@ -546,20 +548,20 @@ func DEBUG_simLinkPeers(p, q *peer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_simFixMTU() {
|
func (c *Core) DEBUG_simFixMTU() {
|
||||||
c.tun.mtu = 65535
|
c.router.tun.mtu = 65535
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
func Util_testAddrIDMask() {
|
func Util_testAddrIDMask() {
|
||||||
for idx := 0; idx < 16; idx++ {
|
for idx := 0; idx < 16; idx++ {
|
||||||
var orig NodeID
|
var orig crypto.NodeID
|
||||||
orig[8] = 42
|
orig[8] = 42
|
||||||
for bidx := 0; bidx < idx; bidx++ {
|
for bidx := 0; bidx < idx; bidx++ {
|
||||||
orig[bidx/8] |= (0x80 >> uint8(bidx%8))
|
orig[bidx/8] |= (0x80 >> uint8(bidx%8))
|
||||||
}
|
}
|
||||||
addr := address_addrForNodeID(&orig)
|
addr := address.AddrForNodeID(&orig)
|
||||||
nid, mask := addr.getNodeIDandMask()
|
nid, mask := addr.GetNodeIDandMask()
|
||||||
for b := 0; b < len(mask); b++ {
|
for b := 0; b < len(mask); b++ {
|
||||||
nid[b] &= mask[b]
|
nid[b] &= mask[b]
|
||||||
orig[b] &= mask[b]
|
orig[b] &= mask[b]
|
||||||
|
@ -8,6 +8,8 @@ package yggdrasil
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dht_lookup_size = 16
|
const dht_lookup_size = 16
|
||||||
@ -15,8 +17,8 @@ const dht_lookup_size = 16
|
|||||||
// dhtInfo represents everything we know about a node in the DHT.
|
// 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 it's NodeID, coords, and timing/ping related info for deciding who/when to ping nodes for maintenance.
|
||||||
type dhtInfo struct {
|
type dhtInfo struct {
|
||||||
nodeID_hidden *NodeID
|
nodeID_hidden *crypto.NodeID
|
||||||
key boxPubKey
|
key crypto.BoxPubKey
|
||||||
coords []byte
|
coords []byte
|
||||||
recv time.Time // When we last received a message
|
recv time.Time // When we last received a message
|
||||||
pings int // Time out if at least 3 consecutive maintenance pings drop
|
pings int // Time out if at least 3 consecutive maintenance pings drop
|
||||||
@ -24,9 +26,9 @@ type dhtInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the *NodeID associated with dhtInfo.key, calculating it on the fly the first time or from a cache all subsequent times.
|
// Returns the *NodeID associated with dhtInfo.key, calculating it on the fly the first time or from a cache all subsequent times.
|
||||||
func (info *dhtInfo) getNodeID() *NodeID {
|
func (info *dhtInfo) getNodeID() *crypto.NodeID {
|
||||||
if info.nodeID_hidden == nil {
|
if info.nodeID_hidden == nil {
|
||||||
info.nodeID_hidden = getNodeID(&info.key)
|
info.nodeID_hidden = crypto.GetNodeID(&info.key)
|
||||||
}
|
}
|
||||||
return info.nodeID_hidden
|
return info.nodeID_hidden
|
||||||
}
|
}
|
||||||
@ -34,36 +36,36 @@ func (info *dhtInfo) getNodeID() *NodeID {
|
|||||||
// Request for a node to do a lookup.
|
// Request for a node to do a lookup.
|
||||||
// Includes our key and coords so they can send a response back, and the destination NodeID we want to ask about.
|
// Includes our key and coords so they can send a response back, and the destination NodeID we want to ask about.
|
||||||
type dhtReq struct {
|
type dhtReq struct {
|
||||||
Key boxPubKey // Key of whoever asked
|
Key crypto.BoxPubKey // Key of whoever asked
|
||||||
Coords []byte // Coords of whoever asked
|
Coords []byte // Coords of whoever asked
|
||||||
Dest NodeID // NodeID they're asking about
|
Dest crypto.NodeID // NodeID they're asking about
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response to a DHT lookup.
|
// Response to a DHT lookup.
|
||||||
// Includes the key and coords of the node that's responding, and the destination they were asked about.
|
// Includes the key and coords of the node that's responding, and the destination they were asked about.
|
||||||
// The main part is Infos []*dhtInfo, the lookup response.
|
// The main part is Infos []*dhtInfo, the lookup response.
|
||||||
type dhtRes struct {
|
type dhtRes struct {
|
||||||
Key boxPubKey // key of the sender
|
Key crypto.BoxPubKey // key of the sender
|
||||||
Coords []byte // coords of the sender
|
Coords []byte // coords of the sender
|
||||||
Dest NodeID
|
Dest crypto.NodeID
|
||||||
Infos []*dhtInfo // response
|
Infos []*dhtInfo // response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parts of a DHT req usable as a key in a map.
|
// Parts of a DHT req usable as a key in a map.
|
||||||
type dhtReqKey struct {
|
type dhtReqKey struct {
|
||||||
key boxPubKey
|
key crypto.BoxPubKey
|
||||||
dest NodeID
|
dest crypto.NodeID
|
||||||
}
|
}
|
||||||
|
|
||||||
// The main DHT struct.
|
// The main DHT struct.
|
||||||
type dht struct {
|
type dht struct {
|
||||||
core *Core
|
core *Core
|
||||||
nodeID NodeID
|
nodeID crypto.NodeID
|
||||||
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
||||||
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
||||||
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
||||||
// These next two could be replaced by a single linked list or similar...
|
// These next two could be replaced by a single linked list or similar...
|
||||||
table map[NodeID]*dhtInfo
|
table map[crypto.NodeID]*dhtInfo
|
||||||
imp []*dhtInfo
|
imp []*dhtInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +82,12 @@ func (t *dht) init(c *Core) {
|
|||||||
// This empties all info from the DHT and drops outstanding requests.
|
// This empties all info from the DHT and drops outstanding requests.
|
||||||
func (t *dht) reset() {
|
func (t *dht) reset() {
|
||||||
t.reqs = make(map[dhtReqKey]time.Time)
|
t.reqs = make(map[dhtReqKey]time.Time)
|
||||||
t.table = make(map[NodeID]*dhtInfo)
|
t.table = make(map[crypto.NodeID]*dhtInfo)
|
||||||
t.imp = nil
|
t.imp = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does a DHT lookup and returns up to dht_lookup_size results.
|
// Does a DHT lookup and returns up to dht_lookup_size results.
|
||||||
func (t *dht) lookup(nodeID *NodeID, everything bool) []*dhtInfo {
|
func (t *dht) lookup(nodeID *crypto.NodeID, everything bool) []*dhtInfo {
|
||||||
results := make([]*dhtInfo, 0, len(t.table))
|
results := make([]*dhtInfo, 0, len(t.table))
|
||||||
for _, info := range t.table {
|
for _, info := range t.table {
|
||||||
results = append(results, info)
|
results = append(results, info)
|
||||||
@ -133,9 +135,9 @@ func (t *dht) insert(info *dhtInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return true if first/second/third are (partially) ordered correctly.
|
// Return true if first/second/third are (partially) ordered correctly.
|
||||||
func dht_ordered(first, second, third *NodeID) bool {
|
func dht_ordered(first, second, third *crypto.NodeID) bool {
|
||||||
lessOrEqual := func(first, second *NodeID) bool {
|
lessOrEqual := func(first, second *crypto.NodeID) bool {
|
||||||
for idx := 0; idx < NodeIDLen; idx++ {
|
for idx := 0; idx < crypto.NodeIDLen; idx++ {
|
||||||
if first[idx] > second[idx] {
|
if first[idx] > second[idx] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ func (t *dht) sendRes(res *dhtRes, req *dhtReq) {
|
|||||||
// Send a reply for a dhtReq
|
// Send a reply for a dhtReq
|
||||||
bs := res.encode()
|
bs := res.encode()
|
||||||
shared := t.core.sessions.getSharedKey(&t.core.boxPriv, &req.Key)
|
shared := t.core.sessions.getSharedKey(&t.core.boxPriv, &req.Key)
|
||||||
payload, nonce := boxSeal(shared, bs, nil)
|
payload, nonce := crypto.BoxSeal(shared, bs, nil)
|
||||||
p := wire_protoTrafficPacket{
|
p := wire_protoTrafficPacket{
|
||||||
Coords: req.Coords,
|
Coords: req.Coords,
|
||||||
ToKey: req.Key,
|
ToKey: req.Key,
|
||||||
@ -252,7 +254,7 @@ func (t *dht) sendReq(req *dhtReq, dest *dhtInfo) {
|
|||||||
// Send a dhtReq to the node in dhtInfo
|
// Send a dhtReq to the node in dhtInfo
|
||||||
bs := req.encode()
|
bs := req.encode()
|
||||||
shared := t.core.sessions.getSharedKey(&t.core.boxPriv, &dest.key)
|
shared := t.core.sessions.getSharedKey(&t.core.boxPriv, &dest.key)
|
||||||
payload, nonce := boxSeal(shared, bs, nil)
|
payload, nonce := crypto.BoxSeal(shared, bs, nil)
|
||||||
p := wire_protoTrafficPacket{
|
p := wire_protoTrafficPacket{
|
||||||
Coords: dest.coords,
|
Coords: dest.coords,
|
||||||
ToKey: dest.key,
|
ToKey: dest.key,
|
||||||
@ -267,7 +269,7 @@ func (t *dht) sendReq(req *dhtReq, dest *dhtInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sends a lookup to this info, looking for the target.
|
// Sends a lookup to this info, looking for the target.
|
||||||
func (t *dht) ping(info *dhtInfo, target *NodeID) {
|
func (t *dht) ping(info *dhtInfo, target *crypto.NodeID) {
|
||||||
// Creates a req for the node at dhtInfo, asking them about the target (if one is given) or themself (if no target is given)
|
// Creates a req for the node at dhtInfo, asking them about the target (if one is given) or themself (if no target is given)
|
||||||
if target == nil {
|
if target == nil {
|
||||||
target = &t.nodeID
|
target = &t.nodeID
|
||||||
|
@ -17,6 +17,8 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/net/icmp"
|
"golang.org/x/net/icmp"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
|
||||||
type macAddress [6]byte
|
type macAddress [6]byte
|
||||||
@ -24,10 +26,10 @@ type macAddress [6]byte
|
|||||||
const len_ETHER = 14
|
const len_ETHER = 14
|
||||||
|
|
||||||
type icmpv6 struct {
|
type icmpv6 struct {
|
||||||
tun *tunDevice
|
tun *tunAdapter
|
||||||
mylladdr net.IP
|
mylladdr net.IP
|
||||||
mymac macAddress
|
mymac macAddress
|
||||||
peermacs map[address]neighbor
|
peermacs map[address.Address]neighbor
|
||||||
}
|
}
|
||||||
|
|
||||||
type neighbor struct {
|
type neighbor struct {
|
||||||
@ -57,9 +59,9 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
|
|||||||
// Initialises the ICMPv6 module by assigning our link-local IPv6 address and
|
// Initialises the ICMPv6 module by assigning our link-local IPv6 address and
|
||||||
// our MAC address. ICMPv6 messages will always appear to originate from these
|
// our MAC address. ICMPv6 messages will always appear to originate from these
|
||||||
// addresses.
|
// addresses.
|
||||||
func (i *icmpv6) init(t *tunDevice) {
|
func (i *icmpv6) init(t *tunAdapter) {
|
||||||
i.tun = t
|
i.tun = t
|
||||||
i.peermacs = make(map[address]neighbor)
|
i.peermacs = make(map[address.Address]neighbor)
|
||||||
|
|
||||||
// Our MAC address and link-local address
|
// Our MAC address and link-local address
|
||||||
i.mymac = macAddress{
|
i.mymac = macAddress{
|
||||||
@ -172,7 +174,7 @@ func (i *icmpv6) parse_packet_tun(datain []byte, datamac *[]byte) ([]byte, error
|
|||||||
}
|
}
|
||||||
case ipv6.ICMPTypeNeighborAdvertisement:
|
case ipv6.ICMPTypeNeighborAdvertisement:
|
||||||
if datamac != nil {
|
if datamac != nil {
|
||||||
var addr address
|
var addr address.Address
|
||||||
var mac macAddress
|
var mac macAddress
|
||||||
copy(addr[:], ipv6Header.Src[:])
|
copy(addr[:], ipv6Header.Src[:])
|
||||||
copy(mac[:], (*datamac)[:])
|
copy(mac[:], (*datamac)[:])
|
||||||
@ -254,7 +256,7 @@ func (i *icmpv6) create_icmpv6_tun(dst net.IP, src net.IP, mtype ipv6.ICMPType,
|
|||||||
return responsePacket, nil
|
return responsePacket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *icmpv6) create_ndp_tap(dst address) ([]byte, error) {
|
func (i *icmpv6) create_ndp_tap(dst address.Address) ([]byte, error) {
|
||||||
// Create the ND payload
|
// Create the ND payload
|
||||||
var payload [28]byte
|
var payload [28]byte
|
||||||
copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00})
|
copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
@ -263,7 +265,7 @@ func (i *icmpv6) create_ndp_tap(dst address) ([]byte, error) {
|
|||||||
copy(payload[22:28], i.mymac[:6])
|
copy(payload[22:28], i.mymac[:6])
|
||||||
|
|
||||||
// Create the ICMPv6 solicited-node address
|
// Create the ICMPv6 solicited-node address
|
||||||
var dstaddr address
|
var dstaddr address.Address
|
||||||
copy(dstaddr[:13], []byte{
|
copy(dstaddr[:13], []byte{
|
||||||
0xFF, 0x02, 0x00, 0x00,
|
0xFF, 0x02, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00,
|
||||||
@ -296,13 +298,13 @@ func (i *icmpv6) create_ndp_tap(dst address) ([]byte, error) {
|
|||||||
// to the Yggdrasil TAP adapter.
|
// to the Yggdrasil TAP adapter.
|
||||||
func (i *icmpv6) handle_ndp(in []byte) ([]byte, error) {
|
func (i *icmpv6) handle_ndp(in []byte) ([]byte, error) {
|
||||||
// Ignore NDP requests for anything outside of fd00::/8
|
// Ignore NDP requests for anything outside of fd00::/8
|
||||||
var source address
|
var source address.Address
|
||||||
copy(source[:], in[8:])
|
copy(source[:], in[8:])
|
||||||
var snet subnet
|
var snet address.Subnet
|
||||||
copy(snet[:], in[8:])
|
copy(snet[:], in[8:])
|
||||||
switch {
|
switch {
|
||||||
case source.isValid():
|
case source.IsValid():
|
||||||
case snet.isValid():
|
case snet.IsValid():
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("Not an NDP for 0200::/7")
|
return nil, errors.New("Not an NDP for 0200::/7")
|
||||||
}
|
}
|
||||||
|
177
src/yggdrasil/nodeinfo.go
Normal file
177
src/yggdrasil/nodeinfo.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package yggdrasil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeinfoPayload []byte
|
||||||
|
|
||||||
|
type nodeinfoCached struct {
|
||||||
|
payload nodeinfoPayload
|
||||||
|
created time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeinfoCallback struct {
|
||||||
|
call func(nodeinfo *nodeinfoPayload)
|
||||||
|
created time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a session nodeinfo packet.
|
||||||
|
type nodeinfoReqRes struct {
|
||||||
|
SendPermPub crypto.BoxPubKey // Sender's permanent key
|
||||||
|
SendCoords []byte // Sender's coords
|
||||||
|
IsResponse bool
|
||||||
|
NodeInfo nodeinfoPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.core = core
|
||||||
|
m.callbacks = make(map[crypto.BoxPubKey]nodeinfoCallback)
|
||||||
|
m.cache = make(map[crypto.BoxPubKey]nodeinfoCached)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
m.callbacksMutex.Lock()
|
||||||
|
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.Sleep(time.Second * 30)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.callbacks[sender] = nodeinfoCallback{
|
||||||
|
created: time.Now(),
|
||||||
|
call: call,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles the callback, if there is one
|
||||||
|
func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo nodeinfoPayload) {
|
||||||
|
m.callbacksMutex.Lock()
|
||||||
|
defer m.callbacksMutex.Unlock()
|
||||||
|
if callback, ok := m.callbacks[sender]; ok {
|
||||||
|
callback.call(&nodeinfo)
|
||||||
|
delete(m.callbacks, sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current node's nodeinfo
|
||||||
|
func (m *nodeinfo) getNodeInfo() nodeinfoPayload {
|
||||||
|
m.myNodeInfoMutex.RLock()
|
||||||
|
defer m.myNodeInfoMutex.RUnlock()
|
||||||
|
return m.myNodeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the current node's nodeinfo
|
||||||
|
func (m *nodeinfo) setNodeInfo(given interface{}) error {
|
||||||
|
m.myNodeInfoMutex.Lock()
|
||||||
|
defer m.myNodeInfoMutex.Unlock()
|
||||||
|
newnodeinfo := map[string]interface{}{
|
||||||
|
"buildname": GetBuildName(),
|
||||||
|
"buildversion": GetBuildVersion(),
|
||||||
|
"buildplatform": runtime.GOOS,
|
||||||
|
"buildarch": runtime.GOARCH,
|
||||||
|
}
|
||||||
|
if nodeinfomap, ok := given.(map[string]interface{}); ok {
|
||||||
|
for key, value := range nodeinfomap {
|
||||||
|
if _, ok := newnodeinfo[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newnodeinfo[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newjson, err := json.Marshal(newnodeinfo); err == nil {
|
||||||
|
if len(newjson) > 16384 {
|
||||||
|
return errors.New("NodeInfo exceeds max length of 16384 bytes")
|
||||||
|
}
|
||||||
|
m.myNodeInfo = newjson
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add nodeinfo into the cache for a node
|
||||||
|
func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload nodeinfoPayload) {
|
||||||
|
m.cacheMutex.Lock()
|
||||||
|
defer m.cacheMutex.Unlock()
|
||||||
|
m.cache[key] = nodeinfoCached{
|
||||||
|
created: time.Now(),
|
||||||
|
payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a nodeinfo entry from the cache
|
||||||
|
func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (nodeinfoPayload, error) {
|
||||||
|
m.cacheMutex.RLock()
|
||||||
|
defer m.cacheMutex.RUnlock()
|
||||||
|
if nodeinfo, ok := m.cache[key]; ok {
|
||||||
|
return nodeinfo.payload, nil
|
||||||
|
}
|
||||||
|
return nodeinfoPayload{}, errors.New("No cache entry found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles a nodeinfo request/response - called from the router
|
||||||
|
func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) {
|
||||||
|
if nodeinfo.IsResponse {
|
||||||
|
m.callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||||
|
m.addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
|
table := m.core.switchTable.table.Load().(lookupTable)
|
||||||
|
nodeinfo := nodeinfoReqRes{
|
||||||
|
SendCoords: table.self.getCoords(),
|
||||||
|
IsResponse: isResponse,
|
||||||
|
NodeInfo: m.core.nodeinfo.getNodeInfo(),
|
||||||
|
}
|
||||||
|
bs := nodeinfo.encode()
|
||||||
|
shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key)
|
||||||
|
payload, nonce := crypto.BoxSeal(shared, bs, nil)
|
||||||
|
p := wire_protoTrafficPacket{
|
||||||
|
Coords: coords,
|
||||||
|
ToKey: key,
|
||||||
|
FromKey: m.core.boxPub,
|
||||||
|
Nonce: *nonce,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
packet := p.encode()
|
||||||
|
m.core.router.out(packet)
|
||||||
|
}
|
@ -8,6 +8,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The peers struct represents peers with an active connection.
|
// The peers struct represents peers with an active connection.
|
||||||
@ -19,7 +22,7 @@ type peers struct {
|
|||||||
mutex sync.Mutex // Synchronize writes to atomic
|
mutex sync.Mutex // Synchronize writes to atomic
|
||||||
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
||||||
authMutex sync.RWMutex
|
authMutex sync.RWMutex
|
||||||
allowedEncryptionPublicKeys map[boxPubKey]struct{}
|
allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes the peers struct.
|
// Initializes the peers struct.
|
||||||
@ -28,11 +31,11 @@ func (ps *peers) init(c *Core) {
|
|||||||
defer ps.mutex.Unlock()
|
defer ps.mutex.Unlock()
|
||||||
ps.putPorts(make(map[switchPort]*peer))
|
ps.putPorts(make(map[switchPort]*peer))
|
||||||
ps.core = c
|
ps.core = c
|
||||||
ps.allowedEncryptionPublicKeys = make(map[boxPubKey]struct{})
|
ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty.
|
// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty.
|
||||||
func (ps *peers) isAllowedEncryptionPublicKey(box *boxPubKey) bool {
|
func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool {
|
||||||
ps.authMutex.RLock()
|
ps.authMutex.RLock()
|
||||||
defer ps.authMutex.RUnlock()
|
defer ps.authMutex.RUnlock()
|
||||||
_, isIn := ps.allowedEncryptionPublicKeys[*box]
|
_, isIn := ps.allowedEncryptionPublicKeys[*box]
|
||||||
@ -40,24 +43,24 @@ func (ps *peers) isAllowedEncryptionPublicKey(box *boxPubKey) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds a key to the whitelist.
|
// Adds a key to the whitelist.
|
||||||
func (ps *peers) addAllowedEncryptionPublicKey(box *boxPubKey) {
|
func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
||||||
ps.authMutex.Lock()
|
ps.authMutex.Lock()
|
||||||
defer ps.authMutex.Unlock()
|
defer ps.authMutex.Unlock()
|
||||||
ps.allowedEncryptionPublicKeys[*box] = struct{}{}
|
ps.allowedEncryptionPublicKeys[*box] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a key from the whitelist.
|
// Removes a key from the whitelist.
|
||||||
func (ps *peers) removeAllowedEncryptionPublicKey(box *boxPubKey) {
|
func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
||||||
ps.authMutex.Lock()
|
ps.authMutex.Lock()
|
||||||
defer ps.authMutex.Unlock()
|
defer ps.authMutex.Unlock()
|
||||||
delete(ps.allowedEncryptionPublicKeys, *box)
|
delete(ps.allowedEncryptionPublicKeys, *box)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the whitelist of allowed keys for incoming connections.
|
// Gets the whitelist of allowed keys for incoming connections.
|
||||||
func (ps *peers) getAllowedEncryptionPublicKeys() []boxPubKey {
|
func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey {
|
||||||
ps.authMutex.RLock()
|
ps.authMutex.RLock()
|
||||||
defer ps.authMutex.RUnlock()
|
defer ps.authMutex.RUnlock()
|
||||||
keys := make([]boxPubKey, 0, len(ps.allowedEncryptionPublicKeys))
|
keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys))
|
||||||
for key := range ps.allowedEncryptionPublicKeys {
|
for key := range ps.allowedEncryptionPublicKeys {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
}
|
}
|
||||||
@ -81,30 +84,30 @@ type peer struct {
|
|||||||
// BUG: sync/atomic, 32 bit platforms need the above to be the first element
|
// BUG: sync/atomic, 32 bit platforms need the above to be the first element
|
||||||
core *Core
|
core *Core
|
||||||
port switchPort
|
port switchPort
|
||||||
box boxPubKey
|
box crypto.BoxPubKey
|
||||||
sig sigPubKey
|
sig crypto.SigPubKey
|
||||||
shared boxSharedKey
|
shared crypto.BoxSharedKey
|
||||||
linkShared boxSharedKey
|
linkShared crypto.BoxSharedKey
|
||||||
endpoint string
|
endpoint string
|
||||||
friendlyName string
|
|
||||||
firstSeen time.Time // To track uptime for getPeers
|
firstSeen time.Time // To track uptime for getPeers
|
||||||
linkOut (chan []byte) // used for protocol traffic (to bypass queues)
|
linkOut (chan []byte) // used for protocol traffic (to bypass queues)
|
||||||
doSend (chan struct{}) // tell the linkLoop to send a switchMsg
|
doSend (chan struct{}) // tell the linkLoop to send a switchMsg
|
||||||
dinfo *dhtInfo // used to keep the DHT working
|
dinfo (chan *dhtInfo) // used to keep the DHT working
|
||||||
out func([]byte) // Set up by whatever created the peers struct, used to send packets to other nodes
|
out func([]byte) // Set up by whatever created the peers struct, used to send packets to other nodes
|
||||||
close func() // Called when a peer is removed, to close the underlying connection, or via admin api
|
close func() // Called when a peer is removed, to close the underlying connection, or via admin api
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
|
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
|
||||||
func (ps *peers) newPeer(box *boxPubKey, sig *sigPubKey, linkShared *boxSharedKey, endpoint string) *peer {
|
func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string) *peer {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
p := peer{box: *box,
|
p := peer{box: *box,
|
||||||
sig: *sig,
|
sig: *sig,
|
||||||
shared: *getSharedKey(&ps.core.boxPriv, box),
|
shared: *crypto.GetSharedKey(&ps.core.boxPriv, box),
|
||||||
linkShared: *linkShared,
|
linkShared: *linkShared,
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
firstSeen: now,
|
firstSeen: now,
|
||||||
doSend: make(chan struct{}, 1),
|
doSend: make(chan struct{}, 1),
|
||||||
|
dinfo: make(chan *dhtInfo, 1),
|
||||||
core: ps.core}
|
core: ps.core}
|
||||||
ps.mutex.Lock()
|
ps.mutex.Lock()
|
||||||
defer ps.mutex.Unlock()
|
defer ps.mutex.Unlock()
|
||||||
@ -177,6 +180,8 @@ func (p *peer) doSendSwitchMsgs() {
|
|||||||
func (p *peer) linkLoop() {
|
func (p *peer) linkLoop() {
|
||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
|
p.doSendSwitchMsgs()
|
||||||
|
var dinfo *dhtInfo
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case _, ok := <-p.doSend:
|
case _, ok := <-p.doSend:
|
||||||
@ -184,12 +189,10 @@ func (p *peer) linkLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.sendSwitchMsg()
|
p.sendSwitchMsg()
|
||||||
|
case dinfo = <-p.dinfo:
|
||||||
case _ = <-tick.C:
|
case _ = <-tick.C:
|
||||||
//break // FIXME disabled the below completely to test something
|
if dinfo != nil {
|
||||||
pdinfo := p.dinfo // FIXME this is a bad workarond NPE on the next line
|
p.core.dht.peers <- dinfo
|
||||||
if pdinfo != nil {
|
|
||||||
dinfo := *pdinfo
|
|
||||||
p.core.dht.peers <- &dinfo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,15 +215,16 @@ func (p *peer) handlePacket(packet []byte) {
|
|||||||
case wire_LinkProtocolTraffic:
|
case wire_LinkProtocolTraffic:
|
||||||
p.handleLinkTraffic(packet)
|
p.handleLinkTraffic(packet)
|
||||||
default:
|
default:
|
||||||
util_putBytes(packet)
|
util.PutBytes(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called to handle traffic or protocolTraffic packets.
|
// Called to handle traffic or protocolTraffic packets.
|
||||||
// In either case, this reads from the coords of the packet header, does a switch lookup, and forwards to the next node.
|
// In either case, this reads from the coords of the packet header, does a switch lookup, and forwards to the next node.
|
||||||
func (p *peer) handleTraffic(packet []byte, pTypeLen int) {
|
func (p *peer) handleTraffic(packet []byte, pTypeLen int) {
|
||||||
if p.port != 0 && p.dinfo == nil {
|
table := p.core.switchTable.getTable()
|
||||||
// Drop traffic until the peer manages to send us at least one good switchMsg
|
if _, isIn := table.elems[p.port]; !isIn && p.port != 0 {
|
||||||
|
// Drop traffic if the peer isn't in the switch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.core.switchTable.packetIn <- packet
|
p.core.switchTable.packetIn <- packet
|
||||||
@ -236,13 +240,13 @@ func (p *peer) sendPacket(packet []byte) {
|
|||||||
// This wraps the packet in the inner (ephemeral) and outer (permanent) crypto layers.
|
// This wraps the packet in the inner (ephemeral) and outer (permanent) crypto layers.
|
||||||
// It sends it to p.linkOut, which bypasses the usual packet queues.
|
// It sends it to p.linkOut, which bypasses the usual packet queues.
|
||||||
func (p *peer) sendLinkPacket(packet []byte) {
|
func (p *peer) sendLinkPacket(packet []byte) {
|
||||||
innerPayload, innerNonce := boxSeal(&p.linkShared, packet, nil)
|
innerPayload, innerNonce := crypto.BoxSeal(&p.linkShared, packet, nil)
|
||||||
innerLinkPacket := wire_linkProtoTrafficPacket{
|
innerLinkPacket := wire_linkProtoTrafficPacket{
|
||||||
Nonce: *innerNonce,
|
Nonce: *innerNonce,
|
||||||
Payload: innerPayload,
|
Payload: innerPayload,
|
||||||
}
|
}
|
||||||
outerPayload := innerLinkPacket.encode()
|
outerPayload := innerLinkPacket.encode()
|
||||||
bs, nonce := boxSeal(&p.shared, outerPayload, nil)
|
bs, nonce := crypto.BoxSeal(&p.shared, outerPayload, nil)
|
||||||
linkPacket := wire_linkProtoTrafficPacket{
|
linkPacket := wire_linkProtoTrafficPacket{
|
||||||
Nonce: *nonce,
|
Nonce: *nonce,
|
||||||
Payload: bs,
|
Payload: bs,
|
||||||
@ -258,7 +262,7 @@ func (p *peer) handleLinkTraffic(bs []byte) {
|
|||||||
if !packet.decode(bs) {
|
if !packet.decode(bs) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
outerPayload, isOK := boxOpen(&p.shared, packet.Payload, &packet.Nonce)
|
outerPayload, isOK := crypto.BoxOpen(&p.shared, packet.Payload, &packet.Nonce)
|
||||||
if !isOK {
|
if !isOK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -266,7 +270,7 @@ func (p *peer) handleLinkTraffic(bs []byte) {
|
|||||||
if !innerPacket.decode(outerPayload) {
|
if !innerPacket.decode(outerPayload) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
payload, isOK := boxOpen(&p.linkShared, innerPacket.Payload, &innerPacket.Nonce)
|
payload, isOK := crypto.BoxOpen(&p.linkShared, innerPacket.Payload, &innerPacket.Nonce)
|
||||||
if !isOK {
|
if !isOK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -278,7 +282,7 @@ func (p *peer) handleLinkTraffic(bs []byte) {
|
|||||||
case wire_SwitchMsg:
|
case wire_SwitchMsg:
|
||||||
p.handleSwitchMsg(payload)
|
p.handleSwitchMsg(payload)
|
||||||
default:
|
default:
|
||||||
util_putBytes(bs)
|
util.PutBytes(bs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +296,7 @@ func (p *peer) sendSwitchMsg() {
|
|||||||
msg.Hops = append(msg.Hops, switchMsgHop{
|
msg.Hops = append(msg.Hops, switchMsgHop{
|
||||||
Port: p.port,
|
Port: p.port,
|
||||||
Next: p.sig,
|
Next: p.sig,
|
||||||
Sig: *sign(&p.core.sigPriv, bs),
|
Sig: *crypto.Sign(&p.core.sigPriv, bs),
|
||||||
})
|
})
|
||||||
packet := msg.encode()
|
packet := msg.encode()
|
||||||
p.sendLinkPacket(packet)
|
p.sendLinkPacket(packet)
|
||||||
@ -316,7 +320,7 @@ func (p *peer) handleSwitchMsg(packet []byte) {
|
|||||||
sigMsg.Hops = msg.Hops[:idx]
|
sigMsg.Hops = msg.Hops[:idx]
|
||||||
loc.coords = append(loc.coords, hop.Port)
|
loc.coords = append(loc.coords, hop.Port)
|
||||||
bs := getBytesForSig(&hop.Next, &sigMsg)
|
bs := getBytesForSig(&hop.Next, &sigMsg)
|
||||||
if !verify(&prevKey, bs, &hop.Sig) {
|
if !crypto.Verify(&prevKey, bs, &hop.Sig) {
|
||||||
p.core.peers.removePeer(p.port)
|
p.core.peers.removePeer(p.port)
|
||||||
}
|
}
|
||||||
prevKey = hop.Next
|
prevKey = hop.Next
|
||||||
@ -324,9 +328,7 @@ func (p *peer) handleSwitchMsg(packet []byte) {
|
|||||||
p.core.switchTable.handleMsg(&msg, p.port)
|
p.core.switchTable.handleMsg(&msg, p.port)
|
||||||
if !p.core.switchTable.checkRoot(&msg) {
|
if !p.core.switchTable.checkRoot(&msg) {
|
||||||
// Bad switch message
|
// Bad switch message
|
||||||
// Stop forwarding traffic from it
|
p.dinfo <- nil
|
||||||
// Stop refreshing it in the DHT
|
|
||||||
p.dinfo = nil
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Pass a mesage to the dht informing it that this peer (still) exists
|
// Pass a mesage to the dht informing it that this peer (still) exists
|
||||||
@ -335,13 +337,12 @@ func (p *peer) handleSwitchMsg(packet []byte) {
|
|||||||
key: p.box,
|
key: p.box,
|
||||||
coords: loc.getCoords(),
|
coords: loc.getCoords(),
|
||||||
}
|
}
|
||||||
//p.core.dht.peers <- &dinfo
|
p.dinfo <- &dinfo
|
||||||
p.dinfo = &dinfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This generates the bytes that we sign or check the signature of for a switchMsg.
|
// This generates the bytes that we sign or check the signature of for a switchMsg.
|
||||||
// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node.
|
// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node.
|
||||||
func getBytesForSig(next *sigPubKey, msg *switchMsg) []byte {
|
func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte {
|
||||||
var loc switchLocator
|
var loc switchLocator
|
||||||
for _, hop := range msg.Hops {
|
for _, hop := range msg.Hops {
|
||||||
loc.coords = append(loc.coords, hop.Port)
|
loc.coords = append(loc.coords, hop.Port)
|
||||||
|
@ -28,17 +28,23 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/net/icmp"
|
"golang.org/x/net/icmp"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
|
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
|
||||||
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
|
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
|
||||||
type router struct {
|
type router struct {
|
||||||
core *Core
|
core *Core
|
||||||
addr address
|
addr address.Address
|
||||||
subnet subnet
|
subnet address.Subnet
|
||||||
in <-chan []byte // packets we received from the network, link to peer's "out"
|
in <-chan []byte // packets we received from the network, link to peer's "out"
|
||||||
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
||||||
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
||||||
|
tun tunAdapter // TUN/TAP adapter
|
||||||
|
adapters []Adapter // Other adapters
|
||||||
recv chan<- []byte // place where the tun pulls received packets from
|
recv chan<- []byte // place where the tun pulls received packets from
|
||||||
send <-chan []byte // place where the tun puts outgoing packets
|
send <-chan []byte // place where the tun puts outgoing packets
|
||||||
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
||||||
@ -55,17 +61,17 @@ type router_recvPacket struct {
|
|||||||
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
|
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
|
||||||
func (r *router) init(core *Core) {
|
func (r *router) init(core *Core) {
|
||||||
r.core = core
|
r.core = core
|
||||||
r.addr = *address_addrForNodeID(&r.core.dht.nodeID)
|
r.addr = *address.AddrForNodeID(&r.core.dht.nodeID)
|
||||||
r.subnet = *address_subnetForNodeID(&r.core.dht.nodeID)
|
r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID)
|
||||||
in := make(chan []byte, 32) // TODO something better than this...
|
in := make(chan []byte, 32) // TODO something better than this...
|
||||||
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)")
|
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)")
|
||||||
p.out = func(packet []byte) {
|
p.out = func(packet []byte) {
|
||||||
// This is to make very sure it never blocks
|
// This is to make very sure it never blocks
|
||||||
select {
|
select {
|
||||||
case in <- packet:
|
case in <- packet:
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
util_putBytes(packet)
|
util.PutBytes(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.in = in
|
r.in = in
|
||||||
@ -75,12 +81,10 @@ func (r *router) init(core *Core) {
|
|||||||
send := make(chan []byte, 32)
|
send := make(chan []byte, 32)
|
||||||
r.recv = recv
|
r.recv = recv
|
||||||
r.send = send
|
r.send = send
|
||||||
r.core.tun.recv = recv
|
|
||||||
r.core.tun.send = send
|
|
||||||
r.reset = make(chan struct{}, 1)
|
r.reset = make(chan struct{}, 1)
|
||||||
r.admin = make(chan func(), 32)
|
r.admin = make(chan func(), 32)
|
||||||
r.cryptokey.init(r.core)
|
r.cryptokey.init(r.core)
|
||||||
// go r.mainLoop()
|
r.tun.init(r.core, send, recv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the mainLoop goroutine.
|
// Starts the mainLoop goroutine.
|
||||||
@ -121,7 +125,7 @@ func (r *router) mainLoop() {
|
|||||||
r.core.switchTable.doMaintenance()
|
r.core.switchTable.doMaintenance()
|
||||||
r.core.dht.doMaintenance()
|
r.core.dht.doMaintenance()
|
||||||
r.core.sessions.cleanup()
|
r.core.sessions.cleanup()
|
||||||
util_getBytes() // To slowly drain things
|
util.GetBytes() // To slowly drain things
|
||||||
}
|
}
|
||||||
case f := <-r.admin:
|
case f := <-r.admin:
|
||||||
f()
|
f()
|
||||||
@ -135,11 +139,11 @@ func (r *router) mainLoop() {
|
|||||||
// If the session hasn't responded recently, it triggers a ping or search to keep things alive or deal with broken coords *relatively* quickly.
|
// If the session hasn't responded recently, it triggers a ping or search to keep things alive or deal with broken coords *relatively* quickly.
|
||||||
// It also deals with oversized packets if there are MTU issues by calling into icmpv6.go to spoof PacketTooBig traffic, or DestinationUnreachable if the other side has their tun/tap disabled.
|
// It also deals with oversized packets if there are MTU issues by calling into icmpv6.go to spoof PacketTooBig traffic, or DestinationUnreachable if the other side has their tun/tap disabled.
|
||||||
func (r *router) sendPacket(bs []byte) {
|
func (r *router) sendPacket(bs []byte) {
|
||||||
var sourceAddr address
|
var sourceAddr address.Address
|
||||||
var destAddr address
|
var destAddr address.Address
|
||||||
var destSnet subnet
|
var destSnet address.Subnet
|
||||||
var destPubKey *boxPubKey
|
var destPubKey *crypto.BoxPubKey
|
||||||
var destNodeID *NodeID
|
var destNodeID *crypto.NodeID
|
||||||
var addrlen int
|
var addrlen int
|
||||||
if bs[0]&0xf0 == 0x60 {
|
if bs[0]&0xf0 == 0x60 {
|
||||||
// Check if we have a fully-sized header
|
// Check if we have a fully-sized header
|
||||||
@ -169,19 +173,19 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
// configured crypto-key routing source subnets
|
// configured crypto-key routing source subnets
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !destAddr.isValid() && !destSnet.isValid() {
|
if !destAddr.IsValid() && !destSnet.IsValid() {
|
||||||
// The addresses didn't match valid Yggdrasil node addresses so let's see
|
// The addresses didn't match valid Yggdrasil node addresses so let's see
|
||||||
// whether it matches a crypto-key routing range instead
|
// whether it matches a crypto-key routing range instead
|
||||||
if key, err := r.cryptokey.getPublicKeyForAddress(destAddr, addrlen); err == nil {
|
if key, err := r.cryptokey.getPublicKeyForAddress(destAddr, addrlen); err == nil {
|
||||||
// A public key was found, get the node ID for the search
|
// A public key was found, get the node ID for the search
|
||||||
destPubKey = &key
|
destPubKey = &key
|
||||||
destNodeID = getNodeID(destPubKey)
|
destNodeID = crypto.GetNodeID(destPubKey)
|
||||||
// Do a quick check to ensure that the node ID refers to a vaild Yggdrasil
|
// Do a quick check to ensure that the node ID refers to a vaild Yggdrasil
|
||||||
// address or subnet - this might be superfluous
|
// address or subnet - this might be superfluous
|
||||||
addr := *address_addrForNodeID(destNodeID)
|
addr := *address.AddrForNodeID(destNodeID)
|
||||||
copy(destAddr[:], addr[:])
|
copy(destAddr[:], addr[:])
|
||||||
copy(destSnet[:], addr[:])
|
copy(destSnet[:], addr[:])
|
||||||
if !destAddr.isValid() && !destSnet.isValid() {
|
if !destAddr.IsValid() && !destSnet.IsValid() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -190,25 +194,25 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
doSearch := func(packet []byte) {
|
doSearch := func(packet []byte) {
|
||||||
var nodeID, mask *NodeID
|
var nodeID, mask *crypto.NodeID
|
||||||
switch {
|
switch {
|
||||||
case destNodeID != nil:
|
case destNodeID != nil:
|
||||||
// We already know the full node ID, probably because it's from a CKR
|
// We already know the full node ID, probably because it's from a CKR
|
||||||
// route in which the public key is known ahead of time
|
// route in which the public key is known ahead of time
|
||||||
nodeID = destNodeID
|
nodeID = destNodeID
|
||||||
var m NodeID
|
var m crypto.NodeID
|
||||||
for i := range m {
|
for i := range m {
|
||||||
m[i] = 0xFF
|
m[i] = 0xFF
|
||||||
}
|
}
|
||||||
mask = &m
|
mask = &m
|
||||||
case destAddr.isValid():
|
case destAddr.IsValid():
|
||||||
// We don't know the full node ID - try and use the address to generate
|
// We don't know the full node ID - try and use the address to generate
|
||||||
// a truncated node ID
|
// a truncated node ID
|
||||||
nodeID, mask = destAddr.getNodeIDandMask()
|
nodeID, mask = destAddr.GetNodeIDandMask()
|
||||||
case destSnet.isValid():
|
case destSnet.IsValid():
|
||||||
// We don't know the full node ID - try and use the subnet to generate
|
// We don't know the full node ID - try and use the subnet to generate
|
||||||
// a truncated node ID
|
// a truncated node ID
|
||||||
nodeID, mask = destSnet.getNodeIDandMask()
|
nodeID, mask = destSnet.GetNodeIDandMask()
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -223,10 +227,10 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
}
|
}
|
||||||
var sinfo *sessionInfo
|
var sinfo *sessionInfo
|
||||||
var isIn bool
|
var isIn bool
|
||||||
if destAddr.isValid() {
|
if destAddr.IsValid() {
|
||||||
sinfo, isIn = r.core.sessions.getByTheirAddr(&destAddr)
|
sinfo, isIn = r.core.sessions.getByTheirAddr(&destAddr)
|
||||||
}
|
}
|
||||||
if destSnet.isValid() {
|
if destSnet.IsValid() {
|
||||||
sinfo, isIn = r.core.sessions.getByTheirSubnet(&destSnet)
|
sinfo, isIn = r.core.sessions.getByTheirSubnet(&destSnet)
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
@ -267,25 +271,6 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
// Drop packets if the session MTU is 0 - this means that one or other
|
// Drop packets if the session MTU is 0 - this means that one or other
|
||||||
// side probably has their TUN adapter disabled
|
// side probably has their TUN adapter disabled
|
||||||
if sinfo.getMTU() == 0 {
|
if sinfo.getMTU() == 0 {
|
||||||
// Get the size of the oversized payload, up to a max of 900 bytes
|
|
||||||
window := 900
|
|
||||||
if len(bs) < window {
|
|
||||||
window = len(bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Destination Unreachable response
|
|
||||||
ptb := &icmp.DstUnreach{
|
|
||||||
Data: bs[:window],
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the ICMPv6 response from it
|
|
||||||
icmpv6Buf, err := r.core.tun.icmpv6.create_icmpv6_tun(
|
|
||||||
bs[8:24], bs[24:40],
|
|
||||||
ipv6.ICMPTypeDestinationUnreachable, 1, ptb)
|
|
||||||
if err == nil {
|
|
||||||
r.recv <- icmpv6Buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't continue - drop the packet
|
// Don't continue - drop the packet
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -304,7 +289,7 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the ICMPv6 response from it
|
// Create the ICMPv6 response from it
|
||||||
icmpv6Buf, err := r.core.tun.icmpv6.create_icmpv6_tun(
|
icmpv6Buf, err := r.tun.icmpv6.create_icmpv6_tun(
|
||||||
bs[8:24], bs[24:40],
|
bs[8:24], bs[24:40],
|
||||||
ipv6.ICMPTypePacketTooBig, 0, ptb)
|
ipv6.ICMPTypePacketTooBig, 0, ptb)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -324,12 +309,12 @@ func (r *router) sendPacket(bs []byte) {
|
|||||||
func (r *router) recvPacket(bs []byte, sinfo *sessionInfo) {
|
func (r *router) recvPacket(bs []byte, sinfo *sessionInfo) {
|
||||||
// Note: called directly by the session worker, not the router goroutine
|
// Note: called directly by the session worker, not the router goroutine
|
||||||
if len(bs) < 24 {
|
if len(bs) < 24 {
|
||||||
util_putBytes(bs)
|
util.PutBytes(bs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var sourceAddr address
|
var sourceAddr address.Address
|
||||||
var dest address
|
var dest address.Address
|
||||||
var snet subnet
|
var snet address.Subnet
|
||||||
var addrlen int
|
var addrlen int
|
||||||
if bs[0]&0xf0 == 0x60 {
|
if bs[0]&0xf0 == 0x60 {
|
||||||
// IPv6 address
|
// IPv6 address
|
||||||
@ -349,17 +334,17 @@ func (r *router) recvPacket(bs []byte, sinfo *sessionInfo) {
|
|||||||
// Check that the packet is destined for either our Yggdrasil address or
|
// Check that the packet is destined for either our Yggdrasil address or
|
||||||
// subnet, or that it matches one of the crypto-key routing source routes
|
// subnet, or that it matches one of the crypto-key routing source routes
|
||||||
if !r.cryptokey.isValidSource(dest, addrlen) {
|
if !r.cryptokey.isValidSource(dest, addrlen) {
|
||||||
util_putBytes(bs)
|
util.PutBytes(bs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// See whether the packet they sent should have originated from this session
|
// See whether the packet they sent should have originated from this session
|
||||||
switch {
|
switch {
|
||||||
case sourceAddr.isValid() && sourceAddr == sinfo.theirAddr:
|
case sourceAddr.IsValid() && sourceAddr == sinfo.theirAddr:
|
||||||
case snet.isValid() && snet == sinfo.theirSubnet:
|
case snet.IsValid() && snet == sinfo.theirSubnet:
|
||||||
default:
|
default:
|
||||||
key, err := r.cryptokey.getPublicKeyForAddress(sourceAddr, addrlen)
|
key, err := r.cryptokey.getPublicKeyForAddress(sourceAddr, addrlen)
|
||||||
if err != nil || key != sinfo.theirPermPub {
|
if err != nil || key != sinfo.theirPermPub {
|
||||||
util_putBytes(bs)
|
util.PutBytes(bs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,7 +370,7 @@ func (r *router) handleIn(packet []byte) {
|
|||||||
// Handles incoming traffic, i.e. encapuslated ordinary IPv6 packets.
|
// Handles incoming traffic, i.e. encapuslated ordinary IPv6 packets.
|
||||||
// Passes them to the crypto session worker to be decrypted and sent to the tun/tap.
|
// Passes them to the crypto session worker to be decrypted and sent to the tun/tap.
|
||||||
func (r *router) handleTraffic(packet []byte) {
|
func (r *router) handleTraffic(packet []byte) {
|
||||||
defer util_putBytes(packet)
|
defer util.PutBytes(packet)
|
||||||
p := wire_trafficPacket{}
|
p := wire_trafficPacket{}
|
||||||
if !p.decode(packet) {
|
if !p.decode(packet) {
|
||||||
return
|
return
|
||||||
@ -405,14 +390,14 @@ func (r *router) handleProto(packet []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Now try to open the payload
|
// Now try to open the payload
|
||||||
var sharedKey *boxSharedKey
|
var sharedKey *crypto.BoxSharedKey
|
||||||
if p.ToKey == r.core.boxPub {
|
if p.ToKey == r.core.boxPub {
|
||||||
// Try to open using our permanent key
|
// Try to open using our permanent key
|
||||||
sharedKey = r.core.sessions.getSharedKey(&r.core.boxPriv, &p.FromKey)
|
sharedKey = r.core.sessions.getSharedKey(&r.core.boxPriv, &p.FromKey)
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bs, isOK := boxOpen(sharedKey, p.Payload, &p.Nonce)
|
bs, isOK := crypto.BoxOpen(sharedKey, p.Payload, &p.Nonce)
|
||||||
if !isOK {
|
if !isOK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -428,17 +413,21 @@ func (r *router) handleProto(packet []byte) {
|
|||||||
r.handlePing(bs, &p.FromKey)
|
r.handlePing(bs, &p.FromKey)
|
||||||
case wire_SessionPong:
|
case wire_SessionPong:
|
||||||
r.handlePong(bs, &p.FromKey)
|
r.handlePong(bs, &p.FromKey)
|
||||||
|
case wire_NodeInfoRequest:
|
||||||
|
fallthrough
|
||||||
|
case wire_NodeInfoResponse:
|
||||||
|
r.handleNodeInfo(bs, &p.FromKey)
|
||||||
case wire_DHTLookupRequest:
|
case wire_DHTLookupRequest:
|
||||||
r.handleDHTReq(bs, &p.FromKey)
|
r.handleDHTReq(bs, &p.FromKey)
|
||||||
case wire_DHTLookupResponse:
|
case wire_DHTLookupResponse:
|
||||||
r.handleDHTRes(bs, &p.FromKey)
|
r.handleDHTRes(bs, &p.FromKey)
|
||||||
default:
|
default:
|
||||||
util_putBytes(packet)
|
util.PutBytes(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes session pings from wire format and passes them to sessions.handlePing where they either create or update a session.
|
// Decodes session pings from wire format and passes them to sessions.handlePing where they either create or update a session.
|
||||||
func (r *router) handlePing(bs []byte, fromKey *boxPubKey) {
|
func (r *router) handlePing(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||||
ping := sessionPing{}
|
ping := sessionPing{}
|
||||||
if !ping.decode(bs) {
|
if !ping.decode(bs) {
|
||||||
return
|
return
|
||||||
@ -448,12 +437,12 @@ func (r *router) handlePing(bs []byte, fromKey *boxPubKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles session pongs (which are really pings with an extra flag to prevent acknowledgement).
|
// Handles session pongs (which are really pings with an extra flag to prevent acknowledgement).
|
||||||
func (r *router) handlePong(bs []byte, fromKey *boxPubKey) {
|
func (r *router) handlePong(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||||
r.handlePing(bs, fromKey)
|
r.handlePing(bs, fromKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes dht requests and passes them to dht.handleReq to trigger a lookup/response.
|
// Decodes dht requests and passes them to dht.handleReq to trigger a lookup/response.
|
||||||
func (r *router) handleDHTReq(bs []byte, fromKey *boxPubKey) {
|
func (r *router) handleDHTReq(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||||
req := dhtReq{}
|
req := dhtReq{}
|
||||||
if !req.decode(bs) {
|
if !req.decode(bs) {
|
||||||
return
|
return
|
||||||
@ -463,7 +452,7 @@ func (r *router) handleDHTReq(bs []byte, fromKey *boxPubKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decodes dht responses and passes them to dht.handleRes to update the DHT table and further pass them to the search code (if applicable).
|
// Decodes dht responses and passes them to dht.handleRes to update the DHT table and further pass them to the search code (if applicable).
|
||||||
func (r *router) handleDHTRes(bs []byte, fromKey *boxPubKey) {
|
func (r *router) handleDHTRes(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||||
res := dhtRes{}
|
res := dhtRes{}
|
||||||
if !res.decode(bs) {
|
if !res.decode(bs) {
|
||||||
return
|
return
|
||||||
@ -472,6 +461,16 @@ func (r *router) handleDHTRes(bs []byte, fromKey *boxPubKey) {
|
|||||||
r.core.dht.handleRes(&res)
|
r.core.dht.handleRes(&res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decodes nodeinfo request
|
||||||
|
func (r *router) handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||||
|
req := nodeinfoReqRes{}
|
||||||
|
if !req.decode(bs) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.SendPermPub = *fromKey
|
||||||
|
r.core.nodeinfo.handleNodeInfo(&req)
|
||||||
|
}
|
||||||
|
|
||||||
// Passed a function to call.
|
// Passed a function to call.
|
||||||
// This will send the function to r.admin and block until it finishes.
|
// This will send the function to r.admin and block until it finishes.
|
||||||
// It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine.
|
// It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine.
|
||||||
|
@ -17,6 +17,8 @@ package yggdrasil
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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.
|
// This defines the maximum number of dhtInfo that we keep track of for nodes to query in an ongoing search.
|
||||||
@ -30,28 +32,28 @@ const search_RETRY_TIME = time.Second
|
|||||||
// Information about an ongoing search.
|
// Information about an ongoing search.
|
||||||
// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
||||||
type searchInfo struct {
|
type searchInfo struct {
|
||||||
dest NodeID
|
dest crypto.NodeID
|
||||||
mask NodeID
|
mask crypto.NodeID
|
||||||
time time.Time
|
time time.Time
|
||||||
packet []byte
|
packet []byte
|
||||||
toVisit []*dhtInfo
|
toVisit []*dhtInfo
|
||||||
visited map[NodeID]bool
|
visited map[crypto.NodeID]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// This stores a map of active searches.
|
// This stores a map of active searches.
|
||||||
type searches struct {
|
type searches struct {
|
||||||
core *Core
|
core *Core
|
||||||
searches map[NodeID]*searchInfo
|
searches map[crypto.NodeID]*searchInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intializes the searches struct.
|
// Intializes the searches struct.
|
||||||
func (s *searches) init(core *Core) {
|
func (s *searches) init(core *Core) {
|
||||||
s.core = core
|
s.core = core
|
||||||
s.searches = make(map[NodeID]*searchInfo)
|
s.searches = make(map[crypto.NodeID]*searchInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new search info, adds it to the searches struct, and returns a pointer to the info.
|
// Creates a new search info, adds it to the searches struct, and returns a pointer to the info.
|
||||||
func (s *searches) createSearch(dest *NodeID, mask *NodeID) *searchInfo {
|
func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID) *searchInfo {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for dest, sinfo := range s.searches {
|
for dest, sinfo := range s.searches {
|
||||||
if now.Sub(sinfo.time) > time.Minute {
|
if now.Sub(sinfo.time) > time.Minute {
|
||||||
@ -102,7 +104,7 @@ func (s *searches) addToSearch(sinfo *searchInfo, res *dhtRes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Deduplicate
|
// Deduplicate
|
||||||
vMap := make(map[NodeID]*dhtInfo)
|
vMap := make(map[crypto.NodeID]*dhtInfo)
|
||||||
for _, info := range sinfo.toVisit {
|
for _, info := range sinfo.toVisit {
|
||||||
vMap[*info.getNodeID()] = info
|
vMap[*info.getNodeID()] = info
|
||||||
}
|
}
|
||||||
@ -163,10 +165,10 @@ func (s *searches) continueSearch(sinfo *searchInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calls create search, and initializes the iterative search parts of the struct before returning it.
|
// Calls create search, and initializes the iterative search parts of the struct before returning it.
|
||||||
func (s *searches) newIterSearch(dest *NodeID, mask *NodeID) *searchInfo {
|
func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID) *searchInfo {
|
||||||
sinfo := s.createSearch(dest, mask)
|
sinfo := s.createSearch(dest, mask)
|
||||||
sinfo.toVisit = s.core.dht.lookup(dest, true)
|
sinfo.toVisit = s.core.dht.lookup(dest, true)
|
||||||
sinfo.visited = make(map[NodeID]bool)
|
sinfo.visited = make(map[crypto.NodeID]bool)
|
||||||
return sinfo
|
return sinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,10 +176,10 @@ func (s *searches) newIterSearch(dest *NodeID, mask *NodeID) *searchInfo {
|
|||||||
// If the response is from the target, get/create a session, trigger a session ping, and return true.
|
// If the response is from the target, get/create a session, trigger a session ping, and return true.
|
||||||
// Otherwise return false.
|
// Otherwise return false.
|
||||||
func (s *searches) checkDHTRes(info *searchInfo, res *dhtRes) bool {
|
func (s *searches) checkDHTRes(info *searchInfo, res *dhtRes) bool {
|
||||||
them := getNodeID(&res.Key)
|
them := crypto.GetNodeID(&res.Key)
|
||||||
var destMasked NodeID
|
var destMasked crypto.NodeID
|
||||||
var themMasked NodeID
|
var themMasked crypto.NodeID
|
||||||
for idx := 0; idx < NodeIDLen; idx++ {
|
for idx := 0; idx < crypto.NodeIDLen; idx++ {
|
||||||
destMasked[idx] = info.dest[idx] & info.mask[idx]
|
destMasked[idx] = info.dest[idx] & info.mask[idx]
|
||||||
themMasked[idx] = them[idx] & info.mask[idx]
|
themMasked[idx] = them[idx] & info.mask[idx]
|
||||||
}
|
}
|
||||||
|
@ -8,23 +8,27 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// All the information we know about an active session.
|
// All the information we know about an active session.
|
||||||
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
||||||
type sessionInfo struct {
|
type sessionInfo struct {
|
||||||
core *Core
|
core *Core
|
||||||
theirAddr address
|
theirAddr address.Address
|
||||||
theirSubnet subnet
|
theirSubnet address.Subnet
|
||||||
theirPermPub boxPubKey
|
theirPermPub crypto.BoxPubKey
|
||||||
theirSesPub boxPubKey
|
theirSesPub crypto.BoxPubKey
|
||||||
mySesPub boxPubKey
|
mySesPub crypto.BoxPubKey
|
||||||
mySesPriv boxPrivKey
|
mySesPriv crypto.BoxPrivKey
|
||||||
sharedSesKey boxSharedKey // derived from session keys
|
sharedSesKey crypto.BoxSharedKey // derived from session keys
|
||||||
theirHandle handle
|
theirHandle crypto.Handle
|
||||||
myHandle handle
|
myHandle crypto.Handle
|
||||||
theirNonce boxNonce
|
theirNonce crypto.BoxNonce
|
||||||
myNonce boxNonce
|
myNonce crypto.BoxNonce
|
||||||
theirMTU uint16
|
theirMTU uint16
|
||||||
myMTU uint16
|
myMTU uint16
|
||||||
wasMTUFixed bool // Was the MTU fixed by a receive error?
|
wasMTUFixed bool // Was the MTU fixed by a receive error?
|
||||||
@ -45,9 +49,9 @@ type sessionInfo struct {
|
|||||||
|
|
||||||
// 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, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
|
||||||
type sessionPing struct {
|
type sessionPing struct {
|
||||||
SendPermPub boxPubKey // Sender's permanent key
|
SendPermPub crypto.BoxPubKey // Sender's permanent key
|
||||||
Handle handle // Random number to ID session
|
Handle crypto.Handle // Random number to ID session
|
||||||
SendSesPub boxPubKey // Session key to use
|
SendSesPub crypto.BoxPubKey // Session key to use
|
||||||
Coords []byte
|
Coords []byte
|
||||||
Tstamp int64 // unix time, but the only real requirement is that it increases
|
Tstamp int64 // unix time, but the only real requirement is that it increases
|
||||||
IsPong bool
|
IsPong bool
|
||||||
@ -69,8 +73,8 @@ func (s *sessionInfo) update(p *sessionPing) bool {
|
|||||||
if p.SendSesPub != s.theirSesPub {
|
if p.SendSesPub != s.theirSesPub {
|
||||||
s.theirSesPub = p.SendSesPub
|
s.theirSesPub = p.SendSesPub
|
||||||
s.theirHandle = p.Handle
|
s.theirHandle = p.Handle
|
||||||
s.sharedSesKey = *getSharedKey(&s.mySesPriv, &s.theirSesPub)
|
s.sharedSesKey = *crypto.GetSharedKey(&s.mySesPriv, &s.theirSesPub)
|
||||||
s.theirNonce = boxNonce{}
|
s.theirNonce = crypto.BoxNonce{}
|
||||||
s.nonceMask = 0
|
s.nonceMask = 0
|
||||||
}
|
}
|
||||||
if p.MTU >= 1280 || p.MTU == 0 {
|
if p.MTU >= 1280 || p.MTU == 0 {
|
||||||
@ -99,15 +103,15 @@ type sessions struct {
|
|||||||
core *Core
|
core *Core
|
||||||
lastCleanup time.Time
|
lastCleanup time.Time
|
||||||
// Maps known permanent keys to their shared key, used by DHT a lot
|
// Maps known permanent keys to their shared key, used by DHT a lot
|
||||||
permShared map[boxPubKey]*boxSharedKey
|
permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey
|
||||||
// Maps (secret) handle onto session info
|
// Maps (secret) handle onto session info
|
||||||
sinfos map[handle]*sessionInfo
|
sinfos map[crypto.Handle]*sessionInfo
|
||||||
// Maps mySesPub onto handle
|
// Maps mySesPub onto handle
|
||||||
byMySes map[boxPubKey]*handle
|
byMySes map[crypto.BoxPubKey]*crypto.Handle
|
||||||
// Maps theirPermPub onto handle
|
// Maps theirPermPub onto handle
|
||||||
byTheirPerm map[boxPubKey]*handle
|
byTheirPerm map[crypto.BoxPubKey]*crypto.Handle
|
||||||
addrToPerm map[address]*boxPubKey
|
addrToPerm map[address.Address]*crypto.BoxPubKey
|
||||||
subnetToPerm map[subnet]*boxPubKey
|
subnetToPerm map[address.Subnet]*crypto.BoxPubKey
|
||||||
// Options from the session firewall
|
// Options from the session firewall
|
||||||
sessionFirewallEnabled bool
|
sessionFirewallEnabled bool
|
||||||
sessionFirewallAllowsDirect bool
|
sessionFirewallAllowsDirect bool
|
||||||
@ -120,12 +124,12 @@ type sessions struct {
|
|||||||
// Initializes the session struct.
|
// Initializes the session struct.
|
||||||
func (ss *sessions) init(core *Core) {
|
func (ss *sessions) init(core *Core) {
|
||||||
ss.core = core
|
ss.core = core
|
||||||
ss.permShared = make(map[boxPubKey]*boxSharedKey)
|
ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey)
|
||||||
ss.sinfos = make(map[handle]*sessionInfo)
|
ss.sinfos = make(map[crypto.Handle]*sessionInfo)
|
||||||
ss.byMySes = make(map[boxPubKey]*handle)
|
ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle)
|
||||||
ss.byTheirPerm = make(map[boxPubKey]*handle)
|
ss.byTheirPerm = make(map[crypto.BoxPubKey]*crypto.Handle)
|
||||||
ss.addrToPerm = make(map[address]*boxPubKey)
|
ss.addrToPerm = make(map[address.Address]*crypto.BoxPubKey)
|
||||||
ss.subnetToPerm = make(map[subnet]*boxPubKey)
|
ss.subnetToPerm = make(map[address.Subnet]*crypto.BoxPubKey)
|
||||||
ss.lastCleanup = time.Now()
|
ss.lastCleanup = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,18 +158,18 @@ func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) {
|
|||||||
|
|
||||||
// Determines whether the session with a given publickey is allowed based on
|
// Determines whether the session with a given publickey is allowed based on
|
||||||
// session firewall rules.
|
// session firewall rules.
|
||||||
func (ss *sessions) isSessionAllowed(pubkey *boxPubKey, initiator bool) bool {
|
func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
||||||
// Allow by default if the session firewall is disabled
|
// Allow by default if the session firewall is disabled
|
||||||
if !ss.sessionFirewallEnabled {
|
if !ss.sessionFirewallEnabled {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Prepare for checking whitelist/blacklist
|
// Prepare for checking whitelist/blacklist
|
||||||
var box boxPubKey
|
var box crypto.BoxPubKey
|
||||||
// Reject blacklisted nodes
|
// Reject blacklisted nodes
|
||||||
for _, b := range ss.sessionFirewallBlacklist {
|
for _, b := range ss.sessionFirewallBlacklist {
|
||||||
key, err := hex.DecodeString(b)
|
key, err := hex.DecodeString(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
copy(box[:boxPubKeyLen], key)
|
copy(box[:crypto.BoxPubKeyLen], key)
|
||||||
if box == *pubkey {
|
if box == *pubkey {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -175,7 +179,7 @@ func (ss *sessions) isSessionAllowed(pubkey *boxPubKey, initiator bool) bool {
|
|||||||
for _, b := range ss.sessionFirewallWhitelist {
|
for _, b := range ss.sessionFirewallWhitelist {
|
||||||
key, err := hex.DecodeString(b)
|
key, err := hex.DecodeString(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
copy(box[:boxPubKeyLen], key)
|
copy(box[:crypto.BoxPubKeyLen], key)
|
||||||
if box == *pubkey {
|
if box == *pubkey {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -208,7 +212,7 @@ func (ss *sessions) isSessionAllowed(pubkey *boxPubKey, initiator bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets the session corresponding to a given handle.
|
// Gets the session corresponding to a given handle.
|
||||||
func (ss *sessions) getSessionForHandle(handle *handle) (*sessionInfo, bool) {
|
func (ss *sessions) getSessionForHandle(handle *crypto.Handle) (*sessionInfo, bool) {
|
||||||
sinfo, isIn := ss.sinfos[*handle]
|
sinfo, isIn := ss.sinfos[*handle]
|
||||||
if isIn && sinfo.timedout() {
|
if isIn && sinfo.timedout() {
|
||||||
// We have a session, but it has timed out
|
// We have a session, but it has timed out
|
||||||
@ -218,7 +222,7 @@ func (ss *sessions) getSessionForHandle(handle *handle) (*sessionInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets a session corresponding to an ephemeral session key used by this node.
|
// Gets a session corresponding to an ephemeral session key used by this node.
|
||||||
func (ss *sessions) getByMySes(key *boxPubKey) (*sessionInfo, bool) {
|
func (ss *sessions) getByMySes(key *crypto.BoxPubKey) (*sessionInfo, bool) {
|
||||||
h, isIn := ss.byMySes[*key]
|
h, isIn := ss.byMySes[*key]
|
||||||
if !isIn {
|
if !isIn {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -228,7 +232,7 @@ func (ss *sessions) getByMySes(key *boxPubKey) (*sessionInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets a session corresponding to a permanent key used by the remote node.
|
// Gets a session corresponding to a permanent key used by the remote node.
|
||||||
func (ss *sessions) getByTheirPerm(key *boxPubKey) (*sessionInfo, bool) {
|
func (ss *sessions) getByTheirPerm(key *crypto.BoxPubKey) (*sessionInfo, bool) {
|
||||||
h, isIn := ss.byTheirPerm[*key]
|
h, isIn := ss.byTheirPerm[*key]
|
||||||
if !isIn {
|
if !isIn {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -238,7 +242,7 @@ func (ss *sessions) getByTheirPerm(key *boxPubKey) (*sessionInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets a session corresponding to an IPv6 address used by the remote node.
|
// Gets a session corresponding to an IPv6 address used by the remote node.
|
||||||
func (ss *sessions) getByTheirAddr(addr *address) (*sessionInfo, bool) {
|
func (ss *sessions) getByTheirAddr(addr *address.Address) (*sessionInfo, bool) {
|
||||||
p, isIn := ss.addrToPerm[*addr]
|
p, isIn := ss.addrToPerm[*addr]
|
||||||
if !isIn {
|
if !isIn {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -248,7 +252,7 @@ func (ss *sessions) getByTheirAddr(addr *address) (*sessionInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets a session corresponding to an IPv6 /64 subnet used by the remote node/network.
|
// Gets a session corresponding to an IPv6 /64 subnet used by the remote node/network.
|
||||||
func (ss *sessions) getByTheirSubnet(snet *subnet) (*sessionInfo, bool) {
|
func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool) {
|
||||||
p, isIn := ss.subnetToPerm[*snet]
|
p, isIn := ss.subnetToPerm[*snet]
|
||||||
if !isIn {
|
if !isIn {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -259,7 +263,7 @@ func (ss *sessions) getByTheirSubnet(snet *subnet) (*sessionInfo, bool) {
|
|||||||
|
|
||||||
// Creates a new session and lazily cleans up old/timedout existing sessions.
|
// Creates a new session and lazily cleans up old/timedout existing sessions.
|
||||||
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
||||||
func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
|
func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
||||||
if ss.sessionFirewallEnabled {
|
if ss.sessionFirewallEnabled {
|
||||||
if !ss.isSessionAllowed(theirPermKey, true) {
|
if !ss.isSessionAllowed(theirPermKey, true) {
|
||||||
return nil
|
return nil
|
||||||
@ -268,12 +272,12 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
|
|||||||
sinfo := sessionInfo{}
|
sinfo := sessionInfo{}
|
||||||
sinfo.core = ss.core
|
sinfo.core = ss.core
|
||||||
sinfo.theirPermPub = *theirPermKey
|
sinfo.theirPermPub = *theirPermKey
|
||||||
pub, priv := newBoxKeys()
|
pub, priv := crypto.NewBoxKeys()
|
||||||
sinfo.mySesPub = *pub
|
sinfo.mySesPub = *pub
|
||||||
sinfo.mySesPriv = *priv
|
sinfo.mySesPriv = *priv
|
||||||
sinfo.myNonce = *newBoxNonce()
|
sinfo.myNonce = *crypto.NewBoxNonce()
|
||||||
sinfo.theirMTU = 1280
|
sinfo.theirMTU = 1280
|
||||||
sinfo.myMTU = uint16(ss.core.tun.mtu)
|
sinfo.myMTU = uint16(ss.core.router.tun.mtu)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
sinfo.time = now
|
sinfo.time = now
|
||||||
sinfo.mtuTime = now
|
sinfo.mtuTime = now
|
||||||
@ -295,9 +299,9 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
|
|||||||
// lower => even nonce
|
// lower => even nonce
|
||||||
sinfo.myNonce[len(sinfo.myNonce)-1] &= 0xfe
|
sinfo.myNonce[len(sinfo.myNonce)-1] &= 0xfe
|
||||||
}
|
}
|
||||||
sinfo.myHandle = *newHandle()
|
sinfo.myHandle = *crypto.NewHandle()
|
||||||
sinfo.theirAddr = *address_addrForNodeID(getNodeID(&sinfo.theirPermPub))
|
sinfo.theirAddr = *address.AddrForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
|
||||||
sinfo.theirSubnet = *address_subnetForNodeID(getNodeID(&sinfo.theirPermPub))
|
sinfo.theirSubnet = *address.SubnetForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
|
||||||
sinfo.send = make(chan []byte, 32)
|
sinfo.send = make(chan []byte, 32)
|
||||||
sinfo.recv = make(chan *wire_trafficPacket, 32)
|
sinfo.recv = make(chan *wire_trafficPacket, 32)
|
||||||
go sinfo.doWorker()
|
go sinfo.doWorker()
|
||||||
@ -324,32 +328,32 @@ func (ss *sessions) cleanup() {
|
|||||||
s.close()
|
s.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
permShared := make(map[boxPubKey]*boxSharedKey, len(ss.permShared))
|
permShared := make(map[crypto.BoxPubKey]*crypto.BoxSharedKey, len(ss.permShared))
|
||||||
for k, v := range ss.permShared {
|
for k, v := range ss.permShared {
|
||||||
permShared[k] = v
|
permShared[k] = v
|
||||||
}
|
}
|
||||||
ss.permShared = permShared
|
ss.permShared = permShared
|
||||||
sinfos := make(map[handle]*sessionInfo, len(ss.sinfos))
|
sinfos := make(map[crypto.Handle]*sessionInfo, len(ss.sinfos))
|
||||||
for k, v := range ss.sinfos {
|
for k, v := range ss.sinfos {
|
||||||
sinfos[k] = v
|
sinfos[k] = v
|
||||||
}
|
}
|
||||||
ss.sinfos = sinfos
|
ss.sinfos = sinfos
|
||||||
byMySes := make(map[boxPubKey]*handle, len(ss.byMySes))
|
byMySes := make(map[crypto.BoxPubKey]*crypto.Handle, len(ss.byMySes))
|
||||||
for k, v := range ss.byMySes {
|
for k, v := range ss.byMySes {
|
||||||
byMySes[k] = v
|
byMySes[k] = v
|
||||||
}
|
}
|
||||||
ss.byMySes = byMySes
|
ss.byMySes = byMySes
|
||||||
byTheirPerm := make(map[boxPubKey]*handle, len(ss.byTheirPerm))
|
byTheirPerm := make(map[crypto.BoxPubKey]*crypto.Handle, len(ss.byTheirPerm))
|
||||||
for k, v := range ss.byTheirPerm {
|
for k, v := range ss.byTheirPerm {
|
||||||
byTheirPerm[k] = v
|
byTheirPerm[k] = v
|
||||||
}
|
}
|
||||||
ss.byTheirPerm = byTheirPerm
|
ss.byTheirPerm = byTheirPerm
|
||||||
addrToPerm := make(map[address]*boxPubKey, len(ss.addrToPerm))
|
addrToPerm := make(map[address.Address]*crypto.BoxPubKey, len(ss.addrToPerm))
|
||||||
for k, v := range ss.addrToPerm {
|
for k, v := range ss.addrToPerm {
|
||||||
addrToPerm[k] = v
|
addrToPerm[k] = v
|
||||||
}
|
}
|
||||||
ss.addrToPerm = addrToPerm
|
ss.addrToPerm = addrToPerm
|
||||||
subnetToPerm := make(map[subnet]*boxPubKey, len(ss.subnetToPerm))
|
subnetToPerm := make(map[address.Subnet]*crypto.BoxPubKey, len(ss.subnetToPerm))
|
||||||
for k, v := range ss.subnetToPerm {
|
for k, v := range ss.subnetToPerm {
|
||||||
subnetToPerm[k] = v
|
subnetToPerm[k] = v
|
||||||
}
|
}
|
||||||
@ -380,15 +384,15 @@ func (ss *sessions) getPing(sinfo *sessionInfo) sessionPing {
|
|||||||
Coords: coords,
|
Coords: coords,
|
||||||
MTU: sinfo.myMTU,
|
MTU: sinfo.myMTU,
|
||||||
}
|
}
|
||||||
sinfo.myNonce.update()
|
sinfo.myNonce.Increment()
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the shared key for a pair of box keys.
|
// Gets the shared key for a pair of box keys.
|
||||||
// Used to cache recently used shared keys for protocol traffic.
|
// Used to cache recently used shared keys for protocol traffic.
|
||||||
// This comes up with dht req/res and session ping/pong traffic.
|
// This comes up with dht req/res and session ping/pong traffic.
|
||||||
func (ss *sessions) getSharedKey(myPriv *boxPrivKey,
|
func (ss *sessions) getSharedKey(myPriv *crypto.BoxPrivKey,
|
||||||
theirPub *boxPubKey) *boxSharedKey {
|
theirPub *crypto.BoxPubKey) *crypto.BoxSharedKey {
|
||||||
if skey, isIn := ss.permShared[*theirPub]; isIn {
|
if skey, isIn := ss.permShared[*theirPub]; isIn {
|
||||||
return skey
|
return skey
|
||||||
}
|
}
|
||||||
@ -401,7 +405,7 @@ func (ss *sessions) getSharedKey(myPriv *boxPrivKey,
|
|||||||
}
|
}
|
||||||
delete(ss.permShared, key)
|
delete(ss.permShared, key)
|
||||||
}
|
}
|
||||||
ss.permShared[*theirPub] = getSharedKey(myPriv, theirPub)
|
ss.permShared[*theirPub] = crypto.GetSharedKey(myPriv, theirPub)
|
||||||
return ss.permShared[*theirPub]
|
return ss.permShared[*theirPub]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +421,7 @@ func (ss *sessions) sendPingPong(sinfo *sessionInfo, isPong bool) {
|
|||||||
ping.IsPong = isPong
|
ping.IsPong = isPong
|
||||||
bs := ping.encode()
|
bs := ping.encode()
|
||||||
shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
|
shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
|
||||||
payload, nonce := boxSeal(shared, bs, nil)
|
payload, nonce := crypto.BoxSeal(shared, bs, nil)
|
||||||
p := wire_protoTrafficPacket{
|
p := wire_protoTrafficPacket{
|
||||||
Coords: sinfo.coords,
|
Coords: sinfo.coords,
|
||||||
ToKey: sinfo.theirPermPub,
|
ToKey: sinfo.theirPermPub,
|
||||||
@ -468,24 +472,6 @@ func (ss *sessions) handlePing(ping *sessionPing) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to subtract one nonce from another, staying in the range +- 64.
|
|
||||||
// This is used by the nonce progression machinery to advance the bitmask of recently received packets (indexed by nonce), or to check the appropriate bit of the bitmask.
|
|
||||||
// It's basically part of the machinery that prevents replays and duplicate packets.
|
|
||||||
func (n *boxNonce) minus(m *boxNonce) int64 {
|
|
||||||
diff := int64(0)
|
|
||||||
for idx := range n {
|
|
||||||
diff *= 256
|
|
||||||
diff += int64(n[idx]) - int64(m[idx])
|
|
||||||
if diff > 64 {
|
|
||||||
diff = 64
|
|
||||||
}
|
|
||||||
if diff < -64 {
|
|
||||||
diff = -64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the MTU of the session.
|
// Get the MTU of the session.
|
||||||
// Will be equal to the smaller of this node's MTU or the remote node's MTU.
|
// 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.
|
// 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.
|
||||||
@ -500,9 +486,9 @@ func (sinfo *sessionInfo) getMTU() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a packet's nonce is recent enough to fall within the window of allowed packets, and not already received.
|
// Checks if a packet's nonce is recent enough to fall within the window of allowed packets, and not already received.
|
||||||
func (sinfo *sessionInfo) nonceIsOK(theirNonce *boxNonce) bool {
|
func (sinfo *sessionInfo) nonceIsOK(theirNonce *crypto.BoxNonce) bool {
|
||||||
// The bitmask is to allow for some non-duplicate out-of-order packets
|
// The bitmask is to allow for some non-duplicate out-of-order packets
|
||||||
diff := theirNonce.minus(&sinfo.theirNonce)
|
diff := theirNonce.Minus(&sinfo.theirNonce)
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -510,10 +496,10 @@ func (sinfo *sessionInfo) nonceIsOK(theirNonce *boxNonce) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Updates the nonce mask by (possibly) shifting the bitmask and setting the bit corresponding to this nonce to 1, and then updating the most recent nonce
|
// Updates the nonce mask by (possibly) shifting the bitmask and setting the bit corresponding to this nonce to 1, and then updating the most recent nonce
|
||||||
func (sinfo *sessionInfo) updateNonce(theirNonce *boxNonce) {
|
func (sinfo *sessionInfo) updateNonce(theirNonce *crypto.BoxNonce) {
|
||||||
// Shift nonce mask if needed
|
// Shift nonce mask if needed
|
||||||
// Set bit
|
// Set bit
|
||||||
diff := theirNonce.minus(&sinfo.theirNonce)
|
diff := theirNonce.Minus(&sinfo.theirNonce)
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
// This nonce is newer, so shift the window before setting the bit, and update theirNonce in the session info.
|
// This nonce is newer, so shift the window before setting the bit, and update theirNonce in the session info.
|
||||||
sinfo.nonceMask <<= uint64(diff)
|
sinfo.nonceMask <<= uint64(diff)
|
||||||
@ -559,7 +545,7 @@ func (sinfo *sessionInfo) doWorker() {
|
|||||||
|
|
||||||
// This encrypts a packet, creates a trafficPacket struct, encodes it, and sends it to router.out to pass it to the switch layer.
|
// This encrypts a packet, creates a trafficPacket struct, encodes it, and sends it to router.out to pass it to the switch layer.
|
||||||
func (sinfo *sessionInfo) doSend(bs []byte) {
|
func (sinfo *sessionInfo) doSend(bs []byte) {
|
||||||
defer util_putBytes(bs)
|
defer util.PutBytes(bs)
|
||||||
if !sinfo.init {
|
if !sinfo.init {
|
||||||
// To prevent using empty session keys
|
// To prevent using empty session keys
|
||||||
return
|
return
|
||||||
@ -593,8 +579,8 @@ func (sinfo *sessionInfo) doSend(bs []byte) {
|
|||||||
coords = wire_put_uint64(flowkey, coords) // Then variable-length encoded flowkey
|
coords = wire_put_uint64(flowkey, coords) // Then variable-length encoded flowkey
|
||||||
}
|
}
|
||||||
// Prepare the payload
|
// Prepare the payload
|
||||||
payload, nonce := boxSeal(&sinfo.sharedSesKey, bs, &sinfo.myNonce)
|
payload, nonce := crypto.BoxSeal(&sinfo.sharedSesKey, bs, &sinfo.myNonce)
|
||||||
defer util_putBytes(payload)
|
defer util.PutBytes(payload)
|
||||||
p := wire_trafficPacket{
|
p := wire_trafficPacket{
|
||||||
Coords: coords,
|
Coords: coords,
|
||||||
Handle: sinfo.theirHandle,
|
Handle: sinfo.theirHandle,
|
||||||
@ -612,13 +598,13 @@ func (sinfo *sessionInfo) doSend(bs []byte) {
|
|||||||
// If a packet does not decrypt successfully, it assumes the packet was truncated, and updates the MTU accordingly.
|
// If a packet does not decrypt successfully, it assumes the packet was truncated, and updates the MTU accordingly.
|
||||||
// TODO? remove the MTU updating part? That should never happen with TCP peers, and the old UDP code that caused it was removed (and if replaced, should be replaced with something that can reliably send messages with an arbitrary size).
|
// TODO? remove the MTU updating part? That should never happen with TCP peers, and the old UDP code that caused it was removed (and if replaced, should be replaced with something that can reliably send messages with an arbitrary size).
|
||||||
func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) {
|
func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) {
|
||||||
defer util_putBytes(p.Payload)
|
defer util.PutBytes(p.Payload)
|
||||||
if !sinfo.nonceIsOK(&p.Nonce) {
|
if !sinfo.nonceIsOK(&p.Nonce) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bs, isOK := boxOpen(&sinfo.sharedSesKey, p.Payload, &p.Nonce)
|
bs, isOK := crypto.BoxOpen(&sinfo.sharedSesKey, p.Payload, &p.Nonce)
|
||||||
if !isOK {
|
if !isOK {
|
||||||
util_putBytes(bs)
|
util.PutBytes(bs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sinfo.updateNonce(&p.Nonce)
|
sinfo.updateNonce(&p.Nonce)
|
||||||
|
@ -16,6 +16,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -30,16 +33,16 @@ const (
|
|||||||
// The coords represent a path from the root to a node.
|
// The coords represent a path from the root to a node.
|
||||||
// This path is generally part of a spanning tree, except possibly the last hop (it can loop when sending coords to your parent, but they see this and know not to use a looping path).
|
// This path is generally part of a spanning tree, except possibly the last hop (it can loop when sending coords to your parent, but they see this and know not to use a looping path).
|
||||||
type switchLocator struct {
|
type switchLocator struct {
|
||||||
root sigPubKey
|
root crypto.SigPubKey
|
||||||
tstamp int64
|
tstamp int64
|
||||||
coords []switchPort
|
coords []switchPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the first sigPubKey has a higher TreeID.
|
// Returns true if the first sigPubKey has a higher TreeID.
|
||||||
func firstIsBetter(first, second *sigPubKey) bool {
|
func firstIsBetter(first, second *crypto.SigPubKey) bool {
|
||||||
// Higher TreeID is better
|
// Higher TreeID is better
|
||||||
ftid := getTreeID(first)
|
ftid := crypto.GetTreeID(first)
|
||||||
stid := getTreeID(second)
|
stid := crypto.GetTreeID(second)
|
||||||
for idx := 0; idx < len(ftid); idx++ {
|
for idx := 0; idx < len(ftid); idx++ {
|
||||||
if ftid[idx] == stid[idx] {
|
if ftid[idx] == stid[idx] {
|
||||||
continue
|
continue
|
||||||
@ -121,7 +124,7 @@ func (x *switchLocator) isAncestorOf(y *switchLocator) bool {
|
|||||||
|
|
||||||
// Information about a peer, used by the switch to build the tree and eventually make routing decisions.
|
// Information about a peer, used by the switch to build the tree and eventually make routing decisions.
|
||||||
type peerInfo struct {
|
type peerInfo struct {
|
||||||
key sigPubKey // ID of this peer
|
key crypto.SigPubKey // ID of this peer
|
||||||
locator switchLocator // Should be able to respond with signatures upon request
|
locator switchLocator // Should be able to respond with signatures upon request
|
||||||
degree uint64 // Self-reported degree
|
degree uint64 // Self-reported degree
|
||||||
time time.Time // Time this node was last seen
|
time time.Time // Time this node was last seen
|
||||||
@ -159,9 +162,9 @@ type switchData struct {
|
|||||||
// All the information stored by the switch.
|
// All the information stored by the switch.
|
||||||
type switchTable struct {
|
type switchTable struct {
|
||||||
core *Core
|
core *Core
|
||||||
key sigPubKey // Our own key
|
key crypto.SigPubKey // Our own key
|
||||||
time time.Time // Time when locator.tstamp was last updated
|
time time.Time // Time when locator.tstamp was last updated
|
||||||
drop map[sigPubKey]int64 // Tstamp associated with a dropped root
|
drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root
|
||||||
mutex sync.RWMutex // Lock for reads/writes of switchData
|
mutex sync.RWMutex // Lock for reads/writes of switchData
|
||||||
parent switchPort // Port of whatever peer is our parent, or self if we're root
|
parent switchPort // Port of whatever peer is our parent, or self if we're root
|
||||||
data switchData //
|
data switchData //
|
||||||
@ -178,7 +181,7 @@ type switchTable struct {
|
|||||||
const SwitchQueueTotalMinSize = 4 * 1024 * 1024
|
const SwitchQueueTotalMinSize = 4 * 1024 * 1024
|
||||||
|
|
||||||
// Initializes the switchTable struct.
|
// Initializes the switchTable struct.
|
||||||
func (t *switchTable) init(core *Core, key sigPubKey) {
|
func (t *switchTable) init(core *Core, key crypto.SigPubKey) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
t.core = core
|
t.core = core
|
||||||
t.key = key
|
t.key = key
|
||||||
@ -187,7 +190,7 @@ func (t *switchTable) init(core *Core, key sigPubKey) {
|
|||||||
t.data = switchData{locator: locator, peers: peers}
|
t.data = switchData{locator: locator, peers: peers}
|
||||||
t.updater.Store(&sync.Once{})
|
t.updater.Store(&sync.Once{})
|
||||||
t.table.Store(lookupTable{})
|
t.table.Store(lookupTable{})
|
||||||
t.drop = make(map[sigPubKey]int64)
|
t.drop = make(map[crypto.SigPubKey]int64)
|
||||||
t.packetIn = make(chan []byte, 1024)
|
t.packetIn = make(chan []byte, 1024)
|
||||||
t.idleIn = make(chan switchPort, 1024)
|
t.idleIn = make(chan switchPort, 1024)
|
||||||
t.admin = make(chan func())
|
t.admin = make(chan func())
|
||||||
@ -302,7 +305,7 @@ func (t *switchTable) cleanDropped() {
|
|||||||
// This is exchanged with peers to construct the spanning tree.
|
// This is exchanged with peers to construct the spanning tree.
|
||||||
// A subset of this information, excluding the signatures, is used to construct locators that are used elsewhere in the code.
|
// A subset of this information, excluding the signatures, is used to construct locators that are used elsewhere in the code.
|
||||||
type switchMsg struct {
|
type switchMsg struct {
|
||||||
Root sigPubKey
|
Root crypto.SigPubKey
|
||||||
TStamp int64
|
TStamp int64
|
||||||
Hops []switchMsgHop
|
Hops []switchMsgHop
|
||||||
}
|
}
|
||||||
@ -310,8 +313,8 @@ type switchMsg struct {
|
|||||||
// This represents the signed information about the path leading from the root the Next node, via the Port specified here.
|
// This represents the signed information about the path leading from the root the Next node, via the Port specified here.
|
||||||
type switchMsgHop struct {
|
type switchMsgHop struct {
|
||||||
Port switchPort
|
Port switchPort
|
||||||
Next sigPubKey
|
Next crypto.SigPubKey
|
||||||
Sig sigBytes
|
Sig crypto.SigBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// This returns a *switchMsg to a copy of this node's current switchMsg, which can safely have additional information appended to Hops and sent to a peer.
|
// This returns a *switchMsg to a copy of this node's current switchMsg, which can safely have additional information appended to Hops and sent to a peer.
|
||||||
@ -690,7 +693,7 @@ func (b *switch_buffers) cleanup(t *switchTable) {
|
|||||||
coords := switch_getPacketCoords(packet.bytes)
|
coords := switch_getPacketCoords(packet.bytes)
|
||||||
if t.selfIsClosest(coords) {
|
if t.selfIsClosest(coords) {
|
||||||
for _, packet := range buf.packets {
|
for _, packet := range buf.packets {
|
||||||
util_putBytes(packet.bytes)
|
util.PutBytes(packet.bytes)
|
||||||
}
|
}
|
||||||
b.size -= buf.size
|
b.size -= buf.size
|
||||||
delete(b.bufs, streamID)
|
delete(b.bufs, streamID)
|
||||||
@ -710,7 +713,7 @@ func (b *switch_buffers) cleanup(t *switchTable) {
|
|||||||
packet, buf.packets = buf.packets[0], buf.packets[1:]
|
packet, buf.packets = buf.packets[0], buf.packets[1:]
|
||||||
buf.size -= uint64(len(packet.bytes))
|
buf.size -= uint64(len(packet.bytes))
|
||||||
b.size -= uint64(len(packet.bytes))
|
b.size -= uint64(len(packet.bytes))
|
||||||
util_putBytes(packet.bytes)
|
util.PutBytes(packet.bytes)
|
||||||
if len(buf.packets) == 0 {
|
if len(buf.packets) == 0 {
|
||||||
delete(b.bufs, streamID)
|
delete(b.bufs, streamID)
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,20 +25,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense
|
const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense
|
||||||
const default_tcp_timeout = 6 * time.Second
|
const default_tcp_timeout = 6 * time.Second
|
||||||
const tcp_ping_interval = (default_tcp_timeout * 2 / 3)
|
const tcp_ping_interval = (default_tcp_timeout * 2 / 3)
|
||||||
|
|
||||||
// Wrapper function for non tcp/ip connections.
|
|
||||||
func setNoDelay(c net.Conn, delay bool) {
|
|
||||||
tcp, ok := c.(*net.TCPConn)
|
|
||||||
if ok {
|
|
||||||
tcp.SetNoDelay(delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The TCP listener and information about active TCP connections, to avoid duplication.
|
// The TCP listener and information about active TCP connections, to avoid duplication.
|
||||||
type tcpInterface struct {
|
type tcpInterface struct {
|
||||||
core *Core
|
core *Core
|
||||||
@ -52,12 +48,22 @@ type tcpInterface struct {
|
|||||||
// This is used as the key to a map that tracks existing connections, to prevent multiple connections to the same keys and local/remote address pair from occuring.
|
// This is used as the key to a map that tracks existing connections, to prevent multiple connections to the same keys and local/remote address pair from occuring.
|
||||||
// Different address combinations are allowed, so multi-homing is still technically possible (but not necessarily advisable).
|
// Different address combinations are allowed, so multi-homing is still technically possible (but not necessarily advisable).
|
||||||
type tcpInfo struct {
|
type tcpInfo struct {
|
||||||
box boxPubKey
|
box crypto.BoxPubKey
|
||||||
sig sigPubKey
|
sig crypto.SigPubKey
|
||||||
localAddr string
|
localAddr string
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper function to set additional options for specific connection types.
|
||||||
|
func (iface *tcpInterface) setExtraOptions(c net.Conn) {
|
||||||
|
switch sock := c.(type) {
|
||||||
|
case *net.TCPConn:
|
||||||
|
sock.SetNoDelay(true)
|
||||||
|
// TODO something for socks5
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the address of the listener.
|
// Returns the address of the listener.
|
||||||
func (iface *tcpInterface) getAddr() *net.TCPAddr {
|
func (iface *tcpInterface) getAddr() *net.TCPAddr {
|
||||||
return iface.serv.Addr().(*net.TCPAddr)
|
return iface.serv.Addr().(*net.TCPAddr)
|
||||||
@ -205,8 +211,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
|||||||
// It defers a bunch of cleanup stuff to tear down all of these things when the reader exists (e.g. due to a closed connection or a timeout).
|
// It defers a bunch of cleanup stuff to tear down all of these things when the reader exists (e.g. due to a closed connection or a timeout).
|
||||||
func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
||||||
defer sock.Close()
|
defer sock.Close()
|
||||||
|
iface.setExtraOptions(sock)
|
||||||
// Get our keys
|
// Get our keys
|
||||||
myLinkPub, myLinkPriv := newBoxKeys() // ephemeral link keys
|
myLinkPub, myLinkPriv := crypto.NewBoxKeys() // ephemeral link keys
|
||||||
meta := version_getBaseMetadata()
|
meta := version_getBaseMetadata()
|
||||||
meta.box = iface.core.boxPub
|
meta.box = iface.core.boxPub
|
||||||
meta.sig = iface.core.sigPub
|
meta.sig = iface.core.sigPub
|
||||||
@ -287,7 +294,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}()
|
}()
|
||||||
// Note that multiple connections to the same node are allowed
|
// Note that multiple connections to the same node are allowed
|
||||||
// E.g. over different interfaces
|
// E.g. over different interfaces
|
||||||
p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String())
|
p := iface.core.peers.newPeer(&info.box, &info.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String())
|
||||||
p.linkOut = make(chan []byte, 1)
|
p.linkOut = make(chan []byte, 1)
|
||||||
in := func(bs []byte) {
|
in := func(bs []byte) {
|
||||||
p.handlePacket(bs)
|
p.handlePacket(bs)
|
||||||
@ -301,7 +308,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
buf := net.Buffers{tcp_msg[:], msgLen, msg}
|
buf := net.Buffers{tcp_msg[:], msgLen, msg}
|
||||||
buf.WriteTo(sock)
|
buf.WriteTo(sock)
|
||||||
atomic.AddUint64(&p.bytesSent, uint64(len(tcp_msg)+len(msgLen)+len(msg)))
|
atomic.AddUint64(&p.bytesSent, uint64(len(tcp_msg)+len(msgLen)+len(msg)))
|
||||||
util_putBytes(msg)
|
util.PutBytes(msg)
|
||||||
}
|
}
|
||||||
timerInterval := tcp_ping_interval
|
timerInterval := tcp_ping_interval
|
||||||
timer := time.NewTimer(timerInterval)
|
timer := time.NewTimer(timerInterval)
|
||||||
@ -342,7 +349,6 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
out <- msg
|
out <- msg
|
||||||
}
|
}
|
||||||
p.close = func() { sock.Close() }
|
p.close = func() { sock.Close() }
|
||||||
setNoDelay(sock, true)
|
|
||||||
go p.linkLoop()
|
go p.linkLoop()
|
||||||
defer func() {
|
defer func() {
|
||||||
// Put all of our cleanup here...
|
// Put all of our cleanup here...
|
||||||
@ -350,8 +356,8 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}()
|
}()
|
||||||
us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
||||||
them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||||
themNodeID := getNodeID(&info.box)
|
themNodeID := crypto.GetNodeID(&info.box)
|
||||||
themAddr := address_addrForNodeID(themNodeID)
|
themAddr := address.AddrForNodeID(themNodeID)
|
||||||
themAddrString := net.IP(themAddr[:]).String()
|
themAddrString := net.IP(themAddr[:]).String()
|
||||||
themString := fmt.Sprintf("%s@%s", themAddrString, them)
|
themString := fmt.Sprintf("%s@%s", themAddrString, them)
|
||||||
iface.core.log.Println("Connected:", themString, "source", us)
|
iface.core.log.Println("Connected:", themString, "source", us)
|
||||||
@ -386,9 +392,9 @@ func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) error {
|
|||||||
// We didn't get the whole message yet
|
// We didn't get the whole message yet
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
newMsg := append(util_getBytes(), msg...)
|
newMsg := append(util.GetBytes(), msg...)
|
||||||
in(newMsg)
|
in(newMsg)
|
||||||
util_yield()
|
util.Yield()
|
||||||
}
|
}
|
||||||
frag = append(bs[:0], frag...)
|
frag = append(bs[:0], frag...)
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,28 @@ package yggdrasil
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/songgao/packets/ethernet"
|
"github.com/songgao/packets/ethernet"
|
||||||
"github.com/yggdrasil-network/water"
|
"github.com/yggdrasil-network/water"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tun_IPv6_HEADER_LENGTH = 40
|
const tun_IPv6_HEADER_LENGTH = 40
|
||||||
const tun_ETHER_HEADER_LENGTH = 14
|
const tun_ETHER_HEADER_LENGTH = 14
|
||||||
|
|
||||||
// Represents a running TUN/TAP interface.
|
// Represents a running TUN/TAP interface.
|
||||||
type tunDevice struct {
|
type tunAdapter struct {
|
||||||
core *Core
|
Adapter
|
||||||
icmpv6 icmpv6
|
icmpv6 icmpv6
|
||||||
send chan<- []byte
|
|
||||||
recv <-chan []byte
|
|
||||||
mtu int
|
mtu int
|
||||||
iface *water.Interface
|
iface *water.Interface
|
||||||
|
mutex sync.RWMutex // Protects the below
|
||||||
|
isOpen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the maximum supported MTU for the platform based on the defaults in
|
// Gets the maximum supported MTU for the platform based on the defaults in
|
||||||
@ -36,22 +39,25 @@ func getSupportedMTU(mtu int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialises the TUN/TAP adapter.
|
// Initialises the TUN/TAP adapter.
|
||||||
func (tun *tunDevice) init(core *Core) {
|
func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
|
||||||
tun.core = core
|
tun.Adapter.init(core, send, recv)
|
||||||
tun.icmpv6.init(tun)
|
tun.icmpv6.init(tun)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
||||||
// the read/write goroutines to handle packets on that interface.
|
// the read/write goroutines to handle packets on that interface.
|
||||||
func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
if ifname == "none" {
|
if ifname == "none" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() { panic(tun.read()) }()
|
tun.mutex.Lock()
|
||||||
go func() { panic(tun.write()) }()
|
tun.isOpen = true
|
||||||
|
tun.mutex.Unlock()
|
||||||
|
go func() { tun.core.log.Println("WARNING: tun.read() exited with error:", tun.read()) }()
|
||||||
|
go func() { tun.core.log.Println("WARNING: tun.write() exited with error:", tun.write()) }()
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -75,14 +81,14 @@ func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int)
|
|||||||
// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP
|
// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP
|
||||||
// mode then additional ethernet encapsulation is added for the benefit of the
|
// mode then additional ethernet encapsulation is added for the benefit of the
|
||||||
// host operating system.
|
// host operating system.
|
||||||
func (tun *tunDevice) write() error {
|
func (tun *tunAdapter) write() error {
|
||||||
for {
|
for {
|
||||||
data := <-tun.recv
|
data := <-tun.recv
|
||||||
if tun.iface == nil {
|
if tun.iface == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tun.iface.IsTAP() {
|
if tun.iface.IsTAP() {
|
||||||
var destAddr address
|
var destAddr address.Address
|
||||||
if data[0]&0xf0 == 0x60 {
|
if data[0]&0xf0 == 0x60 {
|
||||||
if len(data) < 40 {
|
if len(data) < 40 {
|
||||||
panic("Tried to send a packet shorter than an IPv6 header...")
|
panic("Tried to send a packet shorter than an IPv6 header...")
|
||||||
@ -96,7 +102,7 @@ func (tun *tunDevice) write() error {
|
|||||||
} else {
|
} else {
|
||||||
return errors.New("Invalid address family")
|
return errors.New("Invalid address family")
|
||||||
}
|
}
|
||||||
sendndp := func(destAddr address) {
|
sendndp := func(destAddr address.Address) {
|
||||||
neigh, known := tun.icmpv6.peermacs[destAddr]
|
neigh, known := tun.icmpv6.peermacs[destAddr]
|
||||||
known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
|
known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
|
||||||
if !known {
|
if !known {
|
||||||
@ -148,15 +154,29 @@ func (tun *tunDevice) write() error {
|
|||||||
len(data)) // Payload length
|
len(data)) // Payload length
|
||||||
copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
|
copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
|
||||||
if _, err := tun.iface.Write(frame); err != nil {
|
if _, err := tun.iface.Write(frame); err != nil {
|
||||||
|
tun.mutex.RLock()
|
||||||
|
open := tun.isOpen
|
||||||
|
tun.mutex.RUnlock()
|
||||||
|
if !open {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := tun.iface.Write(data); err != nil {
|
if _, err := tun.iface.Write(data); err != nil {
|
||||||
|
tun.mutex.RLock()
|
||||||
|
open := tun.isOpen
|
||||||
|
tun.mutex.RUnlock()
|
||||||
|
if !open {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
util_putBytes(data)
|
}
|
||||||
|
util.PutBytes(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +184,7 @@ func (tun *tunDevice) write() error {
|
|||||||
// is running in TAP mode then the ethernet headers will automatically be
|
// is running in TAP mode then the ethernet headers will automatically be
|
||||||
// processed and stripped if necessary. If an ICMPv6 packet is found, then
|
// processed and stripped if necessary. If an ICMPv6 packet is found, then
|
||||||
// the relevant helper functions in icmpv6.go are called.
|
// the relevant helper functions in icmpv6.go are called.
|
||||||
func (tun *tunDevice) read() error {
|
func (tun *tunAdapter) read() error {
|
||||||
mtu := tun.mtu
|
mtu := tun.mtu
|
||||||
if tun.iface.IsTAP() {
|
if tun.iface.IsTAP() {
|
||||||
mtu += tun_ETHER_HEADER_LENGTH
|
mtu += tun_ETHER_HEADER_LENGTH
|
||||||
@ -173,9 +193,16 @@ func (tun *tunDevice) read() error {
|
|||||||
for {
|
for {
|
||||||
n, err := tun.iface.Read(buf)
|
n, err := tun.iface.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tun.mutex.RLock()
|
||||||
|
open := tun.isOpen
|
||||||
|
tun.mutex.RUnlock()
|
||||||
|
if !open {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
// panic(err)
|
// panic(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
o := 0
|
o := 0
|
||||||
if tun.iface.IsTAP() {
|
if tun.iface.IsTAP() {
|
||||||
o = tun_ETHER_HEADER_LENGTH
|
o = tun_ETHER_HEADER_LENGTH
|
||||||
@ -193,7 +220,7 @@ func (tun *tunDevice) read() error {
|
|||||||
// tun.icmpv6.recv <- b
|
// tun.icmpv6.recv <- b
|
||||||
go tun.icmpv6.parse_packet(b)
|
go tun.icmpv6.parse_packet(b)
|
||||||
}
|
}
|
||||||
packet := append(util_getBytes(), buf[o:n]...)
|
packet := append(util.GetBytes(), buf[o:n]...)
|
||||||
tun.send <- packet
|
tun.send <- packet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,7 +228,10 @@ func (tun *tunDevice) read() error {
|
|||||||
// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil
|
// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil
|
||||||
// process stops. Typically this operation will happen quickly, but on macOS
|
// process stops. Typically this operation will happen quickly, but on macOS
|
||||||
// it can block until a read operation is completed.
|
// it can block until a read operation is completed.
|
||||||
func (tun *tunDevice) close() error {
|
func (tun *tunAdapter) close() error {
|
||||||
|
tun.mutex.Lock()
|
||||||
|
tun.isOpen = false
|
||||||
|
tun.mutex.Unlock()
|
||||||
if tun.iface == nil {
|
if tun.iface == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ type in6_ifreq_lifetime struct {
|
|||||||
// a system socket and making syscalls to the kernel. This is not refined though
|
// 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
|
// and often doesn't work (if at all), therefore if a call fails, it resorts
|
||||||
// to calling "ifconfig" instead.
|
// to calling "ifconfig" instead.
|
||||||
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
var config water.Config
|
var config water.Config
|
||||||
if ifname[:4] == "auto" {
|
if ifname[:4] == "auto" {
|
||||||
ifname = "/dev/tap0"
|
ifname = "/dev/tap0"
|
||||||
@ -103,7 +103,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
|
|||||||
return tun.setupAddress(addr)
|
return tun.setupAddress(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *tunDevice) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
var sfd int
|
var sfd int
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
||||||
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN")
|
tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN")
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ type ifreq struct {
|
|||||||
|
|
||||||
// Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using
|
// Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using
|
||||||
// a system socket and making direct syscalls to the kernel.
|
// a system socket and making direct syscalls to the kernel.
|
||||||
func (tun *tunDevice) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
var fd int
|
var fd int
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Configures the TAP adapter with the correct IPv6 address and MTU.
|
// Configures the TAP adapter with the correct IPv6 address and MTU.
|
||||||
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
var config water.Config
|
var config water.Config
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
config = water.Config{DeviceType: water.TAP}
|
config = water.Config{DeviceType: water.TAP}
|
||||||
@ -48,7 +48,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
|
|||||||
// is used to do this, so there is not a hard requirement on "ip" or "ifconfig"
|
// is used to do this, so there is not a hard requirement on "ip" or "ifconfig"
|
||||||
// to exist on the system, but this will fail if Netlink is not present in the
|
// to exist on the system, but this will fail if Netlink is not present in the
|
||||||
// kernel (it nearly always is).
|
// kernel (it nearly always is).
|
||||||
func (tun *tunDevice) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
// Set address
|
// Set address
|
||||||
var netIF *net.Interface
|
var netIF *net.Interface
|
||||||
ifces, err := net.Interfaces()
|
ifces, err := net.Interfaces()
|
||||||
|
@ -9,7 +9,7 @@ import water "github.com/yggdrasil-network/water"
|
|||||||
|
|
||||||
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
|
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
|
||||||
// no guarantees are made at this point on an unsupported platform.
|
// no guarantees are made at this point on an unsupported platform.
|
||||||
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
var config water.Config
|
var config water.Config
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
config = water.Config{DeviceType: water.TAP}
|
config = water.Config{DeviceType: water.TAP}
|
||||||
@ -27,7 +27,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
|
|||||||
|
|
||||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||||
// write about it to stdout and don't try to do anything further.
|
// write about it to stdout and don't try to do anything further.
|
||||||
func (tun *tunDevice) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows
|
// 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
|
// we don't make use of a direct operating system API to do this - we instead
|
||||||
// delegate the hard work to "netsh".
|
// delegate the hard work to "netsh".
|
||||||
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
if !iftapmode {
|
if !iftapmode {
|
||||||
tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP")
|
tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP")
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the MTU of the TAP adapter.
|
// Sets the MTU of the TAP adapter.
|
||||||
func (tun *tunDevice) setupMTU(mtu int) error {
|
func (tun *tunAdapter) setupMTU(mtu int) error {
|
||||||
// Set MTU
|
// Set MTU
|
||||||
cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface",
|
cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface",
|
||||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||||
@ -82,7 +82,7 @@ func (tun *tunDevice) setupMTU(mtu int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the IPv6 address of the TAP adapter.
|
// Sets the IPv6 address of the TAP adapter.
|
||||||
func (tun *tunDevice) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
// Set address
|
// Set address
|
||||||
cmd := exec.Command("netsh", "interface", "ipv6", "add", "address",
|
cmd := exec.Command("netsh", "interface", "ipv6", "add", "address",
|
||||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||||
|
@ -4,6 +4,8 @@ package yggdrasil
|
|||||||
// Used in the inital connection setup and key exchange
|
// Used in the inital connection setup and key exchange
|
||||||
// Some of this could arguably go in wire.go instead
|
// 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.
|
// 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.
|
// 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.
|
// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open an connection.
|
||||||
@ -12,9 +14,9 @@ type version_metadata struct {
|
|||||||
ver uint64 // 1 byte in this version
|
ver uint64 // 1 byte in this version
|
||||||
// Everything after this point potentially depends on the version number, and is subject to change in future versions
|
// Everything after this point potentially depends on the version number, and is subject to change in future versions
|
||||||
minorVer uint64 // 1 byte in this version
|
minorVer uint64 // 1 byte in this version
|
||||||
box boxPubKey
|
box crypto.BoxPubKey
|
||||||
sig sigPubKey
|
sig crypto.SigPubKey
|
||||||
link boxPubKey
|
link crypto.BoxPubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets a base metadata with no keys set, but with the correct version numbers.
|
// Gets a base metadata with no keys set, but with the correct version numbers.
|
||||||
@ -31,9 +33,9 @@ func version_getMetaLength() (mlen int) {
|
|||||||
mlen += 4 // meta
|
mlen += 4 // meta
|
||||||
mlen += 1 // ver, as long as it's < 127, which it is in this version
|
mlen += 1 // ver, as long as it's < 127, which it is in this version
|
||||||
mlen += 1 // minorVer, as long as it's < 127, which it is in this version
|
mlen += 1 // minorVer, as long as it's < 127, which it is in this version
|
||||||
mlen += boxPubKeyLen // box
|
mlen += crypto.BoxPubKeyLen // box
|
||||||
mlen += sigPubKeyLen // sig
|
mlen += crypto.SigPubKeyLen // sig
|
||||||
mlen += boxPubKeyLen // link
|
mlen += crypto.BoxPubKeyLen // link
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ package yggdrasil
|
|||||||
|
|
||||||
// Packet types, as wire_encode_uint64(type) at the start of each packet
|
// Packet types, as wire_encode_uint64(type) at the start of each packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
wire_Traffic = iota // data being routed somewhere, handle for crypto
|
wire_Traffic = iota // data being routed somewhere, handle for crypto
|
||||||
wire_ProtocolTraffic // protocol traffic, pub keys for crypto
|
wire_ProtocolTraffic // protocol traffic, pub keys for crypto
|
||||||
@ -16,6 +21,8 @@ const (
|
|||||||
wire_SessionPong // inside protocol traffic header
|
wire_SessionPong // inside protocol traffic header
|
||||||
wire_DHTLookupRequest // inside protocol traffic header
|
wire_DHTLookupRequest // inside protocol traffic header
|
||||||
wire_DHTLookupResponse // inside protocol traffic header
|
wire_DHTLookupResponse // inside protocol traffic header
|
||||||
|
wire_NodeInfoRequest // inside protocol traffic header
|
||||||
|
wire_NodeInfoResponse // inside protocol traffic header
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calls wire_put_uint64 on a nil slice.
|
// Calls wire_put_uint64 on a nil slice.
|
||||||
@ -191,14 +198,14 @@ func wire_chop_uint64(toUInt64 *uint64, fromSlice *[]byte) bool {
|
|||||||
// The wire format for ordinary IPv6 traffic encapsulated by the network.
|
// The wire format for ordinary IPv6 traffic encapsulated by the network.
|
||||||
type wire_trafficPacket struct {
|
type wire_trafficPacket struct {
|
||||||
Coords []byte
|
Coords []byte
|
||||||
Handle handle
|
Handle crypto.Handle
|
||||||
Nonce boxNonce
|
Nonce crypto.BoxNonce
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encodes a wire_trafficPacket into its wire format.
|
// Encodes a wire_trafficPacket into its wire format.
|
||||||
func (p *wire_trafficPacket) encode() []byte {
|
func (p *wire_trafficPacket) encode() []byte {
|
||||||
bs := util_getBytes()
|
bs := util.GetBytes()
|
||||||
bs = wire_put_uint64(wire_Traffic, bs)
|
bs = wire_put_uint64(wire_Traffic, bs)
|
||||||
bs = wire_put_coords(p.Coords, bs)
|
bs = wire_put_coords(p.Coords, bs)
|
||||||
bs = append(bs, p.Handle[:]...)
|
bs = append(bs, p.Handle[:]...)
|
||||||
@ -222,16 +229,16 @@ func (p *wire_trafficPacket) decode(bs []byte) bool {
|
|||||||
case !wire_chop_slice(p.Nonce[:], &bs):
|
case !wire_chop_slice(p.Nonce[:], &bs):
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
p.Payload = append(util_getBytes(), bs...)
|
p.Payload = append(util.GetBytes(), bs...)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// The wire format for protocol traffic, such as dht req/res or session ping/pong packets.
|
// The wire format for protocol traffic, such as dht req/res or session ping/pong packets.
|
||||||
type wire_protoTrafficPacket struct {
|
type wire_protoTrafficPacket struct {
|
||||||
Coords []byte
|
Coords []byte
|
||||||
ToKey boxPubKey
|
ToKey crypto.BoxPubKey
|
||||||
FromKey boxPubKey
|
FromKey crypto.BoxPubKey
|
||||||
Nonce boxNonce
|
Nonce crypto.BoxNonce
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +280,7 @@ func (p *wire_protoTrafficPacket) decode(bs []byte) bool {
|
|||||||
// The keys themselves are exchanged as part of the connection setup, and then omitted from the packets.
|
// The keys themselves are exchanged as part of the connection setup, and then omitted from the packets.
|
||||||
// The two layer logic is handled in peers.go, but it's kind of ugly.
|
// The two layer logic is handled in peers.go, but it's kind of ugly.
|
||||||
type wire_linkProtoTrafficPacket struct {
|
type wire_linkProtoTrafficPacket struct {
|
||||||
Nonce boxNonce
|
Nonce crypto.BoxNonce
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,6 +360,47 @@ func (p *sessionPing) decode(bs []byte) bool {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Encodes a nodeinfoReqRes into its wire format.
|
||||||
|
func (p *nodeinfoReqRes) encode() []byte {
|
||||||
|
var pTypeVal uint64
|
||||||
|
if p.IsResponse {
|
||||||
|
pTypeVal = wire_NodeInfoResponse
|
||||||
|
} else {
|
||||||
|
pTypeVal = wire_NodeInfoRequest
|
||||||
|
}
|
||||||
|
bs := wire_encode_uint64(pTypeVal)
|
||||||
|
bs = wire_put_coords(p.SendCoords, bs)
|
||||||
|
if pTypeVal == wire_NodeInfoResponse {
|
||||||
|
bs = append(bs, p.NodeInfo...)
|
||||||
|
}
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decodes an encoded nodeinfoReqRes into the struct, returning true if successful.
|
||||||
|
func (p *nodeinfoReqRes) decode(bs []byte) bool {
|
||||||
|
var pType uint64
|
||||||
|
switch {
|
||||||
|
case !wire_chop_uint64(&pType, &bs):
|
||||||
|
return false
|
||||||
|
case pType != wire_NodeInfoRequest && pType != wire_NodeInfoResponse:
|
||||||
|
return false
|
||||||
|
case !wire_chop_coords(&p.SendCoords, &bs):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if p.IsResponse = pType == wire_NodeInfoResponse; p.IsResponse {
|
||||||
|
if len(bs) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
p.NodeInfo = make(nodeinfoPayload, len(bs))
|
||||||
|
if !wire_chop_slice(p.NodeInfo[:], &bs) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Encodes a dhtReq into its wire format.
|
// Encodes a dhtReq into its wire format.
|
||||||
func (r *dhtReq) encode() []byte {
|
func (r *dhtReq) encode() []byte {
|
||||||
coords := wire_encode_coords(r.Coords)
|
coords := wire_encode_coords(r.Coords)
|
||||||
|
Loading…
Reference in New Issue
Block a user