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

netfilter: nf_tables: variable sized set element keys / data

This patch changes sets to support variable sized set element keys / data
up to 64 bytes each by using variable sized set extensions. This allows
to use concatenations with bigger data items suchs as IPv6 addresses.

As a side effect, small keys/data now don't require the full 16 bytes
of struct nft_data anymore but just the space they need.

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
7d740264 d0a11fc3

+23 -19
+4 -1
include/net/netfilter/nf_tables.h
··· 158 158 * @priv: element private data and extensions 159 159 */ 160 160 struct nft_set_elem { 161 - struct nft_data key; 161 + union { 162 + u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)]; 163 + struct nft_data val; 164 + } key; 162 165 void *priv; 163 166 }; 164 167
+3
include/uapi/linux/netfilter/nf_tables.h
··· 388 388 }; 389 389 #define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) 390 390 391 + /* Maximum length of a value */ 392 + #define NFT_DATA_VALUE_MAXLEN 64 393 + 391 394 /** 392 395 * enum nft_verdict_attributes - nf_tables verdict netlink attributes 393 396 *
+12 -15
net/netfilter/nf_tables_api.c
··· 2608 2608 } 2609 2609 2610 2610 desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); 2611 - if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data)) 2611 + if (desc.klen == 0 || desc.klen > NFT_DATA_VALUE_MAXLEN) 2612 2612 return -EINVAL; 2613 2613 2614 2614 flags = 0; ··· 2634 2634 if (nla[NFTA_SET_DATA_LEN] == NULL) 2635 2635 return -EINVAL; 2636 2636 desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); 2637 - if (desc.dlen == 0 || 2638 - desc.dlen > FIELD_SIZEOF(struct nft_data, data)) 2637 + if (desc.dlen == 0 || desc.dlen > NFT_DATA_VALUE_MAXLEN) 2639 2638 return -EINVAL; 2640 2639 } else 2641 - desc.dlen = sizeof(struct nft_data); 2640 + desc.dlen = sizeof(struct nft_verdict); 2642 2641 } else if (flags & NFT_SET_MAP) 2643 2642 return -EINVAL; 2644 2643 ··· 2853 2854 2854 2855 const struct nft_set_ext_type nft_set_ext_types[] = { 2855 2856 [NFT_SET_EXT_KEY] = { 2856 - .len = sizeof(struct nft_data), 2857 - .align = __alignof__(struct nft_data), 2857 + .align = __alignof__(u32), 2858 2858 }, 2859 2859 [NFT_SET_EXT_DATA] = { 2860 - .len = sizeof(struct nft_data), 2861 - .align = __alignof__(struct nft_data), 2860 + .align = __alignof__(u32), 2862 2861 }, 2863 2862 [NFT_SET_EXT_FLAGS] = { 2864 2863 .len = sizeof(u8), ··· 3296 3299 timeout = set->timeout; 3297 3300 } 3298 3301 3299 - err = nft_data_init(ctx, &elem.key, sizeof(elem.key), &d1, 3302 + err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &d1, 3300 3303 nla[NFTA_SET_ELEM_KEY]); 3301 3304 if (err < 0) 3302 3305 goto err1; ··· 3304 3307 if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) 3305 3308 goto err2; 3306 3309 3307 - nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); 3310 + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, d1.len); 3308 3311 if (timeout > 0) { 3309 3312 nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION); 3310 3313 if (timeout != set->timeout) ··· 3339 3342 goto err3; 3340 3343 } 3341 3344 3342 - nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA); 3345 + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len); 3343 3346 } 3344 3347 3345 3348 /* The full maximum length of userdata can exceed the maximum ··· 3355 3358 } 3356 3359 3357 3360 err = -ENOMEM; 3358 - elem.priv = nft_set_elem_init(set, &tmpl, elem.key.data, data.data, 3361 + elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, data.data, 3359 3362 timeout, GFP_KERNEL); 3360 3363 if (elem.priv == NULL) 3361 3364 goto err3; ··· 3390 3393 if (nla[NFTA_SET_ELEM_DATA] != NULL) 3391 3394 nft_data_uninit(&data, d2.type); 3392 3395 err2: 3393 - nft_data_uninit(&elem.key, d1.type); 3396 + nft_data_uninit(&elem.key.val, d1.type); 3394 3397 err1: 3395 3398 return err; 3396 3399 } ··· 3457 3460 if (nla[NFTA_SET_ELEM_KEY] == NULL) 3458 3461 goto err1; 3459 3462 3460 - err = nft_data_init(ctx, &elem.key, sizeof(elem.key), &desc, 3463 + err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc, 3461 3464 nla[NFTA_SET_ELEM_KEY]); 3462 3465 if (err < 0) 3463 3466 goto err1; ··· 3485 3488 err3: 3486 3489 kfree(trans); 3487 3490 err2: 3488 - nft_data_uninit(&elem.key, desc.type); 3491 + nft_data_uninit(&elem.key.val, desc.type); 3489 3492 err1: 3490 3493 return err; 3491 3494 }
+2 -2
net/netfilter/nft_hash.c
··· 133 133 struct nft_hash_cmp_arg arg = { 134 134 .genmask = nft_genmask_next(read_pnet(&set->pnet)), 135 135 .set = set, 136 - .key = elem->key.data, 136 + .key = elem->key.val.data, 137 137 }; 138 138 139 139 return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, ··· 157 157 struct nft_hash_cmp_arg arg = { 158 158 .genmask = nft_genmask_next(read_pnet(&set->pnet)), 159 159 .set = set, 160 - .key = elem->key.data, 160 + .key = elem->key.val.data, 161 161 }; 162 162 163 163 rcu_read_lock();
+2 -1
net/netfilter/nft_rbtree.c
··· 152 152 while (parent != NULL) { 153 153 rbe = rb_entry(parent, struct nft_rbtree_elem, node); 154 154 155 - d = memcmp(nft_set_ext_key(&rbe->ext), &elem->key, set->klen); 155 + d = memcmp(nft_set_ext_key(&rbe->ext), &elem->key.val, 156 + set->klen); 156 157 if (d < 0) 157 158 parent = parent->rb_left; 158 159 else if (d > 0)