5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-10 05:10:26 +00:00

Merge pull request #96 from neilalexander/dedebug

Create Core API, remove DEBUG function calls
This commit is contained in:
Arceliar 2018-05-29 16:01:23 -05:00 committed by GitHub
commit 742eded4ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 453 additions and 163 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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)
}
}

12
src/yggdrasil/release.go Normal file
View File

@ -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'")
}

View File

@ -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() {

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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...")
}