2017-12-29 04:16:20 +00:00
package main
2018-06-15 09:30:09 +00:00
import (
2021-07-01 13:04:01 +00:00
"context"
2021-05-15 20:55:47 +00:00
"crypto/ed25519"
2023-05-21 14:24:31 +00:00
"crypto/sha1"
2019-06-11 11:50:01 +00:00
"encoding/hex"
2018-06-15 09:30:09 +00:00
"encoding/json"
"flag"
"fmt"
2019-11-10 19:38:35 +00:00
"net"
2018-06-15 09:30:09 +00:00
"os"
"os/signal"
2022-09-03 10:42:05 +00:00
"regexp"
2018-12-14 17:49:42 +00:00
"strings"
2018-06-15 09:30:09 +00:00
"syscall"
2017-12-29 04:16:20 +00:00
2019-01-27 13:31:43 +00:00
"github.com/gologme/log"
2019-06-28 23:32:23 +00:00
gsyslog "github.com/hashicorp/go-syslog"
2023-04-06 20:45:49 +00:00
"github.com/hjson/hjson-go/v4"
2018-06-15 09:30:09 +00:00
"github.com/kardianos/minwinsvc"
2017-12-29 04:16:20 +00:00
2019-11-10 19:38:35 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/address"
2019-05-19 16:27:48 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
2018-12-08 01:56:04 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/config"
2022-09-17 19:07:00 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
2021-05-23 19:33:28 +00:00
2021-05-23 19:42:26 +00: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 13:41:47 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
2019-08-10 21:31:22 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/version"
2018-06-15 09:30:09 +00:00
)
2018-02-16 14:12:44 +00:00
2017-12-29 04:16:20 +00:00
type node struct {
2022-07-24 09:23:25 +00:00
core * core . Core
2022-09-24 13:41:47 +00:00
tun * tun . TunAdapter
2021-06-05 19:57:03 +00:00
multicast * multicast . Multicast
admin * admin . AdminSocket
2017-12-29 04:16:20 +00:00
}
2023-04-06 20:45:49 +00:00
// The main function is responsible for configuring and starting Yggdrasil.
func main ( ) {
2018-05-27 21:13:37 +00: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 21:13:37 +00:00
normaliseconf := flag . Bool ( "normaliseconf" , false , "use in combination with either -useconf or -useconffile, outputs your configuration normalised" )
2023-04-06 20:45:49 +00: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 21:13:37 +00:00
autoconf := flag . Bool ( "autoconf" , false , "automatic mode (dynamic IP, peer with IPv6 neighbors)" )
2019-08-14 19:09:02 +00:00
ver := flag . Bool ( "version" , false , "prints the version of this build" )
2019-06-28 22:45:04 +00: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 18:46:29 +00:00
loglevel := flag . String ( "loglevel" , "info" , "loglevel to enable" )
2018-01-04 22:37:51 +00:00
flag . Parse ( )
2021-07-01 13:04:01 +00:00
2023-04-06 20:45:49 +00: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 20:32:04 +00:00
// Create a new logger that logs output to stdout.
var logger * log . Logger
2023-04-06 20:45:49 +00:00
switch * logto {
2021-06-05 20:32:04 +00:00
case "stdout" :
logger = log . New ( os . Stdout , "" , log . Flags ( ) )
2023-04-06 20:45:49 +00:00
2021-06-05 20:32:04 +00: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 20:45:49 +00:00
2021-06-05 20:32:04 +00:00
default :
2023-04-06 20:45:49 +00:00
if logfd , err := os . OpenFile ( * logto , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 ) ; err == nil {
2021-06-05 20:32:04 +00: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 20:45:49 +00:00
if * normaliseconf {
2021-06-05 20:49:11 +00:00
setLogLevel ( "error" , logger )
} else {
2023-04-06 20:45:49 +00:00
setLogLevel ( * loglevel , logger )
2021-06-05 20:49:11 +00:00
}
2021-06-05 20:32:04 +00:00
2023-04-06 20:45:49 +00: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 20:45:49 +00:00
case * ver :
2019-08-10 21:31:22 +00:00
fmt . Println ( "Build name:" , version . BuildName ( ) )
fmt . Println ( "Build version:" , version . BuildVersion ( ) )
2019-08-14 19:09:02 +00:00
return
2023-04-06 20:45:49 +00:00
case * autoconf :
2018-05-27 21:13:37 +00:00
// Use an autoconf-generated config, this will give us random keys and
2022-09-24 13:41:47 +00:00
// port numbers, and will use an automatically selected TUN interface.
2023-04-06 20:45:49 +00:00
case * useconf :
if _ , err := cfg . ReadFrom ( os . Stdin ) ; err != nil {
panic ( err )
2018-05-23 11:04:27 +00:00
}
2023-04-06 20:45:49 +00: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 22:27:00 +00:00
return
2023-04-06 20:45:49 +00:00
2018-01-04 22:37:51 +00:00
default :
2022-11-08 22:19:43 +00:00
fmt . Println ( "Usage:" )
2018-01-04 22:37:51 +00:00
flag . PrintDefaults ( )
2022-11-08 22:19:43 +00:00
2023-04-06 20:45:49 +00:00
if * getaddr || * getsnet {
2022-11-08 22:19:43 +00:00
fmt . Println ( "\nError: You need to specify some config data using -useconf or -useconffile." )
}
2023-07-15 19:12:14 +00:00
return
2018-01-04 22:37:51 +00:00
}
2023-04-06 20:45:49 +00: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 20:45:49 +00: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 19:33:28 +00:00
}
2023-04-06 20:45:49 +00: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 20:45:49 +00:00
if err != nil {
panic ( err )
}
fmt . Println ( string ( bs ) )
2019-11-10 19:38:35 +00:00
return
2023-04-06 20:45:49 +00: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 20:45:49 +00: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 18:46:29 +00:00
2022-09-03 11:20:57 +00:00
n := & node { }
2022-09-03 10:42:05 +00:00
2023-05-21 14:24:31 +00:00
// Track certificate fingerprints for configured roots, so
// that we can match them using the multicast discriminator.
fingerprints := map [ [ 20 ] byte ] struct { } { }
2022-09-03 10:42:05 +00:00
// Setup the Yggdrasil node itself.
{
2022-10-15 14:42:52 +00:00
options := [ ] core . SetupOption {
core . NodeInfo ( cfg . NodeInfo ) ,
core . NodeInfoPrivacy ( cfg . NodeInfoPrivacy ) ,
}
2022-09-03 16:26:12 +00:00
for _ , addr := range cfg . Listen {
options = append ( options , core . ListenAddress ( addr ) )
}
2022-09-03 10:42:05 +00: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 20:45:49 +00:00
for _ , root := range cfg . RootCertificates {
options = append ( options , core . RootCertificate ( * root ) )
2023-05-21 14:24:31 +00:00
fingerprints [ sha1 . Sum ( root . Raw [ : ] ) ] = struct { } { }
2023-04-06 20:45:49 +00:00
}
2022-09-03 10:42:05 +00: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 20:45:49 +00:00
if n . core , err = core . New ( cfg . Certificate , logger , options ... ) ; err != nil {
2022-07-24 09:23:25 +00:00
panic ( err )
}
}
2022-09-03 10:42:05 +00:00
2022-09-03 10:54:46 +00:00
// Setup the admin socket.
{
options := [ ] admin . SetupOption {
admin . ListenAddress ( cfg . AdminListen ) ,
}
2022-09-03 11:34:29 +00:00
if n . admin , err = admin . New ( n . core , logger , options ... ) ; err != nil {
2022-09-03 10:54:46 +00:00
panic ( err )
}
2022-09-17 19:07:00 +00:00
if n . admin != nil {
n . admin . SetupAdminHandlers ( )
}
2022-09-03 10:54:46 +00:00
}
2022-09-03 10:42:05 +00:00
// Setup the multicast module.
{
options := [ ] multicast . SetupOption { }
for _ , intf := range cfg . MulticastInterfaces {
options = append ( options , multicast . MulticastInterface {
2022-10-26 08:24:24 +00: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 10:42:05 +00:00
} )
}
2023-05-21 14:24:31 +00:00
if len ( fingerprints ) > 0 {
var matcher multicast . DiscriminatorMatch = func ( b [ ] byte ) bool {
// Break apart the discriminator into 20-byte chunks and
// see whether any of them match the configured root CA
// fingerprints. If any of them match, we'll return true.
var f [ 20 ] byte
for len ( b ) >= len ( f ) {
b = b [ copy ( f [ : ] , b ) : ]
if _ , ok := fingerprints [ f ] ; ok {
return true
}
}
return false
}
// Populate our own discriminator with the fingerprints of our
// configured root CAs.
var discriminator multicast . Discriminator
for f := range fingerprints {
discriminator = append ( discriminator , f [ : ] ... )
}
options = append ( options , matcher )
options = append ( options , discriminator )
}
2022-09-03 11:34:29 +00:00
if n . multicast , err = multicast . New ( n . core , logger , options ... ) ; err != nil {
2022-09-03 10:42:05 +00:00
panic ( err )
}
2022-09-17 19:07:00 +00:00
if n . admin != nil && n . multicast != nil {
2022-09-03 10:54:46 +00:00
n . multicast . SetupAdminHandlers ( n . admin )
}
2018-05-27 21:13:37 +00:00
}
2022-09-03 10:42:05 +00:00
2022-09-03 11:20:57 +00:00
// Setup the TUN module.
{
2022-09-24 13:41:47 +00:00
options := [ ] tun . SetupOption {
tun . InterfaceName ( cfg . IfName ) ,
tun . InterfaceMTU ( cfg . IfMTU ) ,
2022-09-03 11:20:57 +00:00
}
2022-09-24 13:41:47 +00:00
if n . tun , err = tun . New ( ipv6rwc . NewReadWriteCloser ( n . core ) , logger , options ... ) ; err != nil {
2022-09-03 11:20:57 +00:00
panic ( err )
}
2022-09-24 13:41:47 +00:00
if n . admin != nil && n . tun != nil {
n . tun . SetupAdminHandlers ( n . admin )
2022-09-03 11:20:57 +00:00
}
2021-05-08 16:52:22 +00:00
}
2022-09-03 11:20:57 +00:00
2022-10-15 15:07:32 +00:00
// Block until we are told to shut down.
2021-07-01 13:04:01 +00:00
<- ctx . Done ( )
2019-07-06 11:17:40 +00:00
2022-10-15 15:07:32 +00:00
// Shut down the node.
2021-06-02 13:40:09 +00:00
_ = n . admin . Stop ( )
_ = n . multicast . Stop ( )
2022-09-24 13:41:47 +00:00
_ = n . tun . Stop ( )
2019-09-18 14:22:17 +00:00
n . core . Stop ( )
2017-12-29 04:16:20 +00:00
}
2021-07-01 13:04:01 +00:00
2023-04-06 20:45:49 +00:00
func setLogLevel ( loglevel string , logger * log . Logger ) {
levels := [ ... ] string { "error" , "warn" , "info" , "debug" , "trace" }
loglevel = strings . ToLower ( loglevel )
2022-10-15 15:07:32 +00:00
2023-04-06 20:45:49 +00:00
contains := func ( ) bool {
for _ , l := range levels {
if l == loglevel {
return true
}
}
return false
}
2022-10-15 15:07:32 +00:00
2023-04-06 20:45:49 +00:00
if ! contains ( ) { // set default log level
logger . Infoln ( "Loglevel parse failed. Set default level(info)" )
loglevel = "info"
}
2022-10-15 15:07:32 +00:00
2023-04-06 20:45:49 +00:00
for _ , l := range levels {
logger . EnableLevel ( l )
if l == loglevel {
break
}
}
2021-07-01 13:04:01 +00:00
}