Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[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                 dst_release(skb->dst);
108                 skb->dst = NULL;
109                 netif_rx(skb);
110                 return -1;
111         } else {
112 #ifdef CONFIG_NETFILTER
113                 ipv6_hdr(skb)->payload_len = htons(skb->len);
114                 __skb_push(skb, skb->data - skb_network_header(skb));
115
116                 NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
117                         ip6_rcv_finish);
118                 return -1;
119 #else
120                 return 1;
121 #endif
122         }
123
124 drop_unlock:
125         spin_unlock(&x->lock);
126         xfrm_state_put(x);
127 drop:
128         while (--xfrm_nr >= 0)
129                 xfrm_state_put(xfrm_vec[xfrm_nr]);
130         kfree_skb(skb);
131         return -1;
132 }
133
134 EXPORT_SYMBOL(xfrm6_rcv_spi);
135
136 int xfrm6_rcv(struct sk_buff **pskb)
137 {
138         return xfrm6_rcv_spi(*pskb, 0);
139 }
140
141 EXPORT_SYMBOL(xfrm6_rcv);
142
143 int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
144                      xfrm_address_t *saddr, u8 proto)
145 {
146         struct xfrm_state *x = NULL;
147         int wildcard = 0;
148         xfrm_address_t *xany;
149         struct xfrm_state *xfrm_vec_one = NULL;
150         int nh = 0;
151         int i = 0;
152
153         xany = (xfrm_address_t *)&in6addr_any;
154
155         for (i = 0; i < 3; i++) {
156                 xfrm_address_t *dst, *src;
157                 switch (i) {
158                 case 0:
159                         dst = daddr;
160                         src = saddr;
161                         break;
162                 case 1:
163                         /* lookup state with wild-card source address */
164                         wildcard = 1;
165                         dst = daddr;
166                         src = xany;
167                         break;
168                 case 2:
169                 default:
170                         /* lookup state with wild-card addresses */
171                         wildcard = 1; /* XXX */
172                         dst = xany;
173                         src = xany;
174                         break;
175                 }
176
177                 x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
178                 if (!x)
179                         continue;
180
181                 spin_lock(&x->lock);
182
183                 if (wildcard) {
184                         if ((x->props.flags & XFRM_STATE_WILDRECV) == 0) {
185                                 spin_unlock(&x->lock);
186                                 xfrm_state_put(x);
187                                 x = NULL;
188                                 continue;
189                         }
190                 }
191
192                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
193                         spin_unlock(&x->lock);
194                         xfrm_state_put(x);
195                         x = NULL;
196                         continue;
197                 }
198                 if (xfrm_state_check_expire(x)) {
199                         spin_unlock(&x->lock);
200                         xfrm_state_put(x);
201                         x = NULL;
202                         continue;
203                 }
204
205                 nh = x->type->input(x, skb);
206                 if (nh <= 0) {
207                         spin_unlock(&x->lock);
208                         xfrm_state_put(x);
209                         x = NULL;
210                         continue;
211                 }
212
213                 x->curlft.bytes += skb->len;
214                 x->curlft.packets++;
215
216                 spin_unlock(&x->lock);
217
218                 xfrm_vec_one = x;
219                 break;
220         }
221
222         if (!xfrm_vec_one)
223                 goto drop;
224
225         /* Allocate new secpath or COW existing one. */
226         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
227                 struct sec_path *sp;
228                 sp = secpath_dup(skb->sp);
229                 if (!sp)
230                         goto drop;
231                 if (skb->sp)
232                         secpath_put(skb->sp);
233                 skb->sp = sp;
234         }
235
236         if (1 + skb->sp->len > XFRM_MAX_DEPTH)
237                 goto drop;
238
239         skb->sp->xvec[skb->sp->len] = xfrm_vec_one;
240         skb->sp->len ++;
241
242         return 1;
243 drop:
244         if (xfrm_vec_one)
245                 xfrm_state_put(xfrm_vec_one);
246         return -1;
247 }
248
249 EXPORT_SYMBOL(xfrm6_input_addr);