1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
24 #include "networkd-netdev.h"
25 #include "networkd-link.h"
26 #include "network-internal.h"
27 #include "path-util.h"
28 #include "conf-files.h"
29 #include "conf-parser.h"
31 #include "siphash24.h"
33 const 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,
49 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
50 [NETDEV_KIND_BRIDGE] = "bridge",
51 [NETDEV_KIND_BOND] = "bond",
52 [NETDEV_KIND_VLAN] = "vlan",
53 [NETDEV_KIND_MACVLAN] = "macvlan",
54 [NETDEV_KIND_VXLAN] = "vxlan",
55 [NETDEV_KIND_IPIP] = "ipip",
56 [NETDEV_KIND_GRE] = "gre",
57 [NETDEV_KIND_SIT] = "sit",
58 [NETDEV_KIND_VETH] = "veth",
59 [NETDEV_KIND_VTI] = "vti",
60 [NETDEV_KIND_DUMMY] = "dummy",
61 [NETDEV_KIND_TUN] = "tun",
62 [NETDEV_KIND_TAP] = "tap",
65 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
66 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
69 static void netdev_cancel_callbacks(NetDev *netdev) {
70 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
71 netdev_join_callback *callback;
76 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
78 while ((callback = netdev->callbacks)) {
80 assert(callback->link);
81 assert(callback->callback);
82 assert(netdev->manager);
83 assert(netdev->manager->rtnl);
85 callback->callback(netdev->manager->rtnl, m, link);
88 LIST_REMOVE(callbacks, netdev->callbacks, callback);
93 static void netdev_free(NetDev *netdev) {
97 netdev_cancel_callbacks(netdev);
100 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
102 free(netdev->filename);
104 free(netdev->description);
105 free(netdev->ifname);
108 condition_free_list(netdev->match_host);
109 condition_free_list(netdev->match_virt);
110 condition_free_list(netdev->match_kernel);
111 condition_free_list(netdev->match_arch);
113 if (NETDEV_VTABLE(netdev) &&
114 NETDEV_VTABLE(netdev)->done)
115 NETDEV_VTABLE(netdev)->done(netdev);
120 NetDev *netdev_unref(NetDev *netdev) {
121 if (netdev && (-- netdev->n_ref <= 0))
127 NetDev *netdev_ref(NetDev *netdev) {
129 assert_se(++ netdev->n_ref >= 2);
134 void netdev_drop(NetDev *netdev) {
135 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
138 netdev->state = NETDEV_STATE_LINGER;
140 log_debug_netdev(netdev, "netdev removed");
142 netdev_cancel_callbacks(netdev);
144 netdev_unref(netdev);
149 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
156 netdev = hashmap_get(manager->netdevs, name);
167 static int netdev_enter_failed(NetDev *netdev) {
168 netdev->state = NETDEV_STATE_FAILED;
173 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
174 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
178 assert(netdev->state == NETDEV_STATE_READY);
179 assert(netdev->manager);
180 assert(netdev->manager->rtnl);
181 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
185 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
186 RTM_SETLINK, link->ifindex);
188 log_error_netdev(netdev,
189 "Could not allocate RTM_SETLINK message: %s",
194 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
196 log_error_netdev(netdev,
197 "Could not append IFLA_MASTER attribute: %s",
202 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
204 log_error_netdev(netdev,
205 "Could not send rtnetlink message: %s",
212 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
217 static int netdev_enter_ready(NetDev *netdev) {
218 netdev_join_callback *callback, *callback_next;
222 assert(netdev->ifname);
224 if (netdev->state != NETDEV_STATE_CREATING)
227 netdev->state = NETDEV_STATE_READY;
229 log_info_netdev(netdev, "netdev ready");
231 LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
232 /* enslave the links that were attempted to be enslaved before the
234 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
238 LIST_REMOVE(callbacks, netdev->callbacks, callback);
239 link_unref(callback->link);
246 /* callback for netdev's created without a backing Link */
247 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
248 _cleanup_netdev_unref_ NetDev *netdev = userdata;
251 assert(netdev->state != _NETDEV_STATE_INVALID);
253 r = sd_rtnl_message_get_errno(m);
255 log_debug_netdev(netdev, "netdev exists, using existing");
257 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
263 log_debug_netdev(netdev, "created");
268 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
272 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
274 if (netdev->state == NETDEV_STATE_READY) {
275 r = netdev_enslave_ready(netdev, link, callback);
279 /* the netdev is not yet read, save this request for when it is*/
280 netdev_join_callback *cb;
282 cb = new0(netdev_join_callback, 1);
286 cb->callback = callback;
290 LIST_PREPEND(callbacks, netdev->callbacks, cb);
292 log_debug_netdev(netdev, "will enslave '%s', when reday",
299 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
302 const char *received_kind;
303 const char *received_name;
309 r = sd_rtnl_message_get_type(message, &type);
311 log_error_netdev(netdev, "Could not get rtnl message type");
315 if (type != RTM_NEWLINK) {
316 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
320 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
322 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
323 netdev_enter_failed(netdev);
325 } else if (ifindex <= 0) {
326 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
327 netdev_enter_failed(netdev);
331 if (netdev->ifindex > 0) {
332 if (netdev->ifindex != ifindex) {
333 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
334 ifindex, netdev->ifindex);
335 netdev_enter_failed(netdev);
338 /* ifindex already set to the same for this netdev */
342 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
344 log_error_netdev(netdev, "Could not get IFNAME");
348 if (!streq(netdev->ifname, received_name)) {
349 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
351 netdev_enter_failed(netdev);
355 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
357 log_error_netdev(netdev, "Could not get LINKINFO");
361 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
363 log_error_netdev(netdev, "Could not get KIND");
367 r = sd_rtnl_message_exit_container(message);
369 log_error_netdev(netdev, "Could not exit container");
373 if (netdev->kind == NETDEV_KIND_TAP)
374 /* the kernel does not distinguish between tun and tap */
377 kind = netdev_kind_to_string(netdev->kind);
379 log_error_netdev(netdev, "Could not get kind");
380 netdev_enter_failed(netdev);
385 if (!streq(kind, received_kind)) {
386 log_error_netdev(netdev,
387 "Received newlink with wrong KIND %s, "
388 "expected %s", received_kind, kind);
389 netdev_enter_failed(netdev);
393 netdev->ifindex = ifindex;
395 log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
397 netdev_enter_ready(netdev);
402 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
404 int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
405 _cleanup_free_ struct ether_addr *mac = NULL;
414 mac = new0(struct ether_addr, 1);
419 sz = sizeof(sd_id128_t) + l;
422 /* fetch some persistent data unique to the machine */
423 r = sd_id128_get_machine((sd_id128_t*) v);
427 /* combine with some data unique (on this machine) to this
429 memcpy(v + sizeof(sd_id128_t), ifname, l);
431 /* Let's hash the host machine ID plus the container name. We
432 * use a fixed, but originally randomly created hash key here. */
433 siphash24(result, v, sz, HASH_KEY.bytes);
435 assert_cc(ETH_ALEN <= sizeof(result));
436 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
438 /* see eth_random_addr in the kernel */
439 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
440 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
448 static int netdev_create(NetDev *netdev, Link *link) {
454 if (NETDEV_VTABLE(netdev)->create) {
455 r = NETDEV_VTABLE(netdev)->create(netdev);
459 log_debug_netdev(netdev, "created");
461 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
463 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
465 log_error_netdev(netdev,
466 "Could not allocate RTM_NEWLINK message: %s",
471 r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname);
473 log_error_netdev(netdev,
474 "Could not append IFLA_IFNAME, attribute: %s",
480 r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
482 log_error_netdev(netdev,
483 "Could not append IFLA_ADDRESS attribute: %s",
490 r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu);
492 log_error_netdev(netdev,
493 "Could not append IFLA_MTU attribute: %s",
500 r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex);
502 log_error_netdev(netdev,
503 "Colud not append IFLA_LINK attribute: %s",
509 r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
511 log_error_netdev(netdev,
512 "Could not append IFLA_LINKINFO attribute: %s",
517 r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA,
518 netdev_kind_to_string(netdev->kind));
520 log_error_netdev(netdev,
521 "Could not append IFLA_INFO_DATA attribute: %s",
526 if (NETDEV_VTABLE(netdev)->fill_message_create) {
527 r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
532 r = sd_rtnl_message_close_container(m);
534 log_error_netdev(netdev,
535 "Could not append IFLA_LINKINFO attribute: %s",
540 r = sd_rtnl_message_close_container(m);
542 log_error_netdev(netdev,
543 "Could not append IFLA_LINKINFO attribute: %s",
549 r = sd_rtnl_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
551 log_error_netdev(netdev,
552 "Could not send rtnetlink message: %s", strerror(-r));
558 netdev->state = NETDEV_STATE_CREATING;
560 log_debug_netdev(netdev, "creating");
566 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
567 int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
571 assert(netdev->manager);
572 assert(netdev->manager->rtnl);
573 assert(NETDEV_VTABLE(netdev));
575 switch (NETDEV_VTABLE(netdev)->create_type) {
576 case NETDEV_CREATE_MASTER:
577 r = netdev_enslave(netdev, link, callback);
582 case NETDEV_CREATE_STACKED:
583 r = netdev_create(netdev, link);
589 assert_not_reached("Can not join independent netdev");
595 static int netdev_load_one(Manager *manager, const char *filename) {
596 _cleanup_netdev_unref_ NetDev *netdev = NULL;
597 _cleanup_free_ NetDev *netdev_raw = NULL;
598 _cleanup_fclose_ FILE *file = NULL;
604 file = fopen(filename, "re");
612 if (null_or_empty_fd(fileno(file))) {
613 log_debug("Skipping empty file: %s", filename);
617 netdev_raw = new0(NetDev, 1);
621 netdev_raw->kind = _NETDEV_KIND_INVALID;
623 r = config_parse(NULL, filename, file,
625 config_item_perf_lookup, network_netdev_gperf_lookup,
626 true, false, true, netdev_raw);
630 r = fseek(file, 0, SEEK_SET);
634 /* skip out early if configuration does not match the environment */
635 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
636 netdev_raw->match_host, netdev_raw->match_virt,
637 netdev_raw->match_kernel, netdev_raw->match_arch,
638 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
641 if (!NETDEV_VTABLE(netdev_raw)) {
642 log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
646 if (!netdev_raw->ifname) {
647 log_warning("NetDev without Name configured in %s. Ignoring", filename);
651 netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
656 netdev->manager = manager;
657 netdev->state = _NETDEV_STATE_INVALID;
658 netdev->kind = netdev_raw->kind;
659 netdev->ifname = netdev_raw->ifname;
661 if (NETDEV_VTABLE(netdev)->init)
662 NETDEV_VTABLE(netdev)->init(netdev);
664 r = config_parse(NULL, filename, file,
665 NETDEV_VTABLE(netdev)->sections,
666 config_item_perf_lookup, network_netdev_gperf_lookup,
667 false, false, false, netdev);
671 /* verify configuration */
672 if (NETDEV_VTABLE(netdev)->config_verify) {
673 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
678 netdev->filename = strdup(filename);
679 if (!netdev->filename)
683 r = netdev_get_mac(netdev->ifname, &netdev->mac);
685 log_error("Failed to generate predictable MAC address for %s",
691 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
695 LIST_HEAD_INIT(netdev->callbacks);
697 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
699 switch (NETDEV_VTABLE(netdev)->create_type) {
700 case NETDEV_CREATE_MASTER:
701 case NETDEV_CREATE_INDEPENDENT:
702 r = netdev_create(netdev, NULL);
716 int netdev_load(Manager *manager) {
723 while ((netdev = hashmap_first(manager->netdevs)))
724 netdev_unref(netdev);
726 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
728 log_error("Failed to enumerate netdev files: %s", strerror(-r));
732 STRV_FOREACH_BACKWARDS(f, files) {
733 r = netdev_load_one(manager, *f);