bridge: make learning and unicast-flood configurable per bridge port
[project/netifd.git] / system-linux.c
index 2d56808..621f99b 100644 (file)
 #define RTN_FAILED_POLICY 12
 #endif
 
-#ifndef RT_TABLE_PRELOCAL
-#define RT_TABLE_PRELOCAL 128
-#endif
-
 #ifndef IFA_F_NOPREFIXROUTE
 #define IFA_F_NOPREFIXROUTE 0x200
 #endif
@@ -332,6 +328,60 @@ static void system_bridge_set_multicast_router(struct device *dev, const char *v
                              dev->ifname, val);
 }
 
+static void system_bridge_set_robustness(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_startup_query_count",
+                             dev->ifname, val);
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_last_member_count",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_query_interval(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_query_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_query_response_interval(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_query_response_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_last_member_interval(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_last_member_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_membership_interval(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_membership_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_other_querier_timeout(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_querier_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_startup_query_interval(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_startup_query_interval",
+                             dev->ifname, val);
+}
+
+static void system_bridge_set_learning(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/sys/class/net/%s/brport/learning", dev->ifname, val);
+}
+
+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 int system_get_sysctl(const char *path, char *buf, const size_t buf_sz)
 {
        int fd = -1, ret = -1;
@@ -608,6 +658,14 @@ int system_bridge_addif(struct device *bridge, struct device *dev)
                system_bridge_set_multicast_router(dev, buf, false);
        }
 
+       if (dev->settings.flags & DEV_OPT_LEARNING &&
+           !dev->settings.learning)
+               system_bridge_set_learning(dev, "0");
+
+       if (dev->settings.flags & DEV_OPT_UNICAST_FLOOD &&
+           !dev->settings.unicast_flood)
+               system_bridge_set_unicast_flood(dev, "0");
+
        return ret;
 }
 
@@ -832,37 +890,98 @@ sec_to_jiffies(int val)
        return (unsigned long) val * 100;
 }
 
-int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
+static void system_bridge_conf_multicast_deps(struct device *bridge,
+                                             struct bridge_config *cfg,
+                                             char *buf,
+                                             int buf_len)
 {
-       char buf[64];
-       unsigned long args[4] = {};
+       int val;
 
-       if (ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname) < 0)
-               return -1;
+       if (cfg->flags & BRIDGE_OPT_ROBUSTNESS ||
+           cfg->flags & BRIDGE_OPT_QUERY_INTERVAL ||
+           cfg->flags & BRIDGE_OPT_QUERY_RESPONSE_INTERVAL) {
+               val = cfg->robustness * cfg->query_interval +
+                       cfg->query_response_interval;
 
-       args[0] = BRCTL_SET_BRIDGE_STP_STATE;
-       args[1] = !!cfg->stp;
-       system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+               snprintf(buf, buf_len, "%i", val);
+               system_bridge_set_membership_interval(bridge, buf);
 
-       args[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
-       args[1] = sec_to_jiffies(cfg->forward_delay);
-       system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+               val = cfg->robustness * cfg->query_interval +
+                       cfg->query_response_interval / 2;
+
+               snprintf(buf, buf_len, "%i", val);
+               system_bridge_set_other_querier_timeout(bridge, buf);
+       }
+
+       if (cfg->flags & BRIDGE_OPT_QUERY_INTERVAL) {
+               val = cfg->query_interval / 4;
+
+               snprintf(buf, buf_len, "%i", val);
+               system_bridge_set_startup_query_interval(bridge, buf);
+       }
+}
 
+static void system_bridge_conf_multicast(struct device *bridge,
+                                        struct bridge_config *cfg,
+                                        char *buf,
+                                        int buf_len)
+{
        system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_snooping",
                bridge->ifname, cfg->igmp_snoop ? "1" : "0");
 
        system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_querier",
                bridge->ifname, cfg->multicast_querier ? "1" : "0");
 
-       snprintf(buf, sizeof(buf), "%i", cfg->hash_max);
+       snprintf(buf, buf_len, "%i", cfg->hash_max);
        system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/hash_max",
                bridge->ifname, buf);
 
        if (bridge->settings.flags & DEV_OPT_MULTICAST_ROUTER) {
-               snprintf(buf, sizeof(buf), "%i", bridge->settings.multicast_router);
+               snprintf(buf, buf_len, "%i", bridge->settings.multicast_router);
                system_bridge_set_multicast_router(bridge, buf, true);
        }
 
+       if (cfg->flags & BRIDGE_OPT_ROBUSTNESS) {
+               snprintf(buf, buf_len, "%i", cfg->robustness);
+               system_bridge_set_robustness(bridge, buf);
+       }
+
+       if (cfg->flags & BRIDGE_OPT_QUERY_INTERVAL) {
+               snprintf(buf, buf_len, "%i", cfg->query_interval);
+               system_bridge_set_query_interval(bridge, buf);
+       }
+
+       if (cfg->flags & BRIDGE_OPT_QUERY_RESPONSE_INTERVAL) {
+               snprintf(buf, buf_len, "%i", cfg->query_response_interval);
+               system_bridge_set_query_response_interval(bridge, buf);
+       }
+
+       if (cfg->flags & BRIDGE_OPT_LAST_MEMBER_INTERVAL) {
+               snprintf(buf, buf_len, "%i", cfg->last_member_interval);
+               system_bridge_set_last_member_interval(bridge, buf);
+       }
+
+       system_bridge_conf_multicast_deps(bridge, cfg, buf, buf_len);
+}
+
+int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
+{
+       char buf[64];
+       unsigned long args[4] = {};
+
+       if (ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname) < 0)
+               return -1;
+
+       args[0] = BRCTL_SET_BRIDGE_STP_STATE;
+       args[1] = !!cfg->stp;
+       system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+
+       args[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+       args[1] = sec_to_jiffies(cfg->forward_delay);
+       system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+
+       system_bridge_conf_multicast(bridge, cfg, buf, sizeof(buf));
+
        args[0] = BRCTL_SET_BRIDGE_PRIORITY;
        args[1] = cfg->priority;
        system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
@@ -1733,8 +1852,6 @@ bool system_resolve_rt_table(const char *name, unsigned int *id)
                table = RT_TABLE_MAIN;
        else if (!strcmp(name, "local"))
                table = RT_TABLE_LOCAL;
-       else if (!strcmp(name, "prelocal"))
-               table = RT_TABLE_PRELOCAL;
 
        /* try to look up name in /etc/iproute2/rt_tables */
        else if ((f = fopen("/etc/iproute2/rt_tables", "r")) != NULL)
@@ -1894,10 +2011,6 @@ int system_flush_iprules(void)
        rule.flags = IPRULE_INET4 | IPRULE_PRIORITY | IPRULE_LOOKUP;
 
        rule.priority = 0;
-       rule.lookup = RT_TABLE_PRELOCAL;
-       rv |= system_iprule(&rule, RTM_NEWRULE);
-
-       rule.priority = 1;
        rule.lookup = RT_TABLE_LOCAL;
        rv |= system_iprule(&rule, RTM_NEWRULE);
 
@@ -1913,10 +2026,6 @@ int system_flush_iprules(void)
        rule.flags = IPRULE_INET6 | IPRULE_PRIORITY | IPRULE_LOOKUP;
 
        rule.priority = 0;
-       rule.lookup = RT_TABLE_PRELOCAL;
-       rv |= system_iprule(&rule, RTM_NEWRULE);
-
-       rule.priority = 1;
        rule.lookup = RT_TABLE_LOCAL;
        rv |= system_iprule(&rule, RTM_NEWRULE);
 
@@ -2224,10 +2333,10 @@ static int system_add_vti_tunnel(const char *name, const char *kind,
        }
 
        if (okey)
-               nla_put_be32(nlm, IFLA_VTI_OKEY, okey);
+               nla_put_u32(nlm, IFLA_VTI_OKEY, htonl(okey));
 
        if (ikey)
-               nla_put_be32(nlm, IFLA_VTI_IKEY, ikey);
+               nla_put_u32(nlm, IFLA_VTI_IKEY, htonl(ikey));
 
        nla_nest_end(nlm, infodata);
        nla_nest_end(nlm, linkinfo);