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 
in_addr_prefix_hash_func(const struct in_addr_prefix * a,struct siphash * state)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 
in_addr_prefix_compare_func(const struct in_addr_prefix * x,const struct in_addr_prefix * y)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 
in_addr_prefix_add(Set ** prefixes,const struct in_addr_prefix * prefix)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 
in_addr_prefixes_reduce(Set * prefixes)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 
in_addr_prefixes_merge(Set ** dest,Set * src)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 
in_addr_prefixes_is_any(Set * prefixes)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 
config_parse_in_addr_prefixes(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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 }
326