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