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
19int 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) {
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
124drop_unlock:
125	spin_unlock(&x->lock);
126	xfrm_state_put(x);
127drop:
128	while (--xfrm_nr >= 0)
129		xfrm_state_put(xfrm_vec[xfrm_nr]);
130	kfree_skb(skb);
131	return -1;
132}
133
134EXPORT_SYMBOL(xfrm6_rcv_spi);
135
136int xfrm6_rcv(struct sk_buff **pskb)
137{
138	return xfrm6_rcv_spi(*pskb, 0);
139}
140
141EXPORT_SYMBOL(xfrm6_rcv);
142
143int 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;
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;
243drop:
244	if (xfrm_vec_one)
245		xfrm_state_put(xfrm_vec_one);
246	return -1;
247}
248
249EXPORT_SYMBOL(xfrm6_input_addr);
250