From 6397f5edb977b5963aa8eec1deaa709355bbbc7d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 10 Feb 2017 06:30:17 +0100 Subject: [PATCH] device: add veth support The veth config code mostly handles the primary interface of a veth pair, the secondary interface is not explicitly referenced and will be found as an unrelated interface after the pair has been created. This doesn't only allow us to keep the veth code simple (and similar to existing device handlers), but will also avoid complicating handling unnecessarily in case the secondary interface is moved into another network namespace. Signed-off-by: Matthias Schiffer --- CMakeLists.txt | 2 +- system-dummy.c | 10 +++ system-linux.c | 61 ++++++++++++++ system.h | 18 +++++ veth.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 veth.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f35d26..d54b6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ SET(SOURCES main.c utils.c system.c tunnel.c handler.c interface.c interface-ip.c interface-event.c iprule.c proto.c proto-static.c proto-shell.c - config.c device.c bridge.c vlan.c alias.c + config.c device.c bridge.c veth.c vlan.c alias.c macvlan.c ubus.c vlandev.c wireless.c) diff --git a/system-dummy.c b/system-dummy.c index 2ea25ac..165438c 100644 --- a/system-dummy.c +++ b/system-dummy.c @@ -281,6 +281,16 @@ int system_macvlan_del(struct device *macvlan) return 0; } +int system_veth_add(struct device *veth, struct veth_config *cfg) +{ + return 0; +} + +int system_veth_del(struct device *veth) +{ + return 0; +} + int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg) { return 0; diff --git a/system-linux.c b/system-linux.c index 8a1173c..e0cb5bc 100644 --- a/system-linux.c +++ b/system-linux.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #ifndef RTN_FAILED_POLICY @@ -1131,6 +1132,66 @@ int system_macvlan_del(struct device *macvlan) return system_link_del(macvlan->ifname); } +int system_veth_add(struct device *veth, struct veth_config *cfg) +{ + struct nl_msg *msg; + struct ifinfomsg empty_iim = {}; + struct nlattr *linkinfo, *data, *veth_info; + int rv; + + msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); + + if (!msg) + return -1; + + nlmsg_append(msg, &empty_iim, sizeof(empty_iim), 0); + + if (cfg->flags & VETH_OPT_MACADDR) + nla_put(msg, IFLA_ADDRESS, sizeof(cfg->macaddr), cfg->macaddr); + nla_put_string(msg, IFLA_IFNAME, veth->ifname); + + if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + nla_put_string(msg, IFLA_INFO_KIND, "veth"); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (!(veth_info = nla_nest_start(msg, VETH_INFO_PEER))) + goto nla_put_failure; + + nlmsg_append(msg, &empty_iim, sizeof(empty_iim), 0); + + if (cfg->flags & VETH_OPT_PEER_NAME) + nla_put_string(msg, IFLA_IFNAME, cfg->peer_name); + if (cfg->flags & VETH_OPT_PEER_MACADDR) + nla_put(msg, IFLA_ADDRESS, sizeof(cfg->peer_macaddr), cfg->peer_macaddr); + + nla_nest_end(msg, veth_info); + nla_nest_end(msg, data); + nla_nest_end(msg, linkinfo); + + rv = system_rtnl_call(msg); + if (rv) { + if (cfg->flags & VETH_OPT_PEER_NAME) + D(SYSTEM, "Error adding veth '%s' with peer '%s': %d\n", veth->ifname, cfg->peer_name, rv); + else + D(SYSTEM, "Error adding veth '%s': %d\n", veth->ifname, rv); + } + + return rv; + +nla_put_failure: + nlmsg_free(msg); + return -ENOMEM; +} + +int system_veth_del(struct device *veth) +{ + return system_link_del(veth->ifname); +} + static int system_vlan(struct device *dev, int id) { struct vlan_ioctl_args ifr = { diff --git a/system.h b/system.h index 9c40e4c..6077b95 100644 --- a/system.h +++ b/system.h @@ -14,6 +14,7 @@ #ifndef __NETIFD_SYSTEM_H #define __NETIFD_SYSTEM_H +#include #include #include #include @@ -82,6 +83,20 @@ struct macvlan_config { unsigned char macaddr[6]; }; +enum veth_opt { + VETH_OPT_MACADDR = (1 << 0), + VETH_OPT_PEER_NAME = (1 << 1), + VETH_OPT_PEER_MACADDR = (1 << 2), +}; + +struct veth_config { + enum veth_opt flags; + + unsigned char macaddr[6]; + char peer_name[IFNAMSIZ]; + unsigned char peer_macaddr[6]; +}; + enum vlan_proto { VLAN_PROTO_8021Q = 0x8100, VLAN_PROTO_8021AD = 0x88A8 @@ -118,6 +133,9 @@ int system_bridge_delif(struct device *bridge, struct device *dev); int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvlan_config *cfg); int system_macvlan_del(struct device *macvlan); +int system_veth_add(struct device *veth, struct veth_config *cfg); +int system_veth_del(struct device *veth); + int system_vlan_add(struct device *dev, int id); int system_vlan_del(struct device *dev); diff --git a/veth.c b/veth.c new file mode 100644 index 0000000..e109f27 --- /dev/null +++ b/veth.c @@ -0,0 +1,247 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2012 Felix Fietkau + * Copyright (C) 2013 Jo-Philipp Wich + * Copyright (C) 2017 Matthias Schiffer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include + +#ifdef linux +#include +#endif + +#include "netifd.h" +#include "device.h" +#include "interface.h" +#include "system.h" + +enum { + VETH_ATTR_MACADDR, + VETH_ATTR_PEER_NAME, + VETH_ATTR_PEER_MACADDR, + __VETH_ATTR_MAX +}; + +static const struct blobmsg_policy veth_attrs[__VETH_ATTR_MAX] = { + [VETH_ATTR_MACADDR] = { "macaddr", BLOBMSG_TYPE_STRING }, + [VETH_ATTR_PEER_NAME] = { "peer_name", BLOBMSG_TYPE_STRING }, + [VETH_ATTR_PEER_MACADDR] = { "peer_macaddr", BLOBMSG_TYPE_STRING }, +}; + +static const struct uci_blob_param_list veth_attr_list = { + .n_params = __VETH_ATTR_MAX, + .params = veth_attrs, + + .n_next = 1, + .next = { &device_attr_list }, +}; + +struct veth { + struct device dev; + + device_state_cb set_state; + + struct blob_attr *config_data; + struct veth_config config; +}; + +static int +veth_set_down(struct veth *veth) +{ + veth->set_state(&veth->dev, false); + system_veth_del(&veth->dev); + + return 0; +} + +static int +veth_set_up(struct veth *veth) +{ + int ret; + + ret = system_veth_add(&veth->dev, &veth->config); + if (ret < 0) + return ret; + + ret = veth->set_state(&veth->dev, true); + if (ret) + goto delete; + + return 0; + +delete: + system_veth_del(&veth->dev); + return ret; +} + +static int +veth_set_state(struct device *dev, bool up) +{ + struct veth *veth; + + D(SYSTEM, "veth_set_state(%s, %u)\n", dev->ifname, up); + + veth = container_of(dev, struct veth, dev); + if (up) + return veth_set_up(veth); + else + return veth_set_down(veth); +} + +static void +veth_free(struct device *dev) +{ + struct veth *veth; + + veth = container_of(dev, struct veth, dev); + free(veth->config_data); + free(veth); +} + +static void +veth_dump_info(struct device *dev, struct blob_buf *b) +{ + struct veth *veth; + + veth = container_of(dev, struct veth, dev); + if (veth->config.flags & VETH_OPT_PEER_NAME) + blobmsg_add_string(b, "peer", veth->config.peer_name); + system_if_dump_info(dev, b); +} + +static void +veth_config_init(struct device *dev) +{ + device_set_present(dev, true); +} + +static void +veth_apply_settings(struct veth *veth, struct blob_attr **tb) +{ + struct veth_config *cfg = &veth->config; + struct blob_attr *cur; + struct ether_addr *ea; + + cfg->flags = 0; + + if ((cur = tb[VETH_ATTR_MACADDR])) + { + ea = ether_aton(blobmsg_data(cur)); + if (ea) { + memcpy(cfg->macaddr, ea, 6); + cfg->flags |= VETH_OPT_MACADDR; + } + } + + if ((cur = tb[VETH_ATTR_PEER_NAME])) + { + strncpy(cfg->peer_name, blobmsg_get_string(cur), sizeof(cfg->peer_name)-1); + cfg->flags |= VETH_OPT_PEER_NAME; + } + + if ((cur = tb[VETH_ATTR_PEER_MACADDR])) + { + ea = ether_aton(blobmsg_data(cur)); + if (ea) { + memcpy(cfg->peer_macaddr, ea, 6); + cfg->flags |= VETH_OPT_PEER_MACADDR; + } + } +} + +static enum dev_change_type +veth_reload(struct device *dev, struct blob_attr *attr) +{ + struct blob_attr *tb_dev[__DEV_ATTR_MAX]; + struct blob_attr *tb_mv[__VETH_ATTR_MAX]; + enum dev_change_type ret = DEV_CONFIG_APPLIED; + struct veth *veth; + + veth = container_of(dev, struct veth, dev); + attr = blob_memdup(attr); + + blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev, + blob_data(attr), blob_len(attr)); + blobmsg_parse(veth_attrs, __VETH_ATTR_MAX, tb_mv, + blob_data(attr), blob_len(attr)); + + device_init_settings(dev, tb_dev); + veth_apply_settings(veth, tb_mv); + + if (veth->config_data) { + struct blob_attr *otb_dev[__DEV_ATTR_MAX]; + struct blob_attr *otb_mv[__VETH_ATTR_MAX]; + + blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev, + blob_data(veth->config_data), blob_len(veth->config_data)); + + if (uci_blob_diff(tb_dev, otb_dev, &device_attr_list, NULL)) + ret = DEV_CONFIG_RESTART; + + blobmsg_parse(veth_attrs, __VETH_ATTR_MAX, otb_mv, + blob_data(veth->config_data), blob_len(veth->config_data)); + + if (uci_blob_diff(tb_mv, otb_mv, &veth_attr_list, NULL)) + ret = DEV_CONFIG_RESTART; + + veth_config_init(dev); + } + + free(veth->config_data); + veth->config_data = attr; + return ret; +} + +static struct device * +veth_create(const char *name, struct device_type *devtype, + struct blob_attr *attr) +{ + struct veth *veth; + struct device *dev = NULL; + + veth = calloc(1, sizeof(*veth)); + if (!veth) + return NULL; + + dev = &veth->dev; + device_init(dev, devtype, name); + dev->config_pending = true; + + veth->set_state = dev->set_state; + dev->set_state = veth_set_state; + + dev->hotplug_ops = NULL; + + veth_reload(dev, attr); + + return dev; +} + +static struct device_type veth_device_type = { + .name = "veth", + .config_params = &veth_attr_list, + .create = veth_create, + .config_init = veth_config_init, + .reload = veth_reload, + .free = veth_free, + .dump_info = veth_dump_info, +}; + +static void __init veth_device_type_init(void) +{ + device_type_add(&veth_device_type); +} -- 2.11.0