1/*
2 * xfrm4_input.c
3 *
4 * Changes:
5 *	YOSHIFUJI Hideaki @USAGI
6 *		Split up af-specific portion
7 *	Derek Atkins <derek@ihtfp.com>
8 *		Add Encapsulation support
9 *
10 */
11
12#include <linux/module.h>
13#include <linux/string.h>
14#include <linux/netfilter.h>
15#include <linux/netfilter_ipv4.h>
16#include <net/ip.h>
17#include <net/xfrm.h>
18
19int xfrm4_rcv(struct sk_buff *skb)
20{
21	return xfrm4_rcv_encap(skb, 0);
22}
23
24EXPORT_SYMBOL(xfrm4_rcv);
25
26static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
27{
28	switch (nexthdr) {
29	case IPPROTO_IPIP:
30	case IPPROTO_IPV6:
31		*spi = ip_hdr(skb)->saddr;
32		*seq = 0;
33		return 0;
34	}
35
36	return xfrm_parse_spi(skb, nexthdr, spi, seq);
37}
38
39#ifdef CONFIG_NETFILTER
40static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)
41{
42	if (skb->dst == NULL) {
43		const struct iphdr *iph = ip_hdr(skb);
44
45		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
46				   skb->dev))
47			goto drop;
48	}
49	return dst_input(skb);
50drop:
51	kfree_skb(skb);
52	return NET_RX_DROP;
53}
54#endif
55
56int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
57{
58	__be32 spi, seq;
59	struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
60	struct xfrm_state *x;
61	int xfrm_nr = 0;
62	int decaps = 0;
63	int err = xfrm4_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq);
64
65	if (err != 0)
66		goto drop;
67
68	do {
69		const struct iphdr *iph = ip_hdr(skb);
70
71		if (xfrm_nr == XFRM_MAX_DEPTH)
72			goto drop;
73
74		x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi,
75				iph->protocol != IPPROTO_IPV6 ? iph->protocol : IPPROTO_IPIP, AF_INET);
76		if (x == NULL)
77			goto drop;
78
79		spin_lock(&x->lock);
80		if (unlikely(x->km.state != XFRM_STATE_VALID))
81			goto drop_unlock;
82
83		if ((x->encap ? x->encap->encap_type : 0) != encap_type)
84			goto drop_unlock;
85
86		if (x->props.replay_window && xfrm_replay_check(x, seq))
87			goto drop_unlock;
88
89		if (xfrm_state_check_expire(x))
90			goto drop_unlock;
91
92		if (x->type->input(x, skb))
93			goto drop_unlock;
94
95		/* only the first xfrm gets the encap type */
96		encap_type = 0;
97
98		if (x->props.replay_window)
99			xfrm_replay_advance(x, seq);
100
101		x->curlft.bytes += skb->len;
102		x->curlft.packets++;
103
104		spin_unlock(&x->lock);
105
106		xfrm_vec[xfrm_nr++] = x;
107
108		if (x->mode->input(x, skb))
109			goto drop;
110
111		if (x->props.mode == XFRM_MODE_TUNNEL) {
112			decaps = 1;
113			break;
114		}
115
116		err = xfrm_parse_spi(skb, ip_hdr(skb)->protocol, &spi, &seq);
117		if (err < 0)
118			goto drop;
119	} while (!err);
120
121	/* Allocate new secpath or COW existing one. */
122
123	if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
124		struct sec_path *sp;
125		sp = secpath_dup(skb->sp);
126		if (!sp)
127			goto drop;
128		if (skb->sp)
129			secpath_put(skb->sp);
130		skb->sp = sp;
131	}
132	if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
133		goto drop;
134
135	memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
136	       xfrm_nr * sizeof(xfrm_vec[0]));
137	skb->sp->len += xfrm_nr;
138
139	nf_reset(skb);
140
141	if (decaps) {
142		dst_release(skb->dst);
143		skb->dst = NULL;
144		netif_rx(skb);
145		return 0;
146	} else {
147#ifdef CONFIG_NETFILTER
148		__skb_push(skb, skb->data - skb_network_header(skb));
149		ip_hdr(skb)->tot_len = htons(skb->len);
150		ip_send_check(ip_hdr(skb));
151
152		NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
153			xfrm4_rcv_encap_finish);
154		return 0;
155#else
156		return -ip_hdr(skb)->protocol;
157#endif
158	}
159
160drop_unlock:
161	spin_unlock(&x->lock);
162	xfrm_state_put(x);
163drop:
164	while (--xfrm_nr >= 0)
165		xfrm_state_put(xfrm_vec[xfrm_nr]);
166
167	kfree_skb(skb);
168	return 0;
169}
170