realtek: l3: skip IPv4 routes without a nexthop device

A blackhole, unreachable or prohibit route has no device behind its
nexthop: fib_create_info() refuses one for these types and skips the
nexthop check that would find one, so fib_nh_dev stays NULL.
otto_l3_fib_add_v4() hands that device to is_vlan_dev() before any
test, and otto_l3_fib_check_v4(), which the delete path runs as well,
does the same in its variable initialisers. On an RTL9303 with the L3
offload built in, "ip route add blackhole 10.99.8.0/24" oopses in the
rtl83xx work queue and panics the box:

  CPU 0 Unable to handle kernel paging request at virtual address
  00000000, epc == 8070fe3c, ra == 80710594
  Workqueue: rtl83xx otto_l3_fib_event_work_do
  epc   : 8070fe3c otto_l3_fib_add_v4+0x1c0/0x794
  Kernel panic - not syncing: Fatal exception

The faulting load is the priv_flags word is_vlan_dev() tests. Such
routes are common on a router, for address space it announces but does
not use.

Handle them where a route through a nexthop object is already handled,
for the same reasons: there is nothing to offload, and the route can
still replace one that was, so the entry for its destination is taken
out, and a host route is trapped where the host table can hold it. The
delete path takes the same exit.

Tested on an RTL9303 (Hasivo S1100W-8XGT-SE) with the L3 offload built
in. Without this change the command above panics the box; with it, a
blackhole /24 stays in the kernel with no entry in hardware, an
offloaded /24 replaced by a blackhole loses its entry and gets it back
when replaced through the gateway again, a blackhole /32 gets a trap
entry in the host table, unreachable and prohibit routes go in as
well, and every delete leaves no entry and no oops. RTL839x, which runs
this code in a stock build, was not tested.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25513
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Gennaro Cimmino authored and Markus Stockhausen committed 2026-10-01 19:49:57 +02:00
1 parent db61fd4444
commit e59089db5f
1 file changed
+11 -8
@@ -1526,13 +1526,14 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
struct fib_nh *nh;
int port, vlan;
/* A route through a nexthop object is not offloaded and has no
* nexthop array to read, but it can replace a route that is. An
* offloaded shorter prefix would forward its traffic, so trap it where
* the host table can hold it.
/* A route through a nexthop object has no nexthop array to read, and a
* blackhole, unreachable or prohibit route has no device behind its
* nexthop. Neither is offloaded, but either can replace a route that
* is. An offloaded shorter prefix would forward its traffic, so trap
* it where the host table can hold it.
*/
if (info->fi->nh) {
dev_dbg(ctrl->dev, "route through a nexthop object, not offloaded\n");
if (info->fi->nh || !fib_info_nh(info->fi, 0)->fib_nh_dev) {
dev_dbg(ctrl->dev, "route not offloaded: no device or a nexthop object\n");
route = otto_l3_route_find(ctrl, info->tb_id, ROUTE_TYPE_IP4UC, info->dst,
NULL, info->dst_len);
if (route)
@@ -1646,8 +1647,10 @@ static int otto_l3_fib_del_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
struct in6_addr gw;
struct fib_nh *nh;
/* A route through a nexthop object holds at most a trap entry */
if (info->fi->nh) {
/* A route through a nexthop object or without a device holds at most a
* trap entry
*/
if (info->fi->nh || !fib_info_nh(info->fi, 0)->fib_nh_dev) {
route = otto_l3_route_find(ctrl, info->tb_id, ROUTE_TYPE_IP4UC, info->dst,
NULL, info->dst_len);
if (route)