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: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
ccbe64be15
@ -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,
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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 @@ proto_again:
|
||||
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;
|
||||
|
@ -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:
|
||||
|
@ -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 \
|
||||
|
206
tools/testing/selftests/net/forwarding/tc_flower_cfm.sh
Executable file
206
tools/testing/selftests/net/forwarding/tc_flower_cfm.sh
Executable file
@ -0,0 +1,206 @@
|
||||
#!/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
|
Loading…
x
Reference in New Issue
Block a user