networkd-netdev.c revision ca4e095ab9e970cb8fa472ae69ea1f0648041722
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina This file is part of systemd.
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina Copyright 2013 Tom Gundersen <teg@jklm.no>
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina systemd is free software; you can redistribute it and/or modify it
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina under the terms of the GNU Lesser General Public License as published by
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina the Free Software Foundation; either version 2.1 of the License, or
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina (at your option) any later version.
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina systemd is distributed in the hope that it will be useful, but
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina WITHOUT ANY WARRANTY; without even the implied warranty of
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina Lesser General Public License for more details.
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina You should have received a copy of the GNU Lesser General Public License
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina along with systemd; If not, see <http://www.gnu.org/licenses/>.
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaconst NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel BřezinaDEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel BřezinaDEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic void netdev_cancel_callbacks(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose callback->callback(netdev->manager->rtnl, m, link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_REMOVE(callbacks, netdev->callbacks, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina hashmap_remove(netdev->manager->netdevs, netdev->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev || netdev->state == NETDEV_STATE_LINGER)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_get(Manager *manager, const char *name, NetDev **ret) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_enter_failed(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not allocate RTM_SETLINK message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not append IFLA_MASTER attribute: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not send rtnetlink message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_enter_ready(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_join_callback *callback, *callback_next;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* enslave the links that were attempted to be enslaved before the
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina * link was ready */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = netdev_enslave_ready(netdev, callback->link, callback->callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_REMOVE(callbacks, netdev->callbacks, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina/* callback for netdev's created without a backing Link */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_netdev_unref_ NetDev *netdev = userdata;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->state != _NETDEV_STATE_INVALID);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "netdev exists, using existing");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina else if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = netdev_enslave_ready(netdev, link, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* the netdev is not yet read, save this request for when it is*/
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_PREPEND(callbacks, netdev->callbacks, cb);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina/* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (NETDEV_VTABLE(netdev)->fill_message_create_on_link) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not allocate RTM_SETLINK message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina NETDEV_VTABLE(netdev)->fill_message_create_on_link(netdev, link, req);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not send rtnetlink message: %s", strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return NETDEV_VTABLE(netdev)->enslave(netdev, link, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert_not_reached("Joining link to netdev of invalid kind");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get rtnl message type");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else if (ifindex <= 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* ifindex already set to the same for this netdev */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get IFNAME");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get LINKINFO");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get KIND");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not exit container");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* the kernel does not distinguish between tun and tap */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get kind");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Received newlink with wrong KIND %s, "
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_get_mac(const char *ifname, struct ether_addr **ret) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* fetch some persistent data unique to the machine */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* combine with some data unique (on this machine) to this
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* Let's hash the host machine ID plus the container name. We
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina * use a fixed, but originally randomly created hash key here. */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina memcpy(mac->ether_addr_octet, result, ETH_ALEN);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* see eth_random_addr in the kernel */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_load_one(Manager *manager, const char *filename) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug("Skipping empty file: %s", filename);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->bond_mode = _NETDEV_BOND_MODE_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Match\0NetDev\0VLAN\0MACVLAN\0VXLAN\0Tunnel\0Peer\0Tun\0Tap\0Bond\0",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina config_item_perf_lookup, network_netdev_gperf_lookup,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina false, false, true, netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* skip out early if configuration does not match the environment */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (net_match_config(NULL, NULL, NULL, NULL, NULL,
return log_oom();
strerror(-r));
char **files, **f;