5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-09-19 14:59:38 +00:00

add tcp support and use it by default

This commit is contained in:
Arceliar 2018-02-09 17:42:55 -06:00
parent 4acce71468
commit d424489329
4 changed files with 16 additions and 291 deletions

View File

@ -43,6 +43,9 @@ In practice, you probably want to run this instead:
This keeps a persistent set of keys (and by extension, IP address) and gives you the option of editing the configuration file.
If you want to use it as an overlay network on top of e.g. the internet, then you can do so by adding the remote devices domain/address and port (as a string, e.g. `"1.2.3.4:5678"`) to the list of `Peers` in the configuration file.
You can control whether or not it peers over TCP or UDP by adding `tcp:` or `udp:` to the start of the string, i.e. `"udp:1.2.3.4:5678"`.
It is currently configured to accept incoming TCP and UDP connections.
In the interest of testing the TCP machinery, it's set to create TCP connections for auto-peering (over link-local IPv6), and to use TCP by default if no transport is specified for a manually configured peer.
### Platforms

View File

@ -1,283 +0,0 @@
package main
import "bytes"
import "encoding/hex"
import "encoding/json"
import "flag"
import "fmt"
import "io/ioutil"
import "net"
import "os"
import "os/signal"
import "time"
import "regexp"
import _ "net/http/pprof"
import "net/http"
import "log"
import "runtime"
import "golang.org/x/net/ipv6"
import . "yggdrasil"
/**
* This is a very crude wrapper around src/yggdrasil
* It can generate a new config (--genconf)
* It can read a config from stdin (--useconf)
* It can run with an automatic config (--autoconf)
*/
type nodeConfig struct {
Listen string
AdminListen string
Peers []string
BoxPub string
BoxPriv string
SigPub string
SigPriv string
Multicast bool
LinkLocal string
IfName string
}
type node struct {
core Core
sock *ipv6.PacketConn
}
func (n *node) init(cfg *nodeConfig, logger *log.Logger) {
boxPub, err := hex.DecodeString(cfg.BoxPub)
if err != nil {
panic(err)
}
boxPriv, err := hex.DecodeString(cfg.BoxPriv)
if err != nil {
panic(err)
}
sigPub, err := hex.DecodeString(cfg.SigPub)
if err != nil {
panic(err)
}
sigPriv, err := hex.DecodeString(cfg.SigPriv)
if err != nil {
panic(err)
}
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)
logger.Println("Started interface")
logger.Println("Starting admin socket...")
n.core.DEBUG_setupAndStartAdminInterface(cfg.AdminListen)
logger.Println("Started admin socket")
go func() {
if len(cfg.Peers) == 0 {
return
}
for {
for _, p := range cfg.Peers {
n.core.DEBUG_addTCPConn(p)
time.Sleep(time.Second)
}
time.Sleep(time.Minute)
}
}()
}
func generateConfig() *nodeConfig {
core := Core{}
bpub, bpriv := core.DEBUG_newBoxKeys()
spub, spriv := core.DEBUG_newSigKeys()
cfg := nodeConfig{}
cfg.Listen = "[::]:0"
cfg.AdminListen = "localhost:9001"
cfg.BoxPub = hex.EncodeToString(bpub[:])
cfg.BoxPriv = hex.EncodeToString(bpriv[:])
cfg.SigPub = hex.EncodeToString(spub[:])
cfg.SigPriv = hex.EncodeToString(spriv[:])
cfg.Peers = []string{}
cfg.Multicast = true
cfg.LinkLocal = ""
cfg.IfName = "auto"
return &cfg
}
func doGenconf() string {
cfg := generateConfig()
bs, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
panic(err)
}
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
tcpAddr := n.core.DEBUG_getGlobalTCPAddr()
anAddr.Port = tcpAddr.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")
var autoconf = flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
func main() {
flag.Parse()
var cfg *nodeConfig
switch {
case *autoconf:
cfg = generateConfig()
case *useconf:
config, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
decoder := json.NewDecoder(bytes.NewReader(config))
cfg = generateConfig()
err = decoder.Decode(cfg)
if err != nil {
panic(err)
}
case *genconf:
fmt.Println(doGenconf())
default:
flag.PrintDefaults()
}
if cfg == nil {
return
}
logger := log.New(os.Stdout, "", log.Flags())
if *pprof {
runtime.SetBlockProfileRate(1)
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()
}
// Setup
logger.Println("Initializing...")
n := node{}
n.init(cfg, logger)
logger.Println("Starting tun...")
//n.core.DEBUG_startTun(cfg.IfName) // 1280, the smallest supported MTU
n.core.DEBUG_startTunWithMTU(cfg.IfName, 65535) // Largest supported MTU
defer func() {
logger.Println("Closing...")
n.core.DEBUG_stopTun()
}()
logger.Println("Started...")
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, os.Kill)
<-c
logger.Println("Stopping...")
}

View File

@ -319,7 +319,7 @@ func (iface *udpInterface) reader() {
////////////////////////////////////////////////////////////////////////////////
const udp_chunkSize = 1024
const udp_chunkSize = 508 // Apparently the maximum guaranteed safe IPv4 size
func udp_decode(bs []byte) (chunks, chunk, count uint8, payload []byte) {
if len(bs) >= 3 {

View File

@ -71,7 +71,8 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) {
}
n.core.DEBUG_setIfceExpr(ifceExpr)
logger.Println("Starting interface...")
n.core.DEBUG_setupAndStartGlobalUDPInterface(cfg.Listen)
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)
@ -82,7 +83,11 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) {
}
for {
for _, p := range cfg.Peers {
n.core.DEBUG_maybeSendUDPKeys(p)
switch {
case len(p) >= 4 && p[:4] == "udp:": n.core.DEBUG_maybeSendUDPKeys(p[4:])
case len(p) >= 4 && p[:4] == "tcp:": n.core.DEBUG_addTCPConn(p[4:])
default: n.core.DEBUG_addTCPConn(p)
}
time.Sleep(time.Second)
}
time.Sleep(time.Minute)
@ -144,7 +149,7 @@ func (n *node) listen() {
}
}
anAddr := string(bs[:nBytes])
addr, err := net.ResolveUDPAddr("udp6", anAddr)
addr, err := net.ResolveTCPAddr("tcp6", anAddr)
if err != nil {
panic(err)
continue
@ -158,7 +163,7 @@ func (n *node) listen() {
saddr := addr.String()
//if _, isIn := n.peers[saddr]; isIn { continue }
//n.peers[saddr] = struct{}{}
n.core.DEBUG_maybeSendUDPKeys(saddr)
n.core.DEBUG_addTCPConn(saddr) // FIXME? can result in 2 connections per peer
//fmt.Println("DEBUG:", "added multicast peer:", saddr)
}
}
@ -168,9 +173,9 @@ func (n *node) announce() {
if err != nil {
panic(err)
}
var anAddr net.UDPAddr
udpAddr := n.core.DEBUG_getGlobalUDPAddr()
anAddr.Port = udpAddr.Port
var anAddr net.TCPAddr
tcpAddr := n.core.DEBUG_getGlobalTCPAddr()
anAddr.Port = tcpAddr.Port
destAddr, err := net.ResolveUDPAddr("udp6", multicastAddr)
if err != nil {
panic(err)