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