diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | system-dummy.c | 10 | ||||
-rw-r--r-- | system-linux.c | 61 | ||||
-rw-r--r-- | system.h | 18 | ||||
-rw-r--r-- | veth.c | 247 |
5 files changed, 337 insertions, 1 deletions
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 <linux/ip6_tunnel.h> #include <linux/ethtool.h> #include <linux/fib_rules.h> +#include <linux/veth.h> #include <linux/version.h> #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 = { @@ -14,6 +14,7 @@ #ifndef __NETIFD_SYSTEM_H #define __NETIFD_SYSTEM_H +#include <net/if.h> #include <sys/time.h> #include <sys/socket.h> #include <arpa/inet.h> @@ -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); @@ -0,0 +1,247 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org> + * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net> + * + * 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 <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <net/ethernet.h> + +#ifdef linux +#include <netinet/ether.h> +#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); +} |