]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-manager.c
dhcp: normalize DHCP host and domain names from leases
[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"
f579559b 30#include "libudev-private.h"
7b77ed8c 31#include "udev-util.h"
1c4baffc 32#include "netlink-util.h"
a97dcc12
TG
33#include "bus-util.h"
34#include "def.h"
60ad0c85 35#include "virt.h"
f579559b 36
1c4baffc 37#include "sd-netlink.h"
5544ee85 38#include "sd-daemon.h"
505f8da7 39
be660c37
AR
40/* use 8 MB for receive socket kernel queue. */
41#define RCVBUF_SIZE (8*1024*1024)
42
2ad8416d
ZJS
43const char* const network_dirs[] = {
44 "/etc/systemd/network",
45 "/run/systemd/network",
46 "/usr/lib/systemd/network",
eed0eee8 47#ifdef HAVE_SPLIT_USR
2ad8416d
ZJS
48 "/lib/systemd/network",
49#endif
50 NULL};
51
11bf3cce
LP
52static int setup_default_address_pool(Manager *m) {
53 AddressPool *p;
54 int r;
55
56 assert(m);
57
58 /* Add in the well-known private address ranges. */
59
60 r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
61 if (r < 0)
62 return r;
63
64 r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
65 if (r < 0)
66 return r;
67
68 r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
69 if (r < 0)
70 return r;
71
72 r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
73 if (r < 0)
74 return r;
75
76 return 0;
77}
78
9c0a72f9
TG
79static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
80 Manager *m = userdata;
81
82 assert(s);
83 assert(m);
84
85 m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
86
87 manager_connect_bus(m);
88
89 return 0;
90}
91
92static int manager_reset_all(Manager *m) {
93 Link *link;
94 Iterator i;
95 int r;
96
97 assert(m);
98
99 HASHMAP_FOREACH(link, m->links, i) {
100 r = link_carrier_reset(link);
101 if (r < 0)
102 log_link_warning_errno(link, r, "could not reset carrier: %m");
103 }
104
105 return 0;
106}
107
19070062 108static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
9c0a72f9
TG
109 Manager *m = userdata;
110 int b, r;
111
19070062 112 assert(message);
9c0a72f9
TG
113
114 r = sd_bus_message_read(message, "b", &b);
115 if (r < 0) {
116 log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
117 return 0;
118 }
119
120 if (b)
121 return 0;
122
123 log_debug("Coming back from suspend, resetting all connections...");
124
125 manager_reset_all(m);
126
127 return 0;
128}
129
130int manager_connect_bus(Manager *m) {
131 int r;
132
133 assert(m);
134
135 r = sd_bus_default_system(&m->bus);
136 if (r == -ENOENT) {
137 /* We failed to connect? Yuck, we must be in early
138 * boot. Let's try in 5s again. As soon as we have
139 * kdbus we can stop doing this... */
140
141 log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
142
143 r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
144 if (r < 0)
145 return log_error_errno(r, "Failed to install bus reconnect time event: %m");
146
147 return 0;
7d6884b6
TA
148 }
149
150 if (r < 0)
9c0a72f9
TG
151 return r;
152
9c0a72f9
TG
153 r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
154 "type='signal',"
155 "sender='org.freedesktop.login1',"
156 "interface='org.freedesktop.login1.Manager',"
157 "member='PrepareForSleep',"
158 "path='/org/freedesktop/login1'",
159 match_prepare_for_sleep,
160 m);
161 if (r < 0)
162 return log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
163
e331e246
TG
164 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m);
165 if (r < 0)
166 return log_error_errno(r, "Failed to add manager object vtable: %m");
167
168 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.Link", link_vtable, link_object_find, m);
169 if (r < 0)
170 return log_error_errno(r, "Failed to add link object vtable: %m");
171
172 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m);
173 if (r < 0)
174 return log_error_errno(r, "Failed to add link enumerator: %m");
3175fcde
TG
175
176 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/network", "org.freedesktop.network1.Network", network_vtable, network_object_find, m);
177 if (r < 0)
178 return log_error_errno(r, "Failed to add network object vtable: %m");
179
180 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/network", network_node_enumerator, m);
181 if (r < 0)
182 return log_error_errno(r, "Failed to add network enumerator: %m");
e331e246
TG
183
184 r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0);
185 if (r < 0)
186 return log_error_errno(r, "Failed to register name: %m");
187
188 r = sd_bus_attach_event(m->bus, m->event, 0);
189 if (r < 0)
190 return log_error_errno(r, "Failed to attach bus to event loop: %m");
191
9c0a72f9
TG
192 return 0;
193}
194
5fae368b
TG
195static int manager_udev_process_link(Manager *m, struct udev_device *device) {
196 Link *link = NULL;
197 int r, ifindex;
5544ee85 198
5fae368b
TG
199 assert(m);
200 assert(device);
5544ee85 201
5fae368b
TG
202 if (!streq_ptr(udev_device_get_action(device), "add"))
203 return 0;
5544ee85 204
5fae368b
TG
205 ifindex = udev_device_get_ifindex(device);
206 if (ifindex <= 0) {
207 log_debug("ignoring udev ADD event for device with invalid ifindex");
208 return 0;
5544ee85
TG
209 }
210
5fae368b
TG
211 r = link_get(m, ifindex, &link);
212 if (r == -ENODEV)
213 return 0;
214 else if (r < 0)
f579559b
TG
215 return r;
216
5fae368b 217 r = link_initialized(link, device);
44de0efc
LP
218 if (r < 0)
219 return r;
220
5fae368b
TG
221 return 0;
222}
be660c37 223
5fae368b
TG
224static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
225 Manager *m = userdata;
226 struct udev_monitor *monitor = m->udev_monitor;
227 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f579559b 228
5fae368b
TG
229 device = udev_monitor_receive_device(monitor);
230 if (!device)
02b59d57
TG
231 return -ENOMEM;
232
5fae368b 233 manager_udev_process_link(m, device);
f579559b
TG
234 return 0;
235}
236
5fae368b
TG
237static int manager_connect_udev(Manager *m) {
238 int r;
f579559b 239
5fae368b
TG
240 /* udev does not initialize devices inside containers,
241 * so we rely on them being already initialized before
242 * entering the container */
243 if (detect_container(NULL) > 0)
244 return 0;
f579559b 245
5fae368b
TG
246 m->udev = udev_new();
247 if (!m->udev)
248 return -ENOMEM;
02b59d57 249
5fae368b
TG
250 m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
251 if (!m->udev_monitor)
252 return -ENOMEM;
02b59d57 253
5fae368b 254 r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
02b59d57 255 if (r < 0)
5fae368b 256 return log_error_errno(r, "Could not add udev monitor filter: %m");
02b59d57 257
5fae368b
TG
258 r = udev_monitor_enable_receiving(m->udev_monitor);
259 if (r < 0) {
260 log_error("Could not enable udev monitor");
02b59d57 261 return r;
667fcc6d 262 }
505f8da7 263
5fae368b
TG
264 r = sd_event_add_io(m->event,
265 &m->udev_event_source,
266 udev_monitor_get_fd(m->udev_monitor),
267 EPOLLIN, manager_dispatch_link_udev,
268 m);
269 if (r < 0)
667fcc6d 270 return r;
505f8da7 271
5fae368b 272 r = sd_event_source_set_description(m->udev_event_source, "networkd-udev");
505f8da7
TG
273 if (r < 0)
274 return r;
11a7f229 275
505f8da7
TG
276 return 0;
277}
f579559b 278
1c4baffc 279static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
505f8da7
TG
280 Manager *m = userdata;
281 Link *link = NULL;
4d473d5d 282 NetDev *netdev = NULL;
f2236469 283 uint16_t type;
ca4e095a 284 const char *name;
505f8da7 285 int r, ifindex;
f579559b 286
505f8da7
TG
287 assert(rtnl);
288 assert(message);
f579559b
TG
289 assert(m);
290
1c4baffc
TG
291 if (sd_netlink_message_is_error(message)) {
292 r = sd_netlink_message_get_errno(message);
45af44d4
TG
293 if (r < 0)
294 log_warning_errno(r, "rtnl: could not receive link: %m");
295
296 return 0;
297 }
298
1c4baffc 299 r = sd_netlink_message_get_type(message, &type);
f2236469 300 if (r < 0) {
45af44d4 301 log_warning_errno(r, "rtnl: could not get message type: %m");
f2236469 302 return 0;
cdfee943
TG
303 } else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
304 log_warning("rtnl: received unexpected message type when processing link");
305 return 0;
f2236469
TG
306 }
307
505f8da7 308 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
45af44d4 309 if (r < 0) {
cdfee943 310 log_warning_errno(r, "rtnl: could not get ifindex from link: %m");
45af44d4
TG
311 return 0;
312 } else if (ifindex <= 0) {
313 log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
505f8da7 314 return 0;
4d473d5d
TG
315 } else
316 link_get(m, ifindex, &link);
f579559b 317
1c4baffc 318 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
45af44d4
TG
319 if (r < 0) {
320 log_warning_errno(r, "rtnl: received link message without ifname: %m");
4d473d5d
TG
321 return 0;
322 } else
f2236469 323 netdev_get(m, name, &netdev);
4d473d5d
TG
324
325 switch (type) {
326 case RTM_NEWLINK:
327 if (!link) {
328 /* link is new, so add it */
329 r = link_add(m, message, &link);
330 if (r < 0) {
17d1f37d 331 log_warning_errno(r, "could not add new link: %m");
4d473d5d
TG
332 return 0;
333 }
334 }
335
336 if (netdev) {
337 /* netdev exists, so make sure the ifindex matches */
505f8da7
TG
338 r = netdev_set_ifindex(netdev, message);
339 if (r < 0) {
17d1f37d 340 log_warning_errno(r, "could not set ifindex on netdev: %m");
505f8da7
TG
341 return 0;
342 }
343 }
e1202047 344
f2236469
TG
345 r = link_update(link, message);
346 if (r < 0)
347 return 0;
4d473d5d
TG
348
349 break;
350
351 case RTM_DELLINK:
352 link_drop(link);
353 netdev_drop(netdev);
354
355 break;
356
357 default:
358 assert_not_reached("Received invalid RTNL message type.");
f2236469 359 }
505f8da7
TG
360
361 return 1;
362}
363
5fae368b
TG
364static int systemd_netlink_fd(void) {
365 int n, fd, rtnl_fd = -EINVAL;
366
367 n = sd_listen_fds(true);
368 if (n <= 0)
369 return -EINVAL;
370
371 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
372 if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
373 if (rtnl_fd >= 0)
374 return -EINVAL;
375
376 rtnl_fd = fd;
377 }
378 }
379
380 return rtnl_fd;
381}
382
383static int manager_connect_rtnl(Manager *m) {
384 int fd, r;
505f8da7
TG
385
386 assert(m);
505f8da7 387
5fae368b
TG
388 fd = systemd_netlink_fd();
389 if (fd < 0)
1c4baffc 390 r = sd_netlink_open(&m->rtnl);
5fae368b 391 else
1c4baffc 392 r = sd_netlink_open_fd(&m->rtnl, fd);
505f8da7
TG
393 if (r < 0)
394 return r;
395
1c4baffc 396 r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
f579559b 397 if (r < 0)
bf5332d2 398 return r;
f579559b 399
1c4baffc 400 r = sd_netlink_attach_event(m->rtnl, m->event, 0);
505f8da7
TG
401 if (r < 0)
402 return r;
f579559b 403
1c4baffc 404 r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
5fae368b
TG
405 if (r < 0)
406 return r;
505f8da7 407
1c4baffc 408 r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m);
5fae368b
TG
409 if (r < 0)
410 return r;
45af44d4 411
1c4baffc 412 r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &link_rtnl_process_address, m);
5fae368b
TG
413 if (r < 0)
414 return r;
415
1c4baffc 416 r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &link_rtnl_process_address, m);
5fae368b
TG
417 if (r < 0)
418 return r;
419
420 return 0;
45af44d4 421}
505f8da7 422
5fae368b
TG
423int manager_new(Manager **ret) {
424 _cleanup_manager_free_ Manager *m = NULL;
45af44d4 425 int r;
f579559b 426
5fae368b
TG
427 m = new0(Manager, 1);
428 if (!m)
429 return -ENOMEM;
45af44d4 430
5fae368b
TG
431 m->state_file = strdup("/run/systemd/netif/state");
432 if (!m->state_file)
433 return -ENOMEM;
434
435 r = sd_event_default(&m->event);
45af44d4
TG
436 if (r < 0)
437 return r;
438
5fae368b
TG
439 sd_event_set_watchdog(m->event, true);
440
441 sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
442 sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
443
444 r = manager_connect_rtnl(m);
45af44d4
TG
445 if (r < 0)
446 return r;
447
5fae368b
TG
448 r = manager_connect_udev(m);
449 if (r < 0)
450 return r;
45af44d4 451
5fae368b
TG
452 m->netdevs = hashmap_new(&string_hash_ops);
453 if (!m->netdevs)
454 return -ENOMEM;
f579559b 455
5fae368b 456 LIST_HEAD_INIT(m->networks);
f579559b 457
5fae368b
TG
458 r = setup_default_address_pool(m);
459 if (r < 0)
460 return r;
f579559b 461
5fae368b
TG
462 *ret = m;
463 m = NULL;
f579559b 464
f579559b
TG
465 return 0;
466}
467
5fae368b
TG
468void manager_free(Manager *m) {
469 Network *network;
470 NetDev *netdev;
471 Link *link;
472 AddressPool *pool;
f579559b 473
5fae368b
TG
474 if (!m)
475 return;
505f8da7 476
5fae368b 477 free(m->state_file);
505f8da7 478
5fae368b
TG
479 udev_monitor_unref(m->udev_monitor);
480 udev_unref(m->udev);
481 sd_bus_unref(m->bus);
482 sd_bus_slot_unref(m->prepare_for_sleep_slot);
483 sd_event_source_unref(m->udev_event_source);
484 sd_event_source_unref(m->bus_retry_event_source);
485 sd_event_unref(m->event);
f579559b 486
5fae368b
TG
487 while ((link = hashmap_first(m->links)))
488 link_unref(link);
489 hashmap_free(m->links);
f579559b 490
5fae368b
TG
491 while ((network = m->networks))
492 network_free(network);
493
dbffab87
TG
494 hashmap_free(m->networks_by_name);
495
5fae368b
TG
496 while ((netdev = hashmap_first(m->netdevs)))
497 netdev_unref(netdev);
498 hashmap_free(m->netdevs);
499
500 while ((pool = m->address_pools))
501 address_pool_free(pool);
502
1c4baffc 503 sd_netlink_unref(m->rtnl);
5fae368b
TG
504
505 free(m);
506}
507
a97dcc12
TG
508static bool manager_check_idle(void *userdata) {
509 Manager *m = userdata;
510 Link *link;
511 Iterator i;
512
513 assert(m);
514
515 HASHMAP_FOREACH(link, m->links, i) {
516 /* we are not woken on udev activity, so let's just wait for the
517 * pending udev event */
518 if (link->state == LINK_STATE_PENDING)
519 return false;
520
521 if (!link->network)
522 continue;
523
524 /* we are not woken on netork activity, so let's stay around */
525 if (link_lldp_enabled(link) ||
526 link_ipv4ll_enabled(link) ||
527 link_dhcp4_server_enabled(link) ||
528 link_dhcp4_enabled(link) ||
529 link_dhcp6_enabled(link))
530 return false;
531 }
532
533 return true;
534}
535
536int manager_run(Manager *m) {
537 assert(m);
538
f806f64c
TG
539 if (m->bus)
540 return bus_event_loop_with_idle(
541 m->event,
542 m->bus,
543 "org.freedesktop.network1",
544 DEFAULT_EXIT_USEC,
545 manager_check_idle,
546 m);
547 else
548 /* failed to connect to the bus, so we lose exit-on-idle logic,
549 this should not happen except if dbus is not around at all */
550 return sd_event_loop(m->event);
a97dcc12
TG
551}
552
5fae368b
TG
553int manager_load_config(Manager *m) {
554 int r;
555
556 /* update timestamp */
557 paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
558
559 r = netdev_load(m);
f579559b
TG
560 if (r < 0)
561 return r;
562
5fae368b 563 r = network_load(m);
9021bb9f
TG
564 if (r < 0)
565 return r;
566
f579559b
TG
567 return 0;
568}
f882c247 569
5fae368b
TG
570bool manager_should_reload(Manager *m) {
571 return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
572}
573
574int manager_rtnl_enumerate_links(Manager *m) {
1c4baffc
TG
575 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
576 sd_netlink_message *link;
f882c247
TG
577 int r;
578
5da8149f 579 assert(m);
5fae368b 580 assert(m->rtnl);
5da8149f 581
5fae368b 582 r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
f882c247
TG
583 if (r < 0)
584 return r;
585
1c4baffc 586 r = sd_netlink_message_request_dump(req, true);
dd3efc09
TG
587 if (r < 0)
588 return r;
589
1c4baffc 590 r = sd_netlink_call(m->rtnl, req, 0, &reply);
f2236469
TG
591 if (r < 0)
592 return r;
593
1c4baffc 594 for (link = reply; link; link = sd_netlink_message_next(link)) {
5fae368b 595 int k;
2e9f08ea 596
6a24f148
TG
597 m->enumerating = true;
598
5fae368b
TG
599 k = manager_rtnl_process_link(m->rtnl, link, m);
600 if (k < 0)
601 r = k;
6a24f148
TG
602
603 m->enumerating = false;
5fae368b 604 }
2e9f08ea 605
5fae368b 606 return r;
f882c247 607}
3bef724f 608
5fae368b 609int manager_rtnl_enumerate_addresses(Manager *m) {
1c4baffc
TG
610 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
611 sd_netlink_message *addr;
1346b1f0
TG
612 int r;
613
5fae368b
TG
614 assert(m);
615 assert(m->rtnl);
bcbca829 616
5fae368b
TG
617 r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
618 if (r < 0)
619 return r;
bcbca829 620
1c4baffc 621 r = sd_netlink_message_request_dump(req, true);
1346b1f0
TG
622 if (r < 0)
623 return r;
624
1c4baffc 625 r = sd_netlink_call(m->rtnl, req, 0, &reply);
5fae368b
TG
626 if (r < 0)
627 return r;
628
1c4baffc 629 for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
5fae368b
TG
630 int k;
631
6a24f148
TG
632 m->enumerating = true;
633
5fae368b
TG
634 k = link_rtnl_process_address(m->rtnl, addr, m);
635 if (k < 0)
636 r = k;
6a24f148
TG
637
638 m->enumerating = false;
5fae368b
TG
639 }
640
641 return r;
1346b1f0
TG
642}
643
c0c743cb
LP
644static int set_put_in_addr(Set *s, const struct in_addr *address) {
645 char *p;
646 int r;
647
648 assert(s);
649
650 r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
651 if (r < 0)
652 return r;
653
654 r = set_consume(s, p);
655 if (r == -EEXIST)
656 return 0;
657
658 return r;
659}
660
661static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
662 int r, i, c = 0;
663
664 assert(s);
665 assert(n <= 0 || addresses);
666
667 for (i = 0; i < n; i++) {
668 r = set_put_in_addr(s, addresses+i);
669 if (r < 0)
670 return r;
671
672 c += r;
673 }
674
675 return c;
676}
677
8612e936
LP
678static void print_string_set(FILE *f, const char *field, Set *s) {
679 bool space = false;
680 Iterator i;
681 char *p;
682
683 if (set_isempty(s))
684 return;
685
686 fputs(field, f);
687
688 SET_FOREACH(p, s, i) {
689 if (space)
690 fputc(' ', f);
691 fputs(p, f);
692 space = true;
693 }
694 fputc('\n', f);
695}
696
bbf7c048 697int manager_save(Manager *m) {
8612e936 698 _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
bbf7c048
TG
699 Link *link;
700 Iterator i;
701 _cleanup_free_ char *temp_path = NULL;
702 _cleanup_fclose_ FILE *f = NULL;
d3df0e39 703 LinkOperationalState operstate = LINK_OPERSTATE_OFF;
e375dcde 704 const char *operstate_str;
bbf7c048
TG
705 int r;
706
707 assert(m);
708 assert(m->state_file);
709
c0c743cb 710 /* We add all NTP and DNS server to a set, to filter out duplicates */
d5099efc 711 dns = set_new(&string_hash_ops);
c0c743cb
LP
712 if (!dns)
713 return -ENOMEM;
714
d5099efc 715 ntp = set_new(&string_hash_ops);
c0c743cb
LP
716 if (!ntp)
717 return -ENOMEM;
718
d5099efc 719 domains = set_new(&string_hash_ops);
8612e936
LP
720 if (!domains)
721 return -ENOMEM;
722
bbf7c048
TG
723 HASHMAP_FOREACH(link, m->links, i) {
724 if (link->flags & IFF_LOOPBACK)
725 continue;
726
e375dcde
TG
727 if (link->operstate > operstate)
728 operstate = link->operstate;
c0c743cb
LP
729
730 if (!link->network)
731 continue;
732
733 /* First add the static configured entries */
734 r = set_put_strdupv(dns, link->network->dns);
735 if (r < 0)
736 return r;
737
738 r = set_put_strdupv(ntp, link->network->ntp);
739 if (r < 0)
740 return r;
741
8612e936
LP
742 r = set_put_strdupv(domains, link->network->domains);
743 if (r < 0)
744 return r;
745
c0c743cb
LP
746 if (!link->dhcp_lease)
747 continue;
748
749 /* Secondly, add the entries acquired via DHCP */
750 if (link->network->dhcp_dns) {
751 const struct in_addr *addresses;
752
753 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
754 if (r > 0) {
755 r = set_put_in_addrv(dns, addresses, r);
756 if (r < 0)
757 return r;
8612e936 758 } else if (r < 0 && r != -ENOENT)
c0c743cb
LP
759 return r;
760 }
761
762 if (link->network->dhcp_ntp) {
763 const struct in_addr *addresses;
764
765 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
766 if (r > 0) {
767 r = set_put_in_addrv(ntp, addresses, r);
768 if (r < 0)
769 return r;
8612e936
LP
770 } else if (r < 0 && r != -ENOENT)
771 return r;
772 }
773
774 if (link->network->dhcp_domains) {
775 const char *domainname;
776
777 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
778 if (r >= 0) {
779 r = set_put_strdup(domains, domainname);
780 if (r < 0)
781 return r;
c0c743cb
LP
782 } else if (r != -ENOENT)
783 return r;
784 }
bbf7c048
TG
785 }
786
e375dcde
TG
787 operstate_str = link_operstate_to_string(operstate);
788 assert(operstate_str);
bbf7c048
TG
789
790 r = fopen_temporary(m->state_file, &f, &temp_path);
791 if (r < 0)
c2d6bd61 792 return r;
bbf7c048
TG
793
794 fchmod(fileno(f), 0644);
795
796 fprintf(f,
797 "# This is private data. Do not parse.\n"
e375dcde 798 "OPER_STATE=%s\n", operstate_str);
bbf7c048 799
8612e936
LP
800 print_string_set(f, "DNS=", dns);
801 print_string_set(f, "NTP=", ntp);
802 print_string_set(f, "DOMAINS=", domains);
c0c743cb 803
c2d6bd61
LP
804 r = fflush_and_check(f);
805 if (r < 0)
806 goto fail;
bbf7c048 807
c2d6bd61 808 if (rename(temp_path, m->state_file) < 0) {
bbf7c048 809 r = -errno;
c2d6bd61 810 goto fail;
bbf7c048
TG
811 }
812
e331e246
TG
813 if (m->operational_state != operstate) {
814 m->operational_state = operstate;
815 r = manager_send_changed(m, "OperationalState", NULL);
816 if (r < 0)
817 log_error_errno(r, "Could not emit changed OperationalState: %m");
818 }
819
c2d6bd61 820 return 0;
bbf7c048 821
c2d6bd61 822fail:
dacd6cee
LP
823 (void) unlink(m->state_file);
824 (void) unlink(temp_path);
825
826 return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
bbf7c048 827}
11bf3cce 828
0dd25fb9 829int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
11bf3cce
LP
830 AddressPool *p;
831 int r;
832
833 assert(m);
834 assert(prefixlen > 0);
835 assert(found);
836
837 LIST_FOREACH(address_pools, p, m->address_pools) {
838 if (p->family != family)
839 continue;
840
841 r = address_pool_acquire(p, prefixlen, found);
842 if (r != 0)
843 return r;
844 }
845
846 return 0;
847}
cb9fc36a
LP
848
849const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
850 if (b == ADDRESS_FAMILY_YES ||
851 b == ADDRESS_FAMILY_NO)
852 return yes_no(b == ADDRESS_FAMILY_YES);
853
854 if (b == ADDRESS_FAMILY_IPV4)
855 return "ipv4";
856 if (b == ADDRESS_FAMILY_IPV6)
857 return "ipv6";
858
859 return NULL;
860}
861
862AddressFamilyBoolean address_family_boolean_from_string(const char *s) {
863 int r;
864
865 /* Make this a true superset of a boolean */
866
867 r = parse_boolean(s);
868 if (r > 0)
869 return ADDRESS_FAMILY_YES;
870 if (r == 0)
871 return ADDRESS_FAMILY_NO;
872
873 if (streq(s, "ipv4"))
874 return ADDRESS_FAMILY_IPV4;
875 if (streq(s, "ipv6"))
876 return ADDRESS_FAMILY_IPV6;
877
878 return _ADDRESS_FAMILY_BOOLEAN_INVALID;
879}
769d324c
LP
880
881DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option");