netifd: Do not add local/source policy rules multiple times
[project/netifd.git] / interface-ip.c
index 220f4a0..b96d98c 100644 (file)
@@ -274,6 +274,24 @@ done:
        return iface;
 }
 
+static void
+interface_set_route_info(struct interface *iface, struct device_route *route)
+{
+       bool v6 = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET6);
+
+       if (!iface)
+               return;
+
+       if (!(route->flags & DEVROUTE_METRIC))
+               route->metric = iface->metric;
+
+       if (!(route->flags & DEVROUTE_TABLE)) {
+               route->table = (v6) ? iface->ip6table : iface->ip4table;
+               if (route->table)
+                       route->flags |= DEVROUTE_SRCTABLE;
+       }
+}
+
 void
 interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
 {
@@ -281,7 +299,6 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
        struct blob_attr *tb[__ROUTE_MAX], *cur;
        struct device_route *route;
        int af = v6 ? AF_INET6 : AF_INET;
-       bool is_proto_route = !!iface;
 
        blobmsg_parse(route_attr, __ROUTE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
 
@@ -327,8 +344,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
        if ((cur = tb[ROUTE_METRIC]) != NULL) {
                route->metric = blobmsg_get_u32(cur);
                route->flags |= DEVROUTE_METRIC;
-       } else
-               route->metric = iface->metric;
+       }
 
        if ((cur = tb[ROUTE_MTU]) != NULL) {
                route->mtu = blobmsg_get_u32(cur);
@@ -354,11 +370,6 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
        if ((cur = tb[ROUTE_ONLINK]) != NULL && blobmsg_get_bool(cur))
                route->flags |= DEVROUTE_ONLINK;
 
-       if (is_proto_route) {
-               route->table = (v6) ? iface->ip6table : iface->ip4table;
-               route->flags |= DEVROUTE_SRCTABLE;
-       }
-
        if ((cur = tb[ROUTE_TABLE]) != NULL) {
                if (!system_resolve_rt_table(blobmsg_data(cur), &route->table)) {
                        DPRINTF("Failed to resolve routing table: %s\n", (char *) blobmsg_data(cur));
@@ -388,6 +399,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
                route->flags |= DEVROUTE_TYPE;
        }
 
+       interface_set_route_info(iface, route);
        vlist_add(&ip->route, &route->node, route);
        return;
 
@@ -440,32 +452,33 @@ static void
 interface_handle_subnet_route(struct interface *iface, struct device_addr *addr, bool add)
 {
        struct device *dev = iface->l3_dev.dev;
-       struct device_route route;
-       bool v6 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6);
+       struct device_route *r = &addr->subnet;
 
        if (addr->flags & DEVADDR_OFFLINK)
                return;
 
-       memset(&route, 0, sizeof(route));
-       route.iface = iface;
-       route.flags = addr->flags;
-       route.mask = addr->mask;
-       memcpy(&route.addr, &addr->addr, sizeof(route.addr));
-       clear_if_addr(&route.addr, route.mask);
-
-       if (add) {
-               route.flags |= DEVADDR_KERNEL;
-               system_del_route(dev, &route);
+       if (!add) {
+               if (!addr->subnet.iface)
+                       return;
 
-               route.flags &= ~DEVADDR_KERNEL;
-               route.metric = iface->metric;
-               route.table = (v6) ? iface->ip6table : iface->ip4table;
-               if (route.table)
-                       route.flags |= DEVROUTE_SRCTABLE;
-               system_add_route(dev, &route);
-       } else {
-               system_del_route(dev, &route);
+               system_del_route(dev, r);
+               memset(r, 0, sizeof(*r));
+               return;
        }
+
+       r->iface = iface;
+       r->flags = addr->flags;
+       r->mask = addr->mask;
+       memcpy(&r->addr, &addr->addr, sizeof(r->addr));
+       clear_if_addr(&r->addr, r->mask);
+
+       r->flags |= DEVADDR_KERNEL;
+       system_del_route(dev, r);
+
+       r->flags &= ~DEVADDR_KERNEL;
+       interface_set_route_info(iface, r);
+
+       system_add_route(dev, r);
 }
 
 static void
@@ -738,6 +751,7 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
        route.mask = addr.mask < 64 ? 64 : addr.mask;
        route.addr = addr.addr;
        clear_if_addr(&route.addr, route.mask);
+       interface_set_route_info(iface, &route);
 
        if (!add && assignment->enabled) {
                time_t now = system_get_rtime();
@@ -1230,10 +1244,11 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 
                if (enabled) {
                        system_add_address(dev, addr);
-                       if (iface->metric)
-                               interface_handle_subnet_route(iface, addr, true);
 
                        addr->policy_table = (v6) ? iface->ip6table : iface->ip4table;
+                       if (iface->metric || addr->policy_table)
+                               interface_handle_subnet_route(iface, addr, true);
+
                        if (addr->policy_table)
                                set_ip_source_policy(true, v6, IPRULE_PRIORITY_ADDR, &addr->addr,
                                                (v6) ? 128 : 32, addr->policy_table, NULL, NULL);
@@ -1258,17 +1273,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
                        continue;
 
                if (_enabled) {
-                       if (!(route->flags & DEVROUTE_METRIC))
-                               route->metric = ip->iface->metric;
-
-                       if (!(route->flags & DEVROUTE_TABLE)) {
-                               route->flags &= ~DEVROUTE_SRCTABLE;
-                               route->table = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET6) ?
-                                                       iface->ip6table : iface->ip4table;
-
-                               if (route->table)
-                                       route->flags |= DEVROUTE_SRCTABLE;
-                       }
+                       interface_set_route_info(ip->iface, route);
 
                        if (system_add_route(dev, route))
                                route->failed = true;
@@ -1284,12 +1289,14 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
                        if (!strcmp(a->name, ip->iface->name))
                                interface_set_prefix_address(a, c, ip->iface, enabled);
 
-       if (ip->iface && ip->iface->l3_dev.dev) {
+       if (ip->iface && ip->iface->policy_rules_set != enabled &&
+           ip->iface->l3_dev.dev) {
                set_ip_lo_policy(enabled, true, ip->iface);
                set_ip_lo_policy(enabled, false, ip->iface);
 
                set_ip_source_policy(enabled, true, IPRULE_PRIORITY_REJECT + ip->iface->l3_dev.dev->ifindex,
                        NULL, 0, 0, ip->iface, "failed_policy");
+               ip->iface->policy_rules_set = enabled;
        }
 }