github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/resources/allocator_unix.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 resources
    19  
    20  import (
    21  	"fmt"
    22  	"net"
    23  	"runtime"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/pkg/errors"
    29  
    30  	"github.com/mysteriumnetwork/node/core/port"
    31  )
    32  
    33  // MaxConnections sets the limit to the maximum number of wireguard connections.
    34  var MaxConnections = 256
    35  
    36  type portSupplier interface {
    37  	Acquire() (port.Port, error)
    38  }
    39  
    40  // Allocator is mock wireguard resource handler.
    41  // It will manage lists of network interfaces names, IP addresses and port for endpoints.
    42  type Allocator struct {
    43  	mu          sync.Mutex
    44  	Ifaces      map[int]struct{}
    45  	IPAddresses map[int]struct{}
    46  
    47  	portSupplier portSupplier
    48  	subnet       net.IPNet
    49  }
    50  
    51  // NewAllocator creates new resource pool for wireguard connection.
    52  func NewAllocator(ports portSupplier, subnet net.IPNet) *Allocator {
    53  	return &Allocator{
    54  		Ifaces:      make(map[int]struct{}),
    55  		IPAddresses: make(map[int]struct{}),
    56  
    57  		portSupplier: ports,
    58  		subnet:       subnet,
    59  	}
    60  }
    61  
    62  // AbandonedInterfaces returns a list of abandoned interfaces that exist in the system,
    63  // but was not allocated by the Allocator.
    64  func (a *Allocator) AbandonedInterfaces() ([]net.Interface, error) {
    65  	a.mu.Lock()
    66  	defer a.mu.Unlock()
    67  
    68  	if runtime.GOOS == "android" {
    69  		list := make([]net.Interface, 0)
    70  		return list, nil
    71  	}
    72  
    73  	ifaces, err := net.Interfaces()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	list := make([]net.Interface, 0)
    79  	for _, iface := range ifaces {
    80  		if strings.HasPrefix(iface.Name, interfacePrefix) {
    81  			ifaceID, err := strconv.Atoi(strings.TrimPrefix(iface.Name, interfacePrefix))
    82  			if err == nil {
    83  				if _, ok := a.Ifaces[ifaceID]; !ok {
    84  					list = append(list, iface)
    85  				}
    86  			}
    87  		}
    88  	}
    89  
    90  	return list, nil
    91  }
    92  
    93  // AllocateInterface provides available name for the wireguard network interface.
    94  func (a *Allocator) AllocateInterface() (string, error) {
    95  	a.mu.Lock()
    96  	defer a.mu.Unlock()
    97  
    98  	if runtime.GOOS == "android" {
    99  		return "myst0", nil
   100  	}
   101  
   102  	ifaces, err := net.Interfaces()
   103  	if err != nil {
   104  		return "", err
   105  	}
   106  
   107  	for i := 0; i < MaxConnections; i++ {
   108  		if _, ok := a.Ifaces[i]; !ok {
   109  			a.Ifaces[i] = struct{}{}
   110  			if interfaceExists(ifaces, fmt.Sprintf("%s%d", interfacePrefix, i)) {
   111  				continue
   112  			}
   113  
   114  			return fmt.Sprintf("%s%d", interfacePrefix, i), nil
   115  		}
   116  	}
   117  
   118  	return "", errors.New("no more unused interfaces")
   119  }
   120  
   121  // AllocateIPNet provides available IP address for the wireguard connection.
   122  func (a *Allocator) AllocateIPNet() (net.IPNet, error) {
   123  	a.mu.Lock()
   124  	defer a.mu.Unlock()
   125  
   126  	for i := 0; i < MaxConnections; i++ {
   127  		if _, ok := a.IPAddresses[i]; !ok {
   128  			a.IPAddresses[i] = struct{}{}
   129  			return calcIPNet(a.subnet, i), nil
   130  		}
   131  	}
   132  	return net.IPNet{}, errors.New("no more unused subnets")
   133  }
   134  
   135  // AllocatePort provides available UDP port for the wireguard endpoint.
   136  func (a *Allocator) AllocatePort() (int, error) {
   137  	a.mu.Lock()
   138  	defer a.mu.Unlock()
   139  
   140  	port, err := a.portSupplier.Acquire()
   141  	if err != nil {
   142  		return 0, err
   143  	}
   144  	return port.Num(), nil
   145  }
   146  
   147  // ReleaseInterface releases name for the wireguard network interface.
   148  func (a *Allocator) ReleaseInterface(iface string) error {
   149  	a.mu.Lock()
   150  	defer a.mu.Unlock()
   151  
   152  	i, err := strconv.Atoi(strings.TrimPrefix(iface, interfacePrefix))
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	if _, ok := a.Ifaces[i]; !ok {
   158  		return errors.New("allocated interface not found")
   159  	}
   160  
   161  	delete(a.Ifaces, i)
   162  	return nil
   163  }
   164  
   165  // ReleaseIPNet releases IP address.
   166  func (a *Allocator) ReleaseIPNet(ipnet net.IPNet) error {
   167  	a.mu.Lock()
   168  	defer a.mu.Unlock()
   169  
   170  	ip4 := ipnet.IP.To4()
   171  	if ip4 == nil {
   172  		return errors.New("allocated subnet not found")
   173  	}
   174  
   175  	i := int(ip4[2])
   176  	if _, ok := a.IPAddresses[i]; !ok {
   177  		return errors.New("allocated subnet not found")
   178  	}
   179  
   180  	delete(a.IPAddresses, i)
   181  	return nil
   182  }
   183  
   184  func interfaceExists(ifaces []net.Interface, name string) bool {
   185  	for _, iface := range ifaces {
   186  		if iface.Name == name {
   187  			return true
   188  		}
   189  	}
   190  	return false
   191  }
   192  
   193  func calcIPNet(ipnet net.IPNet, index int) net.IPNet {
   194  	ip := make(net.IP, len(ipnet.IP))
   195  	copy(ip, ipnet.IP)
   196  	ip = ip.To4()
   197  	ip[2] = byte(index)
   198  	return net.IPNet{IP: ip, Mask: net.IPv4Mask(255, 255, 255, 0)}
   199  }