Skip to content

Commit

Permalink
xfrm: avoid creating temporary SA when there are no listeners
Browse files Browse the repository at this point in the history
In the case when KMs have no listeners, km_query() will fail and
temporary SAs are garbage collected immediately after their allocation.
This causes strain on memory allocation, leading even to OOM since
temporary SA alloc/free cycle is performed for every packet
and garbage collection does not keep up the pace.

The sane thing to do is to make sure we have audience before
temporary SA allocation.

Signed-off-by: Horia Geanta <horia.geanta@freescale.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
  • Loading branch information
horiag authored and klassert committed Feb 13, 2014
1 parent 5826bdd commit 0f24558
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ struct xfrm_mgr {
const struct xfrm_migrate *m,
int num_bundles,
const struct xfrm_kmaddress *k);
bool (*is_alive)(const struct km_event *c);
};

int xfrm_register_km(struct xfrm_mgr *km);
Expand Down Expand Up @@ -1646,6 +1647,20 @@ static inline int xfrm_aevent_is_on(struct net *net)
rcu_read_unlock();
return ret;
}

static inline int xfrm_acquire_is_on(struct net *net)
{
struct sock *nlsk;
int ret = 0;

rcu_read_lock();
nlsk = rcu_dereference(net->xfrm.nlsk);
if (nlsk)
ret = netlink_has_listeners(nlsk, XFRMNLGRP_ACQUIRE);
rcu_read_unlock();

return ret;
}
#endif

static inline int xfrm_alg_len(const struct xfrm_algo *alg)
Expand Down
19 changes: 19 additions & 0 deletions net/key/af_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -3059,6 +3059,24 @@ static u32 get_acqseq(void)
return res;
}

static bool pfkey_is_alive(const struct km_event *c)
{
struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
struct sock *sk;
bool is_alive = false;

rcu_read_lock();
sk_for_each_rcu(sk, &net_pfkey->table) {
if (pfkey_sk(sk)->registered) {
is_alive = true;
break;
}
}
rcu_read_unlock();

return is_alive;
}

static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
{
struct sk_buff *skb;
Expand Down Expand Up @@ -3784,6 +3802,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
.new_mapping = pfkey_send_new_mapping,
.notify_policy = pfkey_send_policy_notify,
.migrate = pfkey_send_migrate,
.is_alive = pfkey_is_alive,
};

static int __net_init pfkey_net_init(struct net *net)
Expand Down
31 changes: 31 additions & 0 deletions net/xfrm/xfrm_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock);
int __xfrm_state_delete(struct xfrm_state *x);

int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
bool km_is_alive(const struct km_event *c);
void km_state_expired(struct xfrm_state *x, int hard, u32 portid);

static DEFINE_SPINLOCK(xfrm_type_lock);
Expand Down Expand Up @@ -788,6 +789,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
struct xfrm_state *best = NULL;
u32 mark = pol->mark.v & pol->mark.m;
unsigned short encap_family = tmpl->encap_family;
struct km_event c;

to_put = NULL;

Expand Down Expand Up @@ -832,6 +834,17 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
error = -EEXIST;
goto out;
}

c.net = net;
/* If the KMs have no listeners (yet...), avoid allocating an SA
* for each and every packet - garbage collection might not
* handle the flood.
*/
if (!km_is_alive(&c)) {
error = -ESRCH;
goto out;
}

x = xfrm_state_alloc(net);
if (x == NULL) {
error = -ENOMEM;
Expand Down Expand Up @@ -1793,6 +1806,24 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address
}
EXPORT_SYMBOL(km_report);

bool km_is_alive(const struct km_event *c)
{
struct xfrm_mgr *km;
bool is_alive = false;

rcu_read_lock();
list_for_each_entry_rcu(km, &xfrm_km_list, list) {
if (km->is_alive && km->is_alive(c)) {
is_alive = true;
break;
}
}
rcu_read_unlock();

return is_alive;
}
EXPORT_SYMBOL(km_is_alive);

int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
{
int err;
Expand Down
6 changes: 6 additions & 0 deletions net/xfrm/xfrm_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -2982,6 +2982,11 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
}

static bool xfrm_is_alive(const struct km_event *c)
{
return (bool)xfrm_acquire_is_on(c->net);
}

static struct xfrm_mgr netlink_mgr = {
.id = "netlink",
.notify = xfrm_send_state_notify,
Expand All @@ -2991,6 +2996,7 @@ static struct xfrm_mgr netlink_mgr = {
.report = xfrm_send_report,
.migrate = xfrm_send_migrate,
.new_mapping = xfrm_send_mapping,
.is_alive = xfrm_is_alive,
};

static int __net_init xfrm_user_net_init(struct net *net)
Expand Down

0 comments on commit 0f24558

Please sign in to comment.