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

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
Loading
Loading
Loading
Loading
+0 −6
Original line number Original line Diff line number Diff line
@@ -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);
+37 −18
Original line number Original line Diff line number Diff line
@@ -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)
	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)
		 */
		 */
		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,
		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,
}
}


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,
	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,
	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,
				      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)
		}
		}
		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;
+0 −10
Original line number Original line Diff line number Diff line
@@ -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 = {
	.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 = {
	.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 = {
	.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 = {
	.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 = {
	.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 = {
	.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 = {
	.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 = {
	.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 = {
	.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,
+2 −1
Original line number Original line Diff line number Diff line
@@ -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 {
	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 {
+74 −5
Original line number Original line Diff line number Diff line
@@ -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,
	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,
	 * 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,
		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);