]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
sd-bus: make sure we properly handle NULL callback functions
[thirdparty/systemd.git] / src / network / networkd-netdev.c
CommitLineData
02b59d57
TG
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 "networkd.h"
c6f7c917 23#include "network-internal.h"
02b59d57
TG
24#include "path-util.h"
25#include "conf-files.h"
26#include "conf-parser.h"
27#include "list.h"
28
672682a6
TG
29#define VLANID_MAX 4094
30
2c5859af 31static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
52433f6b 32 [NETDEV_KIND_BRIDGE] = "bridge",
54abf461
TG
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
fe6b2d55 35 [NETDEV_KIND_MACVLAN] = "macvlan",
7951dea2
SS
36 [NETDEV_KIND_IPIP] = "ipip",
37 [NETDEV_KIND_GRE] = "gre",
38 [NETDEV_KIND_SIT] = "sit",
52433f6b 39};
02b59d57 40
1a436809
TG
41DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
42DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
52433f6b 43
2c5859af 44static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
fe6b2d55
TG
45 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
46 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
47 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
48 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
49};
50
51DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
52DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
53
59cb64e6
TG
54static void netdev_cancel_callbacks(NetDev *netdev) {
55 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
52433f6b
TG
56 netdev_enslave_callback *callback;
57
58 if (!netdev)
02b59d57
TG
59 return;
60
59cb64e6
TG
61 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
62
52433f6b 63 while ((callback = netdev->callbacks)) {
59cb64e6
TG
64 if (m) {
65 assert(callback->link);
66 assert(callback->callback);
67 assert(netdev->manager);
68 assert(netdev->manager->rtnl);
69
70 callback->callback(netdev->manager->rtnl, m, link);
71 }
72
52433f6b 73 LIST_REMOVE(callbacks, netdev->callbacks, callback);
02b59d57
TG
74 free(callback);
75 }
59cb64e6
TG
76}
77
78static void netdev_free(NetDev *netdev) {
79 if (!netdev)
80 return;
81
82 netdev_cancel_callbacks(netdev);
02b59d57 83
52433f6b
TG
84 if (netdev->name)
85 hashmap_remove(netdev->manager->netdevs, netdev->name);
02b59d57 86
52433f6b 87 free(netdev->filename);
02b59d57 88
52433f6b
TG
89 free(netdev->description);
90 free(netdev->name);
02b59d57 91
79e16ce3
LP
92 condition_free_list(netdev->match_host);
93 condition_free_list(netdev->match_virt);
94 condition_free_list(netdev->match_kernel);
95 condition_free_list(netdev->match_arch);
96
52433f6b 97 free(netdev);
02b59d57
TG
98}
99
14b746f7
TG
100NetDev *netdev_unref(NetDev *netdev) {
101 if (netdev && (-- netdev->n_ref <= 0))
102 netdev_free(netdev);
103
104 return NULL;
105}
106
107NetDev *netdev_ref(NetDev *netdev) {
108 if (netdev)
109 assert_se(++ netdev->n_ref >= 2);
110
111 return netdev;
112}
113
2cc7e981
TG
114void netdev_drop(NetDev *netdev) {
115 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
116 return;
117
118 netdev->state = NETDEV_STATE_LINGER;
119
5e273efe 120 log_debug_netdev(netdev, "netdev removed");
370e9930 121
2cc7e981
TG
122 netdev_cancel_callbacks(netdev);
123
124 netdev_unref(netdev);
125
126 return;
127}
128
1a436809
TG
129int netdev_get(Manager *manager, const char *name, NetDev **ret) {
130 NetDev *netdev;
02b59d57
TG
131
132 assert(manager);
133 assert(name);
134 assert(ret);
135
52433f6b
TG
136 netdev = hashmap_get(manager->netdevs, name);
137 if (!netdev) {
02b59d57
TG
138 *ret = NULL;
139 return -ENOENT;
140 }
141
52433f6b 142 *ret = netdev;
02b59d57
TG
143
144 return 0;
145}
146
1a436809 147static int netdev_enter_failed(NetDev *netdev) {
52433f6b 148 netdev->state = NETDEV_STATE_FAILED;
02b59d57
TG
149
150 return 0;
151}
152
1a436809 153static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
cf6a8911 154 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
02b59d57
TG
155 int r;
156
52433f6b
TG
157 assert(netdev);
158 assert(netdev->state == NETDEV_STATE_READY);
4fb7242c
TG
159 assert(netdev->manager);
160 assert(netdev->manager->rtnl);
02b59d57
TG
161 assert(link);
162 assert(callback);
163
151b9b96
LP
164 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
165 RTM_SETLINK, link->ifindex);
02b59d57 166 if (r < 0) {
52433f6b 167 log_error_netdev(netdev,
3333d748
ZJS
168 "Could not allocate RTM_SETLINK message: %s",
169 strerror(-r));
02b59d57
TG
170 return r;
171 }
172
50add290 173 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
02b59d57 174 if (r < 0) {
52433f6b 175 log_error_netdev(netdev,
3333d748
ZJS
176 "Could not append IFLA_MASTER attribute: %s",
177 strerror(-r));
02b59d57
TG
178 return r;
179 }
180
52433f6b 181 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 182 if (r < 0) {
52433f6b 183 log_error_netdev(netdev,
3333d748
ZJS
184 "Could not send rtnetlink message: %s",
185 strerror(-r));
02b59d57
TG
186 return r;
187 }
188
52433f6b 189 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 190
02b59d57
TG
191 return 0;
192}
193
1a436809 194static int netdev_enter_ready(NetDev *netdev) {
52433f6b 195 netdev_enslave_callback *callback;
02b59d57 196
52433f6b
TG
197 assert(netdev);
198 assert(netdev->name);
924fe430 199
ba5596ec
TG
200 if (netdev->state != NETDEV_STATE_CREATING)
201 return 0;
202
52433f6b 203 netdev->state = NETDEV_STATE_READY;
02b59d57 204
52433f6b 205 log_info_netdev(netdev, "netdev ready");
02b59d57 206
52433f6b 207 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
b226d99b 208 /* enslave the links that were attempted to be enslaved before the
02b59d57 209 * link was ready */
52433f6b 210 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
211 }
212
213 return 0;
214}
52433f6b 215static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
1a436809 216 NetDev *netdev = userdata;
172f6635 217 int r;
02b59d57 218
52433f6b 219 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
220
221 r = sd_rtnl_message_get_errno(m);
e09826dc 222 if (r == -EEXIST)
505f8da7
TG
223 log_debug_netdev(netdev, "netdev exists, using existing");
224 else if (r < 0) {
37ebeb77
TG
225 log_warning_netdev(netdev, "netdev could not be greated: %s", strerror(-r));
226 netdev_drop(netdev);
dd3efc09
TG
227
228 return 1;
02b59d57
TG
229 }
230
dd3efc09 231 return 1;
02b59d57
TG
232}
233
7951dea2
SS
234int config_parse_tunnel_address(const char *unit,
235 const char *filename,
236 unsigned line,
237 const char *section,
238 unsigned section_line,
239 const char *lvalue,
240 int ltype,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
244 NetDev *n = data;
245 unsigned char family = AF_INET;
246 int r;
247
248 assert(filename);
249 assert(lvalue);
250 assert(rvalue);
251 assert(data);
252
253 r = net_parse_inaddr(rvalue, &family, n);
254 if (r < 0) {
255 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
256 "Tunnel address is invalid, ignoring assignment: %s", rvalue);
257 return 0;
258 }
259 return 0;
260}
261
1a436809 262static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
cf6a8911 263 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 264 const char *kind;
02b59d57
TG
265 int r;
266
52433f6b 267 assert(netdev);
fe6b2d55
TG
268 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
269 (link && callback));
52433f6b
TG
270 assert(netdev->name);
271 assert(netdev->manager);
272 assert(netdev->manager->rtnl);
02b59d57 273
151b9b96 274 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
02b59d57 275 if (r < 0) {
52433f6b 276 log_error_netdev(netdev,
3333d748
ZJS
277 "Could not allocate RTM_NEWLINK message: %s",
278 strerror(-r));
02b59d57
TG
279 return r;
280 }
281
54abf461
TG
282 if (link) {
283 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
284 if (r < 0) {
285 log_error_netdev(netdev,
286 "Could not append IFLA_LINK attribute: %s",
287 strerror(-r));
288 return r;
289 }
290 }
291
52433f6b 292 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 293 if (r < 0) {
52433f6b 294 log_error_netdev(netdev,
3333d748
ZJS
295 "Could not append IFLA_IFNAME attribute: %s",
296 strerror(-r));
02b59d57
TG
297 return r;
298 }
299
7951dea2
SS
300 if(netdev->mtu) {
301 r = sd_rtnl_message_append_u32(req, IFLA_MTU, netdev->mtu);
302 if (r < 0) {
303 log_error_netdev(netdev,
304 "Could not append IFLA_MTU attribute: %s",
305 strerror(-r));
306 return r;
307 }
308 }
309
ee3a6a51 310 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
02b59d57 311 if (r < 0) {
52433f6b 312 log_error_netdev(netdev,
3333d748
ZJS
313 "Could not open IFLA_LINKINFO container: %s",
314 strerror(-r));
02b59d57
TG
315 return r;
316 }
317
52433f6b
TG
318 kind = netdev_kind_to_string(netdev->kind);
319 if (!kind) {
320 log_error_netdev(netdev, "Invalid kind");
321 return -EINVAL;
322 }
323
d8e538ec 324 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
02b59d57 325 if (r < 0) {
52433f6b 326 log_error_netdev(netdev,
d8e538ec
TG
327 "Could not open IFLA_INFO_DATA container: %s",
328 strerror(-r));
02b59d57
TG
329 return r;
330 }
331
d8e538ec
TG
332 if (netdev->vlanid <= VLANID_MAX) {
333 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
54abf461
TG
334 if (r < 0) {
335 log_error_netdev(netdev,
d8e538ec 336 "Could not append IFLA_VLAN_ID attribute: %s",
54abf461
TG
337 strerror(-r));
338 return r;
339 }
d8e538ec 340 }
54abf461 341
d8e538ec
TG
342 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
343 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
344 if (r < 0) {
345 log_error_netdev(netdev,
346 "Could not append IFLA_MACVLAN_MODE attribute: %s",
347 strerror(-r));
54abf461
TG
348 return r;
349 }
350 }
351
d8e538ec
TG
352 r = sd_rtnl_message_close_container(req);
353 if (r < 0) {
354 log_error_netdev(netdev,
355 "Could not close IFLA_INFO_DATA container %s",
356 strerror(-r));
357 return r;
358 }
359
02b59d57
TG
360 r = sd_rtnl_message_close_container(req);
361 if (r < 0) {
52433f6b 362 log_error_netdev(netdev,
3333d748
ZJS
363 "Could not close IFLA_LINKINFO container %s",
364 strerror(-r));
02b59d57
TG
365 return r;
366 }
367
54abf461
TG
368 if (link)
369 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
370 else
371 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 372 if (r < 0) {
52433f6b 373 log_error_netdev(netdev,
3333d748 374 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
375 return r;
376 }
377
52433f6b 378 log_debug_netdev(netdev, "creating netdev");
02b59d57 379
52433f6b 380 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
381
382 return 0;
383}
384
1a436809 385int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
b226d99b
TG
386 int r;
387
fe6b2d55 388 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
54abf461
TG
389 return netdev_create(netdev, link, callback);
390
7951dea2
SS
391 if(netdev->kind == NETDEV_KIND_IPIP ||
392 netdev->kind == NETDEV_KIND_GRE ||
393 netdev->kind == NETDEV_KIND_SIT)
394 return netdev_create_tunnel(link, netdev_create_handler);
395
52433f6b 396 if (netdev->state == NETDEV_STATE_READY) {
b226d99b
TG
397 r = netdev_enslave_ready(netdev, link, callback);
398 if (r < 0)
399 return r;
02b59d57 400 } else {
52433f6b
TG
401 /* the netdev is not yet read, save this request for when it is*/
402 netdev_enslave_callback *cb;
02b59d57 403
52433f6b 404 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
405 if (!cb)
406 return log_oom();
407
408 cb->callback = callback;
409 cb->link = link;
410
52433f6b 411 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
412 }
413
414 return 0;
415}
416
d39edfc7 417int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
c3ebdce3 418 uint16_t type;
d39edfc7
TG
419 const char *kind;
420 char *received_kind;
c6315a7a 421 char *received_name;
d39edfc7
TG
422 int r, ifindex;
423
50add290 424 assert(netdev);
c3ebdce3 425 assert(message);
02b59d57 426
c3ebdce3 427 r = sd_rtnl_message_get_type(message, &type);
ba5596ec
TG
428 if (r < 0) {
429 log_error_netdev(netdev, "Could not get rtnl message type");
c3ebdce3 430 return r;
ba5596ec 431 }
c3ebdce3 432
ba5596ec
TG
433 if (type != RTM_NEWLINK) {
434 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
c3ebdce3 435 return -EINVAL;
ba5596ec 436 }
d39edfc7 437
a21df104
TG
438 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
439 if (r < 0) {
440 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
441 netdev_enter_failed(netdev);
442 return r;
443 } else if (ifindex <= 0) {
444 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
445 netdev_enter_failed(netdev);
446 return r;
447 }
448
449
450 if (netdev->ifindex > 0) {
451 if (netdev->ifindex != ifindex) {
452 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
453 ifindex, netdev->ifindex);
454 netdev_enter_failed(netdev);
455 return -EEXIST;
456 } else
457 /* ifindex already set to the same for this netdev */
458 return 0;
459 }
460
c6315a7a
TG
461 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
462 if (r < 0) {
463 log_error_netdev(netdev, "Could not get IFNAME");
464 return r;
465 }
466
467 if (!streq(netdev->name, received_name)) {
468 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
469 received_name);
470 netdev_enter_failed(netdev);
471 return r;
472 }
473
d39edfc7
TG
474 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
475 if (r < 0) {
476 log_error_netdev(netdev, "Could not get LINKINFO");
477 return r;
478 }
479
480 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
481 if (r < 0) {
482 log_error_netdev(netdev, "Could not get KIND");
483 return r;
484 }
485
505f8da7
TG
486 r = sd_rtnl_message_exit_container(message);
487 if (r < 0) {
488 log_error_netdev(netdev, "Could not exit container");
489 return r;
490 }
491
c3ebdce3
TG
492 kind = netdev_kind_to_string(netdev->kind);
493 if (!kind) {
494 log_error_netdev(netdev, "Could not get kind");
495 netdev_enter_failed(netdev);
496 return -EINVAL;
497 }
498
d39edfc7 499 if (!streq(kind, received_kind)) {
c6315a7a
TG
500 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
501 "expected %s", received_kind, kind);
d39edfc7
TG
502 netdev_enter_failed(netdev);
503 return r;
504 }
505
50add290 506 netdev->ifindex = ifindex;
52433f6b
TG
507
508 netdev_enter_ready(netdev);
02b59d57
TG
509
510 return 0;
511}
512
52433f6b 513static int netdev_load_one(Manager *manager, const char *filename) {
14b746f7 514 _cleanup_netdev_unref_ NetDev *netdev = NULL;
02b59d57
TG
515 _cleanup_fclose_ FILE *file = NULL;
516 int r;
517
bf1bc670
TA
518 assert(manager);
519 assert(filename);
520
6916ec29
TG
521 if (null_or_empty_path(filename)) {
522 log_debug("skipping empty file: %s", filename);
523 return 0;
524 }
525
02b59d57
TG
526 file = fopen(filename, "re");
527 if (!file) {
528 if (errno == ENOENT)
529 return 0;
530 else
ecb08ec6 531 return -errno;
02b59d57
TG
532 }
533
1a436809 534 netdev = new0(NetDev, 1);
52433f6b 535 if (!netdev)
02b59d57
TG
536 return log_oom();
537
14b746f7 538 netdev->n_ref = 1;
52433f6b
TG
539 netdev->manager = manager;
540 netdev->state = _NETDEV_STATE_INVALID;
541 netdev->kind = _NETDEV_KIND_INVALID;
fe6b2d55 542 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
672682a6 543 netdev->vlanid = VLANID_MAX + 1;
02b59d57 544
7951dea2 545 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0Tunnel\0",
fe6b2d55
TG
546 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
547 false, false, netdev);
02b59d57
TG
548 if (r < 0) {
549 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
550 return r;
449f7554 551 }
02b59d57 552
52433f6b 553 if (netdev->kind == _NETDEV_KIND_INVALID) {
1a436809 554 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
52433f6b
TG
555 return 0;
556 }
557
558 if (!netdev->name) {
1a436809 559 log_warning("NetDev without Name configured in %s. Ignoring", filename);
02b59d57
TG
560 return 0;
561 }
562
672682a6
TG
563 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
564 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
54abf461
TG
565 return 0;
566 }
567
fe6b2d55
TG
568 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
569 log_warning("VLAN Id configured for a %s in %s. Ignoring",
570 netdev_kind_to_string(netdev->kind), filename);
571 return 0;
572 }
573
574 if (netdev->kind != NETDEV_KIND_MACVLAN &&
575 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
576 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
577 netdev_kind_to_string(netdev->kind), filename);
578 return 0;
579 }
580
52433f6b
TG
581 netdev->filename = strdup(filename);
582 if (!netdev->filename)
02b59d57
TG
583 return log_oom();
584
c0dda186 585 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
edbb03e9
TG
586 netdev->match_host, netdev->match_virt,
587 netdev->match_kernel, netdev->match_arch,
bf175aaf 588 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
c0dda186
TG
589 return 0;
590
52433f6b 591 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
592 if (r < 0)
593 return r;
594
52433f6b 595 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 596
fe6b2d55 597 if (netdev->kind != NETDEV_KIND_VLAN &&
7951dea2
SS
598 netdev->kind != NETDEV_KIND_MACVLAN &&
599 netdev->kind != NETDEV_KIND_IPIP &&
600 netdev->kind != NETDEV_KIND_GRE &&
601 netdev->kind != NETDEV_KIND_SIT) {
54abf461
TG
602 r = netdev_create(netdev, NULL, NULL);
603 if (r < 0)
604 return r;
605 }
02b59d57 606
69ceb044
TG
607 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
608
52433f6b 609 netdev = NULL;
02b59d57
TG
610
611 return 0;
612}
613
52433f6b 614int netdev_load(Manager *manager) {
1a436809 615 NetDev *netdev;
02b59d57
TG
616 char **files, **f;
617 int r;
618
619 assert(manager);
620
52433f6b 621 while ((netdev = hashmap_first(manager->netdevs)))
14b746f7 622 netdev_unref(netdev);
02b59d57 623
2ad8416d 624 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
625 if (r < 0) {
626 log_error("Failed to enumerate netdev files: %s", strerror(-r));
627 return r;
628 }
629
630 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 631 r = netdev_load_one(manager, *f);
02b59d57
TG
632 if (r < 0)
633 return r;
634 }
635
636 strv_free(files);
637
638 return 0;
639}