2023-01-28 00:50:21 +07:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-02-27 12:29:42 +07:00
|
|
|
"github.com/mohae/deepcopy"
|
2023-02-27 13:07:36 +07:00
|
|
|
"github.com/okzk/sdnotify"
|
2023-01-28 00:50:21 +07:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/spf13/cobra"
|
2023-01-28 12:27:54 +07:00
|
|
|
"github.com/v2rayA/dae/cmd/internal"
|
2023-01-28 00:50:21 +07:00
|
|
|
"github.com/v2rayA/dae/config"
|
2023-02-07 22:49:30 +07:00
|
|
|
"github.com/v2rayA/dae/control"
|
2023-01-28 00:50:21 +07:00
|
|
|
"github.com/v2rayA/dae/pkg/logger"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
2023-02-09 19:17:45 +07:00
|
|
|
"path/filepath"
|
2023-02-25 01:38:21 +07:00
|
|
|
"runtime"
|
2023-02-09 22:17:49 +07:00
|
|
|
"strings"
|
2023-01-28 00:50:21 +07:00
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2023-02-04 21:02:37 +07:00
|
|
|
cfgFile string
|
|
|
|
disableTimestamp bool
|
2023-01-28 00:50:21 +07:00
|
|
|
|
|
|
|
runCmd = &cobra.Command{
|
|
|
|
Use: "run",
|
|
|
|
Short: "Run dae in the foreground",
|
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
2023-02-01 10:10:41 +07:00
|
|
|
if cfgFile == "" {
|
|
|
|
logrus.Fatalln("Argument \"--config\" or \"-c\" is required but not provided.")
|
|
|
|
}
|
2023-02-05 13:03:34 +07:00
|
|
|
|
2023-02-07 12:49:47 +07:00
|
|
|
// Require "sudo" if necessary.
|
|
|
|
internal.AutoSu()
|
|
|
|
|
2023-02-05 13:03:34 +07:00
|
|
|
// Read config from --config cfgFile.
|
2023-02-25 21:53:18 +07:00
|
|
|
conf, includes, err := readConfig(cfgFile)
|
2023-02-05 13:03:34 +07:00
|
|
|
if err != nil {
|
2023-02-05 20:05:23 +07:00
|
|
|
logrus.Fatalln("readConfig:", err)
|
2023-02-05 13:03:34 +07:00
|
|
|
}
|
|
|
|
|
2023-02-25 21:53:18 +07:00
|
|
|
log := logger.NewLogger(conf.Global.LogLevel, disableTimestamp)
|
2023-02-05 13:03:34 +07:00
|
|
|
logrus.SetLevel(log.Level)
|
2023-02-09 22:17:49 +07:00
|
|
|
|
|
|
|
log.Infof("Include config files: [%v]", strings.Join(includes, ", "))
|
2023-02-25 21:53:18 +07:00
|
|
|
if err := Run(log, conf); err != nil {
|
2023-01-28 00:50:21 +07:00
|
|
|
logrus.Fatalln(err)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2023-02-01 10:10:41 +07:00
|
|
|
runCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file")
|
2023-02-04 21:02:37 +07:00
|
|
|
runCmd.PersistentFlags().BoolVarP(&disableTimestamp, "disable-timestamp", "", false, "disable timestamp")
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
|
|
|
|
2023-02-25 21:53:18 +07:00
|
|
|
func Run(log *logrus.Logger, conf *config.Config) (err error) {
|
2023-01-28 00:50:21 +07:00
|
|
|
|
2023-02-27 12:29:42 +07:00
|
|
|
// New ControlPlane.
|
|
|
|
c, err := newControlPlane(log, nil, conf)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve tproxy TCP/UDP server util signals.
|
|
|
|
var listener *control.Listener
|
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGILL, syscall.SIGUSR1)
|
|
|
|
go func() {
|
2023-02-27 13:36:36 +07:00
|
|
|
readyChan := make(chan bool, 1)
|
|
|
|
go func() {
|
|
|
|
<-readyChan
|
|
|
|
sdnotify.Ready()
|
|
|
|
}()
|
|
|
|
if listener, err = c.ListenAndServe(readyChan, conf.Global.TproxyPort); err != nil {
|
2023-02-27 12:29:42 +07:00
|
|
|
log.Errorln("ListenAndServe:", err)
|
|
|
|
}
|
|
|
|
sigs <- nil
|
|
|
|
}()
|
|
|
|
reloading := false
|
|
|
|
loop:
|
|
|
|
for sig := range sigs {
|
|
|
|
switch sig {
|
|
|
|
case nil:
|
|
|
|
if reloading {
|
|
|
|
reloading = false
|
|
|
|
log.Warnln("[Reload] Serve")
|
2023-02-27 13:36:36 +07:00
|
|
|
readyChan := make(chan bool, 1)
|
2023-02-27 12:29:42 +07:00
|
|
|
go func() {
|
2023-02-27 13:36:36 +07:00
|
|
|
if err := c.Serve(readyChan, listener); err != nil {
|
2023-02-27 12:29:42 +07:00
|
|
|
log.Errorln("ListenAndServe:", err)
|
|
|
|
}
|
|
|
|
sigs <- nil
|
|
|
|
}()
|
2023-02-27 13:36:36 +07:00
|
|
|
<-readyChan
|
|
|
|
sdnotify.Ready()
|
|
|
|
log.Warnln("[Reload] Finished")
|
2023-02-27 12:29:42 +07:00
|
|
|
} else {
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
case syscall.SIGUSR1:
|
|
|
|
// Reload signal.
|
2023-02-27 13:07:36 +07:00
|
|
|
sdnotify.Reloading()
|
2023-02-27 12:29:42 +07:00
|
|
|
log.Warnln("[Reload] Received reload signal; prepare to reload")
|
|
|
|
obj := c.EjectBpf()
|
|
|
|
log.Warnln("[Reload] Load new control plane")
|
|
|
|
newC, err := newControlPlane(log, obj, conf)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"err": err,
|
|
|
|
}).Errorln("failed to reload")
|
2023-02-27 13:07:36 +07:00
|
|
|
sdnotify.Ready()
|
2023-02-27 12:29:42 +07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
log.Warnln("[Reload] Stopped old control plane")
|
|
|
|
c.Close()
|
|
|
|
c = newC
|
|
|
|
reloading = true
|
|
|
|
default:
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if e := c.Close(); e != nil {
|
|
|
|
return fmt.Errorf("close control plane: %w", e)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newControlPlane(log *logrus.Logger, bpf interface{}, conf *config.Config) (c *control.ControlPlane, err error) {
|
2023-02-10 10:04:16 +07:00
|
|
|
/// Get tag -> nodeList mapping.
|
|
|
|
tagToNodeList := map[string][]string{}
|
2023-02-25 21:53:18 +07:00
|
|
|
if len(conf.Node) > 0 {
|
|
|
|
for _, node := range conf.Node {
|
|
|
|
tagToNodeList[""] = append(tagToNodeList[""], string(node))
|
|
|
|
}
|
2023-02-10 10:04:16 +07:00
|
|
|
}
|
2023-01-28 00:50:21 +07:00
|
|
|
// Resolve subscriptions to nodes.
|
2023-02-25 21:53:18 +07:00
|
|
|
for _, sub := range conf.Subscription {
|
|
|
|
tag, nodes, err := internal.ResolveSubscription(log, filepath.Dir(cfgFile), string(sub))
|
2023-01-28 00:50:21 +07:00
|
|
|
if err != nil {
|
|
|
|
log.Warnf(`failed to resolve subscription "%v": %v`, sub, err)
|
|
|
|
}
|
2023-02-10 10:04:16 +07:00
|
|
|
if len(nodes) > 0 {
|
|
|
|
tagToNodeList[tag] = append(tagToNodeList[tag], nodes...)
|
|
|
|
}
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
2023-02-10 10:04:16 +07:00
|
|
|
if len(tagToNodeList) == 0 {
|
2023-02-27 12:29:42 +07:00
|
|
|
return nil, fmt.Errorf("no node found, which could because all subscription resolving failed")
|
2023-02-04 21:02:37 +07:00
|
|
|
}
|
2023-01-28 00:50:21 +07:00
|
|
|
|
2023-02-25 21:53:18 +07:00
|
|
|
if len(conf.Global.LanInterface) == 0 && len(conf.Global.WanInterface) == 0 {
|
2023-02-27 13:57:00 +07:00
|
|
|
log.Warnln("No binding interface.")
|
2023-02-01 09:59:57 +07:00
|
|
|
}
|
|
|
|
|
2023-02-27 12:29:42 +07:00
|
|
|
// Deep copy a conf to avoid modification.
|
|
|
|
conf = deepcopy.Copy(conf).(*config.Config)
|
|
|
|
c, err = control.NewControlPlane(
|
2023-01-28 00:50:21 +07:00
|
|
|
log,
|
2023-02-27 12:29:42 +07:00
|
|
|
bpf,
|
2023-02-10 10:04:16 +07:00
|
|
|
tagToNodeList,
|
2023-02-25 21:53:18 +07:00
|
|
|
conf.Group,
|
|
|
|
&conf.Routing,
|
|
|
|
&conf.Global,
|
|
|
|
&conf.Dns,
|
2023-01-28 00:50:21 +07:00
|
|
|
)
|
|
|
|
if err != nil {
|
2023-02-27 12:29:42 +07:00
|
|
|
return nil, err
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
2023-02-25 01:38:21 +07:00
|
|
|
// Call GC to release memory.
|
|
|
|
runtime.GC()
|
|
|
|
|
2023-02-27 12:29:42 +07:00
|
|
|
return c, nil
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
|
|
|
|
2023-02-25 21:53:18 +07:00
|
|
|
func readConfig(cfgFile string) (conf *config.Config, includes []string, err error) {
|
2023-02-09 22:17:49 +07:00
|
|
|
merger := config.NewMerger(cfgFile)
|
2023-02-10 10:55:00 +07:00
|
|
|
sections, includes, err := merger.Merge()
|
2023-01-28 00:50:21 +07:00
|
|
|
if err != nil {
|
2023-02-09 22:17:49 +07:00
|
|
|
return nil, nil, err
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
2023-02-25 21:53:18 +07:00
|
|
|
if conf, err = config.New(sections); err != nil {
|
2023-02-09 22:17:49 +07:00
|
|
|
return nil, nil, err
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|
2023-02-25 21:53:18 +07:00
|
|
|
return conf, includes, nil
|
2023-01-28 00:50:21 +07:00
|
|
|
}
|