]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
in-addr-prefix-util: introduce several utilities for address prefix
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 14 Sep 2021 05:10:33 +0000 (14:10 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 14 Sep 2021 11:30:09 +0000 (20:30 +0900)
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp6.c
src/shared/in-addr-prefix-util.c [new file with mode: 0644]
src/shared/in-addr-prefix-util.h [new file with mode: 0644]
src/shared/meson.build

index 2187bd0cbacf2522ea78cfa28ff144f5a5b687ef..3d392afc082f007ec721708408bd653e315cd142 100644 (file)
@@ -831,35 +831,6 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
 
 DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
 
-static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
-        assert(a);
-        assert(state);
-
-        siphash24_compress(&a->family, sizeof(a->family), state);
-        siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
-        siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
-}
-
-static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
-        int r;
-
-        assert(x);
-        assert(y);
-
-        r = CMP(x->family, y->family);
-        if (r != 0)
-                return r;
-
-        r = CMP(x->prefixlen, y->prefixlen);
-        if (r != 0)
-                return r;
-
-        return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
-}
-
-DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
-
 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
         assert(addr);
         assert(state);
index f3ead91962315bb586809d6646e309aff27a1ceb..c74b0d512b480ef05ec6e3759956fcc6e4d8fea4 100644 (file)
@@ -20,12 +20,6 @@ struct in_addr_data {
         union in_addr_union address;
 };
 
-struct in_addr_prefix {
-        int family;
-        uint8_t prefixlen;
-        union in_addr_union address;
-};
-
 bool in4_addr_is_null(const struct in_addr *a);
 static inline bool in4_addr_is_set(const struct in_addr *a) {
         return !in4_addr_is_null(a);
@@ -124,8 +118,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
 int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
 
 extern const struct hash_ops in_addr_data_hash_ops;
-extern const struct hash_ops in_addr_prefix_hash_ops;
-extern const struct hash_ops in_addr_prefix_hash_ops_free;
 extern const struct hash_ops in6_addr_hash_ops;
 
 #define IPV4_ADDRESS_FMT_STR     "%u.%u.%u.%u"
index 8138c4eda7d250f410ef797615c5f41d24c3c3db..1a4ccb5e2c8abadab65bdadab3c14308a8b1fce6 100644 (file)
@@ -9,7 +9,7 @@
 #include "dhcp6-internal.h"
 #include "escape.h"
 #include "hexdecoct.h"
-#include "in-addr-util.h"
+#include "in-addr-prefix-util.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
index 824047fcc1dde6711a401536caab9c2913617772..0a7471f0e8e83a3483a38720f53d86dd7c90314e 100644 (file)
@@ -12,6 +12,7 @@
 #include "hashmap.h"
 #include "hostname-setup.h"
 #include "hostname-util.h"
+#include "in-addr-prefix-util.h"
 #include "missing_network.h"
 #include "networkd-address.h"
 #include "networkd-dhcp6.h"
diff --git a/src/shared/in-addr-prefix-util.c b/src/shared/in-addr-prefix-util.c
new file mode 100644 (file)
index 0000000..f907949
--- /dev/null
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "hostname-util.h"
+#include "in-addr-prefix-util.h"
+#include "string-util.h"
+
+/* 0.0.0.0/0 */
+#define IN_ADDR_PREFIX_IPV4_ANY ((struct in_addr_prefix) { .family = AF_INET })
+/* ::/0 */
+#define IN_ADDR_PREFIX_IPV6_ANY ((struct in_addr_prefix) { .family = AF_INET6 })
+/* 127.0.0.0/8 */
+#define IN_ADDR_PREFIX_IPV4_LOCALHOST                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET,                                      \
+                .address.in.s_addr = htobe32(UINT32_C(127) << 24),      \
+                .prefixlen = 8,                                         \
+        })
+/* ::1/128 */
+#define IN_ADDR_PREFIX_IPV6_LOCALHOST                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET6,                                     \
+                .address.in6 = IN6ADDR_LOOPBACK_INIT,                   \
+                .prefixlen = 128,                                       \
+        })
+/* 169.254.0.0/16 */
+#define IN_ADDR_PREFIX_IPV4_LINKLOCAL                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET,                                      \
+                .address.in.s_addr = htobe32((UINT32_C(169) << 24) |    \
+                                             (UINT32_C(254) << 16)),    \
+                .prefixlen = 16,                                        \
+        })
+/* fe80::/64 */
+#define IN_ADDR_PREFIX_IPV6_LINKLOCAL                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET6,                                     \
+                .address.in6.s6_addr[0] = 0xfe,                         \
+                .address.in6.s6_addr[1] = 0x80,                         \
+                .prefixlen = 64,                                        \
+        })
+/* 224.0.0.0/4 */
+#define IN_ADDR_PREFIX_IPV4_MULTICAST                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET,                                      \
+                .address.in.s_addr = htobe32((UINT32_C(224) << 24)),    \
+                .prefixlen = 4,                                         \
+        })
+/* ff00::/8 */
+#define IN_ADDR_PREFIX_IPV6_MULTICAST                                   \
+        ((struct in_addr_prefix) {                                      \
+                .family = AF_INET6,                                     \
+                .address.in6.s6_addr[0] = 0xff,                         \
+                .prefixlen = 8,                                         \
+        })
+
+static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
+        assert(a);
+        assert(state);
+
+        siphash24_compress(&a->family, sizeof(a->family), state);
+        siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
+        siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+}
+
+static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
+        int r;
+
+        assert(x);
+        assert(y);
+
+        r = CMP(x->family, y->family);
+        if (r != 0)
+                return r;
+
+        r = CMP(x->prefixlen, y->prefixlen);
+        if (r != 0)
+                return r;
+
+        return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
+}
+
+DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
+
+int in_addr_prefix_add(Set **prefixes, const struct in_addr_prefix *prefix) {
+        struct in_addr_prefix *copy;
+
+        assert(prefixes);
+        assert(prefix);
+        assert(IN_SET(prefix->family, AF_INET, AF_INET6));
+
+        copy = newdup(struct in_addr_prefix, prefix, 1);
+        if (!copy)
+                return -ENOMEM;
+
+        (void) in_addr_mask(copy->family, &copy->address, copy->prefixlen);
+        return set_ensure_consume(prefixes, &in_addr_prefix_hash_ops_free, copy);
+}
+
+int in_addr_prefixes_reduce(Set *prefixes) {
+        uint32_t ipv4_prefixlen_bits = 0;
+        uint64_t ipv6_prefixlen_bits[128 / sizeof(uint64_t)] = {};
+        uint8_t ipv4_prefixlens[32] = {}, ipv6_prefixlens[128] = {};
+        bool ipv4_has_any = false, ipv6_has_any = false;
+        size_t ipv4_n_prefixlens = 0, ipv6_n_prefixlens = 0;
+        struct in_addr_prefix *p;
+
+        SET_FOREACH(p, prefixes)
+                switch(p->family) {
+                case AF_INET:
+                        assert(p->prefixlen <= 32);
+                        if (p->prefixlen == 0)
+                                ipv4_has_any = true;
+                        else
+                                ipv4_prefixlen_bits |= UINT32_C(1) << (p->prefixlen - 1);
+                        break;
+                case AF_INET6:
+                        assert(p->prefixlen <= 128);
+                        if (p->prefixlen == 0)
+                                ipv6_has_any = true;
+                        else
+                                ipv6_prefixlen_bits[(p->prefixlen - 1) / sizeof(uint64_t)] |=
+                                        UINT64_C(1) << ((p->prefixlen - 1) % sizeof(uint64_t));
+                        break;
+                default:
+                        assert_not_reached();
+                }
+
+        if (!ipv4_has_any)
+                for (size_t i = 0; i < 32; i++)
+                        if (ipv4_prefixlen_bits & (UINT32_C(1) << i))
+                                ipv4_prefixlens[ipv4_n_prefixlens++] = i + 1;
+
+        if (!ipv6_has_any)
+                for (size_t i = 0; i < 128; i++)
+                        if (ipv6_prefixlen_bits[i / sizeof(uint64_t)] &
+                            (UINT64_C(1) << (i % sizeof(uint64_t))))
+                                ipv6_prefixlens[ipv6_n_prefixlens++] = i + 1;
+
+        SET_FOREACH(p, prefixes) {
+                uint8_t *prefixlens;
+                bool covered;
+                size_t *n;
+
+                if (p->prefixlen == 0)
+                        continue;
+
+                switch(p->family) {
+                case AF_INET:
+                        prefixlens = ipv4_prefixlens;
+                        n = &ipv4_n_prefixlens;
+                        covered = ipv4_has_any;
+                        break;
+                case AF_INET6:
+                        prefixlens = ipv6_prefixlens;
+                        n = &ipv6_n_prefixlens;
+                        covered = ipv6_has_any;
+                        break;
+                default:
+                        assert_not_reached();
+                }
+
+                for (size_t i = 0; i < *n; i++) {
+                        struct in_addr_prefix tmp;
+
+                        if (covered)
+                                break;
+
+                        if (prefixlens[i] >= p->prefixlen)
+                                break;
+
+                        tmp = *p;
+                        tmp.prefixlen = prefixlens[i];
+                        (void) in_addr_mask(tmp.family, &tmp.address, tmp.prefixlen);
+
+                        covered = set_contains(prefixes, &tmp);
+                }
+
+                if (covered)
+                        free(set_remove(prefixes, p));
+        }
+
+        return 0;
+}
+
+int in_addr_prefixes_merge(Set **dest, Set *src) {
+        struct in_addr_prefix *p;
+        int r;
+
+        assert(dest);
+
+        SET_FOREACH(p, src) {
+                r = in_addr_prefix_add(dest, p);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+bool in_addr_prefixes_is_any(Set *prefixes) {
+        return
+                set_contains(prefixes, &IN_ADDR_PREFIX_IPV4_ANY) &&
+                set_contains(prefixes, &IN_ADDR_PREFIX_IPV6_ANY);
+}
+
+int config_parse_in_addr_prefixes(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Set **prefixes = data;
+        int r;
+
+        assert(prefixes);
+        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
+
+        if (isempty(rvalue)) {
+                *prefixes = set_free(*prefixes);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&p, &word, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                if (streq(word, "any")) {
+                        /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
+
+                        if (ltype != AF_INET6) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_ANY);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        if (ltype != AF_INET) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_ANY);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                } else if (is_localhost(word)) {
+                        /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
+
+                        if (ltype != AF_INET6) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LOCALHOST);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        if (ltype != AF_INET) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LOCALHOST);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                } else if (streq(word, "link-local")) {
+                        /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
+
+                        if (ltype != AF_INET6) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LINKLOCAL);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        if (ltype != AF_INET) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LINKLOCAL);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                } else if (streq(word, "multicast")) {
+                        /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
+
+                        if (ltype != AF_INET6) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_MULTICAST);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        if (ltype != AF_INET) {
+                                r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_MULTICAST);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                } else {
+                        struct in_addr_prefix a;
+
+                        if (ltype == AF_UNSPEC)
+                                r = in_addr_prefix_from_string_auto(word, &a.family, &a.address, &a.prefixlen);
+                        else {
+                                a.family = ltype;
+                                r = in_addr_prefix_from_string(word, a.family, &a.address, &a.prefixlen);
+                        }
+                        if (r < 0) {
+                                log_syntax(unit, LOG_WARNING, filename, line, r,
+                                           "Address prefix is invalid, ignoring assignment: %s", word);
+                                continue;
+                        }
+
+                        r = in_addr_prefix_add(prefixes, &a);
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+}
diff --git a/src/shared/in-addr-prefix-util.h b/src/shared/in-addr-prefix-util.h
new file mode 100644 (file)
index 0000000..53aaad3
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "set.h"
+
+struct in_addr_prefix {
+        int family;
+        uint8_t prefixlen;
+        union in_addr_union address;
+};
+
+int in_addr_prefix_add(Set **prefixes, const struct in_addr_prefix *prefix);
+int in_addr_prefixes_reduce(Set *prefixes);
+int in_addr_prefixes_merge(Set **dest, Set *src);
+/* Returns true if a set contains the two items necessary for "any" (0.0.0.0/0 and ::/0). */
+bool in_addr_prefixes_is_any(Set *prefixes);
+
+extern const struct hash_ops in_addr_prefix_hash_ops;
+extern const struct hash_ops in_addr_prefix_hash_ops_free;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_prefixes);
index 9d03c9a214e788e26bea53d0c3cedee2f38ec9fe..6e09ef84795ffda5c9821c94245d3e155d0f55d1 100644 (file)
@@ -151,6 +151,8 @@ shared_sources = files('''
         ima-util.h
         import-util.c
         import-util.h
+        in-addr-prefix-util.c
+        in-addr-prefix-util.h
         initreq.h
         install-file.c
         install-file.h