mirror of
https://github.com/KusakabeShi/EtherGuard-VPN.git
synced 2024-12-24 14:58:47 +01:00
276 lines
8.3 KiB
Go
276 lines
8.3 KiB
Go
/* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2017-2021 Kusakabe Si. All Rights Reserved.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/google/shlex"
|
|
|
|
"github.com/KusakabeSi/EtherGuard-VPN/conn"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/device"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/gencfg"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/mtypes"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/path"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/tap"
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
func printExampleEdgeConf() {
|
|
tconfig, _ := gencfg.GetExampleEdgeConf("", true)
|
|
toprint, _ := yaml.Marshal(tconfig)
|
|
fmt.Print(string(toprint))
|
|
}
|
|
|
|
func Edge(configPath string, useUAPI bool, printExample bool, bindmode string) (err error) {
|
|
if printExample {
|
|
printExampleEdgeConf()
|
|
return nil
|
|
}
|
|
var econfig mtypes.EdgeConfig
|
|
//printExampleConf()
|
|
//return
|
|
|
|
err = mtypes.ReadYaml(configPath, &econfig)
|
|
if err != nil {
|
|
fmt.Printf("Error read config: %v\t%v\n", configPath, err)
|
|
return err
|
|
}
|
|
|
|
NodeName := econfig.NodeName
|
|
if len(NodeName) > 32 {
|
|
return errors.New("Node name can't longer than 32 :" + NodeName)
|
|
}
|
|
if econfig.DynamicRoute.DampingResistance < 0 || econfig.DynamicRoute.DampingResistance >= 1 {
|
|
return fmt.Errorf("DampingResistance must in range [0,1) : %v", econfig.DynamicRoute.DampingResistance)
|
|
}
|
|
var logLevel int
|
|
switch econfig.LogLevel.LogLevel {
|
|
case "verbose", "debug":
|
|
logLevel = device.LogLevelVerbose
|
|
case "error":
|
|
logLevel = device.LogLevelError
|
|
case "silent":
|
|
logLevel = device.LogLevelSilent
|
|
default:
|
|
logLevel = device.LogLevelError
|
|
}
|
|
logger := device.NewLogger(
|
|
logLevel,
|
|
fmt.Sprintf("(%s) ", NodeName),
|
|
)
|
|
|
|
if err != nil {
|
|
logger.Errorf("UAPI listen error: %v", err)
|
|
os.Exit(ExitSetupFailed)
|
|
return
|
|
}
|
|
|
|
var thetap tap.Device
|
|
// open TUN device (or use supplied fd)
|
|
switch econfig.Interface.IType {
|
|
case "dummy":
|
|
thetap, err = tap.CreateDummyTAP()
|
|
case "stdio":
|
|
thetap, err = tap.CreateStdIOTAP(econfig.Interface, econfig.NodeID)
|
|
case "udpsock":
|
|
thetap, err = tap.CreateUDPSockTAP(econfig.Interface, econfig.NodeID)
|
|
case "tcpsock":
|
|
thetap, err = tap.CreateSockTAP(econfig.Interface, "tcp", econfig.NodeID, econfig.LogLevel)
|
|
case "unixsock":
|
|
thetap, err = tap.CreateSockTAP(econfig.Interface, "unix", econfig.NodeID, econfig.LogLevel)
|
|
case "unixgramsock":
|
|
thetap, err = tap.CreateSockTAP(econfig.Interface, "unixgram", econfig.NodeID, econfig.LogLevel)
|
|
case "unixpacketsock":
|
|
thetap, err = tap.CreateSockTAP(econfig.Interface, "unixpacket", econfig.NodeID, econfig.LogLevel)
|
|
case "fd":
|
|
thetap, err = tap.CreateFdTAP(econfig.Interface, econfig.NodeID)
|
|
case "vpp":
|
|
thetap, err = tap.CreateVppTAP(econfig.Interface, econfig.NodeID, econfig.LogLevel.LogLevel)
|
|
case "tap":
|
|
thetap, err = tap.CreateTAP(econfig.Interface, econfig.NodeID)
|
|
default:
|
|
return errors.New("Unknow interface type:" + econfig.Interface.IType)
|
|
}
|
|
if err != nil {
|
|
logger.Errorf("Failed to create TAP device: %v", err)
|
|
os.Exit(ExitSetupFailed)
|
|
}
|
|
|
|
if econfig.DefaultTTL <= 0 {
|
|
return errors.New("DefaultTTL must > 0")
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// Config
|
|
if !econfig.DynamicRoute.P2P.UseP2P && !econfig.DynamicRoute.SuperNode.UseSuperNode {
|
|
econfig.LogLevel.LogNTP = false // NTP in static mode is useless
|
|
}
|
|
graph, err := path.NewGraph(3, false, econfig.DynamicRoute.P2P.GraphRecalculateSetting, econfig.DynamicRoute.NTPConfig, econfig.LogLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
graph.SetNHTable(econfig.NextHopTable)
|
|
|
|
the_device := device.NewDevice(thetap, econfig.NodeID, conn.NewDefaultBind(true, true, bindmode), logger, graph, false, configPath, &econfig, nil, nil, Version)
|
|
defer the_device.Close()
|
|
pk, err := device.Str2PriKey(econfig.PrivKey)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
return err
|
|
}
|
|
the_device.SetPrivateKey(pk)
|
|
the_device.IpcSet("fwmark=0\n")
|
|
the_device.IpcSet("listen_port=" + strconv.Itoa(econfig.ListenPort) + "\n")
|
|
the_device.IpcSet("replace_peers=true\n")
|
|
for _, peerconf := range econfig.Peers {
|
|
pk, err := device.Str2PubKey(peerconf.PubKey)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
return err
|
|
}
|
|
the_device.NewPeer(pk, peerconf.NodeID, false, peerconf.PersistentKeepalive)
|
|
if peerconf.EndPoint != "" {
|
|
peer := the_device.LookupPeer(pk)
|
|
err = peer.SetEndpointFromConnURL(peerconf.EndPoint, 0, econfig.AfPrefer, peerconf.Static)
|
|
if err != nil {
|
|
logger.Errorf("Failed to set endpoint %v: %w", peerconf.EndPoint, err)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if econfig.DynamicRoute.SuperNode.UseSuperNode {
|
|
S4 := true
|
|
S6 := true
|
|
if econfig.DynamicRoute.SuperNode.EndpointV4 != "" {
|
|
pk, err := device.Str2PubKey(econfig.DynamicRoute.SuperNode.PubKeyV4)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
return err
|
|
}
|
|
psk, err := device.Str2PSKey(econfig.DynamicRoute.SuperNode.PSKey)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
return err
|
|
}
|
|
peer, err := the_device.NewPeer(pk, mtypes.NodeID_SuperNode, true, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer.SetPSK(psk)
|
|
err = peer.SetEndpointFromConnURL(econfig.DynamicRoute.SuperNode.EndpointV4, 4, 0, true)
|
|
if err != nil {
|
|
logger.Errorf("Failed to set endpoint for supernode v4 %v: %v", econfig.DynamicRoute.SuperNode.EndpointV4, err)
|
|
S4 = false
|
|
}
|
|
}
|
|
if econfig.DynamicRoute.SuperNode.EndpointV6 != "" {
|
|
pk, err := device.Str2PubKey(econfig.DynamicRoute.SuperNode.PubKeyV6)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
}
|
|
psk, err := device.Str2PSKey(econfig.DynamicRoute.SuperNode.PSKey)
|
|
if err != nil {
|
|
fmt.Println("Error decode base64 ", err)
|
|
return err
|
|
}
|
|
peer, err := the_device.NewPeer(pk, mtypes.NodeID_SuperNode, true, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer.SetPSK(psk)
|
|
err = peer.SetEndpointFromConnURL(econfig.DynamicRoute.SuperNode.EndpointV6, 6, 0, true)
|
|
if err != nil {
|
|
logger.Errorf("Failed to set endpoint for supernode v6 %v: %v", econfig.DynamicRoute.SuperNode.EndpointV6, err)
|
|
S6 = false
|
|
}
|
|
if !(S4 || S6) {
|
|
return errors.New("failed to connect to supernode")
|
|
}
|
|
}
|
|
}
|
|
|
|
logger.Verbosef("Device started")
|
|
|
|
errs := make(chan error)
|
|
term := make(chan os.Signal, 1)
|
|
|
|
if useUAPI {
|
|
startUAPI(NodeName, logger, the_device, errs)
|
|
}
|
|
|
|
if econfig.PostScript != "" {
|
|
envs := make(map[string]string)
|
|
nid := econfig.NodeID
|
|
nid_bytearr := []byte{0, 0}
|
|
MacAddr, _ := tap.GetMacAddr(econfig.Interface.MacAddrPrefix, uint32(nid))
|
|
binary.LittleEndian.PutUint16(nid_bytearr, uint16(nid))
|
|
|
|
envs["EG_MODE"] = "edge"
|
|
envs["EG_NODE_NAME"] = econfig.NodeName
|
|
envs["EG_NODE_ID_INT_DEC"] = fmt.Sprintf("%d", nid)
|
|
envs["EG_NODE_ID_BYTE0_DEC"] = fmt.Sprintf("%d", nid_bytearr[0])
|
|
envs["EG_NODE_ID_BYTE1_DEC"] = fmt.Sprintf("%d", nid_bytearr[1])
|
|
envs["EG_NODE_ID_INT_HEX"] = fmt.Sprintf("%x", nid)
|
|
envs["EG_NODE_ID_BYTE0_HEX"] = fmt.Sprintf("%X", nid_bytearr[0])
|
|
envs["EG_NODE_ID_BYTE1_HEX"] = fmt.Sprintf("%X", nid_bytearr[1])
|
|
envs["EG_INTERFACE_NAME"] = econfig.Interface.Name
|
|
envs["EG_INTERFACE_TYPE"] = econfig.Interface.IType
|
|
envs["EG_INTERFACE_MAC_PREFIX"] = econfig.Interface.MacAddrPrefix
|
|
envs["EG_INTERFACE_MAC_ADDR"] = MacAddr.String()
|
|
|
|
cmdarg, err := shlex.Split(econfig.PostScript)
|
|
if err != nil {
|
|
return fmt.Errorf("error parse PostScript %v", err)
|
|
}
|
|
if econfig.LogLevel.LogInternal {
|
|
fmt.Printf("PostScript: exec.Command(%v)\n", cmdarg)
|
|
}
|
|
cmd := exec.Command(cmdarg[0], cmdarg[1:]...)
|
|
cmd.Env = os.Environ()
|
|
for k, v := range envs {
|
|
cmd.Env = append(cmd.Env, k+"="+v)
|
|
}
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("exec.Command(%v) failed with %v", cmdarg, err)
|
|
}
|
|
if econfig.LogLevel.LogInternal {
|
|
fmt.Printf("PostScript output: %s\n", string(out))
|
|
}
|
|
}
|
|
|
|
// wait for program to terminate
|
|
signal.Notify(term, syscall.SIGTERM)
|
|
signal.Notify(term, os.Interrupt)
|
|
|
|
the_device.Chan_Device_Initialized <- struct{}{}
|
|
mtypes.SdNotify(false, mtypes.SdNotifyReady)
|
|
SdNotify, err := mtypes.SdNotify(false, mtypes.SdNotifyReady)
|
|
if econfig.LogLevel.LogInternal {
|
|
fmt.Printf("Internal: SdNotify:%v err:%v\n", SdNotify, err)
|
|
}
|
|
|
|
select {
|
|
case <-term:
|
|
case <-errs:
|
|
case errcode := <-the_device.Wait():
|
|
if errcode != 0 {
|
|
return syscall.Errno(errcode)
|
|
}
|
|
}
|
|
logger.Verbosef("Shutting down")
|
|
return
|
|
}
|