github.com/netdata/go.d.plugin@v0.58.1/pkg/iprange/parse.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package iprange 4 5 import ( 6 "bytes" 7 "fmt" 8 "net" 9 "regexp" 10 "strings" 11 12 "github.com/apparentlymart/go-cidr/cidr" 13 ) 14 15 // ParseRanges parses s as a space separated list of IP Ranges, returning the result and an error if any. 16 // IP Range can be in IPv4 address ("192.0.2.1"), IPv4 range ("192.0.2.0-192.0.2.10") 17 // IPv4 CIDR ("192.0.2.0/24"), IPv4 subnet mask ("192.0.2.0/255.255.255.0"), 18 // IPv6 address ("2001:db8::1"), IPv6 range ("2001:db8::-2001:db8::10"), 19 // or IPv6 CIDR ("2001:db8::/64") form. 20 // IPv4 CIDR, IPv4 subnet mask and IPv6 CIDR ranges don't include network and broadcast addresses. 21 func ParseRanges(s string) ([]Range, error) { 22 parts := strings.Fields(s) 23 if len(parts) == 0 { 24 return nil, nil 25 } 26 27 var ranges []Range 28 for _, v := range parts { 29 r, err := ParseRange(v) 30 if err != nil { 31 return nil, err 32 } 33 34 if r != nil { 35 ranges = append(ranges, r) 36 } 37 } 38 return ranges, nil 39 } 40 41 var ( 42 reRange = regexp.MustCompile("^[0-9a-f.:-]+$") // addr | addr-addr 43 reCIDR = regexp.MustCompile("^[0-9a-f.:]+/[0-9]{1,3}$") // addr/prefix_length 44 reSubnetMask = regexp.MustCompile("^[0-9.]+/[0-9.]{7,}$") // v4_addr/mask 45 ) 46 47 // ParseRange parses s as an IP Range, returning the result and an error if any. 48 // The string s can be in IPv4 address ("192.0.2.1"), IPv4 range ("192.0.2.0-192.0.2.10") 49 // IPv4 CIDR ("192.0.2.0/24"), IPv4 subnet mask ("192.0.2.0/255.255.255.0"), 50 // IPv6 address ("2001:db8::1"), IPv6 range ("2001:db8::-2001:db8::10"), 51 // or IPv6 CIDR ("2001:db8::/64") form. 52 // IPv4 CIDR, IPv4 subnet mask and IPv6 CIDR ranges don't include network and broadcast addresses. 53 func ParseRange(s string) (Range, error) { 54 s = strings.ToLower(s) 55 if s == "" { 56 return nil, nil 57 } 58 59 var r Range 60 switch { 61 case reRange.MatchString(s): 62 r = parseRange(s) 63 case reCIDR.MatchString(s): 64 r = parseCIDR(s) 65 case reSubnetMask.MatchString(s): 66 r = parseSubnetMask(s) 67 } 68 69 if r == nil { 70 return nil, fmt.Errorf("ip range (%s) invalid syntax", s) 71 } 72 return r, nil 73 } 74 75 func parseRange(s string) Range { 76 var start, end net.IP 77 if idx := strings.IndexByte(s, '-'); idx != -1 { 78 start, end = net.ParseIP(s[:idx]), net.ParseIP(s[idx+1:]) 79 } else { 80 start, end = net.ParseIP(s), net.ParseIP(s) 81 } 82 83 return New(start, end) 84 } 85 86 func parseCIDR(s string) Range { 87 ip, network, err := net.ParseCIDR(s) 88 if err != nil { 89 return nil 90 } 91 92 start, end := cidr.AddressRange(network) 93 prefixLen, _ := network.Mask.Size() 94 95 if isV4IP(ip) && prefixLen < 31 || isV6IP(ip) && prefixLen < 127 { 96 start = cidr.Inc(start) 97 end = cidr.Dec(end) 98 } 99 100 return parseRange(fmt.Sprintf("%s-%s", start, end)) 101 } 102 103 func parseSubnetMask(s string) Range { 104 idx := strings.LastIndexByte(s, '/') 105 if idx == -1 { 106 return nil 107 } 108 109 address, mask := s[:idx], s[idx+1:] 110 111 ip := net.ParseIP(mask).To4() 112 if ip == nil { 113 return nil 114 } 115 116 prefixLen, bits := net.IPv4Mask(ip[0], ip[1], ip[2], ip[3]).Size() 117 if prefixLen+bits == 0 { 118 return nil 119 } 120 121 return parseCIDR(fmt.Sprintf("%s/%d", address, prefixLen)) 122 } 123 124 func isV4RangeValid(start, end net.IP) bool { 125 return isV4IP(start) && isV4IP(end) && bytes.Compare(end, start) >= 0 126 } 127 128 func isV6RangeValid(start, end net.IP) bool { 129 return isV6IP(start) && isV6IP(end) && bytes.Compare(end, start) >= 0 130 } 131 132 func isV4IP(ip net.IP) bool { 133 return ip.To4() != nil 134 } 135 136 func isV6IP(ip net.IP) bool { 137 return !isV4IP(ip) && ip.To16() != nil 138 }