From: Matthias Schiffer Date: Fri, 10 Feb 2017 06:33:03 +0100 Subject: netifd: device: add veth support diff --git a/package/network/config/netifd/patches/0001-device-add-veth-support.patch b/package/network/config/netifd/patches/0001-device-add-veth-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..9fdc184e98c070d0636fda5c6e5716295b86ecba --- /dev/null +++ b/package/network/config/netifd/patches/0001-device-add-veth-support.patch @@ -0,0 +1,437 @@ +From 006a6d3084cfd034f7d66cde3a0cbf58ab34c5a7 Mon Sep 17 00:00:00 2001 +Message-Id: <006a6d3084cfd034f7d66cde3a0cbf58ab34c5a7.1486704740.git.mschiffer@universe-factory.net> +From: Matthias Schiffer +Date: Fri, 10 Feb 2017 04:29:09 +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 9c734ea..2dd27c7 100644 +--- a/system-dummy.c ++++ b/system-dummy.c +@@ -275,6 +275,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 2f15bf1..73e841b 100644 +--- a/system-linux.c ++++ b/system-linux.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + + #ifndef RTN_FAILED_POLICY +@@ -1132,6 +1133,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 d5cb4e3..f4dd02b 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.1 +