mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-25 23:01:38 +00:00
Merge pull request #10 from yggdrasil-network/develop
Branch Develop: Base to Fork
This commit is contained in:
commit
6e196b9f81
@ -6,16 +6,14 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
|
||||||
|
"github.com/gologme/log"
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
"github.com/kardianos/minwinsvc"
|
"github.com/kardianos/minwinsvc"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
@ -31,6 +29,119 @@ type node struct {
|
|||||||
core Core
|
core Core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig {
|
||||||
|
// Use a configuration file. If -useconf, the configuration will be read
|
||||||
|
// from stdin. If -useconffile, the configuration will be read from the
|
||||||
|
// filesystem.
|
||||||
|
var conf []byte
|
||||||
|
var err error
|
||||||
|
if *useconffile != "" {
|
||||||
|
// Read the file from the filesystem
|
||||||
|
conf, err = ioutil.ReadFile(*useconffile)
|
||||||
|
} else {
|
||||||
|
// Read the file from stdin.
|
||||||
|
conf, err = ioutil.ReadAll(os.Stdin)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
||||||
|
// throwing everywhere when it's converting things into UTF-16 for the hell
|
||||||
|
// of it - remove it and decode back down into UTF-8. This is necessary
|
||||||
|
// because hjson doesn't know what to do with UTF-16 and will panic
|
||||||
|
if bytes.Compare(conf[0:2], []byte{0xFF, 0xFE}) == 0 ||
|
||||||
|
bytes.Compare(conf[0:2], []byte{0xFE, 0xFF}) == 0 {
|
||||||
|
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||||
|
decoder := utf.NewDecoder()
|
||||||
|
conf, err = decoder.Bytes(conf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate a new configuration - this gives us a set of sane defaults -
|
||||||
|
// then parse the configuration we loaded above on top of it. The effect
|
||||||
|
// of this is that any configuration item that is missing from the provided
|
||||||
|
// configuration will use a sane default.
|
||||||
|
cfg := config.GenerateConfig(false)
|
||||||
|
var dat map[string]interface{}
|
||||||
|
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
confJson, err := json.Marshal(dat)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
json.Unmarshal(confJson, &cfg)
|
||||||
|
// For now we will do a little bit to help the user adjust their
|
||||||
|
// configuration to match the new configuration format, as some of the key
|
||||||
|
// names have changed recently.
|
||||||
|
changes := map[string]string{
|
||||||
|
"Multicast": "",
|
||||||
|
"LinkLocal": "MulticastInterfaces",
|
||||||
|
"BoxPub": "EncryptionPublicKey",
|
||||||
|
"BoxPriv": "EncryptionPrivateKey",
|
||||||
|
"SigPub": "SigningPublicKey",
|
||||||
|
"SigPriv": "SigningPrivateKey",
|
||||||
|
"AllowedBoxPubs": "AllowedEncryptionPublicKeys",
|
||||||
|
}
|
||||||
|
// Loop over the mappings aove and see if we have anything to fix.
|
||||||
|
for from, to := range changes {
|
||||||
|
if _, ok := dat[from]; ok {
|
||||||
|
if to == "" {
|
||||||
|
if !*normaliseconf {
|
||||||
|
log.Println("Warning: Deprecated config option", from, "- please remove")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !*normaliseconf {
|
||||||
|
log.Println("Warning: Deprecated config option", from, "- please rename to", to)
|
||||||
|
}
|
||||||
|
// If the configuration file doesn't already contain a line with the
|
||||||
|
// new name then set it to the old value. This makes sure that we
|
||||||
|
// don't overwrite something that was put there intentionally.
|
||||||
|
if _, ok := dat[to]; !ok {
|
||||||
|
dat[to] = dat[from]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check to see if the peers are in a parsable format, if not then default
|
||||||
|
// them to the TCP scheme
|
||||||
|
if peers, ok := dat["Peers"].([]interface{}); ok {
|
||||||
|
for index, peer := range peers {
|
||||||
|
uri := peer.(string)
|
||||||
|
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(uri, "tcp:") {
|
||||||
|
uri = uri[4:]
|
||||||
|
}
|
||||||
|
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now do the same with the interface peers
|
||||||
|
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
|
||||||
|
for intf, peers := range interfacepeers {
|
||||||
|
for index, peer := range peers.([]interface{}) {
|
||||||
|
uri := peer.(string)
|
||||||
|
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(uri, "tcp:") {
|
||||||
|
uri = uri[4:]
|
||||||
|
}
|
||||||
|
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||||
|
// we generated above.
|
||||||
|
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
// Generates a new configuration and returns it in HJSON format. This is used
|
// Generates a new configuration and returns it in HJSON format. This is used
|
||||||
// with -genconf.
|
// with -genconf.
|
||||||
func doGenconf(isjson bool) string {
|
func doGenconf(isjson bool) string {
|
||||||
@ -58,9 +169,11 @@ func main() {
|
|||||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
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)")
|
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||||
version := flag.Bool("version", false, "prints the version of this build")
|
version := 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")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var cfg *nodeConfig
|
var cfg *nodeConfig
|
||||||
|
var err error
|
||||||
switch {
|
switch {
|
||||||
case *version:
|
case *version:
|
||||||
fmt.Println("Build name:", yggdrasil.GetBuildName())
|
fmt.Println("Build name:", yggdrasil.GetBuildName())
|
||||||
@ -71,114 +184,8 @@ func main() {
|
|||||||
// port numbers, and will use an automatically selected TUN/TAP interface.
|
// port numbers, and will use an automatically selected TUN/TAP interface.
|
||||||
cfg = config.GenerateConfig(true)
|
cfg = config.GenerateConfig(true)
|
||||||
case *useconffile != "" || *useconf:
|
case *useconffile != "" || *useconf:
|
||||||
// Use a configuration file. If -useconf, the configuration will be read
|
// Read the configuration from either stdin or from the filesystem
|
||||||
// from stdin. If -useconffile, the configuration will be read from the
|
cfg = readConfig(useconf, useconffile, normaliseconf)
|
||||||
// filesystem.
|
|
||||||
var configjson []byte
|
|
||||||
var err error
|
|
||||||
if *useconffile != "" {
|
|
||||||
// Read the file from the filesystem
|
|
||||||
configjson, err = ioutil.ReadFile(*useconffile)
|
|
||||||
} else {
|
|
||||||
// Read the file from stdin.
|
|
||||||
configjson, err = ioutil.ReadAll(os.Stdin)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
|
||||||
// throwing everywhere when it's converting things into UTF-16 for the hell
|
|
||||||
// of it - remove it and decode back down into UTF-8. This is necessary
|
|
||||||
// because hjson doesn't know what to do with UTF-16 and will panic
|
|
||||||
if bytes.Compare(configjson[0:2], []byte{0xFF, 0xFE}) == 0 ||
|
|
||||||
bytes.Compare(configjson[0:2], []byte{0xFE, 0xFF}) == 0 {
|
|
||||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
|
||||||
decoder := utf.NewDecoder()
|
|
||||||
configjson, err = decoder.Bytes(configjson)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Generate a new configuration - this gives us a set of sane defaults -
|
|
||||||
// then parse the configuration we loaded above on top of it. The effect
|
|
||||||
// of this is that any configuration item that is missing from the provided
|
|
||||||
// configuration will use a sane default.
|
|
||||||
cfg = config.GenerateConfig(false)
|
|
||||||
var dat map[string]interface{}
|
|
||||||
if err := hjson.Unmarshal(configjson, &dat); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
confJson, err := json.Marshal(dat)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
json.Unmarshal(confJson, &cfg)
|
|
||||||
// For now we will do a little bit to help the user adjust their
|
|
||||||
// configuration to match the new configuration format, as some of the key
|
|
||||||
// names have changed recently.
|
|
||||||
changes := map[string]string{
|
|
||||||
"Multicast": "",
|
|
||||||
"LinkLocal": "MulticastInterfaces",
|
|
||||||
"BoxPub": "EncryptionPublicKey",
|
|
||||||
"BoxPriv": "EncryptionPrivateKey",
|
|
||||||
"SigPub": "SigningPublicKey",
|
|
||||||
"SigPriv": "SigningPrivateKey",
|
|
||||||
"AllowedBoxPubs": "AllowedEncryptionPublicKeys",
|
|
||||||
}
|
|
||||||
// Loop over the mappings aove and see if we have anything to fix.
|
|
||||||
for from, to := range changes {
|
|
||||||
if _, ok := dat[from]; ok {
|
|
||||||
if to == "" {
|
|
||||||
if !*normaliseconf {
|
|
||||||
log.Println("Warning: Deprecated config option", from, "- please remove")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !*normaliseconf {
|
|
||||||
log.Println("Warning: Deprecated config option", from, "- please rename to", to)
|
|
||||||
}
|
|
||||||
// If the configuration file doesn't already contain a line with the
|
|
||||||
// new name then set it to the old value. This makes sure that we
|
|
||||||
// don't overwrite something that was put there intentionally.
|
|
||||||
if _, ok := dat[to]; !ok {
|
|
||||||
dat[to] = dat[from]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check to see if the peers are in a parsable format, if not then default
|
|
||||||
// them to the TCP scheme
|
|
||||||
if peers, ok := dat["Peers"].([]interface{}); ok {
|
|
||||||
for index, peer := range peers {
|
|
||||||
uri := peer.(string)
|
|
||||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(uri, "tcp:") {
|
|
||||||
uri = uri[4:]
|
|
||||||
}
|
|
||||||
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now do the same with the interface peers
|
|
||||||
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
|
|
||||||
for intf, peers := range interfacepeers {
|
|
||||||
for index, peer := range peers.([]interface{}) {
|
|
||||||
uri := peer.(string)
|
|
||||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(uri, "tcp:") {
|
|
||||||
uri = uri[4:]
|
|
||||||
}
|
|
||||||
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
|
||||||
// we generated above.
|
|
||||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// If the -normaliseconf option was specified then remarshal the above
|
// If the -normaliseconf option was specified then remarshal the above
|
||||||
// configuration and print it back to stdout. This lets the user update
|
// configuration and print it back to stdout. This lets the user update
|
||||||
// their configuration file with newly mapped names (like above) or to
|
// their configuration file with newly mapped names (like above) or to
|
||||||
@ -211,51 +218,30 @@ func main() {
|
|||||||
}
|
}
|
||||||
// Create a new logger that logs output to stdout.
|
// Create a new logger that logs output to stdout.
|
||||||
logger := log.New(os.Stdout, "", log.Flags())
|
logger := log.New(os.Stdout, "", log.Flags())
|
||||||
|
//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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
||||||
// don't need to create this manually.
|
// don't need to create this manually.
|
||||||
n := node{}
|
n := node{}
|
||||||
// Check to see if any multicast interface expressions were provided in the
|
|
||||||
// config. If they were then set them now.
|
|
||||||
for _, ll := range cfg.MulticastInterfaces {
|
|
||||||
ifceExpr, err := regexp.Compile(ll)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
n.core.AddMulticastInterfaceExpr(ifceExpr)
|
|
||||||
}
|
|
||||||
// Now that we have a working configuration, we can now actually start
|
// Now that we have a working configuration, we can now actually start
|
||||||
// Yggdrasil. This will start the router, switch, DHT node, TCP and UDP
|
// Yggdrasil. This will start the router, switch, DHT node, TCP and UDP
|
||||||
// sockets, TUN/TAP adapter and multicast discovery port.
|
// sockets, TUN/TAP adapter and multicast discovery port.
|
||||||
if err := n.core.Start(cfg, logger); err != nil {
|
if err := n.core.Start(cfg, logger); err != nil {
|
||||||
logger.Println("An error occurred during startup")
|
logger.Errorln("An error occurred during startup")
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// Check to see if any allowed encryption keys were provided in the config.
|
|
||||||
// If they were then set them now.
|
|
||||||
for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys {
|
|
||||||
n.core.AddAllowedEncryptionPublicKey(pBoxStr)
|
|
||||||
}
|
|
||||||
// If any static peers were provided in the configuration above then we should
|
|
||||||
// configure them. The loop ensures that disconnected peers will eventually
|
|
||||||
// be reconnected with.
|
|
||||||
go func() {
|
|
||||||
if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
for _, peer := range cfg.Peers {
|
|
||||||
n.core.AddPeer(peer, "")
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
for intf, intfpeers := range cfg.InterfacePeers {
|
|
||||||
for _, peer := range intfpeers {
|
|
||||||
n.core.AddPeer(peer, intf)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// The Stop function ensures that the TUN/TAP adapter is correctly shut down
|
// The Stop function ensures that the TUN/TAP adapter is correctly shut down
|
||||||
// before the program exits.
|
// before the program exits.
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -265,11 +251,13 @@ func main() {
|
|||||||
// This is just logged to stdout for the user.
|
// This is just logged to stdout for the user.
|
||||||
address := n.core.GetAddress()
|
address := n.core.GetAddress()
|
||||||
subnet := n.core.GetSubnet()
|
subnet := n.core.GetSubnet()
|
||||||
logger.Printf("Your IPv6 address is %s", address.String())
|
logger.Infof("Your IPv6 address is %s", address.String())
|
||||||
logger.Printf("Your IPv6 subnet is %s", subnet.String())
|
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||||
// Catch interrupts from the operating system to exit gracefully.
|
// Catch interrupts from the operating system to exit gracefully.
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
|
r := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
signal.Notify(r, os.Interrupt, syscall.SIGHUP)
|
||||||
// Create a function to capture the service being stopped on Windows.
|
// Create a function to capture the service being stopped on Windows.
|
||||||
winTerminate := func() {
|
winTerminate := func() {
|
||||||
c <- os.Interrupt
|
c <- os.Interrupt
|
||||||
@ -277,5 +265,18 @@ func main() {
|
|||||||
minwinsvc.SetOnExit(winTerminate)
|
minwinsvc.SetOnExit(winTerminate)
|
||||||
// Wait for the terminate/interrupt signal. Once a signal is received, the
|
// Wait for the terminate/interrupt signal. Once a signal is received, the
|
||||||
// deferred Stop function above will run which will shut down TUN/TAP.
|
// deferred Stop function above will run which will shut down TUN/TAP.
|
||||||
<-c
|
for {
|
||||||
|
select {
|
||||||
|
case _ = <-r:
|
||||||
|
if *useconffile != "" {
|
||||||
|
cfg = readConfig(useconf, useconffile, normaliseconf)
|
||||||
|
n.core.UpdateConfig(cfg)
|
||||||
|
} else {
|
||||||
|
logger.Errorln("Reloading config at runtime is only possible with -useconffile")
|
||||||
|
}
|
||||||
|
case _ = <-c:
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit:
|
||||||
}
|
}
|
||||||
|
125
contrib/ansible/genkeys.go
Normal file
125
contrib/ansible/genkeys.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/)
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var numHosts = flag.Int("hosts", 1, "number of host vars to generate")
|
||||||
|
var keyTries = flag.Int("tries", 1000, "number of tries before taking the best keys")
|
||||||
|
|
||||||
|
type keySet struct {
|
||||||
|
priv []byte
|
||||||
|
pub []byte
|
||||||
|
id []byte
|
||||||
|
ip string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *numHosts > *keyTries {
|
||||||
|
println("Can't generate less keys than hosts.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptionKeys []keySet
|
||||||
|
for i := 0; i < *numHosts + 1; i++ {
|
||||||
|
encryptionKeys = append(encryptionKeys, newBoxKey())
|
||||||
|
}
|
||||||
|
encryptionKeys = sortKeySetArray(encryptionKeys)
|
||||||
|
for i := 0; i < *keyTries - *numHosts - 1; i++ {
|
||||||
|
encryptionKeys[0] = newBoxKey();
|
||||||
|
encryptionKeys = bubbleUpTo(encryptionKeys, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var signatureKeys []keySet
|
||||||
|
for i := 0; i < *numHosts + 1; i++ {
|
||||||
|
signatureKeys = append(signatureKeys, newSigKey())
|
||||||
|
}
|
||||||
|
signatureKeys = sortKeySetArray(signatureKeys)
|
||||||
|
for i := 0; i < *keyTries - *numHosts - 1; i++ {
|
||||||
|
signatureKeys[0] = newSigKey();
|
||||||
|
signatureKeys = bubbleUpTo(signatureKeys, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.MkdirAll("host_vars", 0755)
|
||||||
|
|
||||||
|
for i := 1; i <= *numHosts; i++ {
|
||||||
|
os.MkdirAll(fmt.Sprintf("host_vars/%x", i), 0755)
|
||||||
|
file, err := os.Create(fmt.Sprintf("host_vars/%x/vars", i))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub)))
|
||||||
|
file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n")
|
||||||
|
file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub)))
|
||||||
|
file.WriteString("yggdrasil_signing_private_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n")
|
||||||
|
file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip))
|
||||||
|
|
||||||
|
file, err = os.Create(fmt.Sprintf("host_vars/%x/vault", i))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
file.WriteString(fmt.Sprintf("vault_yggdrasil_encryption_private_key: %v\n", hex.EncodeToString(encryptionKeys[i].priv)))
|
||||||
|
file.WriteString(fmt.Sprintf("vault_yggdrasil_signing_private_key: %v\n", hex.EncodeToString(signatureKeys[i].priv)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBoxKey() keySet {
|
||||||
|
pub, priv := crypto.NewBoxKeys()
|
||||||
|
id := crypto.GetNodeID(pub)
|
||||||
|
ip := net.IP(address.AddrForNodeID(id)[:]).String()
|
||||||
|
return keySet{priv[:], pub[:], id[:], ip}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSigKey() keySet {
|
||||||
|
pub, priv := crypto.NewSigKeys()
|
||||||
|
id := crypto.GetTreeID(pub)
|
||||||
|
return keySet{priv[:], pub[:], id[:], ""}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBetter(oldID, newID []byte) bool {
|
||||||
|
for idx := range oldID {
|
||||||
|
if newID[idx] > oldID[idx] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if newID[idx] < oldID[idx] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortKeySetArray(sets []keySet) []keySet {
|
||||||
|
for i := 0; i < len(sets); i++ {
|
||||||
|
sets = bubbleUpTo(sets, i)
|
||||||
|
}
|
||||||
|
return sets
|
||||||
|
}
|
||||||
|
|
||||||
|
func bubbleUpTo(sets []keySet, num int) []keySet {
|
||||||
|
for i := 0; i < len(sets) - num - 1; i++ {
|
||||||
|
if isBetter(sets[i + 1].id, sets[i].id) {
|
||||||
|
var tmp = sets[i]
|
||||||
|
sets[i] = sets[i + 1]
|
||||||
|
sets[i + 1] = tmp
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sets
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ require (
|
|||||||
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222
|
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222
|
||||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
|
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
|
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8
|
||||||
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
|
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
|
||||||
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae
|
github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
||||||
|
2
go.sum
2
go.sum
@ -18,3 +18,5 @@ golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dz
|
|||||||
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY=
|
||||||
|
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||||
|
@ -3,9 +3,10 @@ package yggdrasil
|
|||||||
// Defines the minimum required struct members for an adapter type (this is
|
// Defines the minimum required struct members for an adapter type (this is
|
||||||
// now the base type for tunAdapter in tun.go)
|
// now the base type for tunAdapter in tun.go)
|
||||||
type Adapter struct {
|
type Adapter struct {
|
||||||
core *Core
|
core *Core
|
||||||
send chan<- []byte
|
send chan<- []byte
|
||||||
recv <-chan []byte
|
recv <-chan []byte
|
||||||
|
reconfigure chan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialises the adapter.
|
// Initialises the adapter.
|
||||||
@ -13,4 +14,5 @@ func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte)
|
|||||||
adapter.core = core
|
adapter.core = core
|
||||||
adapter.send = send
|
adapter.send = send
|
||||||
adapter.recv = recv
|
adapter.recv = recv
|
||||||
|
adapter.reconfigure = make(chan chan error, 1)
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,11 @@ import (
|
|||||||
// TODO: Add authentication
|
// TODO: Add authentication
|
||||||
|
|
||||||
type admin struct {
|
type admin struct {
|
||||||
core *Core
|
core *Core
|
||||||
listenaddr string
|
reconfigure chan chan error
|
||||||
listener net.Listener
|
listenaddr string
|
||||||
handlers []admin_handlerInfo
|
listener net.Listener
|
||||||
|
handlers []admin_handlerInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
type admin_info map[string]interface{}
|
type admin_info map[string]interface{}
|
||||||
@ -51,9 +52,25 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init runs the initial admin setup.
|
// init runs the initial admin setup.
|
||||||
func (a *admin) init(c *Core, listenaddr string) {
|
func (a *admin) init(c *Core) {
|
||||||
a.core = c
|
a.core = c
|
||||||
a.listenaddr = listenaddr
|
a.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-a.reconfigure
|
||||||
|
a.core.configMutex.RLock()
|
||||||
|
if a.core.config.AdminListen != a.core.configOld.AdminListen {
|
||||||
|
a.listenaddr = a.core.config.AdminListen
|
||||||
|
a.close()
|
||||||
|
a.start()
|
||||||
|
}
|
||||||
|
a.core.configMutex.RUnlock()
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
a.core.configMutex.RLock()
|
||||||
|
a.listenaddr = a.core.config.AdminListen
|
||||||
|
a.core.configMutex.RUnlock()
|
||||||
a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) {
|
a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) {
|
||||||
handlers := make(map[string]interface{})
|
handlers := make(map[string]interface{})
|
||||||
for _, handler := range a.handlers {
|
for _, handler := range a.handlers {
|
||||||
@ -331,7 +348,10 @@ func (a *admin) init(c *Core, listenaddr string) {
|
|||||||
}
|
}
|
||||||
var box_pub_key, coords string
|
var box_pub_key, coords string
|
||||||
if in["box_pub_key"] == nil && in["coords"] == nil {
|
if in["box_pub_key"] == nil && in["coords"] == nil {
|
||||||
nodeinfo := []byte(a.core.nodeinfo.getNodeInfo())
|
var nodeinfo []byte
|
||||||
|
a.core.router.doAdmin(func() {
|
||||||
|
nodeinfo = []byte(a.core.router.nodeinfo.getNodeInfo())
|
||||||
|
})
|
||||||
var jsoninfo interface{}
|
var jsoninfo interface{}
|
||||||
if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil {
|
if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil {
|
||||||
return admin_info{}, err
|
return admin_info{}, err
|
||||||
@ -378,7 +398,7 @@ func (a *admin) listen() {
|
|||||||
switch strings.ToLower(u.Scheme) {
|
switch strings.ToLower(u.Scheme) {
|
||||||
case "unix":
|
case "unix":
|
||||||
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
|
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
|
||||||
a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
|
a.core.log.Warnln("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
|
||||||
}
|
}
|
||||||
a.listener, err = net.Listen("unix", a.listenaddr[7:])
|
a.listener, err = net.Listen("unix", a.listenaddr[7:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -386,7 +406,7 @@ func (a *admin) listen() {
|
|||||||
case "@": // maybe abstract namespace
|
case "@": // maybe abstract namespace
|
||||||
default:
|
default:
|
||||||
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
|
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
|
||||||
a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
|
a.core.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,10 +420,10 @@ func (a *admin) listen() {
|
|||||||
a.listener, err = net.Listen("tcp", a.listenaddr)
|
a.listener, err = net.Listen("tcp", a.listenaddr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.core.log.Printf("Admin socket failed to listen: %v", err)
|
a.core.log.Errorf("Admin socket failed to listen: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
a.core.log.Printf("%s admin socket listening on %s",
|
a.core.log.Infof("%s admin socket listening on %s",
|
||||||
strings.ToUpper(a.listener.Addr().Network()),
|
strings.ToUpper(a.listener.Addr().Network()),
|
||||||
a.listener.Addr().String())
|
a.listener.Addr().String())
|
||||||
defer a.listener.Close()
|
defer a.listener.Close()
|
||||||
@ -430,9 +450,9 @@ func (a *admin) handleRequest(conn net.Conn) {
|
|||||||
"status": "error",
|
"status": "error",
|
||||||
"error": "Unrecoverable error, possibly as a result of invalid input types or malformed syntax",
|
"error": "Unrecoverable error, possibly as a result of invalid input types or malformed syntax",
|
||||||
}
|
}
|
||||||
fmt.Println("Admin socket error:", r)
|
a.core.log.Errorln("Admin socket error:", r)
|
||||||
if err := encoder.Encode(&send); err != nil {
|
if err := encoder.Encode(&send); err != nil {
|
||||||
fmt.Println("Admin socket JSON encode error:", err)
|
a.core.log.Errorln("Admin socket JSON encode error:", err)
|
||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
@ -745,35 +765,20 @@ func (a *admin) getData_getSessions() []admin_nodeInfo {
|
|||||||
|
|
||||||
// getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections.
|
// getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections.
|
||||||
func (a *admin) getAllowedEncryptionPublicKeys() []string {
|
func (a *admin) getAllowedEncryptionPublicKeys() []string {
|
||||||
pubs := a.core.peers.getAllowedEncryptionPublicKeys()
|
return a.core.peers.getAllowedEncryptionPublicKeys()
|
||||||
var out []string
|
|
||||||
for _, pub := range pubs {
|
|
||||||
out = append(out, hex.EncodeToString(pub[:]))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addAllowedEncryptionPublicKey whitelists a key for incoming peer connections.
|
// addAllowedEncryptionPublicKey whitelists a key for incoming peer connections.
|
||||||
func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||||
boxBytes, err := hex.DecodeString(bstr)
|
a.core.peers.addAllowedEncryptionPublicKey(bstr)
|
||||||
if err == nil {
|
return nil
|
||||||
var box crypto.BoxPubKey
|
|
||||||
copy(box[:], boxBytes)
|
|
||||||
a.core.peers.addAllowedEncryptionPublicKey(&box)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections.
|
// removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections.
|
||||||
// If none are set, an empty list permits all incoming connections.
|
// If none are set, an empty list permits all incoming connections.
|
||||||
func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||||
boxBytes, err := hex.DecodeString(bstr)
|
a.core.peers.removeAllowedEncryptionPublicKey(bstr)
|
||||||
if err == nil {
|
return nil
|
||||||
var box crypto.BoxPubKey
|
|
||||||
copy(box[:], boxBytes)
|
|
||||||
a.core.peers.removeAllowedEncryptionPublicKey(&box)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID.
|
// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID.
|
||||||
@ -842,7 +847,7 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (
|
|||||||
copy(key[:], keyBytes)
|
copy(key[:], keyBytes)
|
||||||
}
|
}
|
||||||
if !nocache {
|
if !nocache {
|
||||||
if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil {
|
if response, err := a.core.router.nodeinfo.getCachedNodeInfo(key); err == nil {
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -860,14 +865,14 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (
|
|||||||
}
|
}
|
||||||
response := make(chan *nodeinfoPayload, 1)
|
response := make(chan *nodeinfoPayload, 1)
|
||||||
sendNodeInfoRequest := func() {
|
sendNodeInfoRequest := func() {
|
||||||
a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
|
a.core.router.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
|
||||||
defer func() { recover() }()
|
defer func() { recover() }()
|
||||||
select {
|
select {
|
||||||
case response <- nodeinfo:
|
case response <- nodeinfo:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
a.core.nodeinfo.sendNodeInfo(key, coords, false)
|
a.core.router.nodeinfo.sendNodeInfo(key, coords, false)
|
||||||
}
|
}
|
||||||
a.core.router.doAdmin(sendNodeInfoRequest)
|
a.core.router.doAdmin(sendNodeInfoRequest)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -50,24 +50,24 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *cryp
|
|||||||
meta.sig = l.core.sigPub
|
meta.sig = l.core.sigPub
|
||||||
meta.link = *myLinkPub
|
meta.link = *myLinkPub
|
||||||
metaBytes := meta.encode()
|
metaBytes := meta.encode()
|
||||||
l.core.log.Println("toAWDL <- metaBytes")
|
l.core.log.Traceln("toAWDL <- metaBytes")
|
||||||
toAWDL <- metaBytes
|
toAWDL <- metaBytes
|
||||||
l.core.log.Println("metaBytes = <-fromAWDL")
|
l.core.log.Traceln("metaBytes = <-fromAWDL")
|
||||||
metaBytes = <-fromAWDL
|
metaBytes = <-fromAWDL
|
||||||
l.core.log.Println("version_metadata{}")
|
l.core.log.Traceln("version_metadata{}")
|
||||||
meta = version_metadata{}
|
meta = version_metadata{}
|
||||||
if !meta.decode(metaBytes) || !meta.check() {
|
if !meta.decode(metaBytes) || !meta.check() {
|
||||||
return nil, errors.New("Metadata decode failure")
|
return nil, errors.New("Metadata decode failure")
|
||||||
}
|
}
|
||||||
l.core.log.Println("version_getBaseMetadata{}")
|
l.core.log.Traceln("version_getBaseMetadata{}")
|
||||||
base := version_getBaseMetadata()
|
base := version_getBaseMetadata()
|
||||||
if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer {
|
if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer {
|
||||||
return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
||||||
}
|
}
|
||||||
l.core.log.Println("crypto.GetSharedKey")
|
l.core.log.Traceln("crypto.GetSharedKey")
|
||||||
shared := crypto.GetSharedKey(myLinkPriv, &meta.link)
|
shared := crypto.GetSharedKey(myLinkPriv, &meta.link)
|
||||||
//shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey)
|
//shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey)
|
||||||
l.core.log.Println("l.core.peers.newPeer")
|
l.core.log.Traceln("l.core.peers.newPeer")
|
||||||
intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name)
|
intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name)
|
||||||
if intf.peer != nil {
|
if intf.peer != nil {
|
||||||
intf.peer.linkOut = make(chan []byte, 1) // protocol traffic
|
intf.peer.linkOut = make(chan []byte, 1) // protocol traffic
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
type cryptokey struct {
|
type cryptokey struct {
|
||||||
core *Core
|
core *Core
|
||||||
enabled bool
|
enabled bool
|
||||||
|
reconfigure chan chan error
|
||||||
ipv4routes []cryptokey_route
|
ipv4routes []cryptokey_route
|
||||||
ipv6routes []cryptokey_route
|
ipv6routes []cryptokey_route
|
||||||
ipv4cache map[address.Address]cryptokey_route
|
ipv4cache map[address.Address]cryptokey_route
|
||||||
@ -34,12 +35,75 @@ type cryptokey_route struct {
|
|||||||
// Initialise crypto-key routing. This must be done before any other CKR calls.
|
// Initialise crypto-key routing. This must be done before any other CKR calls.
|
||||||
func (c *cryptokey) init(core *Core) {
|
func (c *cryptokey) init(core *Core) {
|
||||||
c.core = core
|
c.core = core
|
||||||
c.ipv4routes = make([]cryptokey_route, 0)
|
c.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-c.reconfigure
|
||||||
|
var err error
|
||||||
|
c.core.router.doAdmin(func() {
|
||||||
|
err = c.core.router.cryptokey.configure()
|
||||||
|
})
|
||||||
|
e <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := c.configure(); err != nil {
|
||||||
|
c.core.log.Errorln("CKR configuration failed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the CKR routes - this must only ever be called from the router
|
||||||
|
// goroutine, e.g. through router.doAdmin
|
||||||
|
func (c *cryptokey) configure() error {
|
||||||
|
c.core.configMutex.RLock()
|
||||||
|
defer c.core.configMutex.RUnlock()
|
||||||
|
|
||||||
|
// Set enabled/disabled state
|
||||||
|
c.setEnabled(c.core.config.TunnelRouting.Enable)
|
||||||
|
|
||||||
|
// Clear out existing routes
|
||||||
c.ipv6routes = make([]cryptokey_route, 0)
|
c.ipv6routes = make([]cryptokey_route, 0)
|
||||||
|
c.ipv4routes = make([]cryptokey_route, 0)
|
||||||
|
|
||||||
|
// Add IPv6 routes
|
||||||
|
for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations {
|
||||||
|
if err := c.addRoute(ipv6, pubkey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add IPv4 routes
|
||||||
|
for ipv4, pubkey := range c.core.config.TunnelRouting.IPv4Destinations {
|
||||||
|
if err := c.addRoute(ipv4, pubkey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear out existing sources
|
||||||
|
c.ipv6sources = make([]net.IPNet, 0)
|
||||||
|
c.ipv4sources = make([]net.IPNet, 0)
|
||||||
|
|
||||||
|
// Add IPv6 sources
|
||||||
|
c.ipv6sources = make([]net.IPNet, 0)
|
||||||
|
for _, source := range c.core.config.TunnelRouting.IPv6Sources {
|
||||||
|
if err := c.addSourceSubnet(source); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add IPv4 sources
|
||||||
|
c.ipv4sources = make([]net.IPNet, 0)
|
||||||
|
for _, source := range c.core.config.TunnelRouting.IPv4Sources {
|
||||||
|
if err := c.addSourceSubnet(source); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wipe the caches
|
||||||
c.ipv4cache = make(map[address.Address]cryptokey_route, 0)
|
c.ipv4cache = make(map[address.Address]cryptokey_route, 0)
|
||||||
c.ipv6cache = make(map[address.Address]cryptokey_route, 0)
|
c.ipv6cache = make(map[address.Address]cryptokey_route, 0)
|
||||||
c.ipv4sources = make([]net.IPNet, 0)
|
|
||||||
c.ipv6sources = make([]net.IPNet, 0)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable or disable crypto-key routing.
|
// Enable or disable crypto-key routing.
|
||||||
@ -128,7 +192,7 @@ func (c *cryptokey) addSourceSubnet(cidr string) error {
|
|||||||
|
|
||||||
// Add the source subnet
|
// Add the source subnet
|
||||||
*routingsources = append(*routingsources, *ipnet)
|
*routingsources = append(*routingsources, *ipnet)
|
||||||
c.core.log.Println("Added CKR source subnet", cidr)
|
c.core.log.Infoln("Added CKR source subnet", cidr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +264,7 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
|||||||
delete(*routingcache, k)
|
delete(*routingcache, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.core.log.Println("Added CKR destination subnet", cidr)
|
c.core.log.Infoln("Added CKR destination subnet", cidr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +358,7 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error {
|
|||||||
for idx, subnet := range *routingsources {
|
for idx, subnet := range *routingsources {
|
||||||
if subnet.String() == ipnet.String() {
|
if subnet.String() == ipnet.String() {
|
||||||
*routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...)
|
*routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...)
|
||||||
c.core.log.Println("Removed CKR source subnet", cidr)
|
c.core.log.Infoln("Removed CKR source subnet", cidr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,7 +407,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error {
|
|||||||
for k := range *routingcache {
|
for k := range *routingcache {
|
||||||
delete(*routingcache, k)
|
delete(*routingcache, k)
|
||||||
}
|
}
|
||||||
c.core.log.Printf("Removed CKR destination subnet %s via %s\n", cidr, dest)
|
c.core.log.Infoln("Removed CKR destination subnet %s via %s\n", cidr, dest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@ package yggdrasil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gologme/log"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
@ -17,10 +18,20 @@ import (
|
|||||||
var buildName string
|
var buildName string
|
||||||
var buildVersion string
|
var buildVersion string
|
||||||
|
|
||||||
|
type module interface {
|
||||||
|
init(*Core, *config.NodeConfig) error
|
||||||
|
start() error
|
||||||
|
}
|
||||||
|
|
||||||
// The Core object represents the Yggdrasil node. You should create a Core
|
// The Core object represents the Yggdrasil node. You should create a Core
|
||||||
// object for each Yggdrasil node you plan to run.
|
// object for each Yggdrasil node you plan to run.
|
||||||
type Core struct {
|
type Core struct {
|
||||||
// This is the main data structure that holds everything else for a node
|
// This is the main data structure that holds everything else for a node
|
||||||
|
// We're going to keep our own copy of the provided config - that way we can
|
||||||
|
// guarantee that it will be covered by the mutex
|
||||||
|
config config.NodeConfig // Active config
|
||||||
|
configOld config.NodeConfig // Previous config
|
||||||
|
configMutex sync.RWMutex // Protects both config and configOld
|
||||||
boxPub crypto.BoxPubKey
|
boxPub crypto.BoxPubKey
|
||||||
boxPriv crypto.BoxPrivKey
|
boxPriv crypto.BoxPrivKey
|
||||||
sigPub crypto.SigPubKey
|
sigPub crypto.SigPubKey
|
||||||
@ -33,17 +44,12 @@ type Core struct {
|
|||||||
admin admin
|
admin admin
|
||||||
searches searches
|
searches searches
|
||||||
multicast multicast
|
multicast multicast
|
||||||
nodeinfo nodeinfo
|
|
||||||
tcp tcpInterface
|
tcp tcpInterface
|
||||||
awdl awdl
|
awdl awdl
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) init(bpub *crypto.BoxPubKey,
|
func (c *Core) init() error {
|
||||||
bpriv *crypto.BoxPrivKey,
|
|
||||||
spub *crypto.SigPubKey,
|
|
||||||
spriv *crypto.SigPrivKey) {
|
|
||||||
// TODO separate init and start functions
|
// TODO separate init and start functions
|
||||||
// Init sets up structs
|
// Init sets up structs
|
||||||
// Start launches goroutines that depend on structs being set up
|
// Start launches goroutines that depend on structs being set up
|
||||||
@ -51,20 +57,104 @@ func (c *Core) init(bpub *crypto.BoxPubKey,
|
|||||||
if c.log == nil {
|
if c.log == nil {
|
||||||
c.log = log.New(ioutil.Discard, "", 0)
|
c.log = log.New(ioutil.Discard, "", 0)
|
||||||
}
|
}
|
||||||
c.boxPub, c.boxPriv = *bpub, *bpriv
|
|
||||||
c.sigPub, c.sigPriv = *spub, *spriv
|
boxPubHex, err := hex.DecodeString(c.config.EncryptionPublicKey)
|
||||||
c.admin.core = c
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
boxPrivHex, err := hex.DecodeString(c.config.EncryptionPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sigPubHex, err := hex.DecodeString(c.config.SigningPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sigPrivHex, err := hex.DecodeString(c.config.SigningPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(c.boxPub[:], boxPubHex)
|
||||||
|
copy(c.boxPriv[:], boxPrivHex)
|
||||||
|
copy(c.sigPub[:], sigPubHex)
|
||||||
|
copy(c.sigPriv[:], sigPrivHex)
|
||||||
|
|
||||||
|
c.admin.init(c)
|
||||||
c.searches.init(c)
|
c.searches.init(c)
|
||||||
c.dht.init(c)
|
c.dht.init(c)
|
||||||
c.sessions.init(c)
|
c.sessions.init(c)
|
||||||
c.multicast.init(c)
|
c.multicast.init(c)
|
||||||
c.peers.init(c)
|
c.peers.init(c)
|
||||||
c.router.init(c)
|
c.router.init(c)
|
||||||
c.switchTable.init(c, c.sigPub) // TODO move before peers? before router?
|
c.switchTable.init(c) // TODO move before peers? before router?
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current build name. This is usually injected if built from git,
|
// If any static peers were provided in the configuration above then we should
|
||||||
// or returns "unknown" otherwise.
|
// configure them. The loop ensures that disconnected peers will eventually
|
||||||
|
// be reconnected with.
|
||||||
|
func (c *Core) addPeerLoop() {
|
||||||
|
for {
|
||||||
|
// Get the peers from the config - these could change!
|
||||||
|
c.configMutex.RLock()
|
||||||
|
peers := c.config.Peers
|
||||||
|
interfacepeers := c.config.InterfacePeers
|
||||||
|
c.configMutex.RUnlock()
|
||||||
|
|
||||||
|
// Add peers from the Peers section
|
||||||
|
for _, peer := range peers {
|
||||||
|
c.AddPeer(peer, "")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add peers from the InterfacePeers section
|
||||||
|
for intf, intfpeers := range interfacepeers {
|
||||||
|
for _, peer := range intfpeers {
|
||||||
|
c.AddPeer(peer, intf)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sit for a while
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig updates the configuration in Core and then signals the
|
||||||
|
// various module goroutines to reconfigure themselves if needed
|
||||||
|
func (c *Core) UpdateConfig(config *config.NodeConfig) {
|
||||||
|
c.configMutex.Lock()
|
||||||
|
c.configOld = c.config
|
||||||
|
c.config = *config
|
||||||
|
c.configMutex.Unlock()
|
||||||
|
|
||||||
|
components := []chan chan error{
|
||||||
|
c.admin.reconfigure,
|
||||||
|
c.searches.reconfigure,
|
||||||
|
c.dht.reconfigure,
|
||||||
|
c.sessions.reconfigure,
|
||||||
|
c.peers.reconfigure,
|
||||||
|
c.router.reconfigure,
|
||||||
|
c.router.tun.reconfigure,
|
||||||
|
c.router.cryptokey.reconfigure,
|
||||||
|
c.switchTable.reconfigure,
|
||||||
|
c.tcp.reconfigure,
|
||||||
|
c.multicast.reconfigure,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, component := range components {
|
||||||
|
response := make(chan error)
|
||||||
|
component <- response
|
||||||
|
if err := <-response; err != nil {
|
||||||
|
c.log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBuildName gets the current build name. This is usually injected if built
|
||||||
|
// from git, or returns "unknown" otherwise.
|
||||||
func GetBuildName() string {
|
func GetBuildName() string {
|
||||||
if buildName == "" {
|
if buildName == "" {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
@ -89,52 +179,28 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
c.log = log
|
c.log = log
|
||||||
|
|
||||||
if name := GetBuildName(); name != "unknown" {
|
if name := GetBuildName(); name != "unknown" {
|
||||||
c.log.Println("Build name:", name)
|
c.log.Infoln("Build name:", name)
|
||||||
}
|
}
|
||||||
if version := GetBuildVersion(); version != "unknown" {
|
if version := GetBuildVersion(); version != "unknown" {
|
||||||
c.log.Println("Build version:", version)
|
c.log.Infoln("Build version:", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Println("Starting up...")
|
c.log.Infoln("Starting up...")
|
||||||
|
|
||||||
var boxPub crypto.BoxPubKey
|
c.configMutex.Lock()
|
||||||
var boxPriv crypto.BoxPrivKey
|
c.config = *nc
|
||||||
var sigPub crypto.SigPubKey
|
c.configOld = c.config
|
||||||
var sigPriv crypto.SigPrivKey
|
c.configMutex.Unlock()
|
||||||
boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
boxPrivHex, err := hex.DecodeString(nc.EncryptionPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sigPubHex, err := hex.DecodeString(nc.SigningPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sigPrivHex, err := hex.DecodeString(nc.SigningPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
copy(boxPub[:], boxPubHex)
|
|
||||||
copy(boxPriv[:], boxPrivHex)
|
|
||||||
copy(sigPub[:], sigPubHex)
|
|
||||||
copy(sigPriv[:], sigPrivHex)
|
|
||||||
|
|
||||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
c.init()
|
||||||
c.admin.init(c, nc.AdminListen)
|
|
||||||
|
|
||||||
c.nodeinfo.init(c)
|
if err := c.tcp.init(c); err != nil {
|
||||||
c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy)
|
c.log.Errorln("Failed to start TCP interface")
|
||||||
|
|
||||||
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {
|
|
||||||
c.log.Println("Failed to start TCP interface")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.awdl.init(c); err != nil {
|
if err := c.awdl.init(c); err != nil {
|
||||||
c.log.Println("Failed to start AWDL interface")
|
c.log.Errorln("Failed to start AWDL interface")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,72 +209,39 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := c.switchTable.start(); err != nil {
|
if err := c.switchTable.start(); err != nil {
|
||||||
c.log.Println("Failed to start switch")
|
c.log.Errorln("Failed to start switch")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable)
|
|
||||||
c.sessions.setSessionFirewallDefaults(
|
|
||||||
nc.SessionFirewall.AllowFromDirect,
|
|
||||||
nc.SessionFirewall.AllowFromRemote,
|
|
||||||
nc.SessionFirewall.AlwaysAllowOutbound,
|
|
||||||
)
|
|
||||||
c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys)
|
|
||||||
c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys)
|
|
||||||
|
|
||||||
if err := c.router.start(); err != nil {
|
if err := c.router.start(); err != nil {
|
||||||
c.log.Println("Failed to start router")
|
c.log.Errorln("Failed to start router")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.router.cryptokey.setEnabled(nc.TunnelRouting.Enable)
|
|
||||||
if c.router.cryptokey.isEnabled() {
|
|
||||||
c.log.Println("Crypto-key routing enabled")
|
|
||||||
for ipv6, pubkey := range nc.TunnelRouting.IPv6Destinations {
|
|
||||||
if err := c.router.cryptokey.addRoute(ipv6, pubkey); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, source := range nc.TunnelRouting.IPv6Sources {
|
|
||||||
if c.router.cryptokey.addSourceSubnet(source); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ipv4, pubkey := range nc.TunnelRouting.IPv4Destinations {
|
|
||||||
if err := c.router.cryptokey.addRoute(ipv4, pubkey); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, source := range nc.TunnelRouting.IPv4Sources {
|
|
||||||
if c.router.cryptokey.addSourceSubnet(source); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.admin.start(); err != nil {
|
if err := c.admin.start(); err != nil {
|
||||||
c.log.Println("Failed to start admin socket")
|
c.log.Errorln("Failed to start admin socket")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.multicast.start(); err != nil {
|
if err := c.multicast.start(); err != nil {
|
||||||
c.log.Println("Failed to start multicast interface")
|
c.log.Errorln("Failed to start multicast interface")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.IP(c.router.addr[:]).String()
|
if err := c.router.tun.start(); err != nil {
|
||||||
if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address.GetPrefix())-1), nc.IfMTU); err != nil {
|
c.log.Errorln("Failed to start TUN/TAP")
|
||||||
c.log.Println("Failed to start TUN/TAP")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Println("Startup complete")
|
go c.addPeerLoop()
|
||||||
|
|
||||||
|
c.log.Infoln("Startup complete")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the Yggdrasil node.
|
// Stops the Yggdrasil node.
|
||||||
func (c *Core) Stop() {
|
func (c *Core) Stop() {
|
||||||
c.log.Println("Stopping...")
|
c.log.Infoln("Stopping...")
|
||||||
c.router.tun.close()
|
c.router.tun.close()
|
||||||
c.admin.close()
|
c.admin.close()
|
||||||
}
|
}
|
||||||
@ -250,12 +283,12 @@ func (c *Core) GetSubnet() *net.IPNet {
|
|||||||
|
|
||||||
// Gets the nodeinfo.
|
// Gets the nodeinfo.
|
||||||
func (c *Core) GetNodeInfo() nodeinfoPayload {
|
func (c *Core) GetNodeInfo() nodeinfoPayload {
|
||||||
return c.nodeinfo.getNodeInfo()
|
return c.router.nodeinfo.getNodeInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the nodeinfo.
|
// Sets the nodeinfo.
|
||||||
func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
|
func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
|
||||||
c.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
|
c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the output logger of the Yggdrasil node after startup. This may be
|
// Sets the output logger of the Yggdrasil node after startup. This may be
|
||||||
@ -270,13 +303,6 @@ func (c *Core) AddPeer(addr string, sintf string) error {
|
|||||||
return c.admin.addPeer(addr, sintf)
|
return c.admin.addPeer(addr, sintf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an expression to select multicast interfaces for peer discovery. This
|
|
||||||
// should be done before calling Start. This function can be called multiple
|
|
||||||
// times to add multiple search expressions.
|
|
||||||
func (c *Core) AddMulticastInterfaceExpr(expr *regexp.Regexp) {
|
|
||||||
c.ifceExpr = append(c.ifceExpr, expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds an allowed public key. This allow peerings to be restricted only to
|
// Adds an allowed public key. This allow peerings to be restricted only to
|
||||||
// keys that you have selected.
|
// keys that you have selected.
|
||||||
func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error {
|
func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error {
|
||||||
|
@ -14,15 +14,18 @@ import _ "golang.org/x/net/ipv6" // TODO put this somewhere better
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "net"
|
import "net"
|
||||||
import "log"
|
|
||||||
import "regexp"
|
import "regexp"
|
||||||
|
import "encoding/hex"
|
||||||
|
|
||||||
import _ "net/http/pprof"
|
import _ "net/http/pprof"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
import "runtime"
|
import "runtime"
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
|
import "github.com/gologme/log"
|
||||||
|
|
||||||
import "github.com/yggdrasil-network/yggdrasil-go/src/address"
|
import "github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
import "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
|
||||||
@ -52,7 +55,17 @@ func StartProfiler(log *log.Logger) error {
|
|||||||
func (c *Core) Init() {
|
func (c *Core) Init() {
|
||||||
bpub, bpriv := crypto.NewBoxKeys()
|
bpub, bpriv := crypto.NewBoxKeys()
|
||||||
spub, spriv := crypto.NewSigKeys()
|
spub, spriv := crypto.NewSigKeys()
|
||||||
c.init(bpub, bpriv, spub, spriv)
|
hbpub := hex.EncodeToString(bpub[:])
|
||||||
|
hbpriv := hex.EncodeToString(bpriv[:])
|
||||||
|
hspub := hex.EncodeToString(spub[:])
|
||||||
|
hspriv := hex.EncodeToString(spriv[:])
|
||||||
|
c.config = config.NodeConfig{
|
||||||
|
EncryptionPublicKey: hbpub,
|
||||||
|
EncryptionPrivateKey: hbpriv,
|
||||||
|
SigningPublicKey: hspub,
|
||||||
|
SigningPrivateKey: hspriv,
|
||||||
|
}
|
||||||
|
c.init( /*bpub, bpriv, spub, spriv*/ )
|
||||||
c.switchTable.start()
|
c.switchTable.start()
|
||||||
c.router.start()
|
c.router.start()
|
||||||
}
|
}
|
||||||
@ -350,7 +363,7 @@ func (c *Core) DEBUG_init(bpub []byte,
|
|||||||
bpriv []byte,
|
bpriv []byte,
|
||||||
spub []byte,
|
spub []byte,
|
||||||
spriv []byte) {
|
spriv []byte) {
|
||||||
var boxPub crypto.BoxPubKey
|
/*var boxPub crypto.BoxPubKey
|
||||||
var boxPriv crypto.BoxPrivKey
|
var boxPriv crypto.BoxPrivKey
|
||||||
var sigPub crypto.SigPubKey
|
var sigPub crypto.SigPubKey
|
||||||
var sigPriv crypto.SigPrivKey
|
var sigPriv crypto.SigPrivKey
|
||||||
@ -358,7 +371,18 @@ func (c *Core) DEBUG_init(bpub []byte,
|
|||||||
copy(boxPriv[:], bpriv)
|
copy(boxPriv[:], bpriv)
|
||||||
copy(sigPub[:], spub)
|
copy(sigPub[:], spub)
|
||||||
copy(sigPriv[:], spriv)
|
copy(sigPriv[:], spriv)
|
||||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)*/
|
||||||
|
hbpub := hex.EncodeToString(bpub[:])
|
||||||
|
hbpriv := hex.EncodeToString(bpriv[:])
|
||||||
|
hspub := hex.EncodeToString(spub[:])
|
||||||
|
hspriv := hex.EncodeToString(spriv[:])
|
||||||
|
c.config = config.NodeConfig{
|
||||||
|
EncryptionPublicKey: hbpub,
|
||||||
|
EncryptionPrivateKey: hbpriv,
|
||||||
|
SigningPublicKey: hspub,
|
||||||
|
SigningPrivateKey: hspriv,
|
||||||
|
}
|
||||||
|
c.init( /*bpub, bpriv, spub, spriv*/ )
|
||||||
|
|
||||||
if err := c.router.start(); err != nil {
|
if err := c.router.start(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -427,7 +451,8 @@ func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) {
|
|||||||
|
|
||||||
//*
|
//*
|
||||||
func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) {
|
func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) {
|
||||||
if err := c.tcp.init(c, addrport, 0); err != nil {
|
c.config.Listen = addrport
|
||||||
|
if err := c.tcp.init(c /*, addrport, 0*/); err != nil {
|
||||||
c.log.Println("Failed to start TCP interface:", err)
|
c.log.Println("Failed to start TCP interface:", err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -474,7 +499,8 @@ func (c *Core) DEBUG_addKCPConn(saddr string) {
|
|||||||
|
|
||||||
func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) {
|
func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) {
|
||||||
a := admin{}
|
a := admin{}
|
||||||
a.init(c, addrport)
|
c.config.AdminListen = addrport
|
||||||
|
a.init(c /*, addrport*/)
|
||||||
c.admin = a
|
c.admin = a
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +518,7 @@ func (c *Core) DEBUG_setLogger(log *log.Logger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) {
|
func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) {
|
||||||
c.ifceExpr = append(c.ifceExpr, expr)
|
c.log.Println("DEBUG_setIfceExpr no longer implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) {
|
func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) {
|
||||||
|
@ -65,11 +65,12 @@ type dhtReqKey struct {
|
|||||||
|
|
||||||
// The main DHT struct.
|
// The main DHT struct.
|
||||||
type dht struct {
|
type dht struct {
|
||||||
core *Core
|
core *Core
|
||||||
nodeID crypto.NodeID
|
reconfigure chan chan error
|
||||||
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
nodeID crypto.NodeID
|
||||||
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
||||||
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
||||||
|
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
||||||
// These next two could be replaced by a single linked list or similar...
|
// These next two could be replaced by a single linked list or similar...
|
||||||
table map[crypto.NodeID]*dhtInfo
|
table map[crypto.NodeID]*dhtInfo
|
||||||
imp []*dhtInfo
|
imp []*dhtInfo
|
||||||
@ -78,6 +79,13 @@ type dht struct {
|
|||||||
// Initializes the DHT.
|
// Initializes the DHT.
|
||||||
func (t *dht) init(c *Core) {
|
func (t *dht) init(c *Core) {
|
||||||
t.core = c
|
t.core = c
|
||||||
|
t.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-t.reconfigure
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
t.nodeID = *t.core.GetNodeID()
|
t.nodeID = *t.core.GetNodeID()
|
||||||
t.peers = make(chan *dhtInfo, 1024)
|
t.peers = make(chan *dhtInfo, 1024)
|
||||||
t.callbacks = make(map[dhtReqKey]dht_callbackInfo)
|
t.callbacks = make(map[dhtReqKey]dht_callbackInfo)
|
||||||
|
@ -77,9 +77,7 @@ func (c *Core) StartJSON(configjson []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
nc.IfName = "dummy"
|
nc.IfName = "dummy"
|
||||||
//c.log.Println(nc.MulticastInterfaces)
|
|
||||||
for _, ll := range nc.MulticastInterfaces {
|
for _, ll := range nc.MulticastInterfaces {
|
||||||
//c.log.Println("Processing MC", ll)
|
|
||||||
ifceExpr, err := regexp.Compile(ll)
|
ifceExpr, err := regexp.Compile(ll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -4,33 +4,46 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
type multicast struct {
|
type multicast struct {
|
||||||
core *Core
|
core *Core
|
||||||
sock *ipv6.PacketConn
|
reconfigure chan chan error
|
||||||
groupAddr string
|
sock *ipv6.PacketConn
|
||||||
|
groupAddr string
|
||||||
|
myAddr *net.TCPAddr
|
||||||
|
myAddrMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicast) init(core *Core) {
|
func (m *multicast) init(core *Core) {
|
||||||
m.core = core
|
m.core = core
|
||||||
|
m.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-m.reconfigure
|
||||||
|
m.myAddrMutex.Lock()
|
||||||
|
m.myAddr = m.core.tcp.getAddr()
|
||||||
|
m.myAddrMutex.Unlock()
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
m.groupAddr = "[ff02::114]:9001"
|
m.groupAddr = "[ff02::114]:9001"
|
||||||
// Check if we've been given any expressions
|
// Check if we've been given any expressions
|
||||||
if len(m.core.ifceExpr) == 0 {
|
if count := len(m.interfaces()); count != 0 {
|
||||||
return
|
m.core.log.Infoln("Found", count, "multicast interface(s)")
|
||||||
}
|
}
|
||||||
// Ask the system for network interfaces
|
|
||||||
m.core.log.Println("Found", len(m.interfaces()), "multicast interface(s)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicast) start() error {
|
func (m *multicast) start() error {
|
||||||
if len(m.core.ifceExpr) == 0 {
|
if len(m.interfaces()) == 0 {
|
||||||
m.core.log.Println("Multicast discovery is disabled")
|
m.core.log.Infoln("Multicast discovery is disabled")
|
||||||
} else {
|
} else {
|
||||||
m.core.log.Println("Multicast discovery is enabled")
|
m.core.log.Infoln("Multicast discovery is enabled")
|
||||||
addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
|
addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -55,6 +68,10 @@ func (m *multicast) start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicast) interfaces() []net.Interface {
|
func (m *multicast) interfaces() []net.Interface {
|
||||||
|
// Get interface expressions from config
|
||||||
|
m.core.configMutex.RLock()
|
||||||
|
exprs := m.core.config.MulticastInterfaces
|
||||||
|
m.core.configMutex.RUnlock()
|
||||||
// Ask the system for network interfaces
|
// Ask the system for network interfaces
|
||||||
var interfaces []net.Interface
|
var interfaces []net.Interface
|
||||||
allifaces, err := net.Interfaces()
|
allifaces, err := net.Interfaces()
|
||||||
@ -75,8 +92,12 @@ func (m *multicast) interfaces() []net.Interface {
|
|||||||
// Ignore point-to-point interfaces
|
// Ignore point-to-point interfaces
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, expr := range m.core.ifceExpr {
|
for _, expr := range exprs {
|
||||||
if expr.MatchString(iface.Name) {
|
e, err := regexp.Compile(expr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if e.MatchString(iface.Name) {
|
||||||
interfaces = append(interfaces, iface)
|
interfaces = append(interfaces, iface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,13 +106,14 @@ func (m *multicast) interfaces() []net.Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicast) announce() {
|
func (m *multicast) announce() {
|
||||||
|
var anAddr net.TCPAddr
|
||||||
|
m.myAddrMutex.Lock()
|
||||||
|
m.myAddr = m.core.tcp.getAddr()
|
||||||
|
m.myAddrMutex.Unlock()
|
||||||
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var anAddr net.TCPAddr
|
|
||||||
myAddr := m.core.tcp.getAddr()
|
|
||||||
anAddr.Port = myAddr.Port
|
|
||||||
destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -103,6 +125,9 @@ func (m *multicast) announce() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
m.myAddrMutex.RLock()
|
||||||
|
anAddr.Port = m.myAddr.Port
|
||||||
|
m.myAddrMutex.RUnlock()
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
addrIP, _, _ := net.ParseCIDR(addr.String())
|
addrIP, _, _ := net.ParseCIDR(addr.String())
|
||||||
if addrIP.To4() != nil {
|
if addrIP.To4() != nil {
|
||||||
|
@ -170,7 +170,7 @@ func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse
|
|||||||
nodeinfo := nodeinfoReqRes{
|
nodeinfo := nodeinfoReqRes{
|
||||||
SendCoords: table.self.getCoords(),
|
SendCoords: table.self.getCoords(),
|
||||||
IsResponse: isResponse,
|
IsResponse: isResponse,
|
||||||
NodeInfo: m.core.nodeinfo.getNodeInfo(),
|
NodeInfo: m.getNodeInfo(),
|
||||||
}
|
}
|
||||||
bs := nodeinfo.encode()
|
bs := nodeinfo.encode()
|
||||||
shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key)
|
shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key)
|
||||||
|
@ -5,6 +5,7 @@ package yggdrasil
|
|||||||
// Live code should be better commented
|
// Live code should be better commented
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -14,15 +15,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// The peers struct represents peers with an active connection.
|
// The peers struct represents peers with an active connection.
|
||||||
// Incomping packets are passed to the corresponding peer, which handles them somehow.
|
// 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 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, 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.
|
||||||
type peers struct {
|
type peers struct {
|
||||||
core *Core
|
core *Core
|
||||||
mutex sync.Mutex // Synchronize writes to atomic
|
reconfigure chan chan error
|
||||||
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
mutex sync.Mutex // Synchronize writes to atomic
|
||||||
authMutex sync.RWMutex
|
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
||||||
allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes the peers struct.
|
// Initializes the peers struct.
|
||||||
@ -31,40 +31,55 @@ func (ps *peers) init(c *Core) {
|
|||||||
defer ps.mutex.Unlock()
|
defer ps.mutex.Unlock()
|
||||||
ps.putPorts(make(map[switchPort]*peer))
|
ps.putPorts(make(map[switchPort]*peer))
|
||||||
ps.core = c
|
ps.core = c
|
||||||
ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{})
|
ps.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-ps.reconfigure
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty.
|
// Returns true if an incoming peer connection to a key is allowed, either
|
||||||
|
// because the key is in the whitelist or because the whitelist is empty.
|
||||||
func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool {
|
func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool {
|
||||||
ps.authMutex.RLock()
|
boxstr := hex.EncodeToString(box[:])
|
||||||
defer ps.authMutex.RUnlock()
|
ps.core.configMutex.RLock()
|
||||||
_, isIn := ps.allowedEncryptionPublicKeys[*box]
|
defer ps.core.configMutex.RUnlock()
|
||||||
return isIn || len(ps.allowedEncryptionPublicKeys) == 0
|
for _, v := range ps.core.config.AllowedEncryptionPublicKeys {
|
||||||
|
if v == boxstr {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(ps.core.config.AllowedEncryptionPublicKeys) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a key to the whitelist.
|
// Adds a key to the whitelist.
|
||||||
func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
func (ps *peers) addAllowedEncryptionPublicKey(box string) {
|
||||||
ps.authMutex.Lock()
|
ps.core.configMutex.RLock()
|
||||||
defer ps.authMutex.Unlock()
|
defer ps.core.configMutex.RUnlock()
|
||||||
ps.allowedEncryptionPublicKeys[*box] = struct{}{}
|
ps.core.config.AllowedEncryptionPublicKeys =
|
||||||
|
append(ps.core.config.AllowedEncryptionPublicKeys, box)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a key from the whitelist.
|
// Removes a key from the whitelist.
|
||||||
func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
func (ps *peers) removeAllowedEncryptionPublicKey(box string) {
|
||||||
ps.authMutex.Lock()
|
ps.core.configMutex.RLock()
|
||||||
defer ps.authMutex.Unlock()
|
defer ps.core.configMutex.RUnlock()
|
||||||
delete(ps.allowedEncryptionPublicKeys, *box)
|
for k, v := range ps.core.config.AllowedEncryptionPublicKeys {
|
||||||
|
if v == box {
|
||||||
|
ps.core.config.AllowedEncryptionPublicKeys =
|
||||||
|
append(ps.core.config.AllowedEncryptionPublicKeys[:k],
|
||||||
|
ps.core.config.AllowedEncryptionPublicKeys[k+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the whitelist of allowed keys for incoming connections.
|
// Gets the whitelist of allowed keys for incoming connections.
|
||||||
func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey {
|
func (ps *peers) getAllowedEncryptionPublicKeys() []string {
|
||||||
ps.authMutex.RLock()
|
ps.core.configMutex.RLock()
|
||||||
defer ps.authMutex.RUnlock()
|
defer ps.core.configMutex.RUnlock()
|
||||||
keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys))
|
return ps.core.config.AllowedEncryptionPublicKeys
|
||||||
for key := range ps.allowedEncryptionPublicKeys {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomically gets a map[switchPort]*peer of known peers.
|
// Atomically gets a map[switchPort]*peer of known peers.
|
||||||
@ -97,7 +112,7 @@ type peer struct {
|
|||||||
close func() // Called when a peer is removed, to close the underlying connection, or via admin api
|
close func() // Called when a peer is removed, to close the underlying connection, or via admin api
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
|
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unoccupied port number.
|
||||||
func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string) *peer {
|
func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string) *peer {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
p := peer{box: *box,
|
p := peer{box: *box,
|
||||||
@ -342,7 +357,7 @@ func (p *peer) handleSwitchMsg(packet []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This generates the bytes that we sign or check the signature of for a switchMsg.
|
// This generates the bytes that we sign or check the signature of for a switchMsg.
|
||||||
// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node.
|
// It begins with the next node's key, followed by the root and the timestamp, followed by coords being advertised to the next node.
|
||||||
func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte {
|
func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte {
|
||||||
var loc switchLocator
|
var loc switchLocator
|
||||||
for _, hop := range msg.Hops {
|
for _, hop := range msg.Hops {
|
||||||
|
@ -37,19 +37,21 @@ import (
|
|||||||
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
|
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
|
||||||
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
|
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
|
||||||
type router struct {
|
type router struct {
|
||||||
core *Core
|
core *Core
|
||||||
addr address.Address
|
reconfigure chan chan error
|
||||||
subnet address.Subnet
|
addr address.Address
|
||||||
in <-chan []byte // packets we received from the network, link to peer's "out"
|
subnet address.Subnet
|
||||||
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
in <-chan []byte // packets we received from the network, link to peer's "out"
|
||||||
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
||||||
tun tunAdapter // TUN/TAP adapter
|
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
||||||
adapters []Adapter // Other adapters
|
tun tunAdapter // TUN/TAP adapter
|
||||||
recv chan<- []byte // place where the tun pulls received packets from
|
adapters []Adapter // Other adapters
|
||||||
send <-chan []byte // place where the tun puts outgoing packets
|
recv chan<- []byte // place where the tun pulls received packets from
|
||||||
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
send <-chan []byte // place where the tun puts outgoing packets
|
||||||
admin chan func() // pass a lambda for the admin socket to query stuff
|
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
||||||
cryptokey cryptokey
|
admin chan func() // pass a lambda for the admin socket to query stuff
|
||||||
|
cryptokey cryptokey
|
||||||
|
nodeinfo nodeinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun.
|
// Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun.
|
||||||
@ -61,6 +63,7 @@ type router_recvPacket struct {
|
|||||||
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
|
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
|
||||||
func (r *router) init(core *Core) {
|
func (r *router) init(core *Core) {
|
||||||
r.core = core
|
r.core = core
|
||||||
|
r.reconfigure = make(chan chan error, 1)
|
||||||
r.addr = *address.AddrForNodeID(&r.core.dht.nodeID)
|
r.addr = *address.AddrForNodeID(&r.core.dht.nodeID)
|
||||||
r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID)
|
r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID)
|
||||||
in := make(chan []byte, 32) // TODO something better than this...
|
in := make(chan []byte, 32) // TODO something better than this...
|
||||||
@ -83,13 +86,17 @@ func (r *router) init(core *Core) {
|
|||||||
r.send = send
|
r.send = send
|
||||||
r.reset = make(chan struct{}, 1)
|
r.reset = make(chan struct{}, 1)
|
||||||
r.admin = make(chan func(), 32)
|
r.admin = make(chan func(), 32)
|
||||||
|
r.nodeinfo.init(r.core)
|
||||||
|
r.core.configMutex.RLock()
|
||||||
|
r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy)
|
||||||
|
r.core.configMutex.RUnlock()
|
||||||
r.cryptokey.init(r.core)
|
r.cryptokey.init(r.core)
|
||||||
r.tun.init(r.core, send, recv)
|
r.tun.init(r.core, send, recv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the mainLoop goroutine.
|
// Starts the mainLoop goroutine.
|
||||||
func (r *router) start() error {
|
func (r *router) start() error {
|
||||||
r.core.log.Println("Starting router")
|
r.core.log.Infoln("Starting router")
|
||||||
go r.mainLoop()
|
go r.mainLoop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -124,6 +131,10 @@ func (r *router) mainLoop() {
|
|||||||
}
|
}
|
||||||
case f := <-r.admin:
|
case f := <-r.admin:
|
||||||
f()
|
f()
|
||||||
|
case e := <-r.reconfigure:
|
||||||
|
r.core.configMutex.RLock()
|
||||||
|
e <- r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy)
|
||||||
|
r.core.configMutex.RUnlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,7 +474,7 @@ func (r *router) handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.SendPermPub = *fromKey
|
req.SendPermPub = *fromKey
|
||||||
r.core.nodeinfo.handleNodeInfo(&req)
|
r.nodeinfo.handleNodeInfo(&req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passed a function to call.
|
// Passed a function to call.
|
||||||
|
@ -30,7 +30,7 @@ const search_MAX_SEARCH_SIZE = 16
|
|||||||
const search_RETRY_TIME = time.Second
|
const search_RETRY_TIME = time.Second
|
||||||
|
|
||||||
// Information about an ongoing search.
|
// Information about an ongoing search.
|
||||||
// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
// Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
||||||
type searchInfo struct {
|
type searchInfo struct {
|
||||||
dest crypto.NodeID
|
dest crypto.NodeID
|
||||||
mask crypto.NodeID
|
mask crypto.NodeID
|
||||||
@ -42,13 +42,21 @@ type searchInfo struct {
|
|||||||
|
|
||||||
// This stores a map of active searches.
|
// This stores a map of active searches.
|
||||||
type searches struct {
|
type searches struct {
|
||||||
core *Core
|
core *Core
|
||||||
searches map[crypto.NodeID]*searchInfo
|
reconfigure chan chan error
|
||||||
|
searches map[crypto.NodeID]*searchInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intializes the searches struct.
|
// Intializes the searches struct.
|
||||||
func (s *searches) init(core *Core) {
|
func (s *searches) init(core *Core) {
|
||||||
s.core = core
|
s.core = core
|
||||||
|
s.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-s.reconfigure
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
s.searches = make(map[crypto.NodeID]*searchInfo)
|
s.searches = make(map[crypto.NodeID]*searchInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
||||||
type sessionInfo struct {
|
type sessionInfo struct {
|
||||||
core *Core
|
core *Core
|
||||||
|
reconfigure chan chan error
|
||||||
theirAddr address.Address
|
theirAddr address.Address
|
||||||
theirSubnet address.Subnet
|
theirSubnet address.Subnet
|
||||||
theirPermPub crypto.BoxPubKey
|
theirPermPub crypto.BoxPubKey
|
||||||
@ -101,6 +102,7 @@ func (s *sessionInfo) timedout() bool {
|
|||||||
// Additionally, stores maps of address/subnet onto keys, and keys onto handles.
|
// Additionally, stores maps of address/subnet onto keys, and keys onto handles.
|
||||||
type sessions struct {
|
type sessions struct {
|
||||||
core *Core
|
core *Core
|
||||||
|
reconfigure chan chan error
|
||||||
lastCleanup time.Time
|
lastCleanup time.Time
|
||||||
// Maps known permanent keys to their shared key, used by DHT a lot
|
// Maps known permanent keys to their shared key, used by DHT a lot
|
||||||
permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey
|
permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey
|
||||||
@ -112,18 +114,29 @@ type sessions struct {
|
|||||||
byTheirPerm map[crypto.BoxPubKey]*crypto.Handle
|
byTheirPerm map[crypto.BoxPubKey]*crypto.Handle
|
||||||
addrToPerm map[address.Address]*crypto.BoxPubKey
|
addrToPerm map[address.Address]*crypto.BoxPubKey
|
||||||
subnetToPerm map[address.Subnet]*crypto.BoxPubKey
|
subnetToPerm map[address.Subnet]*crypto.BoxPubKey
|
||||||
// Options from the session firewall
|
|
||||||
sessionFirewallEnabled bool
|
|
||||||
sessionFirewallAllowsDirect bool
|
|
||||||
sessionFirewallAllowsRemote bool
|
|
||||||
sessionFirewallAlwaysAllowsOutbound bool
|
|
||||||
sessionFirewallWhitelist []string
|
|
||||||
sessionFirewallBlacklist []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes the session struct.
|
// Initializes the session struct.
|
||||||
func (ss *sessions) init(core *Core) {
|
func (ss *sessions) init(core *Core) {
|
||||||
ss.core = core
|
ss.core = core
|
||||||
|
ss.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-ss.reconfigure
|
||||||
|
responses := make(map[crypto.Handle]chan error)
|
||||||
|
for index, session := range ss.sinfos {
|
||||||
|
responses[index] = make(chan error)
|
||||||
|
session.reconfigure <- responses[index]
|
||||||
|
}
|
||||||
|
for _, response := range responses {
|
||||||
|
if err := <-response; err != nil {
|
||||||
|
e <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey)
|
ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey)
|
||||||
ss.sinfos = make(map[crypto.Handle]*sessionInfo)
|
ss.sinfos = make(map[crypto.Handle]*sessionInfo)
|
||||||
ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle)
|
ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle)
|
||||||
@ -133,40 +146,28 @@ func (ss *sessions) init(core *Core) {
|
|||||||
ss.lastCleanup = time.Now()
|
ss.lastCleanup = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable or disable the session firewall
|
// Determines whether the session firewall is enabled.
|
||||||
func (ss *sessions) setSessionFirewallState(enabled bool) {
|
func (ss *sessions) isSessionFirewallEnabled() bool {
|
||||||
ss.sessionFirewallEnabled = enabled
|
ss.core.configMutex.RLock()
|
||||||
}
|
defer ss.core.configMutex.RUnlock()
|
||||||
|
|
||||||
// Set the session firewall defaults (first parameter is whether to allow
|
return ss.core.config.SessionFirewall.Enable
|
||||||
// sessions from direct peers, second is whether to allow from remote nodes).
|
|
||||||
func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) {
|
|
||||||
ss.sessionFirewallAllowsDirect = allowsDirect
|
|
||||||
ss.sessionFirewallAllowsRemote = allowsRemote
|
|
||||||
ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the session firewall whitelist - nodes always allowed to open sessions.
|
|
||||||
func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) {
|
|
||||||
ss.sessionFirewallWhitelist = whitelist
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the session firewall blacklist - nodes never allowed to open sessions.
|
|
||||||
func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) {
|
|
||||||
ss.sessionFirewallBlacklist = blacklist
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines whether the session with a given publickey is allowed based on
|
// Determines whether the session with a given publickey is allowed based on
|
||||||
// session firewall rules.
|
// session firewall rules.
|
||||||
func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
||||||
|
ss.core.configMutex.RLock()
|
||||||
|
defer ss.core.configMutex.RUnlock()
|
||||||
|
|
||||||
// Allow by default if the session firewall is disabled
|
// Allow by default if the session firewall is disabled
|
||||||
if !ss.sessionFirewallEnabled {
|
if !ss.isSessionFirewallEnabled() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Prepare for checking whitelist/blacklist
|
// Prepare for checking whitelist/blacklist
|
||||||
var box crypto.BoxPubKey
|
var box crypto.BoxPubKey
|
||||||
// Reject blacklisted nodes
|
// Reject blacklisted nodes
|
||||||
for _, b := range ss.sessionFirewallBlacklist {
|
for _, b := range ss.core.config.SessionFirewall.BlacklistEncryptionPublicKeys {
|
||||||
key, err := hex.DecodeString(b)
|
key, err := hex.DecodeString(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
copy(box[:crypto.BoxPubKeyLen], key)
|
copy(box[:crypto.BoxPubKeyLen], key)
|
||||||
@ -176,7 +177,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow whitelisted nodes
|
// Allow whitelisted nodes
|
||||||
for _, b := range ss.sessionFirewallWhitelist {
|
for _, b := range ss.core.config.SessionFirewall.WhitelistEncryptionPublicKeys {
|
||||||
key, err := hex.DecodeString(b)
|
key, err := hex.DecodeString(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
copy(box[:crypto.BoxPubKeyLen], key)
|
copy(box[:crypto.BoxPubKeyLen], key)
|
||||||
@ -186,7 +187,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow outbound sessions if appropriate
|
// Allow outbound sessions if appropriate
|
||||||
if ss.sessionFirewallAlwaysAllowsOutbound {
|
if ss.core.config.SessionFirewall.AlwaysAllowOutbound {
|
||||||
if initiator {
|
if initiator {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -200,11 +201,11 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow direct peers if appropriate
|
// Allow direct peers if appropriate
|
||||||
if ss.sessionFirewallAllowsDirect && isDirectPeer {
|
if ss.core.config.SessionFirewall.AllowFromDirect && isDirectPeer {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Allow remote nodes if appropriate
|
// Allow remote nodes if appropriate
|
||||||
if ss.sessionFirewallAllowsRemote && !isDirectPeer {
|
if ss.core.config.SessionFirewall.AllowFromRemote && !isDirectPeer {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Finally, default-deny if not matching any of the above rules
|
// Finally, default-deny if not matching any of the above rules
|
||||||
@ -264,13 +265,12 @@ func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool)
|
|||||||
// Creates a new session and lazily cleans up old/timedout existing sessions.
|
// Creates a new session and lazily cleans up old/timedout existing sessions.
|
||||||
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
||||||
func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
||||||
if ss.sessionFirewallEnabled {
|
if !ss.isSessionAllowed(theirPermKey, true) {
|
||||||
if !ss.isSessionAllowed(theirPermKey, true) {
|
return nil
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sinfo := sessionInfo{}
|
sinfo := sessionInfo{}
|
||||||
sinfo.core = ss.core
|
sinfo.core = ss.core
|
||||||
|
sinfo.reconfigure = make(chan chan error, 1)
|
||||||
sinfo.theirPermPub = *theirPermKey
|
sinfo.theirPermPub = *theirPermKey
|
||||||
pub, priv := crypto.NewBoxKeys()
|
pub, priv := crypto.NewBoxKeys()
|
||||||
sinfo.mySesPub = *pub
|
sinfo.mySesPub = *pub
|
||||||
@ -442,7 +442,7 @@ func (ss *sessions) handlePing(ping *sessionPing) {
|
|||||||
// Get the corresponding session (or create a new session)
|
// Get the corresponding session (or create a new session)
|
||||||
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
|
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
|
||||||
// Check the session firewall
|
// Check the session firewall
|
||||||
if !isIn && ss.sessionFirewallEnabled {
|
if !isIn && ss.isSessionFirewallEnabled() {
|
||||||
if !ss.isSessionAllowed(&ping.SendPermPub, false) {
|
if !ss.isSessionAllowed(&ping.SendPermPub, false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -539,6 +539,8 @@ func (sinfo *sessionInfo) doWorker() {
|
|||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case e := <-sinfo.reconfigure:
|
||||||
|
e <- nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,17 +554,30 @@ func (sinfo *sessionInfo) doSend(bs []byte) {
|
|||||||
}
|
}
|
||||||
// code isn't multithreaded so appending to this is safe
|
// code isn't multithreaded so appending to this is safe
|
||||||
coords := sinfo.coords
|
coords := sinfo.coords
|
||||||
// Read IPv6 flowlabel field (20 bits).
|
// Work out the flowkey - this is used to determine which switch queue
|
||||||
// Assumes packet at least contains IPv6 header.
|
// traffic will be pushed to in the event of congestion
|
||||||
flowkey := uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3])
|
var flowkey uint64
|
||||||
// Check if the flowlabel was specified
|
// Get the IP protocol version from the packet
|
||||||
if flowkey == 0 {
|
switch bs[0] & 0xf0 {
|
||||||
// Does the packet meet the minimum UDP packet size? (others are bigger)
|
case 0x40: // IPv4 packet
|
||||||
if len(bs) >= 48 {
|
// Check the packet meets minimum UDP packet length
|
||||||
// Is the protocol TCP, UDP, SCTP?
|
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
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 0x60: // IPv6 packet
|
||||||
|
// Check if the flowlabel was specified in the packet header
|
||||||
|
flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3])
|
||||||
|
// 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 {
|
if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 {
|
||||||
// if flowlabel was unspecified (0), try to use known protocols' ports
|
|
||||||
// protokey: proto | sport | dport
|
|
||||||
flowkey = uint64(bs[6])<<32 /* proto */ |
|
flowkey = uint64(bs[6])<<32 /* proto */ |
|
||||||
uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ |
|
uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ |
|
||||||
uint64(bs[42])<<8 | uint64(bs[43]) /* dport */
|
uint64(bs[42])<<8 | uint64(bs[43]) /* dport */
|
||||||
@ -610,5 +625,8 @@ func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) {
|
|||||||
sinfo.updateNonce(&p.Nonce)
|
sinfo.updateNonce(&p.Nonce)
|
||||||
sinfo.time = time.Now()
|
sinfo.time = time.Now()
|
||||||
sinfo.bytesRecvd += uint64(len(bs))
|
sinfo.bytesRecvd += uint64(len(bs))
|
||||||
sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}
|
select {
|
||||||
|
case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}:
|
||||||
|
default: // avoid deadlocks, maybe do this somewhere else?...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ package yggdrasil
|
|||||||
// It routes packets based on distance on the spanning tree
|
// It routes packets based on distance on the spanning tree
|
||||||
// In general, this is *not* equivalent to routing on the tree
|
// In general, this is *not* equivalent to routing on the tree
|
||||||
// It falls back to the tree in the worst case, but it can take shortcuts too
|
// It falls back to the tree in the worst case, but it can take shortcuts too
|
||||||
// This is the part that makse routing reasonably efficient on scale-free graphs
|
// This is the part that makes routing reasonably efficient on scale-free graphs
|
||||||
|
|
||||||
// TODO document/comment everything in a lot more detail
|
// TODO document/comment everything in a lot more detail
|
||||||
|
|
||||||
@ -162,6 +162,7 @@ type switchData struct {
|
|||||||
// All the information stored by the switch.
|
// All the information stored by the switch.
|
||||||
type switchTable struct {
|
type switchTable struct {
|
||||||
core *Core
|
core *Core
|
||||||
|
reconfigure chan chan error
|
||||||
key crypto.SigPubKey // Our own key
|
key crypto.SigPubKey // Our own key
|
||||||
time time.Time // Time when locator.tstamp was last updated
|
time time.Time // Time when locator.tstamp was last updated
|
||||||
drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root
|
drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root
|
||||||
@ -181,11 +182,14 @@ type switchTable struct {
|
|||||||
const SwitchQueueTotalMinSize = 4 * 1024 * 1024
|
const SwitchQueueTotalMinSize = 4 * 1024 * 1024
|
||||||
|
|
||||||
// Initializes the switchTable struct.
|
// Initializes the switchTable struct.
|
||||||
func (t *switchTable) init(core *Core, key crypto.SigPubKey) {
|
func (t *switchTable) init(core *Core) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
t.core = core
|
t.core = core
|
||||||
t.key = key
|
t.reconfigure = make(chan chan error, 1)
|
||||||
locator := switchLocator{root: key, tstamp: now.Unix()}
|
t.core.configMutex.RLock()
|
||||||
|
t.key = t.core.sigPub
|
||||||
|
t.core.configMutex.RUnlock()
|
||||||
|
locator := switchLocator{root: t.key, tstamp: now.Unix()}
|
||||||
peers := make(map[switchPort]peerInfo)
|
peers := make(map[switchPort]peerInfo)
|
||||||
t.data = switchData{locator: locator, peers: peers}
|
t.data = switchData{locator: locator, peers: peers}
|
||||||
t.updater.Store(&sync.Once{})
|
t.updater.Store(&sync.Once{})
|
||||||
@ -559,7 +563,7 @@ func (t *switchTable) getTable() lookupTable {
|
|||||||
|
|
||||||
// Starts the switch worker
|
// Starts the switch worker
|
||||||
func (t *switchTable) start() error {
|
func (t *switchTable) start() error {
|
||||||
t.core.log.Println("Starting switch")
|
t.core.log.Infoln("Starting switch")
|
||||||
go t.doWorker()
|
go t.doWorker()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -808,6 +812,8 @@ func (t *switchTable) doWorker() {
|
|||||||
}
|
}
|
||||||
case f := <-t.admin:
|
case f := <-t.admin:
|
||||||
f()
|
f()
|
||||||
|
case e := <-t.reconfigure:
|
||||||
|
e <- nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,11 @@ const tcp_ping_interval = (default_tcp_timeout * 2 / 3)
|
|||||||
// The TCP listener and information about active TCP connections, to avoid duplication.
|
// The TCP listener and information about active TCP connections, to avoid duplication.
|
||||||
type tcpInterface struct {
|
type tcpInterface struct {
|
||||||
core *Core
|
core *Core
|
||||||
|
reconfigure chan chan error
|
||||||
serv net.Listener
|
serv net.Listener
|
||||||
|
serv_stop chan bool
|
||||||
tcp_timeout time.Duration
|
tcp_timeout time.Duration
|
||||||
|
tcp_addr string
|
||||||
mutex sync.Mutex // Protecting the below
|
mutex sync.Mutex // Protecting the below
|
||||||
calls map[string]struct{}
|
calls map[string]struct{}
|
||||||
conns map[tcpInfo](chan struct{})
|
conns map[tcpInfo](chan struct{})
|
||||||
@ -81,10 +84,37 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initializes the struct.
|
// Initializes the struct.
|
||||||
func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err error) {
|
func (iface *tcpInterface) init(core *Core) (err error) {
|
||||||
iface.core = core
|
iface.core = core
|
||||||
|
iface.serv_stop = make(chan bool, 1)
|
||||||
|
iface.reconfigure = make(chan chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-iface.reconfigure
|
||||||
|
iface.core.configMutex.RLock()
|
||||||
|
updated := iface.core.config.Listen != iface.core.configOld.Listen
|
||||||
|
iface.core.configMutex.RUnlock()
|
||||||
|
if updated {
|
||||||
|
iface.serv_stop <- true
|
||||||
|
iface.serv.Close()
|
||||||
|
e <- iface.listen()
|
||||||
|
} else {
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return iface.listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iface *tcpInterface) listen() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
iface.core.configMutex.RLock()
|
||||||
|
iface.tcp_addr = iface.core.config.Listen
|
||||||
|
iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond
|
||||||
|
iface.core.configMutex.RUnlock()
|
||||||
|
|
||||||
iface.tcp_timeout = time.Duration(readTimeout) * time.Millisecond
|
|
||||||
if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout {
|
if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout {
|
||||||
iface.tcp_timeout = default_tcp_timeout
|
iface.tcp_timeout = default_tcp_timeout
|
||||||
}
|
}
|
||||||
@ -93,11 +123,14 @@ func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err
|
|||||||
lc := net.ListenConfig{
|
lc := net.ListenConfig{
|
||||||
Control: iface.tcpContext,
|
Control: iface.tcpContext,
|
||||||
}
|
}
|
||||||
iface.serv, err = lc.Listen(ctx, "tcp", addr)
|
iface.serv, err = lc.Listen(ctx, "tcp", iface.tcp_addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
iface.mutex.Lock()
|
||||||
iface.calls = make(map[string]struct{})
|
iface.calls = make(map[string]struct{})
|
||||||
iface.conns = make(map[tcpInfo](chan struct{}))
|
iface.conns = make(map[tcpInfo](chan struct{}))
|
||||||
|
iface.mutex.Unlock()
|
||||||
go iface.listener()
|
go iface.listener()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -106,16 +139,42 @@ func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err
|
|||||||
// Runs the listener, which spawns off goroutines for incoming connections.
|
// Runs the listener, which spawns off goroutines for incoming connections.
|
||||||
func (iface *tcpInterface) listener() {
|
func (iface *tcpInterface) listener() {
|
||||||
defer iface.serv.Close()
|
defer iface.serv.Close()
|
||||||
iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String())
|
iface.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String())
|
||||||
for {
|
for {
|
||||||
sock, err := iface.serv.Accept()
|
sock, err := iface.serv.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
iface.core.log.Errorln("Failed to accept connection:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-iface.serv_stop:
|
||||||
|
iface.core.log.Errorln("Stopping listener")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go iface.handler(sock, true)
|
||||||
}
|
}
|
||||||
go iface.handler(sock, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if we already have a connection to this node
|
||||||
|
func (iface *tcpInterface) isAlreadyConnected(info tcpInfo) bool {
|
||||||
|
iface.mutex.Lock()
|
||||||
|
defer iface.mutex.Unlock()
|
||||||
|
_, isIn := iface.conns[info]
|
||||||
|
return isIn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if we already are calling this address
|
||||||
|
func (iface *tcpInterface) isAlreadyCalling(saddr string) bool {
|
||||||
|
iface.mutex.Lock()
|
||||||
|
defer iface.mutex.Unlock()
|
||||||
|
_, isIn := iface.calls[saddr]
|
||||||
|
return isIn
|
||||||
|
}
|
||||||
|
|
||||||
// Checks if a connection already exists.
|
// Checks if a connection already exists.
|
||||||
// If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address.
|
// If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address.
|
||||||
// If the dial is successful, it launches the handler.
|
// If the dial is successful, it launches the handler.
|
||||||
@ -127,25 +186,20 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
|||||||
if sintf != "" {
|
if sintf != "" {
|
||||||
callname = fmt.Sprintf("%s/%s", saddr, sintf)
|
callname = fmt.Sprintf("%s/%s", saddr, sintf)
|
||||||
}
|
}
|
||||||
quit := false
|
if iface.isAlreadyCalling(saddr) {
|
||||||
iface.mutex.Lock()
|
|
||||||
if _, isIn := iface.calls[callname]; isIn {
|
|
||||||
quit = true
|
|
||||||
} else {
|
|
||||||
iface.calls[callname] = struct{}{}
|
|
||||||
defer func() {
|
|
||||||
// Block new calls for a little while, to mitigate livelock scenarios
|
|
||||||
time.Sleep(default_tcp_timeout)
|
|
||||||
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
|
|
||||||
iface.mutex.Lock()
|
|
||||||
delete(iface.calls, callname)
|
|
||||||
iface.mutex.Unlock()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
iface.mutex.Unlock()
|
|
||||||
if quit {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
iface.mutex.Lock()
|
||||||
|
iface.calls[callname] = struct{}{}
|
||||||
|
iface.mutex.Unlock()
|
||||||
|
defer func() {
|
||||||
|
// Block new calls for a little while, to mitigate livelock scenarios
|
||||||
|
time.Sleep(default_tcp_timeout)
|
||||||
|
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
|
||||||
|
iface.mutex.Lock()
|
||||||
|
delete(iface.calls, callname)
|
||||||
|
iface.mutex.Unlock()
|
||||||
|
}()
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
var err error
|
var err error
|
||||||
if socksaddr != nil {
|
if socksaddr != nil {
|
||||||
@ -176,35 +230,50 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
|||||||
ief, err := net.InterfaceByName(sintf)
|
ief, err := net.InterfaceByName(sintf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
if ief.Flags&net.FlagUp == 0 {
|
if ief.Flags&net.FlagUp == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addrs, err := ief.Addrs()
|
||||||
|
if err == nil {
|
||||||
|
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addrs, err := ief.Addrs()
|
for addrindex, addr := range addrs {
|
||||||
if err == nil {
|
src, _, err := net.ParseCIDR(addr.String())
|
||||||
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
for _, addr := range addrs {
|
if src.Equal(dst.IP) {
|
||||||
src, _, err := net.ParseCIDR(addr.String())
|
continue
|
||||||
if err != nil {
|
}
|
||||||
continue
|
if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() {
|
||||||
}
|
continue
|
||||||
if (src.To4() != nil) == (dst.IP.To4() != nil) && src.IsGlobalUnicast() {
|
}
|
||||||
dialer.LocalAddr = &net.TCPAddr{
|
bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast()
|
||||||
IP: src,
|
bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast()
|
||||||
Port: 0,
|
if !bothglobal && !bothlinklocal {
|
||||||
}
|
continue
|
||||||
break
|
}
|
||||||
|
if (src.To4() != nil) != (dst.IP.To4() != nil) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bothglobal || bothlinklocal || addrindex == len(addrs)-1 {
|
||||||
|
dialer.LocalAddr = &net.TCPAddr{
|
||||||
|
IP: src,
|
||||||
|
Port: 0,
|
||||||
|
Zone: sintf,
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if dialer.LocalAddr == nil {
|
}
|
||||||
return
|
if dialer.LocalAddr == nil {
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = dialer.Dial("tcp", saddr)
|
conn, err = dialer.Dial("tcp", saddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -244,17 +313,27 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
base := version_getBaseMetadata()
|
base := version_getBaseMetadata()
|
||||||
if meta.meta == base.meta {
|
if meta.meta == base.meta {
|
||||||
if meta.ver > base.ver {
|
if meta.ver > base.ver {
|
||||||
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver)
|
iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver)
|
||||||
} else if meta.ver == base.ver && meta.minorVer > base.minorVer {
|
} else if meta.ver == base.ver && meta.minorVer > base.minorVer {
|
||||||
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO? Block forever to prevent future connection attempts? suppress future messages about the same node?
|
// TODO? Block forever to prevent future connection attempts? suppress future messages about the same node?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
remoteAddr, _, e1 := net.SplitHostPort(sock.RemoteAddr().String())
|
||||||
|
localAddr, _, e2 := net.SplitHostPort(sock.LocalAddr().String())
|
||||||
|
if e1 != nil || e2 != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
info := tcpInfo{ // used as a map key, so don't include ephemeral link key
|
info := tcpInfo{ // used as a map key, so don't include ephemeral link key
|
||||||
box: meta.box,
|
box: meta.box,
|
||||||
sig: meta.sig,
|
sig: meta.sig,
|
||||||
|
localAddr: localAddr,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
}
|
||||||
|
if iface.isAlreadyConnected(info) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Quit the parent call if this is a connection to ourself
|
// Quit the parent call if this is a connection to ourself
|
||||||
equiv := func(k1, k2 []byte) bool {
|
equiv := func(k1, k2 []byte) bool {
|
||||||
@ -265,14 +344,14 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if equiv(info.box[:], iface.core.boxPub[:]) {
|
if equiv(meta.box[:], iface.core.boxPub[:]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if equiv(info.sig[:], iface.core.sigPub[:]) {
|
if equiv(meta.sig[:], iface.core.sigPub[:]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if we're authorized to connect to this key / IP
|
// Check if we're authorized to connect to this key / IP
|
||||||
if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&info.box) {
|
if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&meta.box) {
|
||||||
// Allow unauthorized peers if they're link-local
|
// Allow unauthorized peers if they're link-local
|
||||||
raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||||
raddr := net.ParseIP(raddrStr)
|
raddr := net.ParseIP(raddrStr)
|
||||||
@ -281,15 +360,13 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if we already have a connection to this node, close and block if yes
|
// Check if we already have a connection to this node, close and block if yes
|
||||||
info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String())
|
|
||||||
info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
|
|
||||||
iface.mutex.Lock()
|
iface.mutex.Lock()
|
||||||
if blockChan, isIn := iface.conns[info]; isIn {
|
/*if blockChan, isIn := iface.conns[info]; isIn {
|
||||||
iface.mutex.Unlock()
|
iface.mutex.Unlock()
|
||||||
sock.Close()
|
sock.Close()
|
||||||
<-blockChan
|
<-blockChan
|
||||||
return
|
return
|
||||||
}
|
}*/
|
||||||
blockChan := make(chan struct{})
|
blockChan := make(chan struct{})
|
||||||
iface.conns[info] = blockChan
|
iface.conns[info] = blockChan
|
||||||
iface.mutex.Unlock()
|
iface.mutex.Unlock()
|
||||||
@ -301,7 +378,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}()
|
}()
|
||||||
// Note that multiple connections to the same node are allowed
|
// Note that multiple connections to the same node are allowed
|
||||||
// E.g. over different interfaces
|
// E.g. over different interfaces
|
||||||
p := iface.core.peers.newPeer(&info.box, &info.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String())
|
p := iface.core.peers.newPeer(&meta.box, &meta.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String())
|
||||||
p.linkOut = make(chan []byte, 1)
|
p.linkOut = make(chan []byte, 1)
|
||||||
in := func(bs []byte) {
|
in := func(bs []byte) {
|
||||||
p.handlePacket(bs)
|
p.handlePacket(bs)
|
||||||
@ -363,16 +440,16 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}()
|
}()
|
||||||
us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
||||||
them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||||
themNodeID := crypto.GetNodeID(&info.box)
|
themNodeID := crypto.GetNodeID(&meta.box)
|
||||||
themAddr := address.AddrForNodeID(themNodeID)
|
themAddr := address.AddrForNodeID(themNodeID)
|
||||||
themAddrString := net.IP(themAddr[:]).String()
|
themAddrString := net.IP(themAddr[:]).String()
|
||||||
themString := fmt.Sprintf("%s@%s", themAddrString, them)
|
themString := fmt.Sprintf("%s@%s", themAddrString, them)
|
||||||
iface.core.log.Println("Connected:", themString, "source", us)
|
iface.core.log.Infof("Connected: %s, source: %s", themString, us)
|
||||||
err = iface.reader(sock, in) // In this goroutine, because of defers
|
err = iface.reader(sock, in) // In this goroutine, because of defers
|
||||||
if err == nil {
|
if err == nil {
|
||||||
iface.core.log.Println("Disconnected:", themString, "source", us)
|
iface.core.log.Infof("Disconnected: %s, source: %s", themString, us)
|
||||||
} else {
|
} else {
|
||||||
iface.core.log.Println("Disconnected:", themString, "source", us, "with error:", err)
|
iface.core.log.Infof("Disconnected: %s, source: %s, error: %s", themString, us, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ package yggdrasil
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -42,11 +44,33 @@ func getSupportedMTU(mtu int) int {
|
|||||||
func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
|
func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
|
||||||
tun.Adapter.init(core, send, recv)
|
tun.Adapter.init(core, send, recv)
|
||||||
tun.icmpv6.init(tun)
|
tun.icmpv6.init(tun)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
e := <-tun.reconfigure
|
||||||
|
tun.core.configMutex.RLock()
|
||||||
|
updated := tun.core.config.IfName != tun.core.configOld.IfName ||
|
||||||
|
tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode ||
|
||||||
|
tun.core.config.IfMTU != tun.core.configOld.IfMTU
|
||||||
|
tun.core.configMutex.RUnlock()
|
||||||
|
if updated {
|
||||||
|
tun.core.log.Warnln("Reconfiguring TUN/TAP is not supported yet")
|
||||||
|
e <- nil
|
||||||
|
} else {
|
||||||
|
e <- nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
||||||
// the read/write goroutines to handle packets on that interface.
|
// the read/write goroutines to handle packets on that interface.
|
||||||
func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) start() error {
|
||||||
|
tun.core.configMutex.RLock()
|
||||||
|
ifname := tun.core.config.IfName
|
||||||
|
iftapmode := tun.core.config.IfTAPMode
|
||||||
|
addr := fmt.Sprintf("%s/%d", net.IP(tun.core.router.addr[:]).String(), 8*len(address.GetPrefix())-1)
|
||||||
|
mtu := tun.core.config.IfMTU
|
||||||
|
tun.core.configMutex.RUnlock()
|
||||||
if ifname != "none" {
|
if ifname != "none" {
|
||||||
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -58,8 +82,8 @@ func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int
|
|||||||
tun.mutex.Lock()
|
tun.mutex.Lock()
|
||||||
tun.isOpen = true
|
tun.isOpen = true
|
||||||
tun.mutex.Unlock()
|
tun.mutex.Unlock()
|
||||||
go func() { tun.core.log.Println("WARNING: tun.read() exited with error:", tun.read()) }()
|
go func() { tun.core.log.Errorln("WARNING: tun.read() exited with error:", tun.read()) }()
|
||||||
go func() { tun.core.log.Println("WARNING: tun.write() exited with error:", tun.write()) }()
|
go func() { tun.core.log.Errorln("WARNING: tun.write() exited with error:", tun.write()) }()
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -114,9 +114,9 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Friendly output
|
// Friendly output
|
||||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||||
|
|
||||||
// Create the MTU request
|
// Create the MTU request
|
||||||
var ir in6_ifreq_mtu
|
var ir in6_ifreq_mtu
|
||||||
@ -126,15 +126,15 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
|||||||
// Set the MTU
|
// Set the MTU
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||||
err = errno
|
err = errno
|
||||||
tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno)
|
tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||||
|
|
||||||
// Fall back to ifconfig to set the MTU
|
// Fall back to ifconfig to set the MTU
|
||||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu))
|
cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu))
|
||||||
tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("SIOCSIFMTU fallback failed: %v.", err)
|
tun.core.log.Errorf("SIOCSIFMTU fallback failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,15 +155,15 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
|||||||
// Set the interface address
|
// Set the interface address
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||||
err = errno
|
err = errno
|
||||||
tun.core.log.Printf("Error in SIOCSIFADDR_IN6: %v", errno)
|
tun.core.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno)
|
||||||
|
|
||||||
// Fall back to ifconfig to set the address
|
// Fall back to ifconfig to set the address
|
||||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr)
|
cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr)
|
||||||
tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("SIOCSIFADDR_IN6 fallback failed: %v.", err)
|
tun.core.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
||||||
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
if iftapmode {
|
if iftapmode {
|
||||||
tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN")
|
tun.core.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN")
|
||||||
}
|
}
|
||||||
config := water.Config{DeviceType: water.TUN}
|
config := water.Config{DeviceType: water.TUN}
|
||||||
iface, err := water.New(config)
|
iface, err := water.New(config)
|
||||||
@ -98,19 +98,19 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
|||||||
copy(ir.ifr_name[:], tun.iface.Name())
|
copy(ir.ifr_name[:], tun.iface.Name())
|
||||||
ir.ifru_mtu = uint32(tun.mtu)
|
ir.ifru_mtu = uint32(tun.mtu)
|
||||||
|
|
||||||
tun.core.log.Printf("Interface name: %s", ar.ifra_name)
|
tun.core.log.Infof("Interface name: %s", ar.ifra_name)
|
||||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||||
tun.core.log.Printf("Interface MTU: %d", ir.ifru_mtu)
|
tun.core.log.Infof("Interface MTU: %d", ir.ifru_mtu)
|
||||||
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||||
err = errno
|
err = errno
|
||||||
tun.core.log.Printf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
tun.core.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||||
err = errno
|
err = errno
|
||||||
tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno)
|
tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Friendly output
|
// Friendly output
|
||||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||||
return tun.setupAddress(addr)
|
return tun.setupAddress(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,6 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
|||||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||||
// write about it to stdout and don't try to do anything further.
|
// write about it to stdout and don't try to do anything further.
|
||||||
func (tun *tunAdapter) setupAddress(addr string) error {
|
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||||
tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
tun.core.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
// delegate the hard work to "netsh".
|
// delegate the hard work to "netsh".
|
||||||
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||||
if !iftapmode {
|
if !iftapmode {
|
||||||
tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP")
|
tun.core.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP")
|
||||||
}
|
}
|
||||||
config := water.Config{DeviceType: water.TAP}
|
config := water.Config{DeviceType: water.TAP}
|
||||||
config.PlatformSpecificParams.ComponentID = "tap0901"
|
config.PlatformSpecificParams.ComponentID = "tap0901"
|
||||||
@ -34,16 +34,16 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
|||||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED")
|
cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED")
|
||||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err = cmd.CombinedOutput()
|
output, err = cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Get a new iface
|
// Get a new iface
|
||||||
@ -58,9 +58,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// Friendly output
|
// Friendly output
|
||||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||||
return tun.setupAddress(addr)
|
return tun.setupAddress(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +71,11 @@ func (tun *tunAdapter) setupMTU(mtu int) error {
|
|||||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||||
fmt.Sprintf("mtu=%d", mtu),
|
fmt.Sprintf("mtu=%d", mtu),
|
||||||
"store=active")
|
"store=active")
|
||||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -88,11 +88,11 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
|||||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||||
fmt.Sprintf("addr=%s", addr),
|
fmt.Sprintf("addr=%s", addr),
|
||||||
"store=active")
|
"store=active")
|
||||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||||
tun.core.log.Println(string(output))
|
tun.core.log.Traceln(string(output))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user