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