Files
linux-apfs/net/xfrm/xfrm_input.c
T

263 lines
5.4 KiB
C
Raw Normal View History

2005-04-16 15:20:36 -07:00
/*
* xfrm_input.c
*
* Changes:
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific portion
2007-02-09 23:25:29 +09:00
*
2005-04-16 15:20:36 -07:00
*/
#include <linux/slab.h>
#include <linux/module.h>
2007-11-13 21:44:23 -08:00
#include <linux/netdevice.h>
#include <net/dst.h>
2005-04-16 15:20:36 -07:00
#include <net/ip.h>
#include <net/xfrm.h>
2006-12-06 20:33:20 -08:00
static struct kmem_cache *secpath_cachep __read_mostly;
2005-04-16 15:20:36 -07:00
void __secpath_destroy(struct sec_path *sp)
{
int i;
for (i = 0; i < sp->len; i++)
2006-04-01 00:54:16 -08:00
xfrm_state_put(sp->xvec[i]);
2005-04-16 15:20:36 -07:00
kmem_cache_free(secpath_cachep, sp);
}
EXPORT_SYMBOL(__secpath_destroy);
struct sec_path *secpath_dup(struct sec_path *src)
{
struct sec_path *sp;
2006-12-06 20:33:16 -08:00
sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
2005-04-16 15:20:36 -07:00
if (!sp)
return NULL;
sp->len = 0;
if (src) {
int i;
memcpy(sp, src, sizeof(*sp));
for (i = 0; i < sp->len; i++)
2006-04-01 00:54:16 -08:00
xfrm_state_hold(sp->xvec[i]);
2005-04-16 15:20:36 -07:00
}
atomic_set(&sp->refcnt, 1);
return sp;
}
EXPORT_SYMBOL(secpath_dup);
/* Fetch spi and seq from ipsec header */
2006-09-27 18:47:59 -07:00
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
2005-04-16 15:20:36 -07:00
{
int offset, offset_seq;
2007-10-17 21:30:34 -07:00
int hlen;
2005-04-16 15:20:36 -07:00
switch (nexthdr) {
case IPPROTO_AH:
2007-10-17 21:30:34 -07:00
hlen = sizeof(struct ip_auth_hdr);
2005-04-16 15:20:36 -07:00
offset = offsetof(struct ip_auth_hdr, spi);
offset_seq = offsetof(struct ip_auth_hdr, seq_no);
break;
case IPPROTO_ESP:
2007-10-17 21:30:34 -07:00
hlen = sizeof(struct ip_esp_hdr);
2005-04-16 15:20:36 -07:00
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;
*spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2)));
2005-04-16 15:20:36 -07:00
*seq = 0;
return 0;
default:
return 1;
}
2007-10-17 21:30:34 -07:00
if (!pskb_may_pull(skb, hlen))
2005-04-16 15:20:36 -07:00
return -EINVAL;
*spi = *(__be32*)(skb_transport_header(skb) + offset);
*seq = *(__be32*)(skb_transport_header(skb) + offset_seq);
2005-04-16 15:20:36 -07:00
return 0;
}
EXPORT_SYMBOL(xfrm_parse_spi);
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);
2007-11-13 21:44:23 -08:00
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
int err;
__be32 seq;
struct xfrm_state *x;
2007-11-19 18:50:17 -08:00
xfrm_address_t *daddr;
unsigned int family;
2007-11-13 21:44:23 -08:00
int decaps = 0;
2007-11-19 18:50:17 -08:00
int async = 0;
/* A negative encap_type indicates async resumption. */
if (encap_type < 0) {
async = 1;
2007-12-11 01:53:43 -08:00
x = xfrm_input_state(skb);
2007-11-19 18:50:17 -08:00
seq = XFRM_SKB_CB(skb)->seq;
goto resume;
}
2007-11-13 21:44:23 -08:00
/* 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) {
XFRM_INC_STATS(LINUX_MIB_XFRMINERROR);
goto drop;
}
if (skb->sp)
secpath_put(skb->sp);
skb->sp = sp;
}
2007-11-19 18:50:17 -08:00
daddr = (xfrm_address_t *)(skb_network_header(skb) +
XFRM_SPI_SKB_CB(skb)->daddroff);
family = XFRM_SPI_SKB_CB(skb)->family;
2007-11-19 18:50:17 -08:00
2007-11-13 21:44:23 -08:00
seq = 0;
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
2007-11-13 21:44:23 -08:00
goto drop;
}
2007-11-13 21:44:23 -08:00
do {
if (skb->sp->len == XFRM_MAX_DEPTH) {
XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR);
2007-11-13 21:44:23 -08:00
goto drop;
}
2007-11-13 21:44:23 -08:00
x = xfrm_state_lookup(daddr, spi, nexthdr, family);
if (x == NULL) {
XFRM_INC_STATS(LINUX_MIB_XFRMINNOSTATES);
2007-11-13 21:44:23 -08:00
goto drop;
}
2007-11-13 21:44:23 -08:00
skb->sp->xvec[skb->sp->len++] = x;
2007-11-13 21:44:23 -08:00
spin_lock(&x->lock);
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEINVALID);
2007-11-13 21:44:23 -08:00
goto drop_unlock;
}
2007-11-13 21:44:23 -08:00
if ((x->encap ? x->encap->encap_type : 0) != encap_type) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEINVALID);
2007-11-13 21:44:23 -08:00
goto drop_unlock;
}
2007-11-13 21:44:23 -08:00
if (x->props.replay_window && xfrm_replay_check(x, seq)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSEQOUTOFWINDOW);
2007-11-13 21:44:23 -08:00
goto drop_unlock;
}
2007-11-13 21:44:23 -08:00
if (xfrm_state_check_expire(x)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEEXPIRED);
2007-11-13 21:44:23 -08:00
goto drop_unlock;
}
2007-11-13 21:44:23 -08:00
spin_unlock(&x->lock);
2007-11-19 18:50:17 -08:00
XFRM_SKB_CB(skb)->seq = seq;
2007-11-13 21:44:23 -08:00
nexthdr = x->type->input(x, skb);
2007-11-19 18:50:17 -08:00
if (nexthdr == -EINPROGRESS)
return 0;
resume:
spin_lock(&x->lock);
if (nexthdr <= 0) {
if (nexthdr == -EBADMSG)
x->stats.integrity_failed++;
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEPROTOERROR);
2007-11-13 21:44:23 -08:00
goto drop_unlock;
}
2007-11-13 21:44:23 -08:00
/* 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);
2007-11-19 18:47:58 -08:00
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
if (x->inner_mode->input(x, skb)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
2007-11-13 21:44:23 -08:00
goto drop;
}
2007-11-13 21:44:23 -08:00
if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
decaps = 1;
break;
}
2007-11-19 18:50:17 -08:00
/*
* We need the inner address. However, we only get here for
* transport mode so the outer address is identical.
*/
daddr = &x->id.daddr;
family = x->outer_mode->afinfo->family;
2007-11-19 18:50:17 -08:00
2007-11-13 21:44:23 -08:00
err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
if (err < 0) {
XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
2007-11-13 21:44:23 -08:00
goto drop;
}
2007-11-13 21:44:23 -08:00
} while (!err);
nf_reset(skb);
if (decaps) {
dst_release(skb->dst);
skb->dst = NULL;
netif_rx(skb);
return 0;
} else {
2007-11-19 18:50:17 -08:00
return x->inner_mode->afinfo->transport_finish(skb, async);
2007-11-13 21:44:23 -08:00
}
drop_unlock:
spin_unlock(&x->lock);
drop:
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL(xfrm_input);
2007-11-19 18:50:17 -08:00
int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
{
return xfrm_input(skb, nexthdr, 0, -1);
}
EXPORT_SYMBOL(xfrm_input_resume);
2005-04-16 15:20:36 -07:00
void __init xfrm_input_init(void)
{
secpath_cachep = kmem_cache_create("secpath_cache",
sizeof(struct sec_path),
2006-08-26 19:25:52 -07:00
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
2005-04-16 15:20:36 -07:00
}