diff --git a/src/yggdrasil/config/config.go b/src/yggdrasil/config/config.go index 36efe49..3d2e408 100644 --- a/src/yggdrasil/config/config.go +++ b/src/yggdrasil/config/config.go @@ -10,8 +10,8 @@ type NodeConfig struct { BoxPriv string `json:"EncryptionPrivateKey" comment:"Your private encryption key (do not share this with anyone!)"` SigPub string `json:"SigningPublicKey" comment:"Your public signing key"` SigPriv string `json:"SigningPrivateKey" comment:"Your private signing key (do not share this with anyone!)"` - Multicast bool `comment:"Enable or disable automatic peer discovery on the same LAN using multicast"` - LinkLocal string `json:"MulticastInterfaces" comment:"Regex for which interfaces multicast peer discovery should be enabled on"` + Multicast bool `json:"MulticastEnabled,omitempty" comment:"Enable or disable automatic peer discovery on the same LAN using multicast"` + LinkLocal []string `json:"MulticastInterfaces" comment:"Regexes for which interfaces multicast peer discovery should be enabled\non. If none specified, multicast peer discovery is disabled"` IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\", or \"none\""` IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode (if supported\nby your platform, option will be ignored if not)"` IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local network interface"` diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 10c46f3..7b07794 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -19,10 +19,11 @@ type Core struct { tun tunDevice admin admin searches searches + multicast multicast tcp *tcpInterface udp *udpInterface log *log.Logger - ifceExpr *regexp.Regexp // the zone of link-local IPv6 peers must match this + ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } func (c *Core) Init() { @@ -49,6 +50,7 @@ func (c *Core) init(bpub *boxPubKey, c.searches.init(c) c.dht.init(c) c.sessions.init(c) + c.multicast.init(c) c.peers.init(c) c.router.init(c) c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 32c52e3..7b27082 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -387,6 +387,13 @@ func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) { c.admin = a } +func (c *Core) DEBUG_setupAndStartMulticastInterface() { + m := multicast{} + m.init(c) + c.multicast = m + m.Start() +} + //////////////////////////////////////////////////////////////////////////////// func (c *Core) DEBUG_setLogger(log *log.Logger) { @@ -394,7 +401,7 @@ func (c *Core) DEBUG_setLogger(log *log.Logger) { } func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) { - c.ifceExpr = expr + c.ifceExpr = append(c.ifceExpr, expr) } func (c *Core) DEBUG_addAllowedBoxPub(boxStr string) { diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go new file mode 100644 index 0000000..80dbf07 --- /dev/null +++ b/src/yggdrasil/multicast.go @@ -0,0 +1,160 @@ +package yggdrasil + +import "net" +import "time" +import "fmt" + +import "golang.org/x/net/ipv6" + +type multicast struct { + core *Core + sock *ipv6.PacketConn + groupAddr string + interfaces []net.Interface +} + +func (m *multicast) init(core *Core) { + m.core = core + m.groupAddr = "[ff02::114]:9001" + // Ask the system for network interfaces + allifaces, err := net.Interfaces() + if err != nil { + panic(err) + } + // Work out which interfaces to announce on + for _, iface := range allifaces { + if iface.Flags & net.FlagUp == 0 { + // Ignore interfaces that are down + continue + } + if iface.Flags & net.FlagMulticast == 0 { + // Ignore non-multicast interfaces + continue + } + if iface.Flags & net.FlagPointToPoint != 0 { + // Ignore point-to-point interfaces + continue + } + for _, expr := range m.core.ifceExpr { + m.core.log.Println(expr) + if expr.MatchString(iface.Name) { + m.core.log.Println(iface.Name, "matched", expr) + m.interfaces = append(m.interfaces, iface) + } + } + } + m.core.log.Println("Found", len(m.interfaces), "multicast interfaces") +} + +func (m *multicast) Start() { + if len(m.core.ifceExpr) == 0 { + m.core.log.Println("Not starting multicast discovery") + } else { + m.core.log.Println("Starting multicast discovery...") + addr, err := net.ResolveUDPAddr("udp", m.groupAddr) + if err != nil { + panic(err) + } + listenString := fmt.Sprintf("[::]:%v", addr.Port) + conn, err := net.ListenPacket("udp6", listenString) + if err != nil { + panic(err) + } + //defer conn.Close() // Let it close on its own when the application exits + m.sock = ipv6.NewPacketConn(conn) + if err = m.sock.SetControlMessage(ipv6.FlagDst, true); err != nil { + // Windows can't set this flag, so we need to handle it in other ways + //panic(err) + } + + go m.listen() + go m.announce() + } +} + +func (m *multicast) announce() { + groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + if err != nil { + panic(err) + } + var anAddr net.TCPAddr + myAddr := m.core.DEBUG_getGlobalTCPAddr() + anAddr.Port = myAddr.Port + destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + if err != nil { + panic(err) + } + for { + for _, iface := range m.interfaces { + + m.sock.JoinGroup(&iface, groupAddr) + //err := n.sock.JoinGroup(&iface, groupAddr) + //if err != nil { panic(err) } + addrs, err := iface.Addrs() + if err != nil { + panic(err) + } + for _, addr := range addrs { + addrIP, _, _ := net.ParseCIDR(addr.String()) + if addrIP.To4() != nil { + continue + } // IPv6 only + if !addrIP.IsLinkLocalUnicast() { + continue + } + anAddr.IP = addrIP + anAddr.Zone = iface.Name + destAddr.Zone = iface.Name + msg := []byte(anAddr.String()) + m.sock.WriteTo(msg, nil, destAddr) + break + } + time.Sleep(time.Second) + } + time.Sleep(time.Second) + } +} + +func (m *multicast) listen() { + groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + if err != nil { + panic(err) + } + bs := make([]byte, 2048) + for { + nBytes, rcm, fromAddr, err := m.sock.ReadFrom(bs) + if err != nil { + panic(err) + } + //if rcm == nil { continue } // wat + //fmt.Println("DEBUG:", "packet from:", fromAddr.String()) + if rcm != nil { + // Windows can't set the flag needed to return a non-nil value here + // So only make these checks if we get something useful back + // TODO? Skip them always, I'm not sure if they're really needed... + if !rcm.Dst.IsLinkLocalMulticast() { + continue + } + if !rcm.Dst.Equal(groupAddr.IP) { + continue + } + } + anAddr := string(bs[:nBytes]) + addr, err := net.ResolveTCPAddr("tcp6", anAddr) + if err != nil { + panic(err) + continue + } // Panic for testing, remove later + from := fromAddr.(*net.UDPAddr) + //fmt.Println("DEBUG:", "heard:", addr.IP.String(), "from:", from.IP.String()) + if addr.IP.String() != from.IP.String() { + continue + } + addr.Zone = from.Zone + saddr := addr.String() + //if _, isIn := n.peers[saddr]; isIn { continue } + //n.peers[saddr] = struct{}{} + m.core.DEBUG_addTCPConn(saddr) + //fmt.Println("DEBUG:", "added multicast peer:", saddr) + } +} diff --git a/src/yggdrasil/udp.go b/src/yggdrasil/udp.go index 03663b6..6eeabdc 100644 --- a/src/yggdrasil/udp.go +++ b/src/yggdrasil/udp.go @@ -341,11 +341,17 @@ func (iface *udpInterface) reader() { if them.isValid() { continue } - if udpAddr.IP.IsLinkLocalUnicast() && - !iface.core.ifceExpr.MatchString(udpAddr.Zone) { - continue + if udpAddr.IP.IsLinkLocalUnicast() { + if len(iface.core.ifceExpr) == 0 { + break + } + for _, expr := range iface.core.ifceExpr { + if expr.MatchString(udpAddr.Zone) { + iface.handleKeys(msg, addr) + break + } + } } - iface.handleKeys(msg, addr) case udp_isClose(msg): iface.handleClose(msg, addr) default: diff --git a/yggdrasil.go b/yggdrasil.go index 7115bde..2877dfd 100644 --- a/yggdrasil.go +++ b/yggdrasil.go @@ -17,8 +17,6 @@ import "net/http" import "log" import "runtime" -import "golang.org/x/net/ipv6" - import "yggdrasil" import "yggdrasil/config" @@ -31,7 +29,6 @@ type Core = yggdrasil.Core type node struct { core Core - sock *ipv6.PacketConn } func (n *node) init(cfg *nodeConfig, logger *log.Logger) { @@ -53,11 +50,6 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { } n.core.DEBUG_init(boxPub, boxPriv, sigPub, sigPriv) n.core.DEBUG_setLogger(logger) - ifceExpr, err := regexp.Compile(cfg.LinkLocal) - if err != nil { - panic(err) - } - n.core.DEBUG_setIfceExpr(ifceExpr) logger.Println("Starting interface...") n.core.DEBUG_setupAndStartGlobalTCPInterface(cfg.Listen) // Listen for peers on TCP @@ -69,6 +61,17 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { for _, pBoxStr := range cfg.AllowedBoxPubs { n.core.DEBUG_addAllowedBoxPub(pBoxStr) } + logger.Println(cfg.LinkLocal) + for _, ll := range cfg.LinkLocal { + logger.Println("Adding expression", ll) + ifceExpr, err := regexp.Compile(ll) + if err != nil { + panic(err) + } + logger.Println("Added expression", ifceExpr) + n.core.DEBUG_setIfceExpr(ifceExpr) + } + n.core.DEBUG_setupAndStartMulticastInterface() go func() { if len(cfg.Peers) == 0 { @@ -102,8 +105,8 @@ func generateConfig(isAutoconf bool) *nodeConfig { cfg.SigPriv = hex.EncodeToString(spriv[:]) cfg.Peers = []string{} cfg.AllowedBoxPubs = []string{} - cfg.Multicast = true - cfg.LinkLocal = "" + cfg.Multicast = false + cfg.LinkLocal = []string{} cfg.IfName = core.DEBUG_GetTUNDefaultIfName() cfg.IfMTU = core.DEBUG_GetTUNDefaultIfMTU() cfg.IfTAPMode = core.DEBUG_GetTUNDefaultIfTAPMode() @@ -120,98 +123,6 @@ func doGenconf() string { return string(bs) } -var multicastAddr = "[ff02::114]:9001" - -func (n *node) listen() { - groupAddr, err := net.ResolveUDPAddr("udp6", multicastAddr) - if err != nil { - panic(err) - } - bs := make([]byte, 2048) - for { - nBytes, rcm, fromAddr, err := n.sock.ReadFrom(bs) - if err != nil { - panic(err) - } - //if rcm == nil { continue } // wat - //fmt.Println("DEBUG:", "packet from:", fromAddr.String()) - if rcm != nil { - // Windows can't set the flag needed to return a non-nil value here - // So only make these checks if we get something useful back - // TODO? Skip them always, I'm not sure if they're really needed... - if !rcm.Dst.IsLinkLocalMulticast() { - continue - } - if !rcm.Dst.Equal(groupAddr.IP) { - continue - } - } - anAddr := string(bs[:nBytes]) - addr, err := net.ResolveTCPAddr("tcp6", anAddr) - if err != nil { - panic(err) - continue - } // Panic for testing, remove later - from := fromAddr.(*net.UDPAddr) - //fmt.Println("DEBUG:", "heard:", addr.IP.String(), "from:", from.IP.String()) - if addr.IP.String() != from.IP.String() { - continue - } - addr.Zone = from.Zone - saddr := addr.String() - //if _, isIn := n.peers[saddr]; isIn { continue } - //n.peers[saddr] = struct{}{} - n.core.DEBUG_addTCPConn(saddr) - //fmt.Println("DEBUG:", "added multicast peer:", saddr) - } -} - -func (n *node) announce() { - groupAddr, err := net.ResolveUDPAddr("udp6", multicastAddr) - if err != nil { - panic(err) - } - var anAddr net.TCPAddr - myAddr := n.core.DEBUG_getGlobalTCPAddr() - anAddr.Port = myAddr.Port - destAddr, err := net.ResolveUDPAddr("udp6", multicastAddr) - if err != nil { - panic(err) - } - for { - ifaces, err := net.Interfaces() - if err != nil { - panic(err) - } - for _, iface := range ifaces { - n.sock.JoinGroup(&iface, groupAddr) - //err := n.sock.JoinGroup(&iface, groupAddr) - //if err != nil { panic(err) } - addrs, err := iface.Addrs() - if err != nil { - panic(err) - } - for _, addr := range addrs { - addrIP, _, _ := net.ParseCIDR(addr.String()) - if addrIP.To4() != nil { - continue - } // IPv6 only - if !addrIP.IsLinkLocalUnicast() { - continue - } - anAddr.IP = addrIP - anAddr.Zone = iface.Name - destAddr.Zone = iface.Name - msg := []byte(anAddr.String()) - n.sock.WriteTo(msg, nil, destAddr) - break - } - time.Sleep(time.Second) - } - time.Sleep(time.Second) - } -} - 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") @@ -277,25 +188,6 @@ func main() { 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()) - if cfg.Multicast { - addr, err := net.ResolveUDPAddr("udp", multicastAddr) - if err != nil { - panic(err) - } - listenString := fmt.Sprintf("[::]:%v", addr.Port) - conn, err := net.ListenPacket("udp6", listenString) - if err != nil { - panic(err) - } - //defer conn.Close() // Let it close on its own when the application exits - n.sock = ipv6.NewPacketConn(conn) - if err = n.sock.SetControlMessage(ipv6.FlagDst, true); err != nil { - // Windows can't set this flag, so we need to handle it in other ways - //panic(err) - } - go n.listen() - go n.announce() - } // Catch interrupt to exit gracefully c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM)