commit ae8018ba6e6544d0b47696a00dfc594ae6c65d6f
parent 4e8aab89270a5bbfa3c3d4e300dee5c798efb2fe
Author: Miccah Castorina <contact@miccah.io>
Date: Sun, 12 Dec 2021 21:10:37 -0600
net::ip: implement subnet_contains
Signed-off-by: Miccah Castorina <contact@miccah.io>
Diffstat:
2 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/net/ip/+test.ha b/net/ip/+test.ha
@@ -84,3 +84,33 @@ fn subnet_test_simple(s: str) void = {
subnet_test_simple(subnet_tests[i]);
};
};
+
+@test fn test_subnet_contains() void = {
+ let addr_tests: [](str, str, bool) = [
+ // a, b, want
+ ("10.10.10.0/24", "10.10.10.0", true),
+ ("10.10.10.0/24", "10.10.10.255", true),
+ ("10.10.10.0/24", "10.10.11.0", false),
+ ("127.0.0.1/24", "::1", false),
+ ("::1/8", "::1", true),
+ ];
+ let cidr_tests: [](str, str, bool) = [
+ // a, b, want
+ ("10.10.10.0/24", "10.10.10.0/24", true),
+ ("10.10.10.0/24", "10.10.10.0/25", true),
+ ("10.10.10.0/24", "10.10.10.0/23", false),
+ ("10.10.10.0/24", "10.10.11.0/24", false),
+ ];
+ for (let i = 0z; i < len(addr_tests); i += 1) {
+ const input = addr_tests[i];
+ let a = parsecidr(input.0)!;
+ let b = parse(input.1)!;
+ assert(subnet_contains(a, b) == input.2);
+ };
+ for (let i = 0z; i < len(cidr_tests); i += 1) {
+ const input = cidr_tests[i];
+ let a = parsecidr(input.0)!;
+ let b = parsecidr(input.1)!;
+ assert(subnet_contains(a, b) == input.2);
+ };
+};
diff --git a/net/ip/ip.ha b/net/ip/ip.ha
@@ -350,3 +350,44 @@ fn wanttoken(tok: *strings::tokenizer) (str | invalid) = {
return invalid;
};
};
+
+// Returns whether an [[addr]] (or another [[subnet]]) is contained
+// within a [[subnet]].
+export fn subnet_contains(sub: subnet, item: (addr | subnet)) bool = {
+ let addr: subnet = match (item) {
+ case let addr: addr =>
+ yield subnet{
+ addr = addr,
+ mask = sub.mask,
+ };
+ case let sub: subnet =>
+ yield sub;
+ };
+ // Get byte slices for both addresses and masks.
+ let ipa = match (sub.addr) {
+ case let v4: addr4 => yield v4[..];
+ case let v6: addr6 => yield v6[..];
+ };
+ let maska = match (sub.mask) {
+ case let v4: addr4 => yield v4[..];
+ case let v6: addr6 => yield v6[..];
+ };
+ let ipb = match (addr.addr) {
+ case let v4: addr4 => yield v4[..];
+ case let v6: addr6 => yield v6[..];
+ };
+ let maskb = match (addr.mask) {
+ case let v4: addr4 => yield v4[..];
+ case let v6: addr6 => yield v6[..];
+ };
+ if (len(ipa) != len(ipb) || len(maska) != len(maskb) || len(ipa) != len(maska)) {
+ // Mismatched addr4 and addr6 addresses / masks.
+ return false;
+ };
+ for (let i = 0z; i < len(ipa); i += 1) {
+ if (ipa[i] & maska[i] != ipb[i] & maska[i] || maska[i] > maskb[i]) {
+ return false;
+ };
+ };
+ return true;
+};