xref: /openbmc/linux/net/netfilter/nft_xfrm.c (revision ef4290e6)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  * Generic part shared by ipv4 and ipv6 backends.
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/netlink.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter/nf_tables.h>
13 #include <net/netfilter/nf_tables_core.h>
14 #include <net/netfilter/nf_tables.h>
15 #include <linux/in.h>
16 #include <net/xfrm.h>
17 
18 static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
19 	[NFTA_XFRM_KEY]		= { .type = NLA_U32 },
20 	[NFTA_XFRM_DIR]		= { .type = NLA_U8 },
21 	[NFTA_XFRM_SPNUM]	= { .type = NLA_U32 },
22 	[NFTA_XFRM_DREG]	= { .type = NLA_U32 },
23 };
24 
25 struct nft_xfrm {
26 	enum nft_xfrm_keys	key:8;
27 	u8			dreg;
28 	u8			dir;
29 	u8			spnum;
30 	u8			len;
31 };
32 
33 static int nft_xfrm_get_init(const struct nft_ctx *ctx,
34 			     const struct nft_expr *expr,
35 			     const struct nlattr * const tb[])
36 {
37 	struct nft_xfrm *priv = nft_expr_priv(expr);
38 	unsigned int len = 0;
39 	u32 spnum = 0;
40 	u8 dir;
41 
42 	if (!tb[NFTA_XFRM_KEY] || !tb[NFTA_XFRM_DIR] || !tb[NFTA_XFRM_DREG])
43 		return -EINVAL;
44 
45 	switch (ctx->family) {
46 	case NFPROTO_IPV4:
47 	case NFPROTO_IPV6:
48 	case NFPROTO_INET:
49 		break;
50 	default:
51 		return -EOPNOTSUPP;
52 	}
53 
54 	priv->key = ntohl(nla_get_be32(tb[NFTA_XFRM_KEY]));
55 	switch (priv->key) {
56 	case NFT_XFRM_KEY_REQID:
57 	case NFT_XFRM_KEY_SPI:
58 		len = sizeof(u32);
59 		break;
60 	case NFT_XFRM_KEY_DADDR_IP4:
61 	case NFT_XFRM_KEY_SADDR_IP4:
62 		len = sizeof(struct in_addr);
63 		break;
64 	case NFT_XFRM_KEY_DADDR_IP6:
65 	case NFT_XFRM_KEY_SADDR_IP6:
66 		len = sizeof(struct in6_addr);
67 		break;
68 	default:
69 		return -EINVAL;
70 	}
71 
72 	dir = nla_get_u8(tb[NFTA_XFRM_DIR]);
73 	switch (dir) {
74 	case XFRM_POLICY_IN:
75 	case XFRM_POLICY_OUT:
76 		priv->dir = dir;
77 		break;
78 	default:
79 		return -EINVAL;
80 	}
81 
82 	if (tb[NFTA_XFRM_SPNUM])
83 		spnum = ntohl(nla_get_be32(tb[NFTA_XFRM_SPNUM]));
84 
85 	if (spnum >= XFRM_MAX_DEPTH)
86 		return -ERANGE;
87 
88 	priv->spnum = spnum;
89 
90 	priv->len = len;
91 	return nft_parse_register_store(ctx, tb[NFTA_XFRM_DREG], &priv->dreg,
92 					NULL, NFT_DATA_VALUE, len);
93 }
94 
95 /* Return true if key asks for daddr/saddr and current
96  * state does have a valid address (BEET, TUNNEL).
97  */
98 static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode)
99 {
100 	switch (k) {
101 	case NFT_XFRM_KEY_DADDR_IP4:
102 	case NFT_XFRM_KEY_SADDR_IP4:
103 		if (family == NFPROTO_IPV4)
104 			break;
105 		return false;
106 	case NFT_XFRM_KEY_DADDR_IP6:
107 	case NFT_XFRM_KEY_SADDR_IP6:
108 		if (family == NFPROTO_IPV6)
109 			break;
110 		return false;
111 	default:
112 		return true;
113 	}
114 
115 	return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL;
116 }
117 
118 static void nft_xfrm_state_get_key(const struct nft_xfrm *priv,
119 				   struct nft_regs *regs,
120 				   const struct xfrm_state *state)
121 {
122 	u32 *dest = &regs->data[priv->dreg];
123 
124 	if (!xfrm_state_addr_ok(priv->key,
125 				state->props.family,
126 				state->props.mode)) {
127 		regs->verdict.code = NFT_BREAK;
128 		return;
129 	}
130 
131 	switch (priv->key) {
132 	case NFT_XFRM_KEY_UNSPEC:
133 	case __NFT_XFRM_KEY_MAX:
134 		WARN_ON_ONCE(1);
135 		break;
136 	case NFT_XFRM_KEY_DADDR_IP4:
137 		*dest = (__force __u32)state->id.daddr.a4;
138 		return;
139 	case NFT_XFRM_KEY_DADDR_IP6:
140 		memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr));
141 		return;
142 	case NFT_XFRM_KEY_SADDR_IP4:
143 		*dest = (__force __u32)state->props.saddr.a4;
144 		return;
145 	case NFT_XFRM_KEY_SADDR_IP6:
146 		memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr));
147 		return;
148 	case NFT_XFRM_KEY_REQID:
149 		*dest = state->props.reqid;
150 		return;
151 	case NFT_XFRM_KEY_SPI:
152 		*dest = (__force __u32)state->id.spi;
153 		return;
154 	}
155 
156 	regs->verdict.code = NFT_BREAK;
157 }
158 
159 static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
160 				    struct nft_regs *regs,
161 				    const struct nft_pktinfo *pkt)
162 {
163 	const struct sec_path *sp = skb_sec_path(pkt->skb);
164 	const struct xfrm_state *state;
165 
166 	if (sp == NULL || sp->len <= priv->spnum) {
167 		regs->verdict.code = NFT_BREAK;
168 		return;
169 	}
170 
171 	state = sp->xvec[priv->spnum];
172 	nft_xfrm_state_get_key(priv, regs, state);
173 }
174 
175 static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
176 				  struct nft_regs *regs,
177 				  const struct nft_pktinfo *pkt)
178 {
179 	const struct dst_entry *dst = skb_dst(pkt->skb);
180 	int i;
181 
182 	for (i = 0; dst && dst->xfrm;
183 	     dst = ((const struct xfrm_dst *)dst)->child, i++) {
184 		if (i < priv->spnum)
185 			continue;
186 
187 		nft_xfrm_state_get_key(priv, regs, dst->xfrm);
188 		return;
189 	}
190 
191 	regs->verdict.code = NFT_BREAK;
192 }
193 
194 static void nft_xfrm_get_eval(const struct nft_expr *expr,
195 			      struct nft_regs *regs,
196 			      const struct nft_pktinfo *pkt)
197 {
198 	const struct nft_xfrm *priv = nft_expr_priv(expr);
199 
200 	switch (priv->dir) {
201 	case XFRM_POLICY_IN:
202 		nft_xfrm_get_eval_in(priv, regs, pkt);
203 		break;
204 	case XFRM_POLICY_OUT:
205 		nft_xfrm_get_eval_out(priv, regs, pkt);
206 		break;
207 	default:
208 		WARN_ON_ONCE(1);
209 		regs->verdict.code = NFT_BREAK;
210 		break;
211 	}
212 }
213 
214 static int nft_xfrm_get_dump(struct sk_buff *skb,
215 			     const struct nft_expr *expr, bool reset)
216 {
217 	const struct nft_xfrm *priv = nft_expr_priv(expr);
218 
219 	if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg))
220 		return -1;
221 
222 	if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key)))
223 		return -1;
224 	if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir))
225 		return -1;
226 	if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum)))
227 		return -1;
228 
229 	return 0;
230 }
231 
232 static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
233 			     const struct nft_data **data)
234 {
235 	const struct nft_xfrm *priv = nft_expr_priv(expr);
236 	unsigned int hooks;
237 
238 	switch (priv->dir) {
239 	case XFRM_POLICY_IN:
240 		hooks = (1 << NF_INET_FORWARD) |
241 			(1 << NF_INET_LOCAL_IN) |
242 			(1 << NF_INET_PRE_ROUTING);
243 		break;
244 	case XFRM_POLICY_OUT:
245 		hooks = (1 << NF_INET_FORWARD) |
246 			(1 << NF_INET_LOCAL_OUT) |
247 			(1 << NF_INET_POST_ROUTING);
248 		break;
249 	default:
250 		WARN_ON_ONCE(1);
251 		return -EINVAL;
252 	}
253 
254 	return nft_chain_validate_hooks(ctx->chain, hooks);
255 }
256 
257 static bool nft_xfrm_reduce(struct nft_regs_track *track,
258 			    const struct nft_expr *expr)
259 {
260 	const struct nft_xfrm *priv = nft_expr_priv(expr);
261 	const struct nft_xfrm *xfrm;
262 
263 	if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
264 		nft_reg_track_update(track, expr, priv->dreg, priv->len);
265 		return false;
266 	}
267 
268 	xfrm = nft_expr_priv(track->regs[priv->dreg].selector);
269 	if (priv->key != xfrm->key ||
270 	    priv->dreg != xfrm->dreg ||
271 	    priv->dir != xfrm->dir ||
272 	    priv->spnum != xfrm->spnum) {
273 		nft_reg_track_update(track, expr, priv->dreg, priv->len);
274 		return false;
275 	}
276 
277 	if (!track->regs[priv->dreg].bitwise)
278 		return true;
279 
280 	return nft_expr_reduce_bitwise(track, expr);
281 }
282 
283 static struct nft_expr_type nft_xfrm_type;
284 static const struct nft_expr_ops nft_xfrm_get_ops = {
285 	.type		= &nft_xfrm_type,
286 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
287 	.eval		= nft_xfrm_get_eval,
288 	.init		= nft_xfrm_get_init,
289 	.dump		= nft_xfrm_get_dump,
290 	.validate	= nft_xfrm_validate,
291 	.reduce		= nft_xfrm_reduce,
292 };
293 
294 static struct nft_expr_type nft_xfrm_type __read_mostly = {
295 	.name		= "xfrm",
296 	.ops		= &nft_xfrm_get_ops,
297 	.policy		= nft_xfrm_policy,
298 	.maxattr	= NFTA_XFRM_MAX,
299 	.owner		= THIS_MODULE,
300 };
301 
302 static int __init nft_xfrm_module_init(void)
303 {
304 	return nft_register_expr(&nft_xfrm_type);
305 }
306 
307 static void __exit nft_xfrm_module_exit(void)
308 {
309 	nft_unregister_expr(&nft_xfrm_type);
310 }
311 
312 module_init(nft_xfrm_module_init);
313 module_exit(nft_xfrm_module_exit);
314 
315 MODULE_LICENSE("GPL");
316 MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
317 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
318 MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
319 MODULE_ALIAS_NFT_EXPR("xfrm");
320