github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/supervisor/daemon/daemon.go (about)

     1  /*
     2   * Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package daemon
    19  
    20  import (
    21  	"bufio"
    22  	"encoding/base64"
    23  	"encoding/json"
    24  	"errors"
    25  	"flag"
    26  	"fmt"
    27  	"io"
    28  	"net"
    29  	"strings"
    30  
    31  	"github.com/rs/zerolog/log"
    32  
    33  	"github.com/mysteriumnetwork/node/metadata"
    34  	"github.com/mysteriumnetwork/node/router/network"
    35  	"github.com/mysteriumnetwork/node/services/wireguard/wgcfg"
    36  	"github.com/mysteriumnetwork/node/supervisor/daemon/transport"
    37  	"github.com/mysteriumnetwork/node/supervisor/daemon/wireguard"
    38  )
    39  
    40  // Daemon - supervisor process.
    41  type Daemon struct {
    42  	monitor       *wireguard.Monitor
    43  	tequilapiPort uint16
    44  }
    45  
    46  // New creates a new daemon.
    47  func New() Daemon {
    48  	return Daemon{monitor: wireguard.NewMonitor(), tequilapiPort: defaultPort}
    49  }
    50  
    51  // Start supervisor daemon. Blocks.
    52  func (d *Daemon) Start(options transport.Options) error {
    53  	return transport.Start(d.dialog, options)
    54  }
    55  
    56  // dialog talks to the client via established connection.
    57  func (d *Daemon) dialog(conn io.ReadWriter) {
    58  	scan := bufio.NewScanner(conn)
    59  	answer := responder{conn}
    60  	for scan.Scan() {
    61  		line := scan.Bytes()
    62  		log.Debug().Msgf("> %s", line)
    63  		cmd := strings.Split(string(line), " ")
    64  		op := strings.ToLower(cmd[0])
    65  		switch op {
    66  		case commandVersion:
    67  			answer.ok(metadata.VersionAsString())
    68  		case commandBye:
    69  			answer.ok("bye")
    70  			return
    71  		case commandPing:
    72  			answer.ok("pong")
    73  		case commandWgUp:
    74  			up, err := d.wgUp(cmd...)
    75  			if err != nil {
    76  				log.Err(err).Msgf("%s failed", commandWgUp)
    77  				answer.err(err)
    78  			} else {
    79  				answer.ok(up)
    80  			}
    81  		case commandWgDown:
    82  			err := d.wgDown(cmd...)
    83  			if err != nil {
    84  				log.Err(err).Msgf("%s failed", commandWgDown)
    85  				answer.err(err)
    86  			} else {
    87  				answer.ok()
    88  			}
    89  		case commandWgStats:
    90  			stats, err := d.wgStats(cmd...)
    91  			if err != nil {
    92  				log.Err(err).Msgf("%s failed", commandWgStats)
    93  				answer.err(err)
    94  			} else {
    95  				answer.ok(stats)
    96  			}
    97  		case commandKill:
    98  			if err := d.killMyst(); err != nil {
    99  				log.Err(err).Msgf("%s failed", commandKill)
   100  				answer.err(err)
   101  			} else {
   102  				answer.ok()
   103  			}
   104  		case commandTequilapiSetPort:
   105  			if err := d.setTequilapiPort(cmd); err != nil {
   106  				log.Err(err).Msgf("%s failed", commandTequilapiSetPort)
   107  				answer.err(err)
   108  			} else {
   109  				answer.ok()
   110  			}
   111  		case commandDiscoverGateway:
   112  			t := &network.RoutingTable{}
   113  			gw, err := t.DiscoverGateway()
   114  			if err != nil {
   115  				log.Err(err).Msgf("%s failed", commandDiscoverGateway)
   116  				answer.err(err)
   117  			} else {
   118  				answer.ok(gw.String())
   119  			}
   120  		case commandExcludeRoute:
   121  			if err := d.excludeRoute(cmd...); err != nil {
   122  				log.Err(err).Msgf("%s failed", commandDiscoverGateway)
   123  				answer.err(err)
   124  			} else {
   125  				answer.ok()
   126  			}
   127  		case commandDeleteRoute:
   128  			if err := d.deleteRoute(cmd...); err != nil {
   129  				log.Err(err).Msgf("%s failed", commandDiscoverGateway)
   130  				answer.err(err)
   131  			} else {
   132  				answer.ok()
   133  			}
   134  		}
   135  	}
   136  }
   137  
   138  func (d *Daemon) excludeRoute(args ...string) error {
   139  	flags := flag.NewFlagSet("", flag.ContinueOnError)
   140  
   141  	ip := flags.String("ip", "", "Destination IP address")
   142  	gw := flags.String("gw", "", "Gateway")
   143  
   144  	if err := flags.Parse(args[1:]); err != nil {
   145  		return err
   146  	}
   147  
   148  	if *ip == "" {
   149  		return errors.New("-ip is required")
   150  	}
   151  	if *gw == "" {
   152  		return errors.New("-gw is required")
   153  	}
   154  
   155  	ipAddr := net.ParseIP(*ip)
   156  	gwAddr := net.ParseIP(*gw)
   157  
   158  	t := &network.RoutingTable{}
   159  	return t.ExcludeRule(ipAddr, gwAddr)
   160  }
   161  
   162  func (d *Daemon) deleteRoute(args ...string) error {
   163  	flags := flag.NewFlagSet("", flag.ContinueOnError)
   164  
   165  	ip := flags.String("ip", "", "Destination IP address")
   166  	gw := flags.String("gw", "", "Gateway")
   167  
   168  	if err := flags.Parse(args[1:]); err != nil {
   169  		return err
   170  	}
   171  
   172  	if *ip == "" {
   173  		return errors.New("-ip is required")
   174  	}
   175  	if *gw == "" {
   176  		return errors.New("-gw is required")
   177  	}
   178  
   179  	ipAddr := net.ParseIP(*ip)
   180  	gwAddr := net.ParseIP(*gw)
   181  
   182  	t := &network.RoutingTable{}
   183  	return t.DeleteRule(ipAddr, gwAddr)
   184  }
   185  
   186  func (d *Daemon) wgUp(args ...string) (interfaceName string, err error) {
   187  	flags := flag.NewFlagSet("", flag.ContinueOnError)
   188  	deviceConfigStr := flags.String("config", "", "Device configuration JSON string")
   189  	uid := flags.String("uid", "", "User ID."+
   190  		" On POSIX systems, this is a decimal number representing the uid."+
   191  		" On Windows, this is a security identifier (SID) in a string format.")
   192  	if err := flags.Parse(args[1:]); err != nil {
   193  		return "", err
   194  	}
   195  	if *deviceConfigStr == "" {
   196  		return "", errors.New("-config is required")
   197  	}
   198  	if *uid == "" {
   199  		return "", errors.New("-uid is required")
   200  	}
   201  
   202  	configJSON, err := base64.StdEncoding.DecodeString(*deviceConfigStr)
   203  	if err != nil {
   204  		return "", fmt.Errorf("could not decode config from base64: %w", err)
   205  	}
   206  
   207  	deviceConfig := wgcfg.DeviceConfig{}
   208  	if err := json.Unmarshal(configJSON, &deviceConfig); err != nil {
   209  		return "", fmt.Errorf("could not unmarshal device config: %w", err)
   210  	}
   211  
   212  	return d.monitor.Up(deviceConfig, *uid)
   213  }
   214  
   215  func (d *Daemon) wgDown(args ...string) (err error) {
   216  	flags := flag.NewFlagSet("", flag.ContinueOnError)
   217  	interfaceName := flags.String("iface", "", "")
   218  	if err := flags.Parse(args[1:]); err != nil {
   219  		return err
   220  	}
   221  	if *interfaceName == "" {
   222  		return errors.New("-iface is required")
   223  	}
   224  
   225  	err = d.monitor.Down(*interfaceName)
   226  	if err != nil {
   227  		return fmt.Errorf("failed to down wg interface %s: %w", *interfaceName, err)
   228  	}
   229  
   230  	return nil
   231  }
   232  
   233  func (d *Daemon) wgStats(args ...string) (string, error) {
   234  	flags := flag.NewFlagSet("", flag.ContinueOnError)
   235  	interfaceName := flags.String("iface", "", "")
   236  	if err := flags.Parse(args[1:]); err != nil {
   237  		return "", err
   238  	}
   239  	if *interfaceName == "" {
   240  		return "", errors.New("-iface is required")
   241  	}
   242  	stats, err := d.monitor.Stats(*interfaceName)
   243  	if err != nil {
   244  		return "", fmt.Errorf("could not get device stats for %s interface: %w", *interfaceName, err)
   245  	}
   246  
   247  	statsJSON, err := json.Marshal(stats)
   248  	if err != nil {
   249  		return "", fmt.Errorf("could not marshal stats to JSON: %w", err)
   250  	}
   251  	return string(statsJSON), nil
   252  }