mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-12 06:57:57 +04:00
realtek: l3: keep main /32s out of a local address's entry
The host table holds one entry per IPv4 address, found by the address alone. A /32 in main for an address of the switch shared that entry with the local route: whichever wrote last owned it, a neighbour update of the main route turned the local trap into forwarding, and deleting the main route took the entry from the local one. The kernel looks the local table up first, so the address is the switch's for as long as it holds it. While a local route holds an address, a host route from main for it leaves the entry alone - on add, neighbour update, trap and delete - and keeps its state in memory. When the local route goes, the main route writes its entry back as it last stood. Tested on a Hasivo S1100W-8XGT-SE (RTL9303) with L3 offload enabled, main against main with this and the previous commit. A /32 route through a gateway in hardware, then the same address added to the switch, and 5 echoes to it: - after a neighbour update of the gateway, main turned the address's entry into forwarding and sent all 5 to the gateway, unanswered; now the switch answers all 5. - with a /24 through the gateway covering it and the /32 route deleted, main took the address's entry away and the /24 forwarded all 5; now the switch answers all 5. - with a /24 through another gateway covering it and the address deleted, main took the /32 route's entry with it and the /24 sent all 5 to that other gateway; now the /32 route forwards all 5 to its own. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net> Link: https://github.com/openwrt/openwrt/pull/25598 Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
1 parent
d3301f3ea0
commit
df85424b93
1 file changed
+63
-2
@@ -1186,6 +1186,46 @@ static const char *otto_l3_route_dst(struct otto_l3_route *r, char *buf, size_t
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* The host table holds one entry per address, and the local table is looked up
|
||||
* before main: while an address is the switch's own, the entry is the local
|
||||
* route's, and a host route from main for that address keeps out of it.
|
||||
*/
|
||||
static bool otto_l3_host_shadowed(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r)
|
||||
{
|
||||
struct otto_l3_route *q;
|
||||
|
||||
if (!r->is_host_route || r->tb_id == RT_TABLE_LOCAL)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(q, &ctrl->routes_list, list)
|
||||
if (q != r && q->is_host_route && q->tb_id == RT_TABLE_LOCAL &&
|
||||
q->attr.type == r->attr.type && q->dst_ip == r->dst_ip)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The address is the switch's no longer: a host route from main for it gets
|
||||
* the entry back, as it last wrote it
|
||||
*/
|
||||
static void otto_l3_host_unshadow(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r)
|
||||
{
|
||||
struct otto_l3_route *q;
|
||||
int slot;
|
||||
|
||||
list_for_each_entry(q, &ctrl->routes_list, list) {
|
||||
if (q == r || !q->is_host_route || q->tb_id == RT_TABLE_LOCAL ||
|
||||
q->attr.type != r->attr.type || q->dst_ip != r->dst_ip ||
|
||||
!q->attr.valid)
|
||||
continue;
|
||||
|
||||
slot = ctrl->cfg->find_slot(ctrl, q, false);
|
||||
if (slot >= 0)
|
||||
ctrl->cfg->host_route_write(ctrl, slot, q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r,
|
||||
u64 mac)
|
||||
{
|
||||
@@ -1249,7 +1289,10 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
|
||||
r->pr.dip_m = inet_make_mask(r->prefix_len);
|
||||
}
|
||||
|
||||
if (r->is_host_route) {
|
||||
if (otto_l3_host_shadowed(ctrl, r)) {
|
||||
dev_dbg(ctrl->dev, "%pI4 is an address of the switch, its entry stays\n",
|
||||
&r->dst_ip);
|
||||
} else if (r->is_host_route) {
|
||||
int slot = ctrl->cfg->find_slot(ctrl, r, true);
|
||||
|
||||
if (slot < 0)
|
||||
@@ -1334,6 +1377,9 @@ static void otto_l3_route_trap_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_rout
|
||||
r->attr.ttl_dec = false;
|
||||
r->attr.ttl_check = false;
|
||||
|
||||
if (otto_l3_host_shadowed(ctrl, r))
|
||||
return;
|
||||
|
||||
if (r->is_host_route)
|
||||
ctrl->cfg->host_route_write(ctrl, slot, r);
|
||||
else
|
||||
@@ -1546,7 +1592,9 @@ static void otto_l3_route_remove(struct otto_l3_ctrl *ctrl, struct otto_l3_route
|
||||
char dst[INET6_ADDRSTRLEN + sizeof("/128")];
|
||||
int id;
|
||||
|
||||
if (r->is_host_route) {
|
||||
if (otto_l3_host_shadowed(ctrl, r)) {
|
||||
/* The entry for the address is the local route's */
|
||||
} else if (r->is_host_route) {
|
||||
id = ctrl->cfg->find_slot(ctrl, r, true);
|
||||
if (id >= 0) {
|
||||
dev_dbg(ctrl->dev, "Got id for host route: %d\n", id);
|
||||
@@ -1556,6 +1604,9 @@ static void otto_l3_route_remove(struct otto_l3_ctrl *ctrl, struct otto_l3_route
|
||||
dev_err(ctrl->dev, "Host route %pI4 was not in hardware\n",
|
||||
&r->dst_ip);
|
||||
}
|
||||
|
||||
if (r->tb_id == RT_TABLE_LOCAL)
|
||||
otto_l3_host_unshadow(ctrl, r);
|
||||
} else {
|
||||
/* If there is a HW representation of the route, delete it */
|
||||
if (ctrl->cfg->route_lookup_hw && r->row >= FIRST_PREFIX_ROW) {
|
||||
@@ -1763,6 +1814,9 @@ static void otto_l3_host_route_trap(struct otto_l3_ctrl *ctrl,
|
||||
route->attr.action = ROUTE_ACT_TRAP2CPU;
|
||||
route->attr.type = ROUTE_TYPE_IP4UC;
|
||||
|
||||
if (otto_l3_host_shadowed(ctrl, route))
|
||||
return;
|
||||
|
||||
slot = ctrl->cfg->find_slot(ctrl, route, true);
|
||||
if (slot < 0)
|
||||
slot = ctrl->cfg->find_slot(ctrl, route, false);
|
||||
@@ -1827,6 +1881,9 @@ static void otto_l3_route_trap_new(struct otto_l3_ctrl *ctrl, struct otto_l3_rou
|
||||
r->attr.valid = true;
|
||||
r->attr.action = ROUTE_ACT_TRAP2CPU;
|
||||
|
||||
if (otto_l3_host_shadowed(ctrl, r))
|
||||
return;
|
||||
|
||||
if (r->is_host_route) {
|
||||
slot = ctrl->cfg->find_slot(ctrl, r, true);
|
||||
if (slot < 0)
|
||||
@@ -1950,6 +2007,9 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
route->attr.action = ROUTE_ACT_TRAP2CPU;
|
||||
route->attr.type = ROUTE_TYPE_IP4UC;
|
||||
|
||||
if (otto_l3_host_shadowed(ctrl, route))
|
||||
goto resolve;
|
||||
|
||||
slot = ctrl->cfg->find_slot(ctrl, route, true);
|
||||
if (slot < 0)
|
||||
slot = ctrl->cfg->find_slot(ctrl, route, false);
|
||||
@@ -1974,6 +2034,7 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
if (ctrl->cfg->use_l3_tables && (nh->fib_nh_gw4 || !route->is_host_route))
|
||||
otto_l3_route_trap_new(ctrl, route);
|
||||
|
||||
resolve:
|
||||
/* We need to resolve the mac address of the GW */
|
||||
if (nh->fib_nh_gw4)
|
||||
otto_l3_port_gw_resolve(ctrl, ndev, &arp_tbl, &gw);
|
||||
|
||||
Reference in new issue
Block a user