]>
Commit | Line | Data |
---|---|---|
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 | ||
58 | static 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 | ||
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, ©->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) | |
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 | ||
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 | ||
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 | } |