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