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 ...@@ -123,6 +123,12 @@ struct xfrm_state
/* Data for encapsulator */ /* Data for encapsulator */
struct xfrm_encap_tmpl *encap; 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 */ /* State for replay detection */
struct xfrm_replay_state replay; struct xfrm_replay_state replay;
...@@ -196,6 +202,8 @@ extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); ...@@ -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 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_put_afinfo(struct xfrm_state_afinfo *afinfo);
extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
struct xfrm_decap_state; struct xfrm_decap_state;
struct xfrm_type struct xfrm_type
{ {
...@@ -699,6 +707,11 @@ xfrm_state_addr_check(struct xfrm_state *x, ...@@ -699,6 +707,11 @@ xfrm_state_addr_check(struct xfrm_state *x,
return 0; return 0;
} }
static inline int xfrm_state_kern(struct xfrm_state *x)
{
return atomic_read(&x->tunnel_users);
}
/* /*
* xfrm algorithm information * xfrm algorithm information
*/ */
......
...@@ -269,6 +269,67 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) ...@@ -269,6 +269,67 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
xfrm_state_put(x); 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) static void ipcomp_free_data(struct ipcomp_data *ipcd)
{ {
if (ipcd->tfm) if (ipcd->tfm)
...@@ -308,6 +369,12 @@ static int ipcomp_init_state(struct xfrm_state *x, void *args) ...@@ -308,6 +369,12 @@ static int ipcomp_init_state(struct xfrm_state *x, void *args)
if (!ipcd->tfm) if (!ipcd->tfm)
goto error; 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); calg_desc = xfrm_calg_get_byname(x->calg->alg_name);
BUG_ON(!calg_desc); BUG_ON(!calg_desc);
ipcd->threshold = calg_desc->uinfo.comp.threshold; ipcd->threshold = calg_desc->uinfo.comp.threshold;
......
...@@ -163,36 +163,32 @@ static int ipip_rcv(struct sk_buff *skb) ...@@ -163,36 +163,32 @@ static int ipip_rcv(struct sk_buff *skb)
skb->nh.iph->saddr, skb->nh.iph->saddr,
IPPROTO_IPIP, AF_INET); IPPROTO_IPIP, AF_INET);
if (x) { if (!x)
spin_lock(&x->lock); goto drop;
if (unlikely(x->km.state != XFRM_STATE_VALID)) spin_lock(&x->lock);
goto drop_unlock;
} if (unlikely(x->km.state != XFRM_STATE_VALID))
goto drop_unlock;
err = ipip_xfrm_rcv(x, NULL, skb); err = ipip_xfrm_rcv(x, NULL, skb);
if (err) if (err)
goto drop_unlock; goto drop_unlock;
if (x) { x->curlft.bytes += skb->len;
x->curlft.bytes += skb->len; x->curlft.packets++;
x->curlft.packets++; spin_unlock(&x->lock);
xfrm_state_put(x);
spin_unlock(&x->lock); out:
return err;
xfrm_state_put(x);
}
return 0;
drop_unlock: drop_unlock:
if (x) { spin_unlock(&x->lock);
spin_unlock(&x->lock); xfrm_state_put(x);
xfrm_state_put(x); drop:
} err = NET_RX_DROP;
kfree_skb(skb); kfree_skb(skb);
out: goto out;
return 0;
} }
static void ipip_err(struct sk_buff *skb, u32 info) 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, ...@@ -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; x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x); xfrm_state_put(x);
xfrm_state_put(x1); xfrm_state_put(x1);
...@@ -1286,6 +1287,11 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h ...@@ -1286,6 +1287,11 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
if (x == NULL) if (x == NULL)
return -ESRCH; return -ESRCH;
if (xfrm_state_kern(x)) {
xfrm_state_put(x);
return -EPERM;
}
xfrm_state_delete(x); xfrm_state_delete(x);
xfrm_state_put(x); xfrm_state_put(x);
......
...@@ -304,6 +304,7 @@ EXPORT_SYMBOL(xfrm_state_register_afinfo); ...@@ -304,6 +304,7 @@ EXPORT_SYMBOL(xfrm_state_register_afinfo);
EXPORT_SYMBOL(xfrm_state_unregister_afinfo); EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
EXPORT_SYMBOL(xfrm_state_get_afinfo); EXPORT_SYMBOL(xfrm_state_get_afinfo);
EXPORT_SYMBOL(xfrm_state_put_afinfo); EXPORT_SYMBOL(xfrm_state_put_afinfo);
EXPORT_SYMBOL(xfrm_state_delete_tunnel);
EXPORT_SYMBOL(xfrm_replay_check); EXPORT_SYMBOL(xfrm_replay_check);
EXPORT_SYMBOL(xfrm_replay_advance); EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors); EXPORT_SYMBOL(xfrm_check_selectors);
......
...@@ -172,6 +172,7 @@ struct xfrm_state *xfrm_state_alloc(void) ...@@ -172,6 +172,7 @@ struct xfrm_state *xfrm_state_alloc(void)
if (x) { if (x) {
memset(x, 0, sizeof(struct xfrm_state)); memset(x, 0, sizeof(struct xfrm_state));
atomic_set(&x->refcnt, 1); atomic_set(&x->refcnt, 1);
atomic_set(&x->tunnel_users, 0);
INIT_LIST_HEAD(&x->bydst); INIT_LIST_HEAD(&x->bydst);
INIT_LIST_HEAD(&x->byspi); INIT_LIST_HEAD(&x->byspi);
init_timer(&x->timer); init_timer(&x->timer);
...@@ -234,6 +235,7 @@ static void __xfrm_state_delete(struct xfrm_state *x) ...@@ -234,6 +235,7 @@ static void __xfrm_state_delete(struct xfrm_state *x)
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); spin_lock_bh(&x->lock);
__xfrm_state_delete(x); __xfrm_state_delete(x);
spin_unlock_bh(&x->lock); spin_unlock_bh(&x->lock);
...@@ -248,7 +250,8 @@ void xfrm_state_flush(u8 proto) ...@@ -248,7 +250,8 @@ void xfrm_state_flush(u8 proto)
for (i = 0; i < XFRM_DST_HSIZE; i++) { for (i = 0; i < XFRM_DST_HSIZE; i++) {
restart: restart:
list_for_each_entry(x, xfrm_state_bydst+i, bydst) { 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); xfrm_state_hold(x);
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
...@@ -790,6 +793,20 @@ void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) ...@@ -790,6 +793,20 @@ void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
read_unlock(&afinfo->lock); 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) void __init xfrm_state_init(void)
{ {
int i; int i;
......
...@@ -281,6 +281,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) ...@@ -281,6 +281,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (x == NULL) if (x == NULL)
return -ESRCH; return -ESRCH;
if (xfrm_state_kern(x)) {
xfrm_state_put(x);
return -EPERM;
}
xfrm_state_delete(x); xfrm_state_delete(x);
xfrm_state_put(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