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
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 daddroff = XFRM_SPI_SKB_CB(skb)->daddroff;

107 108 109 110 111 112 113 114 115 116 117 118
	/* 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;
	}

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

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

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

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

135 136 137 138 139 140 141 142 143 144 145 146 147
		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;

148 149
		spin_unlock(&x->lock);

150
		nexthdr = x->type->input(x, skb);
151 152

		spin_lock(&x->lock);
153 154 155
		if (nexthdr <= 0) {
			if (nexthdr == -EBADMSG)
				x->stats.integrity_failed++;
156
			goto drop_unlock;
157
		}
158 159 160 161 162 163 164 165 166 167 168 169

		/* 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);

170 171
		XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;

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 201 202 203
		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 已提交
204 205 206 207
void __init xfrm_input_init(void)
{
	secpath_cachep = kmem_cache_create("secpath_cache",
					   sizeof(struct sec_path),
A
Alexey Dobriyan 已提交
208
					   0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
209
					   NULL);
L
Linus Torvalds 已提交
210
}