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