]>
Commit | Line | Data |
---|---|---|
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 | ||
62 | static 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 | ||
71 | static 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 | ||
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); | |
90 | ||
91 | int 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, ©->address, copy->prefixlen); | |
103 | return set_ensure_consume(prefixes, &in_addr_prefix_hash_ops_free, copy); | |
104 | } | |
105 | ||
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; | |
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 | ||
192 | int 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 | ||
207 | bool 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 | ||
213 | int 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 | } |