xref: /linux/net/netfilter/nft_cmp.c (revision 52338415)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
4  *
5  * Development of this code funded by Astaro AG (http://www.astaro.com/)
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/netlink.h>
12 #include <linux/netfilter.h>
13 #include <linux/netfilter/nf_tables.h>
14 #include <net/netfilter/nf_tables_core.h>
15 #include <net/netfilter/nf_tables_offload.h>
16 #include <net/netfilter/nf_tables.h>
17 
18 struct nft_cmp_expr {
19 	struct nft_data		data;
20 	enum nft_registers	sreg:8;
21 	u8			len;
22 	enum nft_cmp_ops	op:8;
23 };
24 
25 void nft_cmp_eval(const struct nft_expr *expr,
26 		  struct nft_regs *regs,
27 		  const struct nft_pktinfo *pkt)
28 {
29 	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
30 	int d;
31 
32 	d = memcmp(&regs->data[priv->sreg], &priv->data, priv->len);
33 	switch (priv->op) {
34 	case NFT_CMP_EQ:
35 		if (d != 0)
36 			goto mismatch;
37 		break;
38 	case NFT_CMP_NEQ:
39 		if (d == 0)
40 			goto mismatch;
41 		break;
42 	case NFT_CMP_LT:
43 		if (d == 0)
44 			goto mismatch;
45 		/* fall through */
46 	case NFT_CMP_LTE:
47 		if (d > 0)
48 			goto mismatch;
49 		break;
50 	case NFT_CMP_GT:
51 		if (d == 0)
52 			goto mismatch;
53 		/* fall through */
54 	case NFT_CMP_GTE:
55 		if (d < 0)
56 			goto mismatch;
57 		break;
58 	}
59 	return;
60 
61 mismatch:
62 	regs->verdict.code = NFT_BREAK;
63 }
64 
65 static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
66 	[NFTA_CMP_SREG]		= { .type = NLA_U32 },
67 	[NFTA_CMP_OP]		= { .type = NLA_U32 },
68 	[NFTA_CMP_DATA]		= { .type = NLA_NESTED },
69 };
70 
71 static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
72 			const struct nlattr * const tb[])
73 {
74 	struct nft_cmp_expr *priv = nft_expr_priv(expr);
75 	struct nft_data_desc desc;
76 	int err;
77 
78 	err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
79 			    tb[NFTA_CMP_DATA]);
80 	if (err < 0)
81 		return err;
82 
83 	priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
84 	err = nft_validate_register_load(priv->sreg, desc.len);
85 	if (err < 0)
86 		return err;
87 
88 	priv->op  = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
89 	priv->len = desc.len;
90 	return 0;
91 }
92 
93 static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
94 {
95 	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
96 
97 	if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg))
98 		goto nla_put_failure;
99 	if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op)))
100 		goto nla_put_failure;
101 
102 	if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data,
103 			  NFT_DATA_VALUE, priv->len) < 0)
104 		goto nla_put_failure;
105 	return 0;
106 
107 nla_put_failure:
108 	return -1;
109 }
110 
111 static int __nft_cmp_offload(struct nft_offload_ctx *ctx,
112 			     struct nft_flow_rule *flow,
113 			     const struct nft_cmp_expr *priv)
114 {
115 	struct nft_offload_reg *reg = &ctx->regs[priv->sreg];
116 	u8 *mask = (u8 *)&flow->match.mask;
117 	u8 *key = (u8 *)&flow->match.key;
118 
119 	if (priv->op != NFT_CMP_EQ)
120 		return -EOPNOTSUPP;
121 
122 	memcpy(key + reg->offset, &priv->data, priv->len);
123 	memcpy(mask + reg->offset, &reg->mask, priv->len);
124 
125 	flow->match.dissector.used_keys |= BIT(reg->key);
126 	flow->match.dissector.offset[reg->key] = reg->base_offset;
127 
128 	nft_offload_update_dependency(ctx, &priv->data, priv->len);
129 
130 	return 0;
131 }
132 
133 static int nft_cmp_offload(struct nft_offload_ctx *ctx,
134 			   struct nft_flow_rule *flow,
135 			   const struct nft_expr *expr)
136 {
137 	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
138 
139 	return __nft_cmp_offload(ctx, flow, priv);
140 }
141 
142 static const struct nft_expr_ops nft_cmp_ops = {
143 	.type		= &nft_cmp_type,
144 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
145 	.eval		= nft_cmp_eval,
146 	.init		= nft_cmp_init,
147 	.dump		= nft_cmp_dump,
148 	.offload	= nft_cmp_offload,
149 };
150 
151 static int nft_cmp_fast_init(const struct nft_ctx *ctx,
152 			     const struct nft_expr *expr,
153 			     const struct nlattr * const tb[])
154 {
155 	struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
156 	struct nft_data_desc desc;
157 	struct nft_data data;
158 	u32 mask;
159 	int err;
160 
161 	err = nft_data_init(NULL, &data, sizeof(data), &desc,
162 			    tb[NFTA_CMP_DATA]);
163 	if (err < 0)
164 		return err;
165 
166 	priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]);
167 	err = nft_validate_register_load(priv->sreg, desc.len);
168 	if (err < 0)
169 		return err;
170 
171 	desc.len *= BITS_PER_BYTE;
172 	mask = nft_cmp_fast_mask(desc.len);
173 
174 	priv->data = data.data[0] & mask;
175 	priv->len  = desc.len;
176 	return 0;
177 }
178 
179 static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx,
180 				struct nft_flow_rule *flow,
181 				const struct nft_expr *expr)
182 {
183 	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
184 	struct nft_cmp_expr cmp = {
185 		.data	= {
186 			.data	= {
187 				[0] = priv->data,
188 			},
189 		},
190 		.sreg	= priv->sreg,
191 		.len	= priv->len / BITS_PER_BYTE,
192 		.op	= NFT_CMP_EQ,
193 	};
194 
195 	return __nft_cmp_offload(ctx, flow, &cmp);
196 }
197 
198 static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
199 {
200 	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
201 	struct nft_data data;
202 
203 	if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg))
204 		goto nla_put_failure;
205 	if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
206 		goto nla_put_failure;
207 
208 	data.data[0] = priv->data;
209 	if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
210 			  NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
211 		goto nla_put_failure;
212 	return 0;
213 
214 nla_put_failure:
215 	return -1;
216 }
217 
218 const struct nft_expr_ops nft_cmp_fast_ops = {
219 	.type		= &nft_cmp_type,
220 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
221 	.eval		= NULL,	/* inlined */
222 	.init		= nft_cmp_fast_init,
223 	.dump		= nft_cmp_fast_dump,
224 	.offload	= nft_cmp_fast_offload,
225 };
226 
227 static const struct nft_expr_ops *
228 nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
229 {
230 	struct nft_data_desc desc;
231 	struct nft_data data;
232 	enum nft_cmp_ops op;
233 	int err;
234 
235 	if (tb[NFTA_CMP_SREG] == NULL ||
236 	    tb[NFTA_CMP_OP] == NULL ||
237 	    tb[NFTA_CMP_DATA] == NULL)
238 		return ERR_PTR(-EINVAL);
239 
240 	op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
241 	switch (op) {
242 	case NFT_CMP_EQ:
243 	case NFT_CMP_NEQ:
244 	case NFT_CMP_LT:
245 	case NFT_CMP_LTE:
246 	case NFT_CMP_GT:
247 	case NFT_CMP_GTE:
248 		break;
249 	default:
250 		return ERR_PTR(-EINVAL);
251 	}
252 
253 	err = nft_data_init(NULL, &data, sizeof(data), &desc,
254 			    tb[NFTA_CMP_DATA]);
255 	if (err < 0)
256 		return ERR_PTR(err);
257 
258 	if (desc.type != NFT_DATA_VALUE) {
259 		err = -EINVAL;
260 		goto err1;
261 	}
262 
263 	if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
264 		return &nft_cmp_fast_ops;
265 
266 	return &nft_cmp_ops;
267 err1:
268 	nft_data_release(&data, desc.type);
269 	return ERR_PTR(-EINVAL);
270 }
271 
272 struct nft_expr_type nft_cmp_type __read_mostly = {
273 	.name		= "cmp",
274 	.select_ops	= nft_cmp_select_ops,
275 	.policy		= nft_cmp_policy,
276 	.maxattr	= NFTA_CMP_MAX,
277 	.owner		= THIS_MODULE,
278 };
279