From bffaa49ec42ddc7560aff2981266216ab7ce0b20 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 14 Sep 2021 14:10:33 +0900 Subject: [PATCH 1/6] in-addr-prefix-util: introduce several utilities for address prefix --- src/basic/in-addr-util.c | 29 --- src/basic/in-addr-util.h | 8 - src/network/networkd-dhcp-common.c | 2 +- src/network/networkd-dhcp6.c | 1 + src/shared/in-addr-prefix-util.c | 325 +++++++++++++++++++++++++++++ src/shared/in-addr-prefix-util.h | 23 ++ src/shared/meson.build | 2 + 7 files changed, 352 insertions(+), 38 deletions(-) create mode 100644 src/shared/in-addr-prefix-util.c create mode 100644 src/shared/in-addr-prefix-util.h diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 2187bd0cba..3d392afc08 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 f3ead91962..c74b0d512b 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 8138c4eda7..1a4ccb5e2c 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 824047fcc1..0a7471f0e8 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 0000000000..f907949c22 --- /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 0000000000..53aaad356c --- /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 9d03c9a214..6e09ef8479 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 From 0bc311bbe911e1f30d55878bd17ccc4659da0c50 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 14 Sep 2021 14:31:59 +0900 Subject: [PATCH 2/6] test: slightly modernize test-in-addr-util.c --- src/test/test-in-addr-util.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/test/test-in-addr-util.c b/src/test/test-in-addr-util.c index 509ea7e31e..b7171be1a4 100644 --- a/src/test/test-in-addr-util.c +++ b/src/test/test-in-addr-util.c @@ -3,9 +3,9 @@ #include #include -#include "log.h" -#include "strv.h" #include "in-addr-util.h" +#include "strv.h" +#include "tests.h" static void test_in_addr_prefix_from_string_one( const char *p, @@ -238,7 +238,7 @@ static void test_in_addr_prefix_intersect(void) { static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigned pl, const char *after) { union in_addr_union ubefore, uafter, t; - log_info("/* %s(%s, prefixlen=%u) */", __func__, before, pl); + log_debug("/* %s(%s, prefixlen=%u) */", __func__, before, pl); assert_se(in_addr_from_string(f, before, &ubefore) >= 0); @@ -252,6 +252,8 @@ static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigne } static void test_in_addr_prefix_next(void) { + log_info("/* %s */", __func__); + test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0"); test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0"); test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 20, "192.168.16.0"); @@ -276,7 +278,7 @@ static void test_in_addr_prefix_next(void) { static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) { union in_addr_union ubefore, uafter, t; - log_info("/* %s(%s, prefixlen=%u, nth=%"PRIu64") */", __func__, before, pl, nth); + log_debug("/* %s(%s, prefixlen=%u, nth=%"PRIu64") */", __func__, before, pl, nth); assert_se(in_addr_from_string(f, before, &ubefore) >= 0); @@ -290,6 +292,8 @@ static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned } static void test_in_addr_prefix_nth(void) { + log_info("/* %s */", __func__); + test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0"); test_in_addr_prefix_nth_one(AF_INET, "192.168.0.123", 24, 0, "192.168.0.0"); test_in_addr_prefix_nth_one(AF_INET, "192.168.0.123", 24, 1, "192.168.1.0"); @@ -323,7 +327,7 @@ static void test_in_addr_prefix_range_one( union in_addr_union a, s, e; - log_info("/* %s(%s, prefixlen=%u) */", __func__, in, prefixlen); + log_debug("/* %s(%s, prefixlen=%u) */", __func__, in, prefixlen); assert_se(in_addr_from_string(family, in, &a) >= 0); assert_se((in_addr_prefix_range(family, &a, prefixlen, &s, &e) >= 0) == !!expected_start); @@ -343,6 +347,8 @@ static void test_in_addr_prefix_range_one( } static void test_in_addr_prefix_range(void) { + log_info("/* %s */", __func__); + test_in_addr_prefix_range_one(AF_INET, "192.168.123.123", 24, "192.168.123.0", "192.168.124.0"); test_in_addr_prefix_range_one(AF_INET, "192.168.123.123", 16, "192.168.0.0", "192.169.0.0"); @@ -376,6 +382,8 @@ static void test_in_addr_to_string(void) { } int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + test_in_addr_prefix_from_string(); test_in_addr_random_prefix(); test_in_addr_prefix_to_string(); From 0856e78df05c289fd1abe488f52d0f4eb22c410c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 14 Sep 2021 14:55:25 +0900 Subject: [PATCH 3/6] test: add tests for in-addr-prefix-util.c --- src/test/meson.build | 2 + src/test/test-in-addr-prefix-util.c | 94 +++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/test/test-in-addr-prefix-util.c diff --git a/src/test/meson.build b/src/test/meson.build index 46b497672f..f58cf2d843 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -285,6 +285,8 @@ tests += [ [['src/test/test-in-addr-util.c']], + [['src/test/test-in-addr-prefix-util.c']], + [['src/test/test-barrier.c']], [['src/test/test-tmpfiles.c']], diff --git a/src/test/test-in-addr-prefix-util.c b/src/test/test-in-addr-prefix-util.c new file mode 100644 index 0000000000..5db22302bf --- /dev/null +++ b/src/test/test-in-addr-prefix-util.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "in-addr-prefix-util.h" +#include "tests.h" + +static void test_config_parse_in_addr_prefixes_one(int family, const union in_addr_union *addr, uint8_t prefixlen, Set **prefixes) { + _cleanup_free_ char *str = NULL; + + assert_se(in_addr_prefix_to_string(family, addr, prefixlen, &str) >= 0); + assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, str, prefixes, NULL) >= 0); +} + +static void test_config_parse_in_addr_prefixes(Set **ret) { + _cleanup_set_free_ Set *prefixes = NULL; + + log_info("/* %s() */", __func__); + + for (uint32_t i = 0; i < 256; i++) { + /* ipv4 link-local address */ + test_config_parse_in_addr_prefixes_one(AF_INET, &(union in_addr_union) { + .in.s_addr = htobe32((UINT32_C(169) << 24) | + (UINT32_C(254) << 16) | + (i << 8)), + }, 24, &prefixes); + + /* ipv6 multicast address */ + test_config_parse_in_addr_prefixes_one(AF_INET6, &(union in_addr_union) { + .in6.s6_addr[0] = 0xff, + .in6.s6_addr[1] = i, + }, 16, &prefixes); + + for (uint32_t j = 0; j < 256; j++) { + test_config_parse_in_addr_prefixes_one(AF_INET, &(union in_addr_union) { + .in.s_addr = htobe32((UINT32_C(169) << 24) | + (UINT32_C(254) << 16) | + (i << 8) | j), + }, 32, &prefixes); + + test_config_parse_in_addr_prefixes_one(AF_INET6, &(union in_addr_union) { + .in6.s6_addr[0] = 0xff, + .in6.s6_addr[1] = i, + .in6.s6_addr[2] = j, + }, 24, &prefixes); + } + } + + *ret = TAKE_PTR(prefixes); +} + +static void test_in_addr_prefixes_reduce(Set *prefixes) { + log_info("/* %s() */", __func__); + + assert_se(set_size(prefixes) == 2 * 256 * 257); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(in_addr_prefixes_reduce(prefixes) >= 0); + assert_se(set_size(prefixes) == 2 * 256); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, "link-local", &prefixes, NULL) == 0); + assert_se(set_size(prefixes) == 2 * 256 + 2); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(in_addr_prefixes_reduce(prefixes) >= 0); + assert_se(set_size(prefixes) == 256 + 2); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, "multicast", &prefixes, NULL) == 0); + assert_se(set_size(prefixes) == 256 + 4); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(in_addr_prefixes_reduce(prefixes) >= 0); + assert_se(set_size(prefixes) == 4); + assert_se(!in_addr_prefixes_is_any(prefixes)); + + assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, "any", &prefixes, NULL) == 0); + assert_se(set_size(prefixes) == 6); + assert_se(in_addr_prefixes_is_any(prefixes)); + + assert_se(in_addr_prefixes_reduce(prefixes) >= 0); + assert_se(set_size(prefixes) == 2); + assert_se(in_addr_prefixes_is_any(prefixes)); +} + +int main(int argc, char *argv[]) { + _cleanup_set_free_ Set *prefixes = NULL; + + test_setup_logging(LOG_DEBUG); + + test_config_parse_in_addr_prefixes(&prefixes); + test_in_addr_prefixes_reduce(prefixes); + + return 0; +} From 60da07ecc7bb318d217810778e430399ab9a2ec7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 14 Sep 2021 14:11:08 +0900 Subject: [PATCH 4/6] core/cgroup: set bitfield to reduce struct size --- src/core/cgroup.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 99bf7e22d8..5b55dd7121 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -148,10 +148,10 @@ struct CGroupContext { uint64_t memory_max; uint64_t memory_swap_max; - bool default_memory_min_set; - bool default_memory_low_set; - bool memory_min_set; - bool memory_low_set; + bool default_memory_min_set:1; + bool default_memory_low_set:1; + bool memory_min_set:1; + bool memory_low_set:1; LIST_HEAD(IPAddressAccessItem, ip_address_allow); LIST_HEAD(IPAddressAccessItem, ip_address_deny); From 84ebe6f01381c21b88e37e856956c9c9ee6781d6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 10 Sep 2021 13:58:28 +0900 Subject: [PATCH 5/6] core: replace IPAddressAccessItem with struct in_addr_prefix Previously, if a unit file which contains n IPAddressAllow/Deny= lines, then the computational order of parsing the file was O(n^3), as ip_address_access_reduce(), whose order is O(n^2), is called for each line. By replacing in_addr_prefix related functions, now the computational order is O(n log n). Fixes #20680. --- src/analyze/analyze-security.c | 8 +- src/core/bpf-firewall.c | 34 +++-- src/core/cgroup.c | 27 ++-- src/core/cgroup.h | 9 +- src/core/dbus-cgroup.c | 70 +++++---- src/core/ip-address-access.c | 208 -------------------------- src/core/ip-address-access.h | 25 ---- src/core/load-fragment-gperf.gperf.in | 5 +- src/core/manager.h | 1 - src/core/meson.build | 2 - src/core/unit.h | 1 + src/test/test-bpf-firewall.c | 45 ++++-- 12 files changed, 123 insertions(+), 312 deletions(-) delete mode 100644 src/core/ip-address-access.c delete mode 100644 src/core/ip-address-access.h diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index c674961001..400eebbda8 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -11,7 +11,7 @@ #include "bus-util.h" #include "env-util.h" #include "format-table.h" -#include "in-addr-util.h" +#include "in-addr-prefix-util.h" #include "locale-util.h" #include "macro.h" #include "manager.h" @@ -2582,10 +2582,10 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security return log_oom(); } - IPAddressAccessItem *i; + struct in_addr_prefix *i; bool deny_ipv4 = false, deny_ipv6 = false; - LIST_FOREACH(items, i, g->ip_address_deny) { + SET_FOREACH(i, g->ip_address_deny) { if (i->family == AF_INET && i->prefixlen == 0) deny_ipv4 = true; else if (i->family == AF_INET6 && i->prefixlen == 0) @@ -2594,7 +2594,7 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security info->ip_address_deny_all = deny_ipv4 && deny_ipv6; info->ip_address_allow_localhost = info->ip_address_allow_other = false; - LIST_FOREACH(items, i, g->ip_address_allow) { + SET_FOREACH(i, g->ip_address_allow) { if (in_addr_is_localhost(i->family, &i->address)) info->ip_address_allow_localhost = true; else diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index 9317edeb4c..d23b43bcb2 100644 --- a/src/core/bpf-firewall.c +++ b/src/core/bpf-firewall.c @@ -18,7 +18,7 @@ #include "bpf-firewall.h" #include "bpf-program.h" #include "fd-util.h" -#include "ip-address-access.h" +#include "in-addr-prefix-util.h" #include "memory-util.h" #include "missing_syscall.h" #include "unit.h" @@ -335,13 +335,13 @@ static int bpf_firewall_compile_bpf( return 0; } -static int bpf_firewall_count_access_items(IPAddressAccessItem *list, size_t *n_ipv4, size_t *n_ipv6) { - IPAddressAccessItem *a; +static int bpf_firewall_count_access_items(Set *prefixes, size_t *n_ipv4, size_t *n_ipv6) { + struct in_addr_prefix *a; assert(n_ipv4); assert(n_ipv6); - LIST_FOREACH(items, a, list) { + SET_FOREACH(a, prefixes) switch (a->family) { case AF_INET: @@ -355,26 +355,25 @@ static int bpf_firewall_count_access_items(IPAddressAccessItem *list, size_t *n_ default: return -EAFNOSUPPORT; } - } return 0; } static int bpf_firewall_add_access_items( - IPAddressAccessItem *list, + Set *prefixes, int ipv4_map_fd, int ipv6_map_fd, int verdict) { struct bpf_lpm_trie_key *key_ipv4, *key_ipv6; + struct in_addr_prefix *a; uint64_t value = verdict; - IPAddressAccessItem *a; int r; key_ipv4 = alloca0(offsetof(struct bpf_lpm_trie_key, data) + sizeof(uint32_t)); key_ipv6 = alloca0(offsetof(struct bpf_lpm_trie_key, data) + sizeof(uint32_t) * 4); - LIST_FOREACH(items, a, list) { + SET_FOREACH(a, prefixes) switch (a->family) { case AF_INET: @@ -400,7 +399,6 @@ static int bpf_firewall_add_access_items( default: return -EAFNOSUPPORT; } - } return 0; } @@ -414,7 +412,6 @@ static int bpf_firewall_prepare_access_maps( _cleanup_close_ int ipv4_map_fd = -1, ipv6_map_fd = -1; size_t n_ipv4 = 0, n_ipv6 = 0; - IPAddressAccessItem *list; Unit *p; int r; @@ -424,18 +421,29 @@ static int bpf_firewall_prepare_access_maps( for (p = u; p; p = UNIT_GET_SLICE(p)) { CGroupContext *cc; + Set *prefixes; + bool *reduced; cc = unit_get_cgroup_context(p); if (!cc) continue; - list = verdict == ACCESS_ALLOWED ? cc->ip_address_allow : cc->ip_address_deny; + prefixes = verdict == ACCESS_ALLOWED ? cc->ip_address_allow : cc->ip_address_deny; + reduced = verdict == ACCESS_ALLOWED ? &cc->ip_address_allow_reduced : &cc->ip_address_deny_reduced; - bpf_firewall_count_access_items(list, &n_ipv4, &n_ipv6); + if (!*reduced) { + r = in_addr_prefixes_reduce(prefixes); + if (r < 0) + return r; + + *reduced = true; + } + + bpf_firewall_count_access_items(prefixes, &n_ipv4, &n_ipv6); /* Skip making the LPM trie map in cases where we are using "any" in order to hack around * needing CAP_SYS_ADMIN for allocating LPM trie map. */ - if (ip_address_access_item_is_any(list)) { + if (in_addr_prefixes_is_any(prefixes)) { *ret_has_any = true; return 0; } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 52bdf54b5f..2d136793f9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -19,6 +19,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "in-addr-prefix-util.h" #include "io-util.h" #include "ip-protocol-list.h" #include "limits-util.h" @@ -238,8 +239,8 @@ void cgroup_context_done(CGroupContext *c) { cgroup_context_remove_socket_bind(&c->socket_bind_allow); cgroup_context_remove_socket_bind(&c->socket_bind_deny); - c->ip_address_allow = ip_address_access_free_all(c->ip_address_allow); - c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny); + c->ip_address_allow = set_free(c->ip_address_allow); + c->ip_address_deny = set_free(c->ip_address_deny); c->ip_filters_ingress = strv_free(c->ip_filters_ingress); c->ip_filters_egress = strv_free(c->ip_filters_egress); @@ -395,7 +396,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) { CGroupDeviceAllow *a; CGroupContext *c; CGroupSocketBindItem *bi; - IPAddressAccessItem *iaai; + struct in_addr_prefix *iaai; char **path; char cda[FORMAT_CGROUP_DIFF_MAX]; @@ -549,18 +550,18 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) { FORMAT_BYTES(b->wbps)); } - LIST_FOREACH(items, iaai, c->ip_address_allow) { + SET_FOREACH(iaai, c->ip_address_allow) { _cleanup_free_ char *k = NULL; - (void) in_addr_to_string(iaai->family, &iaai->address, &k); - fprintf(f, "%sIPAddressAllow: %s/%u\n", prefix, strnull(k), iaai->prefixlen); + (void) in_addr_prefix_to_string(iaai->family, &iaai->address, iaai->prefixlen, &k); + fprintf(f, "%sIPAddressAllow: %s\n", prefix, strnull(k)); } - LIST_FOREACH(items, iaai, c->ip_address_deny) { + SET_FOREACH(iaai, c->ip_address_deny) { _cleanup_free_ char *k = NULL; - (void) in_addr_to_string(iaai->family, &iaai->address, &k); - fprintf(f, "%sIPAddressDeny: %s/%u\n", prefix, strnull(k), iaai->prefixlen); + (void) in_addr_prefix_to_string(iaai->family, &iaai->address, iaai->prefixlen, &k); + fprintf(f, "%sIPAddressDeny: %s\n", prefix, strnull(k)); } STRV_FOREACH(path, c->ip_filters_ingress) @@ -1555,8 +1556,8 @@ static bool unit_get_needs_bpf_firewall(Unit *u) { return false; if (c->ip_accounting || - c->ip_address_allow || - c->ip_address_deny || + !set_isempty(c->ip_address_allow) || + !set_isempty(c->ip_address_deny) || c->ip_filters_ingress || c->ip_filters_egress) return true; @@ -1567,8 +1568,8 @@ static bool unit_get_needs_bpf_firewall(Unit *u) { if (!c) return false; - if (c->ip_address_allow || - c->ip_address_deny) + if (!set_isempty(c->ip_address_allow) || + !set_isempty(c->ip_address_deny)) return true; } diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 5b55dd7121..9bb968b6c1 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -5,7 +5,6 @@ #include "cgroup-util.h" #include "cpu-set-util.h" -#include "ip-address-access.h" #include "list.h" #include "time-util.h" @@ -153,8 +152,12 @@ struct CGroupContext { bool memory_min_set:1; bool memory_low_set:1; - LIST_HEAD(IPAddressAccessItem, ip_address_allow); - LIST_HEAD(IPAddressAccessItem, ip_address_deny); + Set *ip_address_allow; + Set *ip_address_deny; + /* These two flags indicate that redundant entries have been removed from + * ip_address_allow/ip_address_deny, i.e. in_addr_prefixes_reduce() has already been called. */ + bool ip_address_allow_reduced; + bool ip_address_deny_reduced; char **ip_filters_ingress; char **ip_filters_egress; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 75efa9fe1f..ef7d529de9 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -15,6 +15,7 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" +#include "in-addr-prefix-util.h" #include "ip-protocol-list.h" #include "limits-util.h" #include "parse-util.h" @@ -318,14 +319,17 @@ static int property_get_ip_address_access( void *userdata, sd_bus_error *error) { - IPAddressAccessItem** items = userdata, *i; + Set **prefixes = userdata; + struct in_addr_prefix *i; int r; + assert(prefixes); + r = sd_bus_message_open_container(reply, 'a', "(iayu)"); if (r < 0) return r; - LIST_FOREACH(items, i, *items) { + SET_FOREACH(i, *prefixes) { r = sd_bus_message_open_container(reply, 'r', "iayu"); if (r < 0) @@ -1745,11 +1749,9 @@ int bus_cgroup_set_property( return 1; } else if (STR_IN_SET(name, "IPAddressAllow", "IPAddressDeny")) { - IPAddressAccessItem **list; + _cleanup_set_free_ Set *new_prefixes = NULL; size_t n = 0; - list = streq(name, "IPAddressAllow") ? &c->ip_address_allow : &c->ip_address_deny; - r = sd_bus_message_enter_container(message, 'a', "(iayu)"); if (r < 0) return r; @@ -1789,17 +1791,16 @@ int bus_cgroup_set_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Prefix length %" PRIu32 " too large for address family %s.", prefixlen, af_to_name(family)); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - IPAddressAccessItem *item; + struct in_addr_prefix prefix = { + .family = family, + .prefixlen = prefixlen, + }; - item = new0(IPAddressAccessItem, 1); - if (!item) - return -ENOMEM; + memcpy(&prefix.address, ap, an); - item->family = family; - item->prefixlen = prefixlen; - memcpy(&item->address, ap, an); - - LIST_PREPEND(items, *list, item); + r = in_addr_prefix_add(&new_prefixes, &prefix); + if (r < 0) + return r; } r = sd_bus_message_exit_container(message); @@ -1813,33 +1814,46 @@ int bus_cgroup_set_property( if (r < 0) return r; - *list = ip_address_access_reduce(*list); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - IPAddressAccessItem *item; size_t size = 0; - - if (n == 0) - *list = ip_address_access_free_all(*list); + Set **prefixes; + bool *reduced; unit_invalidate_cgroup_bpf(u); f = open_memstream_unlocked(&buf, &size); if (!f) return -ENOMEM; - fputs(name, f); - fputs("=\n", f); + prefixes = streq(name, "IPAddressAllow") ? &c->ip_address_allow : &c->ip_address_deny; + reduced = streq(name, "IPAddressAllow") ? &c->ip_address_allow_reduced : &c->ip_address_deny_reduced; - LIST_FOREACH(items, item, *list) { - char buffer[CONST_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; + if (n == 0) { + *reduced = true; + *prefixes = set_free(*prefixes); + fputs(name, f); + fputs("=\n", f); + } else { + struct in_addr_prefix *p; - errno = 0; - if (!inet_ntop(item->family, &item->address, buffer, sizeof(buffer))) - return errno_or_else(EINVAL); + *reduced = false; - fprintf(f, "%s=%s/%u\n", name, buffer, item->prefixlen); + r = in_addr_prefixes_merge(prefixes, new_prefixes); + if (r < 0) + return r; + + SET_FOREACH(p, new_prefixes) { + _cleanup_free_ char *buffer = NULL; + + r = in_addr_prefix_to_string(p->family, &p->address, p->prefixlen, &buffer); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + fprintf(f, "%s=%s\n", name, buffer); + } } r = fflush_and_check(f); diff --git a/src/core/ip-address-access.c b/src/core/ip-address-access.c deleted file mode 100644 index a11251e94e..0000000000 --- a/src/core/ip-address-access.c +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include - -#include "alloc-util.h" -#include "bpf-firewall.h" -#include "extract-word.h" -#include "hostname-util.h" -#include "ip-address-access.h" -#include "parse-util.h" -#include "string-util.h" - -int config_parse_ip_address_access( - 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) { - - IPAddressAccessItem **list = data; - const char *p; - int r; - - assert(list); - - if (isempty(rvalue)) { - *list = ip_address_access_free_all(*list); - return 0; - } - - p = rvalue; - - for (;;) { - _cleanup_free_ IPAddressAccessItem *a = NULL; - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, 0); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - break; - } - - a = new0(IPAddressAccessItem, 1); - if (!a) - return log_oom(); - - if (streq(word, "any")) { - /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ - - a->family = AF_INET; - LIST_APPEND(items, *list, a); - - a = new0(IPAddressAccessItem, 1); - if (!a) - return log_oom(); - - a->family = AF_INET6; - - } else if (is_localhost(word)) { - /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ - - a->family = AF_INET; - a->address.in.s_addr = htobe32(0x7f000000); - a->prefixlen = 8; - LIST_APPEND(items, *list, a); - - a = new0(IPAddressAccessItem, 1); - if (!a) - return log_oom(); - - a->family = AF_INET6; - a->address.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; - a->prefixlen = 128; - - } else if (streq(word, "link-local")) { - - /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ - - a->family = AF_INET; - a->address.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); - a->prefixlen = 16; - LIST_APPEND(items, *list, a); - - a = new0(IPAddressAccessItem, 1); - if (!a) - return log_oom(); - - a->family = AF_INET6; - a->address.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xfe800000) - }; - a->prefixlen = 64; - - } else if (streq(word, "multicast")) { - - /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ - - a->family = AF_INET; - a->address.in.s_addr = htobe32((UINT32_C(224) << 24)); - a->prefixlen = 4; - LIST_APPEND(items, *list, a); - - a = new0(IPAddressAccessItem, 1); - if (!a) - return log_oom(); - - a->family = AF_INET6; - a->address.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xff000000) - }; - a->prefixlen = 8; - - } else { - r = in_addr_prefix_from_string_auto(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); - return 0; - } - } - - LIST_APPEND(items, *list, a); - a = NULL; - } - - *list = ip_address_access_reduce(*list); - - return 0; -} - -IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first) { - IPAddressAccessItem *next, *p = first; - - while (p) { - next = p->items_next; - free(p); - - p = next; - } - - return NULL; -} - -IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) { - IPAddressAccessItem *a, *b, *tmp; - int r; - - /* Drops all entries from the list that are covered by another entry in full, thus removing all redundant - * entries. */ - - LIST_FOREACH_SAFE(items, a, tmp, first) { - - /* Drop irrelevant bits */ - (void) in_addr_mask(a->family, &a->address, a->prefixlen); - - LIST_FOREACH(items, b, first) { - - if (a == b) - continue; - - if (a->family != b->family) - continue; - - if (b->prefixlen > a->prefixlen) - continue; - - r = in_addr_prefix_covers(b->family, - &b->address, - b->prefixlen, - &a->address); - if (r > 0) { - /* b covers a fully, then let's drop a */ - LIST_REMOVE(items, first, a); - free(a); - break; - } - } - } - - return first; -} - -bool ip_address_access_item_is_any(IPAddressAccessItem *first) { - /* Check for exactly two entries */ - if (!first || !first->items_next || first->items_next->items_next) - return false; - - /* Check both entries cover the full range */ - if (first->prefixlen != 0 || first->items_next->prefixlen != 0) - return false; - - /* Check that one of them is the IPv4 and the other IPv6 */ - if (!((first->family == AF_INET && first->items_next->family == AF_INET6) || - (first->family == AF_INET6 && first->items_next->family == AF_INET))) - return false; - - /* No need to check the actual addresses, they don't matter if the prefix is zero */ - return true; -} diff --git a/src/core/ip-address-access.h b/src/core/ip-address-access.h deleted file mode 100644 index 71b5459e2a..0000000000 --- a/src/core/ip-address-access.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "conf-parser.h" -#include "in-addr-util.h" -#include "list.h" - -typedef struct IPAddressAccessItem IPAddressAccessItem; - -struct IPAddressAccessItem { - int family; - unsigned char prefixlen; - union in_addr_union address; - LIST_FIELDS(IPAddressAccessItem, items); -}; - -CONFIG_PARSER_PROTOTYPE(config_parse_ip_address_access); - -IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first); - -IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first); - -/* Returns true if a list consists of only the two items necessary for "any" - * (0.0.0.0/0 and ::/0). */ -bool ip_address_access_item_is_any(IPAddressAccessItem *first); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 489841af7a..b3dc3bc0fc 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -221,8 +221,8 @@ {{type}}.Delegate, config_parse_delegate, 0, offsetof({{type}}, cgroup_context) {{type}}.DisableControllers, config_parse_disable_controllers, 0, offsetof({{type}}, cgroup_context) {{type}}.IPAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.ip_accounting) -{{type}}.IPAddressAllow, config_parse_ip_address_access, 0, offsetof({{type}}, cgroup_context.ip_address_allow) -{{type}}.IPAddressDeny, config_parse_ip_address_access, 0, offsetof({{type}}, cgroup_context.ip_address_deny) +{{type}}.IPAddressAllow, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_allow) +{{type}}.IPAddressDeny, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_deny) {{type}}.IPIngressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_ingress) {{type}}.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_egress) {{type}}.ManagedOOMSwap, config_parse_managed_oom_mode, 0, offsetof({{type}}, cgroup_context.moom_swap) @@ -243,6 +243,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include #include "all-units.h" #include "conf-parser.h" +#include "in-addr-prefix-util.h" #include "load-fragment.h" %} struct ConfigPerfItem; diff --git a/src/core/manager.h b/src/core/manager.h index 1220c9fb16..4234e187fd 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -12,7 +12,6 @@ #include "cgroup.h" #include "fdset.h" #include "hashmap.h" -#include "ip-address-access.h" #include "list.h" #include "prioq.h" #include "ratelimit.h" diff --git a/src/core/meson.build b/src/core/meson.build index 87a15ec71a..d36be869ce 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -71,8 +71,6 @@ libcore_sources = ''' generator-setup.h ima-setup.c ima-setup.h - ip-address-access.c - ip-address-access.h job.c job.h kill.c diff --git a/src/core/unit.h b/src/core/unit.h index 80d56aefc1..c8e99acf70 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "sd-id128.h" diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index 8b7d46bee3..1e0ad177b9 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -6,6 +6,7 @@ #include "bpf-firewall.h" #include "bpf-program.h" +#include "in-addr-prefix-util.h" #include "load-fragment.h" #include "manager.h" #include "memory-util.h" @@ -106,21 +107,39 @@ int main(int argc, char *argv[]) { cc->ip_accounting = true; - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL) == 0); - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL) == 0); - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL) == 0); - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL) == 0); - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL) == 0); - assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL) == 0); + assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL) == 0); - assert(cc->ip_address_allow); - assert(cc->ip_address_allow->items_next); - assert(!cc->ip_address_allow->items_next->items_next); + assert_se(set_size(cc->ip_address_allow) == 2); + assert_se(set_size(cc->ip_address_deny) == 4); - /* The deny list is defined redundantly, let's ensure it got properly reduced */ - assert(cc->ip_address_deny); - assert(cc->ip_address_deny->items_next); - assert(!cc->ip_address_deny->items_next->items_next); + /* The deny list is defined redundantly, let's ensure it will be properly reduced */ + assert_se(in_addr_prefixes_reduce(cc->ip_address_allow) >= 0); + assert_se(in_addr_prefixes_reduce(cc->ip_address_deny) >= 0); + + assert_se(set_size(cc->ip_address_allow) == 2); + assert_se(set_size(cc->ip_address_deny) == 2); + + assert_se(set_contains(cc->ip_address_allow, &(struct in_addr_prefix) { + .family = AF_INET, + .address.in.s_addr = htobe32((UINT32_C(10) << 24) | (UINT32_C(1) << 8)), + .prefixlen = 24 })); + assert_se(set_contains(cc->ip_address_allow, &(struct in_addr_prefix) { + .family = AF_INET, + .address.in.s_addr = htobe32(0x7f000002), + .prefixlen = 32 })); + assert_se(set_contains(cc->ip_address_deny, &(struct in_addr_prefix) { + .family = AF_INET, + .address.in.s_addr = htobe32(0x7f000000), + .prefixlen = 25 })); + assert_se(set_contains(cc->ip_address_deny, &(struct in_addr_prefix) { + .family = AF_INET, + .address.in.s_addr = htobe32((UINT32_C(10) << 24) | (UINT32_C(3) << 8)), + .prefixlen = 24 })); assert_se(config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START, "/bin/ping -c 1 127.0.0.2 -W 5", SERVICE(u)->exec_command, u) == 0); assert_se(config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START, "/bin/ping -c 1 127.0.0.3 -W 5", SERVICE(u)->exec_command, u) == 0); From 9d01399d445f5e37497880653733ef3e53a40614 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 10 Sep 2021 15:08:54 +0900 Subject: [PATCH 6/6] network: replace config_parse_address_filter() with config_parse_in_addr_prefixes() --- src/network/networkd-dhcp-common.c | 66 ------------------------ src/network/networkd-dhcp-common.h | 1 - src/network/networkd-network-gperf.gperf | 23 +++++---- 3 files changed, 12 insertions(+), 78 deletions(-) diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 1a4ccb5e2c..8739b62891 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -1265,69 +1265,3 @@ int config_parse_network_duid_rawdata( /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */ return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network); } - -int config_parse_address_filter( - 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 **list = data; - int r; - - assert(filename); - assert(lvalue); - assert(IN_SET(ltype, AF_INET, AF_INET6)); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - *list = set_free(*list); - return 0; - } - - for (const char *p = rvalue;;) { - _cleanup_free_ char *n = NULL; - _cleanup_free_ struct in_addr_prefix *a = NULL; - struct in_addr_prefix prefix; - - r = extract_first_word(&p, &n, NULL, 0); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse NDisc %s=, ignoring assignment: %s", - lvalue, rvalue); - return 0; - } - if (r == 0) - return 0; - - r = in_addr_prefix_from_string(n, ltype, &prefix.address, &prefix.prefixlen); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "NDisc %s= entry is invalid, ignoring assignment: %s", - lvalue, n); - continue; - } - - prefix.family = ltype; - a = newdup(struct in_addr_prefix, &prefix, 1); - if (!a) - return log_oom(); - - r = set_ensure_consume(list, &in_addr_prefix_hash_ops_free, TAKE_PTR(a)); - if (r < 0) - return log_oom(); - if (r == 0) - log_syntax(unit, LOG_WARNING, filename, line, 0, - "%s %s= entry is duplicated, ignoring assignment: %s", - section, lvalue, n); - } -} diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index faabe5430a..4a98038391 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -99,4 +99,3 @@ CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_type); CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata); CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_rawdata); CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_rawdata); -CONFIG_PARSER_PROTOTYPE(config_parse_address_filter); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index b42d5ad3a4..53e23d9c0a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -5,6 +5,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #endif #include #include "conf-parser.h" +#include "in-addr-prefix-util.h" #include "netem.h" #include "net-condition.h" #include "networkd-address-label.h" @@ -228,8 +229,8 @@ DHCPv4.UseTimezone, config_parse_bool, DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline) -DHCPv4.DenyList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_deny_listed_ip) -DHCPv4.AllowList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_allow_listed_ip) +DHCPv4.DenyList, config_parse_in_addr_prefixes, AF_INET, offsetof(Network, dhcp_deny_listed_ip) +DHCPv4.AllowList, config_parse_in_addr_prefixes, AF_INET, offsetof(Network, dhcp_allow_listed_ip) DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, dhcp_ip_service_type) DHCPv4.SendOption, config_parse_dhcp_send_option, AF_INET, offsetof(Network, dhcp_client_send_options) DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options) @@ -262,12 +263,12 @@ IPv6AcceptRA.UseMTU, config_parse_bool, IPv6AcceptRA.DHCPv6Client, config_parse_ipv6_accept_ra_start_dhcp6_client, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client) IPv6AcceptRA.RouteTable, config_parse_dhcp_or_ra_route_table, (RTPROT_RA<<16) | AF_INET6, 0 IPv6AcceptRA.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0 -IPv6AcceptRA.RouterAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_router) -IPv6AcceptRA.RouterDenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_router) -IPv6AcceptRA.PrefixAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_prefix) -IPv6AcceptRA.PrefixDenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) -IPv6AcceptRA.RouteAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_route_prefix) -IPv6AcceptRA.RouteDenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_route_prefix) +IPv6AcceptRA.RouterAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_router) +IPv6AcceptRA.RouterDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_router) +IPv6AcceptRA.PrefixAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_prefix) +IPv6AcceptRA.PrefixDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) +IPv6AcceptRA.RouteAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_route_prefix) +IPv6AcceptRA.RouteDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_route_prefix) DHCPServer.ServerAddress, config_parse_dhcp_server_address, 0, 0 DHCPServer.UplinkInterface, config_parse_uplink, 0, 0 DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_relay_target) @@ -500,7 +501,7 @@ IPv6PrefixDelegation.DNS, config_parse_radv_dns, IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains) IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0 IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) -DHCPv4.BlackList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_deny_listed_ip) +DHCPv4.BlackList, config_parse_in_addr_prefixes, AF_INET, offsetof(Network, dhcp_deny_listed_ip) DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_dhcp_use_dns, AF_UNSPEC, 0 DHCP.UseNTP, config_parse_dhcp_use_ntp, AF_UNSPEC, 0 @@ -528,8 +529,8 @@ DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, DHCPv4.UseDomainName, config_parse_dhcp_use_domains, AF_INET, 0 DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical) DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0 -IPv6AcceptRA.DenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) -IPv6AcceptRA.BlackList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) +IPv6AcceptRA.DenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) +IPv6AcceptRA.BlackList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0 TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_network_emulator_delay, 0, 0 TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_network_emulator_delay, 0, 0