Compare commits

..

1 Commits

Author SHA1 Message Date
Neil Alexander
f325dfc73e Update dependencies, test cross-builds for FreeBSD and OpenBSD in CI 2023-10-27 23:15:34 +01:00
33 changed files with 198 additions and 504 deletions

View File

@@ -16,7 +16,7 @@ jobs:
name: Package (Debian, ${{ matrix.pkgarch }})
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
@@ -107,7 +107,7 @@ jobs:
name: Package (Router, ${{ matrix.pkgarch }})
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:

View File

@@ -26,49 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- in case of vulnerabilities.
-->
## [0.5.4] - 2023-11-27
### Fixed
* Fixed a crash that could happen when calculating the size of bloom filters during encoding
## [0.5.3] - 2023-11-26
### Fixed
* Fixed a data race from buffered pathfinder traffic
* Fix a bug where the next-hop selection may not take shortcuts through treespace
* Backoffs are now reset correctly when a successful handshake is completed
* Backoffs will no longer exceed roughly 4.5 hours when peers are down for a long time
* The `-normaliseconf` option will now work correctly with `PrivateKeyPath`
* Improved the reliability of QUIC peering setup by disabling 0-RTT
## [0.5.2] - 2023-11-06
### Added
* New `-publickey` command line option that prints the derived public key from a configuration file
* Support for connecting to TLS peers via SOCKS with the new `sockstls://` link schema
### Changed
* Stabilise tree parent selection algorithm
* Improved logging when the TUN interface fails to set up
### Fixed
* Fixed a panic that could occur when a connection reaches an inconsistent error state
* The admin socket will now report more peering handshake error conditions in `getPeers`
* Yggdrasil will no longer panic at startup when duplicate peers are configured
* The `build` script will no longer incorrectly import `LDFLAGS` from the environment
## [0.5.1] - 2023-10-28
### Fixed
* Fix the Debian package so that upgrades are handled more smoothly
## [0.5.0] - 2023-10-28
## [0.5.0] - Release Candidate 1
### Added

View File

@@ -16,7 +16,6 @@ import (
"fmt"
"net"
"runtime"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
@@ -28,8 +27,6 @@ type keySet struct {
func main() {
threads := runtime.GOMAXPROCS(0)
fmt.Println("Threads:", threads)
start := time.Now()
var currentBest ed25519.PublicKey
newKeys := make(chan keySet, threads)
for i := 0; i < threads; i++ {
@@ -39,7 +36,7 @@ func main() {
newKey := <-newKeys
if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 {
currentBest = newKey.pub
fmt.Println("-----", time.Since(start))
fmt.Println("-----")
fmt.Println("Priv:", hex.EncodeToString(newKey.priv))
fmt.Println("Pub:", hex.EncodeToString(newKey.pub))
addr := address.AddrForKey(newKey.pub)

View File

@@ -48,9 +48,8 @@ func main() {
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
ver := flag.Bool("version", false, "prints the version of this build")
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
getaddr := flag.Bool("address", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 address")
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
loglevel := flag.String("loglevel", "info", "loglevel to enable")
flag.Parse()
@@ -68,7 +67,7 @@ func main() {
case "syslog":
if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil {
logger = log.New(syslogger, "", log.Flags()&^(log.Ldate|log.Ltime))
logger = log.New(syslogger, "", log.Flags() &^ (log.Ldate | log.Ltime))
}
default:
@@ -114,7 +113,6 @@ func main() {
_ = f.Close()
case *genconf:
cfg.AdminListen = ""
var bs []byte
if *confjson {
bs, err = json.MarshalIndent(cfg, "", " ")
@@ -156,15 +154,7 @@ func main() {
fmt.Println(ipnet.String())
return
case *getpkey:
fmt.Println(hex.EncodeToString(publicKey))
return
case *normaliseconf:
cfg.AdminListen = ""
if cfg.PrivateKeyPath != "" {
cfg.PrivateKey = nil
}
var bs []byte
if *confjson {
bs, err = json.MarshalIndent(cfg, "", " ")
@@ -215,10 +205,6 @@ func main() {
if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil {
panic(err)
}
address, subnet := n.core.Address(), n.core.Subnet()
logger.Printf("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
logger.Printf("Your IPv6 address is %s", address.String())
logger.Printf("Your IPv6 subnet is %s", subnet.String())
}
// Setup the admin socket.
@@ -226,9 +212,6 @@ func main() {
options := []admin.SetupOption{
admin.ListenAddress(cfg.AdminListen),
}
if cfg.LogLookups {
options = append(options, admin.LogLookups{})
}
if n.admin, err = admin.New(n.core, logger, options...); err != nil {
panic(err)
}

View File

@@ -39,8 +39,8 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() {
fmt.Println(" - ", os.Args[0], "list")
fmt.Println(" - ", os.Args[0], "getPeers")
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers")
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers")
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
}
server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint")

BIN
contrib/.DS_Store vendored

Binary file not shown.

View File

@@ -21,16 +21,13 @@ if [ $PKGBRANCH = "master" ]; then
PKGREPLACES=yggdrasil-develop
fi
GOLDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf"
GOLDFLAGS="${GOLDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock"
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -l "${GOLDFLAGS}"
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build -l "${GOLDFLAGS}"
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build
else
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
exit 1
@@ -41,7 +38,7 @@ echo "Building $PKGFILE"
mkdir -p /tmp/$PKGNAME/
mkdir -p /tmp/$PKGNAME/debian/
mkdir -p /tmp/$PKGNAME/usr/bin/
mkdir -p /tmp/$PKGNAME/lib/systemd/system/
mkdir -p /tmp/$PKGNAME/etc/systemd/system/
cat > /tmp/$PKGNAME/debian/changelog << EOF
Please see https://github.com/yggdrasil-network/yggdrasil-go/
@@ -71,52 +68,35 @@ EOF
cat > /tmp/$PKGNAME/debian/install << EOF
usr/bin/yggdrasil usr/bin
usr/bin/yggdrasilctl usr/bin
lib/systemd/system/*.service lib/systemd/system
etc/systemd/system/*.service etc/systemd/system
EOF
cat > /tmp/$PKGNAME/debian/postinst << EOF
#!/bin/sh
systemctl daemon-reload
if ! getent group yggdrasil 2>&1 > /dev/null; then
groupadd --system --force yggdrasil
groupadd --system --force yggdrasil || echo "Failed to create group 'yggdrasil' - please create it manually and reinstall"
fi
if [ ! -d /etc/yggdrasil ];
then
mkdir -p /etc/yggdrasil
chown root:yggdrasil /etc/yggdrasil
chmod 750 /etc/yggdrasil
fi
if [ ! -f /etc/yggdrasil/yggdrasil.conf ];
then
test -f /etc/yggdrasil.conf && mv /etc/yggdrasil.conf /etc/yggdrasil/yggdrasil.conf
fi
if [ -f /etc/yggdrasil/yggdrasil.conf ];
if [ -f /etc/yggdrasil.conf ];
then
mkdir -p /var/backups
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
cp /etc/yggdrasil/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
echo "Normalising and updating /etc/yggdrasil.conf"
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil.conf
chgrp yggdrasil /etc/yggdrasil.conf
echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
chmod 640 /etc/yggdrasil/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/yggdrasil.conf"
/usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
chmod 640 /etc/yggdrasil/yggdrasil.conf
echo "Generating initial configuration file /etc/yggdrasil.conf"
echo "Please familiarise yourself with this file before starting Yggdrasil"
sh -c 'umask 0027 && /usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf'
chgrp yggdrasil /etc/yggdrasil.conf
fi
systemctl enable yggdrasil
systemctl restart yggdrasil
exit 0
EOF
cat > /tmp/$PKGNAME/debian/prerm << EOF
#!/bin/sh
@@ -130,14 +110,13 @@ EOF
cp yggdrasil /tmp/$PKGNAME/usr/bin/
cp yggdrasilctl /tmp/$PKGNAME/usr/bin/
cp contrib/systemd/yggdrasil-default-config.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil-default-config.service
cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil.service
cp contrib/systemd/*.service /tmp/$PKGNAME/etc/systemd/system/
tar --no-xattrs -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \
tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \
usr/bin/yggdrasil usr/bin/yggdrasilctl \
lib/systemd/system/yggdrasil.service \
lib/systemd/system/yggdrasil-default-config.service
tar --no-xattrs -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian .
etc/systemd/system/yggdrasil.service \
etc/systemd/system/yggdrasil-default-config.service
tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian .
echo 2.0 > /tmp/$PKGNAME/debian-binary
ar -r $PKGFILE \

View File

@@ -48,7 +48,6 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
logger.EnableLevel("error")
logger.EnableLevel("warn")
logger.EnableLevel("info")
m.logger = logger
m.config = config.GenerateConfig()
if err := m.config.UnmarshalHJSON(configjson); err != nil {
return err
@@ -71,24 +70,16 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
}
options = append(options, core.AllowedPublicKey(k[:]))
}
for _, lAddr := range m.config.Listen {
options = append(options, core.ListenAddress(lAddr))
}
var err error
m.core, err = core.New(m.config.Certificate, logger, options...)
if err != nil {
panic(err)
}
address, subnet := m.core.Address(), m.core.Subnet()
logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey()))
logger.Infof("Your IPv6 address is %s", address.String())
logger.Infof("Your IPv6 subnet is %s", subnet.String())
}
// Setup the multicast module.
if len(m.config.MulticastInterfaces) > 0 {
var err error
logger.Infof("Initializing multicast %s", "")
options := []multicast.SetupOption{}
for _, intf := range m.config.MulticastInterfaces {
options = append(options, multicast.MulticastInterface{
@@ -100,10 +91,9 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
Password: intf.Password,
})
}
logger.Infof("Starting multicast %s", "")
m.multicast, err = multicast.New(m.core, m.logger, options...)
if err != nil {
logger.Errorln("An error occurred starting multicast:", err)
m.logger.Errorln("An error occurred starting multicast:", err)
}
}
@@ -162,20 +152,15 @@ func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) {
func (m *Yggdrasil) Stop() error {
logger := log.New(m.log, "", 0)
logger.EnableLevel("info")
logger.Infof("Stopping the mobile Yggdrasil instance %s", "")
if m.multicast != nil {
logger.Infof("Stopping multicast %s", "")
if err := m.multicast.Stop(); err != nil {
return err
}
logger.Infof("Stop the mobile Yggdrasil instance %s", "")
if err := m.multicast.Stop(); err != nil {
return err
}
logger.Infof("Stopping TUN device %s", "")
if m.tun != nil {
if err := m.tun.Stop(); err != nil {
return err
}
}
logger.Infof("Stopping Yggdrasil core %s", "")
m.core.Stop()
return nil
}
@@ -223,11 +208,8 @@ func (m *Yggdrasil) GetPeersJSON() (result string) {
IP string
}{}
for _, v := range m.core.GetPeers() {
var ip string
if v.Key != nil {
a := address.AddrForKey(v.Key)
ip = net.IP(a[:]).String()
}
a := address.AddrForKey(v.Key)
ip := net.IP(a[:]).String()
peers = append(peers, struct {
core.PeerInfo
IP string

View File

@@ -1,13 +0,0 @@
[Unit]
Description=Yggdrasil default config generator
ConditionPathExists=|!/etc/yggdrasil/yggdrasil.conf
ConditionFileNotEmpty=|!/etc/yggdrasil/yggdrasil.conf
Wants=local-fs.target
After=local-fs.target
[Service]
Type=oneshot
Group=yggdrasil
ExecStartPre=/usr/bin/mkdir -p /etc/yggdrasil
ExecStart=/usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf
ExecStartPost=/usr/bin/chmod -R 0640 /etc/yggdrasil

View File

@@ -1,25 +0,0 @@
[Unit]
Description=Yggdrasil Network
Wants=network-online.target
Wants=yggdrasil-default-config.service
After=network-online.target
After=yggdrasil-default-config.service
[Service]
Group=yggdrasil
ProtectHome=true
ProtectSystem=strict
NoNewPrivileges=true
RuntimeDirectory=yggdrasil
ReadWritePaths=/var/run/yggdrasil/ /run/yggdrasil/
SyslogIdentifier=yggdrasil
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
ExecStartPre=+-/sbin/modprobe tun
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil/yggdrasil.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
TimeoutStopSec=5
[Install]
WantedBy=multi-user.target

2
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.20
require (
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
github.com/cheggaaa/pb/v3 v3.1.4
github.com/gologme/log v1.3.0

4
go.sum
View File

@@ -1,5 +1,5 @@
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd h1:458tnmZ4zM2gbLtefdYbaxyAJevDNEWu6tLKEqbK4wg=
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ROnmHuGrIZO250Lx/YHmp9o48XE+Vw=
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=

View File

@@ -1,24 +1,9 @@
package admin
import (
"crypto/ed25519"
"encoding/hex"
"encoding/json"
"net"
"sync"
"time"
"github.com/Arceliar/ironwood/network"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
func (c *AdminSocket) _applyOption(opt SetupOption) {
switch v := opt.(type) {
case ListenAddress:
c.config.listenaddr = v
case LogLookups:
c.logLookups()
}
}
@@ -29,51 +14,3 @@ type SetupOption interface {
type ListenAddress string
func (a ListenAddress) isSetupOption() {}
type LogLookups struct{}
func (l LogLookups) isSetupOption() {}
func (a *AdminSocket) logLookups() {
type resi struct {
Address string `json:"addr"`
Key string `json:"key"`
Path []uint64 `json:"path"`
Time int64 `json:"time"`
}
type res struct {
Infos []resi `json:"infos"`
}
type info struct {
path []uint64
time time.Time
}
type edk [ed25519.PublicKeySize]byte
infos := make(map[edk]info)
var m sync.Mutex
a.core.PacketConn.PacketConn.Debug.SetDebugLookupLogger(func(l network.DebugLookupInfo) {
var k edk
copy(k[:], l.Key[:])
m.Lock()
infos[k] = info{path: l.Path, time: time.Now()}
m.Unlock()
})
_ = a.AddHandler(
"lookups", "Dump a record of lookups received in the past hour", []string{},
func(in json.RawMessage) (interface{}, error) {
m.Lock()
rs := make([]resi, 0, len(infos))
for k, v := range infos {
if time.Since(v.time) > 24*time.Hour {
// TODO? automatic cleanup, so we don't need to call lookups periodically to prevent leaks
delete(infos, k)
}
a := address.AddrForKey(ed25519.PublicKey(k[:]))
addr := net.IP(a[:]).String()
rs = append(rs, resi{Address: addr, Key: hex.EncodeToString(k[:]), Path: v.path, Time: v.time.Unix()})
}
m.Unlock()
return &res{Infos: rs}, nil
},
)
}

View File

@@ -1,10 +1,5 @@
package admin
import (
"fmt"
"net/url"
)
type RemovePeerRequest struct {
Uri string `json:"uri"`
Sintf string `json:"interface,omitempty"`
@@ -13,9 +8,5 @@ type RemovePeerRequest struct {
type RemovePeerResponse struct{}
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error {
u, err := url.Parse(req.Uri)
if err != nil {
return fmt.Errorf("unable to parse peering URI: %w", err)
}
return a.core.RemovePeer(u, req.Sintf)
return a.core.RemovePeer(req.Uri, req.Sintf)
}

View File

@@ -40,20 +40,19 @@ import (
// options that are necessary for an Yggdrasil node to run. You will need to
// supply one of these structs to the Yggdrasil core when starting a node.
type NodeConfig struct {
PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"`
PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."`
PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"`
PrivateKeyPath string `json:",omitempty"`
Certificate *tls.Certificate `json:"-"`
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
AdminListen string `json:",omitempty" comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
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."`
LogLookups bool `json:",omitempty"`
}
type MulticastInterfaceConfig struct {

View File

@@ -3,6 +3,7 @@ package core
import (
"crypto/ed25519"
"encoding/json"
"fmt"
"net"
"net/url"
"sync/atomic"
@@ -191,8 +192,28 @@ func (c *Core) AddPeer(u *url.URL, sintf string) error {
// RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer.
// The peer is not disconnected immediately.
func (c *Core) RemovePeer(u *url.URL, sintf string) error {
return c.links.remove(u, sintf, linkTypePersistent)
func (c *Core) RemovePeer(uri string, sourceInterface string) error {
return fmt.Errorf("not implemented yet")
/*
var err error
phony.Block(c, func() {
peer := Peer{uri, sourceInterface}
linkInfo, ok := c.config._peers[peer]
if !ok {
err = fmt.Errorf("peer not configured")
return
}
if ok && linkInfo != nil {
c.links.Act(nil, func() {
if link := c.links._links[*linkInfo]; link != nil {
_ = link.conn.Close()
}
})
}
delete(c.config._peers, peer)
})
return err
*/
}
// CallPeer calls a peer once. This should be specified in the peer URI format,

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/ed25519"
"crypto/tls"
"encoding/hex"
"fmt"
"io"
"net"
@@ -103,6 +104,10 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
); err != nil {
return nil, fmt.Errorf("error creating encryption: %w", err)
}
address, subnet := c.Address(), c.Subnet()
c.log.Infof("Your public key is %s", hex.EncodeToString(c.public))
c.log.Infof("Your IPv6 address is %s", address.String())
c.log.Infof("Your IPv6 subnet is %s", subnet.String())
c.proto.init(c)
if err := c.links.init(c); err != nil {
return nil, fmt.Errorf("error initialising links: %w", err)
@@ -135,14 +140,7 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
}
func (c *Core) RetryPeersNow() {
phony.Block(&c.links, func() {
for _, l := range c.links._links {
select {
case l.kick <- struct{}{}:
default:
}
}
})
// TODO: figure out a way to retrigger peer connections.
}
// Stop shuts down the Yggdrasil node.

View File

@@ -1,3 +1,6 @@
//go:build debug
// +build debug
package core
import (
@@ -5,13 +8,28 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"runtime"
"github.com/gologme/log"
)
// Start the profiler if the required environment variable is set.
// Start the profiler in debug builds, if the required environment variable is set.
func init() {
envVarName := "PPROFLISTEN"
if hostPort := os.Getenv(envVarName); hostPort != "" {
hostPort := os.Getenv(envVarName)
switch {
case hostPort == "":
fmt.Fprintf(os.Stderr, "DEBUG: %s not set, profiler not started.\n", envVarName)
default:
fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort)
go fmt.Println(http.ListenAndServe(hostPort, nil))
go func() { fmt.Println(http.ListenAndServe(hostPort, nil)) }()
}
}
// Starts the function profiler. This is only supported when built with
// '-tags build'.
func StartProfiler(log *log.Logger) error {
runtime.SetBlockProfileRate(1)
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()
return nil
}

View File

@@ -53,11 +53,9 @@ type linkInfo struct {
// link tracks the state of a connection, either persistent or non-persistent
type link struct {
ctx context.Context // Connection context
cancel context.CancelFunc // Stop future redial attempts (when peer removed)
kick chan struct{} // Attempt to reconnect now, if backing off
linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral
linkProto string // Protocol carrier of link, e.g. TCP, AWDL
kick chan struct{} // Attempt to reconnect now, if backing off
linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral
linkProto string // Protocol carrier of link, e.g. TCP, AWDL
// The remaining fields can only be modified safely from within the links actor
_conn *linkConn // Connected link, if any, nil if not connected
_err error // Last error on the connection, if any
@@ -131,7 +129,6 @@ type linkError string
func (e linkError) Error() string { return string(e) }
const ErrLinkAlreadyConfigured = linkError("peer is already configured")
const ErrLinkNotConfigured = linkError("peer is not configured")
const ErrLinkPriorityInvalid = linkError("priority value is invalid")
const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
const ErrLinkPasswordInvalid = linkError("password is invalid")
@@ -179,22 +176,6 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
}
options.password = []byte(p)
}
// SNI headers must contain hostnames and not IP addresses, so we must make sure
// that we do not populate the SNI with an IP literal. We do this by splitting
// the host-port combo from the query option and then seeing if it parses to an
// IP address successfully or not.
if sni := u.Query().Get("sni"); sni != "" {
if net.ParseIP(sni) == nil {
options.tlsSNI = sni
}
}
// If the SNI is not configured still because the above failed then we'll try
// again but this time we'll use the host part of the peering URI instead.
if options.tlsSNI == "" {
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
options.tlsSNI = host
}
}
// If we think we're already connected to this peer, load up
// the existing peer state. Try to kick the peer if possible,
@@ -218,7 +199,6 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
linkProto: strings.ToUpper(u.Scheme),
kick: make(chan struct{}),
}
state.ctx, state.cancel = context.WithCancel(l.core.ctx)
// Store the state of the link so that it can be queried later.
l._links[info] = state
@@ -235,56 +215,37 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
// The caller should check the return value to decide whether
// or not to give up trying.
backoffNow := func() bool {
if backoff < 14 { // Cap at roughly 4.5 hours maximum.
backoff++
}
backoff++
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
select {
case <-state.kick:
return true
case <-state.ctx.Done():
return false
case <-l.core.ctx.Done():
return false
case <-time.After(duration):
return true
case <-state.kick:
return true
case <-l.core.ctx.Done():
return false
}
}
// resetBackoff is called by the connection handler when the
// handshake has successfully completed.
resetBackoff := func() {
backoff = 0
}
// The goroutine is responsible for attempting the connection
// and then running the handler. If the connection is persistent
// then the loop will run endlessly, using backoffs as needed.
// Otherwise the loop will end, cleaning up the link entry.
go func() {
defer phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
defer func() {
phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
}()
// This loop will run each and every time we want to attempt
// a connection to this peer.
// TODO get rid of this loop, this is *exactly* what time.AfterFunc is for, we should just send a signal to the links actor to kick off a goroutine as needed
for {
select {
case <-state.ctx.Done():
// The peering context has been cancelled, so don't try
// to dial again.
return
default:
}
conn, err := l.connect(state.ctx, u, info, options)
if err != nil || conn == nil {
if err == nil && conn == nil {
l.core.log.Warnf("Link %q reached inconsistent error state", u.String())
}
conn, err := l.connect(u, info, options)
if err != nil {
if linkType == linkTypePersistent {
// If the link is a persistent configured peering,
// store information about the connection error so
@@ -333,8 +294,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
// Give the connection to the handler. The handler will block
// for the lifetime of the connection.
if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF {
if err = l.handler(linkType, options, lc); err != nil && err != io.EOF {
l.core.log.Debugf("Link %s error: %s\n", info.uri, err)
} else {
backoff = 0
}
// The handler has stopped running so the connection is dead,
@@ -356,39 +319,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
}
return
}
break
}
}()
})
return retErr
}
func (l *links) remove(u *url.URL, sintf string, linkType linkType) error {
var retErr error
phony.Block(l, func() {
// Generate the link info and see whether we think we already
// have an open peering to this peer.
lu := urlForLinkInfo(*u)
info := linkInfo{
uri: lu.String(),
sintf: sintf,
}
// If this peer is already configured then we will close the
// connection and stop it from retrying.
state, ok := l._links[info]
if ok && state != nil {
state.cancel()
if conn := state._conn; conn != nil {
retErr = conn.Close()
}
return
}
retErr = ErrLinkNotConfigured
})
return retErr
}
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
ctx, cancel := context.WithCancel(l.core.ctx)
var protocol linkProtocol
@@ -432,12 +369,12 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
}
go func() {
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr())
l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
defer l.core.log.Printf("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr())
for {
conn, err := listener.Accept()
if err != nil {
return
continue
}
go func(conn net.Conn) {
defer conn.Close()
@@ -497,7 +434,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
// Give the connection to the handler. The handler will block
// for the lifetime of the connection.
if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF {
if err = l.handler(linkTypeIncoming, options, lc); err != nil && err != io.EOF {
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
}
@@ -516,14 +453,30 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
return li, nil
}
func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
var dialer linkProtocol
switch strings.ToLower(u.Scheme) {
case "tcp":
dialer = l.tcp
case "tls":
// SNI headers must contain hostnames and not IP addresses, so we must make sure
// that we do not populate the SNI with an IP literal. We do this by splitting
// the host-port combo from the query option and then seeing if it parses to an
// IP address successfully or not.
if sni := u.Query().Get("sni"); sni != "" {
if net.ParseIP(sni) == nil {
options.tlsSNI = sni
}
}
// If the SNI is not configured still because the above failed then we'll try
// again but this time we'll use the host part of the peering URI instead.
if options.tlsSNI == "" {
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
options.tlsSNI = host
}
}
dialer = l.tls
case "socks", "sockstls":
case "socks":
dialer = l.socks
case "unix":
dialer = l.unix
@@ -532,10 +485,10 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
default:
return nil, ErrLinkUnrecognisedSchema
}
return dialer.dial(ctx, u, info, options)
return dialer.dial(l.core.ctx, u, info, options)
}
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error {
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error {
meta := version_getBaseMetadata()
meta.publicKey = l.core.public
meta.priority = options.priority
@@ -555,9 +508,8 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
}
meta = version_metadata{}
base := version_getBaseMetadata()
if err := meta.decode(conn, options.password); err != nil {
_ = conn.Close()
return err
if !meta.decode(conn, options.password) {
return conn.Close()
}
if !meta.check() {
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
@@ -606,9 +558,6 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
}
l.core.log.Infof("Connected %s: %s, source %s",
dir, remoteStr, localStr)
if success != nil {
success()
}
err = l.core.HandleConn(meta.publicKey, conn, priority)
switch err {

View File

@@ -24,7 +24,7 @@ type linkQUICStream struct {
}
type linkQUICListener struct {
*quic.Listener
*quic.EarlyListener
ch <-chan *linkQUICStream
}
@@ -50,11 +50,11 @@ func (l *links) newLinkQUIC() *linkQUIC {
}
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig)
qc, err := quic.DialAddrEarly(ctx, url.Host, l.tlsconfig, l.quicconfig)
if err != nil {
return nil, err
}
qs, err := qc.OpenStreamSync(ctx)
qs, err := qc.OpenStream()
if err != nil {
return nil, err
}
@@ -65,14 +65,14 @@ func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, option
}
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
ql, err := quic.ListenAddr(url.Host, l.tlsconfig, l.quicconfig)
ql, err := quic.ListenAddrEarly(url.Host, l.tlsconfig, l.quicconfig)
if err != nil {
return nil, err
}
ch := make(chan *linkQUICStream)
lql := &linkQUICListener{
Listener: ql,
ch: ch,
EarlyListener: ql,
ch: ch,
}
go func() {
for {

View File

@@ -2,7 +2,6 @@ package core
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/url"
@@ -35,16 +34,7 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options
return nil, fmt.Errorf("failed to configure proxy")
}
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
conn, err := dialer.Dial("tcp", pathtokens[0])
if err != nil {
return nil, fmt.Errorf("failed to dial: %w", err)
}
if url.Scheme == "sockstls" {
tlsconfig := l.tls.config.Clone()
tlsconfig.ServerName = options.tlsSNI
conn = tls.Client(conn, tlsconfig)
}
return conn, nil
return dialer.Dial("tcp", pathtokens[0])
}
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {

View File

@@ -13,15 +13,7 @@ func (c *Core) _applyOption(opt SetupOption) (err error) {
if err != nil {
return fmt.Errorf("unable to parse peering URI: %w", err)
}
err = c.links.add(u, v.SourceInterface, linkTypePersistent)
switch err {
case ErrLinkAlreadyConfigured:
// Don't return this error, otherwise we'll panic at startup
// if there are multiple of the same peer configured
return nil
default:
return err
}
return c.links.add(u, v.SourceInterface, linkTypePersistent)
case ListenAddress:
c.config._listeners[v] = struct{}{}
case NodeInfo:

View File

@@ -1,41 +0,0 @@
package core
import (
"net/url"
"testing"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
)
// Tests that duplicate peers in the configuration file
// won't cause an error when the node starts. Otherwise
// we can panic unnecessarily.
func TestDuplicatePeerAtStartup(t *testing.T) {
cfg := config.GenerateConfig()
for i := 0; i < 5; i++ {
cfg.Peers = append(cfg.Peers, "tcp://1.2.3.4:4321")
}
if _, err := New(cfg.Certificate, nil); err != nil {
t.Fatal(err)
}
}
// Tests that duplicate peers given to us through the
// API will still error as expected, even if they didn't
// at startup. We expect to notify the user through the
// admin socket if they try to add a peer that is already
// configured.
func TestDuplicatePeerFromAPI(t *testing.T) {
cfg := config.GenerateConfig()
c, err := New(cfg.Certificate, nil)
if err != nil {
t.Fatal(err)
}
u, _ := url.Parse("tcp://1.2.3.4:4321")
if err := c.AddPeer(u, ""); err != nil {
t.Fatalf("Adding peer failed on first attempt: %s", err)
}
if err := c.AddPeer(u, ""); err == nil {
t.Fatalf("Adding peer should have failed on second attempt")
}
}

View File

@@ -8,7 +8,7 @@ import (
func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) {
config := &tls.Config{
Certificates: []tls.Certificate{*cert},
ClientAuth: tls.NoClientCert,
ClientAuth: tls.RequireAnyClientCert,
GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return cert, nil
},

View File

@@ -87,22 +87,22 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
}
// Decodes version metadata from its wire format into the struct.
func (m *version_metadata) decode(r io.Reader, password []byte) error {
func (m *version_metadata) decode(r io.Reader, password []byte) bool {
bh := [6]byte{}
if _, err := io.ReadFull(r, bh[:]); err != nil {
return err
return false
}
meta := [4]byte{'m', 'e', 't', 'a'}
if !bytes.Equal(bh[:4], meta[:]) {
return fmt.Errorf("invalid handshake preamble")
return false
}
hl := binary.BigEndian.Uint16(bh[4:6])
if hl < ed25519.SignatureSize {
return fmt.Errorf("invalid handshake length")
}
bs := make([]byte, hl)
bs := make([]byte, binary.BigEndian.Uint16(bh[4:6]))
if _, err := io.ReadFull(r, bs); err != nil {
return err
return false
}
if len(bs) < ed25519.SignatureSize {
return false
}
sig := bs[len(bs)-ed25519.SignatureSize:]
bs = bs[:len(bs)-ed25519.SignatureSize]
@@ -132,17 +132,14 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
hasher, err := blake2b.New512(password)
if err != nil {
return fmt.Errorf("invalid password supplied")
return false
}
n, err := hasher.Write(m.publicKey)
if err != nil || n != ed25519.PublicKeySize {
return fmt.Errorf("failed to generate hash")
return false
}
hash := hasher.Sum(nil)
if !ed25519.Verify(m.publicKey, hash, sig) {
return fmt.Errorf("password is incorrect")
}
return nil
return ed25519.Verify(m.publicKey, hash, sig)
}
// Checks that the "meta" bytes and the version numbers are the expected values.

View File

@@ -34,7 +34,7 @@ func TestVersionPasswordAuth(t *testing.T) {
}
var decoded version_metadata
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2) == nil; allowed != tt.allowed {
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed {
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
}
}
@@ -67,8 +67,8 @@ func TestVersionRoundtrip(t *testing.T) {
}
encoded := bytes.NewBuffer(meta)
decoded := &version_metadata{}
if err := decoded.decode(encoded, password); err != nil {
t.Fatalf("failed to decode: %s", err)
if !decoded.decode(encoded, password) {
t.Fatalf("failed to decode")
}
if !reflect.DeepEqual(test, decoded) {
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)

View File

@@ -29,7 +29,7 @@ func (tun *TunAdapter) write() {
bs := buf[TUN_OFFSET_BYTES:]
n, err := tun.rwc.Read(bs)
if err != nil {
tun.log.Errorln("Exiting TUN writer due to core read error:", err)
tun.log.Errorln("Exiting tun writer due to core read error:", err)
return
}
if !tun.isEnabled {

View File

@@ -44,7 +44,7 @@ type TunAdapter struct {
isOpen bool
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
config struct {
fd int32
fd int32
name InterfaceName
mtu InterfaceMTU
}
@@ -116,10 +116,7 @@ func (tun *TunAdapter) _start() error {
tun.addr = tun.rwc.Address()
tun.subnet = tun.rwc.Subnet()
prefix := address.GetPrefix()
var addr string
if tun.addr.IsValid() {
addr = fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1)
}
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1)
if tun.config.name == "none" || tun.config.name == "dummy" {
tun.log.Debugln("Not starting TUN as ifname is none or dummy")
tun.isEnabled = false

View File

@@ -78,7 +78,7 @@ type in6_ifreq_lifetime struct {
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
iface, err := wgtun.CreateTUN(ifname, int(mtu))
if err != nil {
return fmt.Errorf("failed to create TUN: %w", err)
panic(err)
}
tun.iface = iface
if mtu, err := iface.MTU(); err == nil {
@@ -86,10 +86,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
} else {
tun.mtu = 0
}
if addr != "" {
return tun.setupAddress(addr)
}
return nil
return tun.setupAddress(addr)
}
// Configures the "utun" adapter from an existing file descriptor.

View File

@@ -7,7 +7,6 @@ package tun
import (
"encoding/binary"
"fmt"
"os"
"strconv"
"strings"
@@ -25,7 +24,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
}
iface, err := wgtun.CreateTUN(ifname, int(mtu))
if err != nil {
return fmt.Errorf("failed to create TUN: %w", err)
panic(err)
}
tun.iface = iface
if m, err := iface.MTU(); err == nil {
@@ -33,27 +32,24 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
} else {
tun.mtu = 0
}
if addr != "" {
return tun.setupAddress(addr)
}
return nil
return tun.setupAddress(addr)
}
// Configures the "utun" adapter from an existing file descriptor.
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
dfd, err := unix.Dup(int(fd))
if err != nil {
return fmt.Errorf("failed to duplicate FD: %w", err)
return err
}
err = unix.SetNonblock(dfd, true)
if err != nil {
unix.Close(dfd)
return fmt.Errorf("failed to set FD as non-blocking: %w", err)
return err
}
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
if err != nil {
unix.Close(dfd)
return fmt.Errorf("failed to create TUN from FD: %w", err)
return err
}
tun.iface = iface
if m, err := iface.MTU(); err == nil {
@@ -111,8 +107,8 @@ func (tun *TunAdapter) setupAddress(addr string) error {
var err error
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
tun.log.Errorf("Create AF_SYSTEM socket failed: %v.", err)
return fmt.Errorf("failed to open AF_SYSTEM: %w", err)
tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err)
return err
}
var ar in6_aliasreq
@@ -150,13 +146,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
err = errno
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
return fmt.Errorf("failed to call SIOCAIFADDR_IN6: %w", err)
return err
}
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
err = errno
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
return fmt.Errorf("failed to call SIOCSIFMTU: %w", err)
return err
}
return err

View File

@@ -19,7 +19,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
}
iface, err := wgtun.CreateTUN(ifname, int(mtu))
if err != nil {
return fmt.Errorf("failed to create TUN: %w", err)
panic(err)
}
tun.iface = iface
if mtu, err := iface.MTU(); err == nil {
@@ -27,10 +27,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
} else {
tun.mtu = 0
}
if addr != "" {
return tun.setupAddress(addr)
}
return nil
return tun.setupAddress(addr)
}
// Configures the "utun" adapter from an existing file descriptor.
@@ -45,20 +42,20 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
func (tun *TunAdapter) setupAddress(addr string) error {
nladdr, err := netlink.ParseAddr(addr)
if err != nil {
return fmt.Errorf("couldn't parse address %q: %w", addr, err)
return err
}
nlintf, err := netlink.LinkByName(tun.Name())
if err != nil {
return fmt.Errorf("failed to find link by name: %w", err)
return err
}
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
return fmt.Errorf("failed to add address to link: %w", err)
return err
}
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
return fmt.Errorf("failed to set link MTU: %w", err)
return err
}
if err := netlink.LinkSetUp(nlintf); err != nil {
return fmt.Errorf("failed to bring link up: %w", err)
return err
}
// Friendly output
tun.log.Infof("Interface name: %s", tun.Name())

View File

@@ -16,7 +16,7 @@ import (
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
iface, err := wgtun.CreateTUN(ifname, mtu)
if err != nil {
return fmt.Errorf("failed to create TUN: %w", err)
panic(err)
}
tun.iface = iface
if mtu, err := iface.MTU(); err == nil {
@@ -24,10 +24,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
} else {
tun.mtu = 0
}
if addr != "" {
return tun.setupAddress(addr)
}
return nil
return tun.setupAddress(addr)
}
// Configures the "utun" adapter from an existing file descriptor.

View File

@@ -35,11 +35,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
return err
}
tun.iface = iface
if addr != "" {
if err = tun.setupAddress(addr); err != nil {
tun.log.Errorln("Failed to set up TUN address:", err)
return err
}
if err = tun.setupAddress(addr); err != nil {
tun.log.Errorln("Failed to set up TUN address:", err)
return err
}
if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil {
tun.log.Errorln("Failed to set up TUN MTU:", err)