Skip to content

Commit

Permalink
[AF_KEY]: Dump SA/SP entries non-atomically
Browse files Browse the repository at this point in the history
Stop dumping of entries when af_key socket receive queue is getting
full and continue it later when there is more room again.

This fixes dumping of large databases. Currently the entries not
fitting into the receive queue are just dropped (including the
end-of-dump message) which can confuse applications.

Signed-off-by: Timo Teras <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
fabled authored and davem330 committed Mar 4, 2008
1 parent 26bad2c commit 83321d6
Showing 1 changed file with 91 additions and 29 deletions.
120 changes: 91 additions & 29 deletions net/key/af_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,45 @@ 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)
{
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);
Expand Down Expand Up @@ -1709,51 +1741,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 xfrm_state_walk walk;
int rc;
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;

xfrm_state_walk_init(&walk, proto);
rc = xfrm_state_walk(&walk, dump_sa, &data);
xfrm_state_walk_done(&walk);
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 rc;
return pfkey_do_dump(pfk);
}

static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
Expand Down Expand Up @@ -2648,11 +2689,14 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h

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);
Expand All @@ -2662,27 +2706,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 xfrm_policy_walk walk;
int rc;
struct pfkey_sock *pfk = pfkey_sk(sk);

xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
rc = xfrm_policy_walk(&walk, dump_sp, &data);
xfrm_policy_walk_done(&walk);
if (pfk->dump.dump != NULL)
return -EBUSY;

return rc;
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)
Expand Down Expand Up @@ -3687,6 +3744,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;

Expand Down Expand Up @@ -3714,6 +3772,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:
Expand Down

0 comments on commit 83321d6

Please sign in to comment.