[PATCH] interface: reapply routes on l3 link up
Ruslan Isaev
legale.legale at gmail.com
Sun May 24 14:28:32 PDT 2026
Proto interfaces such as WireGuard can lose routes from the kernel FIB
after a link flap while netifd still keeps them marked as enabled.
Because of that, the route stays visible in ifstatus but disappears from
ip route until the protocol is restarted.
Replay the saved config and proto route lists on DEV_EVENT_LINK_UP and
allow the route enable path to call system_add_route() again for routes
that are already marked enabled internally.
This restores routes such as WireGuard allowed_ips after link recovery
without forcing a full proto restart.
Signed-off-by: Ruslan Isaev <legale.legale at gmail.com>
---
interface-ip.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
interface-ip.h | 1 +
interface.c | 18 ++++++++++++++++++
3 files changed, 60 insertions(+), 8 deletions(-)
diff --git a/interface-ip.c b/interface-ip.c
index 8866310..c24a615 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -1631,6 +1631,10 @@ interface_ip_set_route_enabled(struct interface_ip_settings *ip,
struct device_route *route, bool enabled)
{
struct device *dev = ip->iface->l3_dev.dev;
+ char addr[INET6_ADDRSTRLEN];
+ int af;
+ int ret;
+ const char *str;
if (route->flags & DEVADDR_EXTERNAL)
return;
@@ -1638,18 +1642,47 @@ interface_ip_set_route_enabled(struct interface_ip_settings *ip,
if (!enable_route(ip, route))
enabled = false;
- if (route->enabled == enabled)
+ if (!enabled) {
+ if (!route->enabled)
+ return;
+
+ system_del_route(dev, route);
+ route->enabled = false;
+ route->failed = false;
return;
+ }
- if (enabled) {
- interface_set_route_info(ip->iface, route);
+ interface_set_route_info(ip->iface, route);
- if (system_add_route(dev, route))
- route->failed = true;
- } else
- system_del_route(dev, route);
+ af = (route->flags & DEVADDR_FAMILY) == DEVADDR_INET6 ? AF_INET6 : AF_INET;
+ str = inet_ntop(af, &route->addr, addr, sizeof(addr));
+ if (!str)
+ str = "?";
+
+ ret = system_add_route(dev, route);
+ netifd_log_message(L_DEBUG,
+ "Interface '%s': add route %s/%u dev %s ret=%d\n",
+ ip->iface->name, str, route->mask,
+ dev ? dev->ifname : "-", ret);
+
+ if (ret)
+ route->failed = true;
+ else
+ route->failed = false;
+
+ route->enabled = true;
+}
+
+void
+interface_ip_add_routes(struct interface_ip_settings *ip)
+{
+ struct device_route *route;
- route->enabled = enabled;
+ if (!ip->iface->l3_dev.dev)
+ return;
+
+ vlist_for_each_element(&ip->route, route, node)
+ interface_ip_set_route_enabled(ip, route, true);
}
void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
diff --git a/interface-ip.h b/interface-ip.h
index cc7efbd..738a828 100644
--- a/interface-ip.h
+++ b/interface-ip.h
@@ -184,6 +184,7 @@ void interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr,
void interface_ip_update_start(struct interface_ip_settings *ip);
void interface_ip_update_complete(struct interface_ip_settings *ip);
void interface_ip_flush(struct interface_ip_settings *ip);
+void interface_ip_add_routes(struct interface_ip_settings *ip);
void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled);
void interface_ip_update_metric(struct interface_ip_settings *ip, int metric);
diff --git a/interface.c b/interface.c
index c8771bf..a805461 100644
--- a/interface.c
+++ b/interface.c
@@ -422,6 +422,16 @@ interface_set_link_state(struct interface *iface, bool new_state)
}
}
+static void
+interface_retry_routes(struct interface *iface)
+{
+ if (iface->state != IFS_UP || !iface->l3_dev.dev)
+ return;
+
+ interface_ip_add_routes(&iface->config_ip);
+ interface_ip_add_routes(&iface->proto_ip);
+}
+
static void
interface_ext_dev_cb(struct device_user *dep, enum device_event ev)
{
@@ -451,7 +461,12 @@ interface_main_dev_cb(struct device_user *dep, enum device_event ev)
interface_set_enabled(iface, false);
break;
case DEV_EVENT_AUTH_UP:
+ interface_set_link_state(iface, device_link_active(dep->dev));
+ break;
case DEV_EVENT_LINK_UP:
+ if (iface->main_dev.dev == iface->l3_dev.dev)
+ interface_retry_routes(iface);
+ fallthrough;
case DEV_EVENT_LINK_DOWN:
interface_set_link_state(iface, device_link_active(dep->dev));
break;
@@ -474,6 +489,9 @@ interface_l3_dev_cb(struct device_user *dep, enum device_event ev)
return;
switch (ev) {
+ case DEV_EVENT_LINK_UP:
+ interface_retry_routes(iface);
+ break;
case DEV_EVENT_LINK_DOWN:
if (iface->proto_handler->flags & PROTO_FLAG_TEARDOWN_ON_L3_LINK_DOWN)
interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, false);
--
2.39.5
More information about the openwrt-devel
mailing list