system-linux: add device options used by wpad
[project/netifd.git] / system-linux.c
index c5583e079c9b9a3eaabd704bcfe3a959e07bc9bb..b9e440e052d10080112561ea42365db835a1d0ac 100644 (file)
@@ -28,8 +28,8 @@
 
 #include <limits.h>
 #include <arpa/inet.h>
-#include <netinet/ether.h>
 #include <netinet/in.h>
+#include <netinet/ether.h>
 
 #include <linux/rtnetlink.h>
 #include <linux/neighbour.h>
@@ -304,6 +304,11 @@ static void system_set_disable_ipv6(struct device *dev, const char *val)
        system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname, val);
 }
 
+static void system_set_ip6segmentrouting(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled", dev->ifname, val);
+}
+
 static void system_set_rpfilter(struct device *dev, const char *val)
 {
        system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter", dev->ifname, val);
@@ -354,6 +359,36 @@ static void system_set_dadtransmits(struct device *dev, const char *val)
        system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", dev->ifname, val);
 }
 
+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_set_drop_v4_unicast_in_l2_multicast(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val);
+}
+
+static void system_set_drop_v6_unicast_in_l2_multicast(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val);
+}
+
+static void system_set_drop_gratuitous_arp(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", dev->ifname, val);
+}
+
+static void system_set_drop_unsolicited_na(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", dev->ifname, val);
+}
+
+static void system_set_arp_accept(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept", dev->ifname, val);
+}
+
 static void system_bridge_set_multicast_to_unicast(struct device *dev, const char *val)
 {
        system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_to_unicast", dev->ifname, val);
@@ -465,11 +500,6 @@ static void system_bridge_set_unicast_flood(struct device *dev, const char *val)
        system_set_dev_sysctl("/sys/class/net/%s/brport/unicast_flood", dev->ifname, val);
 }
 
-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);
@@ -509,6 +539,12 @@ static int system_get_disable_ipv6(struct device *dev, char *buf, const size_t b
                        dev->ifname, buf, buf_sz);
 }
 
+static int system_get_ip6segmentrouting(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled",
+                       dev->ifname, buf, buf_sz);
+}
+
 static int system_get_rpfilter(struct device *dev, char *buf, const size_t buf_sz)
 {
        return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter",
@@ -575,6 +611,37 @@ static int system_get_sendredirects(struct device *dev, char *buf, const size_t
                        dev->ifname, buf, buf_sz);
 }
 
+
+static int system_get_drop_v4_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_v6_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_gratuitous_arp(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_unsolicited_na(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_arp_accept(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept",
+                       dev->ifname, buf, buf_sz);
+}
+
 /* Evaluate netlink messages */
 static int cb_rtnl_event(struct nl_msg *msg, void *arg)
 {
@@ -597,7 +664,7 @@ 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))
+       if (dev->type == &simple_device_type)
                device_set_present(dev, true);
 
        device_set_link(dev, link_state ? true : false);
@@ -679,9 +746,6 @@ found:
        if (dev->type != &simple_device_type)
                return;
 
-       if (add && system_if_force_external(dev->ifname))
-               return;
-
        device_set_present(dev, add);
 }
 
@@ -816,11 +880,20 @@ int system_bridge_addif(struct device *bridge, struct device *dev)
 {
        char buf[64];
        char *oldbr;
-       int ret = 0;
+       int tries = 0;
+       int ret;
 
+retry:
+       ret = 0;
        oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf));
-       if (!oldbr || strcmp(oldbr, bridge->ifname) != 0)
+       if (!oldbr || strcmp(oldbr, bridge->ifname) != 0) {
                ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
+               tries++;
+               D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s\n",
+                 dev->ifname, bridge->ifname, tries, strerror(errno));
+               if (tries <= 3)
+                       goto retry;
+       }
 
        if (dev->wireless)
                system_bridge_set_wireless(bridge, dev);
@@ -904,6 +977,8 @@ failure:
 int system_if_resolve(struct device *dev)
 {
        struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
        if (!ioctl(sock_ioctl, SIOCGIFINDEX, &ifr))
                return ifr.ifr_ifindex;
@@ -1564,6 +1639,11 @@ system_if_get_settings(struct device *dev, struct device_settings *s)
                s->flags |= DEV_OPT_IPV6;
        }
 
+       if (!system_get_ip6segmentrouting(dev, buf, sizeof(buf))) {
+               s->ip6segmentrouting = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_IP6SEGMENTROUTING;
+       }
+
        if (ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr) == 0) {
                s->promisc = ifr.ifr_flags & IFF_PROMISC;
                s->flags |= DEV_OPT_PROMISC;
@@ -1626,6 +1706,31 @@ system_if_get_settings(struct device *dev, struct device_settings *s)
                s->sendredirects = strtoul(buf, NULL, 0);
                s->flags |= DEV_OPT_SENDREDIRECTS;
        }
+
+       if (!system_get_drop_v4_unicast_in_l2_multicast(dev, buf, sizeof(buf))) {
+               s->drop_v4_unicast_in_l2_multicast = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST;
+       }
+
+       if (!system_get_drop_v6_unicast_in_l2_multicast(dev, buf, sizeof(buf))) {
+               s->drop_v6_unicast_in_l2_multicast = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST;
+       }
+
+       if (!system_get_drop_gratuitous_arp(dev, buf, sizeof(buf))) {
+               s->drop_gratuitous_arp = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_DROP_GRATUITOUS_ARP;
+       }
+
+       if (!system_get_drop_unsolicited_na(dev, buf, sizeof(buf))) {
+               s->drop_unsolicited_na = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_DROP_UNSOLICITED_NA;
+       }
+
+       if (!system_get_arp_accept(dev, buf, sizeof(buf))) {
+               s->arp_accept = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_ARP_ACCEPT;
+       }
 }
 
 void
@@ -1634,92 +1739,106 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned
        struct ifreq ifr;
        char buf[12];
 
+       apply_mask &= s->flags;
+
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
-       if (s->flags & DEV_OPT_MTU & apply_mask) {
+       if (apply_mask & DEV_OPT_MTU) {
                ifr.ifr_mtu = s->mtu;
                if (ioctl(sock_ioctl, SIOCSIFMTU, &ifr) < 0)
                        s->flags &= ~DEV_OPT_MTU;
        }
-       if (s->flags & DEV_OPT_MTU6 & apply_mask) {
+       if (apply_mask & DEV_OPT_MTU6) {
                system_update_ipv6_mtu(dev, s->mtu6);
        }
-       if (s->flags & DEV_OPT_TXQUEUELEN & apply_mask) {
+       if (apply_mask & DEV_OPT_TXQUEUELEN) {
                ifr.ifr_qlen = s->txqueuelen;
                if (ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr) < 0)
                        s->flags &= ~DEV_OPT_TXQUEUELEN;
        }
-       if ((s->flags & DEV_OPT_MACADDR & apply_mask) && !dev->external) {
+       if ((apply_mask & (DEV_OPT_MACADDR | DEV_OPT_DEFAULT_MACADDR)) && !dev->external) {
                ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
                memcpy(&ifr.ifr_hwaddr.sa_data, s->macaddr, sizeof(s->macaddr));
                if (ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr) < 0)
                        s->flags &= ~DEV_OPT_MACADDR;
        }
-       if (s->flags & DEV_OPT_IPV6 & apply_mask)
+       if (apply_mask & DEV_OPT_IPV6)
                system_set_disable_ipv6(dev, s->ipv6 ? "0" : "1");
-       if (s->flags & DEV_OPT_PROMISC & apply_mask) {
+       if (s->flags & DEV_OPT_IP6SEGMENTROUTING & apply_mask) {
+               struct device dummy = {
+                       .ifname = "all",
+               };
+               bool ip6segmentrouting = device_check_ip6segmentrouting();
+
+               system_set_ip6segmentrouting(dev, s->ip6segmentrouting ? "1" : "0");
+               system_set_ip6segmentrouting(&dummy, ip6segmentrouting ? "1" : "0");
+       }
+       if (apply_mask & DEV_OPT_PROMISC) {
                if (system_if_flags(dev->ifname, s->promisc ? IFF_PROMISC : 0,
                                    !s->promisc ? IFF_PROMISC : 0) < 0)
                        s->flags &= ~DEV_OPT_PROMISC;
        }
-       if (s->flags & DEV_OPT_RPFILTER & apply_mask) {
+       if (apply_mask & DEV_OPT_RPFILTER) {
                snprintf(buf, sizeof(buf), "%u", s->rpfilter);
                system_set_rpfilter(dev, buf);
        }
-       if (s->flags & DEV_OPT_ACCEPTLOCAL & apply_mask)
+       if (apply_mask & DEV_OPT_ACCEPTLOCAL)
                system_set_acceptlocal(dev, s->acceptlocal ? "1" : "0");
-       if (s->flags & DEV_OPT_IGMPVERSION & apply_mask) {
+       if (apply_mask & DEV_OPT_IGMPVERSION) {
                snprintf(buf, sizeof(buf), "%u", s->igmpversion);
                system_set_igmpversion(dev, buf);
        }
-       if (s->flags & DEV_OPT_MLDVERSION & apply_mask) {
+       if (apply_mask & DEV_OPT_MLDVERSION) {
                snprintf(buf, sizeof(buf), "%u", s->mldversion);
                system_set_mldversion(dev, buf);
        }
-       if (s->flags & DEV_OPT_NEIGHREACHABLETIME & apply_mask) {
+       if (apply_mask & DEV_OPT_NEIGHREACHABLETIME) {
                snprintf(buf, sizeof(buf), "%u", s->neigh4reachabletime);
                system_set_neigh4reachabletime(dev, buf);
                snprintf(buf, sizeof(buf), "%u", s->neigh6reachabletime);
                system_set_neigh6reachabletime(dev, buf);
        }
-       if (s->flags & DEV_OPT_NEIGHLOCKTIME & apply_mask) {
+       if (apply_mask & DEV_OPT_NEIGHLOCKTIME) {
                snprintf(buf, sizeof(buf), "%d", s->neigh4locktime);
                system_set_neigh4locktime(dev, buf);
        }
-       if (s->flags & DEV_OPT_NEIGHGCSTALETIME & apply_mask) {
+       if (apply_mask & DEV_OPT_NEIGHGCSTALETIME) {
                snprintf(buf, sizeof(buf), "%u", s->neigh4gcstaletime);
                system_set_neigh4gcstaletime(dev, buf);
                snprintf(buf, sizeof(buf), "%u", s->neigh6gcstaletime);
                system_set_neigh6gcstaletime(dev, buf);
        }
-       if (s->flags & DEV_OPT_DADTRANSMITS & apply_mask) {
+       if (apply_mask & DEV_OPT_DADTRANSMITS) {
                snprintf(buf, sizeof(buf), "%u", s->dadtransmits);
                system_set_dadtransmits(dev, buf);
        }
-       if (s->flags & DEV_OPT_MULTICAST & apply_mask) {
+       if (apply_mask & DEV_OPT_MULTICAST) {
                if (system_if_flags(dev->ifname, s->multicast ? IFF_MULTICAST : 0,
                                    !s->multicast ? IFF_MULTICAST : 0) < 0)
                        s->flags &= ~DEV_OPT_MULTICAST;
        }
-       if (s->flags & DEV_OPT_SENDREDIRECTS & apply_mask)
+       if (apply_mask & DEV_OPT_SENDREDIRECTS)
                system_set_sendredirects(dev, s->sendredirects ? "1" : "0");
+       if (apply_mask & DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST)
+               system_set_drop_v4_unicast_in_l2_multicast(dev, s->drop_v4_unicast_in_l2_multicast ? "1" : "0");
+       if (apply_mask & DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST)
+               system_set_drop_v6_unicast_in_l2_multicast(dev, s->drop_v6_unicast_in_l2_multicast ? "1" : "0");
+       if (apply_mask & DEV_OPT_DROP_GRATUITOUS_ARP)
+               system_set_drop_gratuitous_arp(dev, s->drop_gratuitous_arp ? "1" : "0");
+       if (apply_mask & DEV_OPT_DROP_UNSOLICITED_NA)
+               system_set_drop_unsolicited_na(dev, s->drop_unsolicited_na ? "1" : "0");
+       if (apply_mask & DEV_OPT_ARP_ACCEPT)
+               system_set_arp_accept(dev, s->arp_accept ? "1" : "0");
 }
 
 int system_if_up(struct device *dev)
 {
-       system_if_get_settings(dev, &dev->orig_settings);
-       /* Only keep orig settings based on what needs to be set */
-       dev->orig_settings.valid_flags = dev->orig_settings.flags;
-       dev->orig_settings.flags &= dev->settings.flags;
-       system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
        return system_if_flags(dev->ifname, IFF_UP, 0);
 }
 
 int system_if_down(struct device *dev)
 {
-       int ret = system_if_flags(dev->ifname, 0, IFF_UP);
-       system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags);
-       return ret;
+       return system_if_flags(dev->ifname, 0, IFF_UP);
 }
 
 struct if_check_data {
@@ -1741,7 +1860,8 @@ static int cb_if_check_valid(struct nl_msg *msg, void *arg)
        if (nh->nlmsg_type != RTM_NEWLINK)
                return NL_SKIP;
 
-       device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false);
+       if (chk->dev->type == &simple_device_type)
+               device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false);
        device_set_link(chk->dev, ifi->ifi_flags & IFF_LOWER_UP ? true : false);
 
        return NL_OK;
@@ -1758,7 +1878,8 @@ static int cb_if_check_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void
 {
        struct if_check_data *chk = (struct if_check_data *)arg;
 
-       device_set_present(chk->dev, false);
+       if (chk->dev->type == &simple_device_type)
+               device_set_present(chk->dev, false);
        device_set_link(chk->dev, false);
        chk->pending = err->error;
 
@@ -3073,6 +3194,21 @@ 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;
+
+               if ((attrtype == IFLA_VXLAN_GBP) && val)
+                       nla_put_flag(msg, attrtype);
+               else 
+                       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];
@@ -3184,15 +3320,50 @@ 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;
+               }
+
+               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);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_LEARNING, VXLAN_DATA_ATTR_LEARNING, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_RSC , VXLAN_DATA_ATTR_RSC, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_PROXY , VXLAN_DATA_ATTR_PROXY, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_L2MISS , VXLAN_DATA_ATTR_L2MISS, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_L3MISS , VXLAN_DATA_ATTR_L3MISS, false);
+       system_vxlan_map_bool_attr(msg, tb_data, IFLA_VXLAN_GBP , VXLAN_DATA_ATTR_GBP, false);
+
+       if ((cur = tb_data[VXLAN_DATA_ATTR_AGEING])) {
+               uint32_t ageing = blobmsg_get_u32(cur);
+               nla_put_u32(msg, IFLA_VXLAN_AGEING, ageing);
        }
 
-       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);
+       if ((cur = tb_data[VXLAN_DATA_ATTR_LIMIT])) {
+               uint32_t maxaddress = blobmsg_get_u32(cur);
+               nla_put_u32(msg, IFLA_VXLAN_LIMIT, maxaddress);
        }
 
        if ((cur = tb[TUNNEL_ATTR_TOS])) {