[IPSEC]: Separate inner/outer mode processing on input
[pandora-kernel.git] / net / ipv6 / xfrm6_input.c
1 /*
2  * xfrm6_input.c: based on net/ipv4/xfrm4_input.c
3  *
4  * Authors:
5  *      Mitsuru KANDA @USAGI
6  *      Kazunori MIYAZAWA @USAGI
7  *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8  *      YOSHIFUJI Hideaki @USAGI
9  *              IPv6 support
10  */
11
12 #include <linux/module.h>
13 #include <linux/string.h>
14 #include <linux/netfilter.h>
15 #include <linux/netfilter_ipv6.h>
16 #include <net/ipv6.h>
17 #include <net/xfrm.h>
18
19 int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb)
20 {
21         return xfrm6_extract_header(skb);
22 }
23
24 int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
25 {
26         int err;
27         __be32 seq;
28         struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
29         struct xfrm_state *x;
30         int xfrm_nr = 0;
31         int decaps = 0;
32         unsigned int nhoff;
33
34         nhoff = IP6CB(skb)->nhoff;
35
36         seq = 0;
37         if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
38                 goto drop;
39
40         do {
41                 struct ipv6hdr *iph = ipv6_hdr(skb);
42
43                 if (xfrm_nr == XFRM_MAX_DEPTH)
44                         goto drop;
45
46                 x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi,
47                                       nexthdr, AF_INET6);
48                 if (x == NULL)
49                         goto drop;
50                 spin_lock(&x->lock);
51                 if (unlikely(x->km.state != XFRM_STATE_VALID))
52                         goto drop_unlock;
53
54                 if (x->props.replay_window && xfrm_replay_check(x, seq))
55                         goto drop_unlock;
56
57                 if (xfrm_state_check_expire(x))
58                         goto drop_unlock;
59
60                 nexthdr = x->type->input(x, skb);
61                 if (nexthdr <= 0)
62                         goto drop_unlock;
63
64                 skb_network_header(skb)[nhoff] = nexthdr;
65
66                 if (x->props.replay_window)
67                         xfrm_replay_advance(x, seq);
68
69                 x->curlft.bytes += skb->len;
70                 x->curlft.packets++;
71
72                 spin_unlock(&x->lock);
73
74                 xfrm_vec[xfrm_nr++] = x;
75
76                 if (x->inner_mode->input(x, skb))
77                         goto drop;
78
79                 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
80                         decaps = 1;
81                         break;
82                 }
83
84                 if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
85                         goto drop;
86         } while (!err);
87
88         /* Allocate new secpath or COW existing one. */
89         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
90                 struct sec_path *sp;
91                 sp = secpath_dup(skb->sp);
92                 if (!sp)
93                         goto drop;
94                 if (skb->sp)
95                         secpath_put(skb->sp);
96                 skb->sp = sp;
97         }
98
99         if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
100                 goto drop;
101
102         memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
103                xfrm_nr * sizeof(xfrm_vec[0]));
104         skb->sp->len += xfrm_nr;
105
106         nf_reset(skb);
107
108         if (decaps) {
109                 dst_release(skb->dst);
110                 skb->dst = NULL;
111                 netif_rx(skb);
112                 return -1;
113         } else {
114 #ifdef CONFIG_NETFILTER
115                 ipv6_hdr(skb)->payload_len = htons(skb->len);
116                 __skb_push(skb, skb->data - skb_network_header(skb));
117
118                 NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
119                         ip6_rcv_finish);
120                 return -1;
121 #else
122                 return 1;
123 #endif
124         }
125
126 drop_unlock:
127         spin_unlock(&x->lock);
128         xfrm_state_put(x);
129 drop:
130         while (--xfrm_nr >= 0)
131                 xfrm_state_put(xfrm_vec[xfrm_nr]);
132         kfree_skb(skb);
133         return -1;
134 }
135
136 EXPORT_SYMBOL(xfrm6_rcv_spi);
137
138 int xfrm6_rcv(struct sk_buff *skb)
139 {
140         return xfrm6_rcv_spi(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
141                              0);
142 }
143
144 EXPORT_SYMBOL(xfrm6_rcv);
145
146 int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
147                      xfrm_address_t *saddr, u8 proto)
148 {
149         struct xfrm_state *x = NULL;
150         int wildcard = 0;
151         xfrm_address_t *xany;
152         struct xfrm_state *xfrm_vec_one = NULL;
153         int nh = 0;
154         int i = 0;
155
156         xany = (xfrm_address_t *)&in6addr_any;
157
158         for (i = 0; i < 3; i++) {
159                 xfrm_address_t *dst, *src;
160                 switch (i) {
161                 case 0:
162                         dst = daddr;
163                         src = saddr;
164                         break;
165                 case 1:
166                         /* lookup state with wild-card source address */
167                         wildcard = 1;
168                         dst = daddr;
169                         src = xany;
170                         break;
171                 case 2:
172                 default:
173                         /* lookup state with wild-card addresses */
174                         wildcard = 1; /* XXX */
175                         dst = xany;
176                         src = xany;
177                         break;
178                 }
179
180                 x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
181                 if (!x)
182                         continue;
183
184                 spin_lock(&x->lock);
185
186                 if (wildcard) {
187                         if ((x->props.flags & XFRM_STATE_WILDRECV) == 0) {
188                                 spin_unlock(&x->lock);
189                                 xfrm_state_put(x);
190                                 x = NULL;
191                                 continue;
192                         }
193                 }
194
195                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
196                         spin_unlock(&x->lock);
197                         xfrm_state_put(x);
198                         x = NULL;
199                         continue;
200                 }
201                 if (xfrm_state_check_expire(x)) {
202                         spin_unlock(&x->lock);
203                         xfrm_state_put(x);
204                         x = NULL;
205                         continue;
206                 }
207
208                 nh = x->type->input(x, skb);
209                 if (nh <= 0) {
210                         spin_unlock(&x->lock);
211                         xfrm_state_put(x);
212                         x = NULL;
213                         continue;
214                 }
215
216                 x->curlft.bytes += skb->len;
217                 x->curlft.packets++;
218
219                 spin_unlock(&x->lock);
220
221                 xfrm_vec_one = x;
222                 break;
223         }
224
225         if (!xfrm_vec_one)
226                 goto drop;
227
228         /* Allocate new secpath or COW existing one. */
229         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
230                 struct sec_path *sp;
231                 sp = secpath_dup(skb->sp);
232                 if (!sp)
233                         goto drop;
234                 if (skb->sp)
235                         secpath_put(skb->sp);
236                 skb->sp = sp;
237         }
238
239         if (1 + skb->sp->len > XFRM_MAX_DEPTH)
240                 goto drop;
241
242         skb->sp->xvec[skb->sp->len] = xfrm_vec_one;
243         skb->sp->len ++;
244
245         return 1;
246 drop:
247         if (xfrm_vec_one)
248                 xfrm_state_put(xfrm_vec_one);
249         return -1;
250 }
251
252 EXPORT_SYMBOL(xfrm6_input_addr);