Commit ccbe64be authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-flower-add-cfm-support'

Zahari Doychev says:

====================
net: flower: add cfm support

The first patch adds cfm support to the flow dissector.
The second adds the flower classifier support.
The third adds a selftest for the flower cfm functionality.
====================

Link: https://lore.kernel.org/r/20230608105648.266575-1-zahari.doychev@linux.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3a2cb45c 1668a55a
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -301,6 +301,26 @@ struct flow_dissector_key_l2tpv3 {
	__be32 session_id;
};

/**
 * struct flow_dissector_key_cfm
 * @mdl_ver: maintenance domain level (mdl) and cfm protocol version
 * @opcode: code specifying a type of cfm protocol packet
 *
 * See 802.1ag, ITU-T G.8013/Y.1731
 *         1               2
 * |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | mdl | version |     opcode    |
 * +-----+---------+-+-+-+-+-+-+-+-+
 */
struct flow_dissector_key_cfm {
	u8	mdl_ver;
	u8	opcode;
};

#define FLOW_DIS_CFM_MDL_MASK GENMASK(7, 5)
#define FLOW_DIS_CFM_MDL_MAX 7

enum flow_dissector_key_id {
	FLOW_DISSECTOR_KEY_CONTROL, /* struct flow_dissector_key_control */
	FLOW_DISSECTOR_KEY_BASIC, /* struct flow_dissector_key_basic */
@@ -333,6 +353,7 @@ enum flow_dissector_key_id {
	FLOW_DISSECTOR_KEY_NUM_OF_VLANS, /* struct flow_dissector_key_num_of_vlans */
	FLOW_DISSECTOR_KEY_PPPOE, /* struct flow_dissector_key_pppoe */
	FLOW_DISSECTOR_KEY_L2TPV3, /* struct flow_dissector_key_l2tpv3 */
	FLOW_DISSECTOR_KEY_CFM, /* struct flow_dissector_key_cfm */

	FLOW_DISSECTOR_KEY_MAX,
};
+9 −0
Original line number Diff line number Diff line
@@ -596,6 +596,8 @@ enum {

	TCA_FLOWER_L2_MISS,		/* u8 */

	TCA_FLOWER_KEY_CFM,		/* nested */

	__TCA_FLOWER_MAX,
};

@@ -704,6 +706,13 @@ enum {
	TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
};

enum {
	TCA_FLOWER_KEY_CFM_OPT_UNSPEC,
	TCA_FLOWER_KEY_CFM_MD_LEVEL,
	TCA_FLOWER_KEY_CFM_OPCODE,
	TCA_FLOWER_KEY_CFM_OPT_MAX,
};

#define TCA_FLOWER_MASK_FLAGS_RANGE	(1 << 0) /* Range-based match */

/* Match-all classifier */
+30 −0
Original line number Diff line number Diff line
@@ -557,6 +557,30 @@ __skb_flow_dissect_arp(const struct sk_buff *skb,
	return FLOW_DISSECT_RET_OUT_GOOD;
}

static enum flow_dissect_ret
__skb_flow_dissect_cfm(const struct sk_buff *skb,
		       struct flow_dissector *flow_dissector,
		       void *target_container, const void *data,
		       int nhoff, int hlen)
{
	struct flow_dissector_key_cfm *key, *hdr, _hdr;

	if (!dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_CFM))
		return FLOW_DISSECT_RET_OUT_GOOD;

	hdr = __skb_header_pointer(skb, nhoff, sizeof(*key), data, hlen, &_hdr);
	if (!hdr)
		return FLOW_DISSECT_RET_OUT_BAD;

	key = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_CFM,
					target_container);

	key->mdl_ver = hdr->mdl_ver;
	key->opcode = hdr->opcode;

	return FLOW_DISSECT_RET_OUT_GOOD;
}

static enum flow_dissect_ret
__skb_flow_dissect_gre(const struct sk_buff *skb,
		       struct flow_dissector_key_control *key_control,
@@ -1400,6 +1424,12 @@ bool __skb_flow_dissect(const struct net *net,
		break;
	}

	case htons(ETH_P_CFM):
		fdret = __skb_flow_dissect_cfm(skb, flow_dissector,
					       target_container, data,
					       nhoff, hlen);
		break;

	default:
		fdret = FLOW_DISSECT_RET_OUT_BAD;
		break;
+102 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/rhashtable.h>
#include <linux/workqueue.h>
#include <linux/refcount.h>
#include <linux/bitfield.h>

#include <linux/if_ether.h>
#include <linux/in6.h>
@@ -71,6 +72,7 @@ struct fl_flow_key {
	struct flow_dissector_key_num_of_vlans num_of_vlans;
	struct flow_dissector_key_pppoe pppoe;
	struct flow_dissector_key_l2tpv3 l2tpv3;
	struct flow_dissector_key_cfm cfm;
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */

struct fl_flow_mask_range {
@@ -725,6 +727,7 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
	[TCA_FLOWER_KEY_PPP_PROTO]	= { .type = NLA_U16 },
	[TCA_FLOWER_KEY_L2TPV3_SID]	= { .type = NLA_U32 },
	[TCA_FLOWER_L2_MISS]		= NLA_POLICY_MAX(NLA_U8, 1),
	[TCA_FLOWER_KEY_CFM]		= { .type = NLA_NESTED },
};

static const struct nla_policy
@@ -773,6 +776,12 @@ mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = {
	[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL]    = { .type = NLA_U32 },
};

static const struct nla_policy cfm_opt_policy[TCA_FLOWER_KEY_CFM_OPT_MAX] = {
	[TCA_FLOWER_KEY_CFM_MD_LEVEL]	= NLA_POLICY_MAX(NLA_U8,
						FLOW_DIS_CFM_MDL_MAX),
	[TCA_FLOWER_KEY_CFM_OPCODE]	= { .type = NLA_U8 },
};

static void fl_set_key_val(struct nlattr **tb,
			   void *val, int val_type,
			   void *mask, int mask_type, int len)
@@ -1660,6 +1669,53 @@ static bool is_vlan_key(struct nlattr *tb, __be16 *ethertype,
	return false;
}

static void fl_set_key_cfm_md_level(struct nlattr **tb,
				    struct fl_flow_key *key,
				    struct fl_flow_key *mask,
				    struct netlink_ext_ack *extack)
{
	u8 level;

	if (!tb[TCA_FLOWER_KEY_CFM_MD_LEVEL])
		return;

	level = nla_get_u8(tb[TCA_FLOWER_KEY_CFM_MD_LEVEL]);
	key->cfm.mdl_ver = FIELD_PREP(FLOW_DIS_CFM_MDL_MASK, level);
	mask->cfm.mdl_ver = FLOW_DIS_CFM_MDL_MASK;
}

static void fl_set_key_cfm_opcode(struct nlattr **tb,
				  struct fl_flow_key *key,
				  struct fl_flow_key *mask,
				  struct netlink_ext_ack *extack)
{
	fl_set_key_val(tb, &key->cfm.opcode, TCA_FLOWER_KEY_CFM_OPCODE,
		       &mask->cfm.opcode, TCA_FLOWER_UNSPEC,
		       sizeof(key->cfm.opcode));
}

static int fl_set_key_cfm(struct nlattr **tb,
			  struct fl_flow_key *key,
			  struct fl_flow_key *mask,
			  struct netlink_ext_ack *extack)
{
	struct nlattr *nla_cfm_opt[TCA_FLOWER_KEY_CFM_OPT_MAX];
	int err;

	if (!tb[TCA_FLOWER_KEY_CFM])
		return 0;

	err = nla_parse_nested(nla_cfm_opt, TCA_FLOWER_KEY_CFM_OPT_MAX,
			       tb[TCA_FLOWER_KEY_CFM], cfm_opt_policy, extack);
	if (err < 0)
		return err;

	fl_set_key_cfm_opcode(nla_cfm_opt, key, mask, extack);
	fl_set_key_cfm_md_level(nla_cfm_opt, key, mask, extack);

	return 0;
}

static int fl_set_key(struct net *net, struct nlattr **tb,
		      struct fl_flow_key *key, struct fl_flow_key *mask,
		      struct netlink_ext_ack *extack)
@@ -1814,6 +1870,10 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
			       TCA_FLOWER_KEY_L2TPV3_SID,
			       &mask->l2tpv3.session_id, TCA_FLOWER_UNSPEC,
			       sizeof(key->l2tpv3.session_id));
	} else if (key->basic.n_proto  == htons(ETH_P_CFM)) {
		ret = fl_set_key_cfm(tb, key, mask, extack);
		if (ret)
			return ret;
	}

	if (key->basic.ip_proto == IPPROTO_TCP ||
@@ -1996,6 +2056,8 @@ static void fl_init_dissector(struct flow_dissector *dissector,
			     FLOW_DISSECTOR_KEY_PPPOE, pppoe);
	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
			     FLOW_DISSECTOR_KEY_L2TPV3, l2tpv3);
	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
			     FLOW_DISSECTOR_KEY_CFM, cfm);

	skb_flow_dissector_init(dissector, keys, cnt);
}
@@ -3029,6 +3091,43 @@ static int fl_dump_key_ct(struct sk_buff *skb,
	return -EMSGSIZE;
}

static int fl_dump_key_cfm(struct sk_buff *skb,
			   struct flow_dissector_key_cfm *key,
			   struct flow_dissector_key_cfm *mask)
{
	struct nlattr *opts;
	int err;
	u8 mdl;

	if (!memchr_inv(mask, 0, sizeof(*mask)))
		return 0;

	opts = nla_nest_start(skb, TCA_FLOWER_KEY_CFM);
	if (!opts)
		return -EMSGSIZE;

	if (FIELD_GET(FLOW_DIS_CFM_MDL_MASK, mask->mdl_ver)) {
		mdl = FIELD_GET(FLOW_DIS_CFM_MDL_MASK, key->mdl_ver);
		err = nla_put_u8(skb, TCA_FLOWER_KEY_CFM_MD_LEVEL, mdl);
		if (err)
			goto err_cfm_opts;
	}

	if (mask->opcode) {
		err = nla_put_u8(skb, TCA_FLOWER_KEY_CFM_OPCODE, key->opcode);
		if (err)
			goto err_cfm_opts;
	}

	nla_nest_end(skb, opts);

	return 0;

err_cfm_opts:
	nla_nest_cancel(skb, opts);
	return err;
}

static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
			       struct flow_dissector_key_enc_opts *enc_opts)
{
@@ -3316,6 +3415,9 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net,
			     sizeof(key->hash.hash)))
		goto nla_put_failure;

	if (fl_dump_key_cfm(skb, &key->cfm, &mask->cfm))
		goto nla_put_failure;

	return 0;

nla_put_failure:
+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ TEST_PROGS = bridge_igmp.sh \
	tc_flower_router.sh \
	tc_flower.sh \
	tc_flower_l2_miss.sh \
	tc_flower_cfm.sh \
	tc_mpls_l2vpn.sh \
	tc_police.sh \
	tc_shblocks.sh \
Loading