From 04020861091b3ce08ac0caa081007608bcad7bb1 Mon Sep 17 00:00:00 2001 From: Lugossy Zoltan Date: Thu, 17 Nov 2022 21:56:31 +0100 Subject: [PATCH] Attractor ipv4/ipv6-prefix also accept IP range Example: ipv4-prefix: 169.254.100.0-169.254.100.100/24 ipv6-prefix: 100:100::0-100:100::100/64;100:100::a:0-100:100::a:aaa9/64 --- api/v1alpha1/attractor_types.go | 10 ++++- api/v1alpha1/attractor_webhook.go | 4 +- api/v1alpha1/types.go | 41 +++++++++++++++++++ .../bases/meridio.nordix.org_attractors.yaml | 14 +++++-- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/api/v1alpha1/attractor_types.go b/api/v1alpha1/attractor_types.go index 99b79c11..52482b90 100644 --- a/api/v1alpha1/attractor_types.go +++ b/api/v1alpha1/attractor_types.go @@ -48,10 +48,16 @@ type InterfaceSpec struct { // name of the interface 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 + // 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"` - // (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 + // 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"` // interface choice. diff --git a/api/v1alpha1/attractor_webhook.go b/api/v1alpha1/attractor_webhook.go index ea88597b..cb023b95 100644 --- a/api/v1alpha1/attractor_webhook.go +++ b/api/v1alpha1/attractor_webhook.go @@ -92,12 +92,12 @@ func (r *Attractor) validateAttractor() error { if err := r.validateLabels(); err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("labels"), r.ObjectMeta.Labels, err.Error())) } - _, 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/v1alpha1/types.go b/api/v1alpha1/types.go index 3c395d62..ff0d68f8 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/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 3948a411..410c5828 100644 --- a/config/crd/bases/meridio.nordix.org_attractors.yaml +++ b/config/crd/bases/meridio.nordix.org_attractors.yaml @@ -74,12 +74,18 @@ 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 + description: (immutable) ipv4 prefix or range of the interface, + which is used for frontend to set up communication with the + ipv4 gateways 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 + description: (immutable) ipv6 prefix or range of the interface, + which is used for frontend to set up communication with the + ipv6 gateways 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