From 7068160b200d1d074f50f986edd3c1954fa96c73 Mon Sep 17 00:00:00 2001 From: Arano-kai Date: Thu, 14 Nov 2019 13:29:27 +0200 Subject: [PATCH 01/81] Systemd: move config generation to a separate unit - Modular unit composition: different tasks in separate units - Use systemd tool set to run checks - Avoid using inline shell in unit --- contrib/systemd/yggdrasil-default-config.service | 13 +++++++++++++ contrib/systemd/yggdrasil.service | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 contrib/systemd/yggdrasil-default-config.service diff --git a/contrib/systemd/yggdrasil-default-config.service b/contrib/systemd/yggdrasil-default-config.service new file mode 100644 index 00000000..e9fe45be --- /dev/null +++ b/contrib/systemd/yggdrasil-default-config.service @@ -0,0 +1,13 @@ +[Unit] +Description=yggdrasil default config generator +ConditionPathExists=|!/etc/yggdrasil.conf +ConditionFileNotEmpty=|!/etc/yggdrasil.conf +Wants=local-fs.target +After=local-fs.target + +[Service] +Type=oneshot +Group=yggdrasil +StandardOutput=file:/etc/yggdrasil.conf +ExecStart=/usr/bin/yggdrasil -genconf +ExecStartPost=/usr/bin/chmod 0640 /etc/yggdrasil.conf diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index b48ff78b..075c0bd1 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -1,7 +1,9 @@ [Unit] Description=yggdrasil Wants=network.target +Wants=yggdrasil-default-config.service After=network.target +After=yggdrasil-default-config.service [Service] Group=yggdrasil @@ -10,11 +12,6 @@ ProtectSystem=true SyslogIdentifier=yggdrasil CapabilityBoundSet=CAP_NET_ADMIN ExecStartPre=+-/sbin/modprobe tun -ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ - then umask 077; \ - yggdrasil -genconf > /etc/yggdrasil.conf; \ - echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \ - fi" ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf ExecReload=/bin/kill -HUP $MAINPID Restart=always From f5517acc81911cf4477d939fe91fff97ecae3f56 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 16:43:50 +0000 Subject: [PATCH 02/81] Drop Water, use Wireguard tun library, drop TAP support --- go.mod | 1 + go.sum | 5 + src/config/config.go | 2 - src/defaults/defaults.go | 7 +- src/defaults/defaults_darwin.go | 7 +- src/defaults/defaults_freebsd.go | 7 +- src/defaults/defaults_linux.go | 7 +- src/defaults/defaults_netbsd.go | 7 +- src/defaults/defaults_openbsd.go | 7 +- src/defaults/defaults_other.go | 7 +- src/defaults/defaults_windows.go | 7 +- src/tuntap/admin.go | 5 +- src/tuntap/icmpv6.go | 297 +------------------------------ src/tuntap/iface.go | 128 +++---------- src/tuntap/tun.go | 90 ++++------ src/tuntap/tun_bsd.go | 36 ++-- src/tuntap/tun_darwin.go | 21 ++- src/tuntap/tun_linux.go | 32 ++-- src/tuntap/tun_other.go | 27 ++- src/tuntap/tun_windows.go | 62 +------ 20 files changed, 146 insertions(+), 616 deletions(-) diff --git a/go.mod b/go.mod index a45600a3..417e6fa3 100644 --- a/go.mod +++ b/go.mod @@ -18,4 +18,5 @@ require ( golang.org/x/net v0.0.0-20191021144547-ec77196f6094 golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb golang.org/x/text v0.3.2 + golang.zx2c4.com/wireguard v0.0.20191012 ) diff --git a/go.sum b/go.sum index ecfa4626..222728a1 100644 --- a/go.sum +++ b/go.sum @@ -21,16 +21,21 @@ github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmF github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8 h1:YY9Pg2BEp0jeUVU60svTOaDr+fs1ySC9RbdC1Qc6wOw= github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8/go.mod h1:R0SBCsugm+Sf1katgTb2t7GXMm+nRIv43tM4VDZbaOs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8= golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb h1:ZxSglHghKPYD8WDeRUzRJrUJtDF0PxsTUSxyqr9/5BI= golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.zx2c4.com/wireguard v0.0.20191012 h1:sdX+y3hrHkW8KJkjY7ZgzpT5Tqo8XnBkH55U1klphko= +golang.zx2c4.com/wireguard v0.0.20191012/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= diff --git a/src/config/config.go b/src/config/config.go index ac88bfc5..bacf9003 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -71,7 +71,6 @@ type NodeConfig struct { SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` - IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."` IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` 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."` @@ -127,7 +126,6 @@ func GenerateConfig() *NodeConfig { cfg.MulticastInterfaces = defaults.GetDefaults().DefaultMulticastInterfaces cfg.IfName = defaults.GetDefaults().DefaultIfName cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU - cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode cfg.SessionFirewall.Enable = false cfg.SessionFirewall.AllowFromDirect = true cfg.SessionFirewall.AllowFromRemote = true diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index a5784198..ceb58ba8 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -14,8 +14,7 @@ type platformDefaultParameters struct { DefaultMulticastInterfaces []string // TUN/TAP - MaximumIfMTU int - DefaultIfMTU int - DefaultIfName string - DefaultIfTAPMode bool + MaximumIfMTU int + DefaultIfMTU int + DefaultIfName string } diff --git a/src/defaults/defaults_darwin.go b/src/defaults/defaults_darwin.go index 47683bf9..9fbe6d8e 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/defaults/defaults_darwin.go @@ -19,9 +19,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "auto", } } diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index 0e523483..d7dc43b9 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 32767, - DefaultIfMTU: 32767, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, + MaximumIfMTU: 32767, + DefaultIfMTU: 32767, + DefaultIfName: "/dev/tap0", } } diff --git a/src/defaults/defaults_linux.go b/src/defaults/defaults_linux.go index b0aaf855..5f3f12a8 100644 --- a/src/defaults/defaults_linux.go +++ b/src/defaults/defaults_linux.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "auto", } } diff --git a/src/defaults/defaults_netbsd.go b/src/defaults/defaults_netbsd.go index 52a487b7..bc9d7a39 100644 --- a/src/defaults/defaults_netbsd.go +++ b/src/defaults/defaults_netbsd.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 9000, - DefaultIfMTU: 9000, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, + MaximumIfMTU: 9000, + DefaultIfMTU: 9000, + DefaultIfName: "/dev/tun0", } } diff --git a/src/defaults/defaults_openbsd.go b/src/defaults/defaults_openbsd.go index d44e5714..20741ee8 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/defaults/defaults_openbsd.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 16384, - DefaultIfMTU: 16384, - DefaultIfName: "/dev/tap0", - DefaultIfTAPMode: true, + MaximumIfMTU: 16384, + DefaultIfMTU: 16384, + DefaultIfName: "/dev/tun0", } } diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index 0ba825c5..763a7f89 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "none", - DefaultIfTAPMode: false, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "none", } } diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index 6d53225a..ade833d4 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -18,9 +18,8 @@ func GetDefaults() platformDefaultParameters { }, // TUN/TAP - MaximumIfMTU: 65535, - DefaultIfMTU: 65535, - DefaultIfName: "auto", - DefaultIfTAPMode: true, + MaximumIfMTU: 65535, + DefaultIfMTU: 65535, + DefaultIfName: "auto", } } diff --git a/src/tuntap/admin.go b/src/tuntap/admin.go index 778c03ae..c7fc20b0 100644 --- a/src/tuntap/admin.go +++ b/src/tuntap/admin.go @@ -19,9 +19,8 @@ func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) { }() return admin.Info{ - t.iface.Name(): admin.Info{ - "tap_mode": t.iface.IsTAP(), - "mtu": t.mtu, + t.Name(): admin.Info{ + "mtu": t.mtu, }, }, nil }) diff --git a/src/tuntap/icmpv6.go b/src/tuntap/icmpv6.go index e601acb5..67c10e54 100644 --- a/src/tuntap/icmpv6.go +++ b/src/tuntap/icmpv6.go @@ -11,32 +11,16 @@ package tuntap import ( "encoding/binary" - "errors" "net" - "sync" - "time" "golang.org/x/net/icmp" "golang.org/x/net/ipv6" - - "github.com/yggdrasil-network/yggdrasil-go/src/address" ) const len_ETHER = 14 type ICMPv6 struct { - tun *TunAdapter - mylladdr net.IP - mymac net.HardwareAddr - peermacs map[address.Address]neighbor - peermacsmutex sync.RWMutex -} - -type neighbor struct { - mac net.HardwareAddr - learned bool - lastadvertisement time.Time - lastsolicitation time.Time + tun *TunAdapter } // Marshal returns the binary encoding of h. @@ -61,182 +45,6 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { // addresses. func (i *ICMPv6) Init(t *TunAdapter) { i.tun = t - i.peermacsmutex.Lock() - i.peermacs = make(map[address.Address]neighbor) - i.peermacsmutex.Unlock() - - // Our MAC address and link-local address - i.mymac = net.HardwareAddr{ - 0x02, 0x00, 0x00, 0x00, 0x00, 0x02} - i.mylladdr = net.IP{ - 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE} - copy(i.mymac[:], i.tun.addr[:]) - copy(i.mylladdr[9:], i.tun.addr[1:]) -} - -// Parses an incoming ICMPv6 packet. The packet provided may be either an -// ethernet frame containing an IP packet, or the IP packet alone. This is -// determined by whether the TUN/TAP adapter is running in TUN (layer 3) or -// TAP (layer 2) mode. Returns an error condition which is nil if the ICMPv6 -// module handled the packet or contains the error if not. -func (i *ICMPv6) ParsePacket(datain []byte) error { - var response []byte - var err error - - // Parse the frame/packet - if i.tun.IsTAP() { - response, err = i.UnmarshalPacketL2(datain) - } else { - response, err = i.UnmarshalPacket(datain, nil) - } - - if err != nil { - return err - } - - // Write the packet to TUN/TAP - i.tun.iface.Write(response) - return nil -} - -// Unwraps the ethernet headers of an incoming ICMPv6 packet and hands off -// the IP packet to the ParsePacket function for further processing. -// A response buffer is also created for the response message, also complete -// with ethernet headers. -func (i *ICMPv6) UnmarshalPacketL2(datain []byte) ([]byte, error) { - // Ignore non-IPv6 frames - if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) { - return nil, errors.New("Ignoring non-IPv6 frame") - } - - // Hand over to ParsePacket to interpret the IPv6 packet - mac := datain[6:12] - ipv6packet, err := i.UnmarshalPacket(datain[len_ETHER:], &mac) - if err != nil { - return nil, err - } - - // Create the response buffer - dataout := make([]byte, len_ETHER+ipv6.HeaderLen+32) - - // Populate the response ethernet headers - copy(dataout[:6], datain[6:12]) - copy(dataout[6:12], i.mymac[:]) - binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) - - // Copy the returned packet to our response ethernet frame - copy(dataout[len_ETHER:], ipv6packet) - return dataout, nil -} - -// Unwraps the IP headers of an incoming IPv6 packet and performs various -// sanity checks on the packet - i.e. is the packet an ICMPv6 packet, does the -// ICMPv6 message match a known expected type. The relevant handler function -// is then called and a response packet may be returned. -func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) { - // Parse the IPv6 packet headers - ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen]) - if err != nil { - return nil, err - } - - // Check if the packet is IPv6 - if ipv6Header.Version != ipv6.Version { - return nil, errors.New("Ignoring non-IPv6 packet") - } - - // Check if the packet is ICMPv6 - if ipv6Header.NextHeader != 58 { - return nil, errors.New("Ignoring non-ICMPv6 packet") - } - - // Parse the ICMPv6 message contents - icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:]) - if err != nil { - return nil, err - } - - // Check for a supported message type - switch icmpv6Header.Type { - case ipv6.ICMPTypeNeighborSolicitation: - if !i.tun.IsTAP() { - return nil, errors.New("Ignoring Neighbor Solicitation in TUN mode") - } - response, err := i.HandleNDP(datain[ipv6.HeaderLen:]) - if err == nil { - // Create our ICMPv6 response - responsePacket, err := CreateICMPv6( - ipv6Header.Src, i.mylladdr, - ipv6.ICMPTypeNeighborAdvertisement, 0, - &icmp.DefaultMessageBody{Data: response}) - if err != nil { - return nil, err - } - // Send it back - return responsePacket, nil - } else { - return nil, err - } - case ipv6.ICMPTypeNeighborAdvertisement: - if !i.tun.IsTAP() { - return nil, errors.New("Ignoring Neighbor Advertisement in TUN mode") - } - if datamac != nil { - var addr address.Address - var target address.Address - mac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - copy(addr[:], ipv6Header.Src[:]) - copy(target[:], datain[48:64]) - copy(mac[:], (*datamac)[:]) - i.peermacsmutex.Lock() - neighbor := i.peermacs[target] - neighbor.mac = mac - neighbor.learned = true - neighbor.lastadvertisement = time.Now() - i.peermacs[target] = neighbor - i.peermacsmutex.Unlock() - i.tun.log.Debugln("Learned peer MAC", mac.String(), "for", net.IP(target[:]).String()) - /* - i.tun.log.Debugln("Peer MAC table:") - i.peermacsmutex.RLock() - for t, n := range i.peermacs { - if n.learned { - i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "has MAC", n.mac.String()) - } else { - i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "is not learned yet") - } - } - i.peermacsmutex.RUnlock() - */ - } - return nil, errors.New("No response needed") - } - - return nil, errors.New("ICMPv6 type not matched") -} - -// Creates an ICMPv6 packet based on the given icmp.MessageBody and other -// parameters, complete with ethernet and IP headers, which can be written -// directly to a TAP adapter. -func (i *ICMPv6) CreateICMPv6L2(dstmac net.HardwareAddr, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { - // Pass through to CreateICMPv6 - ipv6packet, err := CreateICMPv6(dst, src, mtype, mcode, mbody) - if err != nil { - return nil, err - } - - // Create the response buffer - dataout := make([]byte, len_ETHER+len(ipv6packet)) - - // Populate the response ethernet headers - copy(dataout[:6], dstmac[:6]) - copy(dataout[6:12], i.mymac[:]) - binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) - - // Copy the returned packet to our response ethernet frame - copy(dataout[len_ETHER:], ipv6packet) - return dataout, nil } // Creates an ICMPv6 packet based on the given icmp.MessageBody and other @@ -281,106 +89,3 @@ func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody // Send it back return responsePacket, nil } - -func (i *ICMPv6) Solicit(addr address.Address) { - retries := 5 - for retries > 0 { - retries-- - i.peermacsmutex.RLock() - if n, ok := i.peermacs[addr]; ok && n.learned { - i.tun.log.Debugln("MAC learned for", net.IP(addr[:]).String()) - i.peermacsmutex.RUnlock() - return - } - i.peermacsmutex.RUnlock() - i.tun.log.Debugln("Sending neighbor solicitation for", net.IP(addr[:]).String()) - i.peermacsmutex.Lock() - if n, ok := i.peermacs[addr]; !ok { - i.peermacs[addr] = neighbor{ - lastsolicitation: time.Now(), - } - } else { - n.lastsolicitation = time.Now() - } - i.peermacsmutex.Unlock() - request, err := i.createNDPL2(addr) - if err != nil { - panic(err) - } - if _, err := i.tun.iface.Write(request); err != nil { - panic(err) - } - i.tun.log.Debugln("Sent neighbor solicitation for", net.IP(addr[:]).String()) - time.Sleep(time.Second) - } -} - -func (i *ICMPv6) getNeighbor(addr address.Address) (neighbor, bool) { - i.peermacsmutex.RLock() - defer i.peermacsmutex.RUnlock() - - n, ok := i.peermacs[addr] - return n, ok -} - -func (i *ICMPv6) createNDPL2(dst address.Address) ([]byte, error) { - // Create the ND payload - var payload [28]byte - copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00}) // Flags - copy(payload[4:20], dst[:]) // Destination - copy(payload[20:22], []byte{0x01, 0x01}) // Type & length - copy(payload[22:28], i.mymac[:6]) // Link layer address - - // Create the ICMPv6 solicited-node address - var dstaddr address.Address - copy(dstaddr[:13], []byte{ - 0xFF, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xFF}) - copy(dstaddr[13:], dst[13:16]) - - // Create the multicast MAC - dstmac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - copy(dstmac[:2], []byte{0x33, 0x33}) - copy(dstmac[2:6], dstaddr[12:16]) - - // Create the ND request - requestPacket, err := i.CreateICMPv6L2( - dstmac, dstaddr[:], i.mylladdr, - ipv6.ICMPTypeNeighborSolicitation, 0, - &icmp.DefaultMessageBody{Data: payload[:]}) - if err != nil { - return nil, err - } - - return requestPacket, nil -} - -// Generates a response to an NDP discovery packet. This is effectively called -// when the host operating system generates an NDP request for any address in -// the fd00::/8 range, so that the operating system knows to route that traffic -// to the Yggdrasil TAP adapter. -func (i *ICMPv6) HandleNDP(in []byte) ([]byte, error) { - // Ignore NDP requests for anything outside of fd00::/8 - var source address.Address - copy(source[:], in[8:]) - var snet address.Subnet - copy(snet[:], in[8:]) - switch { - case source.IsValid(): - case snet.IsValid(): - default: - return nil, errors.New("Not an NDP for 0200::/7") - } - - // Create our NDP message body response - body := make([]byte, 28) - binary.BigEndian.PutUint32(body[:4], uint32(0x40000000)) // Flags - copy(body[4:20], in[8:24]) // Target address - body[20] = uint8(2) // Type: Target link-layer address - body[21] = uint8(1) // Length: 1x address (8 bytes) - copy(body[22:28], i.mymac[:6]) - - // Send it back - return body, nil -} diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 3d788b1a..9cb5e370 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -1,11 +1,6 @@ package tuntap import ( - "bytes" - "net" - "time" - - "github.com/songgao/packets/ethernet" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" @@ -14,6 +9,8 @@ import ( "github.com/Arceliar/phony" ) +const TUN_OFFSET_BYTES = 4 + type tunWriter struct { phony.Inbox tun *TunAdapter @@ -25,7 +22,8 @@ func (w *tunWriter) writeFrom(from phony.Actor, b []byte) { }) } -// write is pretty loose with the memory safety rules, e.g. it assumes it can read w.tun.iface.IsTap() safely +// write is pretty loose with the memory safety rules, e.g. it assumes it can +// read w.tun.iface.IsTap() safely func (w *tunWriter) _write(b []byte) { var written int var err error @@ -33,72 +31,17 @@ func (w *tunWriter) _write(b []byte) { if n == 0 { return } - if w.tun.iface.IsTAP() { - sendndp := func(dstAddr address.Address) { - neigh, known := w.tun.icmpv6.getNeighbor(dstAddr) - known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) - if !known { - w.tun.icmpv6.Solicit(dstAddr) - } - } - peermac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - var dstAddr address.Address - var peerknown bool - if b[0]&0xf0 == 0x40 { - dstAddr = w.tun.addr - } else if b[0]&0xf0 == 0x60 { - if !bytes.Equal(w.tun.addr[:16], dstAddr[:16]) && !bytes.Equal(w.tun.subnet[:8], dstAddr[:8]) { - dstAddr = w.tun.addr - } - } - if neighbor, ok := w.tun.icmpv6.getNeighbor(dstAddr); ok && neighbor.learned { - // If we've learned the MAC of a 300::/7 address, for example, or a CKR - // address, use the MAC address of that - peermac = neighbor.mac - peerknown = true - } else if neighbor, ok := w.tun.icmpv6.getNeighbor(w.tun.addr); ok && neighbor.learned { - // Otherwise send directly to the MAC address of the host if that's - // known instead - peermac = neighbor.mac - peerknown = true - } else { - // Nothing has been discovered, try to discover the destination - sendndp(w.tun.addr) - } - if peerknown { - var proto ethernet.Ethertype - switch { - case b[0]&0xf0 == 0x60: - proto = ethernet.IPv6 - case b[0]&0xf0 == 0x40: - proto = ethernet.IPv4 - } - var frame ethernet.Frame - frame.Prepare( - peermac[:6], // Destination MAC address - w.tun.icmpv6.mymac[:6], // Source MAC address - ethernet.NotTagged, // VLAN tagging - proto, // Ethertype - len(b)) // Payload length - copy(frame[tun_ETHER_HEADER_LENGTH:], b[:n]) - n += tun_ETHER_HEADER_LENGTH - written, err = w.tun.iface.Write(frame[:n]) - } else { - w.tun.log.Errorln("TUN/TAP iface write error: no peer MAC known for", net.IP(dstAddr[:]).String(), "- dropping packet") - } - } else { - written, err = w.tun.iface.Write(b[:n]) - util.PutBytes(b) - } + written, err = w.tun.iface.Write(append([]byte{0, 0, 0, 0}, b[:n]...), TUN_OFFSET_BYTES) + util.PutBytes(b) if err != nil { w.tun.Act(w, func() { if !w.tun.isOpen { - w.tun.log.Errorln("TUN/TAP iface write error:", err) + w.tun.log.Errorln("TUN iface write error:", err) } }) } - if written != n { - w.tun.log.Errorln("TUN/TAP iface write mismatch:", written, "bytes written vs", n, "bytes given") + if written-TUN_OFFSET_BYTES != n { + w.tun.log.Errorln("TUN iface write mismatch:", written-TUN_OFFSET_BYTES, "bytes written vs", n, "bytes given") } } @@ -109,13 +52,18 @@ type tunReader struct { func (r *tunReader) _read() { // Get a slice to store the packet in - recvd := util.ResizeBytes(util.GetBytes(), 65535+tun_ETHER_HEADER_LENGTH) - // Wait for a packet to be delivered to us through the TUN/TAP adapter - n, err := r.tun.iface.Read(recvd) - if n <= 0 { + recvd := util.ResizeBytes(util.GetBytes(), r.tun.mtu+TUN_OFFSET_BYTES) + // Wait for a packet to be delivered to us through the TUN adapter + n, err := r.tun.iface.Read(recvd, TUN_OFFSET_BYTES) + if n <= TUN_OFFSET_BYTES || err != nil { + r.tun.log.Errorln("Error reading TUN:", err) + err = r.tun.iface.Flush() + if err != nil { + r.tun.log.Errorln("Unable to flush packets:", err) + } util.PutBytes(recvd) } else { - r.tun.handlePacketFrom(r, recvd[:n], err) + r.tun.handlePacketFrom(r, recvd[TUN_OFFSET_BYTES:n+TUN_OFFSET_BYTES], err) } if err == nil { // Now read again @@ -132,43 +80,17 @@ func (tun *TunAdapter) handlePacketFrom(from phony.Actor, packet []byte, err err // does the work of reading a packet and sending it to the correct tunConn func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if err != nil { - tun.log.Errorln("TUN/TAP iface read error:", err) + tun.log.Errorln("TUN iface read error:", err) return } - // If it's a TAP adapter, update the buffer slice so that we no longer - // include the ethernet headers - offset := 0 - if tun.iface.IsTAP() { - // Set our offset to beyond the ethernet headers - offset = tun_ETHER_HEADER_LENGTH - // Check first of all that we can go beyond the ethernet headers - if len(recvd) <= offset { - return - } - } // Offset the buffer from now on so that we can ignore ethernet frames if // they are present - bs := recvd[offset:] + bs := recvd[:] // Check if the packet is long enough to detect if it's an ICMP packet or not if len(bs) < 7 { - tun.log.Traceln("TUN/TAP iface read undersized unknown packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized unknown packet, length:", len(bs)) return } - // If we detect an ICMP packet then hand it to the ICMPv6 module - if bs[6] == 58 { - // Found an ICMPv6 packet - we need to make sure to give ICMPv6 the full - // Ethernet frame rather than just the IPv6 packet as this is needed for - // NDP to work correctly - if err := tun.icmpv6.ParsePacket(recvd); err == nil { - // We acted on the packet in the ICMPv6 module so don't forward or do - // anything else with it - return - } - } - if offset != 0 { - // Shift forward to avoid leaking bytes off the front of the slice when we eventually store it - bs = append(recvd[:0], bs...) - } // From the IP header, work out what our source and destination addresses // and node IDs are. We will need these in order to work out where to send // the packet @@ -181,7 +103,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if bs[0]&0xf0 == 0x60 { // Check if we have a fully-sized IPv6 header if len(bs) < 40 { - tun.log.Traceln("TUN/TAP iface read undersized ipv6 packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized ipv6 packet, length:", len(bs)) return } // Check the packet size @@ -195,7 +117,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { } else if bs[0]&0xf0 == 0x40 { // Check if we have a fully-sized IPv4 header if len(bs) < 20 { - tun.log.Traceln("TUN/TAP iface read undersized ipv4 packet, length:", len(bs)) + tun.log.Traceln("TUN iface read undersized ipv4 packet, length:", len(bs)) return } // Check the packet size @@ -267,7 +189,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil { // Something went wrong when storing the connection, typically that // something already exists for this address or subnet - tun.log.Debugln("TUN/TAP iface wrap:", err) + tun.log.Debugln("TUN iface wrap:", err) return } for _, packet := range packets { diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 1e994ea5..807ce7fb 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -18,7 +18,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/water" + "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -28,12 +28,11 @@ import ( ) const tun_IPv6_HEADER_LENGTH = 40 -const tun_ETHER_HEADER_LENGTH = 14 -// TunAdapter represents a running TUN/TAP interface and extends the -// yggdrasil.Adapter type. In order to use the TUN/TAP adapter with Yggdrasil, -// you should pass this object to the yggdrasil.SetRouterAdapter() function -// before calling yggdrasil.Start(). +// TunAdapter represents a running TUN interface and extends the +// yggdrasil.Adapter type. In order to use the TUN adapter with Yggdrasil, you +// should pass this object to the yggdrasil.SetRouterAdapter() function before +// calling yggdrasil.Start(). type TunAdapter struct { core *yggdrasil.Core writer tunWriter @@ -48,7 +47,7 @@ type TunAdapter struct { ckr cryptokey icmpv6 ICMPv6 mtu int - iface *water.Interface + iface tun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below //mutex sync.RWMutex // Protects the below addrToConn map[address.Address]*tunConn @@ -64,12 +63,12 @@ type TunOptions struct { // Gets the maximum supported MTU for the platform based on the defaults in // defaults.GetDefaults(). -func getSupportedMTU(mtu int, istapmode bool) int { +func getSupportedMTU(mtu int) int { if mtu < 1280 { return 1280 } - if mtu > MaximumMTU(istapmode) { - return MaximumMTU(istapmode) + if mtu > MaximumMTU() { + return MaximumMTU() } return mtu } @@ -77,55 +76,38 @@ func getSupportedMTU(mtu int, istapmode bool) int { // Name returns the name of the adapter, e.g. "tun0". On Windows, this may // return a canonical adapter name instead. func (tun *TunAdapter) Name() string { - return tun.iface.Name() + if name, err := tun.iface.Name(); err == nil { + return name + } + return "" } // MTU gets the adapter's MTU. This can range between 1280 and 65535, although // the maximum value is determined by your platform. The returned value will // never exceed that of MaximumMTU(). func (tun *TunAdapter) MTU() int { - return getSupportedMTU(tun.mtu, tun.IsTAP()) + return getSupportedMTU(tun.mtu) } -// IsTAP returns true if the adapter is a TAP adapter (Layer 2) or false if it -// is a TUN adapter (Layer 3). -func (tun *TunAdapter) IsTAP() bool { - return tun.iface.IsTAP() -} - -// DefaultName gets the default TUN/TAP interface name for your platform. +// DefaultName gets the default TUN interface name for your platform. func DefaultName() string { return defaults.GetDefaults().DefaultIfName } -// DefaultMTU gets the default TUN/TAP interface MTU for your platform. This can +// DefaultMTU gets the default TUN interface MTU for your platform. This can // be as high as MaximumMTU(), depending on platform, but is never lower than 1280. func DefaultMTU() int { - ehbytes := 0 - if DefaultIsTAP() { - ehbytes = tun_ETHER_HEADER_LENGTH - } - return defaults.GetDefaults().DefaultIfMTU - ehbytes + return defaults.GetDefaults().DefaultIfMTU } -// DefaultIsTAP returns true if the default adapter mode for the current -// platform is TAP (Layer 2) and returns false for TUN (Layer 3). -func DefaultIsTAP() bool { - return defaults.GetDefaults().DefaultIfTAPMode -} - -// MaximumMTU returns the maximum supported TUN/TAP interface MTU for your +// MaximumMTU returns the maximum supported TUN interface MTU for your // platform. This can be as high as 65535, depending on platform, but is never // lower than 1280. -func MaximumMTU(iftapmode bool) int { - ehbytes := 0 - if iftapmode { - ehbytes = tun_ETHER_HEADER_LENGTH - } - return defaults.GetDefaults().MaximumIfMTU - ehbytes +func MaximumMTU() int { + return defaults.GetDefaults().MaximumIfMTU } -// Init initialises the TUN/TAP module. You must have acquired a Listener from +// Init initialises the TUN module. You must have acquired a Listener from // the Yggdrasil core before this point and it must not be in use elsewhere. func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error { tunoptions, ok := options.(TunOptions) @@ -145,7 +127,7 @@ func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log return nil } -// Start the setup process for the TUN/TAP adapter. If successful, starts the +// Start the setup process for the TUN adapter. If successful, starts the // reader actor to handle packets on that interface. func (tun *TunAdapter) Start() error { var err error @@ -157,11 +139,11 @@ func (tun *TunAdapter) Start() error { func (tun *TunAdapter) _start() error { if tun.isOpen { - return errors.New("TUN/TAP module is already started") + return errors.New("TUN module is already started") } current := tun.config.GetCurrent() if tun.config == nil || tun.listener == nil || tun.dialer == nil { - return errors.New("no configuration available to TUN/TAP") + return errors.New("no configuration available to TUN") } var boxPub crypto.BoxPubKey boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey) @@ -174,23 +156,19 @@ func (tun *TunAdapter) _start() error { tun.subnet = *address.SubnetForNodeID(nodeID) addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) if current.IfName == "none" || current.IfName == "dummy" { - tun.log.Debugln("Not starting TUN/TAP as ifname is none or dummy") + tun.log.Debugln("Not starting TUN as ifname is none or dummy") return nil } - if err := tun.setup(current.IfName, current.IfTAPMode, addr, current.IfMTU); err != nil { + if err := tun.setup(current.IfName, addr, current.IfMTU); err != nil { return err } if tun.MTU() != current.IfMTU { - tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) + tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU()) } tun.core.SetMaximumSessionMTU(uint16(tun.MTU())) tun.isOpen = true go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader - tun.icmpv6.Init(tun) - if tun.IsTAP() { - go tun.icmpv6.Solicit(tun.addr) - } tun.ckr.init(tun) return nil } @@ -204,7 +182,7 @@ func (tun *TunAdapter) IsStarted() bool { return isOpen } -// Start the setup process for the TUN/TAP adapter. If successful, starts the +// Start the setup process for the TUN adapter. If successful, starts the // read/write goroutines to handle packets on that interface. func (tun *TunAdapter) Stop() error { var err error @@ -216,7 +194,7 @@ func (tun *TunAdapter) Stop() error { func (tun *TunAdapter) _stop() error { tun.isOpen = false - // by TUN/TAP, e.g. readers/writers, sessions + // by TUN, e.g. readers/writers, sessions if tun.iface != nil { // Just in case we failed to start up the iface for some reason, this can apparently happen on Windows tun.iface.Close() @@ -224,16 +202,16 @@ func (tun *TunAdapter) _stop() error { return nil } -// UpdateConfig updates the TUN/TAP module with the provided config.NodeConfig +// UpdateConfig updates the TUN module with the provided config.NodeConfig // and then signals the various module goroutines to reconfigure themselves if // needed. func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) { - tun.log.Debugln("Reloading TUN/TAP configuration...") + tun.log.Debugln("Reloading TUN configuration...") // Replace the active configuration with the supplied one tun.config.Replace(*config) - // If the MTU has changed in the TUN/TAP module then this is where we would + // If the MTU has changed in the TUN module then this is where we would // tell the router so that updated session pings can be sent. However, we // don't currently update the MTU of the adapter once it has been created so // this doesn't actually happen in the real world yet. @@ -248,14 +226,14 @@ func (tun *TunAdapter) handler() error { // Accept the incoming connection conn, err := tun.listener.Accept() if err != nil { - tun.log.Errorln("TUN/TAP connection accept error:", err) + tun.log.Errorln("TUN connection accept error:", err) return err } phony.Block(tun, func() { if _, err := tun._wrap(conn.(*yggdrasil.Conn)); err != nil { // Something went wrong when storing the connection, typically that // something already exists for this address or subnet - tun.log.Debugln("TUN/TAP handler wrap:", err) + tun.log.Debugln("TUN handler wrap:", err) } }) } diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 78a4adab..7ad89edc 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -1,4 +1,4 @@ -// +build openbsd freebsd netbsd +// +build openbsd freebsd package tuntap @@ -12,7 +12,7 @@ import ( "golang.org/x/sys/unix" - "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 @@ -72,34 +72,18 @@ type in6_ifreq_lifetime struct { ifru_addrlifetime in6_addrlifetime } -// Sets the IPv6 address of the utun adapter. On all BSD platforms (FreeBSD, -// OpenBSD, NetBSD) an attempt is made to set the adapter properties by using -// a system socket and making syscalls to the kernel. This is not refined though -// and often doesn't work (if at all), therefore if a call fails, it resorts -// to calling "ifconfig" instead. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if ifname[:4] == "auto" { - ifname = "/dev/tap0" - } - if len(ifname) < 9 { - panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX") - } - switch { - case iftapmode || ifname[:8] == "/dev/tap": - config = water.Config{DeviceType: water.TAP} - case !iftapmode || ifname[:8] == "/dev/tun": - panic("TUN mode is not currently supported on this platform, please use TAP instead") - default: - panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX") - } - config.Name = ifname - iface, err := water.New(config) +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index ab6f34e3..c9b8bceb 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -12,22 +12,21 @@ import ( "golang.org/x/sys/unix" - water "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) // Configures the "utun" adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - if iftapmode { - tun.log.Warnln("Warning: TAP mode is not supported on this platform, defaulting to TUN") - iftapmode = false - } - config := water.Config{DeviceType: water.TUN} - iface, err := water.New(config) +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } @@ -80,7 +79,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { } var ar in6_aliasreq - copy(ar.ifra_name[:], tun.iface.Name()) + copy(ar.ifra_name[:], tun.Name()) ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask)) b := make([]byte, 16) @@ -104,7 +103,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { ar.ifra_lifetime.ia6t_pltime = darwin_ND6_INFINITE_LIFETIME var ir ifreq - copy(ir.ifr_name[:], tun.iface.Name()) + copy(ir.ifr_name[:], tun.Name()) ir.ifru_mtu = uint32(tun.mtu) tun.log.Infof("Interface name: %s", ar.ifra_name) diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index b5918328..1129cfad 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -6,31 +6,21 @@ package tuntap import ( "github.com/vishvananda/netlink" - - water "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) -// Configures the TAP adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if iftapmode { - config = water.Config{DeviceType: water.TAP} - } else { - config = water.Config{DeviceType: water.TUN} - } - if ifname != "" && ifname != "auto" { - config.Name = ifname - } - iface, err := water.New(config) +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) - // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) - tun.log.Infof("Interface IPv6: %s", addr) - tun.log.Infof("Interface MTU: %d", tun.mtu) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } @@ -56,5 +46,9 @@ func (tun *TunAdapter) setupAddress(addr string) error { if err := netlink.LinkSetUp(nlintf); err != nil { return err } + // Friendly output + tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) return nil } diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index 7d4f0643..c5ff58fb 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -1,27 +1,26 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile +// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile package tuntap -import water "github.com/yggdrasil-network/water" - // This is to catch unsupported platforms // If your platform supports tun devices, you could try configuring it manually -// Creates the TUN/TAP adapter, if supported by the Water library. Note that -// no guarantees are made at this point on an unsupported platform. -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - var config water.Config - if iftapmode { - config = water.Config{DeviceType: water.TAP} - } else { - config = water.Config{DeviceType: water.TUN} - } - iface, err := water.New(config) +import ( + wgtun "golang.zx2c4.com/wireguard/tun" +) + +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 + } return tun.setupAddress(addr) } diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index d4fd1c3d..65417546 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -7,69 +7,23 @@ import ( "strings" "time" - water "github.com/yggdrasil-network/water" + wgtun "golang.zx2c4.com/wireguard/tun" ) // This is to catch Windows platforms -// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows -// we don't make use of a direct operating system API to do this - we instead -// delegate the hard work to "netsh". -func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { - if !iftapmode { - tun.log.Warnln("Warning: TUN mode is not supported on this platform, defaulting to TAP") - iftapmode = true - } - config := water.Config{DeviceType: water.TAP} - config.PlatformSpecificParams.ComponentID = "tap0901" - config.PlatformSpecificParams.Network = "169.254.0.1/32" - if ifname == "auto" { - config.PlatformSpecificParams.InterfaceName = "" - } else { - config.PlatformSpecificParams.InterfaceName = ifname - } - iface, err := water.New(config) - if err != nil { - return err - } - if iface.Name() == "" { - return errors.New("unable to find TAP adapter with component ID " + config.PlatformSpecificParams.ComponentID) - } - // Reset the adapter - this invalidates iface so we'll need to get a new one - cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err - } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect - // Bring the interface back up - cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err = cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err - } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect - // Get a new iface - iface, err = water.New(config) +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu, iftapmode) - err = tun.setupMTU(tun.mtu) - if err != nil { - panic(err) + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(mtu) + } else { + tun.mtu = 0 } - // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) - tun.log.Infof("Interface IPv6: %s", addr) - tun.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } From 235b64345e16d87ee173352aa44705a697911b89 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 18:34:43 +0000 Subject: [PATCH 03/81] Configure addresses and MTUs, fix bugs --- src/tuntap/iface.go | 4 +- src/tuntap/tun_windows.go | 237 +++++++++++++++++++++++++++++++------- 2 files changed, 197 insertions(+), 44 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 9cb5e370..fbb7c86d 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -40,8 +40,8 @@ func (w *tunWriter) _write(b []byte) { } }) } - if written-TUN_OFFSET_BYTES != n { - w.tun.log.Errorln("TUN iface write mismatch:", written-TUN_OFFSET_BYTES, "bytes written vs", n, "bytes given") + if written != n { + w.tun.log.Errorln("TUN iface write mismatch:", written, "bytes written vs", n, "bytes given") } } diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 65417546..8b4f92cf 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -1,70 +1,223 @@ package tuntap import ( + "bytes" "errors" - "fmt" - "os/exec" + "log" + "net" + "runtime" "strings" - "time" + "unsafe" + + "golang.org/x/sys/windows" wgtun "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) // This is to catch Windows platforms // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { - iface, err := wgtun.CreateTUN(ifname, mtu) - if err != nil { - panic(err) - } - tun.iface = iface - if mtu, err := iface.MTU(); err == nil { - tun.mtu = getSupportedMTU(mtu) - } else { - tun.mtu = 0 - } - return tun.setupAddress(addr) + var err error + err = doAsSystem(func() { + iface, err := wgtun.CreateTUN(ifname, mtu) + if err != nil { + panic(err) + } + tun.iface = iface + + if err := tun.setupAddress(addr); err != nil { + tun.log.Errorln("Failed to set up TUN address:", err) + } + if err := tun.setupMTU(getSupportedMTU(mtu)); err != nil { + tun.log.Errorln("Failed to set up TUN MTU:", err) + } + + if mtu, err = iface.MTU(); err == nil { + tun.mtu = mtu + } + }) + return err } // Sets the MTU of the TAP adapter. func (tun *TunAdapter) setupMTU(mtu int) error { - if tun.iface == nil || tun.iface.Name() == "" { - return errors.New("Can't configure MTU as TAP adapter is not present") + if tun.iface == nil || tun.Name() == "" { + return errors.New("Can't configure MTU as TUN adapter is not present") } - // Set MTU - cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface", - fmt.Sprintf("interface=%s", tun.iface.Name()), - fmt.Sprintf("mtu=%d", mtu), - "store=active") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err + if intf, ok := tun.iface.(*wgtun.NativeTun); ok { + luid := winipcfg.LUID(intf.LUID()) + ipfamily, err := luid.IPInterface(windows.AF_INET6) + if err != nil { + return err + } + + ipfamily.NLMTU = uint32(mtu) + intf.ForceMTU(int(ipfamily.NLMTU)) + ipfamily.UseAutomaticMetric = false + ipfamily.Metric = 0 + ipfamily.DadTransmits = 0 + ipfamily.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled + + if err := ipfamily.Set(); err != nil { + return err + } } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect + return nil } // Sets the IPv6 address of the TAP adapter. func (tun *TunAdapter) setupAddress(addr string) error { - if tun.iface == nil || tun.iface.Name() == "" { - return errors.New("Can't configure IPv6 address as TAP adapter is not present") + if tun.iface == nil || tun.Name() == "" { + return errors.New("Can't configure IPv6 address as TUN adapter is not present") } - // Set address - cmd := exec.Command("netsh", "interface", "ipv6", "add", "address", - fmt.Sprintf("interface=%s", tun.iface.Name()), - fmt.Sprintf("addr=%s", addr), - "store=active") - tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorln("Windows netsh failed:", err) - tun.log.Traceln(string(output)) - return err + if intf, ok := tun.iface.(*wgtun.NativeTun); ok { + if ipaddr, ipnet, err := net.ParseCIDR(addr); err == nil { + luid := winipcfg.LUID(intf.LUID()) + addresses := append([]net.IPNet{}, net.IPNet{ + IP: ipaddr, + Mask: ipnet.Mask, + }) + + err := luid.SetIPAddressesForFamily(windows.AF_INET6, addresses) + if err == windows.ERROR_OBJECT_ALREADY_EXISTS { + cleanupAddressesOnDisconnectedInterfaces(windows.AF_INET6, addresses) + err = luid.SetIPAddressesForFamily(windows.AF_INET6, addresses) + } + if err != nil { + return err + } + } else { + return err + } + } else { + return errors.New("unable to get NativeTUN") } - time.Sleep(time.Second) // FIXME artifical delay to give netsh time to take effect return nil } + +/* + * doAsSystem + * SPDX-License-Identifier: LGPL-3.0 + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + */ +func doAsSystem(f func()) error { + runtime.LockOSThread() + defer func() { + windows.RevertToSelf() + runtime.UnlockOSThread() + }() + privileges := windows.Tokenprivileges{ + PrivilegeCount: 1, + Privileges: [1]windows.LUIDAndAttributes{ + { + Attributes: windows.SE_PRIVILEGE_ENABLED, + }, + }, + } + err := windows.LookupPrivilegeValue(nil, windows.StringToUTF16Ptr("SeDebugPrivilege"), &privileges.Privileges[0].Luid) + if err != nil { + return err + } + err = windows.ImpersonateSelf(windows.SecurityImpersonation) + if err != nil { + return err + } + var threadToken windows.Token + err = windows.OpenThreadToken(windows.CurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES, false, &threadToken) + if err != nil { + return err + } + defer threadToken.Close() + err = windows.AdjustTokenPrivileges(threadToken, false, &privileges, uint32(unsafe.Sizeof(privileges)), nil, nil) + if err != nil { + return err + } + + processes, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0) + if err != nil { + return err + } + defer windows.CloseHandle(processes) + + processEntry := windows.ProcessEntry32{Size: uint32(unsafe.Sizeof(windows.ProcessEntry32{}))} + pid := uint32(0) + for err = windows.Process32First(processes, &processEntry); err == nil; err = windows.Process32Next(processes, &processEntry) { + if strings.ToLower(windows.UTF16ToString(processEntry.ExeFile[:])) == "winlogon.exe" { + pid = processEntry.ProcessID + break + } + } + if pid == 0 { + return errors.New("unable to find winlogon.exe process") + } + + winlogonProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, pid) + if err != nil { + return err + } + defer windows.CloseHandle(winlogonProcess) + var winlogonToken windows.Token + err = windows.OpenProcessToken(winlogonProcess, windows.TOKEN_IMPERSONATE|windows.TOKEN_DUPLICATE, &winlogonToken) + if err != nil { + return err + } + defer winlogonToken.Close() + var duplicatedToken windows.Token + err = windows.DuplicateTokenEx(winlogonToken, 0, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken) + if err != nil { + return err + } + defer duplicatedToken.Close() + err = windows.SetThreadToken(nil, duplicatedToken) + if err != nil { + return err + } + f() + return nil +} + +/* + * cleanupAddressesOnDisconnectedInterfaces + * SPDX-License-Identifier: MIT + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ +func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) { + if len(addresses) == 0 { + return + } + includedInAddresses := func(a net.IPNet) bool { + // TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! + for _, addr := range addresses { + ip := addr.IP + if ip4 := ip.To4(); ip4 != nil { + ip = ip4 + } + mA, _ := addr.Mask.Size() + mB, _ := a.Mask.Size() + if bytes.Equal(ip, a.IP) && mA == mB { + return true + } + } + return false + } + interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault) + if err != nil { + return + } + for _, iface := range interfaces { + if iface.OperStatus == winipcfg.IfOperStatusUp { + continue + } + for address := iface.FirstUnicastAddress; address != nil; address = address.Next { + ip := address.Address.IP() + ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} + if includedInAddresses(ipnet) { + log.Printf("Cleaning up stale address %s from interface ā€˜%sā€™", ipnet.String(), iface.FriendlyName()) + iface.LUID.DeleteIPAddress(ipnet) + } + } + } +} From b27ada91910f60baa39187b00b87f9891c267eb9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 18:39:27 +0000 Subject: [PATCH 04/81] Fix bad Name() calls --- src/tuntap/tun_bsd.go | 10 +++++----- src/tuntap/tun_linux.go | 4 ++-- src/tuntap/tun_other.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 7ad89edc..219e3485 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -98,13 +98,13 @@ func (tun *TunAdapter) setupAddress(addr string) error { } // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface name: %s", tun.Name()) tun.log.Infof("Interface IPv6: %s", addr) tun.log.Infof("Interface MTU: %d", tun.mtu) // Create the MTU request var ir in6_ifreq_mtu - copy(ir.ifr_name[:], tun.iface.Name()) + copy(ir.ifr_name[:], tun.Name()) ir.ifru_mtu = int(tun.mtu) // Set the MTU @@ -113,7 +113,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) // Fall back to ifconfig to set the MTU - cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) + cmd := exec.Command("ifconfig", tun.Name(), "mtu", string(tun.mtu)) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { @@ -125,7 +125,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { // Create the address request // FIXME: I don't work! var ar in6_ifreq_addr - copy(ar.ifr_name[:], tun.iface.Name()) + copy(ar.ifr_name[:], tun.Name()) ar.ifru_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifru_addr)) ar.ifru_addr.sin6_family = unix.AF_INET6 parts := strings.Split(strings.Split(addr, "/")[0], ":") @@ -142,7 +142,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) // Fall back to ifconfig to set the address - cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) + cmd := exec.Command("ifconfig", tun.Name(), "inet6", addr) tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 1129cfad..7935e7cf 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -33,7 +33,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { if err != nil { return err } - nlintf, err := netlink.LinkByName(tun.iface.Name()) + nlintf, err := netlink.LinkByName(tun.Name()) if err != nil { return err } @@ -47,7 +47,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { return err } // Friendly output - tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface name: %s", tun.Name()) tun.log.Infof("Interface IPv6: %s", addr) tun.log.Infof("Interface MTU: %d", tun.mtu) return nil diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index c5ff58fb..8a27f57b 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -27,6 +27,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { // We don't know how to set the IPv6 address on an unknown platform, therefore // write about it to stdout and don't try to do anything further. func (tun *TunAdapter) setupAddress(addr string) error { - tun.log.Warnln("Warning: Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.log.Warnln("Warning: Platform not supported, you must set the address of", tun.Name(), "to", addr) return nil } From 15726fe90d9f1cf71df30eaba3e7219f2ad4c1a4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 18:52:12 +0000 Subject: [PATCH 05/81] Don't build for NetBSD (not supported by the TUN package right now) --- .circleci/config.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17cbfade..3e223e55 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,13 +174,6 @@ jobs: GOOS=freebsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-amd64; GOOS=freebsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-i386; - - run: - name: Build for NetBSD - command: | - rm -f {yggdrasil,yggdrasilctl} - GOOS=netbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-netbsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-netbsd-amd64; - GOOS=netbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-netbsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-netbsd-i386; - - run: name: Build for Windows command: | From 7d00206f4bde6de34dfd84ac4d9e092a98ce57de Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 20:07:08 +0000 Subject: [PATCH 06/81] Update platform defaults, handling of 'auto' on Linux/Darwin --- src/defaults/defaults_freebsd.go | 2 +- src/defaults/defaults_netbsd.go | 25 ------------------------- src/defaults/defaults_other.go | 2 +- src/defaults/defaults_windows.go | 2 +- src/tuntap/tun_darwin.go | 3 +++ src/tuntap/tun_linux.go | 3 +++ 6 files changed, 9 insertions(+), 28 deletions(-) delete mode 100644 src/defaults/defaults_netbsd.go diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index d7dc43b9..b08d80d0 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -20,6 +20,6 @@ func GetDefaults() platformDefaultParameters { // TUN/TAP MaximumIfMTU: 32767, DefaultIfMTU: 32767, - DefaultIfName: "/dev/tap0", + DefaultIfName: "/dev/tun0", } } diff --git a/src/defaults/defaults_netbsd.go b/src/defaults/defaults_netbsd.go deleted file mode 100644 index bc9d7a39..00000000 --- a/src/defaults/defaults_netbsd.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build netbsd - -package defaults - -// Sane defaults for the BSD platforms. The "default" options may be -// may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { - return platformDefaultParameters{ - // Admin - DefaultAdminListen: "unix:///var/run/yggdrasil.sock", - - // Configuration (used for yggdrasilctl) - DefaultConfigFile: "/etc/yggdrasil.conf", - - // Multicast interfaces - DefaultMulticastInterfaces: []string{ - ".*", - }, - - // TUN/TAP - MaximumIfMTU: 9000, - DefaultIfMTU: 9000, - DefaultIfName: "/dev/tun0", - } -} diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index 763a7f89..3b035537 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd +// +build !linux,!darwin,!windows,!openbsd,!freebsd package defaults diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index ade833d4..305a2ffe 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -20,6 +20,6 @@ func GetDefaults() platformDefaultParameters { // TUN/TAP MaximumIfMTU: 65535, DefaultIfMTU: 65535, - DefaultIfName: "auto", + DefaultIfName: "Yggdrasil", } } diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index c9b8bceb..cf2fbfb4 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -17,6 +17,9 @@ import ( // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + if ifname == "auto" { + ifname = "utun" + } iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 7935e7cf..4206b26a 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -11,6 +11,9 @@ import ( // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + if ifname == "auto" { + ifname = "\000" + } iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { panic(err) From f95ebeb8216748a5914d9f0fca9eb602f7b16cbe Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 20:08:19 +0000 Subject: [PATCH 07/81] Remove references to TAP --- src/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/config.go b/src/config/config.go index bacf9003..3e1438b8 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -5,7 +5,7 @@ Yggdrasil node. The configuration contains, amongst other things, encryption keys which are used to derive a node's identity, information about peerings and node information that is shared with the network. There are also some module-specific options -related to TUN/TAP, multicast and the admin socket. +related to TUN, multicast and the admin socket. In order for a node to maintain the same identity across restarts, you should persist the configuration onto the filesystem or into some configuration storage @@ -70,8 +70,8 @@ type NodeConfig struct { SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` - IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` - IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` + IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."` TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."` SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` From 3a0870a448f0c1b43a4438a3b2ec9a92d4a4aec4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 20:11:39 +0000 Subject: [PATCH 08/81] Fix IfName 'auto' behaviour on Windows --- src/tuntap/tun_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 8b4f92cf..2afe2da6 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -9,6 +9,7 @@ import ( "strings" "unsafe" + "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "golang.org/x/sys/windows" wgtun "golang.zx2c4.com/wireguard/tun" @@ -19,6 +20,9 @@ import ( // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { + if ifname == "auto" { + ifname = defaults.GetDefaults().IfName + } var err error err = doAsSystem(func() { iface, err := wgtun.CreateTUN(ifname, mtu) From baebaabc437cbe697abae3fcc3d00c25fd48efa0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2019 20:16:24 +0000 Subject: [PATCH 09/81] Fix typo --- src/tuntap/tun_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 2afe2da6..7cedfc85 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -21,7 +21,7 @@ import ( // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { - ifname = defaults.GetDefaults().IfName + ifname = defaults.GetDefaults().DefaultIfName } var err error err = doAsSystem(func() { From 0529910b01015332b0568342f82830baf52fe158 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2019 13:34:27 +0000 Subject: [PATCH 10/81] Reuse GUID so Windows no longer keeps creating new networks each time Yggdrasil starts --- src/tuntap/tun_windows.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 7cedfc85..73ab398d 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -24,8 +24,13 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { ifname = defaults.GetDefaults().DefaultIfName } var err error + var iface wgtun.Device err = doAsSystem(func() { - iface, err := wgtun.CreateTUN(ifname, mtu) + if guid, gerr := windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); gerr == nil { + iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu) + } else { + panic(gerr) + } if err != nil { panic(err) } From d0a307db97a7a6418844aa882cd9e52d25a575c2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2019 13:46:05 +0000 Subject: [PATCH 11/81] Use Wireguard's DoAsSystem, fix build tags and go.mod/go.sum --- go.mod | 8 ++-- go.sum | 21 ++++----- src/tuntap/tun_windows.go | 89 ++------------------------------------- 3 files changed, 18 insertions(+), 100 deletions(-) diff --git a/go.mod b/go.mod index 417e6fa3..6e57de50 100644 --- a/go.mod +++ b/go.mod @@ -9,14 +9,12 @@ require ( github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 - github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 - github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b // indirect github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect - github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 golang.org/x/net v0.0.0-20191021144547-ec77196f6094 - golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb + golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 golang.org/x/text v0.3.2 - golang.zx2c4.com/wireguard v0.0.20191012 + golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5 + golang.zx2c4.com/wireguard/windows v0.0.35 ) diff --git a/go.sum b/go.sum index 222728a1..c1dc2b78 100644 --- a/go.sum +++ b/go.sum @@ -8,18 +8,16 @@ github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:b github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= +github.com/lxn/walk v0.0.0-20191024161928-0ee7d2cded97 h1:3zBUhgnxeLyaImKEtPfKWipiHyI5zYp/V2NN967zPFo= +github.com/lxn/walk v0.0.0-20191024161928-0ee7d2cded97/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1 h1:h0wbuSK8xUNmMwDdCxZx2OLdkVck6Bb31zj4CxCN5I4= +github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w= -github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8oye9a7HmytmZ+tr8Q2vlP0tAHP13kTHzwvQY= -github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c= -github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8 h1:YY9Pg2BEp0jeUVU60svTOaDr+fs1ySC9RbdC1Qc6wOw= -github.com/yggdrasil-network/water v0.0.0-20190812103929-c83fe40250f8/go.mod h1:R0SBCsugm+Sf1katgTb2t7GXMm+nRIv43tM4VDZbaOs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= @@ -30,12 +28,15 @@ golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+T golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb h1:ZxSglHghKPYD8WDeRUzRJrUJtDF0PxsTUSxyqr9/5BI= -golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 h1:zSqWKgm/o7HAnlAzBQ+aetp9fpuyytsXnKA8eiLHYQM= +golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.zx2c4.com/wireguard v0.0.20191012 h1:sdX+y3hrHkW8KJkjY7ZgzpT5Tqo8XnBkH55U1klphko= -golang.zx2c4.com/wireguard v0.0.20191012/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5 h1:tijV0YMbg6JbCA9TNaNVjAK2+6KefpNwF4coc6UkNkQ= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard/windows v0.0.35 h1:egyV5IRbpKr78b+9gkB8AMQ32TtCo0NO//rfh10OSDg= +golang.zx2c4.com/wireguard/windows v0.0.35/go.mod h1:A3MmRXpatlao+Zty3Zxe2nqZ/sQCGYvnMVZ4z7TQY2w= diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 73ab398d..0a14768d 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -1,3 +1,5 @@ +// +build windows + package tuntap import ( @@ -5,14 +7,12 @@ import ( "errors" "log" "net" - "runtime" - "strings" - "unsafe" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "golang.org/x/sys/windows" wgtun "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/windows/elevate" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) @@ -25,7 +25,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { } var err error var iface wgtun.Device - err = doAsSystem(func() { + err = elevate.DoAsSystem(func() { if guid, gerr := windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); gerr == nil { iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu) } else { @@ -107,87 +107,6 @@ func (tun *TunAdapter) setupAddress(addr string) error { return nil } -/* - * doAsSystem - * SPDX-License-Identifier: LGPL-3.0 - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - */ -func doAsSystem(f func()) error { - runtime.LockOSThread() - defer func() { - windows.RevertToSelf() - runtime.UnlockOSThread() - }() - privileges := windows.Tokenprivileges{ - PrivilegeCount: 1, - Privileges: [1]windows.LUIDAndAttributes{ - { - Attributes: windows.SE_PRIVILEGE_ENABLED, - }, - }, - } - err := windows.LookupPrivilegeValue(nil, windows.StringToUTF16Ptr("SeDebugPrivilege"), &privileges.Privileges[0].Luid) - if err != nil { - return err - } - err = windows.ImpersonateSelf(windows.SecurityImpersonation) - if err != nil { - return err - } - var threadToken windows.Token - err = windows.OpenThreadToken(windows.CurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES, false, &threadToken) - if err != nil { - return err - } - defer threadToken.Close() - err = windows.AdjustTokenPrivileges(threadToken, false, &privileges, uint32(unsafe.Sizeof(privileges)), nil, nil) - if err != nil { - return err - } - - processes, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0) - if err != nil { - return err - } - defer windows.CloseHandle(processes) - - processEntry := windows.ProcessEntry32{Size: uint32(unsafe.Sizeof(windows.ProcessEntry32{}))} - pid := uint32(0) - for err = windows.Process32First(processes, &processEntry); err == nil; err = windows.Process32Next(processes, &processEntry) { - if strings.ToLower(windows.UTF16ToString(processEntry.ExeFile[:])) == "winlogon.exe" { - pid = processEntry.ProcessID - break - } - } - if pid == 0 { - return errors.New("unable to find winlogon.exe process") - } - - winlogonProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, pid) - if err != nil { - return err - } - defer windows.CloseHandle(winlogonProcess) - var winlogonToken windows.Token - err = windows.OpenProcessToken(winlogonProcess, windows.TOKEN_IMPERSONATE|windows.TOKEN_DUPLICATE, &winlogonToken) - if err != nil { - return err - } - defer winlogonToken.Close() - var duplicatedToken windows.Token - err = windows.DuplicateTokenEx(winlogonToken, 0, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken) - if err != nil { - return err - } - defer duplicatedToken.Close() - err = windows.SetThreadToken(nil, duplicatedToken) - if err != nil { - return err - } - f() - return nil -} - /* * cleanupAddressesOnDisconnectedInterfaces * SPDX-License-Identifier: MIT From 746fac6594217d3a3cef7379e0fb8636b6498ad2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2019 13:56:48 +0000 Subject: [PATCH 12/81] Fix go.mod/go.sum again and update DoAsSystem call --- go.mod | 10 +++++----- go.sum | 26 ++++++++++++++------------ src/tuntap/tun_windows.go | 27 +++++++++++++-------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 6e57de50..c11ccdf7 100644 --- a/go.mod +++ b/go.mod @@ -11,10 +11,10 @@ require ( github.com/mitchellh/mapstructure v1.1.2 github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/net v0.0.0-20191021144547-ec77196f6094 - golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 + golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c + golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 + golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e golang.org/x/text v0.3.2 - golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5 - golang.zx2c4.com/wireguard/windows v0.0.35 + golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 + golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 ) diff --git a/go.sum b/go.sum index c1dc2b78..9a61fd2c 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,7 @@ github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:b github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= -github.com/lxn/walk v0.0.0-20191024161928-0ee7d2cded97 h1:3zBUhgnxeLyaImKEtPfKWipiHyI5zYp/V2NN967zPFo= -github.com/lxn/walk v0.0.0-20191024161928-0ee7d2cded97/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/walk v0.0.0-20191031081659-c0bb82ae46cb/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1 h1:h0wbuSK8xUNmMwDdCxZx2OLdkVck6Bb31zj4CxCN5I4= github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -20,23 +19,26 @@ github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHS github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 h1:zSqWKgm/o7HAnlAzBQ+aetp9fpuyytsXnKA8eiLHYQM= -golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5 h1:tijV0YMbg6JbCA9TNaNVjAK2+6KefpNwF4coc6UkNkQ= -golang.zx2c4.com/wireguard v0.0.20191013-0.20191022095125-f7d0edd2ecf5/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= -golang.zx2c4.com/wireguard/windows v0.0.35 h1:egyV5IRbpKr78b+9gkB8AMQ32TtCo0NO//rfh10OSDg= -golang.zx2c4.com/wireguard/windows v0.0.35/go.mod h1:A3MmRXpatlao+Zty3Zxe2nqZ/sQCGYvnMVZ4z7TQY2w= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 h1:KxtBKNgJUQG8vwZzJKkwBGOcqp95xLu6A6KIMde1kl0= +golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 h1:TreP+furSwdqoSToFrwb1S5cwxb7jhOsnwj2MsDeT+4= +golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25/go.mod h1:EO8KCpT944a9CnwHJLZ1sl84FfIrY42fP/fcXUuYhKM= diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 0a14768d..9b5f7b0c 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -23,31 +23,30 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { ifname = defaults.GetDefaults().DefaultIfName } - var err error - var iface wgtun.Device - err = elevate.DoAsSystem(func() { - if guid, gerr := windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); gerr == nil { - iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu) - } else { - panic(gerr) + return elevate.DoAsSystem(func() error { + var err error + var iface wgtun.Device + var guid windows.GUID + if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil { + return err } - if err != nil { - panic(err) + if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu); err != nil { + return err } tun.iface = iface - - if err := tun.setupAddress(addr); err != nil { + 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 { + if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil { tun.log.Errorln("Failed to set up TUN MTU:", err) + return err } - if mtu, err = iface.MTU(); err == nil { tun.mtu = mtu } + return nil }) - return err } // Sets the MTU of the TAP adapter. From 6560aac1e9eb3b3d3c1f0ef29ae8ecdf8a34a767 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 13:42:56 -0600 Subject: [PATCH 13/81] fix error spam on shutdown --- src/tuntap/iface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index fbb7c86d..f80ea4f4 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -57,9 +57,9 @@ func (r *tunReader) _read() { n, err := r.tun.iface.Read(recvd, TUN_OFFSET_BYTES) if n <= TUN_OFFSET_BYTES || err != nil { r.tun.log.Errorln("Error reading TUN:", err) - err = r.tun.iface.Flush() - if err != nil { - r.tun.log.Errorln("Unable to flush packets:", err) + ferr := r.tun.iface.Flush() + if ferr != nil { + r.tun.log.Errorln("Unable to flush packets:", ferr) } util.PutBytes(recvd) } else { From f6f9b3ef76bd967ac7ef7fa1a957eb6b3f0c2b82 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 15:01:20 -0600 Subject: [PATCH 14/81] include offset in expected bytes written --- src/tuntap/iface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index f80ea4f4..f1ca0add 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -40,7 +40,7 @@ func (w *tunWriter) _write(b []byte) { } }) } - if written != n { + if written != n+TUN_OFFSET_BYTES { w.tun.log.Errorln("TUN iface write mismatch:", written, "bytes written vs", n, "bytes given") } } From 85c5bc61ac39169f96b9c853d63e165823a014e1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 24 Nov 2019 21:03:02 +0000 Subject: [PATCH 15/81] TUN_OFFSET_BYTES per platform --- src/tuntap/iface.go | 2 -- src/tuntap/tun_bsd.go | 2 ++ src/tuntap/tun_darwin.go | 2 ++ src/tuntap/tun_linux.go | 2 ++ src/tuntap/tun_other.go | 2 ++ src/tuntap/tun_windows.go | 4 +++- 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index f1ca0add..633903da 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -9,8 +9,6 @@ import ( "github.com/Arceliar/phony" ) -const TUN_OFFSET_BYTES = 4 - type tunWriter struct { phony.Inbox tun *TunAdapter diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 219e3485..79184cb9 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -15,6 +15,8 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) +const TUN_OFFSET_BYTES = 0 + const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 type in6_addrlifetime struct { diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index cf2fbfb4..4dab6f31 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -15,6 +15,8 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) +const TUN_OFFSET_BYTES = 0 + // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 4206b26a..ca402522 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -9,6 +9,8 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) +const TUN_OFFSET_BYTES = 0 + // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index 8a27f57b..85934f39 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -9,6 +9,8 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) +const TUN_OFFSET_BYTES = 0 + // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { iface, err := wgtun.CreateTUN(ifname, mtu) diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 9b5f7b0c..e611af76 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -2,6 +2,8 @@ package tuntap +// This is to catch Windows platforms + import ( "bytes" "errors" @@ -16,7 +18,7 @@ import ( "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) -// This is to catch Windows platforms +const TUN_OFFSET_BYTES = 4 // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { From 2982b53555c54eb1952a52ad3788f9375ad9fe52 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 15:09:28 -0600 Subject: [PATCH 16/81] make offset generic over TUN_OFFSET_BYTES so we can make this platform dependent --- src/tuntap/iface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index f1ca0add..5eca3aed 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -31,7 +31,7 @@ func (w *tunWriter) _write(b []byte) { if n == 0 { return } - written, err = w.tun.iface.Write(append([]byte{0, 0, 0, 0}, b[:n]...), TUN_OFFSET_BYTES) + written, err = w.tun.iface.Write(append(make([]byte, TUN_OFFSET_BYTES), b[:n]...), TUN_OFFSET_BYTES) util.PutBytes(b) if err != nil { w.tun.Act(w, func() { From 8f323b740d7171120b6619b320a77973b2ea7d6b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 24 Nov 2019 21:09:29 +0000 Subject: [PATCH 17/81] Revert "TUN_OFFSET_BYTES per platform" This reverts commit 85c5bc61ac39169f96b9c853d63e165823a014e1. --- src/tuntap/iface.go | 2 ++ src/tuntap/tun_bsd.go | 2 -- src/tuntap/tun_darwin.go | 2 -- src/tuntap/tun_linux.go | 2 -- src/tuntap/tun_other.go | 2 -- src/tuntap/tun_windows.go | 4 +--- 6 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 633903da..f1ca0add 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -9,6 +9,8 @@ import ( "github.com/Arceliar/phony" ) +const TUN_OFFSET_BYTES = 4 + type tunWriter struct { phony.Inbox tun *TunAdapter diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 79184cb9..219e3485 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -15,8 +15,6 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) -const TUN_OFFSET_BYTES = 0 - const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 type in6_addrlifetime struct { diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index 4dab6f31..cf2fbfb4 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -15,8 +15,6 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) -const TUN_OFFSET_BYTES = 0 - // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index ca402522..4206b26a 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -9,8 +9,6 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) -const TUN_OFFSET_BYTES = 0 - // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if ifname == "auto" { diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index 85934f39..8a27f57b 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -9,8 +9,6 @@ import ( wgtun "golang.zx2c4.com/wireguard/tun" ) -const TUN_OFFSET_BYTES = 0 - // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { iface, err := wgtun.CreateTUN(ifname, mtu) diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index e611af76..9b5f7b0c 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -2,8 +2,6 @@ package tuntap -// This is to catch Windows platforms - import ( "bytes" "errors" @@ -18,7 +16,7 @@ import ( "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) -const TUN_OFFSET_BYTES = 4 +// This is to catch Windows platforms // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { From 2e95a3131c86389ff2e49c1d5e2c534a50d4eb97 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 15:37:37 -0600 Subject: [PATCH 18/81] comment out pointless error that prints on some platforms and not others --- src/tuntap/iface.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 5eca3aed..463f48f0 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -31,7 +31,7 @@ func (w *tunWriter) _write(b []byte) { if n == 0 { return } - written, err = w.tun.iface.Write(append(make([]byte, TUN_OFFSET_BYTES), b[:n]...), TUN_OFFSET_BYTES) + written, err = w.tun.iface.Write(append(make([]byte, TUN_OFFSET_BYTES), b...), TUN_OFFSET_BYTES) util.PutBytes(b) if err != nil { w.tun.Act(w, func() { @@ -41,7 +41,8 @@ func (w *tunWriter) _write(b []byte) { }) } if written != n+TUN_OFFSET_BYTES { - w.tun.log.Errorln("TUN iface write mismatch:", written, "bytes written vs", n, "bytes given") + // FIXME some platforms return the wrong number of bytes written, causing error spam + //w.tun.log.Errorln("TUN iface write mismatch:", written, "bytes written vs", n+TUN_OFFSET_BYTES, "bytes given") } } From 27cc57dbbc91220c982155e53826f7ca524e0ca8 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 18:24:17 -0600 Subject: [PATCH 19/81] attempt to prevent incorrect idle notification in switch, needs testing --- src/yggdrasil/link.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 1710e202..10a31457 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -64,6 +64,7 @@ type linkInterface struct { closeTimer *time.Timer // Fires when the link has been idle so long we need to close it inSwitch bool // True if the switch is tracking this link stalled bool // True if we haven't been receiving any response traffic + unstalled bool // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up) } func (l *link) init(c *Core) error { @@ -324,11 +325,15 @@ func (intf *linkInterface) notifySent(size int, isLinkTraffic bool) { // Notify the switch that we're ready for more traffic, assuming we're not in a stalled state func (intf *linkInterface) _notifySwitch() { - if !intf.inSwitch && !intf.stalled { - intf.inSwitch = true - intf.link.core.switchTable.Act(intf, func() { - intf.link.core.switchTable._idleIn(intf.peer.port) - }) + if !intf.inSwitch { + if intf.stalled { + intf.unstalled = false + } else { + intf.inSwitch = true + intf.link.core.switchTable.Act(intf, func() { + intf.link.core.switchTable._idleIn(intf.peer.port) + }) + } } } @@ -362,7 +367,9 @@ func (intf *linkInterface) notifyRead(size int) { intf.stallTimer = nil } intf.stalled = false - intf._notifySwitch() + if !intf.unstalled { + intf._notifySwitch() + } if size > 0 && intf.stallTimer == nil { intf.stallTimer = time.AfterFunc(keepAliveTime, intf.notifyDoKeepAlive) } From 3e0799551835c7b1846605bd88ae8a133c00c4ee Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 18:53:58 -0600 Subject: [PATCH 20/81] it helps to actually set the flag... --- src/yggdrasil/link.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 10a31457..cd40a9ea 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -369,6 +369,7 @@ func (intf *linkInterface) notifyRead(size int) { intf.stalled = false if !intf.unstalled { intf._notifySwitch() + intf.unstalled = true } if size > 0 && intf.stallTimer == nil { intf.stallTimer = time.AfterFunc(keepAliveTime, intf.notifyDoKeepAlive) From 38c54efd73f27db2265fbe467593bac22b0a3780 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Nov 2019 22:54:30 -0600 Subject: [PATCH 21/81] Update yggdrasil.service --- contrib/systemd/yggdrasil.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index bdf365c8..b90d09d1 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -8,7 +8,7 @@ Group=yggdrasil ProtectHome=true ProtectSystem=true SyslogIdentifier=yggdrasil -CapabilityBoundingSet=CAP_NET_ADMIN +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW ExecStartPre=+-/sbin/modprobe tun ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ then umask 077; \ From 837e7da7921d78aa58f6e1a10220184d99d3f6e7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 25 Nov 2019 20:13:41 +0000 Subject: [PATCH 22/81] Force packets through the switch to be buffered (seems to help the reordering problem on Windows) --- src/yggdrasil/switch.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index ba30758c..5d355041 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -718,8 +718,14 @@ func (t *switchTable) _handleIn(packet []byte, idle map[switchPort]struct{}, sen if best != nil { if _, isIdle := idle[best.elem.port]; isIdle { delete(idle, best.elem.port) - ports[best.elem.port].sendPacketsFrom(t, [][]byte{packet}) - return true + + // FIXME: This was causing the out-of-order packets on Windows but forcing + // all packets to buffer might have a mild performance penalty + //ports[best.elem.port].sendPacketsFrom(t, [][]byte{packet}) + //return true + t.Act(nil, func() { + t._idleIn(best.elem.port) + }) } } // Didn't find anyone idle to send it to From 98339cdc3f89cc4342c5e7ef0624dc440ff8a4ac Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 25 Nov 2019 17:40:58 -0600 Subject: [PATCH 23/81] possible fix if monotonic time resolution is related to packet reordering --- src/yggdrasil/switch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index ba30758c..ab888a76 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -808,12 +808,12 @@ func (t *switchTable) _handleIdle(port switchPort) bool { packet := buf.packets[0] coords := switch_getPacketCoords(packet.bytes) priority := float64(now.Sub(packet.time)) / float64(buf.size) - if priority > bestPriority && t.portIsCloser(coords, port) { + if priority >= bestPriority && t.portIsCloser(coords, port) { best = streamID bestPriority = priority } } - if bestPriority != 0 { + if best != "" { buf := t.queues.bufs[best] var packet switch_packetInfo // TODO decide if this should be LIFO or FIFO From ad8d30ce74bc90eb9363bf5a341933a2972b892a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 26 Nov 2019 09:44:35 +0000 Subject: [PATCH 24/81] Revert "Force packets through the switch to be buffered (seems to help the reordering problem on Windows)" This reverts commit 837e7da7921d78aa58f6e1a10220184d99d3f6e7. --- src/yggdrasil/switch.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index d0a9f4e0..ab888a76 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -718,14 +718,8 @@ func (t *switchTable) _handleIn(packet []byte, idle map[switchPort]struct{}, sen if best != nil { if _, isIdle := idle[best.elem.port]; isIdle { delete(idle, best.elem.port) - - // FIXME: This was causing the out-of-order packets on Windows but forcing - // all packets to buffer might have a mild performance penalty - //ports[best.elem.port].sendPacketsFrom(t, [][]byte{packet}) - //return true - t.Act(nil, func() { - t._idleIn(best.elem.port) - }) + ports[best.elem.port].sendPacketsFrom(t, [][]byte{packet}) + return true } } // Didn't find anyone idle to send it to From e1b0d0f20c546a905a701bbfef181fe109791a2a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 00:35:29 +0000 Subject: [PATCH 25/81] Appveyor MSI builds for Windows (#621) * Try appveyor for MSI (not finished) * build-msi.sh * Don't shallow clone * Don't set clone depth * Build Yggdrasil for each arch * Try to get rest of branches * Allow upgrades (hopefully) * Try using MajorUpgrade * AllowDowngrades * Try harder to build x86 :-) * Bugfix * Bugfix * AllowSameVersionUpgrades * AllowSameVersionUpgrades * Generate new GUID for each build (might fix upgrades) --- appveyor.yml | 23 +++++ contrib/msi/build-msi.sh | 189 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 appveyor.yml create mode 100644 contrib/msi/build-msi.sh diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..1389bebe --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,23 @@ +version: '{build}' +pull_requests: + do_not_increment_build_number: true +os: Visual Studio 2017 +shallow_clone: false + +environment: + MSYS2_PATH_TYPE: inherit + CHERE_INVOKING: enabled_from_arguments + +install: +- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S curl unzip" + +build_script: +- cmd: >- + cd \projects\yggdrasil-go +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64" +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86" + +test: off + +artifacts: +- path: '*.msi' diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh new file mode 100644 index 00000000..d314b0c6 --- /dev/null +++ b/contrib/msi/build-msi.sh @@ -0,0 +1,189 @@ +#!/bin/sh + +# Get arch from command line if given +PKGARCH=$1 +if [ "${PKGARCH}" == "" ]; +then + echo "tell me the architecture: x86 or x64" + exit 1 +fi + +# Get the rest of the repository history +if [ "${APPVEYOR_REPO_BRANCH}" != "" ]; +then + git fetch --all + git checkout ${APPVEYOR_REPO_BRANCH} +fi + +# Install prerequisites +pacman -S --needed --noconfirm unzip git curl +# export PATH=$PATH:/c/go/bin/ + +# Download the wix tools! +if [ ! -d wixbin ]; +then + curl -LO https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip + if [ `md5sum wix311-binaries.zip | cut -f 1 -d " "` != "47a506f8ab6666ee3cc502fb07d0ee2a" ]; + then + echo "wix package didn't match expected checksum" + exit 1 + fi + mkdir -p wixbin + unzip -o wix311-binaries.zip -d wixbin || ( + echo "failed to unzip WiX" + exit 1 + ) +fi + +# Check the prerequisite files are in place +[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build +[ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build + +# Create the postinstall script +cat > config.bat << EOF +if exist yggdrasil.conf ( + move yggdrasil.conf yggdrasil.conf.backup + yggdrasil.exe -useconffile yggdrasil.conf.backup -normaliseconf > yggdrasil.conf +) else ( + yggdrasil.exe -genconf > yggdrasil.conf +) +EOF + +# Work out metadata for the package info +PKGNAME=$(sh contrib/semver/name.sh) +PKGVERSION=$(sh contrib/semver/version.sh --bare) +PKGVERSIONMS=$(echo $PKGVERSION | tr - .) +[ "${PKGARCH}" == "x64" ] && \ + PKGGUID="77757838-1a23-40a5-a720-c3b43e0260cc" PKGINSTFOLDER="ProgramFiles64Folder" || \ + PKGGUID="54a3294e-a441-4322-aefb-3bb40dd022bb" PKGINSTFOLDER="ProgramFilesFolder" + +# Download the Wintun driver +if [ $PKGARCH = "x64" ]; then + PKGMSMNAME=wintun-x64.msm + curl -o ${PKGMSMNAME} https://www.wintun.net/builds/wintun-amd64-0.7.msm || (echo "couldn't get wintun"; exit 1) +elif [ $PKGARCH = "x86" ]; then + PKGMSMNAME=wintun-x86.msm + curl -o ${PKGMSMNAME} https://www.wintun.net/builds/wintun-x86-0.7.msm || (echo "couldn't get wintun"; exit 1) +else + echo "wasn't sure which architecture to get wintun for" + exit 1 +fi + +# Generate the wix.xml file +cat > wix.xml << EOF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOF + +# Generate the MSI +CANDLEFLAGS="-nologo" +LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61" +wixbin/candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \ +wixbin/light $LIGHTFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj From 41a2e731eb61a274b16b16f4a764fc2665d460d7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 09:52:14 +0000 Subject: [PATCH 26/81] More MSI updates (#622) * Try embedding config script * Update config when installing * Don't update config on uninstall --- contrib/msi/build-msi.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index d314b0c6..de459e54 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -147,7 +147,7 @@ cat > wix.xml << EOF @@ -168,14 +168,16 @@ cat > wix.xml << EOF + ExeCommand="cmd.exe /c updateconfig.bat" + Execute="immediate" + Return="asyncWait" /> + After="InstallFiles"> + NOT Installed AND NOT REMOVE + From b88a623a9f79414e8375e813d50ce6c92f9c1f8c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 09:56:14 +0000 Subject: [PATCH 27/81] Handle pull request branch --- contrib/msi/build-msi.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index de459e54..aecd034f 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -9,7 +9,11 @@ then fi # Get the rest of the repository history -if [ "${APPVEYOR_REPO_BRANCH}" != "" ]; +if [ "${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}" != "" ]; +then + git fetch --all + git checkout ${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH} +elif [ "${APPVEYOR_REPO_BRANCH}" != "" ]; then git fetch --all git checkout ${APPVEYOR_REPO_BRANCH} From a673625e82516c13e7514a9fd607b9a2c42d0c09 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 10:08:01 +0000 Subject: [PATCH 28/81] Configure service with -useconffile --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index aecd034f..22eab223 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -128,7 +128,7 @@ cat > wix.xml << EOF Name="yggdrasil" Start="auto" Type="ownProcess" - Arguments="-autoconf" + Arguments='-useconffile "[YggdrasilInstallFolder]yggdrasil.conf"' Vital="yes" /> Date: Thu, 28 Nov 2019 10:19:47 +0000 Subject: [PATCH 29/81] Fix update action --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 22eab223..ed2b6815 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -173,7 +173,7 @@ cat > wix.xml << EOF Id="UpdateGenerateConfig" Directory="YggdrasilInstallFolder" ExeCommand="cmd.exe /c updateconfig.bat" - Execute="immediate" + Execute="commit" Return="asyncWait" /> From 724446bb04f487455d69f08a0abe1b59814dd444 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 10:42:57 +0000 Subject: [PATCH 30/81] Defer updateconfig --- contrib/msi/build-msi.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index ed2b6815..20f9e9b8 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -45,6 +45,7 @@ fi # Create the postinstall script cat > config.bat << EOF + if exist yggdrasil.conf ( move yggdrasil.conf yggdrasil.conf.backup yggdrasil.exe -useconffile yggdrasil.conf.backup -normaliseconf > yggdrasil.conf @@ -173,8 +174,8 @@ cat > wix.xml << EOF Id="UpdateGenerateConfig" Directory="YggdrasilInstallFolder" ExeCommand="cmd.exe /c updateconfig.bat" - Execute="commit" - Return="asyncWait" /> + Execute="deferred" + Return="check" /> Date: Thu, 28 Nov 2019 10:56:22 +0000 Subject: [PATCH 31/81] Set output logging --- contrib/msi/build-msi.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 20f9e9b8..3f2501d5 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -129,7 +129,7 @@ cat > wix.xml << EOF Name="yggdrasil" Start="auto" Type="ownProcess" - Arguments='-useconffile "[YggdrasilInstallFolder]yggdrasil.conf"' + Arguments='-useconffile "[YggdrasilInstallFolder]yggdrasil.conf" -logto "[YggdrasilInstallFolder]yggdrasil.log"' Vital="yes" /> wix.xml << EOF - + + - Date: Thu, 28 Nov 2019 11:16:36 +0000 Subject: [PATCH 32/81] Don't impersonate user for updateconfig.bat --- contrib/msi/build-msi.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 3f2501d5..c4646ae5 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -179,12 +179,13 @@ cat > wix.xml << EOF Directory="YggdrasilInstallFolder" ExeCommand="cmd.exe /c updateconfig.bat" Execute="deferred" + Impersonate="no" Return="check" /> + Before="InstallServices"> NOT Installed AND NOT REMOVE From 9c113c05bf462b96e4d69ea0b569cb4da6a57e3c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 12:57:19 +0000 Subject: [PATCH 33/81] Use appveyor build folder in case slugs are different --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1389bebe..bd0948de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ install: build_script: - cmd: >- - cd \projects\yggdrasil-go + cd %APPVEYOR_BUILD_FOLDER% - c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64" - c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86" From 3f29a2ff0566879e421c93a0c879a094ff9b29d8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 13:00:52 +0000 Subject: [PATCH 34/81] Some comments --- contrib/msi/build-msi.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index c4646ae5..5daa003b 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -1,5 +1,12 @@ #!/bin/sh +# This script generates an MSI file for Yggdrasil for a given architecture. It +# needs to run on Windows within MSYS2 and Go 1.13 or later must be installed on +# the system and within the PATH. This is ran currently by Appveyor (see +# appveyor.yml in the repository root) for both x86 and x64. +# +# Author: Neil Alexander + # Get arch from command line if given PKGARCH=$1 if [ "${PKGARCH}" == "" ]; @@ -8,7 +15,9 @@ then exit 1 fi -# Get the rest of the repository history +# Get the rest of the repository history. This is needed within Appveyor because +# otherwise we don't get all of the branch histories and therefore the semver +# scripts don't work properly. if [ "${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}" != "" ]; then git fetch --all @@ -19,9 +28,8 @@ then git checkout ${APPVEYOR_REPO_BRANCH} fi -# Install prerequisites +# Install prerequisites within MSYS2 pacman -S --needed --noconfirm unzip git curl -# export PATH=$PATH:/c/go/bin/ # Download the wix tools! if [ ! -d wixbin ]; @@ -39,13 +47,12 @@ then ) fi -# Check the prerequisite files are in place +# Build Yggdrasil! [ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build [ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build # Create the postinstall script cat > config.bat << EOF - if exist yggdrasil.conf ( move yggdrasil.conf yggdrasil.conf.backup yggdrasil.exe -useconffile yggdrasil.conf.backup -normaliseconf > yggdrasil.conf From c17c4af26d9c0a41ce7a6dd23aa35d4eb762ce09 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 13:08:56 +0000 Subject: [PATCH 35/81] Don't normalise on upgrade --- contrib/msi/build-msi.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 5daa003b..5d8eb7db 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -53,10 +53,7 @@ fi # Create the postinstall script cat > config.bat << EOF -if exist yggdrasil.conf ( - move yggdrasil.conf yggdrasil.conf.backup - yggdrasil.exe -useconffile yggdrasil.conf.backup -normaliseconf > yggdrasil.conf -) else ( +if not exist yggdrasil.conf ( yggdrasil.exe -genconf > yggdrasil.conf ) EOF From 71404f5270174d157aa624235713033ef2926f3d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Nov 2019 15:17:49 +0000 Subject: [PATCH 36/81] Clean up appveyor.yml --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bd0948de..58724a24 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,9 +8,6 @@ environment: MSYS2_PATH_TYPE: inherit CHERE_INVOKING: enabled_from_arguments -install: -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S curl unzip" - build_script: - cmd: >- cd %APPVEYOR_BUILD_FOLDER% From c2a8b4bb5767b35974d45dad6777187616553131 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 28 Nov 2019 12:00:00 -0600 Subject: [PATCH 37/81] get rid of an allocation in tunWriter's _write --- src/tuntap/iface.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 463f48f0..5efbc8a7 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -25,14 +25,16 @@ func (w *tunWriter) writeFrom(from phony.Actor, b []byte) { // write is pretty loose with the memory safety rules, e.g. it assumes it can // read w.tun.iface.IsTap() safely func (w *tunWriter) _write(b []byte) { + defer util.PutBytes(b) var written int var err error n := len(b) if n == 0 { return } - written, err = w.tun.iface.Write(append(make([]byte, TUN_OFFSET_BYTES), b...), TUN_OFFSET_BYTES) - util.PutBytes(b) + temp := append(util.ResizeBytes(util.GetBytes(), TUN_OFFSET_BYTES), b...) + defer util.PutBytes(temp) + written, err = w.tun.iface.Write(temp, TUN_OFFSET_BYTES) if err != nil { w.tun.Act(w, func() { if !w.tun.isOpen { From 73f50af3b73e864ac0d18d5f995b3f9c3adb5d4f Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Fri, 29 Nov 2019 11:45:02 +0200 Subject: [PATCH 38/81] Fix typos --- CHANGELOG.md | 8 ++++---- cmd/genkeys/main.go | 2 +- doc/Whitepaper.md | 4 ++-- misc/sim/treesim-forward.py | 2 +- misc/sim/treesim.go | 2 +- src/address/address.go | 4 ++-- src/config/config.go | 2 +- src/crypto/crypto.go | 2 +- src/tuntap/conn.go | 2 +- src/yggdrasil/conn.go | 4 ++-- src/yggdrasil/core_test.go | 2 +- src/yggdrasil/dht.go | 2 +- src/yggdrasil/dialer.go | 2 +- src/yggdrasil/link.go | 2 +- src/yggdrasil/peer.go | 6 +++--- src/yggdrasil/search.go | 2 +- src/yggdrasil/session.go | 8 ++++---- src/yggdrasil/switch.go | 4 ++-- src/yggdrasil/version.go | 6 +++--- 19 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a511c523..d3950f7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load -- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attemps to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do +- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do ### Fixed - The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load @@ -90,7 +90,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds - A memory leak in the add-peer loop has been fixed - The admin socket now reports the correct URI strings for SOCKS peers in `getPeers` -- A race condition when dialling a remote node by both the node address and routed prefix simultaneously has been fixed +- A race condition when dialing a remote node by both the node address and routed prefix simultaneously has been fixed - A race condition between the router and the dial code resulting in a panic has been fixed - A panic which could occur when the TUN/TAP interface disappears (e.g. during soft-shutdown) has been fixed - A bug in the semantic versioning script which accompanies Yggdrasil for builds has been fixed @@ -180,7 +180,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.3.4] - 2019-03-12 ### Added - Support for multiple listeners (although currently only TCP listeners are supported) -- New multicast behaviour where each multicast interface is given it's own link-local listener and does not depend on the `Listen` configuration +- New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration - Blocking detection in the switch to avoid parenting a blocked peer - Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime - Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash @@ -374,7 +374,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Wire format changes (backwards incompatible). - Less maintenance traffic per peer. - Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers). -- Iterative DHT (added some time between v0.1.0 and here). +- Iterative DHT (added sometime between v0.1.0 and here). - Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet. ### Removed diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index 7a0d316a..7b48cdc7 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -3,7 +3,7 @@ This file generates crypto keys. It prints out a new set of keys each time if finds a "better" one. By default, "better" means a higher NodeID (-> higher IP address). -This is because the IP address format can compress leading 1s in the address, to incrase the number of ID bits in the address. +This is because the IP address format can compress leading 1s in the address, to increase the number of ID bits in the address. If run with the "-sig" flag, it generates signing keys instead. A "better" signing key means one with a higher TreeID. diff --git a/doc/Whitepaper.md b/doc/Whitepaper.md index d7f13279..b65963d1 100644 --- a/doc/Whitepaper.md +++ b/doc/Whitepaper.md @@ -65,12 +65,12 @@ Given the coordinates of any two nodes, it is possible to calculate the length o Traffic is forwarded using a [greedy routing](https://en.wikipedia.org/wiki/Small-world_routing#Greedy_routing) scheme, where each node forwards the packet to a one-hop neighbor that is closer to the destination (according to this distance metric) than the current node. In particular, when a packet needs to be forwarded, a node will forward it to whatever peer is closest to the destination in the greedy [metric space](https://en.wikipedia.org/wiki/Metric_space) used by the network, provided that the peer is closer to the destination than the current node. -If no closer peers are idle, then the packet is queued in FIFO order, with separate queues per destination coords (currently, as a bit of a hack, IPv6 flow labels are embedeed after the end of the significant part of the coords, so queues distinguish between different traffic streams with the same destination). +If no closer peers are idle, then the packet is queued in FIFO order, with separate queues per destination coords (currently, as a bit of a hack, IPv6 flow labels are embedded after the end of the significant part of the coords, so queues distinguish between different traffic streams with the same destination). Whenever the node finishes forwarding a packet to a peer, it checks the queues, and will forward the first packet from the queue with the maximum `/`, i.e. the bandwidth the queue is attempting to use, subject to the constraint that the peer is a valid next hop (i.e. closer to the destination than the current node). If no non-empty queue is available, then the peer is added to the idle set, forward packets when the need arises. This acts as a crude approximation of backpressure routing, where the remote queue sizes are assumed to be equal to the distance of a node from a destination (rather than communicating queue size information), and packets are never forwarded "backwards" through the network, but congestion on a local link is routed around when possible. -The queue selection strategy behaves similar to shortest-queue-first, in that a larger fration of available bandwith to sessions that attempt to use less bandwidth, and is loosely based on the rationale behind some proposed solutions to the [cake-cutting](https://en.wikipedia.org/wiki/Fair_cake-cutting) problem. +The queue selection strategy behaves similar to shortest-queue-first, in that a larger fraction of available bandwidth to sessions that attempt to use less bandwidth, and is loosely based on the rationale behind some proposed solutions to the [cake-cutting](https://en.wikipedia.org/wiki/Fair_cake-cutting) problem. The queue size is limited to 4 MB. If a packet is added to a queue and the total size of all queues is larger than this threshold, then a random queue is selected (with odds proportional to relative queue sizes), and the first packet from that queue is dropped, with the process repeated until the total queue size drops below the allowed threshold. diff --git a/misc/sim/treesim-forward.py b/misc/sim/treesim-forward.py index b717b29d..f7ca509d 100644 --- a/misc/sim/treesim-forward.py +++ b/misc/sim/treesim-forward.py @@ -793,7 +793,7 @@ def timelineDimesTest(): store = makeStoreDimesEdges(path, bestRoot) rootID = "R" + bestRoot[1:] assert rootID in store - # Don't forget to set random seed before setitng times + # Don't forget to set random seed before setting times # To make results reproducible nodeIDs = sorted(store.keys()) random.seed(12345) diff --git a/misc/sim/treesim.go b/misc/sim/treesim.go index a62f9ff4..2b155151 100644 --- a/misc/sim/treesim.go +++ b/misc/sim/treesim.go @@ -424,7 +424,7 @@ func main() { //*/ startNetwork(kstore) //time.Sleep(10*time.Second) - // Note that testPaths only works if pressure is turend off + // Note that testPaths only works if pressure is turned off // Otherwise congestion can lead to routing loops? for finished := false; !finished; { finished = testPaths(kstore) diff --git a/src/address/address.go b/src/address/address.go index eba61708..0569af00 100644 --- a/src/address/address.go +++ b/src/address/address.go @@ -15,9 +15,9 @@ type Address [16]byte type Subnet [8]byte // GetPrefix returns the address prefix used by yggdrasil. -// The current implementation requires this to be a muliple of 8 bits + 7 bits. +// The current implementation requires this to be a multiple of 8 bits + 7 bits. // The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1). -// Nodes that configure this differently will be unable to communicate with eachother using IP packets, though routing and the DHT machinery *should* still work. +// Nodes that configure this differently will be unable to communicate with each other using IP packets, though routing and the DHT machinery *should* still work. func GetPrefix() [1]byte { return [...]byte{0x02} } diff --git a/src/config/config.go b/src/config/config.go index 3e1438b8..93b77eed 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -71,7 +71,7 @@ type NodeConfig struct { SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` - IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + IfMTU int `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."` TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."` SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 6c10a2ef..e084d713 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -215,7 +215,7 @@ func GetSharedKey(myPrivKey *BoxPrivKey, return (*BoxSharedKey)(&shared) } -// BoxOpen returns a message and true if it successfull opens a crypto box using the provided shared key and nonce. +// BoxOpen returns a message and true if it successful opens a crypto box using the provided shared key and nonce. func BoxOpen(shared *BoxSharedKey, boxed []byte, nonce *BoxNonce) ([]byte, bool) { diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index 207cd14f..aa149063 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -63,7 +63,7 @@ func (s *tunConn) _read(bs []byte) (err error) { default: isCGA = false } - // Check destiantion addresses + // Check destination addresses switch { case ipv6 && bs[24] == 0x02 && bytes.Equal(s.tun.addr[:16], bs[24:40]): // destination case ipv6 && bs[24] == 0x03 && bytes.Equal(s.tun.subnet[:8], bs[24:32]): // destination diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 67426f4f..9a26432d 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -164,7 +164,7 @@ func (c *Conn) _getDeadlineCancellation(t *time.Time) (util.Cancellation, bool) c := util.CancellationWithDeadline(c.session.cancel, *t) return c, true } else { - // No deadline was set, so just return the existinc cancellation and a dummy value + // No deadline was set, so just return the existing cancellation and a dummy value return c.session.cancel, false } } @@ -279,7 +279,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error { } // WriteFrom should be called by a phony.Actor, and tells the Conn to send a -// message. This is used internaly by Write. If the callback is called with a +// message. This is used internally by Write. If the callback is called with a // non-nil value, then it is safe to reuse the argument FlowKeyMessage. func (c *Conn) WriteFrom(from phony.Actor, msg FlowKeyMessage, callback func(error)) { c.Act(from, func() { diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 364ed0b0..5f302c23 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -22,7 +22,7 @@ func GenerateConfig() *config.NodeConfig { return cfg } -// GetLoggerWithPrefix creates a new logger instance wih prefix. +// GetLoggerWithPrefix creates a new logger instance with prefix. // If verbose is set to true, three log levels are enabled: "info", "warn", "error". func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { l := log.New(os.Stderr, prefix, log.Flags()) diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 575c8b1a..72165948 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -20,7 +20,7 @@ const ( ) // dhtInfo represents everything we know about a node in the DHT. -// This includes its key, a cache of it's NodeID, coords, and timing/ping related info for deciding who/when to ping nodes for maintenance. +// This includes its key, a cache of its NodeID, coords, and timing/ping related info for deciding who/when to ping nodes for maintenance. type dhtInfo struct { nodeID_hidden *crypto.NodeID key crypto.BoxPubKey diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index 47a68813..e9da97a2 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -17,7 +17,7 @@ type Dialer struct { core *Core } -// Dial opens a session to the given node. The first paramter should be "nodeid" +// Dial opens a session to the given node. The first parameter should be "nodeid" // and the second parameter should contain a hexadecimal representation of the // target node ID. It uses DialContext internally. func (d *Dialer) Dial(network, address string) (net.Conn, error) { diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index cd40a9ea..157ea525 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -123,7 +123,7 @@ func (l *link) listen(uri string) error { } func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) { - // Technically anything unique would work for names, but lets pick something human readable, just for debugging + // Technically anything unique would work for names, but let's pick something human readable, just for debugging intf := linkInterface{ name: name, link: l, diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 381e6917..4206857e 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -19,7 +19,7 @@ import ( // The peers struct represents peers with an active connection. // Incoming packets are passed to the corresponding peer, which handles them somehow. // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. -// In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. +// In other cases, its link protocol traffic is used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { core *Core mutex sync.Mutex // Synchronize writes to atomic @@ -90,7 +90,7 @@ func (ps *peers) putPorts(ports map[switchPort]*peer) { ps.ports.Store(ports) } -// Information known about a peer, including thier box/sig keys, precomputed shared keys (static and ephemeral) and a handler for their outgoing traffic +// Information known about a peer, including their box/sig keys, precomputed shared keys (static and ephemeral) and a handler for their outgoing traffic type peer struct { phony.Inbox core *Core @@ -356,7 +356,7 @@ func (p *peer) _handleSwitchMsg(packet []byte) { p.dinfo = nil return } - // Pass a mesage to the dht informing it that this peer (still) exists + // Pass a message to the dht informing it that this peer (still) exists loc.coords = loc.coords[:len(loc.coords)-1] p.dinfo = &dhtInfo{ key: p.box, diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index f52dcbe8..b0fdbb51 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -144,7 +144,7 @@ func (sinfo *searchInfo) doSearchStep() { sinfo.time = time.Now() } -// If we've recenty sent a ping for this search, do nothing. +// If we've recently sent a ping for this search, do nothing. // Otherwise, doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME. func (sinfo *searchInfo) continueSearch() { sinfo.doSearchStep() diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 743dd7a4..ecc7d499 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -39,7 +39,7 @@ type sessionInfo struct { theirMTU uint16 // myMTU uint16 // wasMTUFixed bool // Was the MTU fixed by a receive error? - timeOpened time.Time // Time the sessino was opened + timeOpened time.Time // Time the session was opened time time.Time // Time we last received a packet mtuTime time.Time // time myMTU was last changed pingTime time.Time // time the first ping was sent since the last received packet @@ -55,7 +55,7 @@ type sessionInfo struct { callbacks []chan func() // Finished work from crypto workers } -// Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU. +// Represents a session ping/pong packet, and includes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU. type sessionPing struct { SendPermPub crypto.BoxPubKey // Sender's permanent key Handle crypto.Handle // Random number to ID session @@ -175,7 +175,7 @@ func (ss *sessions) getByTheirPerm(key *crypto.BoxPubKey) (*sessionInfo, bool) { } // Creates a new session and lazily cleans up old existing sessions. This -// includse initializing session info to sane defaults (e.g. lowest supported +// includes initializing session info to sane defaults (e.g. lowest supported // MTU). func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { // TODO: this check definitely needs to be moved @@ -415,7 +415,7 @@ func (sinfo *sessionInfo) _updateNonce(theirNonce *crypto.BoxNonce) { } // Resets all sessions to an uninitialized state. -// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change. +// Called after coord changes, so attempts to use a session will trigger a new ping and notify the remote end of the coord change. // Only call this from the router actor. func (ss *sessions) reset() { for _, _sinfo := range ss.sinfos { diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index ab888a76..653b12f1 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -107,7 +107,7 @@ func (l *switchLocator) getCoords() []byte { return bs } -// Returns true if the this locator represents an ancestor of the locator given as an argument. +// Returns true if this locator represents an ancestor of the locator given as an argument. // Ancestor means that it's the parent node, or the parent of parent, and so on... func (x *switchLocator) isAncestorOf(y *switchLocator) bool { if x.root != y.root { @@ -381,7 +381,7 @@ func (t *switchTable) handleMsg(msg *switchMsg, fromPort switchPort) { // Then the tricky part, it decides if it should update our own locator as a result. // That happens if this node is already our parent, or is advertising a better root, or is advertising a better path to the same root, etc... // There are a lot of very delicate order sensitive checks here, so its' best to just read the code if you need to understand what it's doing. -// It's very important to not change the order of the statements in the case function unless you're absolutely sure that it's safe, including safe if used along side nodes that used the previous order. +// It's very important to not change the order of the statements in the case function unless you're absolutely sure that it's safe, including safe if used alongside nodes that used the previous order. // Set the third arg to true if you're reprocessing an old message, e.g. to find a new parent after one disconnects, to avoid updating some timing related things. func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, reprocessing bool) { // TODO directly use a switchMsg instead of switchMessage + sigs diff --git a/src/yggdrasil/version.go b/src/yggdrasil/version.go index f71780e0..91fcc825 100644 --- a/src/yggdrasil/version.go +++ b/src/yggdrasil/version.go @@ -1,14 +1,14 @@ package yggdrasil // This file contains the version metadata struct -// Used in the inital connection setup and key exchange +// Used in the initial connection setup and key exchange // Some of this could arguably go in wire.go instead import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" // This is the version-specific metadata exchanged at the start of a connection. -// It must always beign with the 4 bytes "meta" and a wire formatted uint64 major version number. -// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open an connection. +// It must always begin with the 4 bytes "meta" and a wire formatted uint64 major version number. +// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open a connection. type version_metadata struct { meta [4]byte ver uint64 // 1 byte in this version From 16e55992b6d1a3e278d6faa2b0c64def2470b2ab Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 29 Nov 2019 11:06:08 +0000 Subject: [PATCH 39/81] Move yggdrasil.conf to ALLUSERSPROFILE --- contrib/msi/build-msi.sh | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 5d8eb7db..4e68b406 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -52,9 +52,14 @@ fi [ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build # Create the postinstall script -cat > config.bat << EOF -if not exist yggdrasil.conf ( - yggdrasil.exe -genconf > yggdrasil.conf +cat > updateconfig.bat << EOF +if not exist %ALLUSERSPROFILE%\\Yggdrasil ( + mkdir %ALLUSERSPROFILE%\\Yggdrasil +) +if not exist %ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf ( + if exist yggdrasil.exe ( + yggdrasil.exe -genconf > %ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf + ) ) EOF @@ -95,9 +100,10 @@ cat > wix.xml << EOF Id="*" Keywords="Installer" Description="Yggdrasil Network Installer" - Comments="This is the Yggdrasil Network binary." + Comments="This is the Yggdrasil Network router for Windows." Manufacturer="github.com/yggdrasil-network" InstallerVersion="200" + InstallScope="perMachine" Languages="1033" Compressed="yes" Platform="${PKGARCH}" @@ -133,7 +139,7 @@ cat > wix.xml << EOF Name="yggdrasil" Start="auto" Type="ownProcess" - Arguments='-useconffile "[YggdrasilInstallFolder]yggdrasil.conf" -logto "[YggdrasilInstallFolder]yggdrasil.log"' + Arguments='-useconffile "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf" -logto "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.log"' Vital="yes" /> wix.xml << EOF Id="Configbat" Name="updateconfig.bat" DiskId="1" - Source="config.bat" + Source="updateconfig.bat" KeyPath="yes"/> @@ -183,13 +189,13 @@ cat > wix.xml << EOF Directory="YggdrasilInstallFolder" ExeCommand="cmd.exe /c updateconfig.bat" Execute="deferred" - Impersonate="no" - Return="check" /> + Return="check" + Impersonate="yes" /> + Before="StartServices"> NOT Installed AND NOT REMOVE @@ -202,4 +208,4 @@ EOF CANDLEFLAGS="-nologo" LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61" wixbin/candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \ -wixbin/light $LIGHTFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj +wixbin/light $LIGHTFLAGS -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj From 729d2ca2ba991b0a26d9a58acff1664b34b1002c Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 29 Nov 2019 17:14:27 -0600 Subject: [PATCH 40/81] Update crypto.go --- src/crypto/crypto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index e084d713..3a6e02d1 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -215,7 +215,7 @@ func GetSharedKey(myPrivKey *BoxPrivKey, return (*BoxSharedKey)(&shared) } -// BoxOpen returns a message and true if it successful opens a crypto box using the provided shared key and nonce. +// BoxOpen returns a message and true if it successfully opens a crypto box using the provided shared key and nonce. func BoxOpen(shared *BoxSharedKey, boxed []byte, nonce *BoxNonce) ([]byte, bool) { From 4159ccb893205f532a5f83d26072ef7a46d8129b Mon Sep 17 00:00:00 2001 From: Anatolii Kurotych Date: Sat, 30 Nov 2019 16:05:44 +0200 Subject: [PATCH 41/81] Fix return value in Multicast.Stop() --- src/multicast/multicast.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 4e0b4f35..93bf94bb 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -112,7 +112,7 @@ func (m *Multicast) Stop() error { err = m._stop() }) m.log.Debugln("Stopped multicast module") - return nil + return err } func (m *Multicast) _stop() error { From 468e3661684dfe11dbc4dcc5856a166b87e21009 Mon Sep 17 00:00:00 2001 From: Anatolii Kurotych Date: Sat, 30 Nov 2019 20:46:29 +0200 Subject: [PATCH 42/81] Use loglevel instead comma-separated list of logging --- cmd/yggdrasil/main.go | 45 ++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 981c6efc..813e950b 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -132,6 +132,32 @@ func doGenconf(isjson bool) string { return string(bs) } +func setLogLevel(loglevel string, logger *log.Logger) { + levels := [...]string{"error", "warn", "info", "debug", "trace"} + loglevel = strings.ToLower(loglevel) + + contains := func() bool { + for _, l := range levels { + if l == loglevel { + return true + } + } + return false + } + + if !contains() { // set default log level + logger.Infoln("Loglevel parse failed. Set default level(info)") + loglevel = "info" + } + + for _, l := range levels { + logger.EnableLevel(l) + if l == loglevel { + break + } + } +} + // The main function is responsible for configuring and starting Yggdrasil. func main() { // Configure the command line parameters. @@ -142,10 +168,10 @@ func main() { confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON") autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") ver := flag.Bool("version", false, "prints the version of this build") - logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable") logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"") getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration") getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration") + loglevel := flag.String("loglevel", "info", "loglevel to enable") flag.Parse() var cfg *config.NodeConfig @@ -239,20 +265,9 @@ func main() { logger = log.New(os.Stdout, "", log.Flags()) logger.Warnln("Logging defaulting to stdout") } - //logger.EnableLevel("error") - //logger.EnableLevel("warn") - //logger.EnableLevel("info") - if levels := strings.Split(*logging, ","); len(levels) > 0 { - for _, level := range levels { - l := strings.TrimSpace(level) - switch l { - case "error", "warn", "info", "trace", "debug": - logger.EnableLevel(l) - default: - continue - } - } - } + + setLogLevel(*loglevel, logger) + // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} From bf5d5b2269329906512e887701a9d27a382b7057 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 4 Dec 2019 09:29:30 +0000 Subject: [PATCH 43/81] Rename service from 'yggdrasil' to 'Yggdrasil' --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 4e68b406..cfa2728d 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -136,7 +136,7 @@ cat > wix.xml << EOF DisplayName="Yggdrasil Service" ErrorControl="normal" LoadOrderGroup="NetworkProvider" - Name="yggdrasil" + Name="Yggdrasil" Start="auto" Type="ownProcess" Arguments='-useconffile "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf" -logto "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.log"' From 1d4119950172d8d65ba24e3c7ae146a89fdf5ec9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 10:55:20 +0000 Subject: [PATCH 44/81] Move Wintun to separate feature --- contrib/msi/build-msi.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index cfa2728d..cf69d6bf 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -177,13 +177,16 @@ cat > wix.xml << EOF SourceFile="${PKGMSMNAME}" /> - - + + + + + Date: Tue, 10 Dec 2019 11:17:15 +0000 Subject: [PATCH 45/81] Update metadata --- contrib/msi/build-msi.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index cf69d6bf..3f80487d 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -100,7 +100,7 @@ cat > wix.xml << EOF Id="*" Keywords="Installer" Description="Yggdrasil Network Installer" - Comments="This is the Yggdrasil Network router for Windows." + Comments="Yggdrasil Network standalone router for Windows." Manufacturer="github.com/yggdrasil-network" InstallerVersion="200" InstallScope="perMachine" @@ -115,7 +115,8 @@ cat > wix.xml << EOF + EmbedCab="yes" + CompressionLevel="high" /> From 3e388cd7f9b6adedf768fea512a7c8e3801d2c49 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:27:49 +0000 Subject: [PATCH 46/81] Try to avoid breaking Wintun during upgrades --- contrib/msi/build-msi.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 3f80487d..358fb775 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -185,6 +185,9 @@ cat > wix.xml << EOF + + NOT UPGRADINGPRODUCTCODE + From 6f927b06133b5a2b5d01cdd8757e5509e45e5403 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:33:52 +0000 Subject: [PATCH 47/81] Reverse upgrade condition --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 358fb775..b6601968 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -186,7 +186,7 @@ cat > wix.xml << EOF - NOT UPGRADINGPRODUCTCODE + UPGRADINGPRODUCTCODE From 4762edc2b3067884c696755d51907c5e467371c0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:38:58 +0000 Subject: [PATCH 48/81] Package display name --- contrib/msi/build-msi.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index b6601968..e291074e 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -83,12 +83,18 @@ else exit 1 fi +if [ $PKGNAME != "master" ]; then + PKGDISPLAYNAME="Yggdrasil Network (${PKGNAME} branch)" +elif + PKGDISPLAYNAME="Yggdrasil Network" +fi + # Generate the wix.xml file cat > wix.xml << EOF Date: Tue, 10 Dec 2019 11:40:16 +0000 Subject: [PATCH 49/81] Fix syntax error in build-msi.sh --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index e291074e..421481cd 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -85,7 +85,7 @@ fi if [ $PKGNAME != "master" ]; then PKGDISPLAYNAME="Yggdrasil Network (${PKGNAME} branch)" -elif +else PKGDISPLAYNAME="Yggdrasil Network" fi From ff5de89762bdda544e52e6d2256e4a92e008fc5a Mon Sep 17 00:00:00 2001 From: cathugger Date: Wed, 11 Dec 2019 15:24:43 +0200 Subject: [PATCH 50/81] util: fix possible OOB in IPv4 flowkey calc, use switch there ihl may grow upto 15*4=60 so extract and check it before using it as offset in flowkey calculation. also replace IFs with switches for protocol matching as it's less redundant and nicer to document. --- src/util/util.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/util.go b/src/util/util.go index a7a54562..c20421d5 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -103,11 +103,11 @@ func GetFlowKey(bs []byte) uint64 { // Get the IP protocol version from the packet switch bs[0] & 0xf0 { case 0x40: // IPv4 packet - // Check the packet meets minimum UDP packet length - if len(bs) >= 24 { - // Is the protocol TCP, UDP or SCTP? - if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { - ihl := bs[0] & 0x0f * 4 // Header length + ihl := (bs[0] & 0x0f) * 4 // whole IPv4 header length (min 20) + // 8 is minimum UDP packet length + if ihl >= 20 && len(bs)-int(ihl) >= 8 { + switch bs[9] /* protocol */ { + case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ : flowkey = uint64(bs[9])<<32 /* proto */ | uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ @@ -119,8 +119,8 @@ func GetFlowKey(bs []byte) uint64 { // If the flowlabel isn't present, make protokey from proto | sport | dport // if the packet meets minimum UDP packet length if flowkey == 0 && len(bs) >= 48 { - // Is the protocol TCP, UDP or SCTP? - if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { + switch bs[9] /* protocol */ { + case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ : flowkey = uint64(bs[6])<<32 /* proto */ | uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ From 5bd9391c615327379b885ed07d003918486dec5a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 25 Dec 2019 17:45:24 -0600 Subject: [PATCH 51/81] slightly cleaner way for yggdrasilctl to os.exit, making sure defers are called --- cmd/yggdrasilctl/main.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 94d90842..697a3faa 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -25,14 +25,20 @@ import ( type admin_info map[string]interface{} func main() { + // makes sure we can use defer and still return an error code to the OS + os.Exit(run()) +} + +func run() int { logbuffer := &bytes.Buffer{} logger := log.New(logbuffer, "", log.Flags()) - defer func() { + defer func() int { if r := recover(); r != nil { logger.Println("Fatal error:", r) fmt.Print(logbuffer) - os.Exit(1) + return 1 } + return 0 }() endpoint := defaults.GetDefaults().DefaultAdminListen @@ -62,12 +68,12 @@ func main() { fmt.Println("Build name:", version.BuildName()) fmt.Println("Build version:", version.BuildVersion()) fmt.Println("To get the version number of the running Yggdrasil node, run", os.Args[0], "getSelf") - return + return 0 } if len(args) == 0 { flag.Usage() - return + return 0 } if *server == endpoint { @@ -176,15 +182,15 @@ func main() { } else { fmt.Println("Admin socket returned an error but didn't specify any error text") } - os.Exit(1) + return 1 } if _, ok := recv["request"]; !ok { fmt.Println("Missing request in response (malformed response?)") - os.Exit(1) + return 1 } if _, ok := recv["response"]; !ok { fmt.Println("Missing response body (malformed response?)") - os.Exit(1) + return 1 } req := recv["request"].(map[string]interface{}) res := recv["response"].(map[string]interface{}) @@ -193,7 +199,7 @@ func main() { if json, err := json.MarshalIndent(res, "", " "); err == nil { fmt.Println(string(json)) } - os.Exit(0) + return 0 } switch strings.ToLower(req["request"].(string)) { @@ -434,7 +440,7 @@ func main() { } if v, ok := recv["status"]; ok && v != "success" { - os.Exit(1) + return 1 } - os.Exit(0) + return 0 } From 9fac5355eb15a03d1eed04365ccc475c35e98294 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 25 Dec 2019 18:55:29 -0600 Subject: [PATCH 52/81] make searches more parallel --- src/yggdrasil/search.go | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index b0fdbb51..903f9c30 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -16,7 +16,6 @@ package yggdrasil import ( "errors" - "sort" "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -104,24 +103,6 @@ func (sinfo *searchInfo) addToSearch(res *dhtRes) { sinfo.toVisit = append(sinfo.toVisit, info) } } - // Deduplicate - vMap := make(map[crypto.NodeID]*dhtInfo) - for _, info := range sinfo.toVisit { - vMap[*info.getNodeID()] = info - } - sinfo.toVisit = sinfo.toVisit[:0] - for _, info := range vMap { - sinfo.toVisit = append(sinfo.toVisit, info) - } - // Sort - sort.SliceStable(sinfo.toVisit, func(i, j int) bool { - // Should return true if i is closer to the destination than j - return dht_ordered(&res.Dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) - }) - // Truncate to some maximum size - if len(sinfo.toVisit) > search_MAX_SEARCH_SIZE { - sinfo.toVisit = sinfo.toVisit[:search_MAX_SEARCH_SIZE] - } } // If there are no nodes left toVisit, then this cleans up the search. @@ -136,12 +117,13 @@ func (sinfo *searchInfo) doSearchStep() { return } // Send to the next search target - var next *dhtInfo - next, sinfo.toVisit = sinfo.toVisit[0], sinfo.toVisit[1:] - rq := dhtReqKey{next.key, sinfo.dest} - sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) - sinfo.searches.router.dht.ping(next, &sinfo.dest) - sinfo.time = time.Now() + for _, next := range sinfo.toVisit { + rq := dhtReqKey{next.key, sinfo.dest} + sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) + sinfo.searches.router.dht.ping(next, &sinfo.dest) + sinfo.time = time.Now() + } + sinfo.toVisit = sinfo.toVisit[:0] } // If we've recently sent a ping for this search, do nothing. From 201dbec63d2bc629bb26ed7515112d223f5147cb Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 25 Dec 2019 19:01:20 -0600 Subject: [PATCH 53/81] always keep the 2 closest nodes in each direction around the dht ring, possibly helps things recover faster after joins/leaves --- src/yggdrasil/dht.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 72165948..013fd1e4 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -387,6 +387,8 @@ func (t *dht) getImportant() []*dhtInfo { if dist < minDist { minDist = dist important = append(important, info) + } else if len(important) < 2 { + important = append(important, info) } } var temp []*dhtInfo @@ -397,6 +399,8 @@ func (t *dht) getImportant() []*dhtInfo { if dist < minDist { minDist = dist temp = append(temp, info) + } else if len(temp) < 2 { + temp = append(temp, info) } } for idx := len(temp) - 1; idx >= 0; idx-- { From 8513f8f4dc019ad1f589e8db5d3d18b3ef290a90 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 4 Jan 2020 16:08:48 -0600 Subject: [PATCH 54/81] constant space searches that should play nicer if searching for an unreachable destination --- src/yggdrasil/search.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 903f9c30..4708a705 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -37,7 +37,7 @@ type searchInfo struct { mask crypto.NodeID time time.Time toVisit []*dhtInfo - visited map[crypto.NodeID]bool + visited crypto.NodeID // Closest address visited so far callback func(*sessionInfo, error) // TODO context.Context for timeout and cancellation } @@ -93,12 +93,16 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { func (sinfo *searchInfo) addToSearch(res *dhtRes) { // Add responses to toVisit if closer to dest than the res node from := dhtInfo{key: res.Key, coords: res.Coords} - sinfo.visited[*from.getNodeID()] = true + if dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { + // Closer to the destination, so update visited + sinfo.visited = *from.getNodeID() + } for _, info := range res.Infos { - if *info.getNodeID() == sinfo.searches.router.dht.nodeID || sinfo.visited[*info.getNodeID()] { + if *info.getNodeID() == sinfo.visited { + // dht_ordered could return true here, but we want to skip it in this case continue } - if dht_ordered(&sinfo.dest, info.getNodeID(), from.getNodeID()) { + if dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { // Response is closer to the destination sinfo.toVisit = append(sinfo.toVisit, info) } @@ -148,7 +152,7 @@ func (sinfo *searchInfo) continueSearch() { // Calls create search, and initializes the iterative search parts of the struct before returning it. func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo { sinfo := s.createSearch(dest, mask, callback) - sinfo.visited = make(map[crypto.NodeID]bool) + sinfo.visited = s.router.dht.nodeID loc := s.router.core.switchTable.getLocator() sinfo.toVisit = append(sinfo.toVisit, &dhtInfo{ key: s.router.core.boxPub, From 8358fe5c5cc4fb66c987abb449c7f59c7e6b0ece Mon Sep 17 00:00:00 2001 From: Adam Ruzicka Date: Sun, 5 Jan 2020 17:27:54 +0000 Subject: [PATCH 55/81] Unify MTU datatypes across the codebase The codebase uses int and unit16 to represent MTU randomly. This change unifies it to a MTU type from types package, which is currently uint16. --- src/config/config.go | 5 ++++- src/defaults/defaults.go | 6 ++++-- src/tuntap/iface.go | 2 +- src/tuntap/tun.go | 15 +++++++++------ src/tuntap/tun_bsd.go | 6 +++--- src/tuntap/tun_darwin.go | 6 +++--- src/tuntap/tun_linux.go | 8 ++++---- src/tuntap/tun_windows.go | 10 +++++----- src/types/types.go | 3 +++ src/yggdrasil/api.go | 8 ++++---- src/yggdrasil/conn.go | 7 +++++-- src/yggdrasil/session.go | 10 +++++----- src/yggdrasil/wire.go | 2 +- 13 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 src/types/types.go diff --git a/src/config/config.go b/src/config/config.go index 93b77eed..95d9bbd1 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -22,8 +22,11 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "github.com/yggdrasil-network/yggdrasil-go/src/types" ) +type MTU = types.MTU + // NodeState represents the active and previous configuration of an Yggdrasil // node. A NodeState object is returned when starting an Yggdrasil node. Note // that this structure and related functions are likely to disappear soon. @@ -71,7 +74,7 @@ type NodeConfig struct { SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` - IfMTU int `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."` + IfMTU MTU `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."` TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."` SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index ceb58ba8..a0d372e4 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -1,5 +1,7 @@ package defaults +import "github.com/yggdrasil-network/yggdrasil-go/src/types" + // Defines which parameters are expected by default for configuration on a // specific platform. These values are populated in the relevant defaults_*.go // for the platform being targeted. They must be set. @@ -14,7 +16,7 @@ type platformDefaultParameters struct { DefaultMulticastInterfaces []string // TUN/TAP - MaximumIfMTU int - DefaultIfMTU int + MaximumIfMTU types.MTU + DefaultIfMTU types.MTU DefaultIfName string } diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 5efbc8a7..1e5902e8 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -55,7 +55,7 @@ type tunReader struct { func (r *tunReader) _read() { // Get a slice to store the packet in - recvd := util.ResizeBytes(util.GetBytes(), r.tun.mtu+TUN_OFFSET_BYTES) + recvd := util.ResizeBytes(util.GetBytes(), int(r.tun.mtu)+TUN_OFFSET_BYTES) // Wait for a packet to be delivered to us through the TUN adapter n, err := r.tun.iface.Read(recvd, TUN_OFFSET_BYTES) if n <= TUN_OFFSET_BYTES || err != nil { diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 807ce7fb..be0ea708 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -25,8 +25,11 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" + "github.com/yggdrasil-network/yggdrasil-go/src/types" ) +type MTU = types.MTU + const tun_IPv6_HEADER_LENGTH = 40 // TunAdapter represents a running TUN interface and extends the @@ -46,7 +49,7 @@ type TunAdapter struct { subnet address.Subnet ckr cryptokey icmpv6 ICMPv6 - mtu int + mtu MTU iface tun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below //mutex sync.RWMutex // Protects the below @@ -63,7 +66,7 @@ type TunOptions struct { // Gets the maximum supported MTU for the platform based on the defaults in // defaults.GetDefaults(). -func getSupportedMTU(mtu int) int { +func getSupportedMTU(mtu MTU) MTU { if mtu < 1280 { return 1280 } @@ -85,7 +88,7 @@ func (tun *TunAdapter) Name() string { // MTU gets the adapter's MTU. This can range between 1280 and 65535, although // the maximum value is determined by your platform. The returned value will // never exceed that of MaximumMTU(). -func (tun *TunAdapter) MTU() int { +func (tun *TunAdapter) MTU() MTU { return getSupportedMTU(tun.mtu) } @@ -96,14 +99,14 @@ func DefaultName() string { // DefaultMTU gets the default TUN interface MTU for your platform. This can // be as high as MaximumMTU(), depending on platform, but is never lower than 1280. -func DefaultMTU() int { +func DefaultMTU() MTU { return defaults.GetDefaults().DefaultIfMTU } // MaximumMTU returns the maximum supported TUN interface MTU for your // platform. This can be as high as 65535, depending on platform, but is never // lower than 1280. -func MaximumMTU() int { +func MaximumMTU() MTU { return defaults.GetDefaults().MaximumIfMTU } @@ -165,7 +168,7 @@ func (tun *TunAdapter) _start() error { if tun.MTU() != current.IfMTU { tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU()) } - tun.core.SetMaximumSessionMTU(uint16(tun.MTU())) + tun.core.SetMaximumSessionMTU(tun.MTU()) tun.isOpen = true go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 219e3485..4710e8cd 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -73,14 +73,14 @@ type in6_ifreq_lifetime struct { } // Configures the TUN adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { - iface, err := wgtun.CreateTUN(ifname, mtu) +func (tun *TunAdapter) setup(ifname string, addr string, mtu MTU) error { + iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { panic(err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(MTU(mtu)) } else { tun.mtu = 0 } diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index cf2fbfb4..b984e164 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -16,17 +16,17 @@ import ( ) // Configures the "utun" adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { +func (tun *TunAdapter) setup(ifname string, addr string, mtu MTU) error { if ifname == "auto" { ifname = "utun" } - iface, err := wgtun.CreateTUN(ifname, mtu) + iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { panic(err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(MTU(mtu)) } else { tun.mtu = 0 } diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 4206b26a..ad7b64ef 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -10,17 +10,17 @@ import ( ) // Configures the TUN adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { +func (tun *TunAdapter) setup(ifname string, addr string, mtu MTU) error { if ifname == "auto" { ifname = "\000" } - iface, err := wgtun.CreateTUN(ifname, mtu) + iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { panic(err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(MTU(mtu)) } else { tun.mtu = 0 } @@ -43,7 +43,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { if err := netlink.AddrAdd(nlintf, nladdr); err != nil { return err } - if err := netlink.LinkSetMTU(nlintf, tun.mtu); err != nil { + if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil { return err } if err := netlink.LinkSetUp(nlintf); err != nil { diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 9b5f7b0c..8eb21658 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -19,7 +19,7 @@ import ( // This is to catch Windows platforms // Configures the TUN adapter with the correct IPv6 address and MTU. -func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { +func (tun *TunAdapter) setup(ifname string, addr string, mtu MTU) error { if ifname == "auto" { ifname = defaults.GetDefaults().DefaultIfName } @@ -30,7 +30,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil { return err } - if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, mtu); err != nil { + if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil { return err } tun.iface = iface @@ -42,15 +42,15 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu int) error { tun.log.Errorln("Failed to set up TUN MTU:", err) return err } - if mtu, err = iface.MTU(); err == nil { - tun.mtu = mtu + if mtu, err := iface.MTU(); err == nil { + tun.mtu = MTU(mtu) } return nil }) } // Sets the MTU of the TAP adapter. -func (tun *TunAdapter) setupMTU(mtu int) error { +func (tun *TunAdapter) setupMTU(mtu MTU) error { if tun.iface == nil || tun.Name() == "" { return errors.New("Can't configure MTU as TUN adapter is not present") } diff --git a/src/types/types.go b/src/types/types.go new file mode 100644 index 00000000..5d1c024f --- /dev/null +++ b/src/types/types.go @@ -0,0 +1,3 @@ +package types + +type MTU uint16 diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 7f82c260..1a98e0d8 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -99,7 +99,7 @@ type Session struct { Coords []uint64 // The coordinates of the remote node BytesSent uint64 // Bytes sent to the session BytesRecvd uint64 // Bytes received from the session - MTU uint16 // The maximum supported message size of the session + MTU MTU // The maximum supported message size of the session Uptime time.Duration // How long this session has been active for WasMTUFixed bool // This field is no longer used } @@ -364,8 +364,8 @@ func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { } // GetMaximumSessionMTU returns the maximum allowed session MTU size. -func (c *Core) GetMaximumSessionMTU() uint16 { - var mtu uint16 +func (c *Core) GetMaximumSessionMTU() MTU { + var mtu MTU phony.Block(&c.router, func() { mtu = c.router.sessions.myMaximumMTU }) @@ -375,7 +375,7 @@ func (c *Core) GetMaximumSessionMTU() uint16 { // SetMaximumSessionMTU sets the maximum allowed session MTU size. The default // value is 65535 bytes. Session pings will be sent to update all open sessions // if the MTU has changed. -func (c *Core) SetMaximumSessionMTU(mtu uint16) { +func (c *Core) SetMaximumSessionMTU(mtu MTU) { phony.Block(&c.router, func() { if c.router.sessions.myMaximumMTU != mtu { c.router.sessions.myMaximumMTU = mtu diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 9a26432d..dab38d3d 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -8,10 +8,13 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" + "github.com/yggdrasil-network/yggdrasil-go/src/types" "github.com/Arceliar/phony" ) +type MTU = types.MTU + // ConnError implements the net.Error interface type ConnError struct { error @@ -65,7 +68,7 @@ type Conn struct { nodeID *crypto.NodeID nodeMask *crypto.NodeID session *sessionInfo - mtu uint16 + mtu MTU readCallback func([]byte) readBuffer chan []byte } @@ -93,7 +96,7 @@ func (c *Conn) String() string { return s } -func (c *Conn) setMTU(from phony.Actor, mtu uint16) { +func (c *Conn) setMTU(from phony.Actor, mtu MTU) { c.Act(from, func() { c.mtu = mtu }) } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index ecc7d499..91c530d4 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -36,8 +36,8 @@ type sessionInfo struct { myHandle crypto.Handle // theirNonce crypto.BoxNonce // myNonce crypto.BoxNonce // - theirMTU uint16 // - myMTU uint16 // + theirMTU MTU // + myMTU MTU // wasMTUFixed bool // Was the MTU fixed by a receive error? timeOpened time.Time // Time the session was opened time time.Time // Time we last received a packet @@ -63,7 +63,7 @@ type sessionPing struct { Coords []byte // Tstamp int64 // unix time, but the only real requirement is that it increases IsPong bool // - MTU uint16 // + MTU MTU // } // Updates session info in response to a ping, after checking that the ping is OK. @@ -117,7 +117,7 @@ type sessions struct { lastCleanup time.Time isAllowedHandler func(pubkey *crypto.BoxPubKey, initiator bool) bool // Returns true or false if session setup is allowed isAllowedMutex sync.RWMutex // Protects the above - myMaximumMTU uint16 // Maximum allowed session MTU + myMaximumMTU MTU // Maximum allowed session MTU permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey // Maps known permanent keys to their shared key, used by DHT a lot sinfos map[crypto.Handle]*sessionInfo // Maps handle onto session info byTheirPerm map[crypto.BoxPubKey]*crypto.Handle // Maps theirPermPub onto handle @@ -385,7 +385,7 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the MTU of the session. // Will be equal to the smaller of this node's MTU or the remote node's MTU. // If sending over links with a maximum message size (this was a thing with the old UDP code), it could be further lowered, to a minimum of 1280. -func (sinfo *sessionInfo) _getMTU() uint16 { +func (sinfo *sessionInfo) _getMTU() MTU { if sinfo.theirMTU == 0 || sinfo.myMTU == 0 { return 0 } diff --git a/src/yggdrasil/wire.go b/src/yggdrasil/wire.go index 4ff19e2d..18a647d8 100644 --- a/src/yggdrasil/wire.go +++ b/src/yggdrasil/wire.go @@ -380,7 +380,7 @@ func (p *sessionPing) decode(bs []byte) bool { if pType == wire_SessionPong { p.IsPong = true } - p.MTU = uint16(mtu) + p.MTU = MTU(mtu) return true } From a2adcbd7e43e584396919ad7063ab0fde5e3ece3 Mon Sep 17 00:00:00 2001 From: William Fleurant Date: Sun, 5 Jan 2020 15:26:08 -0500 Subject: [PATCH 56/81] docker: build and copy genkeys --- contrib/docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index fb4fbfaa..fd2ae981 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -5,13 +5,14 @@ WORKDIR /src ENV CGO_ENABLED=0 -RUN apk add git && ./build +RUN apk add git && ./build && go build -o /src/genkeys cmd/genkeys/main.go FROM docker.io/alpine LABEL maintainer="Christer Waren/CWINFO " COPY --from=builder /src/yggdrasil /usr/bin/yggdrasil COPY --from=builder /src/yggdrasilctl /usr/bin/yggdrasilctl +COPY --from=builder /src/genkeys /usr/bin/genkeys COPY contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh # RUN addgroup -g 1000 -S yggdrasil-network \ From 9304873047a6f3be84fef00c4be528f65e8ee2d7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 5 Jan 2020 22:15:52 +0000 Subject: [PATCH 57/81] Convert nodeinfo to actor --- src/yggdrasil/api.go | 19 +++---- src/yggdrasil/nodeinfo.go | 114 +++++++++++++++++++++++--------------- src/yggdrasil/router.go | 1 + 3 files changed, 77 insertions(+), 57 deletions(-) diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 7f82c260..693dbd06 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -390,17 +390,14 @@ func (c *Core) SetMaximumSessionMTU(mtu uint16) { // necessary when, e.g. crawling the network. func (c *Core) GetNodeInfo(key crypto.BoxPubKey, coords []uint64, nocache bool) (NodeInfoPayload, error) { response := make(chan *NodeInfoPayload, 1) - sendNodeInfoRequest := func() { - c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) { - defer func() { recover() }() - select { - case response <- nodeinfo: - default: - } - }) - c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false) - } - phony.Block(&c.router, sendNodeInfoRequest) + c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) { + defer func() { recover() }() + select { + case response <- nodeinfo: + default: + } + }) + c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false) timer := time.AfterFunc(6*time.Second, func() { close(response) }) defer timer.Stop() for res := range response { diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index 8a5d7872..c3e9a274 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -5,21 +5,19 @@ import ( "errors" "runtime" "strings" - "sync" "time" + "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) type nodeinfo struct { - core *Core - myNodeInfo NodeInfoPayload - myNodeInfoMutex sync.RWMutex - callbacks map[crypto.BoxPubKey]nodeinfoCallback - callbacksMutex sync.Mutex - cache map[crypto.BoxPubKey]nodeinfoCached - cacheMutex sync.RWMutex + phony.Inbox + core *Core + myNodeInfo NodeInfoPayload + callbacks map[crypto.BoxPubKey]nodeinfoCallback + cache map[crypto.BoxPubKey]nodeinfoCached } type nodeinfoCached struct { @@ -43,35 +41,43 @@ type nodeinfoReqRes struct { // Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep // the cache/callback maps clean of stale entries func (m *nodeinfo) init(core *Core) { + m.Act(m, func() { + m._init(core) + }) +} + +func (m *nodeinfo) _init(core *Core) { m.core = core m.callbacks = make(map[crypto.BoxPubKey]nodeinfoCallback) m.cache = make(map[crypto.BoxPubKey]nodeinfoCached) - var f func() - f = func() { - m.callbacksMutex.Lock() - for boxPubKey, callback := range m.callbacks { - if time.Since(callback.created) > time.Minute { - delete(m.callbacks, boxPubKey) - } + m._cleanup() +} + +func (m *nodeinfo) _cleanup() { + for boxPubKey, callback := range m.callbacks { + if time.Since(callback.created) > time.Minute { + delete(m.callbacks, boxPubKey) } - m.callbacksMutex.Unlock() - m.cacheMutex.Lock() - for boxPubKey, cache := range m.cache { - if time.Since(cache.created) > time.Hour { - delete(m.cache, boxPubKey) - } - } - m.cacheMutex.Unlock() - time.AfterFunc(time.Second*30, f) } - go f() + for boxPubKey, cache := range m.cache { + if time.Since(cache.created) > time.Hour { + delete(m.cache, boxPubKey) + } + } + time.AfterFunc(time.Second*30, func() { + m.Act(m, m._cleanup) + }) } // Add a callback for a nodeinfo lookup func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { - m.callbacksMutex.Lock() - defer m.callbacksMutex.Unlock() + m.Act(m, func() { + m._addCallback(sender, call) + }) +} + +func (m *nodeinfo) _addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { m.callbacks[sender] = nodeinfoCallback{ created: time.Now(), call: call, @@ -79,9 +85,7 @@ func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *Node } // Handles the callback, if there is one -func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { - m.callbacksMutex.Lock() - defer m.callbacksMutex.Unlock() +func (m *nodeinfo) _callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { if callback, ok := m.callbacks[sender]; ok { callback.call(&nodeinfo) delete(m.callbacks, sender) @@ -89,16 +93,26 @@ func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { } // Get the current node's nodeinfo -func (m *nodeinfo) getNodeInfo() NodeInfoPayload { - m.myNodeInfoMutex.RLock() - defer m.myNodeInfoMutex.RUnlock() +func (m *nodeinfo) getNodeInfo() (p NodeInfoPayload) { + phony.Block(m, func() { + p = m._getNodeInfo() + }) + return +} + +func (m *nodeinfo) _getNodeInfo() NodeInfoPayload { return m.myNodeInfo } // Set the current node's nodeinfo -func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error { - m.myNodeInfoMutex.Lock() - defer m.myNodeInfoMutex.Unlock() +func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) (err error) { + phony.Block(m, func() { + err = m._setNodeInfo(given, privacy) + }) + return +} + +func (m *nodeinfo) _setNodeInfo(given interface{}, privacy bool) error { defaults := map[string]interface{}{ "buildname": version.BuildName(), "buildversion": version.BuildVersion(), @@ -134,9 +148,7 @@ func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error { } // Add nodeinfo into the cache for a node -func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) { - m.cacheMutex.Lock() - defer m.cacheMutex.Unlock() +func (m *nodeinfo) _addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) { m.cache[key] = nodeinfoCached{ created: time.Now(), payload: payload, @@ -144,9 +156,7 @@ func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPaylo } // Get a nodeinfo entry from the cache -func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) { - m.cacheMutex.RLock() - defer m.cacheMutex.RUnlock() +func (m *nodeinfo) _getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) { if nodeinfo, ok := m.cache[key]; ok { return nodeinfo.payload, nil } @@ -155,21 +165,33 @@ func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, err // Handles a nodeinfo request/response - called from the router func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) { + m.Act(m, func() { + m._handleNodeInfo(nodeinfo) + }) +} + +func (m *nodeinfo) _handleNodeInfo(nodeinfo *nodeinfoReqRes) { if nodeinfo.IsResponse { - m.callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo) - m.addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo) + m._callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo) + m._addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo) } else { - m.sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true) + m._sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true) } } // Send nodeinfo request or response - called from the router func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) { + m.Act(m, func() { + m._sendNodeInfo(key, coords, isResponse) + }) +} + +func (m *nodeinfo) _sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) { table := m.core.switchTable.table.Load().(lookupTable) nodeinfo := nodeinfoReqRes{ SendCoords: table.self.getCoords(), IsResponse: isResponse, - NodeInfo: m.getNodeInfo(), + NodeInfo: m._getNodeInfo(), } bs := nodeinfo.encode() shared := m.core.router.sessions.getSharedKey(&m.core.boxPriv, &key) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 64c81701..bd6eefdf 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -78,6 +78,7 @@ func (r *router) init(core *Core) { func (r *router) reconfigure() { // Reconfigure the router current := r.core.config.GetCurrent() + r.core.log.Println("Reloading NodeInfo...") if err := r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy); err != nil { r.core.log.Errorln("Error reloading NodeInfo:", err) } else { From a5bcc227cae08c6e6d3d7a27466cf1d13f605595 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 5 Jan 2020 23:43:27 +0000 Subject: [PATCH 58/81] Update go.mod/go.sum for golang.org/x dependencies (may resolve #635 possibly?) --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index c11ccdf7..4e084db5 100644 --- a/go.mod +++ b/go.mod @@ -11,9 +11,9 @@ require ( github.com/mitchellh/mapstructure v1.1.2 github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect - golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c - golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 - golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e + golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 + golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 golang.org/x/text v0.3.2 golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 diff --git a/go.sum b/go.sum index 9a61fd2c..9c4082f9 100644 --- a/go.sum +++ b/go.sum @@ -20,20 +20,20 @@ github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmF golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= -golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 h1:/W9OPMnnpmFXHYkcp2rQsbFUbRlRzfECQjmAFiOyHE8= +golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= From da9f02a381cdf35c9d801175909c4d6ac1b87abb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 6 Jan 2020 19:34:03 +0000 Subject: [PATCH 59/81] Add -p for PIE builds, preserve environment LDFLAGS --- build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build b/build index 68dac037..6b355c00 100755 --- a/build +++ b/build @@ -6,10 +6,10 @@ PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} -LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +LDFLAGS="$LDFLAGS -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" ARGS="-v" -while getopts "uaitc:l:dro:" option +while getopts "uaitc:l:dro:p" option do case "$option" in @@ -22,6 +22,7 @@ do d) ARGS="$ARGS -tags debug" DEBUG=true;; r) ARGS="$ARGS -race";; o) ARGS="$ARGS -o $OPTARG";; + p) ARGS="$ARGS -buildmode=pie";; esac done From 507c95efa9350a10e2d8a14db16536d04b34c1e9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 6 Jan 2020 19:37:24 +0000 Subject: [PATCH 60/81] Don't preserve LDFLAGS from environment after all since they are probably go-specific --- build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build b/build index 6b355c00..66f94403 100755 --- a/build +++ b/build @@ -6,7 +6,7 @@ PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} -LDFLAGS="$LDFLAGS -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" ARGS="-v" while getopts "uaitc:l:dro:p" option From c3b1a6af657e70a0c12407cc1b89ffdc17a17869 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 6 Jan 2020 18:37:43 -0600 Subject: [PATCH 61/81] some nodeinfo actor fixes and adjust search timeout --- src/yggdrasil/api.go | 1 + src/yggdrasil/nodeinfo.go | 12 ++++++------ src/yggdrasil/router.go | 2 +- src/yggdrasil/search.go | 6 ++---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 693dbd06..e2c3b97a 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -398,6 +398,7 @@ func (c *Core) GetNodeInfo(key crypto.BoxPubKey, coords []uint64, nocache bool) } }) c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false) + phony.Block(&c.router.nodeinfo, func() {}) // Wait for sendNodeInfo before starting timer timer := time.AfterFunc(6*time.Second, func() { close(response) }) defer timer.Stop() for res := range response { diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index c3e9a274..fc6250d6 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -41,7 +41,7 @@ type nodeinfoReqRes struct { // Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep // the cache/callback maps clean of stale entries func (m *nodeinfo) init(core *Core) { - m.Act(m, func() { + m.Act(nil, func() { m._init(core) }) } @@ -66,13 +66,13 @@ func (m *nodeinfo) _cleanup() { } } time.AfterFunc(time.Second*30, func() { - m.Act(m, m._cleanup) + m.Act(nil, m._cleanup) }) } // Add a callback for a nodeinfo lookup func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { - m.Act(m, func() { + m.Act(nil, func() { m._addCallback(sender, call) }) } @@ -164,8 +164,8 @@ func (m *nodeinfo) _getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, er } // Handles a nodeinfo request/response - called from the router -func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) { - m.Act(m, func() { +func (m *nodeinfo) handleNodeInfo(from phony.Actor, nodeinfo *nodeinfoReqRes) { + m.Act(from, func() { m._handleNodeInfo(nodeinfo) }) } @@ -181,7 +181,7 @@ func (m *nodeinfo) _handleNodeInfo(nodeinfo *nodeinfoReqRes) { // Send nodeinfo request or response - called from the router func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) { - m.Act(m, func() { + m.Act(nil, func() { m._sendNodeInfo(key, coords, isResponse) }) } diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index bd6eefdf..b08a12d3 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -250,5 +250,5 @@ func (r *router) _handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) { return } req.SendPermPub = *fromKey - r.nodeinfo.handleNodeInfo(&req) + r.nodeinfo.handleNodeInfo(r, &req) } diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 4708a705..92fc68b6 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -24,10 +24,8 @@ import ( // This defines the maximum number of dhtInfo that we keep track of for nodes to query in an ongoing search. const search_MAX_SEARCH_SIZE = 16 -// This defines the time after which we send a new search packet. -// Search packets are sent automatically immediately after a response is received. -// So this allows for timeouts and for long searches to become increasingly parallel. -const search_RETRY_TIME = time.Second +// This defines the time after which we time out a search (so it can restart). +const search_RETRY_TIME = 3 * time.Second // Information about an ongoing search. // Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. From 819cf234ae149b28717ae04676c0963d095123f2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 1 Feb 2020 16:32:22 +0000 Subject: [PATCH 62/81] update Wireguard library --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4e084db5..ea1f309b 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,6 @@ require ( golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 golang.org/x/text v0.3.2 - golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 + golang.zx2c4.com/wireguard v0.0.20200121 golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 ) diff --git a/go.sum b/go.sum index 9c4082f9..63638837 100644 --- a/go.sum +++ b/go.sum @@ -40,5 +40,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 h1:KxtBKNgJUQG8vwZzJKkwBGOcqp95xLu6A6KIMde1kl0= golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= +golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 h1:TreP+furSwdqoSToFrwb1S5cwxb7jhOsnwj2MsDeT+4= golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25/go.mod h1:EO8KCpT944a9CnwHJLZ1sl84FfIrY42fP/fcXUuYhKM= From 7e64f54c1ff810b538f40c5e9b5b617b4d1394fd Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 1 Feb 2020 13:58:08 -0600 Subject: [PATCH 63/81] log some info about searches and reduce search traffic (especially when things dead-end) --- src/tuntap/tun.go | 2 +- src/yggdrasil/conn.go | 2 +- src/yggdrasil/search.go | 21 ++++++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index be0ea708..53a17466 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -24,8 +24,8 @@ import ( "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/yggdrasil" "github.com/yggdrasil-network/yggdrasil-go/src/types" + "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) type MTU = types.MTU diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index dab38d3d..25605cd1 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -7,8 +7,8 @@ import ( "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/types" + "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/Arceliar/phony" ) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 92fc68b6..913b40a1 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -38,6 +38,8 @@ type searchInfo struct { visited crypto.NodeID // Closest address visited so far callback func(*sessionInfo, error) // TODO context.Context for timeout and cancellation + send uint64 // log number of requests sent + recv uint64 // log number of responses received } // This stores a map of active searches. @@ -75,6 +77,7 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba // If there is, it adds the response info to the search and triggers a new search step. // If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more. func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { + sinfo.recv++ if res == nil || sinfo.checkDHTRes(res) { // Either we don't recognize this search, or we just finished it return @@ -87,20 +90,13 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { // Adds the information from a dhtRes to an ongoing search. // Info about a node that has already been visited is not re-added to the search. // Duplicate information about nodes toVisit is deduplicated (the newest information is kept). -// The toVisit list is sorted in ascending order of keyspace distance from the destination. func (sinfo *searchInfo) addToSearch(res *dhtRes) { - // Add responses to toVisit if closer to dest than the res node - from := dhtInfo{key: res.Key, coords: res.Coords} - if dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { - // Closer to the destination, so update visited - sinfo.visited = *from.getNodeID() - } for _, info := range res.Infos { if *info.getNodeID() == sinfo.visited { // dht_ordered could return true here, but we want to skip it in this case continue } - if dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { + if dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) && *info.getNodeID() != sinfo.visited { // Response is closer to the destination sinfo.toVisit = append(sinfo.toVisit, info) } @@ -124,6 +120,7 @@ func (sinfo *searchInfo) doSearchStep() { sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) sinfo.searches.router.dht.ping(next, &sinfo.dest) sinfo.time = time.Now() + sinfo.send++ } sinfo.toVisit = sinfo.toVisit[:0] } @@ -163,7 +160,13 @@ func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callb // If the response is from the target, get/create a session, trigger a session ping, and return true. // Otherwise return false. func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { - them := crypto.GetNodeID(&res.Key) + from := dhtInfo{key: res.Key, coords: res.Coords} + if dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { + // Closer to the destination, so update visited + sinfo.searches.router.core.log.Debugln("Updating search:", sinfo.dest, *from.getNodeID(), sinfo.send, sinfo.recv) + sinfo.visited = *from.getNodeID() + } + them := from.getNodeID() var destMasked crypto.NodeID var themMasked crypto.NodeID for idx := 0; idx < crypto.NodeIDLen; idx++ { From 70659bfb91032de2c9647677800dbd32dc307f1e Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 6 Feb 2020 17:38:42 -0600 Subject: [PATCH 64/81] sort search response results before sending requests --- src/yggdrasil/search.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 913b40a1..6bd53c63 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -16,6 +16,7 @@ package yggdrasil import ( "errors" + "sort" "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -100,6 +101,11 @@ func (sinfo *searchInfo) addToSearch(res *dhtRes) { // Response is closer to the destination sinfo.toVisit = append(sinfo.toVisit, info) } + // Sort + sort.SliceStable(sinfo.toVisit, func(i, j int) bool { + // Should return true if i is closer to the destination than j + return dht_ordered(&res.Dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) + }) } } From cd856426e5b8c4e7098cab0aeb087e48df163796 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 6 Feb 2020 18:37:58 -0600 Subject: [PATCH 65/81] search timing changes --- src/yggdrasil/search.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 6bd53c63..bd55e174 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -27,6 +27,7 @@ const search_MAX_SEARCH_SIZE = 16 // This defines the time after which we time out a search (so it can restart). const search_RETRY_TIME = 3 * time.Second +const search_STEP_TIME = 100 * time.Millisecond // Information about an ongoing search. // Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. @@ -78,14 +79,20 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba // If there is, it adds the response info to the search and triggers a new search step. // If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more. func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { - sinfo.recv++ - if res == nil || sinfo.checkDHTRes(res) { - // Either we don't recognize this search, or we just finished it - return + old := sinfo.visited + if res != nil { + sinfo.recv++ + if sinfo.checkDHTRes(res) { + sinfo.searches.router.core.log.Debugln("Finished search:", sinfo.dest, sinfo.send, sinfo.recv) + return // Search finished successfully + } + // Add results to the search + sinfo.addToSearch(res) + } + if res == nil || sinfo.visited != old { + // Continue the search + sinfo.doSearchStep() } - // Add to the search and continue - sinfo.addToSearch(res) - sinfo.doSearchStep() } // Adds the information from a dhtRes to an ongoing search. @@ -104,7 +111,7 @@ func (sinfo *searchInfo) addToSearch(res *dhtRes) { // Sort sort.SliceStable(sinfo.toVisit, func(i, j int) bool { // Should return true if i is closer to the destination than j - return dht_ordered(&res.Dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) + return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) }) } } @@ -121,14 +128,14 @@ func (sinfo *searchInfo) doSearchStep() { return } // Send to the next search target - for _, next := range sinfo.toVisit { + if len(sinfo.toVisit) > 0 { + next := sinfo.toVisit[0] + sinfo.toVisit = sinfo.toVisit[1:] rq := dhtReqKey{next.key, sinfo.dest} sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) sinfo.searches.router.dht.ping(next, &sinfo.dest) - sinfo.time = time.Now() sinfo.send++ } - sinfo.toVisit = sinfo.toVisit[:0] } // If we've recently sent a ping for this search, do nothing. @@ -138,7 +145,7 @@ func (sinfo *searchInfo) continueSearch() { // In case the search dies, try to spawn another thread later // Note that this will spawn multiple parallel searches as time passes // Any that die aren't restarted, but a new one will start later - time.AfterFunc(search_RETRY_TIME, func() { + time.AfterFunc(search_STEP_TIME, func() { sinfo.searches.router.Act(nil, func() { // FIXME this keeps the search alive forever if not for the searches map, fix that newSearchInfo := sinfo.searches.searches[sinfo.dest] @@ -171,6 +178,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { // Closer to the destination, so update visited sinfo.searches.router.core.log.Debugln("Updating search:", sinfo.dest, *from.getNodeID(), sinfo.send, sinfo.recv) sinfo.visited = *from.getNodeID() + sinfo.time = time.Now() } them := from.getNodeID() var destMasked crypto.NodeID From 7c2cb9a02d406241bb92ce20702e9bca29ed5698 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 6 Feb 2020 20:21:17 -0600 Subject: [PATCH 66/81] more search fixes/updates --- src/yggdrasil/search.go | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index bd55e174..ede4547c 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -83,7 +83,6 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { if res != nil { sinfo.recv++ if sinfo.checkDHTRes(res) { - sinfo.searches.router.core.log.Debugln("Finished search:", sinfo.dest, sinfo.send, sinfo.recv) return // Search finished successfully } // Add results to the search @@ -97,22 +96,22 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { // Adds the information from a dhtRes to an ongoing search. // Info about a node that has already been visited is not re-added to the search. -// Duplicate information about nodes toVisit is deduplicated (the newest information is kept). func (sinfo *searchInfo) addToSearch(res *dhtRes) { + // Add to search for _, info := range res.Infos { - if *info.getNodeID() == sinfo.visited { - // dht_ordered could return true here, but we want to skip it in this case - continue + sinfo.toVisit = append(sinfo.toVisit, info) + } + // Sort + sort.SliceStable(sinfo.toVisit, func(i, j int) bool { + // Should return true if i is closer to the destination than j + return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) + }) + // Remove anything too far away + for idx, info := range sinfo.toVisit { + if *info.getNodeID() == sinfo.visited || !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { + sinfo.toVisit = sinfo.toVisit[:idx] + break } - if dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) && *info.getNodeID() != sinfo.visited { - // Response is closer to the destination - sinfo.toVisit = append(sinfo.toVisit, info) - } - // Sort - sort.SliceStable(sinfo.toVisit, func(i, j int) bool { - // Should return true if i is closer to the destination than j - return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) - }) } } @@ -174,7 +173,7 @@ func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callb // Otherwise return false. func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { from := dhtInfo{key: res.Key, coords: res.Coords} - if dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { + if *from.getNodeID() != sinfo.visited && dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { // Closer to the destination, so update visited sinfo.searches.router.core.log.Debugln("Updating search:", sinfo.dest, *from.getNodeID(), sinfo.send, sinfo.recv) sinfo.visited = *from.getNodeID() @@ -202,7 +201,10 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { sinfo.callback(sess, nil) } // Cleanup - delete(sinfo.searches.searches, res.Dest) + if _, isIn := sinfo.searches.searches[sinfo.dest]; isIn { + sinfo.searches.router.core.log.Debugln("Finished search:", sinfo.dest, sinfo.send, sinfo.recv) + delete(sinfo.searches.searches, res.Dest) + } } // They match, so create a session and send a sessionRequest var err error From 3faa0b28545bdf5fbc384a18ad23e79230288695 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 6 Feb 2020 20:47:53 -0600 Subject: [PATCH 67/81] deduplicate the list of nodes to visit in a search (keeping newest rumors) --- src/yggdrasil/search.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index ede4547c..7df3bdd3 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -97,8 +97,19 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { // Adds the information from a dhtRes to an ongoing search. // Info about a node that has already been visited is not re-added to the search. func (sinfo *searchInfo) addToSearch(res *dhtRes) { - // Add to search + // Get a (deduplicated) list of known nodes to check + temp := make(map[crypto.NodeID]*dhtInfo, len(sinfo.toVisit)+len(res.Infos)) + for _, info := range sinfo.toVisit { + temp[*info.getNodeID()] = info + } + // Add new results to the list for _, info := range res.Infos { + temp[*info.getNodeID()] = info + } + // Move list to toVisit + delete(temp, sinfo.visited) + sinfo.toVisit = sinfo.toVisit[:0] + for _, info := range temp { sinfo.toVisit = append(sinfo.toVisit, info) } // Sort @@ -106,9 +117,9 @@ func (sinfo *searchInfo) addToSearch(res *dhtRes) { // Should return true if i is closer to the destination than j return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) }) - // Remove anything too far away + // Remove anything too far away to be useful for idx, info := range sinfo.toVisit { - if *info.getNodeID() == sinfo.visited || !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { + if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { sinfo.toVisit = sinfo.toVisit[:idx] break } From cd9613fddc141a0d31df4975d0fb097418a15d3a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 7 Feb 2020 22:34:54 -0600 Subject: [PATCH 68/81] add some additional debug timing info and logging to dials, and fix an unnecessary delay in search startup --- src/yggdrasil/dialer.go | 6 ++++++ src/yggdrasil/search.go | 16 +++++++++------- src/yggdrasil/session.go | 2 -- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index e9da97a2..490502b4 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -65,12 +65,15 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (net. // DialByNodeIDandMask opens a session to the given node based on raw // NodeID parameters. If ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, beginning *after* the search finishes. func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *crypto.NodeID) (net.Conn, error) { + startDial := time.Now() conn := newConn(d.core, nodeID, nodeMask, nil) if err := conn.search(); err != nil { // TODO: make searches take a context, so they can be cancelled early conn.Close() return nil, err } + endSearch := time.Now() + d.core.log.Debugln("Dial searched for:", nodeID, "in time:", endSearch.Sub(startDial)) conn.session.setConn(nil, conn) var cancel context.CancelFunc if ctx == nil { @@ -80,6 +83,9 @@ func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *cryp defer cancel() select { case <-conn.session.init: + endInit := time.Now() + d.core.log.Debugln("Dial initialized session for:", nodeID, "in time:", endInit.Sub(endSearch)) + d.core.log.Debugln("Finished dial for:", nodeID, "in time:", endInit.Sub(startDial)) return conn, nil case <-ctx.Done(): conn.Close() diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 7df3bdd3..5e1967c7 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -22,9 +22,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) -// This defines the maximum number of dhtInfo that we keep track of for nodes to query in an ongoing search. -const search_MAX_SEARCH_SIZE = 16 - // This defines the time after which we time out a search (so it can restart). const search_RETRY_TIME = 3 * time.Second const search_STEP_TIME = 100 * time.Millisecond @@ -79,7 +76,7 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba // If there is, it adds the response info to the search and triggers a new search step. // If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more. func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { - old := sinfo.visited + var doStep bool if res != nil { sinfo.recv++ if sinfo.checkDHTRes(res) { @@ -87,8 +84,13 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { } // Add results to the search sinfo.addToSearch(res) + // FIXME check this elsewhere so we don't need to create a from struct + from := dhtInfo{key: res.Key, coords: res.Coords} + doStep = sinfo.visited == *from.getNodeID() + } else { + doStep = true } - if res == nil || sinfo.visited != old { + if doStep { // Continue the search sinfo.doSearchStep() } @@ -186,7 +188,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { from := dhtInfo{key: res.Key, coords: res.Coords} if *from.getNodeID() != sinfo.visited && dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { // Closer to the destination, so update visited - sinfo.searches.router.core.log.Debugln("Updating search:", sinfo.dest, *from.getNodeID(), sinfo.send, sinfo.recv) + sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, from.getNodeID(), sinfo.send, sinfo.recv) sinfo.visited = *from.getNodeID() sinfo.time = time.Now() } @@ -213,7 +215,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { } // Cleanup if _, isIn := sinfo.searches.searches[sinfo.dest]; isIn { - sinfo.searches.router.core.log.Debugln("Finished search:", sinfo.dest, sinfo.send, sinfo.recv) + sinfo.searches.router.core.log.Debugln("Finished search:", &sinfo.dest, sinfo.send, sinfo.recv) delete(sinfo.searches.searches, res.Dest) } } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 91c530d4..eaa67fd0 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -43,7 +43,6 @@ type sessionInfo struct { time time.Time // Time we last received a packet mtuTime time.Time // time myMTU was last changed pingTime time.Time // time the first ping was sent since the last received packet - pingSend time.Time // time the last ping was sent coords []byte // coords of destination reset bool // reset if coords change tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation @@ -197,7 +196,6 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.time = now sinfo.mtuTime = now sinfo.pingTime = now - sinfo.pingSend = now sinfo.init = make(chan struct{}) sinfo.cancel = util.NewCancellation() higher := false From d7d0c2629c1e9f800d5e1e587ec73d118610a0fb Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 8 Feb 2020 17:04:00 -0600 Subject: [PATCH 69/81] don't deduplicate search responses, but limit the max number of nodes handled per response --- src/yggdrasil/search.go | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 5e1967c7..8d534d3c 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -25,6 +25,7 @@ import ( // This defines the time after which we time out a search (so it can restart). const search_RETRY_TIME = 3 * time.Second const search_STEP_TIME = 100 * time.Millisecond +const search_MAX_RES_SIZE = 16 // Information about an ongoing search. // Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. @@ -99,26 +100,22 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { // Adds the information from a dhtRes to an ongoing search. // Info about a node that has already been visited is not re-added to the search. func (sinfo *searchInfo) addToSearch(res *dhtRes) { - // Get a (deduplicated) list of known nodes to check - temp := make(map[crypto.NodeID]*dhtInfo, len(sinfo.toVisit)+len(res.Infos)) - for _, info := range sinfo.toVisit { - temp[*info.getNodeID()] = info + // Used in sortng below + sortFunc := func(i, j int) bool { + // Should return true if i is closer to the destination than j + return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) } - // Add new results to the list + // Limit maximum number of results (mitigates DoS where nodes return a large number of bad results) + if len(res.Infos) > search_MAX_RES_SIZE { + sort.SliceStable(res.Infos, sortFunc) + res.Infos = res.Infos[:search_MAX_RES_SIZE] + } + // Append (without deduplication) to list of nodes to try for _, info := range res.Infos { - temp[*info.getNodeID()] = info - } - // Move list to toVisit - delete(temp, sinfo.visited) - sinfo.toVisit = sinfo.toVisit[:0] - for _, info := range temp { sinfo.toVisit = append(sinfo.toVisit, info) } // Sort - sort.SliceStable(sinfo.toVisit, func(i, j int) bool { - // Should return true if i is closer to the destination than j - return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) - }) + sort.SliceStable(sinfo.toVisit, sortFunc) // Remove anything too far away to be useful for idx, info := range sinfo.toVisit { if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { @@ -147,6 +144,7 @@ func (sinfo *searchInfo) doSearchStep() { sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) sinfo.searches.router.dht.ping(next, &sinfo.dest) sinfo.send++ + sinfo.time = time.Now() } } @@ -190,7 +188,6 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { // Closer to the destination, so update visited sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, from.getNodeID(), sinfo.send, sinfo.recv) sinfo.visited = *from.getNodeID() - sinfo.time = time.Now() } them := from.getNodeID() var destMasked crypto.NodeID From d0e6846173ad255460e8521a8dc97beeb7d13e0b Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 8 Feb 2020 20:15:48 -0600 Subject: [PATCH 70/81] work in progress to make searches use parallel threads per response, so one malicious node doesn't block progress from honest ones --- src/yggdrasil/conn.go | 9 +-- src/yggdrasil/search.go | 136 ++++++++++++++++++++++------------------ 2 files changed, 80 insertions(+), 65 deletions(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 25605cd1..7091cf7b 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -130,8 +130,8 @@ func (c *Conn) search() error { close(done) } } - sinfo := c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) - sinfo.continueSearch() + sinfo, infos := c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) + sinfo.continueSearch(infos) } else { err = errors.New("search already exists") close(done) @@ -152,10 +152,11 @@ func (c *Conn) doSearch() { if !isIn { // Nothing was found, so create a new search searchCompleted := func(sinfo *sessionInfo, e error) {} - sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) + var infos []*dhtInfo + sinfo, infos = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) c.core.log.Debugf("%s DHT search started: %p", c.String(), sinfo) // Start the search - sinfo.continueSearch() + sinfo.continueSearch(infos) } } c.core.router.Act(c.session, routerWork) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 8d534d3c..ce78fd1f 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -25,7 +25,6 @@ import ( // This defines the time after which we time out a search (so it can restart). const search_RETRY_TIME = 3 * time.Second const search_STEP_TIME = 100 * time.Millisecond -const search_MAX_RES_SIZE = 16 // Information about an ongoing search. // Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. @@ -34,7 +33,6 @@ type searchInfo struct { dest crypto.NodeID mask crypto.NodeID time time.Time - toVisit []*dhtInfo visited crypto.NodeID // Closest address visited so far callback func(*sessionInfo, error) // TODO context.Context for timeout and cancellation @@ -77,81 +75,62 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba // If there is, it adds the response info to the search and triggers a new search step. // If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more. func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { - var doStep bool if res != nil { sinfo.recv++ if sinfo.checkDHTRes(res) { return // Search finished successfully } - // Add results to the search - sinfo.addToSearch(res) - // FIXME check this elsewhere so we don't need to create a from struct - from := dhtInfo{key: res.Key, coords: res.Coords} - doStep = sinfo.visited == *from.getNodeID() - } else { - doStep = true - } - if doStep { - // Continue the search - sinfo.doSearchStep() + // Use results to start an additional search thread + infos := append([]*dhtInfo(nil), res.Infos...) + infos = sinfo.getAllowedInfos(infos) + sinfo.continueSearch(infos) } } -// Adds the information from a dhtRes to an ongoing search. -// Info about a node that has already been visited is not re-added to the search. -func (sinfo *searchInfo) addToSearch(res *dhtRes) { - // Used in sortng below - sortFunc := func(i, j int) bool { - // Should return true if i is closer to the destination than j - return dht_ordered(&sinfo.dest, sinfo.toVisit[i].getNodeID(), sinfo.toVisit[j].getNodeID()) - } - // Limit maximum number of results (mitigates DoS where nodes return a large number of bad results) - if len(res.Infos) > search_MAX_RES_SIZE { - sort.SliceStable(res.Infos, sortFunc) - res.Infos = res.Infos[:search_MAX_RES_SIZE] - } - // Append (without deduplication) to list of nodes to try - for _, info := range res.Infos { - sinfo.toVisit = append(sinfo.toVisit, info) - } - // Sort - sort.SliceStable(sinfo.toVisit, sortFunc) - // Remove anything too far away to be useful - for idx, info := range sinfo.toVisit { - if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { - sinfo.toVisit = sinfo.toVisit[:idx] - break - } - } -} - -// If there are no nodes left toVisit, then this cleans up the search. +// If there has been no response in too long, then this cleans up the search. // Otherwise, it pops the closest node to the destination (in keyspace) off of the toVisit list and sends a dht ping. -func (sinfo *searchInfo) doSearchStep() { - if len(sinfo.toVisit) == 0 { - if time.Since(sinfo.time) > search_RETRY_TIME { - // Dead end and no response in too long, do cleanup - delete(sinfo.searches.searches, sinfo.dest) - sinfo.callback(nil, errors.New("search reached dead end")) - } +func (sinfo *searchInfo) doSearchStep(infos []*dhtInfo) { + if time.Since(sinfo.time) > search_RETRY_TIME { + // Dead end and no response in too long, do cleanup + // FIXME we should really let all the parallel search threads exist when info is empty, and then delete when no threads are left, instead of keeping them all around until things time out or exit successfully + delete(sinfo.searches.searches, sinfo.dest) + sinfo.callback(nil, errors.New("search reached dead end")) return } - // Send to the next search target - if len(sinfo.toVisit) > 0 { - next := sinfo.toVisit[0] - sinfo.toVisit = sinfo.toVisit[1:] + if len(infos) > 0 { + // Send to the next search target + next := infos[0] rq := dhtReqKey{next.key, sinfo.dest} sinfo.searches.router.dht.addCallback(&rq, sinfo.handleDHTRes) sinfo.searches.router.dht.ping(next, &sinfo.dest) sinfo.send++ - sinfo.time = time.Now() } } +// Get a list of search targets that are close enough to the destination to try +// Requires an initial list as input +func (sinfo *searchInfo) getAllowedInfos(infos []*dhtInfo) []*dhtInfo { + sort.SliceStable(infos, func(i, j int) bool { + // Should return true if i is closer to the destination than j + return dht_ordered(&sinfo.dest, infos[i].getNodeID(), infos[j].getNodeID()) + }) + // Remove anything too far away to be useful + for idx, info := range infos { + if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { + infos = infos[:idx] + break + } + } + return infos +} + // If we've recently sent a ping for this search, do nothing. // Otherwise, doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME. -func (sinfo *searchInfo) continueSearch() { - sinfo.doSearchStep() +func (sinfo *searchInfo) continueSearch(infos []*dhtInfo) { + sinfo.doSearchStep(infos) + if len(infos) > 0 { + infos = infos[1:] // Remove the node we just tried + } // In case the search dies, try to spawn another thread later // Note that this will spawn multiple parallel searches as time passes // Any that die aren't restarted, but a new one will start later @@ -162,21 +141,55 @@ func (sinfo *searchInfo) continueSearch() { if newSearchInfo != sinfo { return } - sinfo.continueSearch() + // Get good infos here instead of at the top, to make sure we can always start things off with a continueSearch call to ourself + infos = sinfo.getAllowedInfos(infos) + sinfo.continueSearch(infos) }) }) } +// Initially start a search +func (sinfo *searchInfo) startSearch() { + loc := sinfo.searches.router.core.switchTable.getLocator() + var infos []*dhtInfo + infos = append(infos, &dhtInfo{ + key: sinfo.searches.router.core.boxPub, + coords: loc.getCoords(), + }) + // Start the search by asking ourself, useful if we're the destination + sinfo.continueSearch(infos) + // Start a timer to clean up the search if everything times out + var cleanupFunc func() + cleanupFunc = func() { + sinfo.searches.router.Act(nil, func() { + // FIXME this keeps the search alive forever if not for the searches map, fix that + newSearchInfo := sinfo.searches.searches[sinfo.dest] + if newSearchInfo != sinfo { + return + } + elapsed := time.Since(sinfo.time) + if elapsed > search_RETRY_TIME { + // cleanup + delete(sinfo.searches.searches, sinfo.dest) + sinfo.callback(nil, errors.New("search reached dead end")) + return + } + time.AfterFunc(search_RETRY_TIME - elapsed, cleanupFunc) + }) + } +} + // Calls create search, and initializes the iterative search parts of the struct before returning it. -func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo { +func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) (*searchInfo, []*dhtInfo) { sinfo := s.createSearch(dest, mask, callback) sinfo.visited = s.router.dht.nodeID loc := s.router.core.switchTable.getLocator() - sinfo.toVisit = append(sinfo.toVisit, &dhtInfo{ + var infos []*dhtInfo + infos = append(infos, &dhtInfo{ key: s.router.core.boxPub, coords: loc.getCoords(), }) // Start the search by asking ourself, useful if we're the destination - return sinfo + return sinfo, infos } // Checks if a dhtRes is good (called by handleDHTRes). @@ -188,6 +201,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { // Closer to the destination, so update visited sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, from.getNodeID(), sinfo.send, sinfo.recv) sinfo.visited = *from.getNodeID() + sinfo.time = time.Now() } them := from.getNodeID() var destMasked crypto.NodeID From 8e05c6c6a740c45a0a4c55dd7f815745dd5b47ba Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 8 Feb 2020 20:26:37 -0600 Subject: [PATCH 71/81] better search cleanup, but needs more testing to make sure it really works --- src/yggdrasil/conn.go | 9 +++--- src/yggdrasil/search.go | 67 +++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 7091cf7b..f6229036 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -130,8 +130,8 @@ func (c *Conn) search() error { close(done) } } - sinfo, infos := c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) - sinfo.continueSearch(infos) + sinfo := c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) + sinfo.startSearch() } else { err = errors.New("search already exists") close(done) @@ -152,11 +152,10 @@ func (c *Conn) doSearch() { if !isIn { // Nothing was found, so create a new search searchCompleted := func(sinfo *sessionInfo, e error) {} - var infos []*dhtInfo - sinfo, infos = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) + sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) c.core.log.Debugf("%s DHT search started: %p", c.String(), sinfo) // Start the search - sinfo.continueSearch(infos) + sinfo.startSearch() } } c.core.router.Act(c.session, routerWork) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index ce78fd1f..bf8c7819 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -83,20 +83,15 @@ func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { // Use results to start an additional search thread infos := append([]*dhtInfo(nil), res.Infos...) infos = sinfo.getAllowedInfos(infos) - sinfo.continueSearch(infos) + if len(infos) > 0 { + sinfo.continueSearch(infos) + } } } // If there has been no response in too long, then this cleans up the search. // Otherwise, it pops the closest node to the destination (in keyspace) off of the toVisit list and sends a dht ping. func (sinfo *searchInfo) doSearchStep(infos []*dhtInfo) { - if time.Since(sinfo.time) > search_RETRY_TIME { - // Dead end and no response in too long, do cleanup - // FIXME we should really let all the parallel search threads exist when info is empty, and then delete when no threads are left, instead of keeping them all around until things time out or exit successfully - delete(sinfo.searches.searches, sinfo.dest) - sinfo.callback(nil, errors.New("search reached dead end")) - return - } if len(infos) > 0 { // Send to the next search target next := infos[0] @@ -124,16 +119,12 @@ func (sinfo *searchInfo) getAllowedInfos(infos []*dhtInfo) []*dhtInfo { return infos } -// If we've recently sent a ping for this search, do nothing. -// Otherwise, doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME. +// Run doSearchStep and schedule another continueSearch to happen after search_RETRY_TIME. +// Must not be called with an empty list of infos func (sinfo *searchInfo) continueSearch(infos []*dhtInfo) { sinfo.doSearchStep(infos) - if len(infos) > 0 { - infos = infos[1:] // Remove the node we just tried - } - // In case the search dies, try to spawn another thread later - // Note that this will spawn multiple parallel searches as time passes - // Any that die aren't restarted, but a new one will start later + infos = infos[1:] // Remove the node we just tried + // In case there's no response, try the next node in infos later time.AfterFunc(search_STEP_TIME, func() { sinfo.searches.router.Act(nil, func() { // FIXME this keeps the search alive forever if not for the searches map, fix that @@ -143,7 +134,9 @@ func (sinfo *searchInfo) continueSearch(infos []*dhtInfo) { } // Get good infos here instead of at the top, to make sure we can always start things off with a continueSearch call to ourself infos = sinfo.getAllowedInfos(infos) - sinfo.continueSearch(infos) + if len(infos) > 0 { + sinfo.continueSearch(infos) + } }) }) } @@ -156,40 +149,34 @@ func (sinfo *searchInfo) startSearch() { key: sinfo.searches.router.core.boxPub, coords: loc.getCoords(), }) - // Start the search by asking ourself, useful if we're the destination - sinfo.continueSearch(infos) - // Start a timer to clean up the search if everything times out - var cleanupFunc func() - cleanupFunc = func() { + // Start the search by asking ourself, useful if we're the destination + sinfo.continueSearch(infos) + // Start a timer to clean up the search if everything times out + var cleanupFunc func() + cleanupFunc = func() { sinfo.searches.router.Act(nil, func() { // FIXME this keeps the search alive forever if not for the searches map, fix that newSearchInfo := sinfo.searches.searches[sinfo.dest] if newSearchInfo != sinfo { return } - elapsed := time.Since(sinfo.time) - if elapsed > search_RETRY_TIME { - // cleanup - delete(sinfo.searches.searches, sinfo.dest) - sinfo.callback(nil, errors.New("search reached dead end")) - return - } - time.AfterFunc(search_RETRY_TIME - elapsed, cleanupFunc) - }) - } + elapsed := time.Since(sinfo.time) + if elapsed > search_RETRY_TIME { + // cleanup + delete(sinfo.searches.searches, sinfo.dest) + sinfo.callback(nil, errors.New("search reached dead end")) + return + } + time.AfterFunc(search_RETRY_TIME-elapsed, cleanupFunc) + }) + } } // Calls create search, and initializes the iterative search parts of the struct before returning it. -func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) (*searchInfo, []*dhtInfo) { +func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo { sinfo := s.createSearch(dest, mask, callback) sinfo.visited = s.router.dht.nodeID - loc := s.router.core.switchTable.getLocator() - var infos []*dhtInfo - infos = append(infos, &dhtInfo{ - key: s.router.core.boxPub, - coords: loc.getCoords(), - }) // Start the search by asking ourself, useful if we're the destination - return sinfo, infos + return sinfo } // Checks if a dhtRes is good (called by handleDHTRes). From 657777881be5d8db66b8db61acd0d256ef1c312a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 8 Feb 2020 20:33:35 -0600 Subject: [PATCH 72/81] actually schedule the search cleanup code to run --- src/yggdrasil/search.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index bf8c7819..584a056d 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -170,6 +170,7 @@ func (sinfo *searchInfo) startSearch() { time.AfterFunc(search_RETRY_TIME-elapsed, cleanupFunc) }) } + time.AfterFunc(search_RETRY_TIME, cleanupFunc) } // Calls create search, and initializes the iterative search parts of the struct before returning it. From c107f891d29246b219f0eb77a846b6a57ac4c381 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:09:24 +0000 Subject: [PATCH 73/81] Implement pubkeys in API functions --- src/crypto/crypto.go | 10 ++++++++++ src/yggdrasil/conn.go | 8 +++++--- src/yggdrasil/dialer.go | 38 ++++++++++++++++++++++++++++++++------ src/yggdrasil/listener.go | 4 ++-- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 3a6e02d1..92adf890 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -194,6 +194,16 @@ type BoxSharedKey [BoxSharedKeyLen]byte // BoxNonce is the nonce used in NaCl-like crypto "box" operations (curve25519+xsalsa20+poly1305), and must not be reused for different messages encrypted using the same BoxSharedKey. type BoxNonce [BoxNonceLen]byte +// String returns a string representation of the "box" key. +func (k BoxPubKey) String() string { + return hex.EncodeToString(k[:]) +} + +// Network returns "pubkey" for "box" keys. +func (n BoxPubKey) Network() string { + return "pubkey" +} + // NewBoxKeys generates a new pair of public/private crypto box keys. func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) { pubBytes, privBytes, err := box.GenerateKey(rand.Reader) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index f6229036..078dca35 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -353,13 +353,15 @@ func (c *Conn) Close() (err error) { // LocalAddr returns the complete node ID of the local side of the connection. // This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { - return crypto.GetNodeID(&c.core.boxPub) + return c.core.boxPub } // RemoteAddr returns the complete node ID of the remote side of the connection. func (c *Conn) RemoteAddr() net.Addr { - // RemoteAddr is set during the dial or accept, and isn't changed, so it's safe to access directly - return c.nodeID + if c.session != nil { + return c.session.theirPermPub + } + return nil } // SetDeadline is equivalent to calling both SetReadDeadline and diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index 490502b4..293f6d0b 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -17,19 +17,32 @@ type Dialer struct { core *Core } -// Dial opens a session to the given node. The first parameter should be "nodeid" -// and the second parameter should contain a hexadecimal representation of the -// target node ID. It uses DialContext internally. +// Dial opens a session to the given node. The first parameter should be +// "pubkey" or "nodeid" and the second parameter should contain a hexadecimal +// representation of the target. It uses DialContext internally. func (d *Dialer) Dial(network, address string) (net.Conn, error) { return d.DialContext(nil, network, address) } -// DialContext is used internally by Dial, and should only be used with a context that includes a timeout. It uses DialByNodeIDandMask internally. +// DialContext is used internally by Dial, and should only be used with a +// context that includes a timeout. It uses DialByNodeIDandMask internally when +// the network is "nodeid", or DialByPublicKey when the network is "pubkey". func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { var nodeID crypto.NodeID var nodeMask crypto.NodeID // Process switch network { + case "pubkey": + dest, err := hex.DecodeString(address) + if err != nil { + return nil, err + } + if len(dest) != crypto.BoxPubKeyLen { + return nil, errors.New("invalid key length supplied") + } + var pubKey crypto.BoxPubKey + copy(pubKey[:], dest) + return d.DialByPublicKey(ctx, &pubKey) case "nodeid": // A node ID was provided - we don't need to do anything special with it if tokens := strings.Split(address, "/"); len(tokens) == 2 { @@ -62,8 +75,9 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (net. } } -// DialByNodeIDandMask opens a session to the given node based on raw -// NodeID parameters. If ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, beginning *after* the search finishes. +// DialByNodeIDandMask opens a session to the given node based on raw NodeID +// parameters. If ctx is nil or has no timeout, then a default timeout of 6 +// seconds will apply, beginning *after* the search finishes. func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *crypto.NodeID) (net.Conn, error) { startDial := time.Now() conn := newConn(d.core, nodeID, nodeMask, nil) @@ -92,3 +106,15 @@ func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *cryp return nil, errors.New("session handshake timeout") } } + +// DialByPublicKey opens a session to the given node based on the public key. If +// ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, +// beginning *after* the search finishes. +func (d *Dialer) DialByPublicKey(ctx context.Context, pubKey *crypto.BoxPubKey) (net.Conn, error) { + nodeID := crypto.GetNodeID(pubKey) + var nodeMask crypto.NodeID + for i := range nodeMask { + nodeMask[i] = 0xFF + } + return d.DialByNodeIDandMask(ctx, nodeID, &nodeMask) +} diff --git a/src/yggdrasil/listener.go b/src/yggdrasil/listener.go index 63830970..1b908b42 100644 --- a/src/yggdrasil/listener.go +++ b/src/yggdrasil/listener.go @@ -39,7 +39,7 @@ func (l *Listener) Close() (err error) { return nil } -// Addr is not implemented for this type yet +// Addr returns the address of the listener func (l *Listener) Addr() net.Addr { - return nil + return l.core.boxPub } From 63936c11b500bb06285980591aeba590f86661d6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:21:58 +0000 Subject: [PATCH 74/81] Update tuntap module, return pointers --- src/tuntap/tun.go | 3 ++- src/yggdrasil/conn.go | 4 ++-- src/yggdrasil/listener.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 53a17466..656ecca7 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -251,7 +251,8 @@ func (tun *TunAdapter) _wrap(conn *yggdrasil.Conn) (c *tunConn, err error) { } c = &s // Get the remote address and subnet of the other side - remoteNodeID := conn.RemoteAddr().(*crypto.NodeID) + remotePubKey := conn.RemoteAddr().(*crypto.BoxPubKey) + remoteNodeID := crypto.GetNodeID(remotePubKey) s.addr = *address.AddrForNodeID(remoteNodeID) s.snet = *address.SubnetForNodeID(remoteNodeID) // Work out if this is already a destination we already know about diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 078dca35..d18f196d 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -353,13 +353,13 @@ func (c *Conn) Close() (err error) { // LocalAddr returns the complete node ID of the local side of the connection. // This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { - return c.core.boxPub + return &c.core.boxPub } // RemoteAddr returns the complete node ID of the remote side of the connection. func (c *Conn) RemoteAddr() net.Addr { if c.session != nil { - return c.session.theirPermPub + return &c.session.theirPermPub } return nil } diff --git a/src/yggdrasil/listener.go b/src/yggdrasil/listener.go index 1b908b42..74ef3e88 100644 --- a/src/yggdrasil/listener.go +++ b/src/yggdrasil/listener.go @@ -41,5 +41,5 @@ func (l *Listener) Close() (err error) { // Addr returns the address of the listener func (l *Listener) Addr() net.Addr { - return l.core.boxPub + return &l.core.boxPub } From d16505e4177926b6dd85bd36b9c3525efeda70a0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:26:18 +0000 Subject: [PATCH 75/81] Update CKR --- src/tuntap/conn.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index aa149063..24ea5ef3 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -92,8 +92,7 @@ func (s *tunConn) _read(bs []byte) (err error) { // The destination address isn't in our CKR allowed range skip = true } else if key, err := s.tun.ckr.getPublicKeyForAddress(srcAddr, addrlen); err == nil { - srcNodeID := crypto.GetNodeID(&key) - if *s.conn.RemoteAddr().(*crypto.NodeID) == *srcNodeID { + if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { // This is the one allowed CKR case, where source and destination addresses are both good } else { // The CKR key associated with this address doesn't match the sender's NodeID @@ -169,8 +168,7 @@ func (s *tunConn) _write(bs []byte) (err error) { // The source address isn't in our CKR allowed range skip = true } else if key, err := s.tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { - dstNodeID := crypto.GetNodeID(&key) - if *s.conn.RemoteAddr().(*crypto.NodeID) == *dstNodeID { + if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { // This is the one allowed CKR case, where source and destination addresses are both good } else { // The CKR key associated with this address doesn't match the sender's NodeID From 6b0b7046453ddecdd22da86bc1f98e8435f0d1ec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:30:47 +0000 Subject: [PATCH 76/81] Update comments --- src/yggdrasil/conn.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index d18f196d..37049c73 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -350,13 +350,14 @@ func (c *Conn) Close() (err error) { return } -// LocalAddr returns the complete node ID of the local side of the connection. -// This is always going to return your own node's node ID. +// LocalAddr returns the complete public key of the local side of the +// connection. This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { return &c.core.boxPub } -// RemoteAddr returns the complete node ID of the remote side of the connection. +// RemoteAddr returns the complete public key of the remote side of the +// connection. func (c *Conn) RemoteAddr() net.Addr { if c.session != nil { return &c.session.theirPermPub From 429189d11dff8f4709ea1910f81f1e3420474037 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:44:20 +0000 Subject: [PATCH 77/81] Use 'curve25519' instead of 'pubkey' --- src/crypto/crypto.go | 4 ++-- src/yggdrasil/dialer.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 92adf890..211a0e54 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -199,9 +199,9 @@ func (k BoxPubKey) String() string { return hex.EncodeToString(k[:]) } -// Network returns "pubkey" for "box" keys. +// Network returns "curve25519" for "box" keys. func (n BoxPubKey) Network() string { - return "pubkey" + return "curve25519" } // NewBoxKeys generates a new pair of public/private crypto box keys. diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index 293f6d0b..9f58d305 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -18,21 +18,21 @@ type Dialer struct { } // Dial opens a session to the given node. The first parameter should be -// "pubkey" or "nodeid" and the second parameter should contain a hexadecimal -// representation of the target. It uses DialContext internally. +// "curve25519" or "nodeid" and the second parameter should contain a +// hexadecimal representation of the target. It uses DialContext internally. func (d *Dialer) Dial(network, address string) (net.Conn, error) { return d.DialContext(nil, network, address) } // DialContext is used internally by Dial, and should only be used with a // context that includes a timeout. It uses DialByNodeIDandMask internally when -// the network is "nodeid", or DialByPublicKey when the network is "pubkey". +// the network is "nodeid", or DialByPublicKey when the network is "curve25519". func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { var nodeID crypto.NodeID var nodeMask crypto.NodeID // Process switch network { - case "pubkey": + case "curve25519": dest, err := hex.DecodeString(address) if err != nil { return nil, err From 6c731c4efc9827115f6404c7ebcdf0c1a5d496c1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:45:11 +0000 Subject: [PATCH 78/81] Fix comment on LocalAddr --- src/yggdrasil/conn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 37049c73..eef57683 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -351,7 +351,7 @@ func (c *Conn) Close() (err error) { } // LocalAddr returns the complete public key of the local side of the -// connection. This is always going to return your own node's node ID. +// connection. This is always going to return your own node's public key. func (c *Conn) LocalAddr() net.Addr { return &c.core.boxPub } From 471fcd7fdfbb316922c0bdbb7b020d54218abcd2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:57:05 +0000 Subject: [PATCH 79/81] Update doc.go dial example --- src/yggdrasil/doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/doc.go b/src/yggdrasil/doc.go index 44d39b66..9b9dd738 100644 --- a/src/yggdrasil/doc.go +++ b/src/yggdrasil/doc.go @@ -113,9 +113,9 @@ a Dialer: // ... } -You can then dial using the 16-byte node ID in hexadecimal format, for example: +You can then dial using the node's public key in hexadecimal format, for example: - conn, err := dialer.Dial("nodeid", "24a58cfce691ec016b0f698f7be1bee983cea263781017e99ad3ef62b4ef710a45d6c1a072c5ce46131bd574b78818c9957042cafeeed13966f349e94eb771bf") + conn, err := dialer.Dial("curve25519", "55071be281f50d0abbda63aadc59755624280c44b2f1f47684317aa4e0325604") if err != nil { // ... } From 012bd9195dab228064d3135fec510086826b5095 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 17 Feb 2020 19:49:03 +0000 Subject: [PATCH 80/81] Update CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3950f7b..ab3e9666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.13] - 2020-02-21 +### Added +- Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows +- Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver) +- NodeInfo code is now actorised, should be more reliable +- The DHT now tries to store the two closest nodes in either direction instead of one, such that if a node goes offline, the replacement is already known +- The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID + +### Changed +- The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified +- DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time +- DHT search results are now sorted +- The systemd service now handles configuration generation in a different unit +- The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses + +### Fixed +- The multicast code no longer panics when shutting down the node +- A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed +- A bug resulting in incorrect idle notifications in the switch should now be fixed +- MTUs are now using a common datatype throughout the codebase + +### Removed +- TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config! +- NetBSD support has been removed until the Wireguard TUN package supports NetBSD + ## [0.3.12] - 2019-11-24 ### Added - New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` From d41da9a97f9bc42fbe75a58dc979289a9ba71d1a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 20 Feb 2020 23:22:42 +0000 Subject: [PATCH 81/81] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c02151f4..dcc9bd91 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ some of the below: - Windows - FreeBSD - OpenBSD -- NetBSD - OpenWrt Please see our [Platforms](https://yggdrasil-network.github.io/platforms.html) pages for more