this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

pkg/net: add InCIDR to check if an IP is within a CIDR range

This function checks whether an IP address is contained within a CIDR
network. The IP can be provided as a string or byte list, and the CIDR
must be in standard notation (e.g., "192.168.1.0/24").

The parameter order (ip, cidr) allows using this as a validator:

ip: net.InCIDR("10.0.0.0/8")

The original proposal was to add IPInCIDR, but that reads oddly to me,
and InCIDR with the first parameter being "ip" is already intuitive.
Moreover, later on we also want an API to check if one CIDR overlaps
with another CIDR, and CIDROverlapsCIDR is an even weirder name.
Wanting to call that API OverlapsCIDR, use InCIDR here.

This helps DFS's inventory module, which needed to roll its own version
of this function via lots of builtin calls like strings.Split,
math/bits.Rsh, net.ParseIP, and other logic like string interpolation.

│ old │ new │
│ B/op │ B/op vs base │
VetInventory 4.565Gi ± ∞ ¹ 4.322Gi ± ∞ ¹ -5.33% (p=1.000 n=1)

│ old │ new │
│ allocs/op │ allocs/op vs base │
VetInventory 47.29M ± ∞ ¹ 43.96M ± ∞ ¹ -7.04% (p=1.000 n=1)

Fixes #3865.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: Iecfe4532d23e9bbaff531eb8ef02a933105da074
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1229759
Reviewed-by: Matthew Sackman <matthew@cue.works>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+117
+15
pkg/net/ip.go
··· 304 304 } 305 305 return netip.PrefixFrom(addr, prefix.Bits()).String(), nil 306 306 } 307 + 308 + // InCIDR reports whether an IP address is contained a CIDR subnet string. 309 + func InCIDR(ip, cidr cue.Value) (bool, error) { 310 + ipAddr := netGetIP(ip) 311 + if !ipAddr.IsValid() { 312 + return false, fmt.Errorf("invalid IP %q", ip) 313 + } 314 + 315 + prefix, err := netGetIPCIDR(cidr) 316 + if err != nil { 317 + return false, err 318 + } 319 + 320 + return prefix.Contains(ipAddr), nil 321 + }
+13
pkg/net/pkg.go
··· 264 264 } 265 265 }, 266 266 }, { 267 + Name: "InCIDR", 268 + Params: []pkg.Param{ 269 + {Kind: adt.TopKind}, 270 + {Kind: adt.TopKind}, 271 + }, 272 + Result: adt.BoolKind, 273 + Func: func(c *pkg.CallCtxt) { 274 + ip, cidr := c.Value(0), c.Value(1) 275 + if c.Do() { 276 + c.Ret, c.Err = InCIDR(ip, cidr) 277 + } 278 + }, 279 + }, { 267 280 Name: "PathEscape", 268 281 Params: []pkg.Param{ 269 282 {Kind: adt.StringKind},
+89
pkg/net/testdata/incidr.txtar
··· 1 + -- in.cue -- 2 + import "net" 3 + 4 + // IPv4 tests 5 + t1: net.InCIDR("192.168.1.5", "192.168.1.0/24") 6 + t2: net.InCIDR("192.168.2.5", "192.168.1.0/24") 7 + t3: net.InCIDR("10.0.0.1", "10.0.0.0/8") 8 + t4: net.InCIDR("11.0.0.1", "10.0.0.0/8") 9 + 10 + // IPv4 with byte list 11 + t5: net.InCIDR([192, 168, 1, 100], "192.168.1.0/24") 12 + t6: net.InCIDR([192, 168, 2, 100], "192.168.1.0/24") 13 + 14 + // IPv6 tests 15 + t7: net.InCIDR("2001:db8::1", "2001:db8::/32") 16 + t8: net.InCIDR("2001:db9::1", "2001:db8::/32") 17 + 18 + // IPv6 with byte list (2001:db8::1 = [32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) 19 + t9: net.InCIDR([32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "2001:db8::/32") 20 + t10: net.InCIDR([32, 1, 13, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "2001:db8::/32") 21 + 22 + // Edge cases 23 + t11: net.InCIDR("192.168.1.0", "192.168.1.0/24") 24 + t12: net.InCIDR("192.168.1.255", "192.168.1.0/24") 25 + t13: net.InCIDR("0.0.0.0", "0.0.0.0/0") 26 + t14: net.InCIDR("255.255.255.255", "0.0.0.0/0") 27 + 28 + // Error cases 29 + t15: net.InCIDR("invalid", "192.168.1.0/24") 30 + t16: net.InCIDR("192.168.1.1", "invalid") 31 + t17: net.InCIDR("192.168.1.1", "192.168.1.0") 32 + t18: net.InCIDR("192.168.1.0/24", "192.168.0.0/16") 33 + 34 + // Validator usage - IP is validated against a CIDR constraint 35 + v1: "192.168.1.50" & net.InCIDR("192.168.1.0/24") 36 + v2: "10.0.0.1" & net.InCIDR("192.168.1.0/24") 37 + v3: [192, 168, 1, 50] & net.InCIDR("192.168.1.0/24") 38 + v4: "2001:db8::1" & net.InCIDR("2001:db8::/32") 39 + -- out/net-v3 -- 40 + Errors: 41 + t15: error in call to net.InCIDR: invalid IP "invalid": 42 + ./in.cue:28:6 43 + t16: error in call to net.InCIDR: netip.ParsePrefix("invalid"): no '/': 44 + ./in.cue:29:6 45 + t17: error in call to net.InCIDR: netip.ParsePrefix("192.168.1.0"): no '/': 46 + ./in.cue:30:6 47 + t18: error in call to net.InCIDR: invalid IP "192.168.1.0/24": 48 + ./in.cue:31:6 49 + v2: invalid value "10.0.0.1" (does not satisfy net.InCIDR("192.168.1.0/24")): 50 + ./in.cue:35:18 51 + ./in.cue:35:5 52 + ./in.cue:35:29 53 + 54 + Result: 55 + // IPv4 tests 56 + t1: true 57 + t2: false 58 + t3: true 59 + t4: false 60 + 61 + // IPv4 with byte list 62 + t5: true 63 + t6: false 64 + 65 + // IPv6 tests 66 + t7: true 67 + t8: false 68 + 69 + // IPv6 with byte list (2001:db8::1 = [32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) 70 + t9: true 71 + t10: false 72 + 73 + // Edge cases 74 + t11: true 75 + t12: true 76 + t13: true 77 + t14: true 78 + 79 + // Error cases 80 + t15: _|_ // t15: error in call to net.InCIDR: invalid IP "invalid" 81 + t16: _|_ // t16: error in call to net.InCIDR: netip.ParsePrefix("invalid"): no '/' 82 + t17: _|_ // t17: error in call to net.InCIDR: netip.ParsePrefix("192.168.1.0"): no '/' 83 + t18: _|_ // t18: error in call to net.InCIDR: invalid IP "192.168.1.0/24" 84 + 85 + // Validator usage - IP is validated against a CIDR constraint 86 + v1: "192.168.1.50" 87 + v2: _|_ // v2: invalid value "10.0.0.1" (does not satisfy net.InCIDR("192.168.1.0/24")) 88 + v3: [192, 168, 1, 50] 89 + v4: "2001:db8::1"