]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/in-addr-prefix-util.c
basic/include: replace _Static_assert() with static_assert()
[thirdparty/systemd.git] / src / shared / in-addr-prefix-util.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
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"
8#include "log.h"
9#include "set.h"
10#include "siphash24.h"
11#include "string-util.h"
12
13/* 0.0.0.0/0 */
14#define IN_ADDR_PREFIX_IPV4_ANY ((struct in_addr_prefix) { .family = AF_INET })
15/* ::/0 */
16#define IN_ADDR_PREFIX_IPV6_ANY ((struct in_addr_prefix) { .family = AF_INET6 })
17/* 127.0.0.0/8 */
18#define IN_ADDR_PREFIX_IPV4_LOCALHOST \
19 ((struct in_addr_prefix) { \
20 .family = AF_INET, \
21 .address.in.s_addr = htobe32(UINT32_C(127) << 24), \
22 .prefixlen = 8, \
23 })
24/* ::1/128 */
25#define IN_ADDR_PREFIX_IPV6_LOCALHOST \
26 ((struct in_addr_prefix) { \
27 .family = AF_INET6, \
28 .address.in6 = IN6ADDR_LOOPBACK_INIT, \
29 .prefixlen = 128, \
30 })
31/* 169.254.0.0/16 */
32#define IN_ADDR_PREFIX_IPV4_LINKLOCAL \
33 ((struct in_addr_prefix) { \
34 .family = AF_INET, \
35 .address.in.s_addr = htobe32((UINT32_C(169) << 24) | \
36 (UINT32_C(254) << 16)), \
37 .prefixlen = 16, \
38 })
39/* fe80::/64 */
40#define IN_ADDR_PREFIX_IPV6_LINKLOCAL \
41 ((struct in_addr_prefix) { \
42 .family = AF_INET6, \
43 .address.in6.s6_addr[0] = 0xfe, \
44 .address.in6.s6_addr[1] = 0x80, \
45 .prefixlen = 64, \
46 })
47/* 224.0.0.0/4 */
48#define IN_ADDR_PREFIX_IPV4_MULTICAST \
49 ((struct in_addr_prefix) { \
50 .family = AF_INET, \
51 .address.in.s_addr = htobe32((UINT32_C(224) << 24)), \
52 .prefixlen = 4, \
53 })
54/* ff00::/8 */
55#define IN_ADDR_PREFIX_IPV6_MULTICAST \
56 ((struct in_addr_prefix) { \
57 .family = AF_INET6, \
58 .address.in6.s6_addr[0] = 0xff, \
59 .prefixlen = 8, \
60 })
61
62static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
63 assert(a);
64 assert(state);
65
66 siphash24_compress_typesafe(a->family, state);
67 siphash24_compress_typesafe(a->prefixlen, state);
68 in_addr_hash_func(&a->address, a->family, state);
69}
70
71static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
72 int r;
73
74 assert(x);
75 assert(y);
76
77 r = CMP(x->family, y->family);
78 if (r != 0)
79 return r;
80
81 r = CMP(x->prefixlen, y->prefixlen);
82 if (r != 0)
83 return r;
84
85 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
86}
87
88DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
89DEFINE_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);
90
91int in_addr_prefix_add(Set **prefixes, const struct in_addr_prefix *prefix) {
92 struct in_addr_prefix *copy;
93
94 assert(prefixes);
95 assert(prefix);
96 assert(IN_SET(prefix->family, AF_INET, AF_INET6));
97
98 copy = newdup(struct in_addr_prefix, prefix, 1);
99 if (!copy)
100 return -ENOMEM;
101
102 (void) in_addr_mask(copy->family, &copy->address, copy->prefixlen);
103 return set_ensure_consume(prefixes, &in_addr_prefix_hash_ops_free, copy);
104}
105
106int 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;
113
114 SET_FOREACH(p, prefixes)
115 switch (p->family) {
116 case AF_INET:
117 assert(p->prefixlen <= 32);
118 if (p->prefixlen == 0)
119 ipv4_has_any = true;
120 else
121 ipv4_prefixlen_bits |= UINT32_C(1) << (p->prefixlen - 1);
122 break;
123 case AF_INET6:
124 assert(p->prefixlen <= 128);
125 if (p->prefixlen == 0)
126 ipv6_has_any = true;
127 else
128 ipv6_prefixlen_bits[(p->prefixlen - 1) / sizeof(uint64_t)] |=
129 UINT64_C(1) << ((p->prefixlen - 1) % sizeof(uint64_t));
130 break;
131 default:
132 assert_not_reached();
133 }
134
135 if (!ipv4_has_any)
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;
139
140 if (!ipv6_has_any)
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;
145
146 SET_FOREACH(p, prefixes) {
147 uint8_t *prefixlens;
148 bool covered;
149 size_t *n;
150
151 if (p->prefixlen == 0)
152 continue;
153
154 switch (p->family) {
155 case AF_INET:
156 prefixlens = ipv4_prefixlens;
157 n = &ipv4_n_prefixlens;
158 covered = ipv4_has_any;
159 break;
160 case AF_INET6:
161 prefixlens = ipv6_prefixlens;
162 n = &ipv6_n_prefixlens;
163 covered = ipv6_has_any;
164 break;
165 default:
166 assert_not_reached();
167 }
168
169 for (size_t i = 0; i < *n; i++) {
170 struct in_addr_prefix tmp;
171
172 if (covered)
173 break;
174
175 if (prefixlens[i] >= p->prefixlen)
176 break;
177
178 tmp = *p;
179 tmp.prefixlen = prefixlens[i];
180 (void) in_addr_mask(tmp.family, &tmp.address, tmp.prefixlen);
181
182 covered = set_contains(prefixes, &tmp);
183 }
184
185 if (covered)
186 free(set_remove(prefixes, p));
187 }
188
189 return 0;
190}
191
192int in_addr_prefixes_merge(Set **dest, Set *src) {
193 struct in_addr_prefix *p;
194 int r;
195
196 assert(dest);
197
198 SET_FOREACH(p, src) {
199 r = in_addr_prefix_add(dest, p);
200 if (r < 0)
201 return r;
202 }
203
204 return 0;
205}
206
207bool in_addr_prefixes_is_any(Set *prefixes) {
208 return
209 set_contains(prefixes, &IN_ADDR_PREFIX_IPV4_ANY) &&
210 set_contains(prefixes, &IN_ADDR_PREFIX_IPV6_ANY);
211}
212
213int config_parse_in_addr_prefixes(
214 const char *unit,
215 const char *filename,
216 unsigned line,
217 const char *section,
218 unsigned section_line,
219 const char *lvalue,
220 int ltype,
221 const char *rvalue,
222 void *data,
223 void *userdata) {
224
225 Set **prefixes = ASSERT_PTR(data);
226 int r;
227
228 assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
229
230 if (isempty(rvalue)) {
231 *prefixes = set_free(*prefixes);
232 return 0;
233 }
234
235 for (const char *p = rvalue;;) {
236 _cleanup_free_ char *word = NULL;
237
238 r = extract_first_word(&p, &word, NULL, 0);
239 if (r == -ENOMEM)
240 return log_oom();
241 if (r < 0) {
242 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
243 return 0;
244 }
245 if (r == 0)
246 return 0;
247
248 if (streq(word, "any")) {
249 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
250
251 if (ltype != AF_INET6) {
252 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_ANY);
253 if (r < 0)
254 return log_oom();
255 }
256
257 if (ltype != AF_INET) {
258 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_ANY);
259 if (r < 0)
260 return log_oom();
261 }
262
263 } else if (is_localhost(word)) {
264 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
265
266 if (ltype != AF_INET6) {
267 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LOCALHOST);
268 if (r < 0)
269 return log_oom();
270 }
271
272 if (ltype != AF_INET) {
273 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LOCALHOST);
274 if (r < 0)
275 return log_oom();
276 }
277
278 } else if (streq(word, "link-local")) {
279 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
280
281 if (ltype != AF_INET6) {
282 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LINKLOCAL);
283 if (r < 0)
284 return log_oom();
285 }
286
287 if (ltype != AF_INET) {
288 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LINKLOCAL);
289 if (r < 0)
290 return log_oom();
291 }
292
293 } else if (streq(word, "multicast")) {
294 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
295
296 if (ltype != AF_INET6) {
297 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_MULTICAST);
298 if (r < 0)
299 return log_oom();
300 }
301
302 if (ltype != AF_INET) {
303 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_MULTICAST);
304 if (r < 0)
305 return log_oom();
306 }
307
308 } else {
309 struct in_addr_prefix a;
310
311 if (ltype == AF_UNSPEC)
312 r = in_addr_prefix_from_string_auto(word, &a.family, &a.address, &a.prefixlen);
313 else {
314 a.family = ltype;
315 r = in_addr_prefix_from_string(word, a.family, &a.address, &a.prefixlen);
316 }
317 if (r < 0) {
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);
321 continue;
322 }
323
324 r = in_addr_prefix_add(prefixes, &a);
325 if (r < 0)
326 return log_oom();
327 }
328 }
329}