in-addr-prefix-util: introduce several utilities for address prefix
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user