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]		= NLA_POLICY_MAX(NLA_BE32, 255),
20 	[NFTA_XFRM_DIR]		= { .type = NLA_U8 },
21 	[NFTA_XFRM_SPNUM]	= NLA_POLICY_MAX(NLA_BE32, 255),
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 
nft_xfrm_get_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])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  */
xfrm_state_addr_ok(enum nft_xfrm_keys k,u8 family,u8 mode)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 	       mode == XFRM_MODE_IPTFS;
117 }
118 
nft_xfrm_state_get_key(const struct nft_xfrm * priv,struct nft_regs * regs,const struct xfrm_state * state)119 static void nft_xfrm_state_get_key(const struct nft_xfrm *priv,
120 				   struct nft_regs *regs,
121 				   const struct xfrm_state *state)
122 {
123 	u32 *dest = &regs->data[priv->dreg];
124 
125 	if (!xfrm_state_addr_ok(priv->key,
126 				state->props.family,
127 				state->props.mode)) {
128 		regs->verdict.code = NFT_BREAK;
129 		return;
130 	}
131 
132 	switch (priv->key) {
133 	case NFT_XFRM_KEY_UNSPEC:
134 	case __NFT_XFRM_KEY_MAX:
135 		WARN_ON_ONCE(1);
136 		break;
137 	case NFT_XFRM_KEY_DADDR_IP4:
138 		*dest = (__force __u32)state->id.daddr.a4;
139 		return;
140 	case NFT_XFRM_KEY_DADDR_IP6:
141 		memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr));
142 		return;
143 	case NFT_XFRM_KEY_SADDR_IP4:
144 		*dest = (__force __u32)state->props.saddr.a4;
145 		return;
146 	case NFT_XFRM_KEY_SADDR_IP6:
147 		memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr));
148 		return;
149 	case NFT_XFRM_KEY_REQID:
150 		*dest = state->props.reqid;
151 		return;
152 	case NFT_XFRM_KEY_SPI:
153 		*dest = (__force __u32)state->id.spi;
154 		return;
155 	}
156 
157 	regs->verdict.code = NFT_BREAK;
158 }
159 
nft_xfrm_get_eval_in(const struct nft_xfrm * priv,struct nft_regs * regs,const struct nft_pktinfo * pkt)160 static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
161 				    struct nft_regs *regs,
162 				    const struct nft_pktinfo *pkt)
163 {
164 	const struct sec_path *sp = skb_sec_path(pkt->skb);
165 	const struct xfrm_state *state;
166 
167 	if (sp == NULL || sp->len <= priv->spnum) {
168 		regs->verdict.code = NFT_BREAK;
169 		return;
170 	}
171 
172 	state = sp->xvec[priv->spnum];
173 	nft_xfrm_state_get_key(priv, regs, state);
174 }
175 
nft_xfrm_get_eval_out(const struct nft_xfrm * priv,struct nft_regs * regs,const struct nft_pktinfo * pkt)176 static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
177 				  struct nft_regs *regs,
178 				  const struct nft_pktinfo *pkt)
179 {
180 	const struct dst_entry *dst = skb_dst(pkt->skb);
181 	int i;
182 
183 	for (i = 0; dst && dst->xfrm;
184 	     dst = ((const struct xfrm_dst *)dst)->child, i++) {
185 		if (i < priv->spnum)
186 			continue;
187 
188 		nft_xfrm_state_get_key(priv, regs, dst->xfrm);
189 		return;
190 	}
191 
192 	regs->verdict.code = NFT_BREAK;
193 }
194 
nft_xfrm_get_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)195 static void nft_xfrm_get_eval(const struct nft_expr *expr,
196 			      struct nft_regs *regs,
197 			      const struct nft_pktinfo *pkt)
198 {
199 	const struct nft_xfrm *priv = nft_expr_priv(expr);
200 
201 	switch (priv->dir) {
202 	case XFRM_POLICY_IN:
203 		nft_xfrm_get_eval_in(priv, regs, pkt);
204 		break;
205 	case XFRM_POLICY_OUT:
206 		nft_xfrm_get_eval_out(priv, regs, pkt);
207 		break;
208 	default:
209 		WARN_ON_ONCE(1);
210 		regs->verdict.code = NFT_BREAK;
211 		break;
212 	}
213 }
214 
nft_xfrm_get_dump(struct sk_buff * skb,const struct nft_expr * expr,bool reset)215 static int nft_xfrm_get_dump(struct sk_buff *skb,
216 			     const struct nft_expr *expr, bool reset)
217 {
218 	const struct nft_xfrm *priv = nft_expr_priv(expr);
219 
220 	if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg))
221 		return -1;
222 
223 	if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key)))
224 		return -1;
225 	if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir))
226 		return -1;
227 	if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum)))
228 		return -1;
229 
230 	return 0;
231 }
232 
nft_xfrm_validate(const struct nft_ctx * ctx,const struct nft_expr * expr)233 static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr)
234 {
235 	const struct nft_xfrm *priv = nft_expr_priv(expr);
236 	unsigned int hooks;
237 
238 	if (ctx->family != NFPROTO_IPV4 &&
239 	    ctx->family != NFPROTO_IPV6 &&
240 	    ctx->family != NFPROTO_INET)
241 		return -EOPNOTSUPP;
242 
243 	switch (priv->dir) {
244 	case XFRM_POLICY_IN:
245 		hooks = (1 << NF_INET_FORWARD) |
246 			(1 << NF_INET_LOCAL_IN) |
247 			(1 << NF_INET_PRE_ROUTING);
248 		break;
249 	case XFRM_POLICY_OUT:
250 		hooks = (1 << NF_INET_FORWARD) |
251 			(1 << NF_INET_LOCAL_OUT) |
252 			(1 << NF_INET_POST_ROUTING);
253 		break;
254 	default:
255 		WARN_ON_ONCE(1);
256 		return -EINVAL;
257 	}
258 
259 	return nft_chain_validate_hooks(ctx->chain, hooks);
260 }
261 
nft_xfrm_reduce(struct nft_regs_track * track,const struct nft_expr * expr)262 static bool nft_xfrm_reduce(struct nft_regs_track *track,
263 			    const struct nft_expr *expr)
264 {
265 	const struct nft_xfrm *priv = nft_expr_priv(expr);
266 	const struct nft_xfrm *xfrm;
267 
268 	if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
269 		nft_reg_track_update(track, expr, priv->dreg, priv->len);
270 		return false;
271 	}
272 
273 	xfrm = nft_expr_priv(track->regs[priv->dreg].selector);
274 	if (priv->key != xfrm->key ||
275 	    priv->dreg != xfrm->dreg ||
276 	    priv->dir != xfrm->dir ||
277 	    priv->spnum != xfrm->spnum) {
278 		nft_reg_track_update(track, expr, priv->dreg, priv->len);
279 		return false;
280 	}
281 
282 	if (!track->regs[priv->dreg].bitwise)
283 		return true;
284 
285 	return nft_expr_reduce_bitwise(track, expr);
286 }
287 
288 static struct nft_expr_type nft_xfrm_type;
289 static const struct nft_expr_ops nft_xfrm_get_ops = {
290 	.type		= &nft_xfrm_type,
291 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
292 	.eval		= nft_xfrm_get_eval,
293 	.init		= nft_xfrm_get_init,
294 	.dump		= nft_xfrm_get_dump,
295 	.validate	= nft_xfrm_validate,
296 	.reduce		= nft_xfrm_reduce,
297 };
298 
299 static struct nft_expr_type nft_xfrm_type __read_mostly = {
300 	.name		= "xfrm",
301 	.ops		= &nft_xfrm_get_ops,
302 	.policy		= nft_xfrm_policy,
303 	.maxattr	= NFTA_XFRM_MAX,
304 	.owner		= THIS_MODULE,
305 };
306 
nft_xfrm_module_init(void)307 static int __init nft_xfrm_module_init(void)
308 {
309 	return nft_register_expr(&nft_xfrm_type);
310 }
311 
nft_xfrm_module_exit(void)312 static void __exit nft_xfrm_module_exit(void)
313 {
314 	nft_unregister_expr(&nft_xfrm_type);
315 }
316 
317 module_init(nft_xfrm_module_init);
318 module_exit(nft_xfrm_module_exit);
319 
320 MODULE_LICENSE("GPL");
321 MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
322 MODULE_AUTHOR("Florian Westphal <[email protected]>");
323 MODULE_AUTHOR("Máté Eckl <[email protected]>");
324 MODULE_ALIAS_NFT_EXPR("xfrm");
325