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