33a1b9200431ae9b760a8a0afcfd290ade34562a
[linux-2.6.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_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->nh.raw[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 = skb->nh.ipv6h;
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->nh.raw[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                 skb->nh.ipv6h->payload_len = htons(skb->len);
116                 __skb_push(skb, skb->data - skb->nh.raw);
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         struct in6_addr any;
151         xfrm_address_t *xany;
152         struct xfrm_state *xfrm_vec_one = NULL;
153         int nh = 0;
154         int i = 0;
155
156         ipv6_addr_set(&any, 0, 0, 0, 0);
157         xany = (xfrm_address_t *)&any;
158
159         for (i = 0; i < 3; i++) {
160                 xfrm_address_t *dst, *src;
161                 switch (i) {
162                 case 0:
163                         dst = daddr;
164                         src = saddr;
165                         break;
166                 case 1:
167                         /* lookup state with wild-card source address */
168                         wildcard = 1;
169                         dst = daddr;
170                         src = xany;
171                         break;
172                 case 2:
173                 default:
174                         /* lookup state with wild-card addresses */
175                         wildcard = 1; /* XXX */
176                         dst = xany;
177                         src = xany;
178                         break;
179                 }
180
181                 x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
182                 if (!x)
183                         continue;
184
185                 spin_lock(&x->lock);
186
187                 if (wildcard) {
188                         if ((x->props.flags & XFRM_STATE_WILDRECV) == 0) {
189                                 spin_unlock(&x->lock);
190                                 xfrm_state_put(x);
191                                 x = NULL;
192                                 continue;
193                         }
194                 }
195
196                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
197                         spin_unlock(&x->lock);
198                         xfrm_state_put(x);
199                         x = NULL;
200                         continue;
201                 }
202                 if (xfrm_state_check_expire(x)) {
203                         spin_unlock(&x->lock);
204                         xfrm_state_put(x);
205                         x = NULL;
206                         continue;
207                 }
208
209                 nh = x->type->input(x, skb);
210                 if (nh <= 0) {
211                         spin_unlock(&x->lock);
212                         xfrm_state_put(x);
213                         x = NULL;
214                         continue;
215                 }
216
217                 x->curlft.bytes += skb->len;
218                 x->curlft.packets++;
219
220                 spin_unlock(&x->lock);
221
222                 xfrm_vec_one = x;
223                 break;
224         }
225
226         if (!xfrm_vec_one)
227                 goto drop;
228
229         /* Allocate new secpath or COW existing one. */
230         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
231                 struct sec_path *sp;
232                 sp = secpath_dup(skb->sp);
233                 if (!sp)
234                         goto drop;
235                 if (skb->sp)
236                         secpath_put(skb->sp);
237                 skb->sp = sp;
238         }
239
240         if (1 + skb->sp->len > XFRM_MAX_DEPTH)
241                 goto drop;
242
243         skb->sp->xvec[skb->sp->len] = xfrm_vec_one;
244         skb->sp->len ++;
245
246         return 1;
247 drop:
248         if (xfrm_vec_one)
249                 xfrm_state_put(xfrm_vec_one);
250         return -1;
251 }
252
253 EXPORT_SYMBOL(xfrm6_input_addr);