Blame view

net/netfilter/nft_cmp.c 6.85 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
96518518c   Patrick McHardy   netfilter: add nf...
2
  /*
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
3
   * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
96518518c   Patrick McHardy   netfilter: add nf...
4
   *
96518518c   Patrick McHardy   netfilter: add nf...
5
6
7
8
9
10
11
12
   * Development of this code funded by Astaro AG (http://www.astaro.com/)
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
8819efc94   Pablo Neira Ayuso   netfilter: nf_tab...
13
  #include <linux/if_arp.h>
96518518c   Patrick McHardy   netfilter: add nf...
14
15
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables_core.h>
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
16
  #include <net/netfilter/nf_tables_offload.h>
96518518c   Patrick McHardy   netfilter: add nf...
17
18
19
20
21
22
23
24
  #include <net/netfilter/nf_tables.h>
  
  struct nft_cmp_expr {
  	struct nft_data		data;
  	enum nft_registers	sreg:8;
  	u8			len;
  	enum nft_cmp_ops	op:8;
  };
10870dd89   Florian Westphal   netfilter: nf_tab...
25
26
27
  void nft_cmp_eval(const struct nft_expr *expr,
  		  struct nft_regs *regs,
  		  const struct nft_pktinfo *pkt)
96518518c   Patrick McHardy   netfilter: add nf...
28
29
30
  {
  	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
  	int d;
e562d860d   Patrick McHardy   netfilter: nf_tab...
31
  	d = memcmp(&regs->data[priv->sreg], &priv->data, priv->len);
96518518c   Patrick McHardy   netfilter: add nf...
32
33
34
35
36
37
38
39
40
41
42
43
  	switch (priv->op) {
  	case NFT_CMP_EQ:
  		if (d != 0)
  			goto mismatch;
  		break;
  	case NFT_CMP_NEQ:
  		if (d == 0)
  			goto mismatch;
  		break;
  	case NFT_CMP_LT:
  		if (d == 0)
  			goto mismatch;
954d82979   Gustavo A. R. Silva   netfilter: Use fa...
44
  		fallthrough;
96518518c   Patrick McHardy   netfilter: add nf...
45
46
47
48
49
50
51
  	case NFT_CMP_LTE:
  		if (d > 0)
  			goto mismatch;
  		break;
  	case NFT_CMP_GT:
  		if (d == 0)
  			goto mismatch;
954d82979   Gustavo A. R. Silva   netfilter: Use fa...
52
  		fallthrough;
96518518c   Patrick McHardy   netfilter: add nf...
53
54
55
56
57
58
59
60
  	case NFT_CMP_GTE:
  		if (d < 0)
  			goto mismatch;
  		break;
  	}
  	return;
  
  mismatch:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
61
  	regs->verdict.code = NFT_BREAK;
96518518c   Patrick McHardy   netfilter: add nf...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  }
  
  static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
  	[NFTA_CMP_SREG]		= { .type = NLA_U32 },
  	[NFTA_CMP_OP]		= { .type = NLA_U32 },
  	[NFTA_CMP_DATA]		= { .type = NLA_NESTED },
  };
  
  static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  			const struct nlattr * const tb[])
  {
  	struct nft_cmp_expr *priv = nft_expr_priv(expr);
  	struct nft_data_desc desc;
  	int err;
d0a11fc3d   Patrick McHardy   netfilter: nf_tab...
76
77
  	err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
  			    tb[NFTA_CMP_DATA]);
fa5950e49   Florian Westphal   netfilter: nf_tab...
78
79
  	if (err < 0)
  		return err;
96518518c   Patrick McHardy   netfilter: add nf...
80

0d2c96af7   Pablo Neira Ayuso   netfilter: nf_tab...
81
82
83
84
85
  	if (desc.type != NFT_DATA_VALUE) {
  		err = -EINVAL;
  		nft_data_release(&priv->data, desc.type);
  		return err;
  	}
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
86
  	priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
87
88
89
90
91
  	err = nft_validate_register_load(priv->sreg, desc.len);
  	if (err < 0)
  		return err;
  
  	priv->op  = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
96518518c   Patrick McHardy   netfilter: add nf...
92
93
94
95
96
97
98
  	priv->len = desc.len;
  	return 0;
  }
  
  static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
99
  	if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg))
96518518c   Patrick McHardy   netfilter: add nf...
100
101
102
103
104
105
106
107
108
109
110
111
  		goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op)))
  		goto nla_put_failure;
  
  	if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data,
  			  NFT_DATA_VALUE, priv->len) < 0)
  		goto nla_put_failure;
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
112
113
114
115
116
117
118
  static int __nft_cmp_offload(struct nft_offload_ctx *ctx,
  			     struct nft_flow_rule *flow,
  			     const struct nft_cmp_expr *priv)
  {
  	struct nft_offload_reg *reg = &ctx->regs[priv->sreg];
  	u8 *mask = (u8 *)&flow->match.mask;
  	u8 *key = (u8 *)&flow->match.key;
a5d45bc0d   Pablo Neira Ayuso   netfilter: nftabl...
119
  	if (priv->op != NFT_CMP_EQ || priv->len > reg->len)
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
120
  		return -EOPNOTSUPP;
a5d45bc0d   Pablo Neira Ayuso   netfilter: nftabl...
121
122
  	memcpy(key + reg->offset, &priv->data, reg->len);
  	memcpy(mask + reg->offset, &reg->mask, reg->len);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
123
124
125
  
  	flow->match.dissector.used_keys |= BIT(reg->key);
  	flow->match.dissector.offset[reg->key] = reg->base_offset;
8819efc94   Pablo Neira Ayuso   netfilter: nf_tab...
126
127
128
129
  	if (reg->key == FLOW_DISSECTOR_KEY_META &&
  	    reg->offset == offsetof(struct nft_flow_key, meta.ingress_iftype) &&
  	    nft_reg_load16(priv->data.data) != ARPHRD_ETHER)
  		return -EOPNOTSUPP;
a5d45bc0d   Pablo Neira Ayuso   netfilter: nftabl...
130
  	nft_offload_update_dependency(ctx, &priv->data, reg->len);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
131
132
133
134
135
136
137
138
139
140
141
142
  
  	return 0;
  }
  
  static int nft_cmp_offload(struct nft_offload_ctx *ctx,
  			   struct nft_flow_rule *flow,
  			   const struct nft_expr *expr)
  {
  	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
  
  	return __nft_cmp_offload(ctx, flow, priv);
  }
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
143
144
  static const struct nft_expr_ops nft_cmp_ops = {
  	.type		= &nft_cmp_type,
96518518c   Patrick McHardy   netfilter: add nf...
145
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
96518518c   Patrick McHardy   netfilter: add nf...
146
147
148
  	.eval		= nft_cmp_eval,
  	.init		= nft_cmp_init,
  	.dump		= nft_cmp_dump,
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
149
  	.offload	= nft_cmp_offload,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
150
  };
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
151
152
153
154
155
156
157
  static int nft_cmp_fast_init(const struct nft_ctx *ctx,
  			     const struct nft_expr *expr,
  			     const struct nlattr * const tb[])
  {
  	struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
  	struct nft_data_desc desc;
  	struct nft_data data;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
158
  	int err;
d0a11fc3d   Patrick McHardy   netfilter: nf_tab...
159
160
  	err = nft_data_init(NULL, &data, sizeof(data), &desc,
  			    tb[NFTA_CMP_DATA]);
fa5950e49   Florian Westphal   netfilter: nf_tab...
161
162
  	if (err < 0)
  		return err;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
163

b1c96ed37   Patrick McHardy   netfilter: nf_tab...
164
  	priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
165
166
167
168
169
  	err = nft_validate_register_load(priv->sreg, desc.len);
  	if (err < 0)
  		return err;
  
  	desc.len *= BITS_PER_BYTE;
d07db9884   Patrick McHardy   netfilter: nf_tab...
170

5f48846da   Phil Sutter   netfilter: nf_tab...
171
172
  	priv->mask = nft_cmp_fast_mask(desc.len);
  	priv->data = data.data[0] & priv->mask;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
173
  	priv->len  = desc.len;
5f48846da   Phil Sutter   netfilter: nf_tab...
174
  	priv->inv  = ntohl(nla_get_be32(tb[NFTA_CMP_OP])) != NFT_CMP_EQ;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
175
176
  	return 0;
  }
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
177
178
179
180
181
182
183
184
185
186
187
188
189
  static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx,
  				struct nft_flow_rule *flow,
  				const struct nft_expr *expr)
  {
  	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
  	struct nft_cmp_expr cmp = {
  		.data	= {
  			.data	= {
  				[0] = priv->data,
  			},
  		},
  		.sreg	= priv->sreg,
  		.len	= priv->len / BITS_PER_BYTE,
5f48846da   Phil Sutter   netfilter: nf_tab...
190
  		.op	= priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ,
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
191
192
193
194
  	};
  
  	return __nft_cmp_offload(ctx, flow, &cmp);
  }
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
195
196
197
  static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
5f48846da   Phil Sutter   netfilter: nf_tab...
198
  	enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
199
  	struct nft_data data;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
200
  	if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg))
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
201
  		goto nla_put_failure;
5f48846da   Phil Sutter   netfilter: nf_tab...
202
  	if (nla_put_be32(skb, NFTA_CMP_OP, htonl(op)))
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  		goto nla_put_failure;
  
  	data.data[0] = priv->data;
  	if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
  			  NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
  		goto nla_put_failure;
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
  
  const struct nft_expr_ops nft_cmp_fast_ops = {
  	.type		= &nft_cmp_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
  	.eval		= NULL,	/* inlined */
  	.init		= nft_cmp_fast_init,
  	.dump		= nft_cmp_fast_dump,
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
221
  	.offload	= nft_cmp_fast_offload,
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
222
  };
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
223
224
  static const struct nft_expr_ops *
  nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
225
226
227
  {
  	struct nft_data_desc desc;
  	struct nft_data data;
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
228
229
230
231
232
233
234
  	enum nft_cmp_ops op;
  	int err;
  
  	if (tb[NFTA_CMP_SREG] == NULL ||
  	    tb[NFTA_CMP_OP] == NULL ||
  	    tb[NFTA_CMP_DATA] == NULL)
  		return ERR_PTR(-EINVAL);
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
235
236
237
238
239
240
241
242
243
244
245
246
  	op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
  	switch (op) {
  	case NFT_CMP_EQ:
  	case NFT_CMP_NEQ:
  	case NFT_CMP_LT:
  	case NFT_CMP_LTE:
  	case NFT_CMP_GT:
  	case NFT_CMP_GTE:
  		break;
  	default:
  		return ERR_PTR(-EINVAL);
  	}
d0a11fc3d   Patrick McHardy   netfilter: nf_tab...
247
248
  	err = nft_data_init(NULL, &data, sizeof(data), &desc,
  			    tb[NFTA_CMP_DATA]);
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
249
250
  	if (err < 0)
  		return ERR_PTR(err);
71df14b0c   Pablo Neira Ayuso   netfilter: nf_tab...
251
252
253
254
  	if (desc.type != NFT_DATA_VALUE) {
  		err = -EINVAL;
  		goto err1;
  	}
5f48846da   Phil Sutter   netfilter: nf_tab...
255
  	if (desc.len <= sizeof(u32) && (op == NFT_CMP_EQ || op == NFT_CMP_NEQ))
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
256
  		return &nft_cmp_fast_ops;
71df14b0c   Pablo Neira Ayuso   netfilter: nf_tab...
257
258
259
  
  	return &nft_cmp_ops;
  err1:
591054469   Pablo Neira Ayuso   netfilter: nf_tab...
260
  	nft_data_release(&data, desc.type);
71df14b0c   Pablo Neira Ayuso   netfilter: nf_tab...
261
  	return ERR_PTR(-EINVAL);
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
262
  }
4e24877e6   Liping Zhang   netfilter: nf_tab...
263
  struct nft_expr_type nft_cmp_type __read_mostly = {
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
264
  	.name		= "cmp",
cb7dbfd03   Patrick McHardy   netfilter: nf_tab...
265
  	.select_ops	= nft_cmp_select_ops,
96518518c   Patrick McHardy   netfilter: add nf...
266
267
  	.policy		= nft_cmp_policy,
  	.maxattr	= NFTA_CMP_MAX,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
268
  	.owner		= THIS_MODULE,
96518518c   Patrick McHardy   netfilter: add nf...
269
  };