Commit 6742b9e3 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nfnetlink: keep going batch handling on missing modules

After a fresh boot with no modules in place at all and a large rulesets, the
existing nfnetlink_rcv_batch() funcion can take long time to commit the ruleset
due to the many abort path. This is specifically a problem for the existing
client of this code, ie. nf_tables, since it results in several
synchronize_rcu() call in a row.

This patch changes the policy to keep full batch processing on missing modules
errors so we abort only once.
Reported-by: default avatarEric Leblond <eric@regit.org>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent dd302b59
...@@ -269,6 +269,12 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb) ...@@ -269,6 +269,12 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
} }
} }
enum {
NFNL_BATCH_FAILURE = (1 << 0),
NFNL_BATCH_DONE = (1 << 1),
NFNL_BATCH_REPLAY = (1 << 2),
};
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
u_int16_t subsys_id) u_int16_t subsys_id)
{ {
...@@ -276,13 +282,15 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -276,13 +282,15 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
const struct nfnetlink_subsystem *ss; const struct nfnetlink_subsystem *ss;
const struct nfnl_callback *nc; const struct nfnl_callback *nc;
bool success = true, done = false;
static LIST_HEAD(err_list); static LIST_HEAD(err_list);
u32 status;
int err; int err;
if (subsys_id >= NFNL_SUBSYS_COUNT) if (subsys_id >= NFNL_SUBSYS_COUNT)
return netlink_ack(skb, nlh, -EINVAL); return netlink_ack(skb, nlh, -EINVAL);
replay: replay:
status = 0;
skb = netlink_skb_clone(oskb, GFP_KERNEL); skb = netlink_skb_clone(oskb, GFP_KERNEL);
if (!skb) if (!skb)
return netlink_ack(oskb, nlh, -ENOMEM); return netlink_ack(oskb, nlh, -ENOMEM);
...@@ -336,10 +344,10 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -336,10 +344,10 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
if (type == NFNL_MSG_BATCH_BEGIN) { if (type == NFNL_MSG_BATCH_BEGIN) {
/* Malformed: Batch begin twice */ /* Malformed: Batch begin twice */
nfnl_err_reset(&err_list); nfnl_err_reset(&err_list);
success = false; status |= NFNL_BATCH_FAILURE;
goto done; goto done;
} else if (type == NFNL_MSG_BATCH_END) { } else if (type == NFNL_MSG_BATCH_END) {
done = true; status |= NFNL_BATCH_DONE;
goto done; goto done;
} else if (type < NLMSG_MIN_TYPE) { } else if (type < NLMSG_MIN_TYPE) {
err = -EINVAL; err = -EINVAL;
...@@ -382,11 +390,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -382,11 +390,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
* original skb. * original skb.
*/ */
if (err == -EAGAIN) { if (err == -EAGAIN) {
nfnl_err_reset(&err_list); status |= NFNL_BATCH_REPLAY;
ss->abort(oskb); goto next;
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
} }
} }
ack: ack:
...@@ -402,7 +407,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -402,7 +407,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
*/ */
nfnl_err_reset(&err_list); nfnl_err_reset(&err_list);
netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM); netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM);
success = false; status |= NFNL_BATCH_FAILURE;
goto done; goto done;
} }
/* We don't stop processing the batch on errors, thus, /* We don't stop processing the batch on errors, thus,
...@@ -410,19 +415,26 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -410,19 +415,26 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
* triggers. * triggers.
*/ */
if (err) if (err)
success = false; status |= NFNL_BATCH_FAILURE;
} }
next:
msglen = NLMSG_ALIGN(nlh->nlmsg_len); msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len) if (msglen > skb->len)
msglen = skb->len; msglen = skb->len;
skb_pull(skb, msglen); skb_pull(skb, msglen);
} }
done: done:
if (success && done) if (status & NFNL_BATCH_REPLAY) {
ss->abort(oskb);
nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
} else if (status == NFNL_BATCH_DONE) {
ss->commit(oskb); ss->commit(oskb);
else } else {
ss->abort(oskb); ss->abort(oskb);
}
nfnl_err_deliver(&err_list, oskb); nfnl_err_deliver(&err_list, oskb);
nfnl_unlock(subsys_id); nfnl_unlock(subsys_id);
......
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