realtek: l3: rtl930x: test the slot that was read

otto_l3_930x_find_slot() probed the host route table and then looked
at the caller's route instead of the entry it had just read. The
placement test asked rt->attr.valid, which both call sites set to true
right before calling, so the first address probed was always returned:
of the 6144 host route slots only the 512 bucket heads of the first
table were reachable, and a second route in a bucket overwrote the
first. The lookup test compared route_entry.dst_ip, which
otto_l3_930x_host_route_read() does not touch when the entry is
invalid or is not IPv4 unicast, so it could match an address the
reader never wrote.

Ask the entry that was read: an empty slot for placement, and for a
lookup a valid entry of the same type whose destination matches. IPv6
unicast destinations are compared with ipv6_addr_equal() instead of
through the IPv4 field.

Measured on an RTL9303 (Hasivo S1100W-8XGT-SE): two /32 host routes
hashing to bucket 281 both ended up at logical index 1686 before this
patch. With it each route takes a slot of its own, which is also what
exposed the duplicate writes the next patch removes.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25170
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Gennaro Cimmino authored and Markus Stockhausen committed 2026-09-14 21:45:55 +02:00
1 parent 522cb4b864
commit 4344b47ebc
1 file changed
+15 -3
@@ -297,10 +297,22 @@ static int otto_l3_930x_find_slot(struct otto_l3_ctrl *ctrl, struct otto_l3_rout
otto_l3_930x_host_route_read(ctrl, idx, &route_entry);
dev_dbg(ctrl->dev, "route valid %d, route dest: %pI4, hit %d\n",
rt->attr.valid, &rt->dst_ip, rt->attr.hit);
if (!must_exist && rt->attr.valid)
return idx;
if (must_exist && route_entry.dst_ip == rt->dst_ip)
if (!must_exist && !route_entry.attr.valid)
return idx;
if (must_exist &&
route_entry.attr.valid &&
route_entry.attr.type == rt->attr.type) {
switch (rt->attr.type) {
case 0:
if (route_entry.dst_ip == rt->dst_ip)
return idx;
break;
case 2:
if (ipv6_addr_equal(&route_entry.dst_ip6, &rt->dst_ip6))
return idx;
break;
}
}
}
}