Commit 89f9ffd3 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller
Browse files

net: mscc: ocelot: deal with problematic MAC_ETYPE VCAP IS2 rules



By default, the VCAP IS2 will produce a single match for each frame, on
the most specific classification.

Example: a ping packet (ICMP over IPv4 over Ethernet) sent from an IP
address of 10.0.0.1 and a MAC address of 96:18:82:00:04:01 will match
this rule:

tc filter add dev swp0 ingress protocol ipv4 \
	flower skip_sw src_ip 10.0.0.1 action drop

but not this one:

tc filter add dev swp0 ingress \
	flower skip_sw src_mac 96:18:82:00:04:01 action drop

Currently the driver does not really warn the user in any way about
this, and the behavior is rather strange anyway.

The current patch is a workaround to force matches on MAC_ETYPE keys
(DMAC and SMAC) for all packets irrespective of higher layer protocol.
The setting is made at the port level.

Of course this breaks all other non-src_mac and non-dst_mac matches, so
rule exclusivity checks have been added to the driver, in order to never
have rules of both types on any ingress port.

The bits that discard higher-level protocol information are set only
once a MAC_ETYPE rule is added to a filter block, and only for the ports
that are bound to that filter block. Then all further non-MAC_ETYPE
rules added to that filter block should be denied by the ports bound to
it.

Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f89370d4
Loading
Loading
Loading
Loading
+102 −1
Original line number Diff line number Diff line
@@ -706,13 +706,114 @@ ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index)
	return NULL;
}

/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based
 * on destination and source MAC addresses, but only on higher-level protocol
 * information. The only frame types to match on keys containing MAC addresses
 * in this case are non-SNAP, non-ARP, non-IP and non-OAM frames.
 *
 * If @on=true, then the above frame types (SNAP, ARP, IP and OAM) will match
 * on MAC_ETYPE keys such as destination and source MAC on this ingress port.
 * However the setting has the side effect of making these frames not matching
 * on any _other_ keys than MAC_ETYPE ones.
 */
static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port,
					  bool on)
{
	u32 val = 0;

	if (on)
		val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(3) |
		      ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(3) |
		      ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(3) |
		      ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(3) |
		      ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(3);

	ocelot_rmw_gix(ocelot, val,
		       ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_M |
		       ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_M |
		       ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_M |
		       ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_M |
		       ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS_M,
		       ANA_PORT_VCAP_S2_CFG, port);
}

static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace)
{
	if (ace->type != OCELOT_ACE_TYPE_ETYPE)
		return false;
	if (ether_addr_to_u64(ace->frame.etype.dmac.value) &
	    ether_addr_to_u64(ace->frame.etype.dmac.mask))
		return true;
	if (ether_addr_to_u64(ace->frame.etype.smac.value) &
	    ether_addr_to_u64(ace->frame.etype.smac.mask))
		return true;
	return false;
}

static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace)
{
	if (ace->type == OCELOT_ACE_TYPE_SNAP)
		return true;
	if (ace->type == OCELOT_ACE_TYPE_ARP)
		return true;
	if (ace->type == OCELOT_ACE_TYPE_IPV4)
		return true;
	if (ace->type == OCELOT_ACE_TYPE_IPV6)
		return true;
	return false;
}

static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
						 struct ocelot_ace_rule *ace)
{
	struct ocelot_acl_block *block = &ocelot->acl_block;
	struct ocelot_ace_rule *tmp;
	unsigned long port;
	int i;

	if (ocelot_ace_is_problematic_mac_etype(ace)) {
		/* Search for any non-MAC_ETYPE rules on the port */
		for (i = 0; i < block->count; i++) {
			tmp = ocelot_ace_rule_get_rule_index(block, i);
			if (tmp->ingress_port_mask & ace->ingress_port_mask &&
			    ocelot_ace_is_problematic_non_mac_etype(tmp))
				return false;
		}

		for_each_set_bit(port, &ace->ingress_port_mask,
				 ocelot->num_phys_ports)
			ocelot_match_all_as_mac_etype(ocelot, port, true);
	} else if (ocelot_ace_is_problematic_non_mac_etype(ace)) {
		/* Search for any MAC_ETYPE rules on the port */
		for (i = 0; i < block->count; i++) {
			tmp = ocelot_ace_rule_get_rule_index(block, i);
			if (tmp->ingress_port_mask & ace->ingress_port_mask &&
			    ocelot_ace_is_problematic_mac_etype(tmp))
				return false;
		}

		for_each_set_bit(port, &ace->ingress_port_mask,
				 ocelot->num_phys_ports)
			ocelot_match_all_as_mac_etype(ocelot, port, false);
	}

	return true;
}

int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
				struct ocelot_ace_rule *rule)
				struct ocelot_ace_rule *rule,
				struct netlink_ext_ack *extack)
{
	struct ocelot_acl_block *block = &ocelot->acl_block;
	struct ocelot_ace_rule *ace;
	int i, index;

	if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
		return -EBUSY;
	}

	/* Add rule to the linked list */
	ocelot_ace_rule_add(ocelot, block, rule);

+3 −2
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ struct ocelot_ace_rule {

	enum ocelot_ace_action action;
	struct ocelot_ace_stats stats;
	u16 ingress_port_mask;
	unsigned long ingress_port_mask;

	enum ocelot_vcap_bit dmac_mc;
	enum ocelot_vcap_bit dmac_bc;
@@ -215,7 +215,8 @@ struct ocelot_ace_rule {
};

int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
				struct ocelot_ace_rule *rule);
				struct ocelot_ace_rule *rule,
				struct netlink_ext_ack *extack);
int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
				struct ocelot_ace_rule *rule);
int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
+1 −1
Original line number Diff line number Diff line
@@ -205,7 +205,7 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
		return ret;
	}

	return ocelot_ace_rule_offload_add(ocelot, ace);
	return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack);
}
EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);