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