Merge tag 'rust-6.9' of https://github.com/Rust-for-Linux/linux
[sfrench/cifs-2.6.git] / net / netfilter / nft_xfrm.c
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
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         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
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
307 static int __init nft_xfrm_module_init(void)
308 {
309         return nft_register_expr(&nft_xfrm_type);
310 }
311
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 <fw@strlen.de>");
323 MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
324 MODULE_ALIAS_NFT_EXPR("xfrm");