]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic: generate netmasks for IPv6 and generic IP family addresses
authorTopi Miettinen <toiwoton@gmail.com>
Sat, 2 Jul 2022 14:15:42 +0000 (17:15 +0300)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 29 Aug 2022 05:23:17 +0000 (14:23 +0900)
Added functions to generate netmasks for IPv6 and generic IP family addresses.

src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/test/test-in-addr-util.c

index fe356c6d15c169875d085767a55330480bdb1f47..cefe3f7661bd6407e51b73f919b5df14accfaff5 100644 (file)
@@ -586,6 +586,7 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
         return 32U - u32ctz(be32toh(addr->s_addr));
 }
 
+/* Calculate an IPv4 netmask from prefix length, for example /8 -> 255.0.0.0. */
 struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
         assert(addr);
         assert(prefixlen <= 32);
@@ -599,6 +600,47 @@ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned cha
         return addr;
 }
 
+/* Calculate an IPv6 netmask from prefix length, for example /16 -> ffff::. */
+struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen) {
+        assert(addr);
+        assert(prefixlen <= 128);
+
+        for (unsigned i = 0; i < 16; i++) {
+                uint8_t mask;
+
+                if (prefixlen >= 8) {
+                        mask = 0xFF;
+                        prefixlen -= 8;
+                } else if (prefixlen > 0) {
+                        mask = 0xFF << (8 - prefixlen);
+                        prefixlen = 0;
+                } else {
+                        assert(prefixlen == 0);
+                        mask = 0;
+                }
+
+                addr->s6_addr[i] = mask;
+        }
+
+        return addr;
+}
+
+/* Calculate an IPv4 or IPv6 netmask from prefix length, for example /8 -> 255.0.0.0 or /16 -> ffff::. */
+int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen) {
+        assert(addr);
+
+        switch (family) {
+        case AF_INET:
+                in4_addr_prefixlen_to_netmask(&addr->in, prefixlen);
+                return 0;
+        case AF_INET6:
+                in6_addr_prefixlen_to_netmask(&addr->in6, prefixlen);
+                return 0;
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
 int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
         uint8_t msb_octet = *(uint8_t*) addr;
 
index fbc60436c799162a098bb5a5ca93ccfb3ddf7d6c..19fa35f1d28b8308e639aacfb181f3f8d0c805f5 100644 (file)
@@ -138,6 +138,8 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
 
 unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
 struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
+struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen);
+int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen);
 int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
 int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
 int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
index f5dcad65d5449ccecd95ae785d937c2bbf89251f..31d767e41d423360490a7d0ce1dc191e609caae6 100644 (file)
@@ -364,4 +364,56 @@ TEST(in_addr_to_string) {
         test_in_addr_to_string_one(AF_INET6, "fe80::");
 }
 
+TEST(in_addr_prefixlen_to_netmask) {
+        union in_addr_union addr;
+        static const char *const ipv4_netmasks[] = {
+                "0.0.0.0", "128.0.0.0", "192.0.0.0", "224.0.0.0", "240.0.0.0",
+                "248.0.0.0", "252.0.0.0", "254.0.0.0", "255.0.0.0",
+                "255.128.0.0", "255.192.0.0", "255.224.0.0", "255.240.0.0",
+                "255.248.0.0", "255.252.0.0", "255.254.0.0", "255.255.0.0",
+                "255.255.128.0", "255.255.192.0", "255.255.224.0", "255.255.240.0",
+                "255.255.248.0", "255.255.252.0", "255.255.254.0", "255.255.255.0",
+                "255.255.255.128", "255.255.255.192", "255.255.255.224", "255.255.255.240",
+                "255.255.255.248", "255.255.255.252", "255.255.255.254", "255.255.255.255",
+        };
+
+        static const char *const ipv6_netmasks[] = {
+                [0]   = "::",
+                [1]   = "8000::",
+                [2]   = "c000::",
+                [7]   = "fe00::",
+                [8]   = "ff00::",
+                [9]   = "ff80::",
+                [16]  = "ffff::",
+                [17]  = "ffff:8000::",
+                [32]  = "ffff:ffff::",
+                [33]  = "ffff:ffff:8000::",
+                [64]  = "ffff:ffff:ffff:ffff::",
+                [65]  = "ffff:ffff:ffff:ffff:8000::",
+                [96]  = "ffff:ffff:ffff:ffff:ffff:ffff::",
+                [97]  = "ffff:ffff:ffff:ffff:ffff:ffff:8000:0",
+                [127] = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
+                [128] = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+        };
+
+        for (unsigned char prefixlen = 0; prefixlen <= 32; prefixlen++) {
+                _cleanup_free_ char *result = NULL;
+
+                assert_se(in_addr_prefixlen_to_netmask(AF_INET, &addr, prefixlen) >= 0);
+                assert_se(in_addr_to_string(AF_INET, &addr, &result) >= 0);
+                printf("test_in_addr_prefixlen_to_netmask: %s == %s\n", ipv4_netmasks[prefixlen], result);
+                assert_se(streq(ipv4_netmasks[prefixlen], result));
+        }
+
+        for (unsigned char prefixlen = 0; prefixlen <= 128; prefixlen++) {
+                _cleanup_free_ char *result = NULL;
+
+                assert_se(in_addr_prefixlen_to_netmask(AF_INET6, &addr, prefixlen) >= 0);
+                assert_se(in_addr_to_string(AF_INET6, &addr, &result) >= 0);
+                printf("test_in_addr_prefixlen_to_netmask: %s\n", result);
+                if (ipv6_netmasks[prefixlen])
+                        assert_se(streq(ipv6_netmasks[prefixlen], result));
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);