]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: tunnel - ensure that enslave callback is always invoked
[thirdparty/systemd.git] / src / network / networkd-netdev.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 "networkd.h"
25 #include "network-internal.h"
26 #include "path-util.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
29 #include "list.h"
30 #include "siphash24.h"
31
32 #define VLANID_MAX 4094
33
34 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
35 [NETDEV_KIND_BRIDGE] = "bridge",
36 [NETDEV_KIND_BOND] = "bond",
37 [NETDEV_KIND_VLAN] = "vlan",
38 [NETDEV_KIND_MACVLAN] = "macvlan",
39 [NETDEV_KIND_VXLAN] = "vxlan",
40 [NETDEV_KIND_IPIP] = "ipip",
41 [NETDEV_KIND_GRE] = "gre",
42 [NETDEV_KIND_SIT] = "sit",
43 [NETDEV_KIND_VETH] = "veth",
44 [NETDEV_KIND_VTI] = "vti"
45 };
46
47 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
48 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
49
50 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
51 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
52 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
53 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
54 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
55 };
56
57 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
58 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
59
60 static void netdev_cancel_callbacks(NetDev *netdev) {
61 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
62 netdev_enslave_callback *callback;
63
64 if (!netdev)
65 return;
66
67 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
68
69 while ((callback = netdev->callbacks)) {
70 if (m) {
71 assert(callback->link);
72 assert(callback->callback);
73 assert(netdev->manager);
74 assert(netdev->manager->rtnl);
75
76 callback->callback(netdev->manager->rtnl, m, link);
77 }
78
79 LIST_REMOVE(callbacks, netdev->callbacks, callback);
80 free(callback);
81 }
82 }
83
84 static void netdev_free(NetDev *netdev) {
85 if (!netdev)
86 return;
87
88 netdev_cancel_callbacks(netdev);
89
90 if (netdev->ifname)
91 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
92
93 free(netdev->filename);
94
95 free(netdev->description);
96 free(netdev->ifname);
97 free(netdev->ifname_peer);
98 free(netdev->mac);
99 free(netdev->mac_peer);
100
101 condition_free_list(netdev->match_host);
102 condition_free_list(netdev->match_virt);
103 condition_free_list(netdev->match_kernel);
104 condition_free_list(netdev->match_arch);
105
106 free(netdev);
107 }
108
109 NetDev *netdev_unref(NetDev *netdev) {
110 if (netdev && (-- netdev->n_ref <= 0))
111 netdev_free(netdev);
112
113 return NULL;
114 }
115
116 NetDev *netdev_ref(NetDev *netdev) {
117 if (netdev)
118 assert_se(++ netdev->n_ref >= 2);
119
120 return netdev;
121 }
122
123 void netdev_drop(NetDev *netdev) {
124 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
125 return;
126
127 netdev->state = NETDEV_STATE_LINGER;
128
129 log_debug_netdev(netdev, "netdev removed");
130
131 netdev_cancel_callbacks(netdev);
132
133 netdev_unref(netdev);
134
135 return;
136 }
137
138 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
139 NetDev *netdev;
140
141 assert(manager);
142 assert(name);
143 assert(ret);
144
145 netdev = hashmap_get(manager->netdevs, name);
146 if (!netdev) {
147 *ret = NULL;
148 return -ENOENT;
149 }
150
151 *ret = netdev;
152
153 return 0;
154 }
155
156 static int netdev_enter_failed(NetDev *netdev) {
157 netdev->state = NETDEV_STATE_FAILED;
158
159 return 0;
160 }
161
162 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
163 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
164 int r;
165
166 assert(netdev);
167 assert(netdev->state == NETDEV_STATE_READY);
168 assert(netdev->manager);
169 assert(netdev->manager->rtnl);
170 assert(link);
171 assert(callback);
172
173 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
174 RTM_SETLINK, link->ifindex);
175 if (r < 0) {
176 log_error_netdev(netdev,
177 "Could not allocate RTM_SETLINK message: %s",
178 strerror(-r));
179 return r;
180 }
181
182 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
183 if (r < 0) {
184 log_error_netdev(netdev,
185 "Could not append IFLA_MASTER attribute: %s",
186 strerror(-r));
187 return r;
188 }
189
190 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
191 if (r < 0) {
192 log_error_netdev(netdev,
193 "Could not send rtnetlink message: %s",
194 strerror(-r));
195 return r;
196 }
197
198 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
199
200 return 0;
201 }
202
203 static int netdev_enter_ready(NetDev *netdev) {
204 netdev_enslave_callback *callback;
205
206 assert(netdev);
207 assert(netdev->ifname);
208
209 if (netdev->state != NETDEV_STATE_CREATING)
210 return 0;
211
212 netdev->state = NETDEV_STATE_READY;
213
214 log_info_netdev(netdev, "netdev ready");
215
216 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
217 /* enslave the links that were attempted to be enslaved before the
218 * link was ready */
219 netdev_enslave_ready(netdev, callback->link, callback->callback);
220 }
221
222 return 0;
223 }
224
225 /* callback for netdev's created without a backing Link */
226 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
227 NetDev *netdev = userdata;
228 int r;
229
230 assert(netdev->state != _NETDEV_STATE_INVALID);
231
232 r = sd_rtnl_message_get_errno(m);
233 if (r == -EEXIST)
234 log_debug_netdev(netdev, "netdev exists, using existing");
235 else if (r < 0) {
236 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
237 netdev_drop(netdev);
238
239 return 1;
240 }
241
242 return 1;
243 }
244
245 int config_parse_tunnel_address(const char *unit,
246 const char *filename,
247 unsigned line,
248 const char *section,
249 unsigned section_line,
250 const char *lvalue,
251 int ltype,
252 const char *rvalue,
253 void *data,
254 void *userdata) {
255 NetDev *n = data;
256 unsigned char family = AF_INET;
257 int r;
258
259 assert(filename);
260 assert(lvalue);
261 assert(rvalue);
262 assert(data);
263
264 r = net_parse_inaddr(rvalue, &family, n);
265 if (r < 0) {
266 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
267 "Tunnel address is invalid, ignoring assignment: %s", rvalue);
268 return 0;
269 }
270 return 0;
271 }
272
273 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
274 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
275 const char *kind;
276 int r;
277
278 assert(netdev);
279 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
280 (link && callback));
281 assert(netdev->ifname);
282 assert(netdev->manager);
283 assert(netdev->manager->rtnl);
284
285 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
286 if (r < 0) {
287 log_error_netdev(netdev,
288 "Could not allocate RTM_NEWLINK message: %s",
289 strerror(-r));
290 return r;
291 }
292
293 if (link) {
294 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
295 if (r < 0) {
296 log_error_netdev(netdev,
297 "Could not append IFLA_LINK attribute: %s",
298 strerror(-r));
299 return r;
300 }
301 }
302
303 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->ifname);
304 if (r < 0) {
305 log_error_netdev(netdev,
306 "Could not append IFLA_IFNAME attribute: %s",
307 strerror(-r));
308 return r;
309 }
310
311 if (netdev->mtu) {
312 r = sd_rtnl_message_append_u32(req, IFLA_MTU, netdev->mtu);
313 if (r < 0) {
314 log_error_netdev(netdev,
315 "Could not append IFLA_MTU attribute: %s",
316 strerror(-r));
317 return r;
318 }
319 }
320
321 if (netdev->mac) {
322 r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, netdev->mac);
323 if (r < 0) {
324 log_error_netdev(netdev,
325 "Colud not append IFLA_ADDRESS attribute: %s",
326 strerror(-r));
327 return r;
328 }
329 }
330
331 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
332 if (r < 0) {
333 log_error_netdev(netdev,
334 "Could not open IFLA_LINKINFO container: %s",
335 strerror(-r));
336 return r;
337 }
338
339 kind = netdev_kind_to_string(netdev->kind);
340 if (!kind) {
341 log_error_netdev(netdev, "Invalid kind");
342 return -EINVAL;
343 }
344
345 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
346 if (r < 0) {
347 log_error_netdev(netdev,
348 "Could not open IFLA_INFO_DATA container: %s",
349 strerror(-r));
350 return r;
351 }
352
353 if (netdev->vlanid <= VLANID_MAX) {
354 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
355 if (r < 0) {
356 log_error_netdev(netdev,
357 "Could not append IFLA_VLAN_ID attribute: %s",
358 strerror(-r));
359 return r;
360 }
361 }
362
363 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
364 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
365 if (r < 0) {
366 log_error_netdev(netdev,
367 "Could not append IFLA_MACVLAN_MODE attribute: %s",
368 strerror(-r));
369 return r;
370 }
371 }
372
373 r = sd_rtnl_message_close_container(req);
374 if (r < 0) {
375 log_error_netdev(netdev,
376 "Could not close IFLA_INFO_DATA container %s",
377 strerror(-r));
378 return r;
379 }
380
381 r = sd_rtnl_message_close_container(req);
382 if (r < 0) {
383 log_error_netdev(netdev,
384 "Could not close IFLA_LINKINFO container %s",
385 strerror(-r));
386 return r;
387 }
388
389 if (link)
390 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
391 else
392 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
393 if (r < 0) {
394 log_error_netdev(netdev,
395 "Could not send rtnetlink message: %s", strerror(-r));
396 return r;
397 }
398
399 log_debug_netdev(netdev, "creating netdev");
400
401 netdev->state = NETDEV_STATE_CREATING;
402
403 return 0;
404 }
405
406 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
407 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
408 int r;
409
410 switch(netdev->kind) {
411 case NETDEV_KIND_VLAN:
412 case NETDEV_KIND_MACVLAN:
413 return netdev_create(netdev, link, callback);
414 case NETDEV_KIND_VXLAN:
415 return netdev_create_vxlan(netdev, link, callback);
416 case NETDEV_KIND_IPIP:
417 case NETDEV_KIND_GRE:
418 case NETDEV_KIND_SIT:
419 case NETDEV_KIND_VTI:
420 return netdev_create_tunnel(netdev, link, callback);
421 default:
422 break;
423 }
424
425 if (netdev->state == NETDEV_STATE_READY) {
426 r = netdev_enslave_ready(netdev, link, callback);
427 if (r < 0)
428 return r;
429 } else {
430 /* the netdev is not yet read, save this request for when it is*/
431 netdev_enslave_callback *cb;
432
433 cb = new0(netdev_enslave_callback, 1);
434 if (!cb)
435 return log_oom();
436
437 cb->callback = callback;
438 cb->link = link;
439
440 LIST_PREPEND(callbacks, netdev->callbacks, cb);
441 }
442
443 return 0;
444 }
445
446 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
447 uint16_t type;
448 const char *kind;
449 char *received_kind;
450 char *received_name;
451 int r, ifindex;
452
453 assert(netdev);
454 assert(message);
455
456 r = sd_rtnl_message_get_type(message, &type);
457 if (r < 0) {
458 log_error_netdev(netdev, "Could not get rtnl message type");
459 return r;
460 }
461
462 if (type != RTM_NEWLINK) {
463 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
464 return -EINVAL;
465 }
466
467 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
468 if (r < 0) {
469 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
470 netdev_enter_failed(netdev);
471 return r;
472 } else if (ifindex <= 0) {
473 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
474 netdev_enter_failed(netdev);
475 return r;
476 }
477
478 if (netdev->ifindex > 0) {
479 if (netdev->ifindex != ifindex) {
480 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
481 ifindex, netdev->ifindex);
482 netdev_enter_failed(netdev);
483 return -EEXIST;
484 } else
485 /* ifindex already set to the same for this netdev */
486 return 0;
487 }
488
489 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
490 if (r < 0) {
491 log_error_netdev(netdev, "Could not get IFNAME");
492 return r;
493 }
494
495 if (!streq(netdev->ifname, received_name)) {
496 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
497 received_name);
498 netdev_enter_failed(netdev);
499 return r;
500 }
501
502 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
503 if (r < 0) {
504 log_error_netdev(netdev, "Could not get LINKINFO");
505 return r;
506 }
507
508 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
509 if (r < 0) {
510 log_error_netdev(netdev, "Could not get KIND");
511 return r;
512 }
513
514 r = sd_rtnl_message_exit_container(message);
515 if (r < 0) {
516 log_error_netdev(netdev, "Could not exit container");
517 return r;
518 }
519
520 kind = netdev_kind_to_string(netdev->kind);
521 if (!kind) {
522 log_error_netdev(netdev, "Could not get kind");
523 netdev_enter_failed(netdev);
524 return -EINVAL;
525 }
526
527 if (!streq(kind, received_kind)) {
528 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
529 "expected %s", received_kind, kind);
530 netdev_enter_failed(netdev);
531 return r;
532 }
533
534 netdev->ifindex = ifindex;
535
536 log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
537
538 netdev_enter_ready(netdev);
539
540 return 0;
541 }
542
543 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
544
545 static int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
546 _cleanup_free_ struct ether_addr *mac = NULL;
547 uint8_t result[8];
548 size_t l, sz;
549 uint8_t *v;
550 int r;
551
552 assert(ifname);
553 assert(ret);
554
555 mac = new0(struct ether_addr, 1);
556 if (!mac)
557 return -ENOMEM;
558
559 l = strlen(ifname);
560 sz = sizeof(sd_id128_t) + l;
561 v = alloca(sz);
562
563 /* fetch some persistent data unique to the machine */
564 r = sd_id128_get_machine((sd_id128_t*) v);
565 if (r < 0)
566 return r;
567
568 /* combine with some data unique (on this machine) to this
569 * netdev */
570 memcpy(v + sizeof(sd_id128_t), ifname, l);
571
572 /* Let's hash the host machine ID plus the container name. We
573 * use a fixed, but originally randomly created hash key here. */
574 siphash24(result, v, sz, HASH_KEY.bytes);
575
576 assert_cc(ETH_ALEN <= sizeof(result));
577 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
578
579 /* see eth_random_addr in the kernel */
580 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
581 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
582
583 *ret = mac;
584 mac = NULL;
585
586 return 0;
587 }
588
589 static int netdev_load_one(Manager *manager, const char *filename) {
590 _cleanup_netdev_unref_ NetDev *netdev = NULL;
591 _cleanup_fclose_ FILE *file = NULL;
592 int r;
593
594 assert(manager);
595 assert(filename);
596
597 if (null_or_empty_path(filename)) {
598 log_debug("skipping empty file: %s", filename);
599 return 0;
600 }
601
602 file = fopen(filename, "re");
603 if (!file) {
604 if (errno == ENOENT)
605 return 0;
606 else
607 return -errno;
608 }
609
610 netdev = new0(NetDev, 1);
611 if (!netdev)
612 return log_oom();
613
614 netdev->n_ref = 1;
615 netdev->manager = manager;
616 netdev->state = _NETDEV_STATE_INVALID;
617 netdev->kind = _NETDEV_KIND_INVALID;
618 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
619 netdev->vlanid = VLANID_MAX + 1;
620 netdev->vxlanid = VXLAN_VID_MAX + 1;
621 netdev->tunnel_pmtudisc = true;
622 netdev->learning = true;
623
624 r = config_parse(NULL, filename, file,
625 "Match\0NetDev\0VLAN\0MACVLAN\0VXLAN\0Tunnel\0Peer\0",
626 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
627 false, false, netdev);
628 if (r < 0) {
629 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
630 return r;
631 }
632
633 if (netdev->kind == _NETDEV_KIND_INVALID) {
634 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
635 return 0;
636 }
637
638 if (!netdev->ifname) {
639 log_warning("NetDev without Name configured in %s. Ignoring", filename);
640 return 0;
641 }
642
643 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
644 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
645 return 0;
646 }
647
648 if (netdev->kind == NETDEV_KIND_VXLAN && netdev->vxlanid > VXLAN_VID_MAX) {
649 log_warning("VXLAN without valid Id configured in %s. Ignoring", filename);
650 return 0;
651 }
652
653 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
654 log_warning("VLAN Id configured for a %s in %s. Ignoring",
655 netdev_kind_to_string(netdev->kind), filename);
656 return 0;
657 }
658
659 if (netdev->kind != NETDEV_KIND_VXLAN && netdev->vxlanid <= VXLAN_VID_MAX) {
660 log_warning("VXLAN Id configured for a %s in %s. Ignoring",
661 netdev_kind_to_string(netdev->kind), filename);
662 return 0;
663 }
664
665 if (netdev->kind != NETDEV_KIND_MACVLAN &&
666 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
667 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
668 netdev_kind_to_string(netdev->kind), filename);
669 return 0;
670 }
671
672 netdev->filename = strdup(filename);
673 if (!netdev->filename)
674 return log_oom();
675
676 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
677 netdev->match_host, netdev->match_virt,
678 netdev->match_kernel, netdev->match_arch,
679 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
680 return 0;
681
682 if (!netdev->mac) {
683 r = netdev_get_mac(netdev->ifname, &netdev->mac);
684 if (r < 0) {
685 log_error("Failed to generate predictable MAC address for %s",
686 netdev->ifname);
687 return r;
688 }
689 }
690
691 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
692 if (r < 0)
693 return r;
694
695 LIST_HEAD_INIT(netdev->callbacks);
696
697 switch (netdev->kind) {
698 case NETDEV_KIND_VETH:
699 if (!netdev->ifname_peer) {
700 log_warning("Veth NetDev without peer name configured "
701 "in %s. Ignoring", filename);
702 return 0;
703 }
704
705 if (!netdev->mac) {
706 r = netdev_get_mac(netdev->ifname_peer, &netdev->mac_peer);
707 if (r < 0) {
708 log_error("Failed to generate predictable MAC address for %s",
709 netdev->ifname_peer);
710 return r;
711 }
712 }
713
714 r = netdev_create_veth(netdev, netdev_create_handler);
715 if (r < 0)
716 return r;
717
718 break;
719 case NETDEV_KIND_BRIDGE:
720 case NETDEV_KIND_BOND:
721 r = netdev_create(netdev, NULL, NULL);
722 if (r < 0)
723 return r;
724 break;
725 default:
726 break;
727 }
728
729 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
730
731 netdev = NULL;
732
733 return 0;
734 }
735
736 int netdev_load(Manager *manager) {
737 NetDev *netdev;
738 char **files, **f;
739 int r;
740
741 assert(manager);
742
743 while ((netdev = hashmap_first(manager->netdevs)))
744 netdev_unref(netdev);
745
746 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
747 if (r < 0) {
748 log_error("Failed to enumerate netdev files: %s", strerror(-r));
749 return r;
750 }
751
752 STRV_FOREACH_BACKWARDS(f, files) {
753 r = netdev_load_one(manager, *f);
754 if (r < 0)
755 return r;
756 }
757
758 strv_free(files);
759
760 return 0;
761 }