]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
Merge pull request #1374 from olof/autoconf_gcrypt_dep
[thirdparty/systemd.git] / src / network / networkd-netdev.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <net/if.h>
23
24 #include "conf-files.h"
25 #include "conf-parser.h"
26 #include "list.h"
27 #include "siphash24.h"
28 #include "netlink-util.h"
29 #include "network-internal.h"
30
31 #include "networkd.h"
32 #include "networkd-netdev.h"
33
34 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
35
36 [NETDEV_KIND_BRIDGE] = &bridge_vtable,
37 [NETDEV_KIND_BOND] = &bond_vtable,
38 [NETDEV_KIND_VLAN] = &vlan_vtable,
39 [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
40 [NETDEV_KIND_MACVTAP] = &macvtap_vtable,
41 [NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
42 [NETDEV_KIND_VXLAN] = &vxlan_vtable,
43 [NETDEV_KIND_IPIP] = &ipip_vtable,
44 [NETDEV_KIND_GRE] = &gre_vtable,
45 [NETDEV_KIND_GRETAP] = &gretap_vtable,
46 [NETDEV_KIND_IP6GRE] = &ip6gre_vtable,
47 [NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable,
48 [NETDEV_KIND_SIT] = &sit_vtable,
49 [NETDEV_KIND_VTI] = &vti_vtable,
50 [NETDEV_KIND_VTI6] = &vti6_vtable,
51 [NETDEV_KIND_VETH] = &veth_vtable,
52 [NETDEV_KIND_DUMMY] = &dummy_vtable,
53 [NETDEV_KIND_TUN] = &tun_vtable,
54 [NETDEV_KIND_TAP] = &tap_vtable,
55 [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
56 };
57
58 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
59 [NETDEV_KIND_BRIDGE] = "bridge",
60 [NETDEV_KIND_BOND] = "bond",
61 [NETDEV_KIND_VLAN] = "vlan",
62 [NETDEV_KIND_MACVLAN] = "macvlan",
63 [NETDEV_KIND_MACVTAP] = "macvtap",
64 [NETDEV_KIND_IPVLAN] = "ipvlan",
65 [NETDEV_KIND_VXLAN] = "vxlan",
66 [NETDEV_KIND_IPIP] = "ipip",
67 [NETDEV_KIND_GRE] = "gre",
68 [NETDEV_KIND_GRETAP] = "gretap",
69 [NETDEV_KIND_IP6GRE] = "ip6gre",
70 [NETDEV_KIND_IP6GRETAP] = "ip6gretap",
71 [NETDEV_KIND_SIT] = "sit",
72 [NETDEV_KIND_VETH] = "veth",
73 [NETDEV_KIND_VTI] = "vti",
74 [NETDEV_KIND_VTI6] = "vti6",
75 [NETDEV_KIND_DUMMY] = "dummy",
76 [NETDEV_KIND_TUN] = "tun",
77 [NETDEV_KIND_TAP] = "tap",
78 [NETDEV_KIND_IP6TNL] = "ip6tnl",
79 };
80
81 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
82 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
83
84 static void netdev_cancel_callbacks(NetDev *netdev) {
85 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
86 netdev_join_callback *callback;
87
88 if (!netdev)
89 return;
90
91 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
92
93 while ((callback = netdev->callbacks)) {
94 if (m) {
95 assert(callback->link);
96 assert(callback->callback);
97 assert(netdev->manager);
98 assert(netdev->manager->rtnl);
99
100 callback->callback(netdev->manager->rtnl, m, callback->link);
101 }
102
103 LIST_REMOVE(callbacks, netdev->callbacks, callback);
104 link_unref(callback->link);
105 free(callback);
106 }
107 }
108
109 static void netdev_free(NetDev *netdev) {
110 if (!netdev)
111 return;
112
113 netdev_cancel_callbacks(netdev);
114
115 if (netdev->ifname)
116 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
117
118 free(netdev->filename);
119
120 free(netdev->description);
121 free(netdev->ifname);
122 free(netdev->mac);
123
124 condition_free_list(netdev->match_host);
125 condition_free_list(netdev->match_virt);
126 condition_free_list(netdev->match_kernel);
127 condition_free_list(netdev->match_arch);
128
129 if (NETDEV_VTABLE(netdev) &&
130 NETDEV_VTABLE(netdev)->done)
131 NETDEV_VTABLE(netdev)->done(netdev);
132
133 free(netdev);
134 }
135
136 NetDev *netdev_unref(NetDev *netdev) {
137 if (netdev && (-- netdev->n_ref <= 0))
138 netdev_free(netdev);
139
140 return NULL;
141 }
142
143 NetDev *netdev_ref(NetDev *netdev) {
144 if (netdev)
145 assert_se(++ netdev->n_ref >= 2);
146
147 return netdev;
148 }
149
150 void netdev_drop(NetDev *netdev) {
151 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
152 return;
153
154 netdev->state = NETDEV_STATE_LINGER;
155
156 log_netdev_debug(netdev, "netdev removed");
157
158 netdev_cancel_callbacks(netdev);
159
160 netdev_unref(netdev);
161
162 return;
163 }
164
165 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
166 NetDev *netdev;
167
168 assert(manager);
169 assert(name);
170 assert(ret);
171
172 netdev = hashmap_get(manager->netdevs, name);
173 if (!netdev) {
174 *ret = NULL;
175 return -ENOENT;
176 }
177
178 *ret = netdev;
179
180 return 0;
181 }
182
183 static int netdev_enter_failed(NetDev *netdev) {
184 netdev->state = NETDEV_STATE_FAILED;
185
186 netdev_cancel_callbacks(netdev);
187
188 return 0;
189 }
190
191 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) {
192 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
193 int r;
194
195 assert(netdev);
196 assert(netdev->state == NETDEV_STATE_READY);
197 assert(netdev->manager);
198 assert(netdev->manager->rtnl);
199 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
200 assert(link);
201 assert(callback);
202
203 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
204 if (r < 0)
205 return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
206
207 r = sd_netlink_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
208 if (r < 0)
209 return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m");
210
211 r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
212 if (r < 0)
213 return log_netdev_error(netdev, "Could not send rtnetlink message: %m");
214
215 link_ref(link);
216
217 log_netdev_debug(netdev, "Enslaving link '%s'", link->ifname);
218
219 return 0;
220 }
221
222 static int netdev_enter_ready(NetDev *netdev) {
223 netdev_join_callback *callback, *callback_next;
224 int r;
225
226 assert(netdev);
227 assert(netdev->ifname);
228
229 if (netdev->state != NETDEV_STATE_CREATING)
230 return 0;
231
232 netdev->state = NETDEV_STATE_READY;
233
234 log_netdev_info(netdev, "netdev ready");
235
236 LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
237 /* enslave the links that were attempted to be enslaved before the
238 * link was ready */
239 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
240 if (r < 0)
241 return r;
242
243 LIST_REMOVE(callbacks, netdev->callbacks, callback);
244 link_unref(callback->link);
245 free(callback);
246 }
247
248 return 0;
249 }
250
251 /* callback for netdev's created without a backing Link */
252 static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
253 _cleanup_netdev_unref_ NetDev *netdev = userdata;
254 int r;
255
256 assert(netdev->state != _NETDEV_STATE_INVALID);
257
258 r = sd_netlink_message_get_errno(m);
259 if (r == -EEXIST)
260 log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
261 else if (r < 0) {
262 log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
263 netdev_drop(netdev);
264
265 return 1;
266 }
267
268 log_netdev_debug(netdev, "Created");
269
270 return 1;
271 }
272
273 int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
274 int r;
275
276 assert(netdev);
277 assert(netdev->manager);
278 assert(netdev->manager->rtnl);
279 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
280
281 if (netdev->state == NETDEV_STATE_READY) {
282 r = netdev_enslave_ready(netdev, link, callback);
283 if (r < 0)
284 return r;
285 } else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
286 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
287
288 r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
289 if (r >= 0)
290 callback(netdev->manager->rtnl, m, link);
291 } else {
292 /* the netdev is not yet read, save this request for when it is */
293 netdev_join_callback *cb;
294
295 cb = new0(netdev_join_callback, 1);
296 if (!cb)
297 return log_oom();
298
299 cb->callback = callback;
300 cb->link = link;
301 link_ref(link);
302
303 LIST_PREPEND(callbacks, netdev->callbacks, cb);
304
305 log_netdev_debug(netdev, "Will enslave '%s', when ready", link->ifname);
306 }
307
308 return 0;
309 }
310
311 int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) {
312 uint16_t type;
313 const char *kind;
314 const char *received_kind;
315 const char *received_name;
316 int r, ifindex;
317
318 assert(netdev);
319 assert(message);
320
321 r = sd_netlink_message_get_type(message, &type);
322 if (r < 0)
323 return log_netdev_error_errno(netdev, r, "Could not get rtnl message type: %m");
324
325 if (type != RTM_NEWLINK) {
326 log_netdev_error(netdev, "Cannot set ifindex from unexpected rtnl message type.");
327 return -EINVAL;
328 }
329
330 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
331 if (r < 0) {
332 log_netdev_error_errno(netdev, r, "Could not get ifindex: %m");
333 netdev_enter_failed(netdev);
334 return r;
335 } else if (ifindex <= 0) {
336 log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex);
337 netdev_enter_failed(netdev);
338 return -EINVAL;
339 }
340
341 if (netdev->ifindex > 0) {
342 if (netdev->ifindex != ifindex) {
343 log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d",
344 ifindex, netdev->ifindex);
345 netdev_enter_failed(netdev);
346 return -EEXIST;
347 } else
348 /* ifindex already set to the same for this netdev */
349 return 0;
350 }
351
352 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &received_name);
353 if (r < 0)
354 return log_netdev_error_errno(netdev, r, "Could not get IFNAME: %m");
355
356 if (!streq(netdev->ifname, received_name)) {
357 log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name);
358 netdev_enter_failed(netdev);
359 return r;
360 }
361
362 r = sd_netlink_message_enter_container(message, IFLA_LINKINFO);
363 if (r < 0)
364 return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m");
365
366 r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind);
367 if (r < 0)
368 return log_netdev_error_errno(netdev, r, "Could not get KIND: %m");
369
370 r = sd_netlink_message_exit_container(message);
371 if (r < 0)
372 return log_netdev_error_errno(netdev, r, "Could not exit container: %m");
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_netlink_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_netlink_message_unref_ sd_netlink_message *m = NULL;
467
468 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
469 if (r < 0)
470 return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
471
472 r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname);
473 if (r < 0)
474 return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
475
476 if (netdev->mac) {
477 r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
478 if (r < 0)
479 return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m");
480 }
481
482 if (netdev->mtu) {
483 r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
484 if (r < 0)
485 return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m");
486 }
487
488 if (link) {
489 r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex);
490 if (r < 0)
491 return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m");
492 }
493
494 r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
495 if (r < 0)
496 return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
497
498 r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
499 if (r < 0)
500 return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
501
502 if (NETDEV_VTABLE(netdev)->fill_message_create) {
503 r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
504 if (r < 0)
505 return r;
506 }
507
508 r = sd_netlink_message_close_container(m);
509 if (r < 0)
510 return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
511
512 r = sd_netlink_message_close_container(m);
513 if (r < 0)
514 return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
515
516 if (link) {
517 r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL);
518 if (r < 0)
519 return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
520
521 link_ref(link);
522 } else {
523 r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
524 if (r < 0)
525 return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
526
527 netdev_ref(netdev);
528 }
529
530 netdev->state = NETDEV_STATE_CREATING;
531
532 log_netdev_debug(netdev, "Creating");
533 }
534
535 return 0;
536 }
537
538 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
539 int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
540 int r;
541
542 assert(netdev);
543 assert(netdev->manager);
544 assert(netdev->manager->rtnl);
545 assert(NETDEV_VTABLE(netdev));
546
547 switch (NETDEV_VTABLE(netdev)->create_type) {
548 case NETDEV_CREATE_MASTER:
549 r = netdev_enslave(netdev, link, callback);
550 if (r < 0)
551 return r;
552
553 break;
554 case NETDEV_CREATE_STACKED:
555 r = netdev_create(netdev, link, callback);
556 if (r < 0)
557 return r;
558
559 break;
560 default:
561 assert_not_reached("Can not join independent netdev");
562 }
563
564 return 0;
565 }
566
567 static int netdev_load_one(Manager *manager, const char *filename) {
568 _cleanup_netdev_unref_ NetDev *netdev = NULL;
569 _cleanup_free_ NetDev *netdev_raw = NULL;
570 _cleanup_fclose_ FILE *file = NULL;
571 int r;
572
573 assert(manager);
574 assert(filename);
575
576 file = fopen(filename, "re");
577 if (!file) {
578 if (errno == ENOENT)
579 return 0;
580 else
581 return -errno;
582 }
583
584 if (null_or_empty_fd(fileno(file))) {
585 log_debug("Skipping empty file: %s", filename);
586 return 0;
587 }
588
589 netdev_raw = new0(NetDev, 1);
590 if (!netdev_raw)
591 return log_oom();
592
593 netdev_raw->kind = _NETDEV_KIND_INVALID;
594
595 r = config_parse(NULL, filename, file,
596 "Match\0NetDev\0",
597 config_item_perf_lookup, network_netdev_gperf_lookup,
598 true, false, true, netdev_raw);
599 if (r < 0)
600 return r;
601
602 r = fseek(file, 0, SEEK_SET);
603 if (r < 0)
604 return -errno;
605
606 /* skip out early if configuration does not match the environment */
607 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
608 netdev_raw->match_host, netdev_raw->match_virt,
609 netdev_raw->match_kernel, netdev_raw->match_arch,
610 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
611 return 0;
612
613 if (!NETDEV_VTABLE(netdev_raw)) {
614 log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
615 return 0;
616 }
617
618 if (!netdev_raw->ifname) {
619 log_warning("NetDev without Name configured in %s. Ignoring", filename);
620 return 0;
621 }
622
623 netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
624 if (!netdev)
625 return log_oom();
626
627 netdev->n_ref = 1;
628 netdev->manager = manager;
629 netdev->state = _NETDEV_STATE_INVALID;
630 netdev->kind = netdev_raw->kind;
631 netdev->ifname = netdev_raw->ifname;
632
633 if (NETDEV_VTABLE(netdev)->init)
634 NETDEV_VTABLE(netdev)->init(netdev);
635
636 r = config_parse(NULL, filename, file,
637 NETDEV_VTABLE(netdev)->sections,
638 config_item_perf_lookup, network_netdev_gperf_lookup,
639 false, false, false, netdev);
640 if (r < 0)
641 return r;
642
643 /* verify configuration */
644 if (NETDEV_VTABLE(netdev)->config_verify) {
645 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
646 if (r < 0)
647 return 0;
648 }
649
650 netdev->filename = strdup(filename);
651 if (!netdev->filename)
652 return log_oom();
653
654 if (!netdev->mac) {
655 r = netdev_get_mac(netdev->ifname, &netdev->mac);
656 if (r < 0)
657 return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname);
658 }
659
660 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
661 if (r < 0)
662 return r;
663
664 LIST_HEAD_INIT(netdev->callbacks);
665
666 log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
667
668 switch (NETDEV_VTABLE(netdev)->create_type) {
669 case NETDEV_CREATE_MASTER:
670 case NETDEV_CREATE_INDEPENDENT:
671 r = netdev_create(netdev, NULL, NULL);
672 if (r < 0)
673 return 0;
674
675 break;
676 default:
677 break;
678 }
679
680 netdev = NULL;
681
682 return 0;
683 }
684
685 int netdev_load(Manager *manager) {
686 _cleanup_strv_free_ char **files = NULL;
687 NetDev *netdev;
688 char **f;
689 int r;
690
691 assert(manager);
692
693 while ((netdev = hashmap_first(manager->netdevs)))
694 netdev_unref(netdev);
695
696 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
697 if (r < 0)
698 return log_error_errno(r, "Failed to enumerate netdev files: %m");
699
700 STRV_FOREACH_BACKWARDS(f, files) {
701 r = netdev_load_one(manager, *f);
702 if (r < 0)
703 return r;
704 }
705
706 return 0;
707 }