From d5f53f4d79231f59494da852b73d2d9d2b9c43cc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 15 Dec 2015 11:57:48 +0100 Subject: interface-ip: fix subnet route handling 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 --- interface-ip.c | 48 +++++++++++++++++++++++++++--------------------- interface-ip.h | 2 ++ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/interface-ip.c b/interface-ip.c index 220f4a0..5533615 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -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; - 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; + 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 @@ -1230,10 +1235,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); diff --git a/interface-ip.h b/interface-ip.h index f24b0ec..bbef62c 100644 --- a/interface-ip.h +++ b/interface-ip.h @@ -110,6 +110,8 @@ struct device_addr { bool failed; unsigned int policy_table; + struct device_route subnet; + /* ipv4 only */ uint32_t broadcast; uint32_t point_to_point; -- cgit v1.2.3