summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/pkg/iprange/parse.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/pkg/iprange/parse.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/pkg/iprange/parse.go b/src/go/collectors/go.d.plugin/pkg/iprange/parse.go
new file mode 100644
index 000000000..3471702a1
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/pkg/iprange/parse.go
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package iprange
+
+import (
+ "bytes"
+ "fmt"
+ "net"
+ "regexp"
+ "strings"
+
+ "github.com/apparentlymart/go-cidr/cidr"
+)
+
+// ParseRanges parses s as a space separated list of IP Ranges, returning the result and an error if any.
+// IP Range can be in IPv4 address ("192.0.2.1"), IPv4 range ("192.0.2.0-192.0.2.10")
+// IPv4 CIDR ("192.0.2.0/24"), IPv4 subnet mask ("192.0.2.0/255.255.255.0"),
+// IPv6 address ("2001:db8::1"), IPv6 range ("2001:db8::-2001:db8::10"),
+// or IPv6 CIDR ("2001:db8::/64") form.
+// IPv4 CIDR, IPv4 subnet mask and IPv6 CIDR ranges don't include network and broadcast addresses.
+func ParseRanges(s string) ([]Range, error) {
+ parts := strings.Fields(s)
+ if len(parts) == 0 {
+ return nil, nil
+ }
+
+ var ranges []Range
+ for _, v := range parts {
+ r, err := ParseRange(v)
+ if err != nil {
+ return nil, err
+ }
+
+ if r != nil {
+ ranges = append(ranges, r)
+ }
+ }
+ return ranges, nil
+}
+
+var (
+ reRange = regexp.MustCompile("^[0-9a-f.:-]+$") // addr | addr-addr
+ reCIDR = regexp.MustCompile("^[0-9a-f.:]+/[0-9]{1,3}$") // addr/prefix_length
+ reSubnetMask = regexp.MustCompile("^[0-9.]+/[0-9.]{7,}$") // v4_addr/mask
+)
+
+// ParseRange parses s as an IP Range, returning the result and an error if any.
+// The string s can be in IPv4 address ("192.0.2.1"), IPv4 range ("192.0.2.0-192.0.2.10")
+// IPv4 CIDR ("192.0.2.0/24"), IPv4 subnet mask ("192.0.2.0/255.255.255.0"),
+// IPv6 address ("2001:db8::1"), IPv6 range ("2001:db8::-2001:db8::10"),
+// or IPv6 CIDR ("2001:db8::/64") form.
+// IPv4 CIDR, IPv4 subnet mask and IPv6 CIDR ranges don't include network and broadcast addresses.
+func ParseRange(s string) (Range, error) {
+ s = strings.ToLower(s)
+ if s == "" {
+ return nil, nil
+ }
+
+ var r Range
+ switch {
+ case reRange.MatchString(s):
+ r = parseRange(s)
+ case reCIDR.MatchString(s):
+ r = parseCIDR(s)
+ case reSubnetMask.MatchString(s):
+ r = parseSubnetMask(s)
+ }
+
+ if r == nil {
+ return nil, fmt.Errorf("ip range (%s) invalid syntax", s)
+ }
+ return r, nil
+}
+
+func parseRange(s string) Range {
+ var start, end net.IP
+ if idx := strings.IndexByte(s, '-'); idx != -1 {
+ start, end = net.ParseIP(s[:idx]), net.ParseIP(s[idx+1:])
+ } else {
+ start, end = net.ParseIP(s), net.ParseIP(s)
+ }
+
+ return New(start, end)
+}
+
+func parseCIDR(s string) Range {
+ ip, network, err := net.ParseCIDR(s)
+ if err != nil {
+ return nil
+ }
+
+ start, end := cidr.AddressRange(network)
+ prefixLen, _ := network.Mask.Size()
+
+ if isV4IP(ip) && prefixLen < 31 || isV6IP(ip) && prefixLen < 127 {
+ start = cidr.Inc(start)
+ end = cidr.Dec(end)
+ }
+
+ return parseRange(fmt.Sprintf("%s-%s", start, end))
+}
+
+func parseSubnetMask(s string) Range {
+ idx := strings.LastIndexByte(s, '/')
+ if idx == -1 {
+ return nil
+ }
+
+ address, mask := s[:idx], s[idx+1:]
+
+ ip := net.ParseIP(mask).To4()
+ if ip == nil {
+ return nil
+ }
+
+ prefixLen, bits := net.IPv4Mask(ip[0], ip[1], ip[2], ip[3]).Size()
+ if prefixLen+bits == 0 {
+ return nil
+ }
+
+ return parseCIDR(fmt.Sprintf("%s/%d", address, prefixLen))
+}
+
+func isV4RangeValid(start, end net.IP) bool {
+ return isV4IP(start) && isV4IP(end) && bytes.Compare(end, start) >= 0
+}
+
+func isV6RangeValid(start, end net.IP) bool {
+ return isV6IP(start) && isV6IP(end) && bytes.Compare(end, start) >= 0
+}
+
+func isV4IP(ip net.IP) bool {
+ return ip.To4() != nil
+}
+
+func isV6IP(ip net.IP) bool {
+ return !isV4IP(ip) && ip.To16() != nil
+}