1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "extract-word.h"
5 #include "hostname-util.h"
6 #include "in-addr-prefix-util.h"
7 #include "string-util.h"
10 #define IN_ADDR_PREFIX_IPV4_ANY ((struct in_addr_prefix) { .family = AF_INET })
12 #define IN_ADDR_PREFIX_IPV6_ANY ((struct in_addr_prefix) { .family = AF_INET6 })
14 #define IN_ADDR_PREFIX_IPV4_LOCALHOST \
15 ((struct in_addr_prefix) { \
17 .address.in.s_addr = htobe32(UINT32_C(127) << 24), \
21 #define IN_ADDR_PREFIX_IPV6_LOCALHOST \
22 ((struct in_addr_prefix) { \
24 .address.in6 = IN6ADDR_LOOPBACK_INIT, \
28 #define IN_ADDR_PREFIX_IPV4_LINKLOCAL \
29 ((struct in_addr_prefix) { \
31 .address.in.s_addr = htobe32((UINT32_C(169) << 24) | \
32 (UINT32_C(254) << 16)), \
36 #define IN_ADDR_PREFIX_IPV6_LINKLOCAL \
37 ((struct in_addr_prefix) { \
39 .address.in6.s6_addr[0] = 0xfe, \
40 .address.in6.s6_addr[1] = 0x80, \
44 #define IN_ADDR_PREFIX_IPV4_MULTICAST \
45 ((struct in_addr_prefix) { \
47 .address.in.s_addr = htobe32((UINT32_C(224) << 24)), \
51 #define IN_ADDR_PREFIX_IPV6_MULTICAST \
52 ((struct in_addr_prefix) { \
54 .address.in6.s6_addr[0] = 0xff, \
58 static void in_addr_prefix_hash_func(const struct in_addr_prefix
*a
, struct siphash
*state
) {
62 siphash24_compress(&a
->family
, sizeof(a
->family
), state
);
63 siphash24_compress(&a
->prefixlen
, sizeof(a
->prefixlen
), state
);
64 siphash24_compress(&a
->address
, FAMILY_ADDRESS_SIZE(a
->family
), state
);
67 static int in_addr_prefix_compare_func(const struct in_addr_prefix
*x
, const struct in_addr_prefix
*y
) {
73 r
= CMP(x
->family
, y
->family
);
77 r
= CMP(x
->prefixlen
, y
->prefixlen
);
81 return memcmp(&x
->address
, &y
->address
, FAMILY_ADDRESS_SIZE(x
->family
));
84 DEFINE_HASH_OPS(in_addr_prefix_hash_ops
, struct in_addr_prefix
, in_addr_prefix_hash_func
, in_addr_prefix_compare_func
);
85 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
);
87 int in_addr_prefix_add(Set
**prefixes
, const struct in_addr_prefix
*prefix
) {
88 struct in_addr_prefix
*copy
;
92 assert(IN_SET(prefix
->family
, AF_INET
, AF_INET6
));
94 copy
= newdup(struct in_addr_prefix
, prefix
, 1);
98 (void) in_addr_mask(copy
->family
, ©
->address
, copy
->prefixlen
);
99 return set_ensure_consume(prefixes
, &in_addr_prefix_hash_ops_free
, copy
);
102 int in_addr_prefixes_reduce(Set
*prefixes
) {
103 uint32_t ipv4_prefixlen_bits
= 0;
104 uint64_t ipv6_prefixlen_bits
[128 / sizeof(uint64_t)] = {};
105 uint8_t ipv4_prefixlens
[32] = {}, ipv6_prefixlens
[128] = {};
106 bool ipv4_has_any
= false, ipv6_has_any
= false;
107 size_t ipv4_n_prefixlens
= 0, ipv6_n_prefixlens
= 0;
108 struct in_addr_prefix
*p
;
110 SET_FOREACH(p
, prefixes
)
113 assert(p
->prefixlen
<= 32);
114 if (p
->prefixlen
== 0)
117 ipv4_prefixlen_bits
|= UINT32_C(1) << (p
->prefixlen
- 1);
120 assert(p
->prefixlen
<= 128);
121 if (p
->prefixlen
== 0)
124 ipv6_prefixlen_bits
[(p
->prefixlen
- 1) / sizeof(uint64_t)] |=
125 UINT64_C(1) << ((p
->prefixlen
- 1) % sizeof(uint64_t));
128 assert_not_reached();
132 for (size_t i
= 0; i
< 32; i
++)
133 if (ipv4_prefixlen_bits
& (UINT32_C(1) << i
))
134 ipv4_prefixlens
[ipv4_n_prefixlens
++] = i
+ 1;
137 for (size_t i
= 0; i
< 128; i
++)
138 if (ipv6_prefixlen_bits
[i
/ sizeof(uint64_t)] &
139 (UINT64_C(1) << (i
% sizeof(uint64_t))))
140 ipv6_prefixlens
[ipv6_n_prefixlens
++] = i
+ 1;
142 SET_FOREACH(p
, prefixes
) {
147 if (p
->prefixlen
== 0)
152 prefixlens
= ipv4_prefixlens
;
153 n
= &ipv4_n_prefixlens
;
154 covered
= ipv4_has_any
;
157 prefixlens
= ipv6_prefixlens
;
158 n
= &ipv6_n_prefixlens
;
159 covered
= ipv6_has_any
;
162 assert_not_reached();
165 for (size_t i
= 0; i
< *n
; i
++) {
166 struct in_addr_prefix tmp
;
171 if (prefixlens
[i
] >= p
->prefixlen
)
175 tmp
.prefixlen
= prefixlens
[i
];
176 (void) in_addr_mask(tmp
.family
, &tmp
.address
, tmp
.prefixlen
);
178 covered
= set_contains(prefixes
, &tmp
);
182 free(set_remove(prefixes
, p
));
188 int in_addr_prefixes_merge(Set
**dest
, Set
*src
) {
189 struct in_addr_prefix
*p
;
194 SET_FOREACH(p
, src
) {
195 r
= in_addr_prefix_add(dest
, p
);
203 bool in_addr_prefixes_is_any(Set
*prefixes
) {
205 set_contains(prefixes
, &IN_ADDR_PREFIX_IPV4_ANY
) &&
206 set_contains(prefixes
, &IN_ADDR_PREFIX_IPV6_ANY
);
209 int config_parse_in_addr_prefixes(
211 const char *filename
,
214 unsigned section_line
,
221 Set
**prefixes
= data
;
225 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
227 if (isempty(rvalue
)) {
228 *prefixes
= set_free(*prefixes
);
232 for (const char *p
= rvalue
;;) {
233 _cleanup_free_
char *word
= NULL
;
235 r
= extract_first_word(&p
, &word
, NULL
, 0);
239 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
245 if (streq(word
, "any")) {
246 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
248 if (ltype
!= AF_INET6
) {
249 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_ANY
);
254 if (ltype
!= AF_INET
) {
255 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_ANY
);
260 } else if (is_localhost(word
)) {
261 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
263 if (ltype
!= AF_INET6
) {
264 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_LOCALHOST
);
269 if (ltype
!= AF_INET
) {
270 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_LOCALHOST
);
275 } else if (streq(word
, "link-local")) {
276 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
278 if (ltype
!= AF_INET6
) {
279 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_LINKLOCAL
);
284 if (ltype
!= AF_INET
) {
285 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_LINKLOCAL
);
290 } else if (streq(word
, "multicast")) {
291 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
293 if (ltype
!= AF_INET6
) {
294 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV4_MULTICAST
);
299 if (ltype
!= AF_INET
) {
300 r
= in_addr_prefix_add(prefixes
, &IN_ADDR_PREFIX_IPV6_MULTICAST
);
306 struct in_addr_prefix a
;
308 if (ltype
== AF_UNSPEC
)
309 r
= in_addr_prefix_from_string_auto(word
, &a
.family
, &a
.address
, &a
.prefixlen
);
312 r
= in_addr_prefix_from_string(word
, a
.family
, &a
.address
, &a
.prefixlen
);
315 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
316 "Address prefix is invalid, ignoring assignment: %s", word
);
320 r
= in_addr_prefix_add(prefixes
, &a
);