Commit 382d3796 authored by James Morris's avatar James Morris Committed by David S. Miller

[IPSEC]: Implement proper IPIP tunnel handling for IPcomp.

parent a2b657a6
......@@ -123,6 +123,12 @@ struct xfrm_state
/* Data for encapsulator */
struct xfrm_encap_tmpl *encap;
/* IPComp needs an IPIP tunnel for handling uncompressed packets */
struct xfrm_state *tunnel;
/* If a tunnel, number of users + 1 */
atomic_t tunnel_users;
/* State for replay detection */
struct xfrm_replay_state replay;
......@@ -196,6 +202,8 @@ extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
struct xfrm_decap_state;
struct xfrm_type
{
......@@ -699,6 +707,11 @@ xfrm_state_addr_check(struct xfrm_state *x,
return 0;
}
static inline int xfrm_state_kern(struct xfrm_state *x)
{
return atomic_read(&x->tunnel_users);
}
/*
* xfrm algorithm information
*/
......
......@@ -269,6 +269,67 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
xfrm_state_put(x);
}
/* We always hold one tunnel user reference to indicate a tunnel */
static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
{
struct xfrm_state *t;
t = xfrm_state_alloc();
if (t == NULL)
goto out;
t->id.proto = IPPROTO_IPIP;
t->id.spi = x->props.saddr.a4;
t->id.daddr.a4 = x->id.daddr.a4;
memcpy(&t->sel, &x->sel, sizeof(t->sel));
t->props.family = AF_INET;
t->props.mode = 1;
t->props.saddr.a4 = x->props.saddr.a4;
t->type = xfrm_get_type(IPPROTO_IPIP, t->props.family);
if (t->type == NULL)
goto error;
if (t->type->init_state(t, NULL))
goto error;
t->km.state = XFRM_STATE_VALID;
atomic_set(&t->tunnel_users, 1);
out:
return t;
error:
xfrm_state_put(t);
t = NULL;
goto out;
}
/*
* Must be protected by xfrm_cfg_sem. State and tunnel user references are
* always incremented on success.
*/
static int ipcomp_tunnel_attach(struct xfrm_state *x)
{
int err = 0;
struct xfrm_state *t;
t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr.a4,
x->props.saddr.a4, IPPROTO_IPIP, AF_INET);
if (!t) {
t = ipcomp_tunnel_create(x);
if (!t) {
err = -EINVAL;
goto out;
}
xfrm_state_insert(t);
xfrm_state_hold(t);
}
x->tunnel = t;
atomic_inc(&t->tunnel_users);
out:
return err;
}
static void ipcomp_free_data(struct ipcomp_data *ipcd)
{
if (ipcd->tfm)
......@@ -308,6 +369,12 @@ static int ipcomp_init_state(struct xfrm_state *x, void *args)
if (!ipcd->tfm)
goto error;
if (x->props.mode) {
err = ipcomp_tunnel_attach(x);
if (err)
goto error;
}
calg_desc = xfrm_calg_get_byname(x->calg->alg_name);
BUG_ON(!calg_desc);
ipcd->threshold = calg_desc->uinfo.comp.threshold;
......
......@@ -163,36 +163,32 @@ static int ipip_rcv(struct sk_buff *skb)
skb->nh.iph->saddr,
IPPROTO_IPIP, AF_INET);
if (x) {
spin_lock(&x->lock);
if (!x)
goto drop;
if (unlikely(x->km.state != XFRM_STATE_VALID))
goto drop_unlock;
}
spin_lock(&x->lock);
if (unlikely(x->km.state != XFRM_STATE_VALID))
goto drop_unlock;
err = ipip_xfrm_rcv(x, NULL, skb);
if (err)
goto drop_unlock;
if (x) {
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock(&x->lock);
xfrm_state_put(x);
}
return 0;
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock(&x->lock);
xfrm_state_put(x);
out:
return err;
drop_unlock:
if (x) {
spin_unlock(&x->lock);
xfrm_state_put(x);
}
spin_unlock(&x->lock);
xfrm_state_put(x);
drop:
err = NET_RX_DROP;
kfree_skb(skb);
out:
return 0;
goto out;
}
static void ipip_err(struct sk_buff *skb, u32 info)
......
......@@ -1241,7 +1241,8 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
}
}
if (x1 && x1->id.spi && hdr->sadb_msg_type == SADB_ADD) {
if (x1 && ((x1->id.spi && hdr->sadb_msg_type == SADB_ADD) ||
(hdr->sadb_msg_type == SADB_UPDATE && xfrm_state_kern(x1)))) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
xfrm_state_put(x1);
......@@ -1286,6 +1287,11 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
if (x == NULL)
return -ESRCH;
if (xfrm_state_kern(x)) {
xfrm_state_put(x);
return -EPERM;
}
xfrm_state_delete(x);
xfrm_state_put(x);
......
......@@ -304,6 +304,7 @@ EXPORT_SYMBOL(xfrm_state_register_afinfo);
EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
EXPORT_SYMBOL(xfrm_state_get_afinfo);
EXPORT_SYMBOL(xfrm_state_put_afinfo);
EXPORT_SYMBOL(xfrm_state_delete_tunnel);
EXPORT_SYMBOL(xfrm_replay_check);
EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors);
......
......@@ -172,6 +172,7 @@ struct xfrm_state *xfrm_state_alloc(void)
if (x) {
memset(x, 0, sizeof(struct xfrm_state));
atomic_set(&x->refcnt, 1);
atomic_set(&x->tunnel_users, 0);
INIT_LIST_HEAD(&x->bydst);
INIT_LIST_HEAD(&x->byspi);
init_timer(&x->timer);
......@@ -234,6 +235,7 @@ static void __xfrm_state_delete(struct xfrm_state *x)
void xfrm_state_delete(struct xfrm_state *x)
{
xfrm_state_delete_tunnel(x);
spin_lock_bh(&x->lock);
__xfrm_state_delete(x);
spin_unlock_bh(&x->lock);
......@@ -248,7 +250,8 @@ void xfrm_state_flush(u8 proto)
for (i = 0; i < XFRM_DST_HSIZE; i++) {
restart:
list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (proto == IPSEC_PROTO_ANY || x->id.proto == proto) {
if (!xfrm_state_kern(x) &&
(proto == IPSEC_PROTO_ANY || x->id.proto == proto)) {
xfrm_state_hold(x);
spin_unlock_bh(&xfrm_state_lock);
......@@ -790,6 +793,20 @@ void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
read_unlock(&afinfo->lock);
}
/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
void xfrm_state_delete_tunnel(struct xfrm_state *x)
{
if (x->tunnel) {
struct xfrm_state *t = x->tunnel;
if (atomic_read(&t->tunnel_users) == 2)
xfrm_state_delete(t);
atomic_dec(&t->tunnel_users);
xfrm_state_put(t);
x->tunnel = NULL;
}
}
void __init xfrm_state_init(void)
{
int i;
......
......@@ -281,6 +281,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (x == NULL)
return -ESRCH;
if (xfrm_state_kern(x)) {
xfrm_state_put(x);
return -EPERM;
}
xfrm_state_delete(x);
xfrm_state_put(x);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment