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