interface: apply device config passed through interface config
[project/netifd.git] / system-linux.c
index bb7ccf9..4737fa6 100644 (file)
@@ -270,6 +270,21 @@ 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);
 }
 
+static void system_set_acceptlocal(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/accept_local", dev->ifname, val);
+}
+
+static void system_set_igmpversion(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/force_igmp_version", dev->ifname, val);
+}
+
+static void system_set_mldversion(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/force_mld_version", dev->ifname, val);
+}
+
 static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz)
 {
        int fd = -1, ret = -1;
@@ -310,6 +325,24 @@ static int system_get_rpfilter(struct device *dev, char *buf, const size_t buf_s
                        dev->ifname, buf, buf_sz);
 }
 
+static int system_get_acceptlocal(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/accept_local",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_igmpversion(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/force_igmp_version",
+                       dev->ifname, buf, buf_sz);
+}
+
+static int system_get_mldversion(struct device *dev, char *buf, const size_t buf_sz)
+{
+       return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/force_mld_version",
+                       dev->ifname, buf, buf_sz);
+}
+
 // Evaluate netlink messages
 static int cb_rtnl_event(struct nl_msg *msg, void *arg)
 {
@@ -969,6 +1002,21 @@ system_if_get_settings(struct device *dev, struct device_settings *s)
                s->rpfilter = strtoul(buf, NULL, 0);
                s->flags |= DEV_OPT_RPFILTER;
        }
+
+       if (!system_get_acceptlocal(dev, buf, sizeof(buf))) {
+               s->acceptlocal = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_ACCEPTLOCAL;
+       }
+
+       if (!system_get_igmpversion(dev, buf, sizeof(buf))) {
+               s->igmpversion = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_IGMPVERSION;
+       }
+
+       if (!system_get_mldversion(dev, buf, sizeof(buf))) {
+               s->mldversion = strtoul(buf, NULL, 0);
+               s->flags |= DEV_OPT_MLDVERSION;
+       }
 }
 
 void
@@ -1010,11 +1058,27 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned
                snprintf(buf, sizeof(buf), "%d", s->rpfilter);
                system_set_rpfilter(dev, buf);
        }
+       if (s->flags & DEV_OPT_ACCEPTLOCAL & apply_mask)
+               system_set_acceptlocal(dev, s->acceptlocal ? "1" : "0");
+       if (s->flags & DEV_OPT_IGMPVERSION & apply_mask) {
+               char buf[2];
+
+               snprintf(buf, sizeof(buf), "%d", s->igmpversion);
+               system_set_igmpversion(dev, buf);
+       }
+       if (s->flags & DEV_OPT_MLDVERSION & apply_mask) {
+               char buf[2];
+
+               snprintf(buf, sizeof(buf), "%d", s->mldversion);
+               system_set_mldversion(dev, buf);
+       }
 }
 
 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.flags &= dev->settings.flags;
        system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
        return system_if_flags(dev->ifname, IFF_UP, 0);
 }
@@ -1022,7 +1086,6 @@ int system_if_up(struct device *dev)
 int system_if_down(struct device *dev)
 {
        int ret = system_if_flags(dev->ifname, 0, IFF_UP);
-       dev->orig_settings.flags &= dev->settings.flags;
        system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags);
        return ret;
 }
@@ -1394,11 +1457,16 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
                                rtm.rtm_table = RT_TABLE_LOCAL;
                }
 
-               if (rtm.rtm_type == RTN_LOCAL || rtm.rtm_type == RTN_NAT)
+               if (rtm.rtm_type == RTN_LOCAL || rtm.rtm_type == RTN_NAT) {
                        rtm.rtm_scope = RT_SCOPE_HOST;
-               else if (rtm.rtm_type == RTN_BROADCAST || rtm.rtm_type == RTN_MULTICAST ||
-                       rtm.rtm_type == RTN_ANYCAST)
+               else if (rtm.rtm_type == RTN_BROADCAST || rtm.rtm_type == RTN_MULTICAST ||
+                               rtm.rtm_type == RTN_ANYCAST) {
                        rtm.rtm_scope = RT_SCOPE_LINK;
+               } else if (rtm.rtm_type == RTN_BLACKHOLE || rtm.rtm_type == RTN_UNREACHABLE ||
+                               rtm.rtm_type == RTN_PROHIBIT || rtm.rtm_type == RTN_FAILED_POLICY) {
+                       rtm.rtm_scope = RT_SCOPE_UNIVERSE;
+                       dev = NULL;
+               }
        }
 
        msg = nlmsg_alloc_simple(cmd, flags);
@@ -1410,8 +1478,12 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
        if (route->mask)
                nla_put(msg, RTA_DST, alen, &route->addr);
 
-       if (route->sourcemask)
-               nla_put(msg, RTA_SRC, alen, &route->source);
+       if (route->sourcemask) {
+               if (rtm.rtm_family == AF_INET)
+                       nla_put(msg, RTA_PREFSRC, alen, &route->source);
+               else
+                       nla_put(msg, RTA_SRC, alen, &route->source);
+       }
 
        if (route->metric > 0)
                nla_put_u32(msg, RTA_PRIORITY, route->metric);