diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 05ba65042b5f..6be9fed50ed5 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -120,12 +120,21 @@ static int sja1105_commit_pvid(struct dsa_switch *ds, int port) if (rc) return rc; - vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; + /* Only force dropping of untagged packets when the port is under a + * VLAN-aware bridge. When the tag_8021q pvid is used, we are + * deliberately removing the RX VLAN from the port's VMEMB_PORT list, + * to prevent DSA tag spoofing from the link partner. Untagged packets + * are the only ones that should be received with tag_8021q, so + * definitely don't drop them. + */ + if (pvid == priv->bridge_pvid[port]) { + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; - match = sja1105_is_vlan_configured(priv, pvid); + match = sja1105_is_vlan_configured(priv, pvid); - if (match < 0 || !(vlan[match].vmemb_port & BIT(port))) - drop_untagged = true; + if (match < 0 || !(vlan[match].vmemb_port & BIT(port))) + drop_untagged = true; + } return sja1105_drop_untagged(ds, port, drop_untagged); } @@ -2343,7 +2352,7 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, } static int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid, - u16 flags) + u16 flags, bool allowed_ingress) { struct sja1105_vlan_lookup_entry *vlan; struct sja1105_table *table; @@ -2365,7 +2374,12 @@ static int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid, vlan[match].type_entry = SJA1110_VLAN_D_TAG; vlan[match].vlanid = vid; vlan[match].vlan_bc |= BIT(port); - vlan[match].vmemb_port |= BIT(port); + + if (allowed_ingress) + vlan[match].vmemb_port |= BIT(port); + else + vlan[match].vmemb_port &= ~BIT(port); + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) vlan[match].tag_port &= ~BIT(port); else @@ -2437,7 +2451,7 @@ static int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port, if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) flags = 0; - rc = sja1105_vlan_add(priv, port, vlan->vid, flags); + rc = sja1105_vlan_add(priv, port, vlan->vid, flags, true); if (rc) return rc; @@ -2467,9 +2481,16 @@ static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, u16 flags) { struct sja1105_private *priv = ds->priv; + bool allowed_ingress = true; int rc; - rc = sja1105_vlan_add(priv, port, vid, flags); + /* Prevent attackers from trying to inject a DSA tag from + * the outside world. + */ + if (dsa_is_user_port(ds, port)) + allowed_ingress = false; + + rc = sja1105_vlan_add(priv, port, vid, flags, allowed_ingress); if (rc) return rc;