Commit ccbe64be authored by Jakub Kicinski's avatar Jakub Kicinski

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3a2cb45c 1668a55a
...@@ -301,6 +301,26 @@ struct flow_dissector_key_l2tpv3 { ...@@ -301,6 +301,26 @@ struct flow_dissector_key_l2tpv3 {
__be32 session_id; __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 { enum flow_dissector_key_id {
FLOW_DISSECTOR_KEY_CONTROL, /* struct flow_dissector_key_control */ FLOW_DISSECTOR_KEY_CONTROL, /* struct flow_dissector_key_control */
FLOW_DISSECTOR_KEY_BASIC, /* struct flow_dissector_key_basic */ FLOW_DISSECTOR_KEY_BASIC, /* struct flow_dissector_key_basic */
...@@ -333,6 +353,7 @@ enum flow_dissector_key_id { ...@@ -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_NUM_OF_VLANS, /* struct flow_dissector_key_num_of_vlans */
FLOW_DISSECTOR_KEY_PPPOE, /* struct flow_dissector_key_pppoe */ FLOW_DISSECTOR_KEY_PPPOE, /* struct flow_dissector_key_pppoe */
FLOW_DISSECTOR_KEY_L2TPV3, /* struct flow_dissector_key_l2tpv3 */ FLOW_DISSECTOR_KEY_L2TPV3, /* struct flow_dissector_key_l2tpv3 */
FLOW_DISSECTOR_KEY_CFM, /* struct flow_dissector_key_cfm */
FLOW_DISSECTOR_KEY_MAX, FLOW_DISSECTOR_KEY_MAX,
}; };
......
...@@ -596,6 +596,8 @@ enum { ...@@ -596,6 +596,8 @@ enum {
TCA_FLOWER_L2_MISS, /* u8 */ TCA_FLOWER_L2_MISS, /* u8 */
TCA_FLOWER_KEY_CFM, /* nested */
__TCA_FLOWER_MAX, __TCA_FLOWER_MAX,
}; };
...@@ -704,6 +706,13 @@ enum { ...@@ -704,6 +706,13 @@ enum {
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1), 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 */ #define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */
/* Match-all classifier */ /* Match-all classifier */
......
...@@ -557,6 +557,30 @@ __skb_flow_dissect_arp(const struct sk_buff *skb, ...@@ -557,6 +557,30 @@ __skb_flow_dissect_arp(const struct sk_buff *skb,
return FLOW_DISSECT_RET_OUT_GOOD; 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 static enum flow_dissect_ret
__skb_flow_dissect_gre(const struct sk_buff *skb, __skb_flow_dissect_gre(const struct sk_buff *skb,
struct flow_dissector_key_control *key_control, struct flow_dissector_key_control *key_control,
...@@ -1400,6 +1424,12 @@ bool __skb_flow_dissect(const struct net *net, ...@@ -1400,6 +1424,12 @@ bool __skb_flow_dissect(const struct net *net,
break; break;
} }
case htons(ETH_P_CFM):
fdret = __skb_flow_dissect_cfm(skb, flow_dissector,
target_container, data,
nhoff, hlen);
break;
default: default:
fdret = FLOW_DISSECT_RET_OUT_BAD; fdret = FLOW_DISSECT_RET_OUT_BAD;
break; break;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/bitfield.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/in6.h> #include <linux/in6.h>
...@@ -71,6 +72,7 @@ struct fl_flow_key { ...@@ -71,6 +72,7 @@ struct fl_flow_key {
struct flow_dissector_key_num_of_vlans num_of_vlans; struct flow_dissector_key_num_of_vlans num_of_vlans;
struct flow_dissector_key_pppoe pppoe; struct flow_dissector_key_pppoe pppoe;
struct flow_dissector_key_l2tpv3 l2tpv3; 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. */ } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
struct fl_flow_mask_range { struct fl_flow_mask_range {
...@@ -725,6 +727,7 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { ...@@ -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_PPP_PROTO] = { .type = NLA_U16 },
[TCA_FLOWER_KEY_L2TPV3_SID] = { .type = NLA_U32 }, [TCA_FLOWER_KEY_L2TPV3_SID] = { .type = NLA_U32 },
[TCA_FLOWER_L2_MISS] = NLA_POLICY_MAX(NLA_U8, 1), [TCA_FLOWER_L2_MISS] = NLA_POLICY_MAX(NLA_U8, 1),
[TCA_FLOWER_KEY_CFM] = { .type = NLA_NESTED },
}; };
static const struct nla_policy static const struct nla_policy
...@@ -773,6 +776,12 @@ mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = { ...@@ -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 }, [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, static void fl_set_key_val(struct nlattr **tb,
void *val, int val_type, void *val, int val_type,
void *mask, int mask_type, int len) void *mask, int mask_type, int len)
...@@ -1660,6 +1669,53 @@ static bool is_vlan_key(struct nlattr *tb, __be16 *ethertype, ...@@ -1660,6 +1669,53 @@ static bool is_vlan_key(struct nlattr *tb, __be16 *ethertype,
return false; 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, static int fl_set_key(struct net *net, struct nlattr **tb,
struct fl_flow_key *key, struct fl_flow_key *mask, struct fl_flow_key *key, struct fl_flow_key *mask,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -1814,6 +1870,10 @@ static int fl_set_key(struct net *net, struct nlattr **tb, ...@@ -1814,6 +1870,10 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
TCA_FLOWER_KEY_L2TPV3_SID, TCA_FLOWER_KEY_L2TPV3_SID,
&mask->l2tpv3.session_id, TCA_FLOWER_UNSPEC, &mask->l2tpv3.session_id, TCA_FLOWER_UNSPEC,
sizeof(key->l2tpv3.session_id)); 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 || if (key->basic.ip_proto == IPPROTO_TCP ||
...@@ -1996,6 +2056,8 @@ static void fl_init_dissector(struct flow_dissector *dissector, ...@@ -1996,6 +2056,8 @@ static void fl_init_dissector(struct flow_dissector *dissector,
FLOW_DISSECTOR_KEY_PPPOE, pppoe); FLOW_DISSECTOR_KEY_PPPOE, pppoe);
FL_KEY_SET_IF_MASKED(mask, keys, cnt, FL_KEY_SET_IF_MASKED(mask, keys, cnt,
FLOW_DISSECTOR_KEY_L2TPV3, l2tpv3); 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); skb_flow_dissector_init(dissector, keys, cnt);
} }
...@@ -3029,6 +3091,43 @@ static int fl_dump_key_ct(struct sk_buff *skb, ...@@ -3029,6 +3091,43 @@ static int fl_dump_key_ct(struct sk_buff *skb,
return -EMSGSIZE; 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, static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
struct flow_dissector_key_enc_opts *enc_opts) struct flow_dissector_key_enc_opts *enc_opts)
{ {
...@@ -3316,6 +3415,9 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net, ...@@ -3316,6 +3415,9 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net,
sizeof(key->hash.hash))) sizeof(key->hash.hash)))
goto nla_put_failure; goto nla_put_failure;
if (fl_dump_key_cfm(skb, &key->cfm, &mask->cfm))
goto nla_put_failure;
return 0; return 0;
nla_put_failure: nla_put_failure:
......
...@@ -84,6 +84,7 @@ TEST_PROGS = bridge_igmp.sh \ ...@@ -84,6 +84,7 @@ TEST_PROGS = bridge_igmp.sh \
tc_flower_router.sh \ tc_flower_router.sh \
tc_flower.sh \ tc_flower.sh \
tc_flower_l2_miss.sh \ tc_flower_l2_miss.sh \
tc_flower_cfm.sh \
tc_mpls_l2vpn.sh \ tc_mpls_l2vpn.sh \
tc_police.sh \ tc_police.sh \
tc_shblocks.sh \ tc_shblocks.sh \
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="match_cfm_opcode match_cfm_level match_cfm_level_and_opcode"
NUM_NETIFS=2
source tc_common.sh
source lib.sh
h1_create()
{
simple_if_init $h1
}
h1_destroy()
{
simple_if_fini $h1
}
h2_create()
{
simple_if_init $h2
tc qdisc add dev $h2 clsact
}
h2_destroy()
{
tc qdisc del dev $h2 clsact
simple_if_fini $h2
}
u8_to_hex()
{
local u8=$1; shift
printf "%02x" $u8
}
generate_cfm_hdr()
{
local mdl=$1; shift
local op=$1; shift
local flags=$1; shift
local tlv_offset=$1; shift
local cfm_hdr=$(:
)"$(u8_to_hex $((mdl << 5))):"$( : MD level and Version
)"$(u8_to_hex $op):"$( : OpCode
)"$(u8_to_hex $flags):"$( : Flags
)"$(u8_to_hex $tlv_offset)"$( : TLV offset
)
echo $cfm_hdr
}
match_cfm_opcode()
{
local ethtype="89 02"; readonly ethtype
RET=0
tc filter add dev $h2 ingress protocol cfm pref 1 handle 101 \
flower cfm op 47 action drop
tc filter add dev $h2 ingress protocol cfm pref 1 handle 102 \
flower cfm op 43 action drop
pkt="$ethtype $(generate_cfm_hdr 7 47 0 32)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
pkt="$ethtype $(generate_cfm_hdr 6 5 0 4)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Did not match on correct opcode"
tc_check_packets "dev $h2 ingress" 102 0
check_err $? "Matched on the wrong opcode"
pkt="$ethtype $(generate_cfm_hdr 0 43 0 12)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Matched on the wrong opcode"
tc_check_packets "dev $h2 ingress" 102 1
check_err $? "Did not match on correct opcode"
tc filter del dev $h2 ingress protocol cfm pref 1 handle 101 flower
tc filter del dev $h2 ingress protocol cfm pref 1 handle 102 flower
log_test "CFM opcode match test"
}
match_cfm_level()
{
local ethtype="89 02"; readonly ethtype
RET=0
tc filter add dev $h2 ingress protocol cfm pref 1 handle 101 \
flower cfm mdl 5 action drop
tc filter add dev $h2 ingress protocol cfm pref 1 handle 102 \
flower cfm mdl 3 action drop
tc filter add dev $h2 ingress protocol cfm pref 1 handle 103 \
flower cfm mdl 0 action drop
pkt="$ethtype $(generate_cfm_hdr 5 42 0 12)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
pkt="$ethtype $(generate_cfm_hdr 6 1 0 70)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
pkt="$ethtype $(generate_cfm_hdr 0 1 0 70)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Did not match on correct level"
tc_check_packets "dev $h2 ingress" 102 0
check_err $? "Matched on the wrong level"
tc_check_packets "dev $h2 ingress" 103 1
check_err $? "Did not match on correct level"
pkt="$ethtype $(generate_cfm_hdr 3 0 0 4)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Matched on the wrong level"
tc_check_packets "dev $h2 ingress" 102 1
check_err $? "Did not match on correct level"
tc_check_packets "dev $h2 ingress" 103 1
check_err $? "Matched on the wrong level"
tc filter del dev $h2 ingress protocol cfm pref 1 handle 101 flower
tc filter del dev $h2 ingress protocol cfm pref 1 handle 102 flower
tc filter del dev $h2 ingress protocol cfm pref 1 handle 103 flower
log_test "CFM level match test"
}
match_cfm_level_and_opcode()
{
local ethtype="89 02"; readonly ethtype
RET=0
tc filter add dev $h2 ingress protocol cfm pref 1 handle 101 \
flower cfm mdl 5 op 41 action drop
tc filter add dev $h2 ingress protocol cfm pref 1 handle 102 \
flower cfm mdl 7 op 42 action drop
pkt="$ethtype $(generate_cfm_hdr 5 41 0 4)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
pkt="$ethtype $(generate_cfm_hdr 7 3 0 4)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
pkt="$ethtype $(generate_cfm_hdr 3 42 0 12)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Did not match on correct level and opcode"
tc_check_packets "dev $h2 ingress" 102 0
check_err $? "Matched on the wrong level and opcode"
pkt="$ethtype $(generate_cfm_hdr 7 42 0 12)"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac "$pkt" -q
tc_check_packets "dev $h2 ingress" 101 1
check_err $? "Matched on the wrong level and opcode"
tc_check_packets "dev $h2 ingress" 102 1
check_err $? "Did not match on correct level and opcode"
tc filter del dev $h2 ingress protocol cfm pref 1 handle 101 flower
tc filter del dev $h2 ingress protocol cfm pref 1 handle 102 flower
log_test "CFM opcode and level match test"
}
setup_prepare()
{
h1=${NETIFS[p1]}
h2=${NETIFS[p2]}
h1mac=$(mac_get $h1)
h2mac=$(mac_get $h2)
vrf_prepare
h1_create
h2_create
}
cleanup()
{
pre_cleanup
h2_destroy
h1_destroy
vrf_cleanup
}
trap cleanup EXIT
setup_prepare
setup_wait
tests_run
exit $EXIT_STATUS
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