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  }