Commit 6956fa39 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-sja1105-vlan-tags'

Vladimir Oltean says:

====================
Make sja1105 treat tag_8021q VLANs more like real DSA tags

This series solves a nuisance with the sja1105 driver, which is that
non-DSA tagged packets sent directly by the DSA master would still exit
the switch just fine.

We also had an issue for packets coming from the outside world with a
crafted DSA tag, the switch would not reject that tag but think it was
valid.
====================
parents 1ca8a193 8ded9160
...@@ -115,12 +115,6 @@ struct sja1105_info { ...@@ -115,12 +115,6 @@ struct sja1105_info {
const struct sja1105_dynamic_table_ops *dyn_ops; const struct sja1105_dynamic_table_ops *dyn_ops;
const struct sja1105_table_ops *static_ops; const struct sja1105_table_ops *static_ops;
const struct sja1105_regs *regs; const struct sja1105_regs *regs;
/* Both E/T and P/Q/R/S have quirks when it comes to popping the S-Tag
* from double-tagged frames. E/T will pop it only when it's equal to
* TPID from the General Parameters Table, while P/Q/R/S will only
* pop it when it's equal to TPID2.
*/
u16 qinq_tpid;
bool can_limit_mcast_flood; bool can_limit_mcast_flood;
int (*reset_cmd)(struct dsa_switch *ds); int (*reset_cmd)(struct dsa_switch *ds);
int (*setup_rgmii_delay)(const void *ctx, int port); int (*setup_rgmii_delay)(const void *ctx, int port);
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "sja1105_tas.h" #include "sja1105_tas.h"
#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull #define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull
#define SJA1105_DEFAULT_VLAN (VLAN_N_VID - 1)
static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
unsigned int startup_delay) unsigned int startup_delay)
...@@ -120,12 +119,24 @@ static int sja1105_commit_pvid(struct dsa_switch *ds, int port) ...@@ -120,12 +119,24 @@ static int sja1105_commit_pvid(struct dsa_switch *ds, int port)
if (rc) if (rc)
return rc; return rc;
/* 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; 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))) if (match < 0 || !(vlan[match].vmemb_port & BIT(port)))
drop_untagged = true; drop_untagged = true;
}
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
drop_untagged = true;
return sja1105_drop_untagged(ds, port, drop_untagged); return sja1105_drop_untagged(ds, port, drop_untagged);
} }
...@@ -208,6 +219,12 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) ...@@ -208,6 +219,12 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
*/ */
if (dsa_port_is_dsa(dp)) if (dsa_port_is_dsa(dp))
dp->learning = true; dp->learning = true;
/* Disallow untagged packets from being received on the
* CPU and DSA ports.
*/
if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))
mac[dp->index].drpuntag = true;
} }
return 0; return 0;
...@@ -2278,15 +2295,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, ...@@ -2278,15 +2295,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
tpid2 = ETH_P_SJA1105; tpid2 = ETH_P_SJA1105;
} }
for (port = 0; port < ds->num_ports; port++) {
struct sja1105_port *sp = &priv->ports[port];
if (enabled)
sp->xmit_tpid = priv->info->qinq_tpid;
else
sp->xmit_tpid = ETH_P_SJA1105;
}
if (priv->vlan_aware == enabled) if (priv->vlan_aware == enabled)
return 0; return 0;
...@@ -2343,7 +2351,7 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, ...@@ -2343,7 +2351,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, 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_vlan_lookup_entry *vlan;
struct sja1105_table *table; struct sja1105_table *table;
...@@ -2365,7 +2373,12 @@ static int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid, ...@@ -2365,7 +2373,12 @@ static int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid,
vlan[match].type_entry = SJA1110_VLAN_D_TAG; vlan[match].type_entry = SJA1110_VLAN_D_TAG;
vlan[match].vlanid = vid; vlan[match].vlanid = vid;
vlan[match].vlan_bc |= BIT(port); vlan[match].vlan_bc |= BIT(port);
if (allowed_ingress)
vlan[match].vmemb_port |= BIT(port); vlan[match].vmemb_port |= BIT(port);
else
vlan[match].vmemb_port &= ~BIT(port);
if (flags & BRIDGE_VLAN_INFO_UNTAGGED) if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
vlan[match].tag_port &= ~BIT(port); vlan[match].tag_port &= ~BIT(port);
else else
...@@ -2437,7 +2450,7 @@ static int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port, ...@@ -2437,7 +2450,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)) if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
flags = 0; flags = 0;
rc = sja1105_vlan_add(priv, port, vlan->vid, flags); rc = sja1105_vlan_add(priv, port, vlan->vid, flags, true);
if (rc) if (rc)
return rc; return rc;
...@@ -2467,9 +2480,16 @@ static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, ...@@ -2467,9 +2480,16 @@ static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
u16 flags) u16 flags)
{ {
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
bool allowed_ingress = true;
int rc; 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) if (rc)
return rc; return rc;
...@@ -2959,7 +2979,6 @@ static int sja1105_setup_ports(struct sja1105_private *priv) ...@@ -2959,7 +2979,6 @@ static int sja1105_setup_ports(struct sja1105_private *priv)
} }
sp->xmit_worker = worker; sp->xmit_worker = worker;
skb_queue_head_init(&sp->xmit_queue); skb_queue_head_init(&sp->xmit_queue);
sp->xmit_tpid = ETH_P_SJA1105;
} }
return 0; return 0;
......
...@@ -575,7 +575,6 @@ const struct sja1105_info sja1105e_info = { ...@@ -575,7 +575,6 @@ const struct sja1105_info sja1105e_info = {
.part_no = SJA1105ET_PART_NO, .part_no = SJA1105ET_PART_NO,
.static_ops = sja1105e_table_ops, .static_ops = sja1105e_table_ops,
.dyn_ops = sja1105et_dyn_ops, .dyn_ops = sja1105et_dyn_ops,
.qinq_tpid = ETH_P_8021Q,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = false, .can_limit_mcast_flood = false,
.ptp_ts_bits = 24, .ptp_ts_bits = 24,
...@@ -608,7 +607,6 @@ const struct sja1105_info sja1105t_info = { ...@@ -608,7 +607,6 @@ const struct sja1105_info sja1105t_info = {
.part_no = SJA1105ET_PART_NO, .part_no = SJA1105ET_PART_NO,
.static_ops = sja1105t_table_ops, .static_ops = sja1105t_table_ops,
.dyn_ops = sja1105et_dyn_ops, .dyn_ops = sja1105et_dyn_ops,
.qinq_tpid = ETH_P_8021Q,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = false, .can_limit_mcast_flood = false,
.ptp_ts_bits = 24, .ptp_ts_bits = 24,
...@@ -641,7 +639,6 @@ const struct sja1105_info sja1105p_info = { ...@@ -641,7 +639,6 @@ const struct sja1105_info sja1105p_info = {
.part_no = SJA1105P_PART_NO, .part_no = SJA1105P_PART_NO,
.static_ops = sja1105p_table_ops, .static_ops = sja1105p_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops, .dyn_ops = sja1105pqrs_dyn_ops,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.ptp_ts_bits = 32, .ptp_ts_bits = 32,
...@@ -675,7 +672,6 @@ const struct sja1105_info sja1105q_info = { ...@@ -675,7 +672,6 @@ const struct sja1105_info sja1105q_info = {
.part_no = SJA1105Q_PART_NO, .part_no = SJA1105Q_PART_NO,
.static_ops = sja1105q_table_ops, .static_ops = sja1105q_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops, .dyn_ops = sja1105pqrs_dyn_ops,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.ptp_ts_bits = 32, .ptp_ts_bits = 32,
...@@ -709,7 +705,6 @@ const struct sja1105_info sja1105r_info = { ...@@ -709,7 +705,6 @@ const struct sja1105_info sja1105r_info = {
.part_no = SJA1105R_PART_NO, .part_no = SJA1105R_PART_NO,
.static_ops = sja1105r_table_ops, .static_ops = sja1105r_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops, .dyn_ops = sja1105pqrs_dyn_ops,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.ptp_ts_bits = 32, .ptp_ts_bits = 32,
...@@ -747,7 +742,6 @@ const struct sja1105_info sja1105s_info = { ...@@ -747,7 +742,6 @@ const struct sja1105_info sja1105s_info = {
.static_ops = sja1105s_table_ops, .static_ops = sja1105s_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops, .dyn_ops = sja1105pqrs_dyn_ops,
.regs = &sja1105pqrs_regs, .regs = &sja1105pqrs_regs,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1105, .tag_proto = DSA_TAG_PROTO_SJA1105,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.ptp_ts_bits = 32, .ptp_ts_bits = 32,
...@@ -784,7 +778,6 @@ const struct sja1105_info sja1110a_info = { ...@@ -784,7 +778,6 @@ const struct sja1105_info sja1110a_info = {
.static_ops = sja1110_table_ops, .static_ops = sja1110_table_ops,
.dyn_ops = sja1110_dyn_ops, .dyn_ops = sja1110_dyn_ops,
.regs = &sja1110_regs, .regs = &sja1110_regs,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1110, .tag_proto = DSA_TAG_PROTO_SJA1110,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.multiple_cascade_ports = true, .multiple_cascade_ports = true,
...@@ -835,7 +828,6 @@ const struct sja1105_info sja1110b_info = { ...@@ -835,7 +828,6 @@ const struct sja1105_info sja1110b_info = {
.static_ops = sja1110_table_ops, .static_ops = sja1110_table_ops,
.dyn_ops = sja1110_dyn_ops, .dyn_ops = sja1110_dyn_ops,
.regs = &sja1110_regs, .regs = &sja1110_regs,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1110, .tag_proto = DSA_TAG_PROTO_SJA1110,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.multiple_cascade_ports = true, .multiple_cascade_ports = true,
...@@ -886,7 +878,6 @@ const struct sja1105_info sja1110c_info = { ...@@ -886,7 +878,6 @@ const struct sja1105_info sja1110c_info = {
.static_ops = sja1110_table_ops, .static_ops = sja1110_table_ops,
.dyn_ops = sja1110_dyn_ops, .dyn_ops = sja1110_dyn_ops,
.regs = &sja1110_regs, .regs = &sja1110_regs,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1110, .tag_proto = DSA_TAG_PROTO_SJA1110,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.multiple_cascade_ports = true, .multiple_cascade_ports = true,
...@@ -937,7 +928,6 @@ const struct sja1105_info sja1110d_info = { ...@@ -937,7 +928,6 @@ const struct sja1105_info sja1110d_info = {
.static_ops = sja1110_table_ops, .static_ops = sja1110_table_ops,
.dyn_ops = sja1110_dyn_ops, .dyn_ops = sja1110_dyn_ops,
.regs = &sja1110_regs, .regs = &sja1110_regs,
.qinq_tpid = ETH_P_8021AD,
.tag_proto = DSA_TAG_PROTO_SJA1110, .tag_proto = DSA_TAG_PROTO_SJA1110,
.can_limit_mcast_flood = true, .can_limit_mcast_flood = true,
.multiple_cascade_ports = true, .multiple_cascade_ports = true,
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#define ETH_P_SJA1105_META 0x0008 #define ETH_P_SJA1105_META 0x0008
#define ETH_P_SJA1110 0xdadc #define ETH_P_SJA1110 0xdadc
#define SJA1105_DEFAULT_VLAN (VLAN_N_VID - 1)
/* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */ /* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */
#define SJA1105_LINKLOCAL_FILTER_A 0x0180C2000000ull #define SJA1105_LINKLOCAL_FILTER_A 0x0180C2000000ull
#define SJA1105_LINKLOCAL_FILTER_A_MASK 0xFFFFFF000000ull #define SJA1105_LINKLOCAL_FILTER_A_MASK 0xFFFFFF000000ull
...@@ -65,7 +67,6 @@ struct sja1105_port { ...@@ -65,7 +67,6 @@ struct sja1105_port {
struct sja1105_tagger_data *data; struct sja1105_tagger_data *data;
struct dsa_port *dp; struct dsa_port *dp;
bool hwts_tx_en; bool hwts_tx_en;
u16 xmit_tpid;
}; };
enum sja1110_meta_tstamp { enum sja1110_meta_tstamp {
......
...@@ -133,14 +133,44 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp, ...@@ -133,14 +133,44 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp,
return NULL; return NULL;
} }
/* Send VLAN tags with a TPID that blends in with whatever VLAN protocol a
* bridge spanning ports of this switch might have.
*/
static u16 sja1105_xmit_tpid(struct dsa_port *dp) static u16 sja1105_xmit_tpid(struct dsa_port *dp)
{ {
struct sja1105_port *sp = dp->priv; struct dsa_switch *ds = dp->ds;
struct dsa_port *other_dp;
u16 proto;
if (unlikely(!dsa_port_is_sja1105(dp))) /* Since VLAN awareness is global, then if this port is VLAN-unaware,
return ETH_P_8021Q; * all ports are. Use the VLAN-unaware TPID used for tag_8021q.
*/
if (!dsa_port_is_vlan_filtering(dp))
return ETH_P_SJA1105;
/* Port is VLAN-aware, so there is a bridge somewhere (a single one,
* we're sure about that). It may not be on this port though, so we
* need to find it.
*/
list_for_each_entry(other_dp, &ds->dst->ports, list) {
if (other_dp->ds != ds)
continue;
if (!other_dp->bridge_dev)
continue;
/* Error is returned only if CONFIG_BRIDGE_VLAN_FILTERING,
* which seems pointless to handle, as our port cannot become
* VLAN-aware in that case.
*/
br_vlan_get_proto(other_dp->bridge_dev, &proto);
return sp->xmit_tpid; return proto;
}
WARN_ONCE(1, "Port is VLAN-aware but cannot find associated bridge!\n");
return ETH_P_SJA1105;
} }
static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb, static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb,
...@@ -168,6 +198,36 @@ static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb, ...@@ -168,6 +198,36 @@ static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb,
return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), tx_vid); return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), tx_vid);
} }
/* Transform untagged control packets into pvid-tagged control packets so that
* all packets sent by this tagger are VLAN-tagged and we can configure the
* switch to drop untagged packets coming from the DSA master.
*/
static struct sk_buff *sja1105_pvid_tag_control_pkt(struct dsa_port *dp,
struct sk_buff *skb, u8 pcp)
{
__be16 xmit_tpid = htons(sja1105_xmit_tpid(dp));
struct vlan_ethhdr *hdr;
/* If VLAN tag is in hwaccel area, move it to the payload
* to deal with both cases uniformly and to ensure that
* the VLANs are added in the right order.
*/
if (unlikely(skb_vlan_tag_present(skb))) {
skb = __vlan_hwaccel_push_inside(skb);
if (!skb)
return NULL;
}
hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
/* If skb is already VLAN-tagged, leave that VLAN ID in place */
if (hdr->h_vlan_proto == xmit_tpid)
return skb;
return vlan_insert_tag(skb, xmit_tpid, (pcp << VLAN_PRIO_SHIFT) |
SJA1105_DEFAULT_VLAN);
}
static struct sk_buff *sja1105_xmit(struct sk_buff *skb, static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
struct net_device *netdev) struct net_device *netdev)
{ {
...@@ -183,8 +243,13 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb, ...@@ -183,8 +243,13 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
* but instead SPI-installed management routes. Part 2 of this * but instead SPI-installed management routes. Part 2 of this
* is the .port_deferred_xmit driver callback. * is the .port_deferred_xmit driver callback.
*/ */
if (unlikely(sja1105_is_link_local(skb))) if (unlikely(sja1105_is_link_local(skb))) {
skb = sja1105_pvid_tag_control_pkt(dp, skb, pcp);
if (!skb)
return NULL;
return sja1105_defer_xmit(dp, skb); return sja1105_defer_xmit(dp, skb);
}
return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp),
((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
...@@ -213,6 +278,10 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb, ...@@ -213,6 +278,10 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb,
return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp),
((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
skb = sja1105_pvid_tag_control_pkt(dp, skb, pcp);
if (!skb)
return NULL;
skb_push(skb, SJA1110_HEADER_LEN); skb_push(skb, SJA1110_HEADER_LEN);
dsa_alloc_etype_header(skb, SJA1110_HEADER_LEN); dsa_alloc_etype_header(skb, SJA1110_HEADER_LEN);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment