]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-address.c
networkd: address - rework firewall rules lifetime
[thirdparty/systemd.git] / src / network / networkd-address.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <net/if.h>
23
24 #include "conf-parser.h"
25 #include "firewall-util.h"
26 #include "netlink-util.h"
27 #include "set.h"
28 #include "utf8.h"
29 #include "util.h"
30
31 #include "networkd.h"
32 #include "networkd-address.h"
33
34 int address_new(Address **ret) {
35 _cleanup_address_free_ Address *address = NULL;
36
37 address = new0(Address, 1);
38 if (!address)
39 return -ENOMEM;
40
41 address->family = AF_UNSPEC;
42 address->scope = RT_SCOPE_UNIVERSE;
43 address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
44 address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
45
46 *ret = address;
47 address = NULL;
48
49 return 0;
50 }
51
52 int address_new_static(Network *network, unsigned section, Address **ret) {
53 _cleanup_address_free_ Address *address = NULL;
54 int r;
55
56 if (section) {
57 address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
58 if (address) {
59 *ret = address;
60 address = NULL;
61
62 return 0;
63 }
64 }
65
66 r = address_new(&address);
67 if (r < 0)
68 return r;
69
70 address->network = network;
71
72 LIST_APPEND(addresses, network->static_addresses, address);
73
74 if (section) {
75 address->section = section;
76 hashmap_put(network->addresses_by_section,
77 UINT_TO_PTR(address->section), address);
78 }
79
80 *ret = address;
81 address = NULL;
82
83 return 0;
84 }
85
86 void address_free(Address *address) {
87 if (!address)
88 return;
89
90 if (address->network) {
91 LIST_REMOVE(addresses, address->network->static_addresses, address);
92
93 if (address->section)
94 hashmap_remove(address->network->addresses_by_section,
95 UINT_TO_PTR(address->section));
96 }
97
98 if (address->link)
99 set_remove(address->link->addresses, address);
100
101 free(address);
102 }
103
104 static void address_hash_func(const void *b, struct siphash *state) {
105 const Address *a = b;
106
107 assert(a);
108
109 siphash24_compress(&a->family, sizeof(a->family), state);
110
111 switch (a->family) {
112 case AF_INET:
113 siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
114
115 /* peer prefix */
116 if (a->prefixlen != 0) {
117 uint32_t prefix;
118
119 if (a->in_addr_peer.in.s_addr != 0)
120 prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
121 else
122 prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
123
124 siphash24_compress(&prefix, sizeof(prefix), state);
125 }
126
127 /* fallthrough */
128 case AF_INET6:
129 /* local address */
130 siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
131
132 break;
133 default:
134 /* treat any other address family as AF_UNSPEC */
135 break;
136 }
137 }
138
139 static int address_compare_func(const void *c1, const void *c2) {
140 const Address *a1 = c1, *a2 = c2;
141
142 if (a1->family < a2->family)
143 return -1;
144 if (a1->family > a2->family)
145 return 1;
146
147 switch (a1->family) {
148 /* use the same notion of equality as the kernel does */
149 case AF_INET:
150 if (a1->prefixlen < a2->prefixlen)
151 return -1;
152 if (a1->prefixlen > a2->prefixlen)
153 return 1;
154
155 /* compare the peer prefixes */
156 if (a1->prefixlen != 0) {
157 /* make sure we don't try to shift by 32.
158 * See ISO/IEC 9899:TC3 ยง 6.5.7.3. */
159 uint32_t b1, b2;
160
161 if (a1->in_addr_peer.in.s_addr != 0)
162 b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
163 else
164 b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen);
165
166 if (a2->in_addr_peer.in.s_addr != 0)
167 b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
168 else
169 b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
170
171 if (b1 < b2)
172 return -1;
173 if (b1 > b2)
174 return 1;
175 }
176
177 /* fall-through */
178 case AF_INET6:
179 return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
180 default:
181 /* treat any other address family as AF_UNSPEC */
182 return 0;
183 }
184 }
185
186 static const struct hash_ops address_hash_ops = {
187 .hash = address_hash_func,
188 .compare = address_compare_func
189 };
190
191 bool address_equal(Address *a1, Address *a2) {
192 if (a1 == a2)
193 return true;
194
195 if (!a1 || !a2)
196 return false;
197
198 return address_compare_func(a1, a2) == 0;
199 }
200
201 static int address_establish(Address *address, Link *link) {
202 bool masq;
203 int r;
204
205 assert(address);
206 assert(link);
207
208 masq = link->network &&
209 link->network->ip_masquerade &&
210 address->family == AF_INET &&
211 address->scope < RT_SCOPE_LINK;
212
213 /* Add firewall entry if this is requested */
214 if (address->ip_masquerade_done != masq) {
215 union in_addr_union masked = address->in_addr;
216 in_addr_mask(address->family, &masked, address->prefixlen);
217
218 r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
219 if (r < 0)
220 log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
221
222 address->ip_masquerade_done = masq;
223 }
224
225 return 0;
226 }
227
228 int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
229 _cleanup_address_free_ Address *address = NULL;
230 int r;
231
232 assert(link);
233 assert(in_addr);
234 assert(ret);
235
236 r = address_new(&address);
237 if (r < 0)
238 return r;
239
240 address->family = family;
241 address->in_addr = *in_addr;
242 address->prefixlen = prefixlen;
243
244 r = set_ensure_allocated(&link->addresses, &address_hash_ops);
245 if (r < 0)
246 return r;
247
248 r = set_put(link->addresses, address);
249 if (r < 0)
250 return r;
251
252 address->link = link;
253
254 *ret = address;
255 address = NULL;
256
257 return 0;
258 }
259
260 static int address_release(Address *address) {
261 int r;
262
263 assert(address);
264 assert(address->link);
265
266 /* Remove masquerading firewall entry if it was added */
267 if (address->ip_masquerade_done) {
268 union in_addr_union masked = address->in_addr;
269 in_addr_mask(address->family, &masked, address->prefixlen);
270
271 r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
272 if (r < 0)
273 log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m");
274
275 address->ip_masquerade_done = false;
276 }
277
278 return 0;
279 }
280
281 int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) {
282 bool ready;
283
284 assert(address);
285 assert(cinfo);
286
287 ready = address_is_ready(address);
288
289 address->flags = flags;
290 address->scope = scope;
291 address->cinfo = *cinfo;
292
293 if (!ready && address_is_ready(address) && address->link)
294 link_check_ready(address->link);
295
296 return 0;
297 }
298
299 int address_drop(Address *address) {
300 Link *link;
301 bool ready;
302
303 assert(address);
304
305 ready = address_is_ready(address);
306 link = address->link;
307
308 address_release(address);
309 address_free(address);
310
311 if (link && !ready)
312 link_check_ready(link);
313
314 return 0;
315 }
316
317 int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
318 Address address = {}, *existing;
319
320 assert(link);
321 assert(in_addr);
322 assert(ret);
323
324 address.family = family;
325 address.in_addr = *in_addr;
326 address.prefixlen = prefixlen;
327
328 existing = set_get(link->addresses, &address);
329 if (!existing)
330 return -ENOENT;
331
332 *ret = existing;
333
334 return 0;
335 }
336
337 int address_remove(Address *address, Link *link,
338 sd_netlink_message_handler_t callback) {
339 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
340 int r;
341
342 assert(address);
343 assert(address->family == AF_INET || address->family == AF_INET6);
344 assert(link);
345 assert(link->ifindex > 0);
346 assert(link->manager);
347 assert(link->manager->rtnl);
348
349 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
350 link->ifindex, address->family);
351 if (r < 0)
352 return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
353
354 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
355 if (r < 0)
356 return log_error_errno(r, "Could not set prefixlen: %m");
357
358 if (address->family == AF_INET)
359 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
360 else if (address->family == AF_INET6)
361 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
362 if (r < 0)
363 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
364
365 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
366 if (r < 0)
367 return log_error_errno(r, "Could not send rtnetlink message: %m");
368
369 link_ref(link);
370
371 return 0;
372 }
373
374 static int address_acquire(Link *link, Address *original, Address **ret) {
375 union in_addr_union in_addr = {};
376 struct in_addr broadcast = {};
377 _cleanup_address_free_ Address *na = NULL;
378 int r;
379
380 assert(link);
381 assert(original);
382 assert(ret);
383
384 /* Something useful was configured? just use it */
385 if (in_addr_is_null(original->family, &original->in_addr) <= 0)
386 return 0;
387
388 /* The address is configured to be 0.0.0.0 or [::] by the user?
389 * Then let's acquire something more useful from the pool. */
390 r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
391 if (r < 0)
392 return log_link_error_errno(link, r, "Failed to acquire address from pool: %m");
393 if (r == 0) {
394 log_link_error(link, "Couldn't find free address for interface, all taken.");
395 return -EBUSY;
396 }
397
398 if (original->family == AF_INET) {
399 /* Pick first address in range for ourselves ... */
400 in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
401
402 /* .. and use last as broadcast address */
403 broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
404 } else if (original->family == AF_INET6)
405 in_addr.in6.s6_addr[15] |= 1;
406
407 r = address_new(&na);
408 if (r < 0)
409 return r;
410
411 na->family = original->family;
412 na->prefixlen = original->prefixlen;
413 na->scope = original->scope;
414 na->cinfo = original->cinfo;
415
416 if (original->label) {
417 na->label = strdup(original->label);
418 if (!na->label)
419 return -ENOMEM;
420 }
421
422 na->broadcast = broadcast;
423 na->in_addr = in_addr;
424
425 LIST_PREPEND(addresses, link->pool_addresses, na);
426
427 *ret = na;
428 na = NULL;
429
430 return 0;
431 }
432
433 int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update) {
434 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
435 int r;
436
437 assert(address);
438 assert(address->family == AF_INET || address->family == AF_INET6);
439 assert(link);
440 assert(link->ifindex > 0);
441 assert(link->manager);
442 assert(link->manager->rtnl);
443
444 r = address_acquire(link, address, &address);
445 if (r < 0)
446 return r;
447
448 if (update)
449 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
450 link->ifindex, address->family);
451 else
452 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
453 link->ifindex, address->family);
454 if (r < 0)
455 return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
456
457 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
458 if (r < 0)
459 return log_error_errno(r, "Could not set prefixlen: %m");
460
461 address->flags |= IFA_F_PERMANENT;
462
463 r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
464 if (r < 0)
465 return log_error_errno(r, "Could not set flags: %m");
466
467 if (address->flags & ~0xff) {
468 r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
469 if (r < 0)
470 return log_error_errno(r, "Could not set extended flags: %m");
471 }
472
473 r = sd_rtnl_message_addr_set_scope(req, address->scope);
474 if (r < 0)
475 return log_error_errno(r, "Could not set scope: %m");
476
477 if (address->family == AF_INET)
478 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
479 else if (address->family == AF_INET6)
480 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
481 if (r < 0)
482 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
483
484 if (!in_addr_is_null(address->family, &address->in_addr_peer)) {
485 if (address->family == AF_INET)
486 r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in);
487 else if (address->family == AF_INET6)
488 r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6);
489 if (r < 0)
490 return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m");
491 } else {
492 if (address->family == AF_INET) {
493 r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
494 if (r < 0)
495 return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
496 }
497 }
498
499 if (address->label) {
500 r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
501 if (r < 0)
502 return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
503 }
504
505 r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO,
506 &address->cinfo);
507 if (r < 0)
508 return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
509
510 r = address_establish(address, link);
511 if (r < 0)
512 return r;
513
514 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
515 if (r < 0) {
516 address_release(address);
517 return log_error_errno(r, "Could not send rtnetlink message: %m");
518 }
519
520 link_ref(link);
521
522 return 0;
523 }
524
525 int config_parse_broadcast(
526 const char *unit,
527 const char *filename,
528 unsigned line,
529 const char *section,
530 unsigned section_line,
531 const char *lvalue,
532 int ltype,
533 const char *rvalue,
534 void *data,
535 void *userdata) {
536
537 Network *network = userdata;
538 _cleanup_address_free_ Address *n = NULL;
539 int r;
540
541 assert(filename);
542 assert(section);
543 assert(lvalue);
544 assert(rvalue);
545 assert(data);
546
547 r = address_new_static(network, section_line, &n);
548 if (r < 0)
549 return r;
550
551 if (n->family == AF_INET6) {
552 log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
553 return 0;
554 }
555
556 r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
557 if (r < 0) {
558 log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue);
559 return 0;
560 }
561
562 n->family = AF_INET;
563 n = NULL;
564
565 return 0;
566 }
567
568 int config_parse_address(const char *unit,
569 const char *filename,
570 unsigned line,
571 const char *section,
572 unsigned section_line,
573 const char *lvalue,
574 int ltype,
575 const char *rvalue,
576 void *data,
577 void *userdata) {
578
579 Network *network = userdata;
580 _cleanup_address_free_ Address *n = NULL;
581 const char *address, *e;
582 union in_addr_union buffer;
583 int r, f;
584
585 assert(filename);
586 assert(section);
587 assert(lvalue);
588 assert(rvalue);
589 assert(data);
590
591 if (streq(section, "Network")) {
592 /* we are not in an Address section, so treat
593 * this as the special '0' section */
594 section_line = 0;
595 }
596
597 r = address_new_static(network, section_line, &n);
598 if (r < 0)
599 return r;
600
601 /* Address=address/prefixlen */
602
603 /* prefixlen */
604 e = strchr(rvalue, '/');
605 if (e) {
606 unsigned i;
607
608 r = safe_atou(e + 1, &i);
609 if (r < 0) {
610 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
611 return 0;
612 }
613
614 n->prefixlen = (unsigned char) i;
615
616 address = strndupa(rvalue, e - rvalue);
617 } else
618 address = rvalue;
619
620 r = in_addr_from_string_auto(address, &f, &buffer);
621 if (r < 0) {
622 log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
623 return 0;
624 }
625
626 if (!e && f == AF_INET) {
627 r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
628 if (r < 0) {
629 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
630 return 0;
631 }
632 }
633
634 if (n->family != AF_UNSPEC && f != n->family) {
635 log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
636 return 0;
637 }
638
639 n->family = f;
640
641 if (streq(lvalue, "Address"))
642 n->in_addr = buffer;
643 else
644 n->in_addr_peer = buffer;
645
646 if (n->family == AF_INET && n->broadcast.s_addr == 0)
647 n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen);
648
649 n = NULL;
650
651 return 0;
652 }
653
654 int config_parse_label(const char *unit,
655 const char *filename,
656 unsigned line,
657 const char *section,
658 unsigned section_line,
659 const char *lvalue,
660 int ltype,
661 const char *rvalue,
662 void *data,
663 void *userdata) {
664 Network *network = userdata;
665 _cleanup_address_free_ Address *n = NULL;
666 char *label;
667 int r;
668
669 assert(filename);
670 assert(section);
671 assert(lvalue);
672 assert(rvalue);
673 assert(data);
674
675 r = address_new_static(network, section_line, &n);
676 if (r < 0)
677 return r;
678
679 label = strdup(rvalue);
680 if (!label)
681 return log_oom();
682
683 if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
684 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
685 free(label);
686 return 0;
687 }
688
689 free(n->label);
690 if (*label)
691 n->label = label;
692 else {
693 free(label);
694 n->label = NULL;
695 }
696
697 n = NULL;
698
699 return 0;
700 }
701
702 bool address_is_ready(const Address *a) {
703 assert(a);
704
705 return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
706 }