diff options
-rw-r--r-- | net/netfilter/nfnetlink.c | 38 |
1 files changed, 25 insertions, 13 deletions
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 8b117c90ecd7..0c0e8ecf02ab 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c | |||
@@ -269,6 +269,12 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb) | |||
269 | } | 269 | } |
270 | } | 270 | } |
271 | 271 | ||
272 | enum { | ||
273 | NFNL_BATCH_FAILURE = (1 << 0), | ||
274 | NFNL_BATCH_DONE = (1 << 1), | ||
275 | NFNL_BATCH_REPLAY = (1 << 2), | ||
276 | }; | ||
277 | |||
272 | static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, | 278 | static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, |
273 | u_int16_t subsys_id) | 279 | u_int16_t subsys_id) |
274 | { | 280 | { |
@@ -276,13 +282,15 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
276 | struct net *net = sock_net(skb->sk); | 282 | struct net *net = sock_net(skb->sk); |
277 | const struct nfnetlink_subsystem *ss; | 283 | const struct nfnetlink_subsystem *ss; |
278 | const struct nfnl_callback *nc; | 284 | const struct nfnl_callback *nc; |
279 | bool success = true, done = false; | ||
280 | static LIST_HEAD(err_list); | 285 | static LIST_HEAD(err_list); |
286 | u32 status; | ||
281 | int err; | 287 | int err; |
282 | 288 | ||
283 | if (subsys_id >= NFNL_SUBSYS_COUNT) | 289 | if (subsys_id >= NFNL_SUBSYS_COUNT) |
284 | return netlink_ack(skb, nlh, -EINVAL); | 290 | return netlink_ack(skb, nlh, -EINVAL); |
285 | replay: | 291 | replay: |
292 | status = 0; | ||
293 | |||
286 | skb = netlink_skb_clone(oskb, GFP_KERNEL); | 294 | skb = netlink_skb_clone(oskb, GFP_KERNEL); |
287 | if (!skb) | 295 | if (!skb) |
288 | return netlink_ack(oskb, nlh, -ENOMEM); | 296 | return netlink_ack(oskb, nlh, -ENOMEM); |
@@ -336,10 +344,10 @@ replay: | |||
336 | if (type == NFNL_MSG_BATCH_BEGIN) { | 344 | if (type == NFNL_MSG_BATCH_BEGIN) { |
337 | /* Malformed: Batch begin twice */ | 345 | /* Malformed: Batch begin twice */ |
338 | nfnl_err_reset(&err_list); | 346 | nfnl_err_reset(&err_list); |
339 | success = false; | 347 | status |= NFNL_BATCH_FAILURE; |
340 | goto done; | 348 | goto done; |
341 | } else if (type == NFNL_MSG_BATCH_END) { | 349 | } else if (type == NFNL_MSG_BATCH_END) { |
342 | done = true; | 350 | status |= NFNL_BATCH_DONE; |
343 | goto done; | 351 | goto done; |
344 | } else if (type < NLMSG_MIN_TYPE) { | 352 | } else if (type < NLMSG_MIN_TYPE) { |
345 | err = -EINVAL; | 353 | err = -EINVAL; |
@@ -382,11 +390,8 @@ replay: | |||
382 | * original skb. | 390 | * original skb. |
383 | */ | 391 | */ |
384 | if (err == -EAGAIN) { | 392 | if (err == -EAGAIN) { |
385 | nfnl_err_reset(&err_list); | 393 | status |= NFNL_BATCH_REPLAY; |
386 | ss->abort(oskb); | 394 | goto next; |
387 | nfnl_unlock(subsys_id); | ||
388 | kfree_skb(skb); | ||
389 | goto replay; | ||
390 | } | 395 | } |
391 | } | 396 | } |
392 | ack: | 397 | ack: |
@@ -402,7 +407,7 @@ ack: | |||
402 | */ | 407 | */ |
403 | nfnl_err_reset(&err_list); | 408 | nfnl_err_reset(&err_list); |
404 | netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM); | 409 | netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM); |
405 | success = false; | 410 | status |= NFNL_BATCH_FAILURE; |
406 | goto done; | 411 | goto done; |
407 | } | 412 | } |
408 | /* We don't stop processing the batch on errors, thus, | 413 | /* We don't stop processing the batch on errors, thus, |
@@ -410,19 +415,26 @@ ack: | |||
410 | * triggers. | 415 | * triggers. |
411 | */ | 416 | */ |
412 | if (err) | 417 | if (err) |
413 | success = false; | 418 | status |= NFNL_BATCH_FAILURE; |
414 | } | 419 | } |
415 | 420 | next: | |
416 | msglen = NLMSG_ALIGN(nlh->nlmsg_len); | 421 | msglen = NLMSG_ALIGN(nlh->nlmsg_len); |
417 | if (msglen > skb->len) | 422 | if (msglen > skb->len) |
418 | msglen = skb->len; | 423 | msglen = skb->len; |
419 | skb_pull(skb, msglen); | 424 | skb_pull(skb, msglen); |
420 | } | 425 | } |
421 | done: | 426 | done: |
422 | if (success && done) | 427 | if (status & NFNL_BATCH_REPLAY) { |
428 | ss->abort(oskb); | ||
429 | nfnl_err_reset(&err_list); | ||
430 | nfnl_unlock(subsys_id); | ||
431 | kfree_skb(skb); | ||
432 | goto replay; | ||
433 | } else if (status == NFNL_BATCH_DONE) { | ||
423 | ss->commit(oskb); | 434 | ss->commit(oskb); |
424 | else | 435 | } else { |
425 | ss->abort(oskb); | 436 | ss->abort(oskb); |
437 | } | ||
426 | 438 | ||
427 | nfnl_err_deliver(&err_list, oskb); | 439 | nfnl_err_deliver(&err_list, oskb); |
428 | nfnl_unlock(subsys_id); | 440 | nfnl_unlock(subsys_id); |