1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "extract-word.h"
5 #include "hash-funcs.h"
6 #include "hostname-util.h"
7 #include "in-addr-prefix-util.h"
10 #include "siphash24.h"
11 #include "string-util.h"
14 #define IN_ADDR_PREFIX_IPV4_ANY ((struct in_addr_prefix) { .family = AF_INET })
16 #define IN_ADDR_PREFIX_IPV6_ANY ((struct in_addr_prefix) { .family = AF_INET6 })
18 #define IN_ADDR_PREFIX_IPV4_LOCALHOST \
19 ((struct in_addr_prefix) { \
21 .address.in.s_addr = htobe32(UINT32_C(127) << 24), \
25 #define IN_ADDR_PREFIX_IPV6_LOCALHOST \
26 ((struct in_addr_prefix) { \
28 .address.in6 = IN6ADDR_LOOPBACK_INIT, \
32 #define IN_ADDR_PREFIX_IPV4_LINKLOCAL \
33 ((struct in_addr_prefix) { \
35 .address.in.s_addr = htobe32((UINT32_C(169) << 24) | \
36 (UINT32_C(254) << 16)), \
40 #define IN_ADDR_PREFIX_IPV6_LINKLOCAL \
41 ((struct in_addr_prefix) { \
43 .address.in6.s6_addr[0] = 0xfe, \
44 .address.in6.s6_addr[1] = 0x80, \
48 #define IN_ADDR_PREFIX_IPV4_MULTICAST \
49 ((struct in_addr_prefix) { \
51 .address.in.s_addr = htobe32((UINT32_C(224) << 24)), \
55 #define IN_ADDR_PREFIX_IPV6_MULTICAST \
56 ((struct in_addr_prefix) { \
58 .address.in6.s6_addr[0] = 0xff, \
62 static void in_addr_prefix_hash_func(const struct in_addr_prefix
*a
, struct siphash
*state
) {
66 siphash24_compress_typesafe(a
->family
, state
);
67 siphash24_compress_typesafe(a
->prefixlen
, state
);
68 in_addr_hash_func(&a
->address
, a
->family
, state
);
71 static int in_addr_prefix_compare_func(const struct in_addr_prefix
*x
, const struct in_addr_prefix
*y
) {
77 r
= CMP(x
->family
, y
->family
);
81 r
= CMP(x
->prefixlen
, y
->prefixlen
);
85 return memcmp(&x
->address
, &y
->address
, FAMILY_ADDRESS_SIZE(x
->family
));
88 DEFINE_HASH_OPS(in_addr_prefix_hash_ops
, struct in_addr_prefix
, in_addr_prefix_hash_func
, in_addr_prefix_compare_func
);
89 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
);
91 int in_addr_prefix_add(Set
**prefixes
, const struct in_addr_prefix
*prefix
) {
92 struct in_addr_prefix
*copy
;
96 assert(IN_SET(prefix
->family
, AF_INET
, AF_INET6
));
98 copy
= newdup(struct in_addr_prefix
, prefix
, 1);
102 (void) in_addr_mask(copy
->family
, ©
->address
, copy
->prefixlen
);
103 return set_ensure_consume(prefixes
, &in_addr_prefix_hash_ops_free
, copy
);
106 int in_addr_prefixes_reduce(Set
*prefixes
) {
107 uint32_t ipv4_prefixlen_bits
= 0;
108 uint64_t ipv6_prefixlen_bits
[128 / sizeof(uint64_t)] = {};
109 uint8_t ipv4_prefixlens
[32] = {}, ipv6_prefixlens
[128] = {};
110 bool ipv4_has_any
= false, ipv6_has_any
= false;
111 size_t ipv4_n_prefixlens
= 0, ipv6_n_prefixlens
= 0;
112 struct in_addr_prefix
*p
;
114 SET_FOREACH(p
, prefixes
)
117 assert(p
->prefixlen
<= 32);
118 if (p
->prefixlen
== 0)
121 ipv4_prefixlen_bits
|= UINT32_C(1) << (p
->prefixlen
- 1);
124 assert(p
->prefixlen
<= 128);
125 if (p
->prefixlen
== 0)
128 ipv6_prefixlen_bits
[(p
->prefixlen
- 1) / sizeof(uint64_t)] |=
129 UINT64_C(1) << ((p
->prefixlen
- 1) % sizeof(uint64_t));
132 assert_not_reached();
136 for (size_t i
= 0; i
< 32; i
++)
137 if (ipv4_prefixlen_bits
& (UINT32_C(1) << i
))
138 ipv4_prefixlens
[ipv4_n_prefixlens
++] = i
+ 1;
141 for (size_t i
= 0; i
< 128; i
++)
142 if (ipv6_prefixlen_bits
[i
/ sizeof(uint64_t)] &
143 (UINT64_C(1) << (i
% sizeof(uint64_t))))
144 ipv6_prefixlens
[ipv6_n_prefixlens
++] = i
+ 1;
146 SET_FOREACH(p
, prefixes
) {
151 if (p
->prefixlen
== 0)
156 prefixlens
= ipv4_prefixlens
;
157 n
= &ipv4_n_prefixlens
;
158 covered
= ipv4_has_any
;
161 prefixlens
= ipv6_prefixlens
;
162 n
= &ipv6_n_prefixlens
;
163 covered
= ipv6_has_any
;
166 assert_not_reached();
169 for (size_t i
= 0; i
< *n
; i
++) {
170 struct in_addr_prefix tmp
;
175 if (prefixlens
[i
] >= p
->prefixlen
)
179 tmp
.prefixlen
= prefixlens
[i
];
180 (void) in_addr_mask(tmp
.family
, &tmp
.address
, tmp
.prefixlen
);
182 covered
= set_contains(prefixes
, &tmp
);
186 free(set_remove(prefixes
, p
));
192 int in_addr_prefixes_merge(Set
**dest
, Set
*src
) {
193 struct in_addr_prefix
*p
;
198 SET_FOREACH(p
, src
) {
199 r
= in_addr_prefix_add(dest
, p
);
207 bool in_addr_prefixes_is_any(Set
*prefixes
) {
209 set_contains(prefixes
, &IN_ADDR_PREFIX_IPV4_ANY
) &&
210 set_contains(prefixes
, &IN_ADDR_PREFIX_IPV6_ANY
);
213 int config_parse_in_addr_prefixes(
215 const char *filename
,
218 unsigned section_line
,
225 Set
**prefixes
= ASSERT_PTR(data
);
228 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
230 if (isempty(rvalue
)) {
231 *prefixes
= set_free(*prefixes
);
235 for (const char *p
= rvalue
;;) {
236 _cleanup_free_
char *word
= NULL
;
238 r
= extract_first_word(&p
, &word
, NULL
, 0);
242 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
248 if (streq(word
, "any")) {
249 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
251 if (ltype
!= AF_INET6
) {
252 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_ANY
);
257 if (ltype
!= AF_INET
) {
258 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_ANY
);
263 } else if (is_localhost(word
)) {
264 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
266 if (ltype
!= AF_INET6
) {
267 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_LOCALHOST
);
272 if (ltype
!= AF_INET
) {
273 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_LOCALHOST
);
278 } else if (streq(word
, "link-local")) {
279 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
281 if (ltype
!= AF_INET6
) {
282 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_LINKLOCAL
);
287 if (ltype
!= AF_INET
) {
288 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_LINKLOCAL
);
293 } else if (streq(word
, "multicast")) {
294 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
296 if (ltype
!= AF_INET6
) {
297 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_MULTICAST
);
302 if (ltype
!= AF_INET
) {
303 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_MULTICAST
);
309 struct in_addr_prefix a
;
311 if (ltype
== AF_UNSPEC
)
312 r
= in_addr_prefix_from_string_auto(word
, &a
.family
, &a
.address
, &a
.prefixlen
);
315 r
= in_addr_prefix_from_string(word
, a
.family
, &a
.address
, &a
.prefixlen
);
318 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
319 "Invalid address prefix is specified in [%s] %s=, ignoring assignment: %s",
320 section
, lvalue
, word
);
324 r
= in_addr_prefix_add(prefixes
, &a
);