mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-14 10:57:56 +04:00
realtek: l3: key routes on a 16-byte gateway address
Routes are grouped by the gateway they use, so that resolving one neighbour reprograms every route behind it. The key is four bytes wide, which is the whole of an IPv4 gateway and a quarter of any other kind, and the field is read nowhere else: it never reaches a register, and what the hardware is given is the next hop MAC the resolution produced. Widen it to a full address and store an IPv4 gateway v4-mapped. Within one family the mapping is injective, so two IPv4 gateways share a key exactly when they shared one before, including the connected routes that arrive with no gateway at all and go on sharing a single one; mapping instead of zero-extending is also what keeps that shared key distinct from the all-zero address a gateway of another family can carry. What a wider key cannot do is say which family a gateway belongs to. An IPv4 gateway written v4-mapped is a valid IPv6 gateway, and the kernel accepts one on an IPv6 route, so a single key can name two neighbours that are looked up in different tables and answer with different addresses. The routes behind a key are therefore reprogrammed, and a delete matched, only for the family the event came from. The allocators and the next hop update take the gateway by address rather than by value, so the compiler refuses a bare address of the wrong width where one enters them. The raw lookup in the delete path takes a void pointer and gets no such help; it is fed the same mapped address its route was keyed on. The one message of the next hop update that names a gateway to the user reads the IPv4 address back out of the mapped one, so it prints what it printed before. Assisted-by: Claude:claude-opus-5 Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net> Link: https://github.com/openwrt/openwrt/pull/25507 Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
1 parent
ace2caa02c
commit
655b49fda2
2 files changed
+41
-23
No files matched your search
@@ -18,7 +18,7 @@
|
||||
#include "rtl-otto.h"
|
||||
|
||||
static const struct rhashtable_params otto_l3_route_ht_params = {
|
||||
.key_len = sizeof(u32),
|
||||
.key_len = sizeof(struct in6_addr),
|
||||
.key_offset = offsetof(struct otto_l3_route, gw_ip),
|
||||
.head_offset = offsetof(struct otto_l3_route, linkage),
|
||||
};
|
||||
@@ -27,7 +27,7 @@ struct otto_l3_net_event_work {
|
||||
struct work_struct work;
|
||||
struct otto_l3_ctrl *ctrl;
|
||||
u64 mac;
|
||||
u32 gw_addr;
|
||||
struct in6_addr gw_addr;
|
||||
};
|
||||
|
||||
struct otto_l3_fib_event_work {
|
||||
@@ -1040,7 +1040,8 @@ static void otto_l3_route_compact(struct otto_l3_ctrl *ctrl, struct otto_l3_rout
|
||||
}
|
||||
|
||||
/* Updates an L3 next hop entry in the ROUTING table */
|
||||
static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, __be32 ip_addr, u64 mac)
|
||||
static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, u8 type,
|
||||
const struct in6_addr *gw, u64 mac)
|
||||
{
|
||||
struct rtl838x_switch_priv *priv = ctrl->priv;
|
||||
bool require_existing = ctrl->cfg->use_l3_tables;
|
||||
@@ -1057,7 +1058,7 @@ static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, __be32 ip_addr, u64
|
||||
* of the driver walk that list with no lock.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
known = rhltable_lookup(&ctrl->routes, &ip_addr, otto_l3_route_ht_params);
|
||||
known = rhltable_lookup(&ctrl->routes, gw, otto_l3_route_ht_params);
|
||||
rcu_read_unlock();
|
||||
if (!known)
|
||||
return -ENOENT;
|
||||
@@ -1065,11 +1066,14 @@ static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, __be32 ip_addr, u64
|
||||
list_for_each_entry(r, &ctrl->routes_list, list) {
|
||||
bool no_port;
|
||||
|
||||
if (r->gw_ip != ip_addr)
|
||||
/* An IPv4 gateway written v4-mapped is a valid IPv6 one, so
|
||||
* the key alone does not say whose route this is.
|
||||
*/
|
||||
if (r->attr.type != type || !ipv6_addr_equal(&r->gw_ip, gw))
|
||||
continue;
|
||||
|
||||
dev_dbg(ctrl->dev, "%s: Setting up fwding: ip %pI4, GW mac %016llx\n",
|
||||
__func__, &ip_addr, mac);
|
||||
dev_dbg(ctrl->dev, "setting up fwding: gw %pI6c, mac %016llx\n",
|
||||
gw, mac);
|
||||
|
||||
dev_dbg(ctrl->dev, "Route with id %d to %pI4 / %d\n",
|
||||
r->id, &r->dst_ip, r->prefix_len);
|
||||
@@ -1096,7 +1100,7 @@ static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, __be32 ip_addr, u64
|
||||
r->nh.port == priv->r->port_ignore;
|
||||
if (no_port && r->attr.action != ROUTE_ACT_TRAP2CPU)
|
||||
dev_info(ctrl->dev, "no port for %pI4, routing %pI4/%d in software\n",
|
||||
&ip_addr, &r->dst_ip, r->prefix_len);
|
||||
&gw->s6_addr32[3], &r->dst_ip, r->prefix_len);
|
||||
|
||||
r->attr.valid = true;
|
||||
r->attr.action = no_port ? ROUTE_ACT_TRAP2CPU : ROUTE_ACT_FORWARD;
|
||||
@@ -1188,9 +1192,12 @@ static int otto_l3_port_ipv4_resolve(struct otto_l3_ctrl *ctrl,
|
||||
* resolve the neigh.
|
||||
*/
|
||||
if (n->nud_state & NUD_VALID) {
|
||||
struct in6_addr gw;
|
||||
|
||||
mac = ether_addr_to_u64(n->ha);
|
||||
dev_info(ctrl->dev, "resolved mac: %016llx\n", mac);
|
||||
otto_l3_nexthop_update(ctrl, ip_addr, mac);
|
||||
ipv6_addr_set_v4mapped(ip_addr, &gw);
|
||||
otto_l3_nexthop_update(ctrl, ROUTE_TYPE_IP4UC, &gw, mac);
|
||||
} else {
|
||||
dev_info(ctrl->dev, "need to wait\n");
|
||||
neigh_event_send(n, NULL);
|
||||
@@ -1347,7 +1354,8 @@ static void otto_l3_route_teardown(struct otto_l3_ctrl *ctrl, struct otto_l3_rou
|
||||
otto_l3_route_remove(ctrl, r);
|
||||
}
|
||||
|
||||
static struct otto_l3_route *otto_l3_host_route_alloc(struct otto_l3_ctrl *ctrl, u32 ip)
|
||||
static struct otto_l3_route *otto_l3_host_route_alloc(struct otto_l3_ctrl *ctrl,
|
||||
const struct in6_addr *gw)
|
||||
{
|
||||
struct otto_l3_route *r;
|
||||
int idx = 0, err;
|
||||
@@ -1361,7 +1369,7 @@ static struct otto_l3_route *otto_l3_host_route_alloc(struct otto_l3_ctrl *ctrl,
|
||||
mutex_unlock(ctrl->lock);
|
||||
return NULL;
|
||||
}
|
||||
dev_dbg(ctrl->dev, "id: %d, ip %pI4\n", idx, &ip);
|
||||
dev_dbg(ctrl->dev, "id: %d, gw %pI6c\n", idx, gw);
|
||||
|
||||
r = kzalloc(sizeof(*r), GFP_KERNEL);
|
||||
if (!r) {
|
||||
@@ -1375,7 +1383,7 @@ static struct otto_l3_route *otto_l3_host_route_alloc(struct otto_l3_ctrl *ctrl,
|
||||
r->id = idx + MAX_ROUTES;
|
||||
r->row = -1; /* placed by find_slot(), not by row */
|
||||
|
||||
r->gw_ip = ip;
|
||||
r->gw_ip = *gw;
|
||||
r->pr.id = -1; /* We still need to allocate a rule in HW */
|
||||
r->pr.packet_cntr = -1;
|
||||
r->is_host_route = true;
|
||||
@@ -1400,7 +1408,8 @@ out_free:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl, u32 ip)
|
||||
static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl,
|
||||
const struct in6_addr *gw)
|
||||
{
|
||||
struct otto_l3_route *r;
|
||||
int idx = 0, err;
|
||||
@@ -1414,7 +1423,7 @@ static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl, u32
|
||||
mutex_unlock(ctrl->lock);
|
||||
return NULL;
|
||||
}
|
||||
dev_dbg(ctrl->dev, "id: %d, ip %pI4\n", idx, &ip);
|
||||
dev_dbg(ctrl->dev, "id: %d, gw %pI6c\n", idx, gw);
|
||||
|
||||
r = kzalloc(sizeof(*r), GFP_KERNEL);
|
||||
if (!r) {
|
||||
@@ -1424,7 +1433,7 @@ static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl, u32
|
||||
|
||||
r->id = idx;
|
||||
r->row = -1; /* placed when the gateway resolves */
|
||||
r->gw_ip = ip;
|
||||
r->gw_ip = *gw;
|
||||
r->pr.id = -1; /* We still need to allocate a rule in HW */
|
||||
r->pr.packet_cntr = -1;
|
||||
r->is_host_route = false;
|
||||
@@ -1477,9 +1486,11 @@ static void otto_l3_host_route_trap(struct otto_l3_ctrl *ctrl,
|
||||
struct fib_entry_notifier_info *info)
|
||||
{
|
||||
struct otto_l3_route *route;
|
||||
struct in6_addr gw;
|
||||
int slot;
|
||||
|
||||
route = otto_l3_host_route_alloc(ctrl, 0);
|
||||
ipv6_addr_set_v4mapped(0, &gw);
|
||||
route = otto_l3_host_route_alloc(ctrl, &gw);
|
||||
if (!route)
|
||||
return;
|
||||
|
||||
@@ -1508,6 +1519,7 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
struct rtl838x_switch_priv *priv = ctrl->priv;
|
||||
struct otto_l3_route *route;
|
||||
struct net_device *ndev;
|
||||
struct in6_addr gw;
|
||||
struct fib_nh *nh;
|
||||
int port, vlan;
|
||||
|
||||
@@ -1552,10 +1564,11 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
}
|
||||
|
||||
/* Allocate route or host-route entry (if hardware supports this) */
|
||||
ipv6_addr_set_v4mapped(nh->fib_nh_gw4, &gw);
|
||||
if (info->dst_len == 32 && ctrl->cfg->host_route_write)
|
||||
route = otto_l3_host_route_alloc(ctrl, nh->fib_nh_gw4);
|
||||
route = otto_l3_host_route_alloc(ctrl, &gw);
|
||||
else
|
||||
route = otto_l3_route_alloc(ctrl, nh->fib_nh_gw4);
|
||||
route = otto_l3_route_alloc(ctrl, &gw);
|
||||
|
||||
if (route)
|
||||
dev_info(ctrl->dev, "route hashtable extended for gw %pI4\n", &nh->fib_nh_gw4);
|
||||
@@ -1626,6 +1639,7 @@ static int otto_l3_fib_del_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
struct rhlist_head *tmp, *list;
|
||||
struct otto_l3_route *route;
|
||||
bool found = false;
|
||||
struct in6_addr gw;
|
||||
struct fib_nh *nh;
|
||||
|
||||
/* A route through a nexthop object holds at most a trap entry */
|
||||
@@ -1642,15 +1656,18 @@ static int otto_l3_fib_del_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
||||
if (otto_l3_fib_check_v4(ctrl, info, FIB_EVENT_ENTRY_DEL))
|
||||
return 0;
|
||||
|
||||
ipv6_addr_set_v4mapped(nh->fib_nh_gw4, &gw);
|
||||
|
||||
rcu_read_lock();
|
||||
list = rhltable_lookup(&ctrl->routes, &nh->fib_nh_gw4, otto_l3_route_ht_params);
|
||||
list = rhltable_lookup(&ctrl->routes, &gw, otto_l3_route_ht_params);
|
||||
if (!list) {
|
||||
rcu_read_unlock();
|
||||
dev_err(ctrl->dev, "no such gateway: %pI4\n", &nh->fib_nh_gw4);
|
||||
return -ENOENT;
|
||||
}
|
||||
rhl_for_each_entry_rcu(route, tmp, list, linkage) {
|
||||
if (route->dst_ip == info->dst && route->prefix_len == info->dst_len) {
|
||||
if (route->attr.type == ROUTE_TYPE_IP4UC &&
|
||||
route->dst_ip == info->dst && route->prefix_len == info->dst_len) {
|
||||
dev_info(ctrl->dev, "found a route with id %d, nh-id %d\n",
|
||||
route->id, route->nh.id);
|
||||
found = true;
|
||||
@@ -1796,7 +1813,8 @@ static void otto_l3_net_event_work_do(struct work_struct *work)
|
||||
struct otto_l3_net_event_work *net_work =
|
||||
container_of(work, struct otto_l3_net_event_work, work);
|
||||
|
||||
otto_l3_nexthop_update(net_work->ctrl, net_work->gw_addr, net_work->mac);
|
||||
otto_l3_nexthop_update(net_work->ctrl, ROUTE_TYPE_IP4UC, &net_work->gw_addr,
|
||||
net_work->mac);
|
||||
|
||||
kfree(net_work);
|
||||
}
|
||||
@@ -1833,7 +1851,7 @@ static int otto_l3_netevent_notifier(struct notifier_block *this, unsigned long
|
||||
net_work->ctrl = ctrl;
|
||||
|
||||
net_work->mac = ether_addr_to_u64(n->ha);
|
||||
net_work->gw_addr = *(__be32 *)n->primary_key;
|
||||
ipv6_addr_set_v4mapped(*(__be32 *)n->primary_key, &net_work->gw_addr);
|
||||
|
||||
dev_dbg(ctrl->dev, "updating neighbour on port %d, mac %016llx\n",
|
||||
port, net_work->mac);
|
||||
|
||||
@@ -71,7 +71,7 @@ struct otto_l3_nexthop {
|
||||
};
|
||||
|
||||
struct otto_l3_route {
|
||||
u32 gw_ip; /* IP of the route's gateway */
|
||||
struct in6_addr gw_ip; /* Gateway of the route, IPv4 v4-mapped */
|
||||
u32 dst_ip; /* IP of the destination net */
|
||||
struct in6_addr dst_ip6;
|
||||
int prefix_len; /* Network prefix len of the destination net */
|
||||
|
||||
Reference in new issue
Block a user