mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-24 19:17:51 +04:00
realtek: l3: rtl839x: route with the egress device's MAC
On RTL839x every routed frame leaves with the switch's own MAC, whatever device the route goes out of, and frames to a device with a MAC of its own are not routed: every ROUTING_SA_CTRL entry the driver writes (0 to 14) holds the switch's MAC, and every ROUTING row names entry 0 (#25768, items 3 and 4). The 16 entries are the source MACs a ROUTING row names (SW_MAC_IDX, Realtek GPL SDK, dal_cypress_l3_routeEntry_set()) and, as measured, also the MACs whose frames the switch routes. Entry 0 stays the switch's MAC (dal_cypress_l3_init()). Any other MAC that a route goes out of a device with takes an entry, per MAC and held by the routes through it, the on-link prefix included, so a device routes on its MAC while it has an address, if an entry was free when it got it. A ROUTING row is now keyed on the gateway's MAC and that entry. Unused entries hold FF:FF:FF:FF:FF:FE, as the SDK leaves them. What the hardware cannot do traps. A route through a gateway that finds no entry free traps, and asks again on the gateway's next neighbour update. Two devices with different MACs on one VLAN reach a gateway through one L2 entry, which can name only one of their rows: the routes already forwarding keep it. A device that takes a new MAC has its entry rewritten when only its routes hold it and no other entry has the new MAC; otherwise its routes move to an entry for the new MAC, those through a gateway trapping until a resolve gives them their new row, and the old entry, still routed, is given up only with the rows that name it. Measured on an HPE 1920-48G (RTL8393), on mainf29dfe25bawith the previous patch and bench debugfs files, before the rebase onto e5ec5e199f; 8 echo requests per case, forwarding off, switch.3 and switch.7 with MACs of their own (M3, M7), gateway G in VLAN 3 and, with the same MAC, in VLAN 7, sending now and then as a live gateway does: - from VLAN 7 to M7, route out of switch.3: 8/8 at G, TTL 63, source M3 (on the #25808 image: 0/8, all to the CPU) - routes via G out of switch.3 and switch.7: two ROUTING rows, 8/8 with M3 and 8/8 with M7 - switch.3 down: its entry freed, the switch.7 route still 8/8 with M7; up again: M3 back, 8/8 - switch.7 to a new MAC: its entry rewritten in place, 8/8 with the new MAC, the switch.3 route unchanged - switch.7 to M3, which switch.3's entry holds: its routes moved to that entry, its old one freed, both routes 8/8 with M3 - with one entry held, 16 more VLAN devices with MACs of their own: the entries ran out after 14, and a gateway route out of a device without one trapped; once the others were gone it took an entry on its next neighbour update (debugfs and register reads, no traffic; G was never on those VLANs, so beforee5ec5e199fits routes there got an invented L2 entry, and on main they trap for want of one: only the switch MAC entries were checked) - rerun from #25805 and #25808: two prefixes in both orders, one gateway for two routes, neighbour loss: same results Other RTL839x boards: routes keep entry 0, as before, while every device they go out of has the MAC eth0 had at probe. A device with another MAC now takes an entry, and frames to it are routed by the switch instead of the CPU. RTL930x sets no set_switch_mac: its routes take no entry, and its netdev notifier path is unchanged. Compile-tested on rtl930x. RTL838x and RTL931x have no setup, so their FIB and neighbour events are ignored and none of this runs there. Not tested: two devices with different MACs on one VLAN, a move to a new entry, a device going back to the switch's MAC, a failed ROUTING row write. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net> Link: https://github.com/openwrt/openwrt/pull/25822 Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
1 parent
b3524cbdfa
commit
404823678f
3 files changed
+275
-22
No files matched your search
@@ -306,9 +306,9 @@ int otto_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop
|
|||||||
nh->rvid = e.rvid;
|
nh->rvid = e.rvid;
|
||||||
nh->dev_id = e.stack_dev;
|
nh->dev_id = e.stack_dev;
|
||||||
/* If the entry is already a valid next hop entry, don't change it, but
|
/* If the entry is already a valid next hop entry, don't change it, but
|
||||||
* for the DMAC entry it names, one per gateway MAC: every route through
|
* for the DMAC entry it names: every route through the entry holds that
|
||||||
* the entry holds that one, and the entry may still name one given up
|
* one, and the entry may still name one given up while it stayed a next
|
||||||
* while it stayed a next hop.
|
* hop, or, on RTL839x, the row of the device's old MAC.
|
||||||
*/
|
*/
|
||||||
if (e.next_hop) {
|
if (e.next_hop) {
|
||||||
if (e.nh_route_id == nh->dmac_id)
|
if (e.nh_route_id == nh->dmac_id)
|
||||||
|
|||||||
@@ -47,7 +47,11 @@
|
|||||||
* holds an L2 entry for its MAC. A trapping rule only takes frames addressed
|
* holds an L2 entry for its MAC. A trapping rule only takes frames addressed
|
||||||
* to one of the switch's MACs, so a frame for another station goes its way. A
|
* to one of the switch's MACs, so a frame for another station goes its way. A
|
||||||
* destination no rule matches needs no catch-all: its frames are addressed to
|
* destination no rule matches needs no catch-all: its frames are addressed to
|
||||||
* the switch, which traps them.
|
* the switch, which traps them. The switch's MACs (ROUTING_SA_CTRL) are both
|
||||||
|
* the source MACs a routed frame leaves with and the MACs whose frames are
|
||||||
|
* routed: entry 0 is the switch's own, and a device with a MAC of its own
|
||||||
|
* takes another for as long as a route out of it holds one, its on-link
|
||||||
|
* prefix included.
|
||||||
*
|
*
|
||||||
* State per family:
|
* State per family:
|
||||||
* - RTL930x: IPv4 and IPv6 unicast routes through a gateway are forwarded.
|
* - RTL930x: IPv4 and IPv6 unicast routes through a gateway are forwarded.
|
||||||
@@ -63,7 +67,9 @@
|
|||||||
* interfaces share one 1536 byte MTU. The programmed state is shown in
|
* interfaces share one 1536 byte MTU. The programmed state is shown in
|
||||||
* debugfs, under realtek_otto_l3.
|
* debugfs, under realtek_otto_l3.
|
||||||
* - RTL839x: IPv4 unicast routes through a gateway are forwarded, through the
|
* - RTL839x: IPv4 unicast routes through a gateway are forwarded, through the
|
||||||
* first 512 PIE rules. A route counts its packets from when its gateway
|
* first 512 PIE rules, with the MAC of the device they go out of. Up to 15
|
||||||
|
* MACs besides the switch's own are taken; a route out of a device whose
|
||||||
|
* MAC finds no entry traps. A route counts its packets from when its gateway
|
||||||
* resolves, while a counter is free. Trapped to the CPU: the same IPv4
|
* resolves, while a counter is free. Trapped to the CPU: the same IPv4
|
||||||
* routes as on RTL930x. No IPv6, and an event the driver could not take is
|
* routes as on RTL930x. No IPv6, and an event the driver could not take is
|
||||||
* not replayed.
|
* not replayed.
|
||||||
@@ -186,9 +192,9 @@ static void otto_l3_839x_nexthop_read(struct otto_l3_ctrl *ctrl, int idx, struct
|
|||||||
rt->nh.gw = v >> 16;
|
rt->nh.gw = v >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A ROUTING row holds a gateway's MAC and the index of the source MAC to
|
/* A ROUTING row holds a gateway's MAC and the switch MAC entry to route with
|
||||||
* route with (SW_MAC_IDX). The routes through one gateway share its row, the
|
* (SW_MAC_IDX). The routes through one gateway out of one device share a row,
|
||||||
* way RTL930x routes share a DMAC entry: the L2 entry of the gateway names
|
* the way RTL930x routes share a DMAC entry: the L2 entry of the gateway names
|
||||||
* one row, whichever route claimed it.
|
* one row, whichever route claimed it.
|
||||||
*/
|
*/
|
||||||
static int otto_l3_839x_set_dmac(struct otto_l3_ctrl *ctrl, u32 idx,
|
static int otto_l3_839x_set_dmac(struct otto_l3_ctrl *ctrl, u32 idx,
|
||||||
@@ -199,6 +205,12 @@ static int otto_l3_839x_set_dmac(struct otto_l3_ctrl *ctrl, u32 idx,
|
|||||||
return otto_table_write(RTL8390_TBL_ROUTING, idx, &data);
|
return otto_table_write(RTL8390_TBL_ROUTING, idx, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void otto_l3_839x_set_switch_mac(struct otto_l3_ctrl *ctrl, int idx, u64 mac)
|
||||||
|
{
|
||||||
|
sw_w32(mac >> 32, RTL839X_ROUTING_SA_CTRL + idx * 8);
|
||||||
|
sw_w32(mac, RTL839X_ROUTING_SA_CTRL + idx * 8 + 4);
|
||||||
|
}
|
||||||
|
|
||||||
/* The prefix rows of an RTL839x are the PIE rules L3 keeps for itself. A rule
|
/* The prefix rows of an RTL839x are the PIE rules L3 keeps for itself. A rule
|
||||||
* matches the destination prefix. One that forwards points at the gateway's
|
* matches the destination prefix. One that forwards points at the gateway's
|
||||||
* L2 entry, which names the ROUTING row holding the gateway's MAC. One that
|
* L2 entry, which names the ROUTING row holding the gateway's MAC. One that
|
||||||
@@ -280,10 +292,13 @@ static void otto_l3_839x_setup_port_macs(struct otto_l3_ctrl *ctrl)
|
|||||||
dev = priv->ports[priv->r->cpu_port].dp->user;
|
dev = priv->ports[priv->r->cpu_port].dp->user;
|
||||||
mac = ether_addr_to_u64(dev->dev_addr);
|
mac = ether_addr_to_u64(dev->dev_addr);
|
||||||
|
|
||||||
for (int i = 0; i < 15; i++) {
|
/* Entry 0 is the switch's own MAC, the others wait for a device with a
|
||||||
sw_w32(mac >> 32, RTL839X_ROUTING_SA_CTRL + i * 8);
|
* MAC of its own (otto_l3_switch_mac_get())
|
||||||
sw_w32(mac, RTL839X_ROUTING_SA_CTRL + i * 8 + 4);
|
*/
|
||||||
}
|
ctrl->switch_macs[0] = mac;
|
||||||
|
otto_l3_839x_set_switch_mac(ctrl, 0, mac);
|
||||||
|
for (int i = 1; i < MAX_SWITCH_MACS; i++)
|
||||||
|
otto_l3_839x_set_switch_mac(ctrl, i, SWITCH_MAC_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int otto_l3_839x_setup(struct otto_l3_ctrl *ctrl)
|
static int otto_l3_839x_setup(struct otto_l3_ctrl *ctrl)
|
||||||
@@ -1687,12 +1702,12 @@ static bool otto_l3_dmac_equal(const struct otto_l3_dmac *a, const struct otto_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Routes through one gateway share its L2 entry, and that names a single
|
/* Routes through one gateway share its L2 entry, and that names a single
|
||||||
* DMAC entry, whichever route claimed it first. So the entry is held per MAC
|
* DMAC entry, whichever route claimed it first. So the entry is held per MAC,
|
||||||
* by every route through it, the way the SDK holds it
|
* and on RTL839x per switch MAC entry too, by every route through it, the way
|
||||||
* (_dal_longan_l3_dmacEntry_alloc()), not per route: a route that goes leaves
|
* the SDK holds it (_dal_longan_l3_dmacEntry_alloc()), not per route: a route
|
||||||
* it to the others. A route holds one at most, and there are no more routes
|
* that goes leaves it to the others. A route holds one at most, and there are
|
||||||
* than entries. Entry 0 stays unused, as it did while route 0, which is never
|
* no more routes than entries. Entry 0 stays unused, as it did while route 0,
|
||||||
* allocated, named it.
|
* which is never allocated, named it.
|
||||||
*/
|
*/
|
||||||
static int otto_l3_dmac_get(struct otto_l3_ctrl *ctrl, const struct otto_l3_dmac *dmac)
|
static int otto_l3_dmac_get(struct otto_l3_ctrl *ctrl, const struct otto_l3_dmac *dmac)
|
||||||
{
|
{
|
||||||
@@ -1740,6 +1755,89 @@ static void otto_l3_dmac_put(struct otto_l3_ctrl *ctrl, int idx)
|
|||||||
mutex_unlock(ctrl->lock);
|
mutex_unlock(ctrl->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On RTL839x the source MACs a routed frame leaves with are also the MACs
|
||||||
|
* whose frames are routed. Entry 0 is the switch's own; each other MAC a route
|
||||||
|
* goes out of a device with, its on-link prefix and its addresses included,
|
||||||
|
* takes an entry while a route holds it, so a device with a MAC of its own
|
||||||
|
* routes what arrives on it while it has an address. Devices with one MAC
|
||||||
|
* share its entry. An entry no route holds any more goes back to the unused
|
||||||
|
* value, after the ROUTING rows that named it, which the routes held too.
|
||||||
|
*/
|
||||||
|
static int otto_l3_switch_mac_get(struct otto_l3_ctrl *ctrl, u64 mac)
|
||||||
|
{
|
||||||
|
int i, unused = -1;
|
||||||
|
|
||||||
|
if (mac == ctrl->switch_macs[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(ctrl->lock);
|
||||||
|
|
||||||
|
for (i = 1; i < MAX_SWITCH_MACS; i++) {
|
||||||
|
if (ctrl->switch_mac_refs[i] && ctrl->switch_macs[i] == mac)
|
||||||
|
break;
|
||||||
|
if (unused < 0 && !ctrl->switch_mac_refs[i])
|
||||||
|
unused = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == MAX_SWITCH_MACS) {
|
||||||
|
i = unused;
|
||||||
|
if (i < 0) {
|
||||||
|
mutex_unlock(ctrl->lock);
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl->switch_macs[i] = mac;
|
||||||
|
ctrl->cfg->set_switch_mac(ctrl, i, mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl->switch_mac_refs[i]++;
|
||||||
|
mutex_unlock(ctrl->lock);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void otto_l3_switch_mac_put(struct otto_l3_ctrl *ctrl, int idx)
|
||||||
|
{
|
||||||
|
if (idx <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mutex_lock(ctrl->lock);
|
||||||
|
if (!--ctrl->switch_mac_refs[idx])
|
||||||
|
ctrl->cfg->set_switch_mac(ctrl, idx, SWITCH_MAC_FREE);
|
||||||
|
mutex_unlock(ctrl->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A route the switch cannot route keeps to the CPU, its next hop as it was */
|
||||||
|
static void otto_l3_route_trap_sw(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r)
|
||||||
|
{
|
||||||
|
if (r->attr.action == ROUTE_ACT_TRAP2CPU)
|
||||||
|
return;
|
||||||
|
|
||||||
|
r->attr.action = ROUTE_ACT_TRAP2CPU;
|
||||||
|
r->attr.ttl_dec = false;
|
||||||
|
r->attr.ttl_check = false;
|
||||||
|
otto_l3_route_rewrite(ctrl, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A gateway's L2 entry names one ROUTING row, which on RTL839x also names the
|
||||||
|
* switch MAC entry. Two devices with different MACs on one VLAN reach a
|
||||||
|
* gateway through the same L2 entry, but each with a row of its own: the
|
||||||
|
* routes forwarding through the entry keep it.
|
||||||
|
*/
|
||||||
|
static bool otto_l3_l2_entry_taken(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r,
|
||||||
|
const struct otto_l3_dmac *key)
|
||||||
|
{
|
||||||
|
struct otto_l3_route *q;
|
||||||
|
|
||||||
|
list_for_each_entry(q, &ctrl->routes_list, list)
|
||||||
|
if (q != r && q->attr.action == ROUTE_ACT_FORWARD && q->nh.mac == key->mac &&
|
||||||
|
q->nh.rvid == r->nh.rvid && q->nh.dmac_id >= 0 &&
|
||||||
|
!otto_l3_dmac_equal(&ctrl->dmacs[q->nh.dmac_id], key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r,
|
static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_route *r,
|
||||||
u64 mac)
|
u64 mac)
|
||||||
{
|
{
|
||||||
@@ -1750,6 +1848,8 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
|
|||||||
struct otto_l3_dmac key = { .mac = mac };
|
struct otto_l3_dmac key = { .mac = mac };
|
||||||
bool first = !r->nh.mac;
|
bool first = !r->nh.mac;
|
||||||
bool no_port, no_l2, trap;
|
bool no_port, no_l2, trap;
|
||||||
|
int old_sa = -1, sa;
|
||||||
|
bool new_sa = false;
|
||||||
int dmac = r->id;
|
int dmac = r->id;
|
||||||
|
|
||||||
if (otto_l3_row_frozen(ctrl, r))
|
if (otto_l3_row_frozen(ctrl, r))
|
||||||
@@ -1772,14 +1872,47 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
|
|||||||
otto_l3_route_rewrite(ctrl, r);
|
otto_l3_route_rewrite(ctrl, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* An RTL839x route routes with the switch MAC entry of its device's
|
||||||
|
* MAC. One without, or whose device took another MAC, takes one here,
|
||||||
|
* and gives the old one up only with the ROUTING row that names it.
|
||||||
|
* With none free it keeps to the CPU, its next hop untouched.
|
||||||
|
*/
|
||||||
|
if (ctrl->cfg->set_switch_mac &&
|
||||||
|
(r->switch_mac_id < 0 || ctrl->switch_macs[r->switch_mac_id] != r->switch_mac)) {
|
||||||
|
sa = otto_l3_switch_mac_get(ctrl, r->switch_mac);
|
||||||
|
if (sa < 0) {
|
||||||
|
otto_l3_route_trap_sw(ctrl, r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
old_sa = r->switch_mac_id;
|
||||||
|
r->switch_mac_id = sa;
|
||||||
|
new_sa = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An RTL839x ROUTING row also names the switch MAC entry */
|
||||||
|
if (r->switch_mac_id > 0)
|
||||||
|
key.smac_id = r->switch_mac_id;
|
||||||
|
|
||||||
if (ctrl->cfg->set_dmac) {
|
if (ctrl->cfg->set_dmac) {
|
||||||
dmac = otto_l3_dmac_get(ctrl, &key);
|
dmac = otto_l3_dmac_get(ctrl, &key);
|
||||||
if (dmac < 0) {
|
if (dmac < 0) {
|
||||||
dev_err(ctrl->dev, "no DMAC entry for %016llx: %d\n", mac, dmac);
|
dev_err(ctrl->dev, "no DMAC entry for %016llx: %d\n", mac, dmac);
|
||||||
return;
|
goto undo_sa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctrl->cfg->set_switch_mac && otto_l3_l2_entry_taken(ctrl, r, &key)) {
|
||||||
|
if (!r->l2_taken)
|
||||||
|
dev_info(ctrl->dev, "gateway %pI4 also reached from another MAC on VLAN %d, routing %s in software\n",
|
||||||
|
&r->gw_ip.s6_addr32[3], r->nh.rvid,
|
||||||
|
otto_l3_route_dst(r, dst, sizeof(dst)));
|
||||||
|
r->l2_taken = true;
|
||||||
|
otto_l3_dmac_put(ctrl, dmac);
|
||||||
|
otto_l3_route_trap_sw(ctrl, r);
|
||||||
|
goto undo_sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->l2_taken = false;
|
||||||
r->nh.mac = r->nh.gw = mac;
|
r->nh.mac = r->nh.gw = mac;
|
||||||
r->nh.port = priv->r->port_ignore;
|
r->nh.port = priv->r->port_ignore;
|
||||||
r->nh.id = r->id;
|
r->nh.id = r->id;
|
||||||
@@ -1790,8 +1923,11 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
|
|||||||
if (!no_l2)
|
if (!no_l2)
|
||||||
r->nh.l2_installed = true;
|
r->nh.l2_installed = true;
|
||||||
|
|
||||||
/* The route names its gateway's entry now, through its L2 entry */
|
/* The route names its gateway's entry now, through its L2 entry, and
|
||||||
|
* the old row gave up the switch MAC entry it named
|
||||||
|
*/
|
||||||
otto_l3_dmac_put(ctrl, old_dmac);
|
otto_l3_dmac_put(ctrl, old_dmac);
|
||||||
|
otto_l3_switch_mac_put(ctrl, old_sa);
|
||||||
|
|
||||||
/* A next hop with no port delivers the frame twice, so where the
|
/* A next hop with no port delivers the frame twice, so where the
|
||||||
* route entry can trap, let the CPU route it alone until an update
|
* route entry can trap, let the CPU route it alone until an update
|
||||||
@@ -1837,6 +1973,13 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
otto_l3_route_install(ctrl, r);
|
otto_l3_route_install(ctrl, r);
|
||||||
|
return;
|
||||||
|
|
||||||
|
undo_sa:
|
||||||
|
if (new_sa) {
|
||||||
|
otto_l3_switch_mac_put(ctrl, r->switch_mac_id);
|
||||||
|
r->switch_mac_id = old_sa;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The hardware would go on forwarding to the address the gateway last
|
/* The hardware would go on forwarding to the address the gateway last
|
||||||
@@ -2068,6 +2211,81 @@ struct otto_l3_egress_work {
|
|||||||
int ifindex;
|
int ifindex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Whether an entry other than @skip holds @mac */
|
||||||
|
static bool otto_l3_switch_mac_held(struct otto_l3_ctrl *ctrl, u64 mac, int skip)
|
||||||
|
{
|
||||||
|
if (mac == ctrl->switch_macs[0])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (int i = 1; i < MAX_SWITCH_MACS; i++)
|
||||||
|
if (i != skip && ctrl->switch_mac_refs[i] && ctrl->switch_macs[i] == mac)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An RTL839x device that took a new MAC routes with it. A switch MAC entry
|
||||||
|
* that only routes out of the device hold takes the new MAC where it is, and
|
||||||
|
* the ROUTING rows naming it follow. Routes on any other entry move: one
|
||||||
|
* without a gateway names no row and moves now; one through a gateway traps
|
||||||
|
* and moves in otto_l3_route_update_hw(), with its row, once the resolve
|
||||||
|
* asked for here brings its gateway back.
|
||||||
|
*/
|
||||||
|
static void otto_l3_switch_mac_sync(struct otto_l3_ctrl *ctrl, struct net_device *dev, u64 mac)
|
||||||
|
{
|
||||||
|
struct otto_l3_route *r;
|
||||||
|
bool none = false;
|
||||||
|
int old;
|
||||||
|
|
||||||
|
/* An entry keeps to one MAC, so it is only rewritten where no other
|
||||||
|
* entry has the new one
|
||||||
|
*/
|
||||||
|
for (int k = 1; k < MAX_SWITCH_MACS; k++) {
|
||||||
|
unsigned int n = 0;
|
||||||
|
|
||||||
|
list_for_each_entry(r, &ctrl->routes_list, list)
|
||||||
|
if (r->gw_ifindex == dev->ifindex && r->switch_mac_id == k)
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if (n && n == ctrl->switch_mac_refs[k] && !otto_l3_switch_mac_held(ctrl, mac, k)) {
|
||||||
|
mutex_lock(ctrl->lock);
|
||||||
|
ctrl->switch_macs[k] = mac;
|
||||||
|
ctrl->cfg->set_switch_mac(ctrl, k, mac);
|
||||||
|
mutex_unlock(ctrl->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(r, &ctrl->routes_list, list) {
|
||||||
|
if (r->gw_ifindex != dev->ifindex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r->switch_mac = mac;
|
||||||
|
old = r->switch_mac_id;
|
||||||
|
if (old >= 0 && ctrl->switch_macs[old] == mac)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (r->gw_ip.s6_addr32[3]) {
|
||||||
|
otto_l3_route_trap_sw(ctrl, r);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
otto_l3_switch_mac_put(ctrl, old);
|
||||||
|
r->switch_mac_id = otto_l3_switch_mac_get(ctrl, mac);
|
||||||
|
none |= r->switch_mac_id < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (none)
|
||||||
|
dev_err(ctrl->dev, "no switch MAC entry free for %pM, routes out of %s stay in software\n",
|
||||||
|
dev->dev_addr, dev->name);
|
||||||
|
|
||||||
|
/* One resolve brings back every route through a gateway */
|
||||||
|
list_for_each_entry(r, &ctrl->routes_list, list)
|
||||||
|
if (r->gw_ifindex == dev->ifindex && r->attr.type == ROUTE_TYPE_IP4UC &&
|
||||||
|
r->gw_ip.s6_addr32[3] && r->attr.action == ROUTE_ACT_TRAP2CPU &&
|
||||||
|
!otto_l3_gw_seen(ctrl, r, NULL))
|
||||||
|
otto_l3_port_gw_resolve(ctrl, dev, &arp_tbl, &r->gw_ip);
|
||||||
|
}
|
||||||
|
|
||||||
/* Whether every hold on an egress interface is a route out of this device */
|
/* Whether every hold on an egress interface is a route out of this device */
|
||||||
static bool otto_l3_intf_only_dev(struct otto_l3_ctrl *ctrl, int if_id, int ifindex)
|
static bool otto_l3_intf_only_dev(struct otto_l3_ctrl *ctrl, int if_id, int ifindex)
|
||||||
{
|
{
|
||||||
@@ -2105,6 +2323,11 @@ static void otto_l3_egress_sync(struct work_struct *work)
|
|||||||
|
|
||||||
mac = ether_addr_to_u64(dev->dev_addr);
|
mac = ether_addr_to_u64(dev->dev_addr);
|
||||||
|
|
||||||
|
if (ctrl->cfg->set_switch_mac) {
|
||||||
|
otto_l3_switch_mac_sync(ctrl, dev, mac);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
list_for_each_entry(r, &ctrl->routes_list, list) {
|
list_for_each_entry(r, &ctrl->routes_list, list) {
|
||||||
if (r->gw_ifindex != dev->ifindex || r->nh.if_id < 0)
|
if (r->gw_ifindex != dev->ifindex || r->nh.if_id < 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -2166,6 +2389,13 @@ static int otto_l3_netdev_notifier(struct notifier_block *nb, unsigned long even
|
|||||||
if (!net_eq(dev_net(netdev_notifier_info_to_dev(ptr)), &init_net))
|
if (!net_eq(dev_net(netdev_notifier_info_to_dev(ptr)), &init_net))
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
/* RTL839x keeps no router MAC table: the routes hold its switch MACs */
|
||||||
|
if (!ctrl->cfg->set_router_mac) {
|
||||||
|
if (event == NETDEV_CHANGEADDR)
|
||||||
|
otto_l3_egress_sync_request(ctrl, netdev_notifier_info_to_dev(ptr));
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_CHANGEUPPER:
|
case NETDEV_CHANGEUPPER:
|
||||||
/* A port joins or leaves its master. One joining a bridge stops
|
/* A port joins or leaves its master. One joining a bridge stops
|
||||||
@@ -2297,6 +2527,7 @@ static void otto_l3_route_teardown(struct otto_l3_ctrl *ctrl, struct otto_l3_rou
|
|||||||
if (r->is_host_route || !ctrl->prefix_rows_stale) {
|
if (r->is_host_route || !ctrl->prefix_rows_stale) {
|
||||||
otto_l3_route_put_intf(ctrl, r);
|
otto_l3_route_put_intf(ctrl, r);
|
||||||
otto_l3_dmac_put(ctrl, r->nh.dmac_id);
|
otto_l3_dmac_put(ctrl, r->nh.dmac_id);
|
||||||
|
otto_l3_switch_mac_put(ctrl, r->switch_mac_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
otto_l3_route_free(ctrl, r);
|
otto_l3_route_free(ctrl, r);
|
||||||
@@ -2337,6 +2568,7 @@ static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl,
|
|||||||
r->pr.packet_cntr = -1;
|
r->pr.packet_cntr = -1;
|
||||||
r->nh.if_id = -1;
|
r->nh.if_id = -1;
|
||||||
r->nh.dmac_id = -1;
|
r->nh.dmac_id = -1;
|
||||||
|
r->switch_mac_id = -1;
|
||||||
r->is_host_route = host;
|
r->is_host_route = host;
|
||||||
INIT_LIST_HEAD(&r->srcs);
|
INIT_LIST_HEAD(&r->srcs);
|
||||||
|
|
||||||
@@ -2534,6 +2766,14 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
|||||||
route->nh.rvid = vlan;
|
route->nh.rvid = vlan;
|
||||||
route->gw_ifindex = ndev->ifindex;
|
route->gw_ifindex = ndev->ifindex;
|
||||||
|
|
||||||
|
if (ctrl->cfg->set_switch_mac) {
|
||||||
|
route->switch_mac = ether_addr_to_u64(ndev->dev_addr);
|
||||||
|
route->switch_mac_id = otto_l3_switch_mac_get(ctrl, route->switch_mac);
|
||||||
|
if (route->switch_mac_id < 0)
|
||||||
|
dev_err(ctrl->dev, "no switch MAC entry free for %pM, %pI4/%d stays in software\n",
|
||||||
|
ndev->dev_addr, &info->dst, info->dst_len);
|
||||||
|
}
|
||||||
|
|
||||||
if (ctrl->cfg->set_router_mac) {
|
if (ctrl->cfg->set_router_mac) {
|
||||||
u64 mac = ether_addr_to_u64(ndev->dev_addr);
|
u64 mac = ether_addr_to_u64(ndev->dev_addr);
|
||||||
|
|
||||||
@@ -2578,6 +2818,7 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
|
|||||||
out_free_rmac:
|
out_free_rmac:
|
||||||
out_free_rt:
|
out_free_rt:
|
||||||
otto_l3_route_put_intf(ctrl, route);
|
otto_l3_route_put_intf(ctrl, route);
|
||||||
|
otto_l3_switch_mac_put(ctrl, route->switch_mac_id);
|
||||||
otto_l3_route_free(ctrl, route);
|
otto_l3_route_free(ctrl, route);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -4169,6 +4410,7 @@ const struct otto_l3_config otto_l3_838x_cfg = {
|
|||||||
|
|
||||||
const struct otto_l3_config otto_l3_839x_cfg = {
|
const struct otto_l3_config otto_l3_839x_cfg = {
|
||||||
.set_dmac = otto_l3_839x_set_dmac,
|
.set_dmac = otto_l3_839x_set_dmac,
|
||||||
|
.set_switch_mac = otto_l3_839x_set_switch_mac,
|
||||||
.route_rows_move = otto_l3_839x_route_rows_move,
|
.route_rows_move = otto_l3_839x_route_rows_move,
|
||||||
.route_write = otto_l3_839x_route_write,
|
.route_write = otto_l3_839x_route_write,
|
||||||
.setup = otto_l3_839x_setup,
|
.setup = otto_l3_839x_setup,
|
||||||
@@ -4382,7 +4624,7 @@ int otto_l3_probe(struct device *dev, struct rtl838x_switch_priv *priv)
|
|||||||
return dev_err_probe(dev, err, "Failed to register netevent notifier\n");
|
return dev_err_probe(dev, err, "Failed to register netevent notifier\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctrl->cfg->set_router_mac) {
|
if (ctrl->cfg->set_router_mac || ctrl->cfg->set_switch_mac) {
|
||||||
ctrl->nd_nb.notifier_call = otto_l3_netdev_notifier;
|
ctrl->nd_nb.notifier_call = otto_l3_netdev_notifier;
|
||||||
err = register_netdevice_notifier(&ctrl->nd_nb);
|
err = register_netdevice_notifier(&ctrl->nd_nb);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ struct fib6_info;
|
|||||||
#define MAX_SMACS 64
|
#define MAX_SMACS 64
|
||||||
#define MAX_DMACS 2048
|
#define MAX_DMACS 2048
|
||||||
#define MAX_ROUTER_MACS 64
|
#define MAX_ROUTER_MACS 64
|
||||||
|
#define MAX_SWITCH_MACS 16
|
||||||
|
/* What an unused switch MAC entry holds (Realtek GPL SDK, dal_cypress_l3_init()) */
|
||||||
|
#define SWITCH_MAC_FREE 0xfffffffffffeULL
|
||||||
#define MAX_HOST_ROUTES 1536
|
#define MAX_HOST_ROUTES 1536
|
||||||
#define MAX_ROUTES 512
|
#define MAX_ROUTES 512
|
||||||
|
|
||||||
@@ -97,7 +100,9 @@ struct otto_l3_route {
|
|||||||
bool srcs_incomplete; /* a source could not be tracked */
|
bool srcs_incomplete; /* a source could not be tracked */
|
||||||
struct list_head list; /* all routes, for every lookup */
|
struct list_head list; /* all routes, for every lookup */
|
||||||
u32 tb_id; /* routing table the route came from */
|
u32 tb_id; /* routing table the route came from */
|
||||||
u16 switch_mac_id; /* Index into switch's own MACs, RTL839X only */
|
int switch_mac_id; /* Switch MAC entry it routes with, RTL839x */
|
||||||
|
u64 switch_mac; /* MAC of its device, which that entry holds */
|
||||||
|
bool l2_taken; /* its gateway's L2 entry names another row */
|
||||||
struct otto_l3_nexthop nh;
|
struct otto_l3_nexthop nh;
|
||||||
struct pie_rule pr;
|
struct pie_rule pr;
|
||||||
struct otto_l3_route_attr attr;
|
struct otto_l3_route_attr attr;
|
||||||
@@ -131,6 +136,10 @@ struct otto_l3_config {
|
|||||||
void (*set_nexthop)(struct otto_l3_ctrl *ctrl, int idx, u16 dmac_id, u16 interface);
|
void (*set_nexthop)(struct otto_l3_ctrl *ctrl, int idx, u16 dmac_id, u16 interface);
|
||||||
int (*route_lookup_hw)(struct otto_l3_ctrl *ctrl, struct otto_l3_route *rt);
|
int (*route_lookup_hw)(struct otto_l3_ctrl *ctrl, struct otto_l3_route *rt);
|
||||||
int (*route_rows_move)(struct otto_l3_ctrl *ctrl, int dst, int src, int count);
|
int (*route_rows_move)(struct otto_l3_ctrl *ctrl, int dst, int src, int count);
|
||||||
|
/* RTL839x: one entry of the switch's own MACs, the source MACs it
|
||||||
|
* routes with and the MACs it routes what arrives on
|
||||||
|
*/
|
||||||
|
void (*set_switch_mac)(struct otto_l3_ctrl *ctrl, int idx, u64 mac);
|
||||||
void (*route_read)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt);
|
void (*route_read)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt);
|
||||||
void (*route_write)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt);
|
void (*route_write)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt);
|
||||||
int (*setup)(struct otto_l3_ctrl *ctrl);
|
int (*setup)(struct otto_l3_ctrl *ctrl);
|
||||||
@@ -155,6 +164,8 @@ struct otto_l3_ctrl {
|
|||||||
unsigned int intf_refs[MAX_SMACS]; /* routes holding each */
|
unsigned int intf_refs[MAX_SMACS]; /* routes holding each */
|
||||||
struct otto_l3_dmac *dmacs; /* what each DMAC entry holds */
|
struct otto_l3_dmac *dmacs; /* what each DMAC entry holds */
|
||||||
unsigned int *dmac_refs; /* routes holding each */
|
unsigned int *dmac_refs; /* routes holding each */
|
||||||
|
u64 switch_macs[MAX_SWITCH_MACS]; /* MAC of each switch MAC entry */
|
||||||
|
unsigned int switch_mac_refs[MAX_SWITCH_MACS]; /* routes holding each */
|
||||||
DECLARE_BITMAP(router_mac_bm, MAX_ROUTER_MACS); /* entries written here */
|
DECLARE_BITMAP(router_mac_bm, MAX_ROUTER_MACS); /* entries written here */
|
||||||
bool prefix_rows_stale; /* a move failed, the rows are not where we say */
|
bool prefix_rows_stale; /* a move failed, the rows are not where we say */
|
||||||
bool v4_fwd_off; /* policy rules keep IPv4 forwarding in software */
|
bool v4_fwd_off; /* policy rules keep IPv4 forwarding in software */
|
||||||
|
|||||||
Reference in new issue
Block a user