github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/endpoint/kernelspace/client.go (about) 1 /* 2 * Copyright (C) 2018 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 kernelspace 19 20 import ( 21 "encoding/base64" 22 "fmt" 23 "net" 24 "strconv" 25 "time" 26 27 "github.com/pkg/errors" 28 "golang.zx2c4.com/wireguard/wgctrl" 29 "golang.zx2c4.com/wireguard/wgctrl/wgtypes" 30 31 "github.com/mysteriumnetwork/node/services/wireguard/connection/dns" 32 "github.com/mysteriumnetwork/node/services/wireguard/wgcfg" 33 "github.com/mysteriumnetwork/node/utils" 34 "github.com/mysteriumnetwork/node/utils/actionstack" 35 "github.com/mysteriumnetwork/node/utils/cmdutil" 36 "github.com/mysteriumnetwork/node/utils/netutil" 37 ) 38 39 type client struct { 40 iface string 41 wgClient *wgctrl.Client 42 dnsManager dns.Manager 43 } 44 45 // NewWireguardClient creates new wireguard kernel space client. 46 func NewWireguardClient() (*client, error) { 47 wgClient, err := wgctrl.New() 48 if err != nil { 49 return nil, err 50 } 51 return &client{ 52 wgClient: wgClient, 53 dnsManager: dns.NewManager(), 54 }, nil 55 } 56 57 func (c *client) ReConfigureDevice(config wgcfg.DeviceConfig) error { 58 err := c.configureDevice(config) 59 if err != nil { 60 return err 61 } 62 63 return nil 64 } 65 66 func (c *client) ConfigureDevice(config wgcfg.DeviceConfig) error { 67 rollback := actionstack.NewActionStack() 68 69 if err := c.up(config.IfaceName); err != nil { 70 return err 71 } 72 rollback.Push(func() { 73 _ = c.DestroyDevice(config.IfaceName) 74 }) 75 76 if config.Peer.Endpoint != nil { 77 if err := netutil.AddDefaultRoute(config.IfaceName); err != nil { 78 rollback.Run() 79 return err 80 } 81 } 82 83 err := c.configureDevice(config) 84 if err != nil { 85 rollback.Run() 86 return err 87 } 88 89 return nil 90 } 91 92 func (c *client) configureDevice(config wgcfg.DeviceConfig) error { 93 if err := cmdutil.SudoExec("ip", "address", "replace", "dev", config.IfaceName, config.Subnet.String()); err != nil { 94 return err 95 } 96 97 if config.MTU > 0 { 98 if err := cmdutil.SudoExec("ip", "link", "set", "dev", config.IfaceName, "mtu", strconv.Itoa(config.MTU)); err != nil { 99 return err 100 } 101 } 102 103 peer, err := peerConfig(config.Peer) 104 if err != nil { 105 return err 106 } 107 108 privateKey, err := stringToKey(config.PrivateKey) 109 if err != nil { 110 return err 111 } 112 113 c.iface = config.IfaceName 114 deviceConfig := wgtypes.Config{ 115 PrivateKey: &privateKey, 116 ListenPort: &config.ListenPort, 117 Peers: []wgtypes.PeerConfig{peer}, 118 ReplacePeers: true, 119 } 120 121 if err := c.wgClient.ConfigureDevice(c.iface, deviceConfig); err != nil { 122 return fmt.Errorf("could not configure kernel space device: %w", err) 123 } 124 125 if err := c.dnsManager.Set(dns.Config{ 126 ScriptDir: config.DNSScriptDir, 127 IfaceName: config.IfaceName, 128 DNS: config.DNS, 129 }); err != nil { 130 return fmt.Errorf("could not set DNS: %w", err) 131 } 132 133 return nil 134 } 135 136 func peerConfig(peer wgcfg.Peer) (wgtypes.PeerConfig, error) { 137 endpoint := peer.Endpoint 138 publicKey, err := stringToKey(peer.PublicKey) 139 if err != nil { 140 return wgtypes.PeerConfig{}, errors.Wrap(err, "could not convert string key to wgtypes.Key") 141 } 142 143 // Apply keep alive interval 144 var keepAliveInterval *time.Duration 145 if peer.KeepAlivePeriodSeconds > 0 { 146 interval := time.Duration(peer.KeepAlivePeriodSeconds) * time.Second 147 keepAliveInterval = &interval 148 } 149 150 // Apply allowed IPs network 151 var allowedIPs []net.IPNet 152 for _, ip := range peer.AllowedIPs { 153 _, network, err := net.ParseCIDR(ip) 154 if err != nil { 155 return wgtypes.PeerConfig{}, fmt.Errorf("could not parse IP %q: %v", ip, err) 156 } 157 allowedIPs = append(allowedIPs, *network) 158 } 159 160 return wgtypes.PeerConfig{ 161 Endpoint: endpoint, 162 PublicKey: publicKey, 163 AllowedIPs: allowedIPs, 164 PersistentKeepaliveInterval: keepAliveInterval, 165 }, nil 166 } 167 168 func (c *client) PeerStats(string) (wgcfg.Stats, error) { 169 d, err := c.wgClient.Device(c.iface) 170 if err != nil { 171 return wgcfg.Stats{}, err 172 } 173 174 if len(d.Peers) != 1 { 175 return wgcfg.Stats{}, errors.New("kernelspace: exactly 1 peer expected") 176 } 177 178 return wgcfg.Stats{ 179 BytesReceived: uint64(d.Peers[0].ReceiveBytes), 180 BytesSent: uint64(d.Peers[0].TransmitBytes), 181 LastHandshake: d.Peers[0].LastHandshakeTime, 182 }, nil 183 } 184 185 func (c *client) DestroyDevice(name string) error { 186 return cmdutil.SudoExec("ip", "link", "del", "dev", name) 187 } 188 189 func (c *client) up(iface string) error { 190 rollback := actionstack.NewActionStack() 191 if d, err := c.wgClient.Device(iface); err != nil || d.Name != iface { 192 if err := cmdutil.SudoExec("ip", "link", "add", "dev", iface, "type", "wireguard"); err != nil { 193 return err 194 } 195 } 196 rollback.Push(func() { 197 _ = c.DestroyDevice(iface) 198 }) 199 200 if err := cmdutil.SudoExec("ip", "link", "set", "dev", iface, "up"); err != nil { 201 rollback.Run() 202 return err 203 } 204 205 return nil 206 } 207 208 func (c *client) Close() (err error) { 209 errs := utils.ErrorCollection{} 210 if err := c.DestroyDevice(c.iface); err != nil { 211 errs.Add(err) 212 } 213 if err := c.wgClient.Close(); err != nil { 214 errs.Add(err) 215 } 216 if err := c.dnsManager.Clean(); err != nil { 217 errs.Add(err) 218 } 219 if err := errs.Error(); err != nil { 220 return fmt.Errorf("could not close client: %w", err) 221 } 222 return nil 223 } 224 225 func stringToKey(key string) (wgtypes.Key, error) { 226 k, err := base64.StdEncoding.DecodeString(key) 227 if err != nil { 228 return wgtypes.Key{}, err 229 } 230 return wgtypes.NewKey(k) 231 }