]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-manager.c
sd-rtnl: introduce sd_rtnl_new_from_netlink
[thirdparty/systemd.git] / src / network / networkd-manager.c
CommitLineData
f579559b
TG
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
091a364c 22#include <sys/socket.h>
bbf7c048 23#include <linux/if.h>
3bef724f 24
2dcf7ec6 25#include "conf-parser.h"
f579559b
TG
26#include "path-util.h"
27#include "networkd.h"
3be1d7e0 28#include "networkd-netdev.h"
0b1831c2 29#include "networkd-link.h"
e16cb2e4 30#include "network-internal.h"
f579559b 31#include "libudev-private.h"
7b77ed8c 32#include "udev-util.h"
50add290 33#include "rtnl-util.h"
3bef724f 34#include "mkdir.h"
60ad0c85 35#include "virt.h"
f579559b 36
505f8da7
TG
37#include "sd-rtnl.h"
38
be660c37
AR
39/* use 8 MB for receive socket kernel queue. */
40#define RCVBUF_SIZE (8*1024*1024)
41
2ad8416d
ZJS
42const char* const network_dirs[] = {
43 "/etc/systemd/network",
44 "/run/systemd/network",
45 "/usr/lib/systemd/network",
eed0eee8 46#ifdef HAVE_SPLIT_USR
2ad8416d
ZJS
47 "/lib/systemd/network",
48#endif
49 NULL};
50
11bf3cce
LP
51static int setup_default_address_pool(Manager *m) {
52 AddressPool *p;
53 int r;
54
55 assert(m);
56
57 /* Add in the well-known private address ranges. */
58
59 r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
60 if (r < 0)
61 return r;
62
63 r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
64 if (r < 0)
65 return r;
66
67 r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
68 if (r < 0)
69 return r;
70
71 r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
72 if (r < 0)
73 return r;
74
75 return 0;
76}
77
f579559b
TG
78int manager_new(Manager **ret) {
79 _cleanup_manager_free_ Manager *m = NULL;
80 int r;
81
82 m = new0(Manager, 1);
83 if (!m)
84 return -ENOMEM;
85
85b5673b 86 m->state_file = strdup("/run/systemd/netif/state");
bbf7c048
TG
87 if (!m->state_file)
88 return -ENOMEM;
89
afc6adb5 90 r = sd_event_default(&m->event);
f579559b
TG
91 if (r < 0)
92 return r;
93
cde93897
LP
94 sd_event_set_watchdog(m->event, true);
95
186fe1db
LP
96 sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
97 sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
98
897e184c
TG
99 r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR,
100 RTNLGRP_IPV6_IFADDR);
f579559b
TG
101 if (r < 0)
102 return r;
103
be660c37
AR
104 r = sd_rtnl_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
105 if (r < 0)
106 return r;
107
1346b1f0 108 r = sd_bus_default_system(&m->bus);
bcbca829 109 if (r < 0 && r != -ENOENT) /* TODO: drop when we can rely on kdbus */
1346b1f0
TG
110 return r;
111
60ad0c85
TG
112 /* udev does not initialize devices inside containers,
113 * so we rely on them being already initialized before
114 * entering the container */
505f8da7
TG
115 if (detect_container(NULL) <= 0) {
116 m->udev = udev_new();
117 if (!m->udev)
60ad0c85 118 return -ENOMEM;
505f8da7 119
60ad0c85
TG
120 m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
121 if (!m->udev_monitor)
122 return -ENOMEM;
123 }
f579559b 124
d5099efc 125 m->netdevs = hashmap_new(&string_hash_ops);
52433f6b 126 if (!m->netdevs)
02b59d57
TG
127 return -ENOMEM;
128
f579559b
TG
129 LIST_HEAD_INIT(m->networks);
130
11bf3cce
LP
131 r = setup_default_address_pool(m);
132 if (r < 0)
133 return r;
134
f579559b
TG
135 *ret = m;
136 m = NULL;
137
138 return 0;
139}
140
141void manager_free(Manager *m) {
0617ffab 142 Network *network;
1a436809 143 NetDev *netdev;
0617ffab 144 Link *link;
11bf3cce 145 AddressPool *pool;
0617ffab 146
624b5a63
TG
147 if (!m)
148 return;
149
bbf7c048
TG
150 free(m->state_file);
151
f579559b
TG
152 udev_monitor_unref(m->udev_monitor);
153 udev_unref(m->udev);
1346b1f0 154 sd_bus_unref(m->bus);
f579559b
TG
155 sd_event_source_unref(m->udev_event_source);
156 sd_event_unref(m->event);
0617ffab 157
0617ffab 158 while ((link = hashmap_first(m->links)))
14b746f7 159 link_unref(link);
f579559b 160 hashmap_free(m->links);
0617ffab 161
2292547a
TG
162 while ((network = m->networks))
163 network_free(network);
164
52433f6b 165 while ((netdev = hashmap_first(m->netdevs)))
14b746f7 166 netdev_unref(netdev);
52433f6b 167 hashmap_free(m->netdevs);
02b59d57 168
11bf3cce
LP
169 while ((pool = m->address_pools))
170 address_pool_free(pool);
171
f579559b
TG
172 sd_rtnl_unref(m->rtnl);
173
174 free(m);
175}
176
02b59d57
TG
177int manager_load_config(Manager *m) {
178 int r;
179
180 /* update timestamp */
2ad8416d 181 paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
02b59d57 182
52433f6b 183 r = netdev_load(m);
02b59d57
TG
184 if (r < 0)
185 return r;
186
187 r = network_load(m);
188 if (r < 0)
189 return r;
190
191 return 0;
192}
193
194bool manager_should_reload(Manager *m) {
2ad8416d 195 return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
02b59d57
TG
196}
197
505f8da7 198static int manager_udev_process_link(Manager *m, struct udev_device *device) {
11a7f229 199 Link *link = NULL;
667fcc6d 200 int r, ifindex;
f579559b 201
11a7f229
TG
202 assert(m);
203 assert(device);
f579559b 204
505f8da7
TG
205 if (!streq_ptr(udev_device_get_action(device), "add"))
206 return 0;
207
667fcc6d
TG
208 ifindex = udev_device_get_ifindex(device);
209 if (ifindex <= 0) {
210 log_debug("ignoring udev ADD event for device with invalid ifindex");
211 return 0;
212 }
505f8da7 213
667fcc6d
TG
214 r = link_get(m, ifindex, &link);
215 if (r == -ENODEV)
505f8da7 216 return 0;
667fcc6d
TG
217 else if (r < 0)
218 return r;
505f8da7
TG
219
220 r = link_initialized(link, device);
221 if (r < 0)
222 return r;
11a7f229 223
505f8da7
TG
224 return 0;
225}
f579559b 226
505f8da7
TG
227static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
228 Manager *m = userdata;
229 Link *link = NULL;
4d473d5d 230 NetDev *netdev = NULL;
f2236469 231 uint16_t type;
ca4e095a 232 const char *name;
505f8da7 233 int r, ifindex;
f579559b 234
505f8da7
TG
235 assert(rtnl);
236 assert(message);
f579559b
TG
237 assert(m);
238
45af44d4
TG
239 if (sd_rtnl_message_is_error(message)) {
240 r = sd_rtnl_message_get_errno(message);
241 if (r < 0)
242 log_warning_errno(r, "rtnl: could not receive link: %m");
243
244 return 0;
245 }
246
f2236469
TG
247 r = sd_rtnl_message_get_type(message, &type);
248 if (r < 0) {
45af44d4 249 log_warning_errno(r, "rtnl: could not get message type: %m");
f2236469
TG
250 return 0;
251 }
252
505f8da7 253 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
45af44d4
TG
254 if (r < 0) {
255 log_warning_errno(r, "rtnl: could not get ifindex: %m");
256 return 0;
257 } else if (ifindex <= 0) {
258 log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
505f8da7 259 return 0;
4d473d5d
TG
260 } else
261 link_get(m, ifindex, &link);
f579559b 262
505f8da7 263 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &name);
45af44d4
TG
264 if (r < 0) {
265 log_warning_errno(r, "rtnl: received link message without ifname: %m");
4d473d5d
TG
266 return 0;
267 } else
f2236469 268 netdev_get(m, name, &netdev);
4d473d5d
TG
269
270 switch (type) {
271 case RTM_NEWLINK:
272 if (!link) {
273 /* link is new, so add it */
274 r = link_add(m, message, &link);
275 if (r < 0) {
17d1f37d 276 log_warning_errno(r, "could not add new link: %m");
4d473d5d
TG
277 return 0;
278 }
279 }
280
281 if (netdev) {
282 /* netdev exists, so make sure the ifindex matches */
505f8da7
TG
283 r = netdev_set_ifindex(netdev, message);
284 if (r < 0) {
17d1f37d 285 log_warning_errno(r, "could not set ifindex on netdev: %m");
505f8da7
TG
286 return 0;
287 }
288 }
e1202047 289
f2236469
TG
290 r = link_update(link, message);
291 if (r < 0)
292 return 0;
4d473d5d
TG
293
294 break;
295
296 case RTM_DELLINK:
297 link_drop(link);
298 netdev_drop(netdev);
299
300 break;
301
302 default:
303 assert_not_reached("Received invalid RTNL message type.");
f2236469 304 }
505f8da7
TG
305
306 return 1;
307}
308
309int manager_rtnl_enumerate_links(Manager *m) {
310 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
311 sd_rtnl_message *link;
45af44d4 312 int r;
505f8da7
TG
313
314 assert(m);
315 assert(m->rtnl);
316
317 r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
318 if (r < 0)
319 return r;
320
321 r = sd_rtnl_message_request_dump(req, true);
f579559b 322 if (r < 0)
bf5332d2 323 return r;
f579559b 324
505f8da7
TG
325 r = sd_rtnl_call(m->rtnl, req, 0, &reply);
326 if (r < 0)
327 return r;
f579559b 328
505f8da7 329 for (link = reply; link; link = sd_rtnl_message_next(link)) {
45af44d4 330 int k;
505f8da7 331
45af44d4 332 k = manager_rtnl_process_link(m->rtnl, link, m);
505f8da7 333 if (k < 0)
45af44d4
TG
334 r = k;
335 }
336
337 return r;
338}
505f8da7 339
45af44d4
TG
340int manager_rtnl_enumerate_addresses(Manager *m) {
341 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
342 sd_rtnl_message *addr;
343 int r;
f579559b 344
45af44d4
TG
345 assert(m);
346 assert(m->rtnl);
347
348 r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
349 if (r < 0)
350 return r;
351
352 r = sd_rtnl_message_request_dump(req, true);
353 if (r < 0)
354 return r;
355
356 r = sd_rtnl_call(m->rtnl, req, 0, &reply);
357 if (r < 0)
358 return r;
359
360 for (addr = reply; addr; addr = sd_rtnl_message_next(addr)) {
361 int k;
362
363 k = link_rtnl_process_address(m->rtnl, addr, m);
f579559b
TG
364 if (k < 0)
365 r = k;
366 }
367
f579559b
TG
368 return r;
369}
370
371static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
372 Manager *m = userdata;
373 struct udev_monitor *monitor = m->udev_monitor;
7b77ed8c 374 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f579559b
TG
375
376 device = udev_monitor_receive_device(monitor);
377 if (!device)
378 return -ENOMEM;
379
505f8da7 380 manager_udev_process_link(m, device);
f579559b
TG
381 return 0;
382}
383
384int manager_udev_listen(Manager *m) {
385 int r;
386
505f8da7
TG
387 if (detect_container(NULL) > 0)
388 return 0;
389
390 assert(m->udev_monitor);
391
f579559b 392 r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
f647962d
MS
393 if (r < 0)
394 return log_error_errno(r, "Could not add udev monitor filter: %m");
f579559b 395
f579559b
TG
396 r = udev_monitor_enable_receiving(m->udev_monitor);
397 if (r < 0) {
398 log_error("Could not enable udev monitor");
399 return r;
400 }
401
402 r = sd_event_add_io(m->event,
151b9b96 403 &m->udev_event_source,
f579559b
TG
404 udev_monitor_get_fd(m->udev_monitor),
405 EPOLLIN, manager_dispatch_link_udev,
151b9b96 406 m);
f579559b
TG
407 if (r < 0)
408 return r;
409
356779df 410 r = sd_event_source_set_description(m->udev_event_source, "networkd-udev");
9021bb9f
TG
411 if (r < 0)
412 return r;
413
f579559b
TG
414 return 0;
415}
f882c247
TG
416
417int manager_rtnl_listen(Manager *m) {
418 int r;
419
5da8149f
TG
420 assert(m);
421
f882c247
TG
422 r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
423 if (r < 0)
424 return r;
425
dd3efc09
TG
426 r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
427 if (r < 0)
428 return r;
429
f2236469
TG
430 r = sd_rtnl_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m);
431 if (r < 0)
432 return r;
433
fbbeb65a 434 r = sd_rtnl_add_match(m->rtnl, RTM_NEWADDR, &link_rtnl_process_address, m);
2e9f08ea
TG
435 if (r < 0)
436 return r;
437
fbbeb65a 438 r = sd_rtnl_add_match(m->rtnl, RTM_DELADDR, &link_rtnl_process_address, m);
2e9f08ea
TG
439 if (r < 0)
440 return r;
441
f882c247
TG
442 return 0;
443}
3bef724f 444
1346b1f0
TG
445int manager_bus_listen(Manager *m) {
446 int r;
447
bcbca829
TG
448 assert(m->event);
449
450 if (!m->bus) /* TODO: drop when we can rely on kdbus */
451 return 0;
452
1346b1f0
TG
453 r = sd_bus_attach_event(m->bus, m->event, 0);
454 if (r < 0)
455 return r;
456
457 return 0;
458}
459
c0c743cb
LP
460static int set_put_in_addr(Set *s, const struct in_addr *address) {
461 char *p;
462 int r;
463
464 assert(s);
465
466 r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
467 if (r < 0)
468 return r;
469
470 r = set_consume(s, p);
471 if (r == -EEXIST)
472 return 0;
473
474 return r;
475}
476
477static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
478 int r, i, c = 0;
479
480 assert(s);
481 assert(n <= 0 || addresses);
482
483 for (i = 0; i < n; i++) {
484 r = set_put_in_addr(s, addresses+i);
485 if (r < 0)
486 return r;
487
488 c += r;
489 }
490
491 return c;
492}
493
8612e936
LP
494static void print_string_set(FILE *f, const char *field, Set *s) {
495 bool space = false;
496 Iterator i;
497 char *p;
498
499 if (set_isempty(s))
500 return;
501
502 fputs(field, f);
503
504 SET_FOREACH(p, s, i) {
505 if (space)
506 fputc(' ', f);
507 fputs(p, f);
508 space = true;
509 }
510 fputc('\n', f);
511}
512
bbf7c048 513int manager_save(Manager *m) {
8612e936 514 _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
bbf7c048
TG
515 Link *link;
516 Iterator i;
517 _cleanup_free_ char *temp_path = NULL;
518 _cleanup_fclose_ FILE *f = NULL;
d3df0e39 519 LinkOperationalState operstate = LINK_OPERSTATE_OFF;
e375dcde 520 const char *operstate_str;
bbf7c048
TG
521 int r;
522
523 assert(m);
524 assert(m->state_file);
525
c0c743cb 526 /* We add all NTP and DNS server to a set, to filter out duplicates */
d5099efc 527 dns = set_new(&string_hash_ops);
c0c743cb
LP
528 if (!dns)
529 return -ENOMEM;
530
d5099efc 531 ntp = set_new(&string_hash_ops);
c0c743cb
LP
532 if (!ntp)
533 return -ENOMEM;
534
d5099efc 535 domains = set_new(&string_hash_ops);
8612e936
LP
536 if (!domains)
537 return -ENOMEM;
538
bbf7c048
TG
539 HASHMAP_FOREACH(link, m->links, i) {
540 if (link->flags & IFF_LOOPBACK)
541 continue;
542
e375dcde
TG
543 if (link->operstate > operstate)
544 operstate = link->operstate;
c0c743cb
LP
545
546 if (!link->network)
547 continue;
548
549 /* First add the static configured entries */
550 r = set_put_strdupv(dns, link->network->dns);
551 if (r < 0)
552 return r;
553
554 r = set_put_strdupv(ntp, link->network->ntp);
555 if (r < 0)
556 return r;
557
8612e936
LP
558 r = set_put_strdupv(domains, link->network->domains);
559 if (r < 0)
560 return r;
561
c0c743cb
LP
562 if (!link->dhcp_lease)
563 continue;
564
565 /* Secondly, add the entries acquired via DHCP */
566 if (link->network->dhcp_dns) {
567 const struct in_addr *addresses;
568
569 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
570 if (r > 0) {
571 r = set_put_in_addrv(dns, addresses, r);
572 if (r < 0)
573 return r;
8612e936 574 } else if (r < 0 && r != -ENOENT)
c0c743cb
LP
575 return r;
576 }
577
578 if (link->network->dhcp_ntp) {
579 const struct in_addr *addresses;
580
581 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
582 if (r > 0) {
583 r = set_put_in_addrv(ntp, addresses, r);
584 if (r < 0)
585 return r;
8612e936
LP
586 } else if (r < 0 && r != -ENOENT)
587 return r;
588 }
589
590 if (link->network->dhcp_domains) {
591 const char *domainname;
592
593 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
594 if (r >= 0) {
595 r = set_put_strdup(domains, domainname);
596 if (r < 0)
597 return r;
c0c743cb
LP
598 } else if (r != -ENOENT)
599 return r;
600 }
bbf7c048
TG
601 }
602
e375dcde
TG
603 operstate_str = link_operstate_to_string(operstate);
604 assert(operstate_str);
bbf7c048
TG
605
606 r = fopen_temporary(m->state_file, &f, &temp_path);
607 if (r < 0)
c2d6bd61 608 return r;
bbf7c048
TG
609
610 fchmod(fileno(f), 0644);
611
612 fprintf(f,
613 "# This is private data. Do not parse.\n"
e375dcde 614 "OPER_STATE=%s\n", operstate_str);
bbf7c048 615
8612e936
LP
616 print_string_set(f, "DNS=", dns);
617 print_string_set(f, "NTP=", ntp);
618 print_string_set(f, "DOMAINS=", domains);
c0c743cb 619
c2d6bd61
LP
620 r = fflush_and_check(f);
621 if (r < 0)
622 goto fail;
bbf7c048 623
c2d6bd61 624 if (rename(temp_path, m->state_file) < 0) {
bbf7c048 625 r = -errno;
c2d6bd61 626 goto fail;
bbf7c048
TG
627 }
628
c2d6bd61 629 return 0;
bbf7c048 630
c2d6bd61 631fail:
da927ba9 632 log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
c2d6bd61
LP
633 unlink(m->state_file);
634 unlink(temp_path);
bbf7c048
TG
635 return r;
636}
11bf3cce 637
0dd25fb9 638int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
11bf3cce
LP
639 AddressPool *p;
640 int r;
641
642 assert(m);
643 assert(prefixlen > 0);
644 assert(found);
645
646 LIST_FOREACH(address_pools, p, m->address_pools) {
647 if (p->family != family)
648 continue;
649
650 r = address_pool_acquire(p, prefixlen, found);
651 if (r != 0)
652 return r;
653 }
654
655 return 0;
656}
cb9fc36a
LP
657
658const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
659 if (b == ADDRESS_FAMILY_YES ||
660 b == ADDRESS_FAMILY_NO)
661 return yes_no(b == ADDRESS_FAMILY_YES);
662
663 if (b == ADDRESS_FAMILY_IPV4)
664 return "ipv4";
665 if (b == ADDRESS_FAMILY_IPV6)
666 return "ipv6";
667
668 return NULL;
669}
670
671AddressFamilyBoolean address_family_boolean_from_string(const char *s) {
672 int r;
673
674 /* Make this a true superset of a boolean */
675
676 r = parse_boolean(s);
677 if (r > 0)
678 return ADDRESS_FAMILY_YES;
679 if (r == 0)
680 return ADDRESS_FAMILY_NO;
681
682 if (streq(s, "ipv4"))
683 return ADDRESS_FAMILY_IPV4;
684 if (streq(s, "ipv6"))
685 return ADDRESS_FAMILY_IPV6;
686
687 return _ADDRESS_FAMILY_BOOLEAN_INVALID;
688}
769d324c
LP
689
690DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option");