mirror of
https://github.com/systemd/systemd.git
synced 2025-01-25 10:04:04 +03:00
326 lines
13 KiB
C
326 lines
13 KiB
C
/* 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();
|
|
}
|
|
}
|
|
}
|