ipsec: pfkey should ignore events when no listeners
[safe/jmp/linux-2.6] / net / key / af_key.c
index 8b5f486..7470e36 100644 (file)
@@ -48,6 +48,17 @@ struct pfkey_sock {
        struct sock     sk;
        int             registered;
        int             promisc;
+
+       struct {
+               uint8_t         msg_version;
+               uint32_t        msg_pid;
+               int             (*dump)(struct pfkey_sock *sk);
+               void            (*done)(struct pfkey_sock *sk);
+               union {
+                       struct xfrm_policy_walk policy;
+                       struct xfrm_state_walk  state;
+               } u;
+       } dump;
 };
 
 static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
@@ -55,6 +66,27 @@ static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
        return (struct pfkey_sock *)sk;
 }
 
+static int pfkey_can_dump(struct sock *sk)
+{
+       if (3 * atomic_read(&sk->sk_rmem_alloc) <= 2 * sk->sk_rcvbuf)
+               return 1;
+       return 0;
+}
+
+static int pfkey_do_dump(struct pfkey_sock *pfk)
+{
+       int rc;
+
+       rc = pfk->dump.dump(pfk);
+       if (rc == -ENOBUFS)
+               return 0;
+
+       pfk->dump.done(pfk);
+       pfk->dump.dump = NULL;
+       pfk->dump.done = NULL;
+       return rc;
+}
+
 static void pfkey_sock_destruct(struct sock *sk)
 {
        skb_queue_purge(&sk->sk_receive_queue);
@@ -1466,7 +1498,8 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
                err = xfrm_state_update(x);
 
        xfrm_audit_state_add(x, err ? 0 : 1,
-                            audit_get_loginuid(current), 0);
+                            audit_get_loginuid(current),
+                            audit_get_sessionid(current), 0);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -1520,7 +1553,8 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        km_state_notify(x, &c);
 out:
        xfrm_audit_state_delete(x, err ? 0 : 1,
-                              audit_get_loginuid(current), 0);
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current), 0);
        xfrm_state_put(x);
 
        return err;
@@ -1696,6 +1730,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hd
                return -EINVAL;
 
        audit_info.loginuid = audit_get_loginuid(current);
+       audit_info.sessionid = audit_get_sessionid(current);
        audit_info.secid = 0;
        err = xfrm_state_flush(proto, &audit_info);
        if (err)
@@ -1709,45 +1744,60 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hd
        return 0;
 }
 
-struct pfkey_dump_data
-{
-       struct sk_buff *skb;
-       struct sadb_msg *hdr;
-       struct sock *sk;
-};
-
 static int dump_sa(struct xfrm_state *x, int count, void *ptr)
 {
-       struct pfkey_dump_data *data = ptr;
+       struct pfkey_sock *pfk = ptr;
        struct sk_buff *out_skb;
        struct sadb_msg *out_hdr;
 
+       if (!pfkey_can_dump(&pfk->sk))
+               return -ENOBUFS;
+
        out_skb = pfkey_xfrm_state2msg(x);
        if (IS_ERR(out_skb))
                return PTR_ERR(out_skb);
 
        out_hdr = (struct sadb_msg *) out_skb->data;
-       out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+       out_hdr->sadb_msg_version = pfk->dump.msg_version;
        out_hdr->sadb_msg_type = SADB_DUMP;
        out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
        out_hdr->sadb_msg_errno = 0;
        out_hdr->sadb_msg_reserved = 0;
        out_hdr->sadb_msg_seq = count;
-       out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+       out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
+       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
        return 0;
 }
 
+static int pfkey_dump_sa(struct pfkey_sock *pfk)
+{
+       return xfrm_state_walk(&pfk->dump.u.state, dump_sa, (void *) pfk);
+}
+
+static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
+{
+       xfrm_state_walk_done(&pfk->dump.u.state);
+}
+
 static int pfkey_dump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
 {
        u8 proto;
-       struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+       struct pfkey_sock *pfk = pfkey_sk(sk);
+
+       if (pfk->dump.dump != NULL)
+               return -EBUSY;
 
        proto = pfkey_satype2proto(hdr->sadb_msg_satype);
        if (proto == 0)
                return -EINVAL;
 
-       return xfrm_state_walk(proto, dump_sa, &data);
+       pfk->dump.msg_version = hdr->sadb_msg_version;
+       pfk->dump.msg_pid = hdr->sadb_msg_pid;
+       pfk->dump.dump = pfkey_dump_sa;
+       pfk->dump.done = pfkey_dump_sa_done;
+       xfrm_state_walk_init(&pfk->dump.u.state, proto);
+
+       return pfkey_do_dump(pfk);
 }
 
 static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
@@ -1780,7 +1830,9 @@ static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
 
 static u32 gen_reqid(void)
 {
+       struct xfrm_policy_walk walk;
        u32 start;
+       int rc;
        static u32 reqid = IPSEC_MANUAL_REQID_MAX;
 
        start = reqid;
@@ -1788,8 +1840,10 @@ static u32 gen_reqid(void)
                ++reqid;
                if (reqid == 0)
                        reqid = IPSEC_MANUAL_REQID_MAX+1;
-               if (xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, check_reqid,
-                                    (void*)&reqid) != -EEXIST)
+               xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
+               rc = xfrm_policy_walk(&walk, check_reqid, (void*)&reqid);
+               xfrm_policy_walk_done(&walk);
+               if (rc != -EEXIST)
                        return reqid;
        } while (reqid != start);
        return 0;
@@ -1856,7 +1910,7 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
                t->encap_family = xp->family;
 
        /* No way to set this via kame pfkey */
-       t->aalgos = t->ealgos = t->calgos = ~0;
+       t->allalgs = 1;
        xp->xfrm_nr++;
        return 0;
 }
@@ -2241,7 +2295,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
                        goto out;
                }
 
-               err = security_xfrm_policy_alloc(xp, uctx);
+               err = security_xfrm_policy_alloc(&xp->security, uctx);
                kfree(uctx);
 
                if (err)
@@ -2273,7 +2327,8 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
                                 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
 
        xfrm_audit_policy_add(xp, err ? 0 : 1,
-                            audit_get_loginuid(current), 0);
+                             audit_get_loginuid(current),
+                             audit_get_sessionid(current), 0);
 
        if (err)
                goto out;
@@ -2301,10 +2356,11 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
        int err;
        struct sadb_address *sa;
        struct sadb_x_policy *pol;
-       struct xfrm_policy *xp, tmp;
+       struct xfrm_policy *xp;
        struct xfrm_selector sel;
        struct km_event c;
        struct sadb_x_sec_ctx *sec_ctx;
+       struct xfrm_sec_ctx *pol_ctx = NULL;
 
        if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
                                     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
@@ -2334,30 +2390,28 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
                sel.dport_mask = htons(0xffff);
 
        sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
-       memset(&tmp, 0, sizeof(struct xfrm_policy));
-
        if (sec_ctx != NULL) {
                struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
 
                if (!uctx)
                        return -ENOMEM;
 
-               err = security_xfrm_policy_alloc(&tmp, uctx);
+               err = security_xfrm_policy_alloc(&pol_ctx, uctx);
                kfree(uctx);
-
                if (err)
                        return err;
        }
 
-       xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN, pol->sadb_x_policy_dir-1,
-                                  &sel, tmp.security, 1, &err);
-       security_xfrm_policy_free(&tmp);
-
+       xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN,
+                                  pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
+                                  1, &err);
+       security_xfrm_policy_free(pol_ctx);
        if (xp == NULL)
                return -ENOENT;
 
        xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current), 0);
+                                audit_get_loginuid(current),
+                                audit_get_sessionid(current), 0);
 
        if (err)
                goto out;
@@ -2618,7 +2672,8 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
 
        if (delete) {
                xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current), 0);
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current), 0);
 
                if (err)
                        goto out;
@@ -2638,11 +2693,14 @@ out:
 
 static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
 {
-       struct pfkey_dump_data *data = ptr;
+       struct pfkey_sock *pfk = ptr;
        struct sk_buff *out_skb;
        struct sadb_msg *out_hdr;
        int err;
 
+       if (!pfkey_can_dump(&pfk->sk))
+               return -ENOBUFS;
+
        out_skb = pfkey_xfrm_policy2msg_prep(xp);
        if (IS_ERR(out_skb))
                return PTR_ERR(out_skb);
@@ -2652,21 +2710,40 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
                return err;
 
        out_hdr = (struct sadb_msg *) out_skb->data;
-       out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+       out_hdr->sadb_msg_version = pfk->dump.msg_version;
        out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
        out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
        out_hdr->sadb_msg_errno = 0;
        out_hdr->sadb_msg_seq = count;
-       out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+       out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
+       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
        return 0;
 }
 
+static int pfkey_dump_sp(struct pfkey_sock *pfk)
+{
+       return xfrm_policy_walk(&pfk->dump.u.policy, dump_sp, (void *) pfk);
+}
+
+static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
+{
+       xfrm_policy_walk_done(&pfk->dump.u.policy);
+}
+
 static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
 {
-       struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+       struct pfkey_sock *pfk = pfkey_sk(sk);
+
+       if (pfk->dump.dump != NULL)
+               return -EBUSY;
 
-       return xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_sp, &data);
+       pfk->dump.msg_version = hdr->sadb_msg_version;
+       pfk->dump.msg_pid = hdr->sadb_msg_pid;
+       pfk->dump.dump = pfkey_dump_sp;
+       pfk->dump.done = pfkey_dump_sp_done;
+       xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
+
+       return pfkey_do_dump(pfk);
 }
 
 static int key_notify_policy_flush(struct km_event *c)
@@ -2696,6 +2773,7 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
        int err;
 
        audit_info.loginuid = audit_get_loginuid(current);
+       audit_info.sessionid = audit_get_sessionid(current);
        audit_info.secid = 0;
        err = xfrm_policy_flush(XFRM_POLICY_TYPE_MAIN, &audit_info);
        if (err)
@@ -2952,6 +3030,9 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c)
 
 static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
 {
+       if (atomic_read(&pfkey_socks_nr) == 0)
+               return 0;
+
        switch (c->event) {
        case XFRM_MSG_EXPIRE:
                return key_notify_sa_expire(x, c);
@@ -3225,7 +3306,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
                if ((*dir = verify_sec_ctx_len(p)))
                        goto out;
                uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
-               *dir = security_xfrm_policy_alloc(xp, uctx);
+               *dir = security_xfrm_policy_alloc(&xp->security, uctx);
                kfree(uctx);
 
                if (*dir)
@@ -3671,6 +3752,7 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
                         int flags)
 {
        struct sock *sk = sock->sk;
+       struct pfkey_sock *pfk = pfkey_sk(sk);
        struct sk_buff *skb;
        int copied, err;
 
@@ -3698,6 +3780,10 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
 
        err = (flags & MSG_TRUNC) ? skb->len : copied;
 
+       if (pfk->dump.dump != NULL &&
+           3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
+               pfkey_do_dump(pfk);
+
 out_free:
        skb_free_datagram(sk, skb);
 out: