realtek: dsa: drop untagged frames of ports without PVID

Every port is a member of VID 0, so that the frames the CPU sends with
its PVID 0 reach the ports with untagged VLANs. A port without PVID
keeps PVID 0 too, so in a VLAN-filtering bridge its untagged and
priority tagged frames land in VID 0 and are flooded to the other ports
of the bridge, tagged VID 0, where the bridge would drop them.

The acceptable frame type of a port tells the two apart (one bit each
for inner and outer, tagged and untagged frames; vendor SDK). While a
port without PVID is in a VLAN-filtering bridge, it accepts only tagged
frames, outer tagged ones as an 802.1ad port; standalone ports, which
can have VLAN filtering on, and every other state accept all. The
driver tracks the bridge membership itself, since DSA rolls back a
failed join through port_bridge_leave() before it clears dp->bridge.
VID 0 and the CPU path stay as they are. The register has this layout
on all four families; only RTL839x sets its address here, the others
keep their behaviour until tested.

HPE 1920-48G (RTL8393), lan48 a UCI trunk with VLAN 3 and 7 tagged,
lan47 an access port in VLAN 1: untagged and priority tagged ARP from
the host behind lan48 left lan47 tagged VID 0, 3 of 3 each; now none
does. STP, LLDP, LACP and EAPOL frames on lan48 still reach the CPU,
5 of 5 each, and VLAN 3 tagged frames pass. With a PVID on lan48 again,
and in a bridge without VLAN filtering, the ARP leaves lan47 untagged;
as standalone ports both accept all frames again.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25992
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Gennaro Cimmino authored and Markus Stockhausen committed 2026-10-19 18:29:19 +02:00
1 parent 14423144a5
commit e4c5f850ee
5 files changed
+41

No files matched your search

@@ -840,6 +840,9 @@ static int rtldsa_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_b
priv->ports[port].qinq = true;
}
priv->ports[port].bridged = true;
otto_vlan_port_untagged_set(priv, port);
rtldsa_update_port_member(priv, port, bridge.dev, true);
if (priv->r->set_static_move_action)
@@ -868,6 +871,9 @@ static void rtldsa_port_bridge_leave(struct dsa_switch *ds, int port, struct dsa
priv->ports[port].qinq = false;
}
priv->ports[port].bridged = false;
otto_vlan_port_untagged_set(priv, port);
if (priv->r->set_static_move_action)
priv->r->set_static_move_action(port, true);
@@ -84,6 +84,8 @@ struct rtldsa_port {
bool phy:1;
bool isolated:1;
bool qinq:1;
bool bridged:1;
bool vlan_filtering:1;
unsigned long rate_police_egress;
unsigned long rate_police_ingress;
unsigned long cached_flags;
@@ -464,6 +466,7 @@ struct rtldsa_config {
u32 (*l2_hash_key)(struct otto_dsa_priv *priv, u64 seed);
u64 (*read_mcast_pmask)(int idx);
void (*write_mcast_pmask)(int idx, u64 portmask);
u32 vlan_port_aft;
void (*vlan_fwd_on_inner)(struct otto_dsa_priv *priv, int port, bool is_set);
void (*vlan_qinq_setup)(struct otto_dsa_priv *priv);
void (*vlan_port_qinq_set)(struct otto_dsa_priv *priv, int port, bool enable);
@@ -51,6 +51,7 @@
#define RTL839X_SPCL_TRAP_EAPOL_CTRL (0x105C)
#define RTL839X_SPCL_TRAP_SWITCH_MAC_CTRL (0x1068)
#define RTL839X_VLAN_PORT_AFT (0x2600)
#define RTL839X_IMR_GLB (0x0064)
#define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068)
@@ -239,6 +240,7 @@ const struct rtldsa_config rtldsa_839x_cfg = {
.vlan_profile_get = otto_vlan_839x_profile_get,
.vlan_profile_dump = otto_vlan_839x_profile_dump,
.vlan_profile_setup = otto_vlan_839x_profile_setup,
.vlan_port_aft = RTL839X_VLAN_PORT_AFT,
.vlan_fwd_on_inner = otto_vlan_839x_port_forward_on_inner,
.vlan_port_keep_tag_set = otto_vlan_839x_port_keep_tag_set,
.vlan_port_pvidmode_set = otto_vlan_839x_port_pvid_mode_set,
@@ -133,6 +133,31 @@
#define RTL931X_VLAN_PORT_TAG_ITPID_IDX_MASK GENMASK(2, 1)
#define RTL931X_VLAN_PORT_TAG_ITPID_KEEP_MASK GENMASK(0, 0)
#define RTLDSA_VLAN_PORT_AFT_ITAG_UNTAG BIT(1)
#define RTLDSA_VLAN_PORT_AFT_OTAG_UNTAG BIT(3)
#define RTLDSA_VLAN_PORT_AFT_ALL GENMASK(3, 0)
/*
* Every port is a member of VID 0 for the frames the CPU sends, see
* otto_vlan_setup(). A port without PVID in a VLAN-filtering bridge must not
* flood its untagged frames there, so it accepts only tagged ones, outer tagged
* ones as an 802.1ad port. Standalone ports can have VLAN filtering on.
*/
void otto_vlan_port_untagged_set(struct otto_dsa_priv *priv, int port)
{
struct rtldsa_port *p = &priv->ports[port];
u32 aft = RTLDSA_VLAN_PORT_AFT_ALL;
if (!priv->r->vlan_port_aft || port == priv->r->cpu_port)
return;
if (p->bridged && p->vlan_filtering && !p->pvid)
aft &= ~(p->qinq ? RTLDSA_VLAN_PORT_AFT_OTAG_UNTAG :
RTLDSA_VLAN_PORT_AFT_ITAG_UNTAG);
regmap_write(priv->map, priv->r->vlan_port_aft + (port << 2), aft);
}
static void otto_vlan_port_pvid_set(struct otto_dsa_priv *priv,
int port, int pvid)
{
@@ -145,6 +170,7 @@ static void otto_vlan_port_pvid_set(struct otto_dsa_priv *priv,
PBVLAN_MODE_UNTAG_AND_PRITAG);
priv->ports[port].pvid = pvid;
otto_vlan_port_untagged_set(priv, port);
}
/* Initialize all VLANS */
@@ -905,6 +931,9 @@ int otto_vlan_port_filtering(struct dsa_switch *ds, int port,
priv->r->set_vlan_egr_filter(port, EGR_DISABLE);
}
priv->ports[port].vlan_filtering = vlan_filtering;
otto_vlan_port_untagged_set(priv, port);
/* Do we need to do something to the CPU-Port, too? */
mutex_unlock(&priv->reg_mutex);
@@ -152,6 +152,7 @@ void otto_vlan_931x_port_pvid_set(struct otto_dsa_priv *priv, int port, enum pbv
int pvid);
void otto_vlan_setup(struct otto_dsa_priv *priv);
void otto_vlan_port_untagged_set(struct otto_dsa_priv *priv, int port);
int otto_vlan_port_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
struct netlink_ext_ack *extack);
int otto_vlan_port_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan,