in-addr-prefix-util: introduce several utilities for address prefix

This commit is contained in:
Yu Watanabe
2021-09-14 20:30:09 +09:00
parent c4f06a75ea
commit bffaa49ec4
7 changed files with 352 additions and 38 deletions
-29
View 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);
-8
View 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"
+1 -1
View 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"
+1
View 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"
+325
View File
@@ -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();
}
}
}
+23
View File
@@ -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);
+2
View 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