networkd-netdev.c revision ca4e095ab9e970cb8fa472ae69ea1f0648041722
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina/***
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina This file is part of systemd.
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina Copyright 2013 Tom Gundersen <teg@jklm.no>
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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
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
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řezina***/
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include <net/if.h>
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-bridge.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-bond.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-vlan.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-macvlan.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-vxlan.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-tunnel.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-veth.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-dummy.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "networkd-netdev-tuntap.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "network-internal.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "path-util.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "conf-files.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "conf-parser.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "list.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina#include "siphash24.h"
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaconst NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_BRIDGE] = &bridge_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_BOND] = &bond_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VLAN] = &vlan_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VXLAN] = &vxlan_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_IPIP] = &ipip_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_GRE] = &gre_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_SIT] = &sit_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VTI] = &vti_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VETH] = &veth_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_DUMMY] = &dummy_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_TUN] = &tun_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_TAP] = &tap_vtable,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina};
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_BRIDGE] = "bridge",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_BOND] = "bond",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VLAN] = "vlan",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_MACVLAN] = "macvlan",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VXLAN] = "vxlan",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_IPIP] = "ipip",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_GRE] = "gre",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_SIT] = "sit",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VETH] = "veth",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_VTI] = "vti",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_DUMMY] = "dummy",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_TUN] = "tun",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina [NETDEV_KIND_TAP] = "tap",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina};
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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řezina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic void netdev_cancel_callbacks(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_join_callback *callback;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina while ((callback = netdev->callbacks)) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (m) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(callback->link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(callback->callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->manager);
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose assert(netdev->manager->rtnl);
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose callback->callback(netdev->manager->rtnl, m, link);
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose }
35fa5a83ce8badf6bc868937047f44c3f32b7c28Sumit Bose
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_REMOVE(callbacks, netdev->callbacks, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic void netdev_free(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_cancel_callbacks(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->ifname)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina hashmap_remove(netdev->manager->netdevs, netdev->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->filename);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->description);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->ifname_peer);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->mac);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->mac_peer);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->user_name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev->group_name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina condition_free_list(netdev->match_host);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina condition_free_list(netdev->match_virt);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina condition_free_list(netdev->match_kernel);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina condition_free_list(netdev->match_arch);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel BřezinaNetDev *netdev_unref(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev && (-- netdev->n_ref <= 0))
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_free(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel BřezinaNetDev *netdev_ref(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert_se(++ netdev->n_ref >= 2);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return netdev;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinavoid netdev_drop(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev || netdev->state == NETDEV_STATE_LINGER)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->state = NETDEV_STATE_LINGER;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "netdev removed");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_cancel_callbacks(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_unref(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_get(Manager *manager, const char *name, NetDev **ret) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina NetDev *netdev;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(manager);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(ret);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev = hashmap_get(manager->netdevs, name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina *ret = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -ENOENT;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina *ret = netdev;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_enter_failed(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->state = NETDEV_STATE_FAILED;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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 int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->state == NETDEV_STATE_READY);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->manager);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->manager->rtnl);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina RTM_SETLINK, link->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not allocate RTM_SETLINK message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not append IFLA_MASTER attribute: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not send rtnetlink message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina link_ref(link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_enter_ready(NetDev *netdev) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_join_callback *callback, *callback_next;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->state != NETDEV_STATE_CREATING)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->state = NETDEV_STATE_READY;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_info_netdev(netdev, "netdev ready");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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 if (r < 0)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_REMOVE(callbacks, netdev->callbacks, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina link_unref(callback->link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina free(callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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 int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->state != _NETDEV_STATE_INVALID);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_get_errno(m);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r == -EEXIST)
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řezina netdev_drop(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 1;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 1;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->state == NETDEV_STATE_READY) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = netdev_enslave_ready(netdev, link, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* the netdev is not yet read, save this request for when it is*/
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_join_callback *cb;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina cb = new0(netdev_join_callback, 1);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!cb)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return log_oom();
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina cb->callback = callback;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina cb->link = link;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina link_ref(link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina LIST_PREPEND(callbacks, netdev->callbacks, cb);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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 int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->manager);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev->manager->rtnl);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(NETDEV_VTABLE(netdev));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina RTM_NEWLINK, 0);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not allocate RTM_SETLINK message: %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina NETDEV_VTABLE(netdev)->fill_message_create_on_link(netdev, link, req);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Could not send rtnetlink message: %s", strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina link_ref(link);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else if (NETDEV_VTABLE(netdev)->enslave) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return NETDEV_VTABLE(netdev)->enslave(netdev, link, callback);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert_not_reached("Joining link to netdev of invalid kind");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina uint16_t type;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina const char *kind;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina const char *received_kind;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina const char *received_name;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina int r, ifindex;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(message);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_get_type(message, &type);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get rtnl message type");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (type != RTM_NEWLINK) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -EINVAL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else if (ifindex <= 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->ifindex > 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->ifindex != ifindex) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina ifindex, netdev->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -EEXIST;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina } else
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* ifindex already set to the same for this netdev */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get IFNAME");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!streq(netdev->ifname, received_name)) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina received_name);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get LINKINFO");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get KIND");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_rtnl_message_exit_container(message);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not exit container");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (netdev->kind == NETDEV_KIND_TAP)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* the kernel does not distinguish between tun and tap */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina kind = "tun";
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina else {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina kind = netdev_kind_to_string(netdev->kind);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!kind) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev, "Could not get kind");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -EINVAL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!streq(kind, received_kind)) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_error_netdev(netdev,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "Received newlink with wrong KIND %s, "
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina "expected %s", received_kind, kind);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_failed(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->ifindex = ifindex;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev_enter_ready(netdev);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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řezina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinaint netdev_get_mac(const char *ifname, struct ether_addr **ret) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_free_ struct ether_addr *mac = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina uint8_t result[8];
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina size_t l, sz;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina uint8_t *v;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(ret);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina mac = new0(struct ether_addr, 1);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!mac)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -ENOMEM;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina l = strlen(ifname);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina sz = sizeof(sd_id128_t) + l;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina v = alloca(sz);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* fetch some persistent data unique to the machine */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = sd_id128_get_machine((sd_id128_t*) v);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (r < 0)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina /* combine with some data unique (on this machine) to this
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina * netdev */
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina memcpy(v + sizeof(sd_id128_t), ifname, l);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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 siphash24(result, v, sz, HASH_KEY.bytes);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert_cc(ETH_ALEN <= sizeof(result));
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina memcpy(mac->ether_addr_octet, result, ETH_ALEN);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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řezina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina *ret = mac;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina mac = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina}
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březinastatic int netdev_load_one(Manager *manager, const char *filename) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_netdev_unref_ NetDev *netdev = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina _cleanup_fclose_ FILE *file = NULL;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina int r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(manager);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina assert(filename);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina file = fopen(filename, "re");
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!file) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (errno == ENOENT)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina else
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return -errno;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (null_or_empty_fd(fileno(file))) {
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina log_debug("Skipping empty file: %s", filename);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina }
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev = new0(NetDev, 1);
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina if (!netdev)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return log_oom();
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->n_ref = 1;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->manager = manager;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->state = _NETDEV_STATE_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->kind = _NETDEV_KIND_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->bond_mode = _NETDEV_BOND_MODE_INVALID;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->vlanid = VLANID_MAX + 1;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->vxlanid = VXLAN_VID_MAX + 1;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->tunnel_pmtudisc = true;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->learning = true;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina r = config_parse(NULL, filename, file,
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 if (r < 0)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return r;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
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,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->match_host, netdev->match_virt,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina netdev->match_kernel, netdev->match_arch,
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina return 0;
d3dee2a07f1a8ee9ae6f94e149ced754ef76c248Pavel Březina
if (!NETDEV_VTABLE(netdev)) {
log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
return 0;
}
/* verify configuration */
if (NETDEV_VTABLE(netdev)->config_verify) {
r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
if (r < 0)
return 0;
}
if (!netdev->ifname) {
log_warning("NetDev without Name configured in %s. Ignoring", filename);
return 0;
}
if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
log_warning("VLAN Id configured for a %s in %s. Ignoring",
netdev_kind_to_string(netdev->kind), filename);
return 0;
}
if (netdev->kind != NETDEV_KIND_VXLAN && netdev->vxlanid <= VXLAN_VID_MAX) {
log_warning("VXLAN Id configured for a %s in %s. Ignoring",
netdev_kind_to_string(netdev->kind), filename);
return 0;
}
if (netdev->kind != NETDEV_KIND_MACVLAN &&
netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
netdev_kind_to_string(netdev->kind), filename);
return 0;
}
netdev->filename = strdup(filename);
if (!netdev->filename)
return log_oom();
if (!netdev->mac) {
r = netdev_get_mac(netdev->ifname, &netdev->mac);
if (r < 0) {
log_error("Failed to generate predictable MAC address for %s",
netdev->ifname);
return r;
}
}
r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
if (r < 0)
return r;
LIST_HEAD_INIT(netdev->callbacks);
log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
/* create netdev */
if (NETDEV_VTABLE(netdev)->fill_message_create) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
if (r < 0) {
log_error_netdev(netdev,
"Could not allocate RTM_NEWLINK message: %s",
strerror(-r));
return r;
}
r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, m);
if (r < 0)
return r;
r = sd_rtnl_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
if (r < 0) {
log_error_netdev(netdev,
"Could not send rtnetlink message: %s", strerror(-r));
return r;
}
netdev_ref(netdev);
log_debug_netdev(netdev, "creating");
netdev->state = NETDEV_STATE_CREATING;
} else if (NETDEV_VTABLE(netdev)->create) {
r = NETDEV_VTABLE(netdev)->create(netdev);
if (r < 0)
return r;
}
netdev = NULL;
return 0;
}
int netdev_load(Manager *manager) {
NetDev *netdev;
char **files, **f;
int r;
assert(manager);
while ((netdev = hashmap_first(manager->netdevs)))
netdev_unref(netdev);
r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
if (r < 0) {
log_error("Failed to enumerate netdev files: %s", strerror(-r));
return r;
}
STRV_FOREACH_BACKWARDS(f, files) {
r = netdev_load_one(manager, *f);
if (r < 0)
return r;
}
strv_free(files);
return 0;
}