From d3301f3ea02298a506d32510671174f60e27982f Mon Sep 17 00:00:00 2001 From: Gennaro Cimmino Date: Sat, 3 Oct 2026 22:37:26 +0200 Subject: [PATCH] realtek: l3: place local table rows ahead of main's The kernel looks the local table up before main, so an address of the switch is delivered to it even when main holds a route of the same length to that address. The hardware answers with the first prefix row that matches, and a route is placed after every row of its length or longer: whichever of the two came first won, and a /128 route in main added before the address forwarded the address's traffic away. Place a local table entry after the longer rows only, ahead of main's of the same length. Main's row stays, and answers again once the address is gone. Tested on a Hasivo S1100W-8XGT-SE (RTL9303) with L3 offload enabled, main against main with this and the next commit, which only touches the IPv4 host table. fd00:9f::1/128 through a gateway in hardware, then fd00:9f::1/64 added to the switch, and 5 echoes to the address: main left the route's row ahead of the address's and forwarded all 5 to the gateway, unanswered; now the switch answers all 5. With the address gone again, the route forwards all 5 to its gateway on both. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino Link: https://github.com/openwrt/openwrt/pull/25598 Signed-off-by: Markus Stockhausen --- .../linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 817b6022b18..9496aa4e253 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 @@ -1046,7 +1046,12 @@ static int otto_l3_route_place(struct otto_l3_ctrl *ctrl, struct otto_l3_route * if (ctrl->prefix_rows_stale) return -1; - below = otto_l3_prefix_rows(ctrl, r->attr.type, r->prefix_len); + /* The local table is looked up before main, so its entries go ahead of + * main's of the same length: the hardware answers with the first row + * that matches. + */ + below = otto_l3_prefix_rows(ctrl, r->attr.type, + r->prefix_len + (r->tb_id == RT_TABLE_LOCAL)); rows = otto_l3_prefix_rows(ctrl, r->attr.type, 0); if (r->attr.type == ROUTE_TYPE_IP6UC) {