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