diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 2b3e6a2309aa..0b105264cc4f 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -259,11 +259,15 @@ struct nft_set_iter { * @klen: key length * @dlen: data length * @size: number of set elements + * @subkeylen: element subkey lengths + * @num_subkeys: number of subkeys in element */ struct nft_set_desc { unsigned int klen; unsigned int dlen; unsigned int size; + u8 subkey_len[NFT_REG32_COUNT]; + u8 num_subkeys; }; /** diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 79ab18b218be..d8ea2e72c960 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -48,6 +48,7 @@ enum nft_registers { #define NFT_REG_SIZE 16 #define NFT_REG32_SIZE 4 +#define NFT_REG32_COUNT (NFT_REG32_15 - NFT_REG32_00 + 1) /** * enum nft_verdicts - nf_tables internal verdicts @@ -298,13 +299,27 @@ enum nft_set_policies { }; /** + * enum nft_set_subkey_attributes - subkeys for multiple ranged fields + * + * @NFTA_SET_SUBKEY_LEN: length of single field, in bits (NLA_U32) + */ +enum nft_set_subkey_attributes { + NFTA_SET_SUBKEY_UNSPEC, + NFTA_SET_SUBKEY_LEN, + __NFTA_SET_SUBKEY_MAX +}; +#define NFTA_SET_SUBKEY_MAX (__NFTA_SET_SUBKEY_MAX - 1) + +/** * enum nft_set_desc_attributes - set element description * * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32) + * @NFTA_SET_DESC_SUBKEYS: element subkeys in set (NLA_NESTED) */ enum nft_set_desc_attributes { NFTA_SET_DESC_UNSPEC, NFTA_SET_DESC_SIZE, + NFTA_SET_DESC_SUBKEYS, __NFTA_SET_DESC_MAX }; #define NFTA_SET_DESC_MAX (__NFTA_SET_DESC_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b5051f4dbb26..1de97ec8d73d 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3357,6 +3357,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 }, + [NFTA_SET_DESC_SUBKEYS] = { .type = NLA_NESTED }, }; static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net, @@ -3763,6 +3764,51 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk, return err; } +static const struct nla_policy nft_subkey_policy[NFTA_SET_SUBKEY_MAX + 1] = { + [NFTA_SET_SUBKEY_LEN] = { .type = NLA_U32 }, +}; + +static int nft_set_desc_subkey_parse(const struct nlattr *attr, + struct nft_set_desc *desc) +{ + struct nlattr *tb[NFTA_SET_SUBKEY_MAX + 1]; + int err; + + err = nla_parse_nested_deprecated(tb, NFTA_SET_SUBKEY_MAX, attr, + nft_subkey_policy, NULL); + if (err < 0) + return err; + + if (!tb[NFTA_SET_SUBKEY_LEN]) + return -EINVAL; + + desc->subkey_len[desc->num_subkeys++] = + ntohl(nla_get_be32(tb[NFTA_SET_SUBKEY_LEN])); + + return 0; +} + +static int nft_set_desc_subkeys(struct nft_set_desc *desc, + const struct nlattr *nla) +{ + struct nlattr *attr; + int rem, err; + + nla_for_each_nested(attr, nla, rem) { + if (nla_type(attr) != NFTA_LIST_ELEM) + return -EINVAL; + + if (desc->num_subkeys >= NFT_REG32_COUNT) + return -E2BIG; + + err = nft_set_desc_subkey_parse(attr, desc); + if (err < 0) + return err; + } + + return 0; +} + static int nf_tables_set_desc_parse(struct nft_set_desc *desc, const struct nlattr *nla) { @@ -3776,8 +3822,10 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc, if (da[NFTA_SET_DESC_SIZE] != NULL) desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE])); + if (da[NFTA_SET_DESC_SUBKEYS]) + err = nft_set_desc_subkeys(desc, da[NFTA_SET_DESC_SUBKEYS]); - return 0; + return err; } static int nf_tables_newset(struct net *net, struct sock *nlsk,