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