Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / net / netfilter / nft_xfrm.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation.
5  *
6  * Generic part shared by ipv4 and ipv6 backends.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/netlink.h>
13 #include <linux/netfilter.h>
14 #include <linux/netfilter/nf_tables.h>
15 #include <net/netfilter/nf_tables_core.h>
16 #include <net/netfilter/nf_tables.h>
17 #include <linux/in.h>
18 #include <net/xfrm.h>
19
20 static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
21         [NFTA_XFRM_KEY]         = { .type = NLA_U32 },
22         [NFTA_XFRM_DIR]         = { .type = NLA_U8 },
23         [NFTA_XFRM_SPNUM]       = { .type = NLA_U32 },
24         [NFTA_XFRM_DREG]        = { .type = NLA_U32 },
25 };
26
27 struct nft_xfrm {
28         enum nft_xfrm_keys      key:8;
29         enum nft_registers      dreg:8;
30         u8                      dir;
31         u8                      spnum;
32 };
33
34 static int nft_xfrm_get_init(const struct nft_ctx *ctx,
35                              const struct nft_expr *expr,
36                              const struct nlattr * const tb[])
37 {
38         struct nft_xfrm *priv = nft_expr_priv(expr);
39         unsigned int len = 0;
40         u32 spnum = 0;
41         u8 dir;
42
43         if (!tb[NFTA_XFRM_KEY] || !tb[NFTA_XFRM_DIR] || !tb[NFTA_XFRM_DREG])
44                 return -EINVAL;
45
46         switch (ctx->family) {
47         case NFPROTO_IPV4:
48         case NFPROTO_IPV6:
49         case NFPROTO_INET:
50                 break;
51         default:
52                 return -EOPNOTSUPP;
53         }
54
55         priv->key = ntohl(nla_get_u32(tb[NFTA_XFRM_KEY]));
56         switch (priv->key) {
57         case NFT_XFRM_KEY_REQID:
58         case NFT_XFRM_KEY_SPI:
59                 len = sizeof(u32);
60                 break;
61         case NFT_XFRM_KEY_DADDR_IP4:
62         case NFT_XFRM_KEY_SADDR_IP4:
63                 len = sizeof(struct in_addr);
64                 break;
65         case NFT_XFRM_KEY_DADDR_IP6:
66         case NFT_XFRM_KEY_SADDR_IP6:
67                 len = sizeof(struct in6_addr);
68                 break;
69         default:
70                 return -EINVAL;
71         }
72
73         dir = nla_get_u8(tb[NFTA_XFRM_DIR]);
74         switch (dir) {
75         case XFRM_POLICY_IN:
76         case XFRM_POLICY_OUT:
77                 priv->dir = dir;
78                 break;
79         default:
80                 return -EINVAL;
81         }
82
83         if (tb[NFTA_XFRM_SPNUM])
84                 spnum = ntohl(nla_get_be32(tb[NFTA_XFRM_SPNUM]));
85
86         if (spnum >= XFRM_MAX_DEPTH)
87                 return -ERANGE;
88
89         priv->spnum = spnum;
90
91         priv->dreg = nft_parse_register(tb[NFTA_XFRM_DREG]);
92         return nft_validate_register_store(ctx, priv->dreg, NULL,
93                                            NFT_DATA_VALUE, len);
94 }
95
96 /* Return true if key asks for daddr/saddr and current
97  * state does have a valid address (BEET, TUNNEL).
98  */
99 static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode)
100 {
101         switch (k) {
102         case NFT_XFRM_KEY_DADDR_IP4:
103         case NFT_XFRM_KEY_SADDR_IP4:
104                 if (family == NFPROTO_IPV4)
105                         break;
106                 return false;
107         case NFT_XFRM_KEY_DADDR_IP6:
108         case NFT_XFRM_KEY_SADDR_IP6:
109                 if (family == NFPROTO_IPV6)
110                         break;
111                 return false;
112         default:
113                 return true;
114         }
115
116         return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL;
117 }
118
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 = 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 = 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 = state->id.spi;
154                 return;
155         }
156
157         regs->verdict.code = NFT_BREAK;
158 }
159
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
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
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
215 static int nft_xfrm_get_dump(struct sk_buff *skb,
216                              const struct nft_expr *expr)
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
233 static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
234                              const struct nft_data **data)
235 {
236         const struct nft_xfrm *priv = nft_expr_priv(expr);
237         unsigned int hooks;
238
239         switch (priv->dir) {
240         case XFRM_POLICY_IN:
241                 hooks = (1 << NF_INET_FORWARD) |
242                         (1 << NF_INET_LOCAL_IN) |
243                         (1 << NF_INET_PRE_ROUTING);
244                 break;
245         case XFRM_POLICY_OUT:
246                 hooks = (1 << NF_INET_FORWARD) |
247                         (1 << NF_INET_LOCAL_OUT) |
248                         (1 << NF_INET_POST_ROUTING);
249                 break;
250         default:
251                 WARN_ON_ONCE(1);
252                 return -EINVAL;
253         }
254
255         return nft_chain_validate_hooks(ctx->chain, hooks);
256 }
257
258
259 static struct nft_expr_type nft_xfrm_type;
260 static const struct nft_expr_ops nft_xfrm_get_ops = {
261         .type           = &nft_xfrm_type,
262         .size           = NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
263         .eval           = nft_xfrm_get_eval,
264         .init           = nft_xfrm_get_init,
265         .dump           = nft_xfrm_get_dump,
266         .validate       = nft_xfrm_validate,
267 };
268
269 static struct nft_expr_type nft_xfrm_type __read_mostly = {
270         .name           = "xfrm",
271         .ops            = &nft_xfrm_get_ops,
272         .policy         = nft_xfrm_policy,
273         .maxattr        = NFTA_XFRM_MAX,
274         .owner          = THIS_MODULE,
275 };
276
277 static int __init nft_xfrm_module_init(void)
278 {
279         return nft_register_expr(&nft_xfrm_type);
280 }
281
282 static void __exit nft_xfrm_module_exit(void)
283 {
284         nft_unregister_expr(&nft_xfrm_type);
285 }
286
287 module_init(nft_xfrm_module_init);
288 module_exit(nft_xfrm_module_exit);
289
290 MODULE_LICENSE("GPL");
291 MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
292 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
293 MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
294 MODULE_ALIAS_NFT_EXPR("xfrm");