Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[linux-2.6] / 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_rcv_spi(struct sk_buff *skb, __be32 spi)
20 {
21         int err;
22         __be32 seq;
23         struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
24         struct xfrm_state *x;
25         int xfrm_nr = 0;
26         int decaps = 0;
27         int nexthdr;
28         unsigned int nhoff;
29
30         nhoff = IP6CB(skb)->nhoff;
31         nexthdr = skb_network_header(skb)[nhoff];
32
33         seq = 0;
34         if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
35                 goto drop;
36
37         do {
38                 struct ipv6hdr *iph = ipv6_hdr(skb);
39
40                 if (xfrm_nr == XFRM_MAX_DEPTH)
41                         goto drop;
42
43                 x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi,
44                                 nexthdr != IPPROTO_IPIP ? nexthdr : IPPROTO_IPV6, AF_INET6);
45                 if (x == NULL)
46                         goto drop;
47                 spin_lock(&x->lock);
48                 if (unlikely(x->km.state != XFRM_STATE_VALID))
49                         goto drop_unlock;
50
51                 if (x->props.replay_window && xfrm_replay_check(x, seq))
52                         goto drop_unlock;
53
54                 if (xfrm_state_check_expire(x))
55                         goto drop_unlock;
56
57                 nexthdr = x->type->input(x, skb);
58                 if (nexthdr <= 0)
59                         goto drop_unlock;
60
61                 skb_network_header(skb)[nhoff] = nexthdr;
62
63                 if (x->props.replay_window)
64                         xfrm_replay_advance(x, seq);
65
66                 x->curlft.bytes += skb->len;
67                 x->curlft.packets++;
68
69                 spin_unlock(&x->lock);
70
71                 xfrm_vec[xfrm_nr++] = x;
72
73                 if (x->mode->input(x, skb))
74                         goto drop;
75
76                 if (x->props.mode == XFRM_MODE_TUNNEL) { /* XXX */
77                         decaps = 1;
78                         break;
79                 }
80
81                 if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
82                         goto drop;
83         } while (!err);
84
85         /* Allocate new secpath or COW existing one. */
86         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
87                 struct sec_path *sp;
88                 sp = secpath_dup(skb->sp);
89                 if (!sp)
90                         goto drop;
91                 if (skb->sp)
92                         secpath_put(skb->sp);
93                 skb->sp = sp;
94         }
95
96         if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
97                 goto drop;
98
99         memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
100                xfrm_nr * sizeof(xfrm_vec[0]));
101         skb->sp->len += xfrm_nr;
102         skb->ip_summed = CHECKSUM_NONE;
103
104         nf_reset(skb);
105
106         if (decaps) {
107                 if (!(skb->dev->flags&IFF_LOOPBACK)) {
108                         dst_release(skb->dst);
109                         skb->dst = NULL;
110                 }
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 **pskb)
139 {
140         return xfrm6_rcv_spi(*pskb, 0);
141 }
142
143 EXPORT_SYMBOL(xfrm6_rcv);
144
145 int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
146                      xfrm_address_t *saddr, u8 proto)
147 {
148         struct xfrm_state *x = NULL;
149         int wildcard = 0;
150         xfrm_address_t *xany;
151         struct xfrm_state *xfrm_vec_one = NULL;
152         int nh = 0;
153         int i = 0;
154
155         xany = (xfrm_address_t *)&in6addr_any;
156
157         for (i = 0; i < 3; i++) {
158                 xfrm_address_t *dst, *src;
159                 switch (i) {
160                 case 0:
161                         dst = daddr;
162                         src = saddr;
163                         break;
164                 case 1:
165                         /* lookup state with wild-card source address */
166                         wildcard = 1;
167                         dst = daddr;
168                         src = xany;
169                         break;
170                 case 2:
171                 default:
172                         /* lookup state with wild-card addresses */
173                         wildcard = 1; /* XXX */
174                         dst = xany;
175                         src = xany;
176                         break;
177                 }
178
179                 x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
180                 if (!x)
181                         continue;
182
183                 spin_lock(&x->lock);
184
185                 if (wildcard) {
186                         if ((x->props.flags & XFRM_STATE_WILDRECV) == 0) {
187                                 spin_unlock(&x->lock);
188                                 xfrm_state_put(x);
189                                 x = NULL;
190                                 continue;
191                         }
192                 }
193
194                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
195                         spin_unlock(&x->lock);
196                         xfrm_state_put(x);
197                         x = NULL;
198                         continue;
199                 }
200                 if (xfrm_state_check_expire(x)) {
201                         spin_unlock(&x->lock);
202                         xfrm_state_put(x);
203                         x = NULL;
204                         continue;
205                 }
206
207                 nh = x->type->input(x, skb);
208                 if (nh <= 0) {
209                         spin_unlock(&x->lock);
210                         xfrm_state_put(x);
211                         x = NULL;
212                         continue;
213                 }
214
215                 x->curlft.bytes += skb->len;
216                 x->curlft.packets++;
217
218                 spin_unlock(&x->lock);
219
220                 xfrm_vec_one = x;
221                 break;
222         }
223
224         if (!xfrm_vec_one)
225                 goto drop;
226
227         /* Allocate new secpath or COW existing one. */
228         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
229                 struct sec_path *sp;
230                 sp = secpath_dup(skb->sp);
231                 if (!sp)
232                         goto drop;
233                 if (skb->sp)
234                         secpath_put(skb->sp);
235                 skb->sp = sp;
236         }
237
238         if (1 + skb->sp->len > XFRM_MAX_DEPTH)
239                 goto drop;
240
241         skb->sp->xvec[skb->sp->len] = xfrm_vec_one;
242         skb->sp->len ++;
243
244         return 1;
245 drop:
246         if (xfrm_vec_one)
247                 xfrm_state_put(xfrm_vec_one);
248         return -1;
249 }
250
251 EXPORT_SYMBOL(xfrm6_input_addr);