diff --git a/misc/sim/run-sim b/misc/sim/run-sim index a2ce2b63..985fe2f3 100755 --- a/misc/sim/run-sim +++ b/misc/sim/run-sim @@ -1,4 +1,4 @@ #!/bin/bash export GOPATH=$PWD go get -d yggdrasil -go run misc/sim/treesim.go +go run -tags debug misc/sim/treesim.go diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 5bf9d3fa..05e5c9a1 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -213,7 +213,11 @@ func (a *admin) init(c *Core, listenaddr string) { }, errors.New("Failed to remove allowed box pub key") } }) +} + +func (a *admin) start() error { go a.listen() + return nil } func (a *admin) listen() { @@ -356,11 +360,11 @@ func (a *admin) addPeer(addr string) error { if err == nil { switch strings.ToLower(u.Scheme) { case "tcp": - a.core.DEBUG_addTCPConn(u.Host) + a.core.tcp.connect(u.Host) case "udp": - a.core.DEBUG_maybeSendUDPKeys(u.Host) + a.core.udp.connect(u.Host) case "socks": - a.core.DEBUG_addSOCKSConn(u.Host, u.Path[1:]) + a.core.tcp.connectSOCKS(u.Host, u.Path[1:]) default: return errors.New("invalid peer: " + addr) } @@ -368,13 +372,13 @@ func (a *admin) addPeer(addr string) error { // no url scheme provided addr = strings.ToLower(addr) if strings.HasPrefix(addr, "udp:") { - a.core.DEBUG_maybeSendUDPKeys(addr[4:]) + a.core.udp.connect(addr[4:]) return nil } else { if strings.HasPrefix(addr, "tcp:") { addr = addr[4:] } - a.core.DEBUG_addTCPConn(addr) + a.core.tcp.connect(addr) return nil } return errors.New("invalid peer: " + addr) @@ -421,13 +425,10 @@ func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error func (a *admin) getData_getSelf() *admin_nodeInfo { table := a.core.switchTable.table.Load().(lookupTable) - addr := (*a.core.GetAddress())[:] - subnet := (*a.core.GetSubnet())[:] - subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0) coords := table.self.getCoords() self := admin_nodeInfo{ - {"ip", net.IP(addr[:]).String()}, - {"subnet", fmt.Sprintf("%s/64", net.IP(subnet[:]).String())}, + {"ip", a.core.GetAddress().String()}, + {"subnet", a.core.GetSubnet().String()}, {"coords", fmt.Sprint(coords)}, } return &self diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 2dd8f85a..9938266e 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -3,7 +3,13 @@ package yggdrasil import "io/ioutil" import "log" import "regexp" +import "net" +import "fmt" +import "encoding/hex" +import "yggdrasil/config" +// The Core object represents the Yggdrasil node. You should create a Core +// object for each Yggdrasil node you plan to run. type Core struct { // This is the main data structure that holds everything else for a node boxPub boxPubKey @@ -26,13 +32,6 @@ type Core struct { ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } -func (c *Core) Init() { - // Only called by the simulator, to set up nodes with random keys - bpub, bpriv := newBoxKeys() - spub, spriv := newSigKeys() - c.init(bpub, bpriv, spub, spriv) -} - func (c *Core) init(bpub *boxPubKey, bpriv *boxPrivKey, spub *sigPubKey, @@ -42,7 +41,9 @@ func (c *Core) init(bpub *boxPubKey, // Start launches goroutines that depend on structs being set up // This is pretty much required to completely avoid race conditions util_initByteStore() - c.log = log.New(ioutil.Discard, "", 0) + if c.log == nil { + c.log = log.New(ioutil.Discard, "", 0) + } c.boxPub, c.boxPriv = *bpub, *bpriv c.sigPub, c.sigPriv = *spub, *spriv c.admin.core = c @@ -57,18 +58,176 @@ func (c *Core) init(bpub *boxPubKey, c.tun.init(c) } +// Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging +// through the provided log.Logger. The started stack will include TCP and UDP +// sockets, a multicast discovery socket, an admin socket, router, switch and +// DHT node. +func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { + c.log = log + c.log.Println("Starting up...") + + var boxPub boxPubKey + var boxPriv boxPrivKey + var sigPub sigPubKey + var sigPriv sigPrivKey + 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.admin.init(c, nc.AdminListen) + + if err := c.tcp.init(c, nc.Listen); err != nil { + c.log.Println("Failed to start TCP interface") + return err + } + + if err := c.udp.init(c, nc.Listen); err != nil { + c.log.Println("Failed to start UDP interface") + return err + } + + if err := c.router.start(); err != nil { + c.log.Println("Failed to start router") + return err + } + + if err := c.switchTable.start(); err != nil { + c.log.Println("Failed to start switch table ticker") + return err + } + + if err := c.admin.start(); err != nil { + c.log.Println("Failed to start admin socket") + return err + } + + if err := c.multicast.start(); err != nil { + c.log.Println("Failed to start multicast interface") + return err + } + + ip := net.IP(c.router.addr[:]).String() + if err := c.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/8", ip), nc.IfMTU); err != nil { + c.log.Println("Failed to start TUN/TAP") + return err + } + + c.log.Println("Startup complete") + return nil +} + +// Stops the Yggdrasil node. +func (c *Core) Stop() { + c.log.Println("Stopping...") + c.tun.close() +} + +// Generates a new encryption keypair. The encryption keys are used to +// encrypt traffic and to derive the IPv6 address/subnet of the node. +func (c *Core) NewEncryptionKeys() (*boxPubKey, *boxPrivKey) { + return newBoxKeys() +} + +// Generates a new signing keypair. The signing keys are used to derive the +// structure of the spanning tree. +func (c *Core) NewSigningKeys() (*sigPubKey, *sigPrivKey) { + return newSigKeys() +} + +// Gets the node ID. func (c *Core) GetNodeID() *NodeID { return getNodeID(&c.boxPub) } +// Gets the tree ID. func (c *Core) GetTreeID() *TreeID { return getTreeID(&c.sigPub) } -func (c *Core) GetAddress() *address { - return address_addrForNodeID(c.GetNodeID()) +// Gets the IPv6 address of the Yggdrasil node. This is always a /128. +func (c *Core) GetAddress() *net.IP { + address := net.IP(address_addrForNodeID(c.GetNodeID())[:]) + return &address } -func (c *Core) GetSubnet() *subnet { - return address_subnetForNodeID(c.GetNodeID()) +// Gets the routed IPv6 subnet of the Yggdrasil node. This is always a /64. +func (c *Core) GetSubnet() *net.IPNet { + subnet := address_subnetForNodeID(c.GetNodeID())[:] + subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0) + return &net.IPNet{ IP: subnet, Mask: net.CIDRMask(64, 128) } +} + +// Sets the output logger of the Yggdrasil node after startup. This may be +// useful if you want to redirect the output later. +func (c *Core) SetLogger(log *log.Logger) { + c.log = log +} + +// Adds a peer. This should be specified in the peer URI format, i.e. +// tcp://a.b.c.d:e, udp://a.b.c.d:e, socks://a.b.c.d:e/f.g.h.i:j +func (c *Core) AddPeer(addr string) error { + return c.admin.addPeer(addr) +} + +// 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 +// keys that you have selected. +func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error { + return c.admin.addAllowedEncryptionPublicKey(boxStr) +} + +// Gets the default TUN/TAP interface name for your platform. +func (c *Core) GetTUNDefaultIfName() string { + return getDefaults().defaultIfName +} + +// Gets the default TUN/TAP interface MTU for your platform. This can be as high +// as 65535, depending on platform, but is never lower than 1280. +func (c *Core) GetTUNDefaultIfMTU() int { + return getDefaults().defaultIfMTU +} + +// Gets the maximum supported TUN/TAP interface MTU for your platform. This +// can be as high as 65535, depending on platform, but is never lower than 1280. +func (c *Core) GetTUNMaximumIfMTU() int { + return getDefaults().maximumIfMTU +} + +// Gets the default TUN/TAP interface mode for your platform. +func (c *Core) GetTUNDefaultIfTAPMode() bool { + return getDefaults().defaultIfTAPMode +} + +// Gets the current TUN/TAP interface name. +func (c *Core) GetTUNIfName() string { + return c.tun.iface.Name() +} + +// Gets the current TUN/TAP interface MTU. +func (c *Core) GetTUNIfMTU() int { + return c.tun.mtu } diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 40cc5c4f..42d2b471 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -1,3 +1,5 @@ +// +build debug + package yggdrasil // These are functions that should not exist @@ -15,6 +17,28 @@ import "net" import "log" import "regexp" +import _ "net/http/pprof" +import "net/http" +import "runtime" + +// Starts the function profiler. This is only supported when built with +// '-tags build'. +func StartProfiler(log *log.Logger) error { + runtime.SetBlockProfileRate(1) + go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() + return nil +} + +// This function is only called by the simulator to set up a node with random +// keys. It should not be used and may be removed in the future. +func (c *Core) Init() { + bpub, bpriv := newBoxKeys() + spub, spriv := newSigKeys() + c.init(bpub, bpriv, spub, spriv) +} + +//////////////////////////////////////////////////////////////////////////////// + // Core func (c *Core) DEBUG_getSigningPublicKey() sigPubKey { @@ -279,6 +303,14 @@ func (c *Core) DEBUG_init(bpub []byte, copy(sigPub[:], spub) copy(sigPriv[:], spriv) c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) + + if err := c.router.start(); err != nil { + panic(err) + } + + if err := c.switchTable.start(); err != nil { + panic(err) + } } //////////////////////////////////////////////////////////////////////////////// @@ -432,3 +464,28 @@ func DEBUG_simLinkPeers(p, q *peer) { func (c *Core) DEBUG_simFixMTU() { c.tun.mtu = 65535 } + +//////////////////////////////////////////////////////////////////////////////// + +func Util_testAddrIDMask() { + for idx := 0; idx < 16; idx++ { + var orig NodeID + orig[8] = 42 + for bidx := 0; bidx < idx; bidx++ { + orig[bidx/8] |= (0x80 >> uint8(bidx%8)) + } + addr := address_addrForNodeID(&orig) + nid, mask := addr.getNodeIDandMask() + for b := 0; b < len(mask); b++ { + nid[b] &= mask[b] + orig[b] &= mask[b] + } + if *nid != orig { + fmt.Println(orig) + fmt.Println(*addr) + fmt.Println(*nid) + fmt.Println(*mask) + panic(idx) + } + } +} diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 96c5218f..fa14b2ac 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -12,7 +12,7 @@ import "errors" type macAddress [6]byte -const ETHER = 14 +const len_ETHER = 14 type icmpv6 struct { tun *tunDevice @@ -79,13 +79,13 @@ func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { } // Hand over to parse_packet_tun to interpret the IPv6 packet - ipv6packet, err := i.parse_packet_tun(datain[ETHER:]) + ipv6packet, err := i.parse_packet_tun(datain[len_ETHER:]) if err != nil { return nil, err } // Create the response buffer - dataout := make([]byte, ETHER+ipv6.HeaderLen+32) + dataout := make([]byte, len_ETHER+ipv6.HeaderLen+32) // Populate the response ethernet headers copy(dataout[:6], datain[6:12]) @@ -93,7 +93,7 @@ func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) // Copy the returned packet to our response ethernet frame - copy(dataout[ETHER:], ipv6packet) + copy(dataout[len_ETHER:], ipv6packet) return dataout, nil } @@ -157,7 +157,7 @@ func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, src net.IP, mt } // Create the response buffer - dataout := make([]byte, ETHER+len(ipv6packet)) + dataout := make([]byte, len_ETHER+len(ipv6packet)) // Populate the response ethernet headers copy(dataout[:6], dstmac[:6]) @@ -165,7 +165,7 @@ func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, src net.IP, mt binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) // Copy the returned packet to our response ethernet frame - copy(dataout[ETHER:], ipv6packet) + copy(dataout[len_ETHER:], ipv6packet) return dataout, nil } diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 6f11cb6a..c86159c7 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -48,19 +48,19 @@ func (m *multicast) init(core *Core) { m.core.log.Println("Found", len(m.interfaces), "multicast interface(s)") } -func (m *multicast) start() { +func (m *multicast) start() error { if len(m.core.ifceExpr) == 0 { m.core.log.Println("Multicast discovery is disabled") } else { m.core.log.Println("Multicast discovery is enabled") addr, err := net.ResolveUDPAddr("udp", m.groupAddr) if err != nil { - panic(err) + return err } listenString := fmt.Sprintf("[::]:%v", addr.Port) conn, err := net.ListenPacket("udp6", listenString) if err != nil { - panic(err) + return err } //defer conn.Close() // Let it close on its own when the application exits m.sock = ipv6.NewPacketConn(conn) @@ -72,6 +72,7 @@ func (m *multicast) start() { go m.listen() go m.announce() } + return nil } func (m *multicast) announce() { @@ -80,7 +81,7 @@ func (m *multicast) announce() { panic(err) } var anAddr net.TCPAddr - myAddr := m.core.DEBUG_getGlobalTCPAddr() + myAddr := m.core.tcp.getAddr() anAddr.Port = myAddr.Port destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { @@ -155,7 +156,7 @@ func (m *multicast) listen() { saddr := addr.String() //if _, isIn := n.peers[saddr]; isIn { continue } //n.peers[saddr] = struct{}{} - m.core.DEBUG_addTCPConn(saddr) + m.core.tcp.connect(saddr) //fmt.Println("DEBUG:", "added multicast peer:", saddr) } } diff --git a/src/yggdrasil/release.go b/src/yggdrasil/release.go new file mode 100644 index 00000000..fdbe8d5f --- /dev/null +++ b/src/yggdrasil/release.go @@ -0,0 +1,12 @@ +// +build !debug + +package yggdrasil + +import "errors" +import "log" + +// Starts the function profiler. This is only supported when built with +// '-tags build'. +func StartProfiler(_ *log.Logger) error { + return errors.New("Release builds do not support -pprof, build using '-tags debug'") +} diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index ad11d6e7..545a15be 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -64,7 +64,13 @@ func (r *router) init(core *Core) { r.core.tun.send = send r.reset = make(chan struct{}, 1) r.admin = make(chan func()) + // go r.mainLoop() +} + +func (r *router) start() error { + r.core.log.Println("Starting router") go r.mainLoop() + return nil } func (r *router) mainLoop() { diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index a005b106..1db63f61 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -167,6 +167,9 @@ func (t *switchTable) init(core *Core, key sigPubKey) { t.updater.Store(&sync.Once{}) t.table.Store(lookupTable{}) t.drop = make(map[sigPubKey]int64) +} + +func (t *switchTable) start() error { doTicker := func() { ticker := time.NewTicker(time.Second) defer ticker.Stop() @@ -176,6 +179,7 @@ func (t *switchTable) init(core *Core, key sigPubKey) { } } go doTicker() + return nil } func (t *switchTable) getLocator() switchLocator { diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 869b6afd..c152d0d1 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -16,6 +16,7 @@ import "errors" import "sync" import "fmt" import "bufio" +import "golang.org/x/net/proxy" const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense @@ -42,6 +43,32 @@ type tcpInfo struct { remoteAddr string } +func (iface *tcpInterface) getAddr() *net.TCPAddr { + return iface.serv.Addr().(*net.TCPAddr) +} + +func (iface *tcpInterface) connect(addr string) { + iface.call(addr) +} + +func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { + go func() { + dialer, err := proxy.SOCKS5("tcp", socksaddr, nil, proxy.Direct) + if err == nil { + conn, err := dialer.Dial("tcp", peeraddr) + if err == nil { + iface.callWithConn(&wrappedConn{ + c: conn, + raddr: &wrappedAddr{ + network: "tcp", + addr: peeraddr, + }, + }) + } + } + }() +} + func (iface *tcpInterface) init(core *Core, addr string) (err error) { iface.core = core @@ -51,7 +78,8 @@ func (iface *tcpInterface) init(core *Core, addr string) (err error) { iface.conns = make(map[tcpInfo](chan struct{})) go iface.listener() } - return + + return err } func (iface *tcpInterface) listener() { @@ -274,12 +302,14 @@ func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) { sock.SetReadDeadline(timeout) n, err := sock.Read(bs[len(frag):]) if err != nil || n == 0 { + // iface.core.log.Println(err) break } frag = bs[:len(frag)+n] for { msg, ok, err := tcp_chop_msg(&frag) if err != nil { + // iface.core.log.Println(err) return } if !ok { diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 5f5e50a0..1ecc53b2 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -5,8 +5,8 @@ package yggdrasil import "github.com/songgao/packets/ethernet" import "github.com/yggdrasil-network/water" -const IPv6_HEADER_LENGTH = 40 -const ETHER_HEADER_LENGTH = 14 +const tun_IPv6_HEADER_LENGTH = 40 +const tun_ETHER_HEADER_LENGTH = 14 type tunDevice struct { core *Core @@ -36,6 +36,15 @@ func (tun *tunDevice) init(core *Core) { tun.icmpv6.init(tun) } +func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int) error { + if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { + return err + } + go func() { panic(tun.read()) }() + go func() { panic(tun.write()) }() + return nil +} + func (tun *tunDevice) write() error { for { data := <-tun.recv @@ -50,7 +59,7 @@ func (tun *tunDevice) write() error { ethernet.NotTagged, // VLAN tagging ethernet.IPv6, // Ethertype len(data)) // Payload length - copy(frame[ETHER_HEADER_LENGTH:], data[:]) + copy(frame[tun_ETHER_HEADER_LENGTH:], data[:]) if _, err := tun.iface.Write(frame); err != nil { panic(err) } @@ -66,7 +75,7 @@ func (tun *tunDevice) write() error { func (tun *tunDevice) read() error { mtu := tun.mtu if tun.iface.IsTAP() { - mtu += ETHER_HEADER_LENGTH + mtu += tun_ETHER_HEADER_LENGTH } buf := make([]byte, mtu) for { @@ -77,10 +86,10 @@ func (tun *tunDevice) read() error { } o := 0 if tun.iface.IsTAP() { - o = ETHER_HEADER_LENGTH + o = tun_ETHER_HEADER_LENGTH } if buf[o]&0xf0 != 0x60 || - n != 256*int(buf[o+4])+int(buf[o+5])+IPv6_HEADER_LENGTH+o { + n != 256*int(buf[o+4])+int(buf[o+5])+tun_IPv6_HEADER_LENGTH+o { // Either not an IPv6 packet or not the complete packet for some reason //panic("Should not happen in testing") continue diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go index 72b9cf08..6096d6ac 100644 --- a/src/yggdrasil/tun_darwin.go +++ b/src/yggdrasil/tun_darwin.go @@ -33,7 +33,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) return tun.setupAddress(addr) } -const SIOCAIFADDR_IN6 = 2155899162 +const darwin_SIOCAIFADDR_IN6 = 2155899162 type in6_addrlifetime struct { ia6t_expire float64 @@ -103,9 +103,9 @@ func (tun *tunDevice) setupAddress(addr string) error { tun.core.log.Printf("Interface IPv6: %s", addr) tun.core.log.Printf("Interface MTU: %d", ir.ifru_mtu) - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(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 - tun.core.log.Printf("Error in SIOCAIFADDR_IN6: %v", errno) + tun.core.log.Printf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) return err } diff --git a/src/yggdrasil/udp.go b/src/yggdrasil/udp.go index 02fb9d6d..0be507f3 100644 --- a/src/yggdrasil/udp.go +++ b/src/yggdrasil/udp.go @@ -65,6 +65,25 @@ type udpKeys struct { sig sigPubKey } +func (iface *udpInterface) getAddr() *net.UDPAddr { + return iface.sock.LocalAddr().(*net.UDPAddr) +} + +func (iface *udpInterface) connect(saddr string) { + udpAddr, err := net.ResolveUDPAddr("udp", saddr) + if err != nil { + panic(err) + } + var addr connAddr + addr.fromUDPAddr(udpAddr) + iface.mutex.RLock() + _, isIn := iface.conns[addr] + iface.mutex.RUnlock() + if !isIn { + iface.sendKeys(addr) + } +} + func (iface *udpInterface) init(core *Core, addr string) (err error) { iface.core = core udpAddr, err := net.ResolveUDPAddr("udp", addr) diff --git a/src/yggdrasil/util.go b/src/yggdrasil/util.go index f8826491..908b1ae9 100644 --- a/src/yggdrasil/util.go +++ b/src/yggdrasil/util.go @@ -2,34 +2,10 @@ package yggdrasil // These are misc. utility functions that didn't really fit anywhere else -import "fmt" import "runtime" //import "sync" -func Util_testAddrIDMask() { - for idx := 0; idx < 16; idx++ { - var orig NodeID - orig[8] = 42 - for bidx := 0; bidx < idx; bidx++ { - orig[bidx/8] |= (0x80 >> uint8(bidx%8)) - } - addr := address_addrForNodeID(&orig) - nid, mask := addr.getNodeIDandMask() - for b := 0; b < len(mask); b++ { - nid[b] &= mask[b] - orig[b] &= mask[b] - } - if *nid != orig { - fmt.Println(orig) - fmt.Println(*addr) - fmt.Println(*nid) - fmt.Println(*mask) - panic(idx) - } - } -} - func util_yield() { runtime.Gosched() } diff --git a/yggdrasil.go b/yggdrasil.go index 91403bba..1fef8ac7 100644 --- a/yggdrasil.go +++ b/yggdrasil.go @@ -5,18 +5,13 @@ import "encoding/hex" import "flag" import "fmt" import "io/ioutil" -import "net" import "os" import "os/signal" import "syscall" import "time" import "regexp" import "math/rand" - -import _ "net/http/pprof" -import "net/http" import "log" -import "runtime" import "yggdrasil" import "yggdrasil/config" @@ -32,63 +27,20 @@ type node struct { core Core } -func (n *node) init(cfg *nodeConfig, logger *log.Logger) { - boxPub, err := hex.DecodeString(cfg.EncryptionPublicKey) - if err != nil { - panic(err) - } - boxPriv, err := hex.DecodeString(cfg.EncryptionPrivateKey) - if err != nil { - panic(err) - } - sigPub, err := hex.DecodeString(cfg.SigningPublicKey) - if err != nil { - panic(err) - } - sigPriv, err := hex.DecodeString(cfg.SigningPrivateKey) - if err != nil { - panic(err) - } - n.core.DEBUG_init(boxPub, boxPriv, sigPub, sigPriv) - n.core.DEBUG_setLogger(logger) - - logger.Println("Starting interface...") - n.core.DEBUG_setupAndStartGlobalTCPInterface(cfg.Listen) // Listen for peers on TCP - n.core.DEBUG_setupAndStartGlobalUDPInterface(cfg.Listen) // Also listen on UDP, TODO allow separate configuration for ip/port to listen on each of these - logger.Println("Started interface") - logger.Println("Starting admin socket...") - n.core.DEBUG_setupAndStartAdminInterface(cfg.AdminListen) - logger.Println("Started admin socket") - for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { - n.core.DEBUG_addAllowedEncryptionPublicKey(pBoxStr) - } - for _, ll := range cfg.MulticastInterfaces { - ifceExpr, err := regexp.Compile(ll) - if err != nil { - panic(err) - } - n.core.DEBUG_setIfceExpr(ifceExpr) - } - n.core.DEBUG_setupAndStartMulticastInterface() - - go func() { - if len(cfg.Peers) == 0 { - return - } - for { - for _, p := range cfg.Peers { - n.core.DEBUG_addPeer(p) - time.Sleep(time.Second) - } - time.Sleep(time.Minute) - } - }() -} - +// Generates default configuration. This is used when outputting the -genconf +// parameter and also when using -autoconf. The isAutoconf flag is used to +// determine whether the operating system should select a free port by itself +// (which guarantees that there will not be a conflict with any other services) +// or whether to generate a random port number. The only side effect of setting +// isAutoconf is that the TCP and UDP ports will likely end up with different +// port numbers. func generateConfig(isAutoconf bool) *nodeConfig { + // Create a new core. core := Core{} - bpub, bpriv := core.DEBUG_newBoxKeys() - spub, spriv := core.DEBUG_newSigKeys() + // Generate encryption keys. + bpub, bpriv := core.NewEncryptionKeys() + spub, spriv := core.NewSigningKeys() + // Create a node configuration and populate it. cfg := nodeConfig{} if isAutoconf { cfg.Listen = "[::]:0" @@ -104,13 +56,15 @@ func generateConfig(isAutoconf bool) *nodeConfig { cfg.Peers = []string{} cfg.AllowedEncryptionPublicKeys = []string{} cfg.MulticastInterfaces = []string{".*"} - cfg.IfName = core.DEBUG_GetTUNDefaultIfName() - cfg.IfMTU = core.DEBUG_GetTUNDefaultIfMTU() - cfg.IfTAPMode = core.DEBUG_GetTUNDefaultIfTAPMode() + cfg.IfName = core.GetTUNDefaultIfName() + cfg.IfMTU = core.GetTUNDefaultIfMTU() + cfg.IfTAPMode = core.GetTUNDefaultIfTAPMode() return &cfg } +// Generates a new configuration and returns it in HJSON format. This is used +// with -genconf. func doGenconf() string { cfg := generateConfig(false) bs, err := hjson.Marshal(cfg) @@ -120,30 +74,43 @@ func doGenconf() string { return string(bs) } -var pprof = flag.Bool("pprof", false, "Run pprof, see http://localhost:6060/debug/pprof/") -var genconf = flag.Bool("genconf", false, "print a new config to stdout") -var useconf = flag.Bool("useconf", false, "read config from stdin") -var useconffile = flag.String("useconffile", "", "read config from specified file path") -var normaliseconf = flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised") -var autoconf = flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") - +// The main function is responsible for configuring and starting Yggdrasil. func main() { + // Configure the command line parameters. + pprof := flag.Bool("pprof", false, "Run pprof, see http://localhost:6060/debug/pprof/") + genconf := flag.Bool("genconf", false, "print a new config to stdout") + useconf := flag.Bool("useconf", false, "read config from stdin") + useconffile := flag.String("useconffile", "", "read config from specified file path") + normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised") + autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") flag.Parse() + var cfg *nodeConfig switch { case *autoconf: + // Use an autoconf-generated config, this will give us random keys and + // port numbers, and will use an automatically selected TUN/TAP interface. cfg = generateConfig(true) case *useconffile != "" || *useconf: + // Use a configuration file. If -useconf, the configuration will be read + // from stdin. If -useconffile, the configuration will be read from the + // filesystem. var config []byte var err error if *useconffile != "" { + // Read the file from the filesystem config, err = ioutil.ReadFile(*useconffile) } else { + // Read the file from stdin. config, err = ioutil.ReadAll(os.Stdin) } 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 = generateConfig(false) var dat map[string]interface{} if err := hjson.Unmarshal(config, &dat); err != nil { @@ -155,7 +122,8 @@ func main() { } 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 + // configuration to match the new configuration format, as some of the key + // names have changed recently. changes := map[string]string{ "Multicast": "", "LinkLocal": "MulticastInterfaces", @@ -165,6 +133,7 @@ func main() { "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 == "" { @@ -175,15 +144,24 @@ func main() { 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] } } } } + // 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 + // configuration and print it back to stdout. This lets the user update + // their configuration file with newly mapped names (like above) or to + // convert from plain JSON to commented HJSON. if *normaliseconf { bs, err := hjson.Marshal(cfg) if err != nil { @@ -193,48 +171,86 @@ func main() { return } case *genconf: + // Generate a new configuration and print it to stdout. fmt.Println(doGenconf()) default: + // No flags were provided, therefore print the list of flags to stdout. flag.PrintDefaults() } + // Have we got a working configuration? If we don't then it probably means + // that neither -autoconf, -useconf or -useconffile were set above. Stop + // if we don't. if cfg == nil { return } + // Create a new logger that logs output to stdout. logger := log.New(os.Stdout, "", log.Flags()) + // If the -pprof flag was provided then start the pprof service on port 6060. if *pprof { - runtime.SetBlockProfileRate(1) - go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() + if err := yggdrasil.StartProfiler(logger); err != nil { + logger.Println(err) + } } - // Setup - logger.Println("Initializing...") + // Setup the Yggdrasil node itself. The node{} type includes a Core, so we + // don't need to create this manually. n := node{} - n.init(cfg, logger) - if cfg.IfName != "none" { - logger.Println("Starting TUN/TAP...") - } else { - logger.Println("Not starting TUN/TAP") + // 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) } - //n.core.DEBUG_startTun(cfg.IfName) // 1280, the smallest supported MTU - n.core.DEBUG_startTunWithMTU(cfg.IfName, cfg.IfTAPMode, cfg.IfMTU) // Largest supported MTU - defer func() { - logger.Println("Closing...") - n.core.DEBUG_stopTun() + // 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, and we have provided any allowed + // encryption keys or multicast interface expressions, we can now actually + // start Yggdrasil. This will start the router, switch, DHT node, TCP and UDP + // sockets, TUN/TAP adapter and multicast discovery port. + if err := n.core.Start(cfg, logger); err != nil { + logger.Println("An error occurred during startup") + panic(err) + } + // 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 { + return + } + for { + for _, p := range cfg.Peers { + n.core.AddPeer(p) + time.Sleep(time.Second) + } + time.Sleep(time.Minute) + } }() - logger.Println("Started...") - address := (*n.core.GetAddress())[:] - subnet := (*n.core.GetSubnet())[:] - subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0) - logger.Printf("Your IPv6 address is %s", net.IP(address).String()) - logger.Printf("Your IPv6 subnet is %s/64", net.IP(subnet).String()) - // Catch interrupt to exit gracefully + // The Stop function ensures that the TUN/TAP adapter is correctly shut down + // before the program exits. + defer func() { + n.core.Stop() + }() + // Make some nice output that tells us what our IPv6 address and subnet are. + // This is just logged to stdout for the user. + address := n.core.GetAddress() + subnet := n.core.GetSubnet() + logger.Printf("Your IPv6 address is %s", address.String()) + logger.Printf("Your IPv6 subnet is %s", subnet.String()) + // Catch interrupts from the operating system to exit gracefully. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) - // Create a function to capture the service being stopped on Windows + // Create a function to capture the service being stopped on Windows. winTerminate := func() { c <- os.Interrupt } minwinsvc.SetOnExit(winTerminate) - // Wait for the terminate/interrupt signal + // Wait for the terminate/interrupt signal. Once a signal is received, the + // deferred Stop function above will run which will shut down TUN/TAP. <-c - logger.Println("Stopping...") }