xfrm_input.c 4.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6
/*
 * xfrm_input.c
 *
 * Changes:
 * 	YOSHIFUJI Hideaki @USAGI
 * 		Split up af-specific portion
7
 *
L
Linus Torvalds 已提交
8 9 10 11
 */

#include <linux/slab.h>
#include <linux/module.h>
12 13
#include <linux/netdevice.h>
#include <net/dst.h>
L
Linus Torvalds 已提交
14 15 16
#include <net/ip.h>
#include <net/xfrm.h>

17
static struct kmem_cache *secpath_cachep __read_mostly;
L
Linus Torvalds 已提交
18 19 20 21 22

void __secpath_destroy(struct sec_path *sp)
{
	int i;
	for (i = 0; i < sp->len; i++)
23
		xfrm_state_put(sp->xvec[i]);
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31
	kmem_cache_free(secpath_cachep, sp);
}
EXPORT_SYMBOL(__secpath_destroy);

struct sec_path *secpath_dup(struct sec_path *src)
{
	struct sec_path *sp;

32
	sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41
	if (!sp)
		return NULL;

	sp->len = 0;
	if (src) {
		int i;

		memcpy(sp, src, sizeof(*sp));
		for (i = 0; i < sp->len; i++)
42
			xfrm_state_hold(sp->xvec[i]);
L
Linus Torvalds 已提交
43 44 45 46 47 48 49 50
	}
	atomic_set(&sp->refcnt, 1);
	return sp;
}
EXPORT_SYMBOL(secpath_dup);

/* Fetch spi and seq from ipsec header */

A
Al Viro 已提交
51
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
L
Linus Torvalds 已提交
52 53
{
	int offset, offset_seq;
54
	int hlen;
L
Linus Torvalds 已提交
55 56 57

	switch (nexthdr) {
	case IPPROTO_AH:
58
		hlen = sizeof(struct ip_auth_hdr);
L
Linus Torvalds 已提交
59 60 61 62
		offset = offsetof(struct ip_auth_hdr, spi);
		offset_seq = offsetof(struct ip_auth_hdr, seq_no);
		break;
	case IPPROTO_ESP:
63
		hlen = sizeof(struct ip_esp_hdr);
L
Linus Torvalds 已提交
64 65 66 67 68 69
		offset = offsetof(struct ip_esp_hdr, spi);
		offset_seq = offsetof(struct ip_esp_hdr, seq_no);
		break;
	case IPPROTO_COMP:
		if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
			return -EINVAL;
70
		*spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2)));
L
Linus Torvalds 已提交
71 72 73 74 75 76
		*seq = 0;
		return 0;
	default:
		return 1;
	}

77
	if (!pskb_may_pull(skb, hlen))
L
Linus Torvalds 已提交
78 79
		return -EINVAL;

80 81
	*spi = *(__be32*)(skb_transport_header(skb) + offset);
	*seq = *(__be32*)(skb_transport_header(skb) + offset_seq);
L
Linus Torvalds 已提交
82 83 84 85
	return 0;
}
EXPORT_SYMBOL(xfrm_parse_spi);

86 87 88 89 90 91 92 93 94 95 96 97 98
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
{
	int err;

	err = x->outer_mode->afinfo->extract_input(x, skb);
	if (err)
		return err;

	skb->protocol = x->inner_mode->afinfo->eth_proto;
	return x->inner_mode->input2(x, skb);
}
EXPORT_SYMBOL(xfrm_prepare_input);

99 100 101 102 103 104 105 106 107
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
	int err;
	__be32 seq;
	struct xfrm_state *x;
	int decaps = 0;
	unsigned int nhoff = XFRM_SPI_SKB_CB(skb)->nhoff;
	unsigned int daddroff = XFRM_SPI_SKB_CB(skb)->daddroff;

108 109 110 111 112 113 114 115 116 117 118 119
	/* Allocate new secpath or COW existing one. */
	if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
		struct sec_path *sp;

		sp = secpath_dup(skb->sp);
		if (!sp)
			goto drop;
		if (skb->sp)
			secpath_put(skb->sp);
		skb->sp = sp;
	}

120 121 122 123 124
	seq = 0;
	if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
		goto drop;

	do {
125
		if (skb->sp->len == XFRM_MAX_DEPTH)
126 127 128 129 130 131 132 133
			goto drop;

		x = xfrm_state_lookup((xfrm_address_t *)
				      (skb_network_header(skb) + daddroff),
				      spi, nexthdr, AF_INET);
		if (x == NULL)
			goto drop;

134 135
		skb->sp->xvec[skb->sp->len++] = x;

136 137 138 139 140 141 142 143 144 145 146 147 148 149
		spin_lock(&x->lock);
		if (unlikely(x->km.state != XFRM_STATE_VALID))
			goto drop_unlock;

		if ((x->encap ? x->encap->encap_type : 0) != encap_type)
			goto drop_unlock;

		if (x->props.replay_window && xfrm_replay_check(x, seq))
			goto drop_unlock;

		if (xfrm_state_check_expire(x))
			goto drop_unlock;

		nexthdr = x->type->input(x, skb);
150 151 152
		if (nexthdr <= 0) {
			if (nexthdr == -EBADMSG)
				x->stats.integrity_failed++;
153
			goto drop_unlock;
154
		}
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

		skb_network_header(skb)[nhoff] = nexthdr;

		/* only the first xfrm gets the encap type */
		encap_type = 0;

		if (x->props.replay_window)
			xfrm_replay_advance(x, seq);

		x->curlft.bytes += skb->len;
		x->curlft.packets++;

		spin_unlock(&x->lock);

		if (x->inner_mode->input(x, skb))
			goto drop;

		if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
			decaps = 1;
			break;
		}

		err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
		if (err < 0)
			goto drop;
	} while (!err);

	nf_reset(skb);

	if (decaps) {
		dst_release(skb->dst);
		skb->dst = NULL;
		netif_rx(skb);
		return 0;
	} else {
		return x->inner_mode->afinfo->transport_finish(skb, 0);
	}

drop_unlock:
	spin_unlock(&x->lock);
drop:
	kfree_skb(skb);
	return 0;
}
EXPORT_SYMBOL(xfrm_input);

L
Linus Torvalds 已提交
201 202 203 204
void __init xfrm_input_init(void)
{
	secpath_cachep = kmem_cache_create("secpath_cache",
					   sizeof(struct sec_path),
A
Alexey Dobriyan 已提交
205
					   0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
206
					   NULL);
L
Linus Torvalds 已提交
207
}