net: dsa: motorcomm: Extend ACL with UDF

The native ACL key set cannot express IP TTL or ICMP type/code. Use the
User Defined Filter windows to match them in the tc flower offload.

Signed-off-by: David Yang <mmyangfl@gmail.com>
Link: https://patch.msgid.link/20261017023136.2961543-1-mmyangfl@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
David Yang 2026-10-17 10:31:32 +08:00 committed by Paolo Abeni
parent 39e24cf5e2
commit 279d5c8afc
3 changed files with 144 additions and 10 deletions

View File

@ -440,7 +440,7 @@ enum yt921x_fdb_entry_status {
#define YT921X_TAG_LEN 8
#define YT921X_ACL_BLK_NUM 48
#define YT921X_UDF_NUM 8
#define YT921X_UDF_NUM 16
#define YT921X_LED_GROUP_NUM 3
#define YT921X_LED_PORT_NUM 10

View File

@ -543,6 +543,24 @@ yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord,
}
}
struct yt921x_udf_entry {
unsigned char type;
unsigned char offset;
u8 key;
u8 mask;
};
struct yt921x_udf_map {
unsigned char type;
unsigned char offset;
};
/* UDF windows are preselected - that's enough for now. */
static struct yt921x_udf_map yt921x_udf_maps[] = {
{YT921X_UDF_TYPE_L3, 7},
{YT921X_UDF_TYPE_L4, 0},
};
struct yt921x_acl_rule_ext {
struct yt921x_acl_rule r;
@ -556,9 +574,13 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct yt921x_acl_entry *entries = ruleext->r.entries;
struct netlink_ext_ack *extack = cls->common.extack;
struct yt921x_udf_entry udfs[YT921X_UDF_NUM];
const struct flow_dissector *dissector;
struct yt921x_acl_entry *entry;
unsigned int udfs_cnt = 0;
unsigned int size = 0;
__be16 n_proto = 0;
u8 ip_proto = 0;
bool use_dport;
bool use_sport;
@ -575,7 +597,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) {
BIT_ULL(FLOW_DISSECTOR_KEY_TCP) |
BIT_ULL(FLOW_DISSECTOR_KEY_ICMP))) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used");
return -EOPNOTSUPP;
}
@ -741,7 +764,9 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
if (match.mask->n_proto) {
enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER;
if (match.mask->n_proto == htons(~0))
if (match.mask->n_proto == htons(~0)) {
n_proto = match.key->n_proto;
switch (match.key->n_proto) {
case htons(ETH_P_IP):
l3type = YT921X_L3_TYPE_IPV4;
@ -762,6 +787,7 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
l3type = YT921X_L3_TYPE_ERP;
break;
}
}
if (l3type != YT921X_L3_TYPE_OTHER) {
size = yt921x_acl_entries_set_l3_type(entries,
@ -783,7 +809,9 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
if (match.mask->ip_proto) {
enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER;
if (match.mask->ip_proto == (u8)~0)
if (match.mask->ip_proto == (u8)~0) {
ip_proto = match.key->ip_proto;
switch (match.key->ip_proto) {
case IPPROTO_TCP:
l4type = YT921X_L4_TYPE_TCP;
@ -801,6 +829,7 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
l4type = YT921X_L4_TYPE_IGMP;
break;
}
}
if (l4type != YT921X_L4_TYPE_OTHER) {
size = yt921x_acl_entries_set_l4_type(entries,
@ -852,10 +881,25 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
struct flow_match_ip match;
flow_rule_match_ip(rule, &match);
if (match.mask->ttl) {
NL_SET_ERR_MSG_MOD(extack,
"Matching on TTL not supported");
return -EOPNOTSUPP;
if (n_proto != htons(ETH_P_IP) &&
n_proto != htons(ETH_P_IPV6)) {
NL_SET_ERR_MSG_MOD(extack,
"Matching on TTL requires protocol ip or ipv6");
return -EOPNOTSUPP;
}
if (udfs_cnt >= YT921X_UDF_NUM)
goto err;
udfs[udfs_cnt] = (struct yt921x_udf_entry){
.type = YT921X_UDF_TYPE_L3,
.offset = n_proto == htons(ETH_P_IP) ? 8 : 7,
.key = match.key->ttl,
.mask = match.mask->ttl,
};
udfs_cnt++;
}
if (match.mask->tos) {
@ -889,6 +933,82 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
}
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) {
struct flow_match_icmp match;
flow_rule_match_icmp(rule, &match);
if (match.mask->type || match.mask->code) {
if (ip_proto != IPPROTO_ICMP &&
ip_proto != IPPROTO_ICMPV6) {
NL_SET_ERR_MSG_MOD(extack,
"Matching on ICMP type/code requires ip_proto icmp or icmpv6");
return -EOPNOTSUPP;
}
if (match.mask->type) {
if (udfs_cnt >= YT921X_UDF_NUM)
goto err;
udfs[udfs_cnt] = (struct yt921x_udf_entry){
.type = YT921X_UDF_TYPE_L4,
.offset = 0,
.key = match.key->type,
.mask = match.mask->type,
};
udfs_cnt++;
}
if (match.mask->code) {
if (udfs_cnt >= YT921X_UDF_NUM)
goto err;
udfs[udfs_cnt] = (struct yt921x_udf_entry){
.type = YT921X_UDF_TYPE_L4,
.offset = 1,
.key = match.key->code,
.mask = match.mask->code,
};
udfs_cnt++;
}
}
}
/* Compile UDFs */
for (unsigned int i = 0; i < udfs_cnt; i++) {
unsigned int o;
u32 mask;
u32 key;
for (o = 0; o < ARRAY_SIZE(yt921x_udf_maps); o++)
if (udfs[i].type == yt921x_udf_maps[o].type &&
(udfs[i].offset == yt921x_udf_maps[o].offset ||
udfs[i].offset == yt921x_udf_maps[o].offset + 1))
break;
if (WARN_ON(o == ARRAY_SIZE(yt921x_udf_maps)))
return -EOPNOTSUPP;
entry = yt921x_acl_entries_find(entries, &size,
YT921X_ACL_TYPE_UDF0 + o / 2);
if (!entry)
goto err;
key = udfs[i].key;
mask = udfs[i].mask;
if (udfs[i].offset == yt921x_udf_maps[o].offset) {
key <<= 8;
mask <<= 8;
}
if (!(o % 2)) {
key <<= 16;
mask <<= 16;
}
entry->key[0] |= key;
entry->mask[0] |= mask;
}
if (!size) {
NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen");
return -EOPNOTSUPP;
@ -1579,5 +1699,13 @@ int yt921x_chip_setup_acl(struct yt921x_priv *priv)
if (res)
return res;
for (unsigned int o = 0; o < ARRAY_SIZE(yt921x_udf_maps); o++) {
ctrl = YT921X_UDF_CTRL_UDF_OFFSET(yt921x_udf_maps[o].offset) |
YT921X_UDF_CTRL_UDF_TYPE(yt921x_udf_maps[o].type);
res = yt921x_reg_write(priv, YT921X_UDFn_CTRL(o), ctrl);
if (res)
return res;
}
return 0;
}

View File

@ -118,6 +118,9 @@ enum yt921x_acl_type {
YT921X_ACL_TYPE_IPV6_SA3,
YT921X_ACL_TYPE_MISC,
YT921X_ACL_TYPE_L4,
/* Each User Defined Filter entry contains 2 UDF windows (UDF_CTRL 2i
* and 2i+1)
*/
YT921X_ACL_TYPE_UDF0,
YT921X_ACL_TYPE_UDF1,
YT921X_ACL_TYPE_UDF2,
@ -255,12 +258,15 @@ enum yt921x_l4_type {
#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x))
#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7)
#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x))
#define YT921X_UDF_CTRL_UDF_TYPE_ETH YT921X_UDF_CTRL_UDF_TYPE(0)
#define YT921X_UDF_CTRL_UDF_TYPE_L3 YT921X_UDF_CTRL_UDF_TYPE(1)
#define YT921X_UDF_CTRL_UDF_TYPE_L4 YT921X_UDF_CTRL_UDF_TYPE(2)
#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0)
#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x))
enum yt921x_udf_type {
YT921X_UDF_TYPE_L2,
YT921X_UDF_TYPE_L3,
YT921X_UDF_TYPE_L4,
};
#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port))
#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */
#define YT921X_METER_SLOT 0x220104