summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2015-12-15 11:57:48 +0100
committerFelix Fietkau <nbd@openwrt.org>2015-12-15 11:57:52 +0100
commitd5f53f4d79231f59494da852b73d2d9d2b9c43cc (patch)
treee6faf6554096249b7658634259ce971c82a67be2
parente911dd3a798a543d40b93fe8dcb0824bd011c621 (diff)
downloadnetifd-d5f53f4d79231f59494da852b73d2d9d2b9c43cc.tar.gz
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 <nbd@openwrt.org>
-rw-r--r--interface-ip.c48
-rw-r--r--interface-ip.h2
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;