Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_tables: support optional userdata for set elements

Add an userdata set extension and allow the user to attach arbitrary
data to set elements. This is intended to hold TLV encoded data like
comments or DNS annotations that have no meaning to the kernel.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Patrick McHardy and committed by
Pablo Neira Ayuso
68e942e8 22fe54d5

+43
+7
include/net/netfilter/nf_tables.h
··· 350 350 * @NFT_SET_EXT_FLAGS: element flags 351 351 * @NFT_SET_EXT_TIMEOUT: element timeout 352 352 * @NFT_SET_EXT_EXPIRATION: element expiration time 353 + * @NFT_SET_EXT_USERDATA: user data associated with the element 353 354 * @NFT_SET_EXT_NUM: number of extension types 354 355 */ 355 356 enum nft_set_extensions { ··· 359 358 NFT_SET_EXT_FLAGS, 360 359 NFT_SET_EXT_TIMEOUT, 361 360 NFT_SET_EXT_EXPIRATION, 361 + NFT_SET_EXT_USERDATA, 362 362 NFT_SET_EXT_NUM 363 363 }; 364 364 ··· 464 462 static inline unsigned long *nft_set_ext_expiration(const struct nft_set_ext *ext) 465 463 { 466 464 return nft_set_ext(ext, NFT_SET_EXT_EXPIRATION); 465 + } 466 + 467 + static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext *ext) 468 + { 469 + return nft_set_ext(ext, NFT_SET_EXT_USERDATA); 467 470 } 468 471 469 472 static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
+2
include/uapi/linux/netfilter/nf_tables.h
··· 292 292 * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) 293 293 * @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64) 294 294 * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64) 295 + * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY) 295 296 */ 296 297 enum nft_set_elem_attributes { 297 298 NFTA_SET_ELEM_UNSPEC, ··· 301 300 NFTA_SET_ELEM_FLAGS, 302 301 NFTA_SET_ELEM_TIMEOUT, 303 302 NFTA_SET_ELEM_EXPIRATION, 303 + NFTA_SET_ELEM_USERDATA, 304 304 __NFTA_SET_ELEM_MAX 305 305 }; 306 306 #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
+34
net/netfilter/nf_tables_api.c
··· 2872 2872 .len = sizeof(unsigned long), 2873 2873 .align = __alignof__(unsigned long), 2874 2874 }, 2875 + [NFT_SET_EXT_USERDATA] = { 2876 + .len = sizeof(struct nft_userdata), 2877 + .align = __alignof__(struct nft_userdata), 2878 + }, 2875 2879 }; 2876 2880 EXPORT_SYMBOL_GPL(nft_set_ext_types); 2877 2881 ··· 2888 2884 [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, 2889 2885 [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, 2890 2886 [NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 }, 2887 + [NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY, 2888 + .len = NFT_USERDATA_MAXLEN }, 2891 2889 }; 2892 2890 2893 2891 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { ··· 2967 2961 2968 2962 if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION, 2969 2963 cpu_to_be64(jiffies_to_msecs(expires)))) 2964 + goto nla_put_failure; 2965 + } 2966 + 2967 + if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) { 2968 + struct nft_userdata *udata; 2969 + 2970 + udata = nft_set_ext_userdata(ext); 2971 + if (nla_put(skb, NFTA_SET_ELEM_USERDATA, 2972 + udata->len + 1, udata->data)) 2970 2973 goto nla_put_failure; 2971 2974 } 2972 2975 ··· 3247 3232 struct nft_set_ext *ext; 3248 3233 struct nft_set_elem elem; 3249 3234 struct nft_set_binding *binding; 3235 + struct nft_userdata *udata; 3250 3236 struct nft_data data; 3251 3237 enum nft_registers dreg; 3252 3238 struct nft_trans *trans; 3253 3239 u64 timeout; 3254 3240 u32 flags; 3241 + u8 ulen; 3255 3242 int err; 3256 3243 3257 3244 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, ··· 3342 3325 nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA); 3343 3326 } 3344 3327 3328 + /* The full maximum length of userdata can exceed the maximum 3329 + * offset value (U8_MAX) for following extensions, therefor it 3330 + * must be the last extension added. 3331 + */ 3332 + ulen = 0; 3333 + if (nla[NFTA_SET_ELEM_USERDATA] != NULL) { 3334 + ulen = nla_len(nla[NFTA_SET_ELEM_USERDATA]); 3335 + if (ulen > 0) 3336 + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA, 3337 + ulen); 3338 + } 3339 + 3345 3340 err = -ENOMEM; 3346 3341 elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, 3347 3342 timeout, GFP_KERNEL); ··· 3363 3334 ext = nft_set_elem_ext(set, elem.priv); 3364 3335 if (flags) 3365 3336 *nft_set_ext_flags(ext) = flags; 3337 + if (ulen > 0) { 3338 + udata = nft_set_ext_userdata(ext); 3339 + udata->len = ulen - 1; 3340 + nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); 3341 + } 3366 3342 3367 3343 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); 3368 3344 if (trans == NULL)