OSDN Git Service

net: flower: add support for matching cfm fields
authorZahari Doychev <zdoychev@maxlinear.com>
Thu, 8 Jun 2023 10:56:47 +0000 (12:56 +0200)
committerJakub Kicinski <kuba@kernel.org>
Tue, 13 Jun 2023 00:01:45 +0000 (17:01 -0700)
Add support to the tc flower classifier to match based on fields in CFM
information elements like level and opcode.

tc filter add dev ens6 ingress protocol 802.1q \
flower vlan_id 698 vlan_ethtype 0x8902 cfm mdl 5 op 46 \
action drop

Signed-off-by: Zahari Doychev <zdoychev@maxlinear.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/uapi/linux/pkt_cls.h
net/sched/cls_flower.c

index 00933dd..7865f5a 100644 (file)
@@ -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 */
index e02ecab..56065cc 100644 (file)
@@ -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 @@ nla_put_failure:
        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: