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