Skip to content

Commit 49499c3

Browse files
kaberummakynes
authored andcommitted
netfilter: nf_tables: switch registers to 32 bit addressing
Switch the nf_tables registers from 128 bit addressing to 32 bit addressing to support so called concatenations, where multiple values can be concatenated over multiple registers for O(1) exact matches of multiple dimensions using sets. The old register values are mapped to areas of 128 bits for compatibility. When dumping register numbers, values are expressed using the old values if they refer to the beginning of a 128 bit area for compatibility. To support concatenations, register loads of less than a full 32 bit value need to be padded. This mainly affects the payload and exthdr expressions, which both unconditionally zero the last word before copying the data. Userspace fully passes the testsuite using both old and new register addressing. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent b1c96ed commit 49499c3

File tree

16 files changed

+110
-48
lines changed

16 files changed

+110
-48
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,15 @@ struct nft_data {
6464
*/
6565
struct nft_regs {
6666
union {
67-
struct nft_data data[NFT_REG_MAX + 1];
67+
u32 data[20];
6868
struct nft_verdict verdict;
6969
};
7070
};
7171

72-
static inline void nft_data_copy(struct nft_data *dst,
73-
const struct nft_data *src)
72+
static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
73+
unsigned int len)
7474
{
75-
BUILD_BUG_ON(__alignof__(*dst) != __alignof__(u64));
76-
*(u64 *)&dst->data[0] = *(u64 *)&src->data[0];
77-
*(u64 *)&dst->data[2] = *(u64 *)&src->data[2];
75+
memcpy(dst, src, len);
7876
}
7977

8078
static inline void nft_data_debug(const struct nft_data *data)
@@ -502,8 +500,7 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
502500

503501
void *nft_set_elem_init(const struct nft_set *set,
504502
const struct nft_set_ext_tmpl *tmpl,
505-
const struct nft_data *key,
506-
const struct nft_data *data,
503+
const u32 *key, const u32 *data,
507504
u64 timeout, gfp_t gfp);
508505
void nft_set_elem_destroy(const struct nft_set *set, void *elem);
509506

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,45 @@
55
#define NFT_CHAIN_MAXNAMELEN 32
66
#define NFT_USERDATA_MAXLEN 256
77

8+
/**
9+
* enum nft_registers - nf_tables registers
10+
*
11+
* nf_tables used to have five registers: a verdict register and four data
12+
* registers of size 16. The data registers have been changed to 16 registers
13+
* of size 4. For compatibility reasons, the NFT_REG_[1-4] registers still
14+
* map to areas of size 16, the 4 byte registers are addressed using
15+
* NFT_REG32_00 - NFT_REG32_15.
16+
*/
817
enum nft_registers {
918
NFT_REG_VERDICT,
1019
NFT_REG_1,
1120
NFT_REG_2,
1221
NFT_REG_3,
1322
NFT_REG_4,
14-
__NFT_REG_MAX
23+
__NFT_REG_MAX,
24+
25+
NFT_REG32_00 = 8,
26+
MFT_REG32_01,
27+
NFT_REG32_02,
28+
NFT_REG32_03,
29+
NFT_REG32_04,
30+
NFT_REG32_05,
31+
NFT_REG32_06,
32+
NFT_REG32_07,
33+
NFT_REG32_08,
34+
NFT_REG32_09,
35+
NFT_REG32_10,
36+
NFT_REG32_11,
37+
NFT_REG32_12,
38+
NFT_REG32_13,
39+
NFT_REG32_14,
40+
NFT_REG32_15,
1541
};
1642
#define NFT_REG_MAX (__NFT_REG_MAX - 1)
1743

44+
#define NFT_REG_SIZE 16
45+
#define NFT_REG32_SIZE 4
46+
1847
/**
1948
* enum nft_verdicts - nf_tables internal verdicts
2049
*

net/bridge/netfilter/nft_meta_bridge.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
2424
{
2525
const struct nft_meta *priv = nft_expr_priv(expr);
2626
const struct net_device *in = pkt->in, *out = pkt->out;
27-
u32 *dest = &regs->data[priv->dreg].data[0];
27+
u32 *dest = &regs->data[priv->dreg];
2828
const struct net_bridge_port *p;
2929

3030
switch (priv->key) {

net/ipv4/netfilter/nft_redir_ipv4.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr,
2727
memset(&mr, 0, sizeof(mr));
2828
if (priv->sreg_proto_min) {
2929
mr.range[0].min.all =
30-
*(__be16 *)&regs->data[priv->sreg_proto_min].data[0];
30+
*(__be16 *)&regs->data[priv->sreg_proto_min];
3131
mr.range[0].max.all =
32-
*(__be16 *)&regs->data[priv->sreg_proto_max].data[0];
32+
*(__be16 *)&regs->data[priv->sreg_proto_max];
3333
mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
3434
}
3535

net/ipv6/netfilter/nft_redir_ipv6.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr,
2727
memset(&range, 0, sizeof(range));
2828
if (priv->sreg_proto_min) {
2929
range.min_proto.all =
30-
*(__be16 *)&regs->data[priv->sreg_proto_min].data[0];
30+
*(__be16 *)&regs->data[priv->sreg_proto_min],
3131
range.max_proto.all =
32-
*(__be16 *)&regs->data[priv->sreg_proto_max].data[0];
32+
*(__be16 *)&regs->data[priv->sreg_proto_max],
3333
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
3434
}
3535

net/netfilter/nf_tables_api.c

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3201,8 +3201,7 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
32013201

32023202
void *nft_set_elem_init(const struct nft_set *set,
32033203
const struct nft_set_ext_tmpl *tmpl,
3204-
const struct nft_data *key,
3205-
const struct nft_data *data,
3204+
const u32 *key, const u32 *data,
32063205
u64 timeout, gfp_t gfp)
32073206
{
32083207
struct nft_set_ext *ext;
@@ -3357,7 +3356,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
33573356
}
33583357

33593358
err = -ENOMEM;
3360-
elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data,
3359+
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.data, data.data,
33613360
timeout, GFP_KERNEL);
33623361
if (elem.priv == NULL)
33633362
goto err3;
@@ -4122,14 +4121,47 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
41224121
return 0;
41234122
}
41244123

4124+
/**
4125+
* nft_parse_register - parse a register value from a netlink attribute
4126+
*
4127+
* @attr: netlink attribute
4128+
*
4129+
* Parse and translate a register value from a netlink attribute.
4130+
* Registers used to be 128 bit wide, these register numbers will be
4131+
* mapped to the corresponding 32 bit register numbers.
4132+
*/
41254133
unsigned int nft_parse_register(const struct nlattr *attr)
41264134
{
4127-
return ntohl(nla_get_be32(attr));
4135+
unsigned int reg;
4136+
4137+
reg = ntohl(nla_get_be32(attr));
4138+
switch (reg) {
4139+
case NFT_REG_VERDICT...NFT_REG_4:
4140+
return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
4141+
default:
4142+
return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
4143+
}
41284144
}
41294145
EXPORT_SYMBOL_GPL(nft_parse_register);
41304146

4147+
/**
4148+
* nft_dump_register - dump a register value to a netlink attribute
4149+
*
4150+
* @skb: socket buffer
4151+
* @attr: attribute number
4152+
* @reg: register number
4153+
*
4154+
* Construct a netlink attribute containing the register number. For
4155+
* compatibility reasons, register numbers being a multiple of 4 are
4156+
* translated to the corresponding 128 bit register numbers.
4157+
*/
41314158
int nft_dump_register(struct sk_buff *skb, unsigned int attr, unsigned int reg)
41324159
{
4160+
if (reg % (NFT_REG_SIZE / NFT_REG32_SIZE) == 0)
4161+
reg = reg / (NFT_REG_SIZE / NFT_REG32_SIZE);
4162+
else
4163+
reg = reg - NFT_REG_SIZE / NFT_REG32_SIZE + NFT_REG32_00;
4164+
41334165
return nla_put_be32(skb, attr, htonl(reg));
41344166
}
41354167
EXPORT_SYMBOL_GPL(nft_dump_register);
@@ -4145,14 +4177,13 @@ EXPORT_SYMBOL_GPL(nft_dump_register);
41454177
*/
41464178
int nft_validate_register_load(enum nft_registers reg, unsigned int len)
41474179
{
4148-
if (reg <= NFT_REG_VERDICT)
4180+
if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
41494181
return -EINVAL;
4150-
if (reg > NFT_REG_MAX)
4151-
return -ERANGE;
41524182
if (len == 0)
41534183
return -EINVAL;
4154-
if (len > FIELD_SIZEOF(struct nft_data, data))
4184+
if (reg * NFT_REG32_SIZE + len > FIELD_SIZEOF(struct nft_regs, data))
41554185
return -ERANGE;
4186+
41564187
return 0;
41574188
}
41584189
EXPORT_SYMBOL_GPL(nft_validate_register_load);
@@ -4200,13 +4231,12 @@ int nft_validate_register_store(const struct nft_ctx *ctx,
42004231

42014232
return 0;
42024233
default:
4203-
if (reg < NFT_REG_1)
4234+
if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
42044235
return -EINVAL;
4205-
if (reg > NFT_REG_MAX)
4206-
return -ERANGE;
42074236
if (len == 0)
42084237
return -EINVAL;
4209-
if (len > FIELD_SIZEOF(struct nft_data, data))
4238+
if (reg * NFT_REG32_SIZE + len >
4239+
FIELD_SIZEOF(struct nft_regs, data))
42104240
return -ERANGE;
42114241

42124242
if (data != NULL && type != NFT_DATA_VALUE)

net/netfilter/nf_tables_core.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ static void nft_cmp_fast_eval(const struct nft_expr *expr,
7070
const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
7171
u32 mask = nft_cmp_fast_mask(priv->len);
7272

73-
if ((regs->data[priv->sreg].data[0] & mask) == priv->data)
73+
if ((regs->data[priv->sreg] & mask) == priv->data)
7474
return;
7575
regs->verdict.code = NFT_BREAK;
7676
}
@@ -81,7 +81,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
8181
{
8282
const struct nft_payload *priv = nft_expr_priv(expr);
8383
const struct sk_buff *skb = pkt->skb;
84-
u32 *dest = &regs->data[priv->dreg].data[0];
84+
u32 *dest = &regs->data[priv->dreg];
8585
unsigned char *ptr;
8686

8787
if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
@@ -94,6 +94,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
9494
if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
9595
return false;
9696

97+
*dest = 0;
9798
if (priv->len == 2)
9899
*(u16 *)dest = *(u16 *)ptr;
99100
else if (priv->len == 4)

net/netfilter/nft_bitwise.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ static void nft_bitwise_eval(const struct nft_expr *expr,
3030
const struct nft_pktinfo *pkt)
3131
{
3232
const struct nft_bitwise *priv = nft_expr_priv(expr);
33-
const u32 *src = &regs->data[priv->sreg].data[0];
34-
u32 *dst = &regs->data[priv->dreg].data[0];
33+
const u32 *src = &regs->data[priv->sreg];
34+
u32 *dst = &regs->data[priv->dreg];
3535
unsigned int i;
3636

3737
for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++)

net/netfilter/nft_byteorder.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ static void nft_byteorder_eval(const struct nft_expr *expr,
3030
const struct nft_pktinfo *pkt)
3131
{
3232
const struct nft_byteorder *priv = nft_expr_priv(expr);
33-
u32 *src = &regs->data[priv->sreg].data[0];
34-
u32 *dst = &regs->data[priv->dreg].data[0];
33+
u32 *src = &regs->data[priv->sreg];
34+
u32 *dst = &regs->data[priv->dreg];
3535
union { u32 u32; u16 u16; } *s, *d;
3636
unsigned int i;
3737

net/netfilter/nft_ct.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
3535
const struct nft_pktinfo *pkt)
3636
{
3737
const struct nft_ct *priv = nft_expr_priv(expr);
38-
u32 *dest = &regs->data[priv->dreg].data[0];
38+
u32 *dest = &regs->data[priv->dreg];
3939
enum ip_conntrack_info ctinfo;
4040
const struct nf_conn *ct;
4141
const struct nf_conn_help *help;
@@ -156,7 +156,7 @@ static void nft_ct_set_eval(const struct nft_expr *expr,
156156
const struct nft_ct *priv = nft_expr_priv(expr);
157157
struct sk_buff *skb = pkt->skb;
158158
#ifdef CONFIG_NF_CONNTRACK_MARK
159-
u32 value = regs->data[priv->sreg].data[0];
159+
u32 value = regs->data[priv->sreg];
160160
#endif
161161
enum ip_conntrack_info ctinfo;
162162
struct nf_conn *ct;

0 commit comments

Comments
 (0)