aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_policy.c25
-rw-r--r--net/xfrm/xfrm_state.c116
-rw-r--r--net/xfrm/xfrm_user.c399
3 files changed, 490 insertions, 50 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 98ec53bd3ac7..f5eae9febd26 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -26,8 +26,8 @@
#include <net/xfrm.h>
#include <net/ip.h>
-DECLARE_MUTEX(xfrm_cfg_sem);
-EXPORT_SYMBOL(xfrm_cfg_sem);
+DEFINE_MUTEX(xfrm_cfg_mutex);
+EXPORT_SYMBOL(xfrm_cfg_mutex);
static DEFINE_RWLOCK(xfrm_policy_lock);
@@ -203,7 +203,7 @@ static void xfrm_policy_timer(unsigned long data)
}
if (warn)
- km_policy_expired(xp, dir, 0);
+ km_policy_expired(xp, dir, 0, 0);
if (next != LONG_MAX &&
!mod_timer(&xp->timer, jiffies + make_jiffies(next)))
xfrm_pol_hold(xp);
@@ -216,7 +216,7 @@ out:
expired:
read_unlock(&xp->lock);
if (!xfrm_policy_delete(xp, dir))
- km_policy_expired(xp, dir, 1);
+ km_policy_expired(xp, dir, 1, 0);
xfrm_pol_put(xp);
}
@@ -621,6 +621,7 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir)
}
return -ENOENT;
}
+EXPORT_SYMBOL(xfrm_policy_delete);
int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
{
@@ -782,7 +783,7 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
int nx = 0;
int err;
u32 genid;
- u16 family = dst_orig->ops->family;
+ u16 family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
u32 sk_sid = security_sk_sid(sk, fl, dir);
restart:
@@ -796,13 +797,14 @@ restart:
if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0;
- policy = flow_cache_lookup(fl, sk_sid, family, dir,
- xfrm_policy_lookup);
+ policy = flow_cache_lookup(fl, sk_sid, dst_orig->ops->family,
+ dir, xfrm_policy_lookup);
}
if (!policy)
return 0;
+ family = dst_orig->ops->family;
policy->curlft.use_time = (unsigned long)xtime.tv_sec;
switch (policy->action) {
@@ -885,8 +887,6 @@ restart:
* We can't enlist stable bundles either.
*/
write_unlock_bh(&policy->lock);
-
- xfrm_pol_put(policy);
if (dst)
dst_free(dst);
@@ -997,13 +997,6 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
struct sec_decap_state *xvec = &(skb->sp->x[i]);
if (!xfrm_selector_match(&xvec->xvec->sel, &fl, family))
return 0;
-
- /* If there is a post_input processor, try running it */
- if (xvec->xvec->type->post_input &&
- (xvec->xvec->type->post_input)(xvec->xvec,
- &(xvec->decap),
- skb) != 0)
- return 0;
}
}
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index e12d0be5f976..a8e14dc1b04e 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -20,6 +20,15 @@
#include <linux/module.h>
#include <asm/uaccess.h>
+struct sock *xfrm_nl;
+EXPORT_SYMBOL(xfrm_nl);
+
+u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
+EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
+
+u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
+EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
+
/* Each xfrm_state may be linked to two tables:
1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
@@ -50,18 +59,20 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock);
static int xfrm_state_gc_flush_bundles;
-static int __xfrm_state_delete(struct xfrm_state *x);
+int __xfrm_state_delete(struct xfrm_state *x);
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
-static int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
-static void km_state_expired(struct xfrm_state *x, int hard);
+int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
+void km_state_expired(struct xfrm_state *x, int hard, u32 pid);
static void xfrm_state_gc_destroy(struct xfrm_state *x)
{
if (del_timer(&x->timer))
BUG();
+ if (del_timer(&x->rtimer))
+ BUG();
kfree(x->aalg);
kfree(x->ealg);
kfree(x->calg);
@@ -153,7 +164,7 @@ static void xfrm_timer_handler(unsigned long data)
x->km.dying = warn;
if (warn)
- km_state_expired(x, 0);
+ km_state_expired(x, 0, 0);
resched:
if (next != LONG_MAX &&
!mod_timer(&x->timer, jiffies + make_jiffies(next)))
@@ -168,13 +179,15 @@ expired:
goto resched;
}
if (!__xfrm_state_delete(x) && x->id.spi)
- km_state_expired(x, 1);
+ km_state_expired(x, 1, 0);
out:
spin_unlock(&x->lock);
xfrm_state_put(x);
}
+static void xfrm_replay_timer_handler(unsigned long data);
+
struct xfrm_state *xfrm_state_alloc(void)
{
struct xfrm_state *x;
@@ -190,11 +203,16 @@ struct xfrm_state *xfrm_state_alloc(void)
init_timer(&x->timer);
x->timer.function = xfrm_timer_handler;
x->timer.data = (unsigned long)x;
+ init_timer(&x->rtimer);
+ x->rtimer.function = xfrm_replay_timer_handler;
+ x->rtimer.data = (unsigned long)x;
x->curlft.add_time = (unsigned long)xtime.tv_sec;
x->lft.soft_byte_limit = XFRM_INF;
x->lft.soft_packet_limit = XFRM_INF;
x->lft.hard_byte_limit = XFRM_INF;
x->lft.hard_packet_limit = XFRM_INF;
+ x->replay_maxage = 0;
+ x->replay_maxdiff = 0;
spin_lock_init(&x->lock);
}
return x;
@@ -212,7 +230,7 @@ void __xfrm_state_destroy(struct xfrm_state *x)
}
EXPORT_SYMBOL(__xfrm_state_destroy);
-static int __xfrm_state_delete(struct xfrm_state *x)
+int __xfrm_state_delete(struct xfrm_state *x)
{
int err = -ESRCH;
@@ -220,14 +238,16 @@ static int __xfrm_state_delete(struct xfrm_state *x)
x->km.state = XFRM_STATE_DEAD;
spin_lock(&xfrm_state_lock);
list_del(&x->bydst);
- atomic_dec(&x->refcnt);
+ __xfrm_state_put(x);
if (x->id.spi) {
list_del(&x->byspi);
- atomic_dec(&x->refcnt);
+ __xfrm_state_put(x);
}
spin_unlock(&xfrm_state_lock);
if (del_timer(&x->timer))
- atomic_dec(&x->refcnt);
+ __xfrm_state_put(x);
+ if (del_timer(&x->rtimer))
+ __xfrm_state_put(x);
/* The number two in this test is the reference
* mentioned in the comment below plus the reference
@@ -243,12 +263,13 @@ static int __xfrm_state_delete(struct xfrm_state *x)
* The xfrm_state_alloc call gives a reference, and that
* is what we are dropping here.
*/
- atomic_dec(&x->refcnt);
+ __xfrm_state_put(x);
err = 0;
}
return err;
}
+EXPORT_SYMBOL(__xfrm_state_delete);
int xfrm_state_delete(struct xfrm_state *x)
{
@@ -426,6 +447,10 @@ static void __xfrm_state_insert(struct xfrm_state *x)
if (!mod_timer(&x->timer, jiffies + HZ))
xfrm_state_hold(x);
+ if (x->replay_maxage &&
+ !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
+ xfrm_state_hold(x);
+
wake_up(&km_waitq);
}
@@ -580,7 +605,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
(x->curlft.bytes >= x->lft.soft_byte_limit ||
x->curlft.packets >= x->lft.soft_packet_limit)) {
x->km.dying = 1;
- km_state_expired(x, 0);
+ km_state_expired(x, 0, 0);
}
return 0;
}
@@ -762,6 +787,61 @@ out:
}
EXPORT_SYMBOL(xfrm_state_walk);
+
+void xfrm_replay_notify(struct xfrm_state *x, int event)
+{
+ struct km_event c;
+ /* we send notify messages in case
+ * 1. we updated on of the sequence numbers, and the seqno difference
+ * is at least x->replay_maxdiff, in this case we also update the
+ * timeout of our timer function
+ * 2. if x->replay_maxage has elapsed since last update,
+ * and there were changes
+ *
+ * The state structure must be locked!
+ */
+
+ switch (event) {
+ case XFRM_REPLAY_UPDATE:
+ if (x->replay_maxdiff &&
+ (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
+ (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))
+ return;
+
+ break;
+
+ case XFRM_REPLAY_TIMEOUT:
+ if ((x->replay.seq == x->preplay.seq) &&
+ (x->replay.bitmap == x->preplay.bitmap) &&
+ (x->replay.oseq == x->preplay.oseq))
+ return;
+
+ break;
+ }
+
+ memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
+ c.event = XFRM_MSG_NEWAE;
+ c.data.aevent = event;
+ km_state_notify(x, &c);
+
+ if (x->replay_maxage &&
+ !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
+ xfrm_state_hold(x);
+}
+EXPORT_SYMBOL(xfrm_replay_notify);
+
+static void xfrm_replay_timer_handler(unsigned long data)
+{
+ struct xfrm_state *x = (struct xfrm_state*)data;
+
+ spin_lock(&x->lock);
+
+ if (xfrm_aevent_is_on() && x->km.state == XFRM_STATE_VALID)
+ xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
+
+ spin_unlock(&x->lock);
+}
+
int xfrm_replay_check(struct xfrm_state *x, u32 seq)
{
u32 diff;
@@ -805,6 +885,9 @@ void xfrm_replay_advance(struct xfrm_state *x, u32 seq)
diff = x->replay.seq - seq;
x->replay.bitmap |= (1U << diff);
}
+
+ if (xfrm_aevent_is_on())
+ xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
}
EXPORT_SYMBOL(xfrm_replay_advance);
@@ -835,11 +918,12 @@ void km_state_notify(struct xfrm_state *x, struct km_event *c)
EXPORT_SYMBOL(km_policy_notify);
EXPORT_SYMBOL(km_state_notify);
-static void km_state_expired(struct xfrm_state *x, int hard)
+void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
{
struct km_event c;
c.data.hard = hard;
+ c.pid = pid;
c.event = XFRM_MSG_EXPIRE;
km_state_notify(x, &c);
@@ -847,11 +931,12 @@ static void km_state_expired(struct xfrm_state *x, int hard)
wake_up(&km_waitq);
}
+EXPORT_SYMBOL(km_state_expired);
/*
* We send to all registered managers regardless of failure
* We are happy with one success
*/
-static int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
+int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
{
int err = -EINVAL, acqret;
struct xfrm_mgr *km;
@@ -865,6 +950,7 @@ static int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_polic
read_unlock(&xfrm_km_lock);
return err;
}
+EXPORT_SYMBOL(km_query);
int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
{
@@ -883,17 +969,19 @@ int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
}
EXPORT_SYMBOL(km_new_mapping);
-void km_policy_expired(struct xfrm_policy *pol, int dir, int hard)
+void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
{
struct km_event c;
c.data.hard = hard;
+ c.pid = pid;
c.event = XFRM_MSG_POLEXPIRE;
km_policy_notify(pol, dir, &c);
if (hard)
wake_up(&km_waitq);
}
+EXPORT_SYMBOL(km_policy_expired);
int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
{
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index ac87a09ba83e..81d1005830f4 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -28,8 +28,6 @@
#include <net/netlink.h>
#include <asm/uaccess.h>
-static struct sock *xfrm_nl;
-
static int verify_one_alg(struct rtattr **xfrma, enum xfrm_attr_type_t type)
{
struct rtattr *rt = xfrma[type - 1];
@@ -103,9 +101,6 @@ static inline int verify_sec_ctx_len(struct rtattr **xfrma)
uctx = RTA_DATA(rt);
- if (uctx->ctx_len > PAGE_SIZE)
- return -EINVAL;
-
len += sizeof(struct xfrm_user_sec_ctx);
len += uctx->ctx_len;
@@ -276,6 +271,56 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
x->props.flags = p->flags;
}
+/*
+ * someday when pfkey also has support, we could have the code
+ * somehow made shareable and move it to xfrm_state.c - JHS
+ *
+*/
+static int xfrm_update_ae_params(struct xfrm_state *x, struct rtattr **xfrma)
+{
+ int err = - EINVAL;
+ struct rtattr *rp = xfrma[XFRMA_REPLAY_VAL-1];
+ struct rtattr *lt = xfrma[XFRMA_LTIME_VAL-1];
+ struct rtattr *et = xfrma[XFRMA_ETIMER_THRESH-1];
+ struct rtattr *rt = xfrma[XFRMA_REPLAY_THRESH-1];
+
+ if (rp) {
+ struct xfrm_replay_state *replay;
+ if (RTA_PAYLOAD(rp) < sizeof(*replay))
+ goto error;
+ replay = RTA_DATA(rp);
+ memcpy(&x->replay, replay, sizeof(*replay));
+ memcpy(&x->preplay, replay, sizeof(*replay));
+ }
+
+ if (lt) {
+ struct xfrm_lifetime_cur *ltime;
+ if (RTA_PAYLOAD(lt) < sizeof(*ltime))
+ goto error;
+ ltime = RTA_DATA(lt);
+ x->curlft.bytes = ltime->bytes;
+ x->curlft.packets = ltime->packets;
+ x->curlft.add_time = ltime->add_time;
+ x->curlft.use_time = ltime->use_time;
+ }
+
+ if (et) {
+ if (RTA_PAYLOAD(et) < sizeof(u32))
+ goto error;
+ x->replay_maxage = *(u32*)RTA_DATA(et);
+ }
+
+ if (rt) {
+ if (RTA_PAYLOAD(rt) < sizeof(u32))
+ goto error;
+ x->replay_maxdiff = *(u32*)RTA_DATA(rt);
+ }
+
+ return 0;
+error:
+ return err;
+}
+
static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
struct rtattr **xfrma,
int *errp)
@@ -311,6 +356,18 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
goto error;
x->km.seq = p->seq;
+ x->replay_maxdiff = sysctl_xfrm_aevent_rseqth;
+ /* sysctl_xfrm_aevent_etime is in 100ms units */
+ x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M;
+ x->preplay.bitmap = 0;
+ x->preplay.seq = x->replay.seq+x->replay_maxdiff;
+ x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;
+
+ /* override default values from above */
+
+ err = xfrm_update_ae_params(x, (struct rtattr **)xfrma);
+ if (err < 0)
+ goto error;
return x;
@@ -345,7 +402,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
- xfrm_state_put(x);
+ __xfrm_state_put(x);
goto out;
}
@@ -1025,9 +1082,142 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma
return 0;
}
-static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+
+static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c)
+{
+ struct xfrm_aevent_id *id;
+ struct nlmsghdr *nlh;
+ struct xfrm_lifetime_cur ltime;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id));
+ id = NLMSG_DATA(nlh);
+ nlh->nlmsg_flags = 0;
+
+ id->sa_id.daddr = x->id.daddr;
+ id->sa_id.spi = x->id.spi;
+ id->sa_id.family = x->props.family;
+ id->sa_id.proto = x->id.proto;
+ id->flags = c->data.aevent;
+
+ RTA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay);
+
+ ltime.bytes = x->curlft.bytes;
+ ltime.packets = x->curlft.packets;
+ ltime.add_time = x->curlft.add_time;
+ ltime.use_time = x->curlft.use_time;
+
+ RTA_PUT(skb, XFRMA_LTIME_VAL, sizeof(struct xfrm_lifetime_cur), &ltime);
+
+ if (id->flags&XFRM_AE_RTHR) {
+ RTA_PUT(skb,XFRMA_REPLAY_THRESH,sizeof(u32),&x->replay_maxdiff);
+ }
+
+ if (id->flags&XFRM_AE_ETHR) {
+ u32 etimer = x->replay_maxage*10/HZ;
+ RTA_PUT(skb,XFRMA_ETIMER_THRESH,sizeof(u32),&etimer);
+ }
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+rtattr_failure:
+nlmsg_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+ struct xfrm_state *x;
+ struct sk_buff *r_skb;
+ int err;
+ struct km_event c;
+ struct xfrm_aevent_id *p = NLMSG_DATA(nlh);
+ int len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id));
+ struct xfrm_usersa_id *id = &p->sa_id;
+
+ len += RTA_SPACE(sizeof(struct xfrm_replay_state));
+ len += RTA_SPACE(sizeof(struct xfrm_lifetime_cur));
+
+ if (p->flags&XFRM_AE_RTHR)
+ len+=RTA_SPACE(sizeof(u32));
+
+ if (p->flags&XFRM_AE_ETHR)
+ len+=RTA_SPACE(sizeof(u32));
+
+ r_skb = alloc_skb(len, GFP_ATOMIC);
+ if (r_skb == NULL)
+ return -ENOMEM;
+
+ x = xfrm_state_lookup(&id->daddr, id->spi, id->proto, id->family);
+ if (x == NULL) {
+ kfree(r_skb);
+ return -ESRCH;
+ }
+
+ /*
+ * XXX: is this lock really needed - none of the other
+ * gets lock (the concern is things getting updated
+ * while we are still reading) - jhs
+ */
+ spin_lock_bh(&x->lock);
+ c.data.aevent = p->flags;
+ c.seq = nlh->nlmsg_seq;
+ c.pid = nlh->nlmsg_pid;
+
+ if (build_aevent(r_skb, x, &c) < 0)
+ BUG();
+ err = netlink_unicast(xfrm_nl, r_skb,
+ NETLINK_CB(skb).pid, MSG_DONTWAIT);
+ spin_unlock_bh(&x->lock);
+ xfrm_state_put(x);
+ return err;
+}
+
+static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
{
+ struct xfrm_state *x;
struct km_event c;
+ int err = - EINVAL;
+ struct xfrm_aevent_id *p = NLMSG_DATA(nlh);
+ struct rtattr *rp = xfrma[XFRMA_REPLAY_VAL-1];
+ struct rtattr *lt = xfrma[XFRMA_LTIME_VAL-1];
+
+ if (!lt && !rp)
+ return err;
+
+ /* pedantic mode - thou shalt sayeth replaceth */
+ if (!(nlh->nlmsg_flags&NLM_F_REPLACE))
+ return err;
+
+ x = xfrm_state_lookup(&p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
+ if (x == NULL)
+ return -ESRCH;
+
+ if (x->km.state != XFRM_STATE_VALID)
+ goto out;
+
+ spin_lock_bh(&x->lock);
+ err = xfrm_update_ae_params(x,(struct rtattr **)xfrma);
+ spin_unlock_bh(&x->lock);
+ if (err < 0)
+ goto out;
+
+ c.event = nlh->nlmsg_type;
+ c.seq = nlh->nlmsg_seq;
+ c.pid = nlh->nlmsg_pid;
+ c.data.aevent = XFRM_AE_CU;
+ km_state_notify(x, &c);
+ err = 0;
+out:
+ xfrm_state_put(x);
+ return err;
+}
+
+static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+struct km_event c;
xfrm_policy_flush();
c.event = nlh->nlmsg_type;
@@ -1037,6 +1227,139 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, void **x
return 0;
}
+static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+ struct xfrm_policy *xp;
+ struct xfrm_user_polexpire *up = NLMSG_DATA(nlh);
+ struct xfrm_userpolicy_info *p = &up->pol;
+ int err = -ENOENT;
+
+ if (p->index)
+ xp = xfrm_policy_byid(p->dir, p->index, 0);
+ else {
+ struct rtattr **rtattrs = (struct rtattr **)xfrma;
+ struct rtattr *rt = rtattrs[XFRMA_SEC_CTX-1];
+ struct xfrm_policy tmp;
+
+ err = verify_sec_ctx_len(rtattrs);
+ if (err)
+ return err;
+
+ memset(&tmp, 0, sizeof(struct xfrm_policy));
+ if (rt) {
+ struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt);
+
+ if ((err = security_xfrm_policy_alloc(&tmp, uctx)))
+ return err;
+ }
+ xp = xfrm_policy_bysel_ctx(p->dir, &p->sel, tmp.security, 0);
+ security_xfrm_policy_free(&tmp);
+ }
+
+ if (xp == NULL)
+ return err;
+ read_lock(&xp->lock);
+ if (xp->dead) {
+ read_unlock(&xp->lock);
+ goto out;
+ }
+
+ read_unlock(&xp->lock);
+ err = 0;
+ if (up->hard) {
+ xfrm_policy_delete(xp, p->dir);
+ } else {
+ // reset the timers here?
+ printk("Dont know what to do with soft policy expire\n");
+ }
+ km_policy_expired(xp, p->dir, up->hard, current->pid);
+
+out:
+ xfrm_pol_put(xp);
+ return err;
+}
+
+static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+ struct xfrm_state *x;
+ int err;
+ struct xfrm_user_expire *ue = NLMSG_DATA(nlh);
+ struct xfrm_usersa_info *p = &ue->state;
+
+ x = xfrm_state_lookup(&p->id.daddr, p->id.spi, p->id.proto, p->family);
+ err = -ENOENT;
+
+ if (x == NULL)
+ return err;
+
+ err = -EINVAL;
+
+ spin_lock_bh(&x->lock);
+ if (x->km.state != XFRM_STATE_VALID)
+ goto out;
+ km_state_expired(x, ue->hard, current->pid);
+
+ if (ue->hard)
+ __xfrm_state_delete(x);
+out:
+ spin_unlock_bh(&x->lock);
+ xfrm_state_put(x);
+ return err;
+}
+
+static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+ struct xfrm_policy *xp;
+ struct xfrm_user_tmpl *ut;
+ int i;
+ struct rtattr *rt = xfrma[XFRMA_TMPL-1];
+
+ struct xfrm_user_acquire *ua = NLMSG_DATA(nlh);
+ struct xfrm_state *x = xfrm_state_alloc();
+ int err = -ENOMEM;
+
+ if (!x)
+ return err;
+
+ err = verify_newpolicy_info(&ua->policy);
+ if (err) {
+ printk("BAD policy passed\n");
+ kfree(x);
+ return err;
+ }
+
+ /* build an XP */
+ xp = xfrm_policy_construct(&ua->policy, (struct rtattr **) xfrma, &err); if (!xp) {
+ kfree(x);
+ return err;
+ }
+
+ memcpy(&x->id, &ua->id, sizeof(ua->id));
+ memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
+ memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
+
+ ut = RTA_DATA(rt);
+ /* extract the templates and for each call km_key */
+ for (i = 0; i < xp->xfrm_nr; i++, ut++) {
+ struct xfrm_tmpl *t = &xp->xfrm_vec[i];
+ memcpy(&x->id, &t->id, sizeof(x->id));
+ x->props.mode = t->mode;
+ x->props.reqid = t->reqid;
+ x->props.family = ut->family;
+ t->aalgos = ua->aalgos;
+ t->ealgos = ua->ealgos;
+ t->calgos = ua->calgos;
+ err = km_query(x, t, xp);
+
+ }
+
+ kfree(x);
+ kfree(xp);
+
+ return 0;
+}
+
+
#define XMSGSIZE(type) NLMSG_LENGTH(sizeof(struct type))
static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
@@ -1054,6 +1377,8 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire),
[XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush),
[XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = NLMSG_LENGTH(0),
+ [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
+ [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
};
#undef XMSGSIZE
@@ -1071,10 +1396,15 @@ static struct xfrm_link {
[XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy,
.dump = xfrm_dump_policy },
[XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi },
+ [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire },
+ [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire },
[XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy },
[XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa },
+ [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire},
[XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa },
[XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy },
+ [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae },
+ [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae },
};
static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
@@ -1156,26 +1486,26 @@ static void xfrm_netlink_rcv(struct sock *sk, int len)
unsigned int qlen = 0;
do {
- down(&xfrm_cfg_sem);
+ mutex_lock(&xfrm_cfg_mutex);
netlink_run_queue(sk, &qlen, &xfrm_user_rcv_msg);
- up(&xfrm_cfg_sem);
+ mutex_unlock(&xfrm_cfg_mutex);
} while (qlen);
}
-static int build_expire(struct sk_buff *skb, struct xfrm_state *x, int hard)
+static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c)
{
struct xfrm_user_expire *ue;
struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
- nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_EXPIRE,
+ nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_EXPIRE,
sizeof(*ue));
ue = NLMSG_DATA(nlh);
nlh->nlmsg_flags = 0;
copy_to_user_state(x, &ue->state);
- ue->hard = (hard != 0) ? 1 : 0;
+ ue->hard = (c->data.hard != 0) ? 1 : 0;
nlh->nlmsg_len = skb->tail - b;
return skb->len;
@@ -1194,13 +1524,31 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
if (skb == NULL)
return -ENOMEM;
- if (build_expire(skb, x, c->data.hard) < 0)
+ if (build_expire(skb, x, c) < 0)
BUG();
NETLINK_CB(skb).dst_group = XFRMNLGRP_EXPIRE;
return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
}
+static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c)
+{
+ struct sk_buff *skb;
+ int len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id));
+
+ len += RTA_SPACE(sizeof(struct xfrm_replay_state));
+ len += RTA_SPACE(sizeof(struct xfrm_lifetime_cur));
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ if (build_aevent(skb, x, c) < 0)
+ BUG();
+
+ NETLINK_CB(skb).dst_group = XFRMNLGRP_AEVENTS;
+ return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
+}
+
static int xfrm_notify_sa_flush(struct km_event *c)
{
struct xfrm_usersa_flush *p;
@@ -1313,6 +1661,8 @@ static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c)
switch (c->event) {
case XFRM_MSG_EXPIRE:
return xfrm_exp_state_notify(x, c);
+ case XFRM_MSG_NEWAE:
+ return xfrm_aevent_state_notify(x, c);
case XFRM_MSG_DELSA:
case XFRM_MSG_UPDSA:
case XFRM_MSG_NEWSA:
@@ -1443,13 +1793,14 @@ static struct xfrm_policy *xfrm_compile_policy(u16 family, int opt,
}
static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
- int dir, int hard)
+ int dir, struct km_event *c)
{
struct xfrm_user_polexpire *upe;
struct nlmsghdr *nlh;
+ int hard = c->data.hard;
unsigned char *b = skb->tail;
- nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe));
+ nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe));
upe = NLMSG_DATA(nlh);
nlh->nlmsg_flags = 0;
@@ -1480,7 +1831,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_eve
if (skb == NULL)
return -ENOMEM;
- if (build_polexpire(skb, xp, dir, c->data.hard) < 0)
+ if (build_polexpire(skb, xp, dir, c) < 0)
BUG();
NETLINK_CB(skb).dst_group = XFRMNLGRP_EXPIRE;
@@ -1596,12 +1947,15 @@ static struct xfrm_mgr netlink_mgr = {
static int __init xfrm_user_init(void)
{
+ struct sock *nlsk;
+
printk(KERN_INFO "Initializing IPsec netlink socket\n");
- xfrm_nl = netlink_kernel_create(NETLINK_XFRM, XFRMNLGRP_MAX,
- xfrm_netlink_rcv, THIS_MODULE);
- if (xfrm_nl == NULL)
+ nlsk = netlink_kernel_create(NETLINK_XFRM, XFRMNLGRP_MAX,
+ xfrm_netlink_rcv, THIS_MODULE);
+ if (nlsk == NULL)
return -ENOMEM;
+ rcu_assign_pointer(xfrm_nl, nlsk);
xfrm_register_km(&netlink_mgr);
@@ -1610,11 +1964,16 @@ static int __init xfrm_user_init(void)
static void __exit xfrm_user_exit(void)
{
+ struct sock *nlsk = xfrm_nl;
+
xfrm_unregister_km(&netlink_mgr);
- sock_release(xfrm_nl->sk_socket);
+ rcu_assign_pointer(xfrm_nl, NULL);
+ synchronize_rcu();
+ sock_release(nlsk->sk_socket);
}
module_init(xfrm_user_init);
module_exit(xfrm_user_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM);
+