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  }