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 }