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)  	}  } +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,  				u_int16_t subsys_id)  { @@ -276,13 +282,15 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct net *net = sock_net(skb->sk);  	const struct nfnetlink_subsystem *ss;  	const struct nfnl_callback *nc; -	bool success = true, done = false;  	static LIST_HEAD(err_list); +	u32 status;  	int err;  	if (subsys_id >= NFNL_SUBSYS_COUNT)  		return netlink_ack(skb, nlh, -EINVAL);  replay: +	status = 0; +  	skb = netlink_skb_clone(oskb, GFP_KERNEL);  	if (!skb)  		return netlink_ack(oskb, nlh, -ENOMEM); @@ -336,10 +344,10 @@ replay:  		if (type == NFNL_MSG_BATCH_BEGIN) {  			/* Malformed: Batch begin twice */  			nfnl_err_reset(&err_list); -			success = false; +			status |= NFNL_BATCH_FAILURE;  			goto done;  		} else if (type == NFNL_MSG_BATCH_END) { -			done = true; +			status |= NFNL_BATCH_DONE;  			goto done;  		} else if (type < NLMSG_MIN_TYPE) {  			err = -EINVAL; @@ -382,11 +390,8 @@ replay:  			 * original skb.  			 */  			if (err == -EAGAIN) { -				nfnl_err_reset(&err_list); -				ss->abort(oskb); -				nfnl_unlock(subsys_id); -				kfree_skb(skb); -				goto replay; +				status |= NFNL_BATCH_REPLAY; +				goto next;  			}  		}  ack: @@ -402,7 +407,7 @@ ack:  				 */  				nfnl_err_reset(&err_list);  				netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM); -				success = false; +				status |= NFNL_BATCH_FAILURE;  				goto done;  			}  			/* We don't stop processing the batch on errors, thus, @@ -410,19 +415,26 @@ ack:  			 * triggers.  			 */  			if (err) -				success = false; +				status |= NFNL_BATCH_FAILURE;  		} - +next:  		msglen = NLMSG_ALIGN(nlh->nlmsg_len);  		if (msglen > skb->len)  			msglen = skb->len;  		skb_pull(skb, msglen);  	}  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); -	else +	} else {  		ss->abort(oskb); +	}  	nfnl_err_deliver(&err_list, oskb);  	nfnl_unlock(subsys_id); | 
