netifd: vxlan: refactor mapping of boolean attrs
[project/netifd.git] / system-linux.c
index c406a3d5085fffdbba99b47b8d30f22bed68e90f..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>
@@ -45,6 +46,8 @@
 #include <linux/veth.h>
 #include <linux/version.h>
 
+#include <sched.h>
+
 #ifndef RTN_FAILED_POLICY
 #define RTN_FAILED_POLICY 12
 #endif
@@ -71,6 +74,7 @@
 #include "netifd.h"
 #include "device.h"
 #include "system.h"
+#include "utils.h"
 
 struct event_socket {
        struct uloop_fd uloop;
@@ -466,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;
@@ -588,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:
@@ -650,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)
@@ -840,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;
@@ -985,7 +1046,7 @@ static void
 system_if_clear_entries(struct device *dev, int type, int af)
 {
        struct clear_data clr;
-       struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+       struct nl_cb *cb;
        struct rtmsg rtm = {
                .rtm_family = af,
                .rtm_flags = RTM_F_CLONED,
@@ -993,9 +1054,6 @@ system_if_clear_entries(struct device *dev, int type, int af)
        int flags = NLM_F_DUMP;
        int pending = 1;
 
-       if (!cb)
-               return;
-
        clr.af = af;
        clr.dev = dev;
        clr.type = type;
@@ -1011,6 +1069,10 @@ system_if_clear_entries(struct device *dev, int type, int af)
                return;
        }
 
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb)
+               return;
+
        clr.msg = nlmsg_alloc_simple(type, flags);
        if (!clr.msg)
                goto out;
@@ -1160,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);
@@ -1242,6 +1305,30 @@ nla_put_failure:
        return -ENOMEM;
 }
 
+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,
+       };
+
+       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);
+       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);
+}
+
 static int system_link_del(const char *ifname)
 {
        struct nl_msg *msg;
@@ -1265,6 +1352,20 @@ int system_macvlan_del(struct device *macvlan)
        return system_link_del(macvlan->ifname);
 }
 
+int system_netns_open(const pid_t target_ns)
+{
+       char pid_net_path[PATH_MAX];
+
+       snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%u/ns/net", target_ns);
+
+       return open(pid_net_path, O_RDONLY);
+}
+
+int system_netns_set(int netns_fd)
+{
+       return setns(netns_fd, CLONE_NEWNET);
+}
+
 int system_veth_add(struct device *veth, struct veth_config *cfg)
 {
        struct nl_msg *msg;
@@ -1359,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);
@@ -1389,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);
 
@@ -2385,7 +2508,7 @@ time_t system_get_rtime(void)
        struct timespec ts;
        struct timeval tv;
 
-       if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts) == 0)
+       if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
                return ts.tv_sec;
 
        if (gettimeofday(&tv, NULL) == 0)
@@ -2950,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];
@@ -3061,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};
 
-       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);
+               uint32_t low = blobmsg_get_u32(cur);
+               if (low < 1 || low > 65535 - 1) {
+                       ret = -EINVAL;
+                       goto failure;
+               }
+
+               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;