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 }