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