github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/api/types/network/ipam.go (about) 1 package network 2 3 import ( 4 "errors" 5 "fmt" 6 "net/netip" 7 8 "github.com/docker/docker/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 // ValidateIPAM checks whether the network's IPAM passed as argument is valid. It returns a joinError of the list of 34 // errors found. 35 func ValidateIPAM(ipam *IPAM, enableIPv6 bool) error { 36 if ipam == nil { 37 return nil 38 } 39 40 var errs []error 41 for _, cfg := range ipam.Config { 42 subnet, err := netip.ParsePrefix(cfg.Subnet) 43 if err != nil { 44 errs = append(errs, fmt.Errorf("invalid subnet %s: invalid CIDR block notation", cfg.Subnet)) 45 continue 46 } 47 subnetFamily := ip4 48 if subnet.Addr().Is6() { 49 subnetFamily = ip6 50 } 51 52 if !enableIPv6 && subnetFamily == ip6 { 53 continue 54 } 55 56 if subnet != subnet.Masked() { 57 errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked())) 58 } 59 60 if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 { 61 errs = append(errs, ipRangeErrs...) 62 } 63 64 if err := validateAddress(cfg.Gateway, subnet, subnetFamily); err != nil { 65 errs = append(errs, fmt.Errorf("invalid gateway %s: %w", cfg.Gateway, err)) 66 } 67 68 for auxName, aux := range cfg.AuxAddress { 69 if err := validateAddress(aux, subnet, subnetFamily); err != nil { 70 errs = append(errs, fmt.Errorf("invalid auxiliary address %s: %w", auxName, err)) 71 } 72 } 73 } 74 75 if err := multierror.Join(errs...); err != nil { 76 return fmt.Errorf("invalid network config:\n%w", err) 77 } 78 79 return nil 80 } 81 82 func validateIPRange(ipRange string, subnet netip.Prefix, subnetFamily ipFamily) []error { 83 if ipRange == "" { 84 return nil 85 } 86 prefix, err := netip.ParsePrefix(ipRange) 87 if err != nil { 88 return []error{fmt.Errorf("invalid ip-range %s: invalid CIDR block notation", ipRange)} 89 } 90 family := ip4 91 if prefix.Addr().Is6() { 92 family = ip6 93 } 94 95 if family != subnetFamily { 96 return []error{fmt.Errorf("invalid ip-range %s: parent subnet is an %s block", ipRange, subnetFamily)} 97 } 98 99 var errs []error 100 if prefix.Bits() < subnet.Bits() { 101 errs = append(errs, fmt.Errorf("invalid ip-range %s: CIDR block is bigger than its parent subnet %s", ipRange, subnet)) 102 } 103 if prefix != prefix.Masked() { 104 errs = append(errs, fmt.Errorf("invalid ip-range %s: it should be %s", prefix, prefix.Masked())) 105 } 106 if !subnet.Overlaps(prefix) { 107 errs = append(errs, fmt.Errorf("invalid ip-range %s: parent subnet %s doesn't contain ip-range", ipRange, subnet)) 108 } 109 110 return errs 111 } 112 113 func validateAddress(address string, subnet netip.Prefix, subnetFamily ipFamily) error { 114 if address == "" { 115 return nil 116 } 117 addr, err := netip.ParseAddr(address) 118 if err != nil { 119 return errors.New("invalid address") 120 } 121 family := ip4 122 if addr.Is6() { 123 family = ip6 124 } 125 126 if family != subnetFamily { 127 return fmt.Errorf("parent subnet is an %s block", subnetFamily) 128 } 129 if !subnet.Contains(addr) { 130 return fmt.Errorf("parent subnet %s doesn't contain this address", subnet) 131 } 132 133 return nil 134 }