From: Yu Watanabe Date: Tue, 14 Sep 2021 05:10:33 +0000 (+0900) Subject: in-addr-prefix-util: introduce several utilities for address prefix X-Git-Tag: v250-rc1~682^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bffaa49ec42ddc7560aff2981266216ab7ce0b20;p=thirdparty%2Fsystemd.git in-addr-prefix-util: introduce several utilities for address prefix --- diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 2187bd0cbac..3d392afc082 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -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); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index f3ead919623..c74b0d512b4 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -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" diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 8138c4eda7d..1a4ccb5e2c8 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -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" diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 824047fcc1d..0a7471f0e8e 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -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 index 00000000000..f907949c220 --- /dev/null +++ b/src/shared/in-addr-prefix-util.c @@ -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, ©->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 index 00000000000..53aaad356cd --- /dev/null +++ b/src/shared/in-addr-prefix-util.h @@ -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); diff --git a/src/shared/meson.build b/src/shared/meson.build index 9d03c9a214e..6e09ef84795 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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