]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/in-addr-prefix-util.c
e23b8f9d13d57197c194d7cc289819b0a2240a3f
[thirdparty/systemd.git] / src / shared / in-addr-prefix-util.c
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
58 static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
59 assert(a);
60 assert(state);
61
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);
65 }
66
67 static 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
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);
86
87 int 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
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;
109
110 SET_FOREACH(p, prefixes)
111 switch (p->family) {
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
150 switch (p->family) {
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
188 int 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
203 bool 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
209 int 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
221 Set **prefixes = data;
222 int r;
223
224 assert(prefixes);
225 assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
226
227 if (isempty(rvalue)) {
228 *prefixes = set_free(*prefixes);
229 return 0;
230 }
231
232 for (const char *p = rvalue;;) {
233 _cleanup_free_ char *word = NULL;
234
235 r = extract_first_word(&p, &word, NULL, 0);
236 if (r == -ENOMEM)
237 return log_oom();
238 if (r < 0) {
239 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
240 return 0;
241 }
242 if (r == 0)
243 return 0;
244
245 if (streq(word, "any")) {
246 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
247
248 if (ltype != AF_INET6) {
249 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_ANY);
250 if (r < 0)
251 return log_oom();
252 }
253
254 if (ltype != AF_INET) {
255 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_ANY);
256 if (r < 0)
257 return log_oom();
258 }
259
260 } else if (is_localhost(word)) {
261 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
262
263 if (ltype != AF_INET6) {
264 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LOCALHOST);
265 if (r < 0)
266 return log_oom();
267 }
268
269 if (ltype != AF_INET) {
270 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LOCALHOST);
271 if (r < 0)
272 return log_oom();
273 }
274
275 } else if (streq(word, "link-local")) {
276 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
277
278 if (ltype != AF_INET6) {
279 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_LINKLOCAL);
280 if (r < 0)
281 return log_oom();
282 }
283
284 if (ltype != AF_INET) {
285 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_LINKLOCAL);
286 if (r < 0)
287 return log_oom();
288 }
289
290 } else if (streq(word, "multicast")) {
291 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
292
293 if (ltype != AF_INET6) {
294 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV4_MULTICAST);
295 if (r < 0)
296 return log_oom();
297 }
298
299 if (ltype != AF_INET) {
300 r = in_addr_prefix_add(prefixes, &IN_ADDR_PREFIX_IPV6_MULTICAST);
301 if (r < 0)
302 return log_oom();
303 }
304
305 } else {
306 struct in_addr_prefix a;
307
308 if (ltype == AF_UNSPEC)
309 r = in_addr_prefix_from_string_auto(word, &a.family, &a.address, &a.prefixlen);
310 else {
311 a.family = ltype;
312 r = in_addr_prefix_from_string(word, a.family, &a.address, &a.prefixlen);
313 }
314 if (r < 0) {
315 log_syntax(unit, LOG_WARNING, filename, line, r,
316 "Address prefix is invalid, ignoring assignment: %s", word);
317 continue;
318 }
319
320 r = in_addr_prefix_add(prefixes, &a);
321 if (r < 0)
322 return log_oom();
323 }
324 }
325 }