interface-ip: fix subnet route handling
authorFelix Fietkau <nbd@openwrt.org>
Tue, 15 Dec 2015 10:57:48 +0000 (11:57 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 15 Dec 2015 10:57:52 +0000 (11:57 +0100)
When the kernel subnet route has to be replaced, the cleanup call needs
to match the properties of the replacement route exactly, mainly the
metric and the routing table.
Fix handling this by embedding the device_route for the subnet in the
device_addr struct and using it in the cleanup path.

This fixes issues on config reload with changes to the routing table

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
interface-ip.c
interface-ip.h

index 220f4a0..5533615 100644 (file)
@@ -440,32 +440,37 @@ static void
 interface_handle_subnet_route(struct interface *iface, struct device_addr *addr, bool add)
 {
        struct device *dev = iface->l3_dev.dev;
 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);
        bool v6 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6);
+       struct device_route *r = &addr->subnet;
 
        if (addr->flags & DEVADDR_OFFLINK)
                return;
 
 
        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;
+       r->metric = iface->metric;
+       r->table = (v6) ? iface->ip6table : iface->ip4table;
+       if (r->table)
+               r->flags |= DEVROUTE_SRCTABLE;
+
+       system_add_route(dev, r);
 }
 
 static void
 }
 
 static void
@@ -1230,10 +1235,11 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 
                if (enabled) {
                        system_add_address(dev, addr);
 
                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;
 
                        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);
                        if (addr->policy_table)
                                set_ip_source_policy(true, v6, IPRULE_PRIORITY_ADDR, &addr->addr,
                                                (v6) ? 128 : 32, addr->policy_table, NULL, NULL);
index f24b0ec..bbef62c 100644 (file)
@@ -110,6 +110,8 @@ struct device_addr {
        bool failed;
        unsigned int policy_table;
 
        bool failed;
        unsigned int policy_table;
 
+       struct device_route subnet;
+
        /* ipv4 only */
        uint32_t broadcast;
        uint32_t point_to_point;
        /* ipv4 only */
        uint32_t broadcast;
        uint32_t point_to_point;