github.com/noisysockets/noisysockets@v0.21.2-0.20240515114641-7f467e651c90/network.go (about)

     1  // SPDX-License-Identifier: MPL-2.0
     2  /*
     3   * Copyright (C) 2024 The Noisy Sockets Authors.
     4   *
     5   * This Source Code Form is subject to the terms of the Mozilla Public
     6   * License, v. 2.0. If a copy of the MPL was not distributed with this
     7   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     8   *
     9   * Portions of this file are based on code originally from wireguard-go,
    10   *
    11   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
    12   *
    13   * Permission is hereby granted, free of charge, to any person obtaining a copy of
    14   * this software and associated documentation files (the "Software"), to deal in
    15   * the Software without restriction, including without limitation the rights to
    16   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
    17   * of the Software, and to permit persons to whom the Software is furnished to do
    18   * so, subject to the following conditions:
    19   *
    20   * The above copyright notice and this permission notice shall be included in all
    21   * copies or substantial portions of the Software.
    22   *
    23   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    26   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    29   * SOFTWARE.
    30   */
    31  
    32  package noisysockets
    33  
    34  import (
    35  	"fmt"
    36  	"log/slog"
    37  	"net/netip"
    38  	"strconv"
    39  	"strings"
    40  
    41  	"context"
    42  	"regexp"
    43  	"time"
    44  
    45  	stdnet "net"
    46  
    47  	miekgdns "github.com/miekg/dns"
    48  	"github.com/noisysockets/netstack/pkg/tcpip"
    49  	"github.com/noisysockets/netstack/pkg/tcpip/adapters/gonet"
    50  	"github.com/noisysockets/netstack/pkg/tcpip/network/ipv4"
    51  	"github.com/noisysockets/netstack/pkg/tcpip/network/ipv6"
    52  	"github.com/noisysockets/netstack/pkg/tcpip/stack"
    53  	"github.com/noisysockets/netstack/pkg/tcpip/transport/icmp"
    54  	"github.com/noisysockets/netstack/pkg/tcpip/transport/tcp"
    55  	"github.com/noisysockets/netstack/pkg/tcpip/transport/udp"
    56  	"github.com/noisysockets/noisysockets/config"
    57  	latestconfig "github.com/noisysockets/noisysockets/config/v1alpha2"
    58  	"github.com/noisysockets/noisysockets/internal/conn"
    59  	"github.com/noisysockets/noisysockets/internal/dns"
    60  	"github.com/noisysockets/noisysockets/internal/dns/addrselect"
    61  	"github.com/noisysockets/noisysockets/internal/transport"
    62  	"github.com/noisysockets/noisysockets/internal/util"
    63  	"github.com/noisysockets/noisysockets/network"
    64  	"github.com/noisysockets/noisysockets/types"
    65  )
    66  
    67  var (
    68  	ErrCanceled          = fmt.Errorf("operation was canceled")
    69  	ErrTimeout           = fmt.Errorf("i/o timeout")
    70  	ErrNumericPort       = fmt.Errorf("port must be numeric")
    71  	ErrNoSuitableAddress = fmt.Errorf("no suitable address found")
    72  	ErrMissingAddress    = fmt.Errorf("missing address")
    73  	ErrUnknownPeer       = fmt.Errorf("unknown peer")
    74  )
    75  
    76  var protoSplitter = regexp.MustCompile(`^(tcp|udp)(4|6)?$`)
    77  
    78  type NoisySocketsNetwork struct {
    79  	logger         *slog.Logger
    80  	peers          *peerList
    81  	rt             *routingTable
    82  	transport      *transport.Transport
    83  	stack          *stack.Stack
    84  	hostname       string
    85  	interfaceAddrs []netip.Addr
    86  	domain         string
    87  	resolver       dns.Resolver
    88  }
    89  
    90  // OpenNetwork creates a new network using the provided configuration.
    91  // The returned network is a userspace WireGuard peer that exposes
    92  // Dial() and Listen() methods compatible with the net package.
    93  func OpenNetwork(logger *slog.Logger, conf *latestconfig.Config) (network.Network, error) {
    94  	var privateKey types.NoisePrivateKey
    95  	if err := privateKey.UnmarshalText([]byte(conf.PrivateKey)); err != nil {
    96  		return nil, fmt.Errorf("failed to parse private key: %w", err)
    97  	}
    98  
    99  	publicKey := privateKey.Public()
   100  
   101  	logger = logger.With(slog.String("id", publicKey.DisplayString()))
   102  
   103  	net := &NoisySocketsNetwork{
   104  		logger: logger,
   105  		peers:  newPeerList(),
   106  		rt:     newRoutingTable(logger),
   107  		stack: stack.New(stack.Options{
   108  			NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
   109  			TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
   110  			HandleLocal:        true,
   111  		}),
   112  	}
   113  
   114  	net.domain = config.DefaultDomain
   115  	if conf.DNS != nil && conf.DNS.Domain != "" {
   116  		net.domain = miekgdns.Fqdn(conf.DNS.Domain)
   117  	}
   118  
   119  	// Parse local addresses.
   120  	var err error
   121  	net.interfaceAddrs, err = util.ParseAddrList(conf.IPs)
   122  	if err != nil {
   123  		return nil, fmt.Errorf("could not parse local addresses: %w", err)
   124  	}
   125  
   126  	// Add the local node to the list of peers.
   127  	net.hostname = conf.Name
   128  	if net.hostname != "" {
   129  		p := newPeer(nil, net.hostname, publicKey)
   130  		p.AddAddresses(net.interfaceAddrs...)
   131  		net.peers.add(p)
   132  	}
   133  
   134  	if conf.DNS != nil && len(conf.DNS.Nameservers) > 0 {
   135  		nameservers, err := util.ParseAddrPortList(conf.DNS.Nameservers)
   136  		if err != nil {
   137  			return nil, fmt.Errorf("could not parse nameserver addresses: %w", err)
   138  		}
   139  
   140  		net.resolver = dns.NewUDPResolver(net, nameservers)
   141  	}
   142  
   143  	sourceSink, err := newSourceSink(logger, net.rt, net.stack, net.interfaceAddrs)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("could not create source sink: %w", err)
   146  	}
   147  
   148  	net.transport = transport.NewTransport(logger, sourceSink, conn.NewStdNetBind())
   149  
   150  	net.transport.SetPrivateKey(privateKey)
   151  
   152  	if err := net.transport.UpdatePort(conf.ListenPort); err != nil {
   153  		return nil, fmt.Errorf("failed to update port: %w", err)
   154  	}
   155  
   156  	// Add peers.
   157  	for _, peerConf := range conf.Peers {
   158  		if err := net.AddPeer(peerConf); err != nil {
   159  			return nil, fmt.Errorf("could not add peer %s: %w", peerConf.Name, err)
   160  		}
   161  	}
   162  
   163  	// Add routes.
   164  	for _, routeConf := range conf.Routes {
   165  		destination, err := netip.ParsePrefix(routeConf.Destination)
   166  		if err != nil {
   167  			return nil, fmt.Errorf("could not parse route destination: %w", err)
   168  		}
   169  
   170  		if err := net.AddRoute(destination, routeConf.Via); err != nil {
   171  			return nil, fmt.Errorf("could not add route: %w", err)
   172  		}
   173  	}
   174  
   175  	logger.Debug("Bringing transport up")
   176  
   177  	if err := net.transport.Up(); err != nil {
   178  		return nil, fmt.Errorf("failed to bring transport up: %w", err)
   179  	}
   180  
   181  	return net, nil
   182  }
   183  
   184  func (net *NoisySocketsNetwork) Close() error {
   185  	net.stack.Close()
   186  
   187  	if err := net.transport.Close(); err != nil {
   188  		return fmt.Errorf("failed to close transport: %w", err)
   189  	}
   190  
   191  	return nil
   192  }
   193  
   194  func (net *NoisySocketsNetwork) Hostname() (string, error) {
   195  	return net.hostname, nil
   196  }
   197  
   198  func (net *NoisySocketsNetwork) InterfaceAddrs() ([]stdnet.Addr, error) {
   199  	var addrs []stdnet.Addr
   200  	for _, addr := range net.interfaceAddrs {
   201  		addrs = append(addrs, &stdnet.IPAddr{
   202  			IP: stdnet.IP(addr.AsSlice()),
   203  		})
   204  	}
   205  
   206  	return addrs, nil
   207  }
   208  
   209  func (net *NoisySocketsNetwork) LookupHost(host string) ([]string, error) {
   210  	logger := net.logger.With(slog.String("host", host))
   211  
   212  	logger.Debug("Looking up host")
   213  
   214  	var addrs []netip.Addr
   215  
   216  	// Host is an IP address.
   217  	if addr, err := netip.ParseAddr(host); err == nil {
   218  		addrs = append(addrs, addr)
   219  
   220  		logger.Debug("Host is an IP address")
   221  
   222  		goto LOOKUP_HOST_DONE
   223  	}
   224  
   225  	// Fully qualify the host name (if not already).
   226  	host = miekgdns.Fqdn(host)
   227  
   228  	{
   229  		// Check if the host name is a peer name (strip the default search domain suffix if present).
   230  		peerName := strings.TrimSuffix(strings.TrimSuffix(host, net.domain), ".")
   231  
   232  		// Host is the name of a peer.
   233  		if p, ok := net.peers.getByName(peerName); ok {
   234  			addrs = p.Addresses()
   235  
   236  			logger.Debug("Host is the name of a peer")
   237  
   238  			goto LOOKUP_HOST_DONE
   239  		}
   240  	}
   241  
   242  	// Host is a DNS name.
   243  	if net.resolver != nil {
   244  		logger.Debug("Resolving host, using DNS")
   245  
   246  		var err error
   247  		addrs, err = net.resolver.LookupHost(host)
   248  		if err != nil {
   249  			return nil, err
   250  		}
   251  
   252  		if len(addrs) >= 0 {
   253  			logger.Debug("Host is a DNS name")
   254  		}
   255  	}
   256  
   257  LOOKUP_HOST_DONE:
   258  	if len(addrs) == 0 {
   259  		return nil, &stdnet.DNSError{Err: "no such host", Name: host}
   260  	}
   261  
   262  	addrselect.SortByRFC6724(net, addrs)
   263  
   264  	addrsStrings := make([]string, 0, len(addrs))
   265  	for _, addr := range addrs {
   266  		addrsStrings = append(addrsStrings, addr.String())
   267  	}
   268  
   269  	return addrsStrings, nil
   270  }
   271  
   272  func (net *NoisySocketsNetwork) Dial(network, address string) (stdnet.Conn, error) {
   273  	return net.DialContext(context.Background(), network, address)
   274  }
   275  
   276  func (net *NoisySocketsNetwork) DialContext(ctx context.Context, network, address string) (stdnet.Conn, error) {
   277  	net.logger.Debug("Dialing", slog.String("network", network), slog.String("address", address))
   278  
   279  	acceptV4, acceptV6 := true, true
   280  	matches := protoSplitter.FindStringSubmatch(network)
   281  	if matches == nil {
   282  		return nil, &stdnet.OpError{Op: "dial", Err: stdnet.UnknownNetworkError(network)}
   283  	} else if len(matches[2]) != 0 {
   284  		acceptV4 = matches[2][0] == '4'
   285  		acceptV6 = !acceptV4
   286  	}
   287  
   288  	host, sport, err := stdnet.SplitHostPort(address)
   289  	if err != nil {
   290  		return nil, &stdnet.OpError{Op: "dial", Err: err}
   291  	}
   292  
   293  	port, err := strconv.Atoi(sport)
   294  	if err != nil || port < 0 || port > 65535 {
   295  		return nil, &stdnet.OpError{Op: "dial", Err: ErrNumericPort}
   296  	}
   297  
   298  	allAddr, err := net.LookupHost(host)
   299  	if err != nil {
   300  		return nil, &stdnet.OpError{Op: "dial", Err: err}
   301  	}
   302  
   303  	var addrs []netip.AddrPort
   304  	for _, addr := range allAddr {
   305  		ip, err := netip.ParseAddr(addr)
   306  		if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
   307  			addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
   308  		}
   309  	}
   310  	if len(addrs) == 0 && len(allAddr) != 0 {
   311  		return nil, &stdnet.OpError{Op: "dial", Err: ErrNoSuitableAddress}
   312  	}
   313  
   314  	var firstErr error
   315  	for i, addr := range addrs {
   316  		select {
   317  		case <-ctx.Done():
   318  			err := ctx.Err()
   319  			if err == context.Canceled {
   320  				err = ErrCanceled
   321  			} else if err == context.DeadlineExceeded {
   322  				err = ErrTimeout
   323  			}
   324  			return nil, &stdnet.OpError{Op: "dial", Err: err}
   325  		default:
   326  		}
   327  
   328  		dialCtx := ctx
   329  		if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
   330  			partialDeadline, err := partialDeadline(time.Now(), deadline, len(addrs)-i)
   331  			if err != nil {
   332  				if firstErr == nil {
   333  					firstErr = &stdnet.OpError{Op: "dial", Err: err}
   334  				}
   335  				break
   336  			}
   337  			if partialDeadline.Before(deadline) {
   338  				var cancel context.CancelFunc
   339  				dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
   340  				defer cancel()
   341  			}
   342  		}
   343  
   344  		fa, pn := convertToFullAddr(addr)
   345  
   346  		var c stdnet.Conn
   347  		switch matches[1] {
   348  		case "tcp":
   349  			c, err = gonet.DialContextTCP(dialCtx, net.stack, fa, pn)
   350  		case "udp":
   351  			c, err = gonet.DialUDP(net.stack, nil, &fa, pn)
   352  		}
   353  		if err == nil {
   354  			return c, nil
   355  		}
   356  		if firstErr == nil {
   357  			firstErr = err
   358  		}
   359  	}
   360  	if firstErr == nil {
   361  		firstErr = &stdnet.OpError{Op: "dial", Err: ErrMissingAddress}
   362  	}
   363  
   364  	return nil, firstErr
   365  }
   366  
   367  func (net *NoisySocketsNetwork) Listen(network, address string) (stdnet.Listener, error) {
   368  	net.logger.Debug("Listening",
   369  		slog.String("network", network), slog.String("address", address))
   370  
   371  	acceptV4, acceptV6 := true, true
   372  	matches := protoSplitter.FindStringSubmatch(network)
   373  	if matches == nil {
   374  		return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError(network)}
   375  	} else if len(matches[2]) != 0 {
   376  		acceptV4 = matches[2][0] == '4'
   377  		acceptV6 = !acceptV4
   378  	}
   379  
   380  	if matches[1] != "tcp" {
   381  		return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError(network)}
   382  	}
   383  
   384  	host, sport, err := stdnet.SplitHostPort(address)
   385  	if err != nil {
   386  		return nil, &stdnet.OpError{Op: "listen", Err: err}
   387  	}
   388  
   389  	port, err := strconv.Atoi(sport)
   390  	if err != nil || port < 0 || port > 65535 {
   391  		return nil, &stdnet.OpError{Op: "listen", Err: ErrNumericPort}
   392  	}
   393  
   394  	var addr netip.AddrPort
   395  	if host != "" && !(host == "0.0.0.0" || host == "[::]") {
   396  		ip, err := netip.ParseAddr(host)
   397  		if err != nil {
   398  			return nil, &stdnet.OpError{Op: "listen", Err: err}
   399  		}
   400  
   401  		if ip.Is4() && !acceptV4 {
   402  			return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError("tcp4")}
   403  		}
   404  
   405  		if ip.Is6() && !acceptV6 {
   406  			return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError("tcp6")}
   407  		}
   408  
   409  		addr = netip.AddrPortFrom(ip, uint16(port))
   410  	} else {
   411  		for _, localAddr := range net.interfaceAddrs {
   412  			if localAddr.Is6() && acceptV6 {
   413  				addr = netip.AddrPortFrom(localAddr, uint16(port))
   414  				break
   415  			}
   416  			if localAddr.Is4() && acceptV4 {
   417  				addr = netip.AddrPortFrom(localAddr, uint16(port))
   418  				break
   419  			}
   420  		}
   421  	}
   422  
   423  	fa, pn := convertToFullAddr(addr)
   424  	lis, err := gonet.ListenTCP(net.stack, fa, pn)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	return &listener{Listener: lis, peers: net.peers}, nil
   430  }
   431  
   432  func (net *NoisySocketsNetwork) ListenPacket(network, address string) (stdnet.PacketConn, error) {
   433  	net.logger.Debug("Listening for packets",
   434  		slog.String("network", network), slog.String("address", address))
   435  
   436  	acceptV4, acceptV6 := true, true
   437  	matches := protoSplitter.FindStringSubmatch(network)
   438  	if matches == nil {
   439  		return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError(network)}
   440  	} else if len(matches[2]) != 0 {
   441  		acceptV4 = matches[2][0] == '4'
   442  		acceptV6 = !acceptV4
   443  	}
   444  
   445  	if matches[1] != "udp" {
   446  		return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError(network)}
   447  	}
   448  
   449  	host, sport, err := stdnet.SplitHostPort(address)
   450  	if err != nil {
   451  		return nil, &stdnet.OpError{Op: "listen", Err: err}
   452  	}
   453  
   454  	port, err := strconv.Atoi(sport)
   455  	if err != nil || port < 0 || port > 65535 {
   456  		return nil, &stdnet.OpError{Op: "listen", Err: ErrNumericPort}
   457  	}
   458  
   459  	var addr netip.AddrPort
   460  	if host != "" && !(host == "0.0.0.0" || host == "[::]") {
   461  		ip, err := netip.ParseAddr(host)
   462  		if err != nil {
   463  			return nil, &stdnet.OpError{Op: "listen", Err: err}
   464  		}
   465  
   466  		if ip.Is4() && !acceptV4 {
   467  			return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError("udp4")}
   468  		}
   469  
   470  		if ip.Is6() && !acceptV6 {
   471  			return nil, &stdnet.OpError{Op: "listen", Err: stdnet.UnknownNetworkError("udp6")}
   472  		}
   473  
   474  		addr = netip.AddrPortFrom(ip, uint16(port))
   475  	} else {
   476  		for _, localAddr := range net.interfaceAddrs {
   477  			if localAddr.Is6() && acceptV6 {
   478  				addr = netip.AddrPortFrom(localAddr, uint16(port))
   479  				break
   480  			}
   481  			if localAddr.Is4() && acceptV4 {
   482  				addr = netip.AddrPortFrom(localAddr, uint16(port))
   483  				break
   484  			}
   485  		}
   486  	}
   487  
   488  	fa, pn := convertToFullAddr(addr)
   489  	pc, err := gonet.DialUDP(net.stack, &fa, nil, pn)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	return &packetConn{PacketConn: pc, peers: net.peers}, nil
   495  }
   496  
   497  // AddPeer adds a wireguard peer to the network.
   498  func (net *NoisySocketsNetwork) AddPeer(peerConf latestconfig.PeerConfig) error {
   499  	var publicKey types.NoisePublicKey
   500  	if err := publicKey.UnmarshalText([]byte(peerConf.PublicKey)); err != nil {
   501  		return fmt.Errorf("failed to parse peer public key: %w", err)
   502  	}
   503  
   504  	net.logger.Debug("Adding peer",
   505  		slog.String("name", peerConf.Name),
   506  		slog.String("peer", publicKey.DisplayString()))
   507  
   508  	var addrs []netip.Addr
   509  	for _, ip := range peerConf.IPs {
   510  		addr, err := netip.ParseAddr(ip)
   511  		if err != nil {
   512  			return fmt.Errorf("could not parse peer address %q: %v", ip, err)
   513  		}
   514  		addrs = append(addrs, addr)
   515  	}
   516  
   517  	transportPeer, err := net.transport.NewPeer(publicKey)
   518  	if err != nil {
   519  		return fmt.Errorf("failed to create transport peer: %w", err)
   520  	}
   521  
   522  	p := newPeer(transportPeer, peerConf.Name, publicKey)
   523  	p.AddAddresses(addrs...)
   524  
   525  	// Add the peer to the list of peers.
   526  	net.peers.add(p)
   527  
   528  	// Add the peer to the routing table.
   529  	if err := net.rt.update(p); err != nil {
   530  		return fmt.Errorf("could not add peer to routing table: %w", err)
   531  	}
   532  
   533  	// Regularly send keepalives to the peer to keep NAT mappings valid.
   534  	// This could be configurable but I think it's a good default to avoid footguns.
   535  	p.SetKeepAliveInterval(25 * time.Second)
   536  
   537  	if peerConf.Endpoint != "" {
   538  		peerEndpointHost, peerEndpointPortStr, err := stdnet.SplitHostPort(peerConf.Endpoint)
   539  		if err != nil {
   540  			return fmt.Errorf("failed to parse peer endpoint: %w", err)
   541  		}
   542  
   543  		peerEndpointAddrs, err := stdnet.LookupHost(peerEndpointHost)
   544  		if err != nil {
   545  			return fmt.Errorf("failed to resolve peer address: %w", err)
   546  		}
   547  
   548  		peerEndpointPort, err := strconv.Atoi(peerEndpointPortStr)
   549  		if err != nil {
   550  			return fmt.Errorf("failed to parse peer port: %w", err)
   551  		}
   552  
   553  		// TODO: try all resolved addresses until one works.
   554  		p.SetEndpoint(netip.AddrPortFrom(netip.MustParseAddr(peerEndpointAddrs[0]), uint16(peerEndpointPort)))
   555  
   556  		p.Start()
   557  
   558  		if err := p.SendKeepalive(); err != nil {
   559  			net.logger.Warn("Failed to send initial keepalive", "peer", peerConf.Name, "error", err)
   560  		}
   561  	}
   562  
   563  	return nil
   564  }
   565  
   566  // RemovePeer removes a wireguard peer from the network.
   567  func (net *NoisySocketsNetwork) RemovePeer(publicKey types.NoisePublicKey) error {
   568  	net.logger.Debug("Removing peer", slog.String("peer", publicKey.DisplayString()))
   569  
   570  	// Remove the peer from the transport.
   571  	net.transport.RemovePeer(publicKey)
   572  
   573  	// Remove the peer from the peer list.
   574  	p, ok := net.peers.remove(publicKey)
   575  	if !ok {
   576  		return ErrUnknownPeer
   577  	}
   578  
   579  	// Remove the peer from the routing table.
   580  	if err := net.rt.remove(p); err != nil {
   581  		return fmt.Errorf("could not remove peer from routing table: %w", err)
   582  	}
   583  
   584  	return nil
   585  }
   586  
   587  // GetPeer returns a peer by its public key.
   588  func (net *NoisySocketsNetwork) GetPeer(publicKey types.NoisePublicKey) (*Peer, bool) {
   589  	return net.peers.get(publicKey)
   590  }
   591  
   592  // ListPeers returns a list of the public keys of all known peers.
   593  func (net *NoisySocketsNetwork) ListPeers() []types.NoisePublicKey {
   594  	var publicKeys []types.NoisePublicKey
   595  	_ = net.peers.forEach(func(p *Peer) error {
   596  		publicKeys = append(publicKeys, p.PublicKey())
   597  		return nil
   598  	})
   599  
   600  	return publicKeys
   601  }
   602  
   603  // AddRoute adds a routing table entry for the network.
   604  func (net *NoisySocketsNetwork) AddRoute(destination netip.Prefix, viaPeerName string) error {
   605  	net.logger.Debug("Adding route",
   606  		slog.String("destination", destination.String()),
   607  		slog.String("via", viaPeerName))
   608  
   609  	p, ok := net.peers.getByName(viaPeerName)
   610  	if !ok {
   611  		return ErrUnknownPeer
   612  	}
   613  
   614  	p.AddDestinationPrefixes(destination)
   615  
   616  	if err := net.rt.update(p); err != nil {
   617  		return fmt.Errorf("could not sync routing table: %w", err)
   618  	}
   619  
   620  	return nil
   621  }
   622  
   623  // RemoveRoute removes a routing table entry for the network.
   624  func (net *NoisySocketsNetwork) RemoveRoute(destination netip.Prefix) error {
   625  	net.logger.Debug("Removing route", slog.String("destination", destination.String()))
   626  
   627  	p, ok := net.peers.getByDestination(destination)
   628  	if !ok {
   629  		return fmt.Errorf("could not find peer for destination prefix: %v", destination)
   630  	}
   631  
   632  	p.RemoveDestinationPrefixes(destination)
   633  
   634  	if err := net.rt.update(p); err != nil {
   635  		return fmt.Errorf("could not sync routing table: %w", err)
   636  	}
   637  
   638  	return nil
   639  }
   640  
   641  func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
   642  	var protoNumber tcpip.NetworkProtocolNumber
   643  	if endpoint.Addr().Is4() {
   644  		protoNumber = ipv4.ProtocolNumber
   645  	} else {
   646  		protoNumber = ipv6.ProtocolNumber
   647  	}
   648  	return tcpip.FullAddress{
   649  		NIC:  1,
   650  		Addr: tcpip.AddrFromSlice(endpoint.Addr().AsSlice()),
   651  		Port: endpoint.Port(),
   652  	}, protoNumber
   653  }
   654  
   655  func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
   656  	if deadline.IsZero() {
   657  		return deadline, nil
   658  	}
   659  
   660  	timeRemaining := deadline.Sub(now)
   661  	if timeRemaining <= 0 {
   662  		return time.Time{}, ErrTimeout
   663  	}
   664  
   665  	timeout := timeRemaining / time.Duration(addrsRemaining)
   666  	const saneMinimum = 2 * time.Second
   667  	if timeout < saneMinimum {
   668  		if timeRemaining < saneMinimum {
   669  			timeout = timeRemaining
   670  		} else {
   671  			timeout = saneMinimum
   672  		}
   673  	}
   674  
   675  	return now.Add(timeout), nil
   676  }