From 47b46ced13d24735182e48e63fbbeaa5bc5f41a4 Mon Sep 17 00:00:00 2001 From: Gennaro Cimmino Date: Sun, 4 Oct 2026 20:47:37 +0200 Subject: [PATCH] realtek: l3: keep a trap whose source list is incomplete A prefix with source-specific IPv6 routes gets one trap row, which keeps a list of their sources and goes once the list is empty and no route without a source is left. When the allocation for a source failed, that source was left out: once the list was empty the row went, or a route without a source took it over, though the kernel still had the source-specific route the row was trapping for. Mark the row when a source could not be tracked, count it as still standing for sources, and ask for a FIB replay, which builds the list again from scratch. Tested on a Hasivo S1100W-8XGT-SE (RTL9303) with L3 offload enabled, through a bench-only debugfs counter that fails the next source allocation: under an IPv6 /32 forwarding in hardware, a /48 with a source-specific route via another host and a source-specific blackhole for the sender, whose tracking fails. Once the first is deleted, the kernel drops the sender's echoes: without this change the trap row goes and the /32 forwards all 5 to the host, with it the row stays, the FIB is replayed and none arrive. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino Link: https://github.com/openwrt/openwrt/pull/25623 Signed-off-by: Markus Stockhausen --- .../files-6.18/drivers/net/dsa/rtl83xx/l3.c | 25 ++++++++++++++++--- .../files-6.18/drivers/net/dsa/rtl83xx/l3.h | 1 + 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c index 36d42d47b94..9bfdf3003c0 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c @@ -2235,6 +2235,14 @@ static struct otto_l3_route_src *otto_l3_route_src_find(struct otto_l3_route *r, return NULL; } +/* A source that could not be tracked is still a route the row stands for */ +static bool otto_l3_route_has_srcs(struct otto_l3_route *r) +{ + return !list_empty(&r->srcs) || r->srcs_incomplete; +} + +static void otto_l3_resync_request(struct otto_l3_ctrl *ctrl); + /* A source-specific route matches only the packets from its source, and the * hardware matches on the destination alone, so the destination traps for as * long as one of them remains. The trap row counts the members of each source @@ -2278,8 +2286,17 @@ static int otto_l3_fib_add_v6_src(struct otto_l3_ctrl *ctrl, struct fib6_info *r s = otto_l3_route_src_find(route, &rt->fib6_src); if (!s) { s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) + if (!s) { + /* The row keeps trapping whatever the list says; a replay + * builds the list again + */ + route->srcs_incomplete = true; + dev_err(ctrl->dev, "%pI6c/%d from %pI6c/%d: no memory to track the source\n", + &rt->fib6_dst.addr, rt->fib6_dst.plen, + &rt->fib6_src.addr, rt->fib6_src.plen); + otto_l3_resync_request(ctrl); return -ENOMEM; + } s->addr = rt->fib6_src.addr; s->plen = rt->fib6_src.plen; list_add_tail(&s->list, &route->srcs); @@ -2312,7 +2329,7 @@ static int otto_l3_fib_del_v6_src(struct otto_l3_ctrl *ctrl, struct fib6_info *r list_del(&s->list); kfree(s); - if (list_empty(&route->srcs) && !route->members) + if (!otto_l3_route_has_srcs(route) && !route->members) otto_l3_route_teardown(ctrl, route); return 0; @@ -2389,7 +2406,7 @@ static int otto_l3_fib_add_v6(struct otto_l3_ctrl *ctrl, struct fib6_entry_notif */ route = otto_l3_route_find(ctrl, rt->fib6_table->tb6_id, ROUTE_TYPE_IP6UC, 0, &rt->fib6_dst.addr, rt->fib6_dst.plen); - if (route && !list_empty(&route->srcs)) { + if (route && otto_l3_route_has_srcs(route)) { route->members = members; return 0; } @@ -2507,7 +2524,7 @@ static int otto_l3_fib_del_v6(struct otto_l3_ctrl *ctrl, struct fib6_entry_notif if (route && ipv6_addr_any(&route->gw_ip)) { if (route->members > members) route->members -= members; - else if (!list_empty(&route->srcs)) + else if (otto_l3_route_has_srcs(route)) route->members = 0; else otto_l3_route_teardown(ctrl, route); diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h index 324d53e5467..acbc5e20865 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h @@ -87,6 +87,7 @@ struct otto_l3_route { int row; /* Row it occupies in the prefix route table */ unsigned int members; /* FIB entries a trap row stands for */ struct list_head srcs; /* source-specific routes a trap row stands for */ + bool srcs_incomplete; /* a source could not be tracked */ struct rhlist_head linkage; struct list_head list; /* all routes, for lookups by destination */ u32 tb_id; /* routing table the route came from */