github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/compiler/eval/cidr.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the Apache License Version 2.0.
     3  // This product includes software developed at Datadog (https://www.datadoghq.com/).
     4  // Copyright 2016-present Datadog, Inc.
     5  
     6  // Package eval holds eval related files
     7  package eval
     8  
     9  import (
    10  	"errors"
    11  	"net"
    12  	"strings"
    13  )
    14  
    15  var (
    16  	// IPV4Mask32 ipv4 ip address
    17  	IPV4Mask32 = net.CIDRMask(32, 8*net.IPv4len)
    18  	// IPV6Mask128 ipv6 ip address
    19  	IPV6Mask128 = net.CIDRMask(128, 8*net.IPv6len)
    20  )
    21  
    22  // IPNetFromIP returns a IPNET version of the IP
    23  func IPNetFromIP(ip net.IP) *net.IPNet {
    24  	var mask = IPV4Mask32
    25  	if len(ip) == net.IPv6len {
    26  		mask = IPV6Mask128
    27  	}
    28  
    29  	return &net.IPNet{
    30  		IP:   ip,
    31  		Mask: mask,
    32  	}
    33  }
    34  
    35  // CIDRValues describes a set of CIDRs
    36  type CIDRValues struct {
    37  	ipnets []*net.IPNet
    38  
    39  	// caches
    40  	fieldValues []FieldValue
    41  
    42  	exists map[string]bool
    43  }
    44  
    45  // AppendCIDR append a CIDR notation
    46  func (c *CIDRValues) AppendCIDR(cidr string) error {
    47  	if c.exists[cidr] {
    48  		return nil
    49  	}
    50  
    51  	_, ipnet, err := net.ParseCIDR(cidr)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	c.ipnets = append(c.ipnets, ipnet)
    57  	c.fieldValues = append(c.fieldValues, FieldValue{Type: IPNetValueType, Value: *ipnet})
    58  
    59  	if c.exists == nil {
    60  		c.exists = make(map[string]bool)
    61  	}
    62  	c.exists[cidr] = true
    63  
    64  	return nil
    65  }
    66  
    67  func isZeros(p net.IP) bool {
    68  	for i := 0; i < len(p); i++ {
    69  		if p[i] != 0 {
    70  			return false
    71  		}
    72  	}
    73  	return true
    74  }
    75  
    76  // ParseCIDR converts an IP/CIDR notation to an IPNet object
    77  func ParseCIDR(ip string) (*net.IPNet, error) {
    78  	var ipnet *net.IPNet
    79  	if !strings.Contains(ip, "/") {
    80  		if ipnet = IPNetFromIP(net.ParseIP(ip)); isZeros(ipnet.IP) {
    81  			return nil, errors.New("unknown IP address format")
    82  		}
    83  	} else {
    84  		var err error
    85  		if _, ipnet, err = net.ParseCIDR(ip); err != nil {
    86  			return nil, err
    87  		}
    88  	}
    89  
    90  	return ipnet, nil
    91  }
    92  
    93  // AppendIP append ip notation
    94  func (c *CIDRValues) AppendIP(ip string) error {
    95  	if c.exists[ip] {
    96  		return nil
    97  	}
    98  
    99  	ipnet, err := ParseCIDR(ip)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	c.ipnets = append(c.ipnets, ipnet)
   105  	c.fieldValues = append(c.fieldValues, FieldValue{Type: IPNetValueType, Value: *ipnet})
   106  
   107  	if c.exists == nil {
   108  		c.exists = make(map[string]bool)
   109  	}
   110  	c.exists[ip] = true
   111  
   112  	return nil
   113  }
   114  
   115  // Contains returns whether the values match the provided IPNet
   116  func (c *CIDRValues) Contains(ipnet *net.IPNet) bool {
   117  	for _, n := range c.ipnets {
   118  		if IPNetsMatch(n, ipnet) {
   119  			return true
   120  		}
   121  	}
   122  
   123  	return false
   124  }
   125  
   126  // Match returns whether the values matches the provided IPNets
   127  func (c *CIDRValues) Match(ipnets []net.IPNet) bool {
   128  	for _, n := range c.ipnets {
   129  		for _, ipnet := range ipnets {
   130  			if IPNetsMatch(n, &ipnet) {
   131  				return true
   132  			}
   133  		}
   134  	}
   135  
   136  	return false
   137  }
   138  
   139  // MatchAll returns whether the values matches all the provided IPNets
   140  func (c *CIDRValues) MatchAll(ipnets []net.IPNet) bool {
   141  	for _, n := range c.ipnets {
   142  		for _, ipnet := range ipnets {
   143  			if !IPNetsMatch(n, &ipnet) {
   144  				return false
   145  			}
   146  		}
   147  	}
   148  
   149  	return true
   150  }
   151  
   152  // IPNetsMatch returns whether the IPNets match
   153  func IPNetsMatch(i1, i2 *net.IPNet) bool {
   154  	return i1.Contains(i2.IP) || i2.Contains(i1.IP)
   155  }