2017-12-28 22:16:20 -06:00
package main
2018-06-15 04:30:09 -05:00
import (
2021-07-01 08:04:01 -05:00
"context"
2021-05-15 15:55:47 -05:00
"crypto/ed25519"
2019-06-11 12:50:01 +01:00
"encoding/hex"
2018-06-15 04:30:09 -05:00
"encoding/json"
"flag"
"fmt"
2019-11-10 19:38:35 +00:00
"net"
2018-06-15 04:30:09 -05:00
"os"
"os/signal"
2022-09-03 11:42:05 +01:00
"regexp"
2018-12-14 17:49:42 +00:00
"strings"
2018-06-15 04:30:09 -05:00
"syscall"
2017-12-28 22:16:20 -06:00
2019-01-27 13:31:43 +00:00
"github.com/gologme/log"
2019-06-29 00:32:23 +01:00
gsyslog "github.com/hashicorp/go-syslog"
2023-04-06 21:45:49 +01:00
"github.com/hjson/hjson-go/v4"
2018-06-15 04:30:09 -05:00
"github.com/kardianos/minwinsvc"
2017-12-28 22:16:20 -06:00
2019-11-10 19:38:35 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/address"
2019-05-19 17:27:48 +01:00
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
2018-12-07 19:56:04 -06:00
"github.com/yggdrasil-network/yggdrasil-go/src/config"
2022-09-17 20:07:00 +01:00
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
2021-05-23 14:33:28 -05:00
2021-05-23 14:42:26 -05:00
"github.com/yggdrasil-network/yggdrasil-go/src/core"
2019-03-28 16:13:14 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
2022-09-24 14:41:47 +01:00
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
2019-08-11 00:31:22 +03:00
"github.com/yggdrasil-network/yggdrasil-go/src/version"
2018-06-15 04:30:09 -05:00
)
2018-02-16 14:12:44 +00:00
2017-12-28 22:16:20 -06:00
type node struct {
2022-07-24 10:23:25 +01:00
core * core . Core
2022-09-24 14:41:47 +01:00
tun * tun . TunAdapter
2021-06-05 20:57:03 +01:00
multicast * multicast . Multicast
admin * admin . AdminSocket
2017-12-28 22:16:20 -06:00
}
2023-04-06 21:45:49 +01:00
// The main function is responsible for configuring and starting Yggdrasil.
func main ( ) {
2018-05-27 22:13:37 +01:00
genconf := flag . Bool ( "genconf" , false , "print a new config to stdout" )
2018-12-02 23:52:57 +00:00
useconf := flag . Bool ( "useconf" , false , "read HJSON/JSON config from stdin" )
useconffile := flag . String ( "useconffile" , "" , "read HJSON/JSON config from specified file path" )
2018-05-27 22:13:37 +01:00
normaliseconf := flag . Bool ( "normaliseconf" , false , "use in combination with either -useconf or -useconffile, outputs your configuration normalised" )
2023-04-06 21:45:49 +01:00
exportkey := flag . Bool ( "exportkey" , false , "use in combination with either -useconf or -useconffile, outputs your private key in PEM format" )
exportcsr := flag . Bool ( "exportcsr" , false , "use in combination with either -useconf or -useconffile, outputs your self-signed certificate request in PEM format" )
exportcert := flag . Bool ( "exportcert" , false , "use in combination with either -useconf or -useconffile, outputs your self-signed certificate in PEM format" )
2018-12-02 23:49:48 +00:00
confjson := flag . Bool ( "json" , false , "print configuration from -genconf or -normaliseconf as JSON instead of HJSON" )
2018-05-27 22:13:37 +01:00
autoconf := flag . Bool ( "autoconf" , false , "automatic mode (dynamic IP, peer with IPv6 neighbors)" )
2019-08-14 20:09:02 +01:00
ver := flag . Bool ( "version" , false , "prints the version of this build" )
2019-06-28 23:45:04 +01:00
logto := flag . String ( "logto" , "stdout" , "file path to log to, \"syslog\" or \"stdout\"" )
2019-11-10 19:38:35 +00:00
getaddr := flag . Bool ( "address" , false , "returns the IPv6 address as derived from the supplied configuration" )
getsnet := flag . Bool ( "subnet" , false , "returns the IPv6 subnet as derived from the supplied configuration" )
2019-11-30 20:46:29 +02:00
loglevel := flag . String ( "loglevel" , "info" , "loglevel to enable" )
2018-01-04 22:37:51 +00:00
flag . Parse ( )
2021-07-01 08:04:01 -05:00
2023-04-06 21:45:49 +01:00
// Catch interrupts from the operating system to exit gracefully.
ctx , cancel := signal . NotifyContext ( context . Background ( ) , os . Interrupt , syscall . SIGTERM )
// Capture the service being stopped on Windows.
minwinsvc . SetOnExit ( cancel )
2021-06-05 21:32:04 +01:00
// Create a new logger that logs output to stdout.
var logger * log . Logger
2023-04-06 21:45:49 +01:00
switch * logto {
2021-06-05 21:32:04 +01:00
case "stdout" :
logger = log . New ( os . Stdout , "" , log . Flags ( ) )
2023-04-06 21:45:49 +01:00
2021-06-05 21:32:04 +01:00
case "syslog" :
if syslogger , err := gsyslog . NewLogger ( gsyslog . LOG_NOTICE , "DAEMON" , version . BuildName ( ) ) ; err == nil {
logger = log . New ( syslogger , "" , log . Flags ( ) )
}
2023-04-06 21:45:49 +01:00
2021-06-05 21:32:04 +01:00
default :
2023-04-06 21:45:49 +01:00
if logfd , err := os . OpenFile ( * logto , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 ) ; err == nil {
2021-06-05 21:32:04 +01:00
logger = log . New ( logfd , "" , log . Flags ( ) )
}
}
if logger == nil {
logger = log . New ( os . Stdout , "" , log . Flags ( ) )
logger . Warnln ( "Logging defaulting to stdout" )
}
2023-04-06 21:45:49 +01:00
if * normaliseconf {
2021-06-05 21:49:11 +01:00
setLogLevel ( "error" , logger )
} else {
2023-04-06 21:45:49 +01:00
setLogLevel ( * loglevel , logger )
2021-06-05 21:49:11 +01:00
}
2021-06-05 21:32:04 +01:00
2023-04-06 21:45:49 +01:00
cfg := config . GenerateConfig ( )
2018-12-30 12:26:55 +00:00
var err error
2018-01-04 22:37:51 +00:00
switch {
2023-04-06 21:45:49 +01:00
case * ver :
2019-08-11 00:31:22 +03:00
fmt . Println ( "Build name:" , version . BuildName ( ) )
fmt . Println ( "Build version:" , version . BuildVersion ( ) )
2019-08-14 20:09:02 +01:00
return
2023-04-06 21:45:49 +01:00
case * autoconf :
2018-05-27 22:13:37 +01:00
// Use an autoconf-generated config, this will give us random keys and
2022-09-24 14:41:47 +01:00
// port numbers, and will use an automatically selected TUN interface.
2023-04-06 21:45:49 +01:00
case * useconf :
if _ , err := cfg . ReadFrom ( os . Stdin ) ; err != nil {
panic ( err )
2018-05-23 12:04:27 +01:00
}
2023-04-06 21:45:49 +01:00
case * useconffile != "" :
f , err := os . Open ( * useconffile )
if err != nil {
panic ( err )
}
if _ , err := cfg . ReadFrom ( f ) ; err != nil {
panic ( err )
}
_ = f . Close ( )
case * genconf :
var bs [ ] byte
if * confjson {
bs , err = json . MarshalIndent ( cfg , "" , " " )
} else {
bs , err = hjson . Marshal ( cfg )
}
if err != nil {
panic ( err )
}
fmt . Println ( string ( bs ) )
2021-07-03 17:27:00 -05:00
return
2023-04-06 21:45:49 +01:00
2018-01-04 22:37:51 +00:00
default :
2022-11-08 23:19:43 +01:00
fmt . Println ( "Usage:" )
2018-01-04 22:37:51 +00:00
flag . PrintDefaults ( )
2022-11-08 23:19:43 +01:00
2023-04-06 21:45:49 +01:00
if * getaddr || * getsnet {
2022-11-08 23:19:43 +01:00
fmt . Println ( "\nError: You need to specify some config data using -useconf or -useconffile." )
}
2018-01-04 22:37:51 +00:00
}
2023-04-06 21:45:49 +01:00
privateKey := ed25519 . PrivateKey ( cfg . PrivateKey )
publicKey := privateKey . Public ( ) . ( ed25519 . PublicKey )
switch {
case * getaddr :
addr := address . AddrForKey ( publicKey )
ip := net . IP ( addr [ : ] )
fmt . Println ( ip . String ( ) )
2018-01-04 22:37:51 +00:00
return
2023-04-06 21:45:49 +01:00
case * getsnet :
snet := address . SubnetForKey ( publicKey )
ipnet := net . IPNet {
IP : append ( snet [ : ] , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ,
Mask : net . CIDRMask ( len ( snet ) * 8 , 128 ) ,
2021-05-23 14:33:28 -05:00
}
2023-04-06 21:45:49 +01:00
fmt . Println ( ipnet . String ( ) )
return
case * normaliseconf :
var bs [ ] byte
if * confjson {
bs , err = json . MarshalIndent ( cfg , "" , " " )
} else {
bs , err = hjson . Marshal ( cfg )
2019-11-10 19:38:35 +00:00
}
2023-04-06 21:45:49 +01:00
if err != nil {
panic ( err )
}
fmt . Println ( string ( bs ) )
2019-11-10 19:38:35 +00:00
return
2023-04-06 21:45:49 +01:00
case * exportkey :
pem , err := cfg . MarshalPEMPrivateKey ( )
if err != nil {
panic ( err )
}
fmt . Println ( string ( pem ) )
return
case * exportcsr :
pem , err := cfg . GenerateCertificateSigningRequest ( )
if err != nil {
panic ( err )
2019-11-10 19:38:35 +00:00
}
2023-04-06 21:45:49 +01:00
fmt . Println ( string ( pem ) )
return
case * exportcert :
pem , err := cfg . MarshalPEMCertificate ( )
if err != nil {
panic ( err )
}
fmt . Println ( string ( pem ) )
2019-11-10 19:38:35 +00:00
return
}
2019-11-30 20:46:29 +02:00
2022-09-03 12:20:57 +01:00
n := & node { }
2022-09-03 11:42:05 +01:00
// Setup the Yggdrasil node itself.
{
2022-10-15 15:42:52 +01:00
options := [ ] core . SetupOption {
core . NodeInfo ( cfg . NodeInfo ) ,
core . NodeInfoPrivacy ( cfg . NodeInfoPrivacy ) ,
}
2022-09-03 17:26:12 +01:00
for _ , addr := range cfg . Listen {
options = append ( options , core . ListenAddress ( addr ) )
}
2022-09-03 11:42:05 +01:00
for _ , peer := range cfg . Peers {
options = append ( options , core . Peer { URI : peer } )
}
for intf , peers := range cfg . InterfacePeers {
for _ , peer := range peers {
options = append ( options , core . Peer { URI : peer , SourceInterface : intf } )
}
}
2023-04-06 21:45:49 +01:00
for _ , root := range cfg . RootCertificates {
options = append ( options , core . RootCertificate ( * root ) )
}
2022-09-03 11:42:05 +01:00
for _ , allowed := range cfg . AllowedPublicKeys {
k , err := hex . DecodeString ( allowed )
if err != nil {
panic ( err )
}
options = append ( options , core . AllowedPublicKey ( k [ : ] ) )
}
2023-04-06 21:45:49 +01:00
if n . core , err = core . New ( cfg . Certificate , logger , options ... ) ; err != nil {
2022-07-24 10:23:25 +01:00
panic ( err )
}
}
2022-09-03 11:42:05 +01:00
2022-09-03 11:54:46 +01:00
// Setup the admin socket.
{
options := [ ] admin . SetupOption {
admin . ListenAddress ( cfg . AdminListen ) ,
}
2022-09-03 12:34:29 +01:00
if n . admin , err = admin . New ( n . core , logger , options ... ) ; err != nil {
2022-09-03 11:54:46 +01:00
panic ( err )
}
2022-09-17 20:07:00 +01:00
if n . admin != nil {
n . admin . SetupAdminHandlers ( )
}
2022-09-03 11:54:46 +01:00
}
2022-09-03 11:42:05 +01:00
// Setup the multicast module.
{
options := [ ] multicast . SetupOption { }
for _ , intf := range cfg . MulticastInterfaces {
options = append ( options , multicast . MulticastInterface {
2022-10-26 09:24:24 +01:00
Regex : regexp . MustCompile ( intf . Regex ) ,
Beacon : intf . Beacon ,
Listen : intf . Listen ,
Port : intf . Port ,
2022-11-01 18:34:49 +00:00
Priority : uint8 ( intf . Priority ) ,
2022-09-03 11:42:05 +01:00
} )
}
2022-09-03 12:34:29 +01:00
if n . multicast , err = multicast . New ( n . core , logger , options ... ) ; err != nil {
2022-09-03 11:42:05 +01:00
panic ( err )
}
2022-09-17 20:07:00 +01:00
if n . admin != nil && n . multicast != nil {
2022-09-03 11:54:46 +01:00
n . multicast . SetupAdminHandlers ( n . admin )
}
2018-05-27 22:13:37 +01:00
}
2022-09-03 11:42:05 +01:00
2022-09-03 12:20:57 +01:00
// Setup the TUN module.
{
2022-09-24 14:41:47 +01:00
options := [ ] tun . SetupOption {
tun . InterfaceName ( cfg . IfName ) ,
tun . InterfaceMTU ( cfg . IfMTU ) ,
2022-09-03 12:20:57 +01:00
}
2022-09-24 14:41:47 +01:00
if n . tun , err = tun . New ( ipv6rwc . NewReadWriteCloser ( n . core ) , logger , options ... ) ; err != nil {
2022-09-03 12:20:57 +01:00
panic ( err )
}
2022-09-24 14:41:47 +01:00
if n . admin != nil && n . tun != nil {
n . tun . SetupAdminHandlers ( n . admin )
2022-09-03 12:20:57 +01:00
}
2021-05-08 11:52:22 -05:00
}
2022-09-03 12:20:57 +01:00
2022-10-15 16:07:32 +01:00
// Block until we are told to shut down.
2021-07-01 08:04:01 -05:00
<- ctx . Done ( )
2019-07-06 12:17:40 +01:00
2022-10-15 16:07:32 +01:00
// Shut down the node.
2021-06-02 14:40:09 +01:00
_ = n . admin . Stop ( )
_ = n . multicast . Stop ( )
2022-09-24 14:41:47 +01:00
_ = n . tun . Stop ( )
2019-09-18 15:22:17 +01:00
n . core . Stop ( )
2017-12-28 22:16:20 -06:00
}
2021-07-01 08:04:01 -05:00
2023-04-06 21:45:49 +01:00
func setLogLevel ( loglevel string , logger * log . Logger ) {
levels := [ ... ] string { "error" , "warn" , "info" , "debug" , "trace" }
loglevel = strings . ToLower ( loglevel )
2022-10-15 16:07:32 +01:00
2023-04-06 21:45:49 +01:00
contains := func ( ) bool {
for _ , l := range levels {
if l == loglevel {
return true
}
}
return false
}
2022-10-15 16:07:32 +01:00
2023-04-06 21:45:49 +01:00
if ! contains ( ) { // set default log level
logger . Infoln ( "Loglevel parse failed. Set default level(info)" )
loglevel = "info"
}
2022-10-15 16:07:32 +01:00
2023-04-06 21:45:49 +01:00
for _ , l := range levels {
logger . EnableLevel ( l )
if l == loglevel {
break
}
}
2021-07-01 08:04:01 -05:00
}