github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/api/types/network/ipam.go (about)

     1  package network
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/netip"
     7  
     8  	"github.com/Prakhar-Agarwal-byte/moby/internal/multierror"
     9  )
    10  
    11  // IPAM represents IP Address Management
    12  type IPAM struct {
    13  	Driver  string
    14  	Options map[string]string // Per network IPAM driver options
    15  	Config  []IPAMConfig
    16  }
    17  
    18  // IPAMConfig represents IPAM configurations
    19  type IPAMConfig struct {
    20  	Subnet     string            `json:",omitempty"`
    21  	IPRange    string            `json:",omitempty"`
    22  	Gateway    string            `json:",omitempty"`
    23  	AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
    24  }
    25  
    26  type ipFamily string
    27  
    28  const (
    29  	ip4 ipFamily = "IPv4"
    30  	ip6 ipFamily = "IPv6"
    31  )
    32  
    33  // HasIPv6Subnets checks whether there's any IPv6 subnets in the ipam parameter. It ignores any invalid Subnet and nil
    34  // ipam.
    35  func HasIPv6Subnets(ipam *IPAM) bool {
    36  	if ipam == nil {
    37  		return false
    38  	}
    39  
    40  	for _, cfg := range ipam.Config {
    41  		subnet, err := netip.ParsePrefix(cfg.Subnet)
    42  		if err != nil {
    43  			continue
    44  		}
    45  
    46  		if subnet.Addr().Is6() {
    47  			return true
    48  		}
    49  	}
    50  
    51  	return false
    52  }
    53  
    54  // ValidateIPAM checks whether the network's IPAM passed as argument is valid. It returns a joinError of the list of
    55  // errors found.
    56  func ValidateIPAM(ipam *IPAM) error {
    57  	if ipam == nil {
    58  		return nil
    59  	}
    60  
    61  	var errs []error
    62  	for _, cfg := range ipam.Config {
    63  		subnet, err := netip.ParsePrefix(cfg.Subnet)
    64  		if err != nil {
    65  			errs = append(errs, fmt.Errorf("invalid subnet %s: invalid CIDR block notation", cfg.Subnet))
    66  			continue
    67  		}
    68  		subnetFamily := ip4
    69  		if subnet.Addr().Is6() {
    70  			subnetFamily = ip6
    71  		}
    72  
    73  		if subnet != subnet.Masked() {
    74  			errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked()))
    75  		}
    76  
    77  		if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 {
    78  			errs = append(errs, ipRangeErrs...)
    79  		}
    80  
    81  		if err := validateAddress(cfg.Gateway, subnet, subnetFamily); err != nil {
    82  			errs = append(errs, fmt.Errorf("invalid gateway %s: %w", cfg.Gateway, err))
    83  		}
    84  
    85  		for auxName, aux := range cfg.AuxAddress {
    86  			if err := validateAddress(aux, subnet, subnetFamily); err != nil {
    87  				errs = append(errs, fmt.Errorf("invalid auxiliary address %s: %w", auxName, err))
    88  			}
    89  		}
    90  	}
    91  
    92  	if err := multierror.Join(errs...); err != nil {
    93  		return fmt.Errorf("invalid network config:\n%w", err)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func validateIPRange(ipRange string, subnet netip.Prefix, subnetFamily ipFamily) []error {
   100  	if ipRange == "" {
   101  		return nil
   102  	}
   103  	prefix, err := netip.ParsePrefix(ipRange)
   104  	if err != nil {
   105  		return []error{fmt.Errorf("invalid ip-range %s: invalid CIDR block notation", ipRange)}
   106  	}
   107  	family := ip4
   108  	if prefix.Addr().Is6() {
   109  		family = ip6
   110  	}
   111  
   112  	if family != subnetFamily {
   113  		return []error{fmt.Errorf("invalid ip-range %s: parent subnet is an %s block", ipRange, subnetFamily)}
   114  	}
   115  
   116  	var errs []error
   117  	if prefix.Bits() < subnet.Bits() {
   118  		errs = append(errs, fmt.Errorf("invalid ip-range %s: CIDR block is bigger than its parent subnet %s", ipRange, subnet))
   119  	}
   120  	if prefix != prefix.Masked() {
   121  		errs = append(errs, fmt.Errorf("invalid ip-range %s: it should be %s", prefix, prefix.Masked()))
   122  	}
   123  	if !subnet.Overlaps(prefix) {
   124  		errs = append(errs, fmt.Errorf("invalid ip-range %s: parent subnet %s doesn't contain ip-range", ipRange, subnet))
   125  	}
   126  
   127  	return errs
   128  }
   129  
   130  func validateAddress(address string, subnet netip.Prefix, subnetFamily ipFamily) error {
   131  	if address == "" {
   132  		return nil
   133  	}
   134  	addr, err := netip.ParseAddr(address)
   135  	if err != nil {
   136  		return errors.New("invalid address")
   137  	}
   138  	family := ip4
   139  	if addr.Is6() {
   140  		family = ip6
   141  	}
   142  
   143  	if family != subnetFamily {
   144  		return fmt.Errorf("parent subnet is an %s block", subnetFamily)
   145  	}
   146  	if !subnet.Contains(addr) {
   147  		return fmt.Errorf("parent subnet %s doesn't contain this address", subnet)
   148  	}
   149  
   150  	return nil
   151  }