device: Support multicast config option
[project/netifd.git] / system-linux.c
index b190ec6..86e373c 100644 (file)
@@ -1053,7 +1053,7 @@ int system_vlandev_del(struct device *vlandev)
        return system_link_del(vlandev->ifname);
 }
 
-static void
+void
 system_if_get_settings(struct device *dev, struct device_settings *s)
 {
        struct ifreq ifr;
@@ -1089,6 +1089,9 @@ system_if_get_settings(struct device *dev, struct device_settings *s)
        if (ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr) == 0) {
                s->promisc = ifr.ifr_flags & IFF_PROMISC;
                s->flags |= DEV_OPT_PROMISC;
+
+               s->multicast = ifr.ifr_flags & IFF_MULTICAST;
+               s->flags |= DEV_OPT_MULTICAST;
        }
 
        if (!system_get_rpfilter(dev, buf, sizeof(buf))) {
@@ -1140,6 +1143,8 @@ system_if_set_rps_xps_val(const char *path, int val)
        snprintf(val_buf, sizeof(val_buf), "%x", val);
        for (i = 0; i < gl.gl_pathc; i++)
                system_set_sysctl(gl.gl_pathv[i], val_buf);
+
+       globfree(&gl);
 }
 
 static void
@@ -1217,6 +1222,11 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned
                snprintf(buf, sizeof(buf), "%d", s->dadtransmits);
                system_set_dadtransmits(dev, buf);
        }
+       if (s->flags & DEV_OPT_MULTICAST & apply_mask) {
+               if (system_if_flags(dev->ifname, s->multicast ? IFF_MULTICAST : 0,
+                                   !s->multicast ? IFF_MULTICAST : 0) < 0)
+                       s->flags &= ~DEV_OPT_MULTICAST;
+       }
 
        system_if_apply_rps_xps(dev, s);
 }
@@ -1792,10 +1802,8 @@ static int system_iprule(struct iprule *rule, int cmd)
                .rtm_flags = 0,
        };
 
-       if (cmd == RTM_NEWRULE) {
+       if (cmd == RTM_NEWRULE)
                rtm.rtm_type = RTN_UNICAST;
-               rtm.rtm_flags |= NLM_F_REPLACE | NLM_F_EXCL;
-       }
 
        if (rule->invert)
                rtm.rtm_flags |= FIB_RULE_INVERT;
@@ -2135,6 +2143,103 @@ failure:
 }
 #endif
 
+#ifdef IFLA_VTI_MAX
+static int system_add_vti_tunnel(const char *name, const char *kind,
+                                const unsigned int link, struct blob_attr **tb, bool v6)
+{
+       struct nl_msg *nlm;
+       struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, };
+       struct blob_attr *cur;
+       uint32_t ikey = 0, okey = 0;
+       int ret = 0;
+
+       nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+       if (!nlm)
+               return -1;
+
+       nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+       nla_put_string(nlm, IFLA_IFNAME, name);
+
+       struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO);
+       if (!linkinfo) {
+               ret = -ENOMEM;
+               goto failure;
+       }
+
+       nla_put_string(nlm, IFLA_INFO_KIND, kind);
+       struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA);
+       if (!infodata) {
+               ret = -ENOMEM;
+               goto failure;
+       }
+
+       if (link)
+               nla_put_u32(nlm, IFLA_VTI_LINK, link);
+
+       if ((cur = tb[TUNNEL_ATTR_INFO]) && (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)) {
+               if (sscanf(blobmsg_get_string(cur), "%u,%u",
+                       &ikey, &okey) < 2) {
+                       ret = -EINVAL;
+                       goto failure;
+               }
+       }
+
+       if (v6) {
+               struct in6_addr in6buf;
+               if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+                       if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+                               ret = -EINVAL;
+                               goto failure;
+                       }
+                       nla_put(nlm, IFLA_VTI_LOCAL, sizeof(in6buf), &in6buf);
+               }
+
+               if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+                       if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+                               ret = -EINVAL;
+                               goto failure;
+                       }
+                       nla_put(nlm, IFLA_VTI_REMOTE, sizeof(in6buf), &in6buf);
+               }
+
+       } else {
+               struct in_addr inbuf;
+
+               if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+                       if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+                               ret = -EINVAL;
+                               goto failure;
+                       }
+                       nla_put(nlm, IFLA_VTI_LOCAL, sizeof(inbuf), &inbuf);
+               }
+
+               if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+                       if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+                               ret = -EINVAL;
+                               goto failure;
+                       }
+                       nla_put(nlm, IFLA_VTI_REMOTE, sizeof(inbuf), &inbuf);
+               }
+
+       }
+
+       if (okey)
+               nla_put_u32(nlm, IFLA_VTI_OKEY, okey);
+
+       if (ikey)
+               nla_put_u32(nlm, IFLA_VTI_IKEY, ikey);
+
+       nla_nest_end(nlm, infodata);
+       nla_nest_end(nlm, linkinfo);
+
+       return system_rtnl_call(nlm);
+
+failure:
+       nlmsg_free(nlm);
+       return ret;
+}
+#endif
+
 static int system_add_proto_tunnel(const char *name, const uint8_t proto, const unsigned int link, struct blob_attr **tb)
 {
        struct blob_attr *cur;
@@ -2203,7 +2308,8 @@ static int __system_del_ip_tunnel(const char *name, struct blob_attr **tb)
        str = blobmsg_data(cur);
 
        if (!strcmp(str, "greip") || !strcmp(str, "gretapip") ||
-           !strcmp(str, "greip6") || !strcmp(str, "gretapip6"))
+           !strcmp(str, "greip6") || !strcmp(str, "gretapip6") ||
+           !strcmp(str, "vtiip") || !strcmp(str, "vtiip6"))
                return system_link_del(name);
        else
                return tunnel_ioctl(name, SIOCDELTUNNEL, NULL);
@@ -2417,6 +2523,12 @@ failure:
                return system_add_gre_tunnel(name, "ip6gre", link, tb, true);
        } else if (!strcmp(str, "gretapip6")) {
                return system_add_gre_tunnel(name, "ip6gretap", link, tb, true);
+#ifdef IFLA_VTI_MAX
+       } else if (!strcmp(str, "vtiip")) {
+               return system_add_vti_tunnel(name, "vti", link, tb, false);
+       } else if (!strcmp(str, "vtiip6")) {
+               return system_add_vti_tunnel(name, "vti6", link, tb, true);
+#endif
 #endif
        } else if (!strcmp(str, "ipip")) {
                return system_add_proto_tunnel(name, IPPROTO_IPIP, link, tb);