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