netifd: vxlan: refactor mapping of boolean attrs
[project/netifd.git] / system-linux.c
index d36d2878b93ba0927166b29a54916d323aeb024b..d87a9eee3e0eddfcc55eceb0b724cddb459412ba 100644 (file)
@@ -26,6 +26,7 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 
+#include <limits.h>
 #include <arpa/inet.h>
 #include <netinet/ether.h>
 #include <netinet/in.h>
@@ -73,6 +74,7 @@
 #include "netifd.h"
 #include "device.h"
 #include "system.h"
+#include "utils.h"
 
 struct event_socket {
        struct uloop_fd uloop;
@@ -468,6 +470,11 @@ static void system_set_sendredirects(struct device *dev, const char *val)
        system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/send_redirects", dev->ifname, val);
 }
 
+static void system_bridge_set_vlan_filtering(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/vlan_filtering", dev->ifname, val);
+}
+
 static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz)
 {
        int fd = -1, ret = -1;
@@ -590,6 +597,9 @@ static int cb_rtnl_event(struct nl_msg *msg, void *arg)
        if (!system_get_dev_sysctl("/sys/class/net/%s/carrier", dev->ifname, buf, sizeof(buf)))
                link_state = strtoul(buf, NULL, 0);
 
+       if (dev->type == &simple_device_type && !system_if_force_external(dev->ifname))
+               device_set_present(dev, true);
+
        device_set_link(dev, link_state ? true : false);
 
 out:
@@ -652,13 +662,15 @@ handle_hotplug_msg(char *data, int size)
 move:
        dev = device_find(interface_old);
        if (!dev)
-               goto found;
+               return;
 
        if (dev->type != &simple_device_type)
                goto found;
 
        device_set_present(dev, false);
 
+       return;
+
 found:
        dev = device_find(interface);
        if (!dev)
@@ -842,6 +854,53 @@ int system_bridge_delif(struct device *bridge, struct device *dev)
        return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL);
 }
 
+int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags)
+{
+       struct ifinfomsg ifi = { .ifi_family = PF_BRIDGE, };
+       struct bridge_vlan_info vinfo = { .vid = vid, };
+       unsigned short flags = 0;
+       struct nlattr *afspec;
+       struct nl_msg *nlm;
+       int ret = 0;
+
+       ifi.ifi_index = if_nametoindex(iface);
+       if (!ifi.ifi_index)
+               return -1;
+
+       nlm = nlmsg_alloc_simple(add ? RTM_SETLINK : RTM_DELLINK, NLM_F_REQUEST);
+       if (!nlm)
+               return -1;
+
+       nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+
+       if (vflags & BRVLAN_F_SELF)
+               flags |= BRIDGE_FLAGS_SELF;
+
+       if (vflags & BRVLAN_F_PVID)
+               vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
+
+       if (vflags & BRVLAN_F_UNTAGGED)
+               vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+
+       afspec = nla_nest_start(nlm, IFLA_AF_SPEC);
+       if (!afspec) {
+               ret = -ENOMEM;
+               goto failure;
+       }
+
+       if (flags)
+               nla_put_u16(nlm, IFLA_BRIDGE_FLAGS, flags);
+
+       nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
+       nla_nest_end(nlm, afspec);
+
+       return system_rtnl_call(nlm);
+
+failure:
+       nlmsg_free(nlm);
+       return ret;
+}
+
 int system_if_resolve(struct device *dev)
 {
        struct ifreq ifr;
@@ -1163,6 +1222,7 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
        system_bridge_set_forward_delay(bridge, buf);
 
        system_bridge_conf_multicast(bridge, cfg, buf, sizeof(buf));
+       system_bridge_set_vlan_filtering(bridge, cfg->vlan_filtering ? "1" : "0");
 
        snprintf(buf, sizeof(buf), "%d", cfg->priority);
        system_bridge_set_priority(bridge, buf);
@@ -1245,21 +1305,26 @@ nla_put_failure:
        return -ENOMEM;
 }
 
-int system_link_netns_move(const char *ifname, int netns_fd)
+int system_link_netns_move(struct device *dev, int netns_fd, const char *target_ifname)
 {
        struct nl_msg *msg;
        struct ifinfomsg iim = {
                .ifi_family = AF_UNSPEC,
-               .ifi_index = 0,
        };
 
+       if (!dev)
+               return -1;
+
+       iim.ifi_index = system_if_resolve(dev);
        msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST);
 
        if (!msg)
                return -1;
 
        nlmsg_append(msg, &iim, sizeof(iim), 0);
-       nla_put_string(msg, IFLA_IFNAME, ifname);
+       if (target_ifname)
+               nla_put_string(msg, IFLA_IFNAME, target_ifname);
+
        nla_put_u32(msg, IFLA_NET_NS_FD, netns_fd);
        return system_rtnl_call(msg);
 }
@@ -1395,8 +1460,10 @@ int system_vlan_del(struct device *dev)
 int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg)
 {
        struct nl_msg *msg;
-       struct nlattr *linkinfo, *data;
+       struct nlattr *linkinfo, *data, *qos;
        struct ifinfomsg iim = { .ifi_family = AF_UNSPEC };
+       struct vlan_qos_mapping *elem;
+       struct ifla_vlan_qos_mapping nl_qos_map;
        int rv;
 
        msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
@@ -1425,6 +1492,26 @@ int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlande
                netifd_log_message(L_WARNING, "%s Your kernel is older than linux 3.10.0, 802.1ad is not supported defaulting to 802.1q", vlandev->type->name);
 #endif
 
+       if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
+               goto nla_put_failure;
+
+       vlist_simple_for_each_element(&cfg->ingress_qos_mapping_list, elem, node) {
+               nl_qos_map.from = elem->from;
+               nl_qos_map.to = elem->to;
+               nla_put(msg, IFLA_VLAN_QOS_MAPPING, sizeof(nl_qos_map), &nl_qos_map);
+       }
+       nla_nest_end(msg, qos);
+
+       if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
+               goto nla_put_failure;
+
+       vlist_simple_for_each_element(&cfg->egress_qos_mapping_list, elem, node) {
+               nl_qos_map.from = elem->from;
+               nl_qos_map.to = elem->to;
+               nla_put(msg, IFLA_VLAN_QOS_MAPPING, sizeof(nl_qos_map), &nl_qos_map);
+       }
+       nla_nest_end(msg, qos);
+
        nla_nest_end(msg, data);
        nla_nest_end(msg, linkinfo);
 
@@ -2986,6 +3073,17 @@ failure:
 #endif
 
 #ifdef IFLA_VXLAN_MAX
+static void system_vxlan_map_bool_attr(struct nl_msg *msg, struct blob_attr **tb_data, int attrtype, int vxlandatatype, bool invert) {
+       struct blob_attr *cur;
+       if ((cur = tb_data[vxlandatatype])) {
+               bool val = blobmsg_get_bool(cur);
+               if (invert) {
+                       val = !val;
+               }
+               nla_put_u8(msg, attrtype, val);
+       }
+}
+
 static int system_add_vxlan(const char *name, const unsigned int link, struct blob_attr **tb, bool v6)
 {
        struct blob_attr *tb_data[__VXLAN_DATA_ATTR_MAX];
@@ -3097,17 +3195,36 @@ static int system_add_vxlan(const char *name, const unsigned int link, struct bl
        }
        nla_put_u16(msg, IFLA_VXLAN_PORT, htons(port));
 
-       if ((cur = tb_data[VXLAN_DATA_ATTR_RXCSUM])) {
-               bool rxcsum = blobmsg_get_bool(cur);
-               nla_put_u8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, !rxcsum);
-       }
+       if ((cur = tb_data[VXLAN_DATA_ATTR_SRCPORTMIN])) {
+               struct ifla_vxlan_port_range srcports = {0,0};
+
+               uint32_t low = blobmsg_get_u32(cur);
+               if (low < 1 || low > 65535 - 1) {
+                       ret = -EINVAL;
+                       goto failure;
+               }
 
-       if ((cur = tb_data[VXLAN_DATA_ATTR_TXCSUM])) {
-               bool txcsum = blobmsg_get_bool(cur);
-               nla_put_u8(msg, IFLA_VXLAN_UDP_CSUM, txcsum);
-               nla_put_u8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, !txcsum);
+               srcports.low = htons((uint16_t) low);
+               srcports.high = htons((uint16_t) (low+1));
+
+               if ((cur = tb_data[VXLAN_DATA_ATTR_SRCPORTMAX])) {
+                       uint32_t high = blobmsg_get_u32(cur);
+                       if (high < 1 || high > 65535) {
+                               ret = -EINVAL;
+                               goto failure;
+                       }
+
+                       if (high > low)
+                               srcports.high = htons((uint16_t) high);
+               }
+
+               nla_put(msg, IFLA_VXLAN_PORT_RANGE, sizeof(srcports), &srcports);
        }
 
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_UDP_CSUM, VXLAN_DATA_ATTR_TXCSUM, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, VXLAN_DATA_ATTR_RXCSUM, true);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, VXLAN_DATA_ATTR_TXCSUM, true);
+
        if ((cur = tb[TUNNEL_ATTR_TOS])) {
                char *str = blobmsg_get_string(cur);
                unsigned tos = 1;