github.com/moby/docker@v26.1.3+incompatible/libnetwork/netutils/utils.go (about)

     1  // Network utility functions.
     2  
     3  package netutils
     4  
     5  import (
     6  	"context"
     7  	"crypto/rand"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"net"
    13  	"strings"
    14  	"sync"
    15  
    16  	"github.com/containerd/log"
    17  	"github.com/docker/docker/libnetwork/types"
    18  )
    19  
    20  var (
    21  	// ErrNetworkOverlapsWithNameservers preformatted error
    22  	ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
    23  	// ErrNetworkOverlaps preformatted error
    24  	ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
    25  )
    26  
    27  // CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
    28  func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
    29  	if len(nameservers) > 0 {
    30  		for _, ns := range nameservers {
    31  			_, nsNetwork, err := net.ParseCIDR(ns)
    32  			if err != nil {
    33  				return err
    34  			}
    35  			if NetworkOverlaps(toCheck, nsNetwork) {
    36  				return ErrNetworkOverlapsWithNameservers
    37  			}
    38  		}
    39  	}
    40  	return nil
    41  }
    42  
    43  // NetworkOverlaps detects overlap between one IPNet and another
    44  func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
    45  	return netX.Contains(netY.IP) || netY.Contains(netX.IP)
    46  }
    47  
    48  // NetworkRange calculates the first and last IP addresses in an IPNet
    49  func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
    50  	if network == nil {
    51  		return nil, nil
    52  	}
    53  
    54  	firstIP := network.IP.Mask(network.Mask)
    55  	lastIP := types.GetIPCopy(firstIP)
    56  	for i := 0; i < len(firstIP); i++ {
    57  		lastIP[i] = firstIP[i] | ^network.Mask[i]
    58  	}
    59  
    60  	if network.IP.To4() != nil {
    61  		firstIP = firstIP.To4()
    62  		lastIP = lastIP.To4()
    63  	}
    64  
    65  	return firstIP, lastIP
    66  }
    67  
    68  func genMAC(ip net.IP) net.HardwareAddr {
    69  	hw := make(net.HardwareAddr, 6)
    70  	// The first byte of the MAC address has to comply with these rules:
    71  	// 1. Unicast: Set the least-significant bit to 0.
    72  	// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
    73  	hw[0] = 0x02
    74  	// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
    75  	// Since this address is locally administered, we can do whatever we want as long as
    76  	// it doesn't conflict with other addresses.
    77  	hw[1] = 0x42
    78  	// Fill the remaining 4 bytes based on the input
    79  	if ip == nil {
    80  		rand.Read(hw[2:])
    81  	} else {
    82  		copy(hw[2:], ip.To4())
    83  	}
    84  	return hw
    85  }
    86  
    87  // GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC)
    88  func GenerateRandomMAC() net.HardwareAddr {
    89  	return genMAC(nil)
    90  }
    91  
    92  // GenerateMACFromIP returns a locally administered MAC address where the 4 least
    93  // significant bytes are derived from the IPv4 address.
    94  func GenerateMACFromIP(ip net.IP) net.HardwareAddr {
    95  	return genMAC(ip)
    96  }
    97  
    98  // GenerateRandomName returns a string of the specified length, created by joining the prefix to random hex characters.
    99  // The length must be strictly larger than len(prefix), or an error will be returned.
   100  func GenerateRandomName(prefix string, length int) (string, error) {
   101  	if length <= len(prefix) {
   102  		return "", fmt.Errorf("invalid length %d for prefix %s", length, prefix)
   103  	}
   104  
   105  	// We add 1 here as integer division will round down, and we want to round up.
   106  	b := make([]byte, (length-len(prefix)+1)/2)
   107  	if _, err := io.ReadFull(rand.Reader, b); err != nil {
   108  		return "", err
   109  	}
   110  
   111  	// By taking a slice here, we ensure that the string is always the correct length.
   112  	return (prefix + hex.EncodeToString(b))[:length], nil
   113  }
   114  
   115  // ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in
   116  // the dotted decimal form . This is used to setup the IP to service name mapping in the optimal
   117  // way for the DNS PTR queries.
   118  func ReverseIP(IP string) string {
   119  	var reverseIP []string
   120  
   121  	if net.ParseIP(IP).To4() != nil {
   122  		reverseIP = strings.Split(IP, ".")
   123  		l := len(reverseIP)
   124  		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
   125  			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
   126  		}
   127  	} else {
   128  		reverseIP = strings.Split(IP, ":")
   129  
   130  		// Reversed IPv6 is represented in dotted decimal instead of the typical
   131  		// colon hex notation
   132  		for key := range reverseIP {
   133  			if len(reverseIP[key]) == 0 { // expand the compressed 0s
   134  				reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":"))
   135  			} else if len(reverseIP[key]) < 4 { // 0-padding needed
   136  				reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key]
   137  			}
   138  		}
   139  
   140  		reverseIP = strings.Split(strings.Join(reverseIP, ""), "")
   141  
   142  		l := len(reverseIP)
   143  		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
   144  			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
   145  		}
   146  	}
   147  
   148  	return strings.Join(reverseIP, ".")
   149  }
   150  
   151  var (
   152  	v6ListenableCached bool
   153  	v6ListenableOnce   sync.Once
   154  )
   155  
   156  // IsV6Listenable returns true when `[::1]:0` is listenable.
   157  // IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option.
   158  func IsV6Listenable() bool {
   159  	v6ListenableOnce.Do(func() {
   160  		ln, err := net.Listen("tcp6", "[::1]:0")
   161  		if err != nil {
   162  			// When the kernel was booted with `ipv6.disable=1`,
   163  			// we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol"
   164  			// https://github.com/moby/moby/issues/42288
   165  			log.G(context.TODO()).Debugf("v6Listenable=false (%v)", err)
   166  		} else {
   167  			v6ListenableCached = true
   168  			ln.Close()
   169  		}
   170  	})
   171  	return v6ListenableCached
   172  }