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