From 42052644ef46950abcd07659e55124691b40e650 Mon Sep 17 00:00:00 2001 From: Lugossy Zoltan Date: Fri, 6 Jan 2023 14:35:31 +0100 Subject: [PATCH] Attractor ipv4/ipv6-prefix also accept IP range Allows limiting the pool of IP addresses to allocate IPs from. -Enables coordination of multiple Attractors when they share the same external network. -Can be also used to exclude reserved IPs of Gateway Routers. Example: ipv4-prefix: 169.254.100.0-169.254.100.100/24 ipv6-prefix: 100:100::0-100:100::100/64;100:100::2000-100:100::ffff/64 --- api/v1/attractor_types.go | 10 ++++- api/v1/attractor_webhook.go | 4 +- api/v1/types.go | 41 +++++++++++++++++++ .../bases/meridio.nordix.org_attractors.yaml | 18 +++++--- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/api/v1/attractor_types.go b/api/v1/attractor_types.go index e3e0eeeb..d6452fd8 100644 --- a/api/v1/attractor_types.go +++ b/api/v1/attractor_types.go @@ -50,12 +50,18 @@ type InterfaceSpec struct { // +kubebuilder:validation:Pattern=`^[^:\//\s]{1,13}$` Name string `json:"name"` - // (immutable) IPv4 prefix of the interface, which is used for frontend to set up communication with the ipv4 gateways. + // (immutable) IPv4 prefix or range of the interface, which is used for frontend to set up communication with the ipv4 gateways. // If the type is "nsm-vlan", this information must be specified. + // For example, '192.168.100.0/24', '192.168.100.1-192.168.100.100/24'. + // Multiple IP ranges can be combined using semicolon delimiter, but they must belong to the same network. + // For example, '192.168.100.3-192.168.100.100/24;192.168.100.200-192.168.100.250/24'. PrefixIPv4 string `json:"ipv4-prefix,omitempty"` - // (immutable) IPv6 prefix of the interface, which is used for frontend to set up communication with the ipv6 gateways. + // (immutable) IPv6 prefix or range of the interface, which is used for frontend to set up communication with the ipv6 gateways. // If the type is "nsm-vlan", this information must be specified. + // For example, '100:100::/64', '100:100::bbbb-100:100::cccc/64'. + // Multiple IP ranges can be combined using semicolon delimiter, but they must belong to the same network. + // For example, '100:100::2-100:100::ffff/64;100:100::a:2-100:100::a:f/64;100:100::e:2-100:100::e:f/64'. PrefixIPv6 string `json:"ipv6-prefix,omitempty"` // Interface choice. diff --git a/api/v1/attractor_webhook.go b/api/v1/attractor_webhook.go index 166e3581..2010779b 100644 --- a/api/v1/attractor_webhook.go +++ b/api/v1/attractor_webhook.go @@ -94,12 +94,12 @@ func (r *Attractor) validateAttractor() error { } switch r.Spec.Interface.Type { case NSMVlan: - _, err := validatePrefix(r.Spec.Interface.PrefixIPv4) + err := validatePrefixAndRange(r.Spec.Interface.PrefixIPv4) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("ipv4-prefix"), r.Spec.Interface.PrefixIPv4, err.Error())) } - _, err = validatePrefix(r.Spec.Interface.PrefixIPv6) + err = validatePrefixAndRange(r.Spec.Interface.PrefixIPv6) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("ipv6-prefix"), r.Spec.Interface.PrefixIPv6, err.Error())) } diff --git a/api/v1/types.go b/api/v1/types.go index 2193eb4c..facf5b07 100644 --- a/api/v1/types.go +++ b/api/v1/types.go @@ -98,6 +98,47 @@ func validatePrefix(p string) (*net.IPNet, error) { return n, nil } +func validatePrefixAndRange(p string) error { + var err error + var network *net.IPNet + composite := strings.Split(p, ";") + for _, c := range composite { + if r := strings.SplitN(c, "-", 2); len(r) == 2 { + // validate IP range + firstip := net.ParseIP(r[0]) + if firstip == nil { + return fmt.Errorf("'%s' is not a valid IP range start", r[0]) + } + _, ipNet, err := net.ParseCIDR(r[1]) + if err != nil { + return fmt.Errorf("'%s' is not a valid CIDR: %s", r[1], err) + } + if !ipNet.Contains(firstip) { + return fmt.Errorf("'%s' is not a valid IP range start for CIDR %s", ipNet.String(), firstip) + } + if network == nil { + network = ipNet + } else { + if !network.IP.Equal(ipNet.IP) { + return fmt.Errorf("network mismatch: '%s' != '%s'", network, ipNet) + } + netOnes, netBits := network.Mask.Size() + ones, bits := ipNet.Mask.Size() + if netOnes != ones || netBits != bits { + return fmt.Errorf("network mask mismatch: %v != %v", network.Mask, ipNet.Mask) + } + } + } else { + // validate prefix + if len(composite) > 1 { + return fmt.Errorf("%s composite subnet config is invalid", composite) + } + _, err = validatePrefix(c) + } + } + return err +} + type InterfaceType string const ( diff --git a/config/crd/bases/meridio.nordix.org_attractors.yaml b/config/crd/bases/meridio.nordix.org_attractors.yaml index 3d64c85a..1d2a1dc9 100644 --- a/config/crd/bases/meridio.nordix.org_attractors.yaml +++ b/config/crd/bases/meridio.nordix.org_attractors.yaml @@ -74,14 +74,20 @@ spec: description: defines the interface information that attractor use properties: ipv4-prefix: - description: (immutable) IPv4 prefix of the interface, which is - used for frontend to set up communication with the ipv4 gateways. - If the type is "nsm-vlan", this information must be specified. + description: (immutable) IPv4 prefix or range of the interface, + which is used for frontend to set up communication with the + ipv4 gateways. If the type is "nsm-vlan", this information must + be specified. For example, '192.168.100.0/24', '192.168.100.1-192.168.100.100/24'. + Multiple IP ranges can be combined using semicolon delimiter, + but they must belong to the same network. For example, '192.168.100.3-192.168.100.100/24;192.168.100.200-192.168.100.250/24'. type: string ipv6-prefix: - description: (immutable) IPv6 prefix of the interface, which is - used for frontend to set up communication with the ipv6 gateways. - If the type is "nsm-vlan", this information must be specified. + description: (immutable) IPv6 prefix or range of the interface, + which is used for frontend to set up communication with the + ipv6 gateways. If the type is "nsm-vlan", this information must + be specified. For example, '100:100::/64', '100:100::bbbb-100:100::cccc/64'. + Multiple IP ranges can be combined using semicolon delimiter, + but they must belong to the same network. For example, '100:100::2-100:100::ffff/64;100:100::a:2-100:100::a:f/64;100:100::e:2-100:100::e:f/64'. type: string name: description: Name of the interface. Must be a valid Linux kernel