Skip to content

Commit cbb8125

Browse files
committed
netfilter: nfnetlink: deliver netlink errors on batch completion
We have to wait until the full batch has been processed to deliver the netlink error messages to userspace. Otherwise, we may deliver duplicated errors to userspace in case that we need to abort and replay the transaction if any of the required modules needs to be autoloaded. A simple way to reproduce this (assumming nft_meta is not loaded) with the following test file: add table filter add chain filter test add chain bad test # intentional wrong unexistent table add rule filter test meta mark 0 Then, when trying to load the batch: # nft -f test test:4:1-19: Error: Could not process rule: No such file or directory add chain bad test ^^^^^^^^^^^^^^^^^^^ test:4:1-19: Error: Could not process rule: No such file or directory add chain bad test ^^^^^^^^^^^^^^^^^^^ The error is reported twice, once when the batch is aborted due to missing nft_meta and another when it is fully processed. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent ae82ddc commit cbb8125

File tree

1 file changed

+63
-1
lines changed

1 file changed

+63
-1
lines changed

net/netfilter/nfnetlink.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,51 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
222222
}
223223
}
224224

225+
struct nfnl_err {
226+
struct list_head head;
227+
struct nlmsghdr *nlh;
228+
int err;
229+
};
230+
231+
static int nfnl_err_add(struct list_head *list, struct nlmsghdr *nlh, int err)
232+
{
233+
struct nfnl_err *nfnl_err;
234+
235+
nfnl_err = kmalloc(sizeof(struct nfnl_err), GFP_KERNEL);
236+
if (nfnl_err == NULL)
237+
return -ENOMEM;
238+
239+
nfnl_err->nlh = nlh;
240+
nfnl_err->err = err;
241+
list_add_tail(&nfnl_err->head, list);
242+
243+
return 0;
244+
}
245+
246+
static void nfnl_err_del(struct nfnl_err *nfnl_err)
247+
{
248+
list_del(&nfnl_err->head);
249+
kfree(nfnl_err);
250+
}
251+
252+
static void nfnl_err_reset(struct list_head *err_list)
253+
{
254+
struct nfnl_err *nfnl_err, *next;
255+
256+
list_for_each_entry_safe(nfnl_err, next, err_list, head)
257+
nfnl_err_del(nfnl_err);
258+
}
259+
260+
static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
261+
{
262+
struct nfnl_err *nfnl_err, *next;
263+
264+
list_for_each_entry_safe(nfnl_err, next, err_list, head) {
265+
netlink_ack(skb, nfnl_err->nlh, nfnl_err->err);
266+
nfnl_err_del(nfnl_err);
267+
}
268+
}
269+
225270
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
226271
u_int16_t subsys_id)
227272
{
@@ -230,6 +275,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
230275
const struct nfnetlink_subsystem *ss;
231276
const struct nfnl_callback *nc;
232277
bool success = true, done = false;
278+
static LIST_HEAD(err_list);
233279
int err;
234280

235281
if (subsys_id >= NFNL_SUBSYS_COUNT)
@@ -287,6 +333,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
287333
type = nlh->nlmsg_type;
288334
if (type == NFNL_MSG_BATCH_BEGIN) {
289335
/* Malformed: Batch begin twice */
336+
nfnl_err_reset(&err_list);
290337
success = false;
291338
goto done;
292339
} else if (type == NFNL_MSG_BATCH_END) {
@@ -333,6 +380,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
333380
* original skb.
334381
*/
335382
if (err == -EAGAIN) {
383+
nfnl_err_reset(&err_list);
336384
ss->abort(skb);
337385
nfnl_unlock(subsys_id);
338386
kfree_skb(nskb);
@@ -341,11 +389,24 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
341389
}
342390
ack:
343391
if (nlh->nlmsg_flags & NLM_F_ACK || err) {
392+
/* Errors are delivered once the full batch has been
393+
* processed, this avoids that the same error is
394+
* reported several times when replaying the batch.
395+
*/
396+
if (nfnl_err_add(&err_list, nlh, err) < 0) {
397+
/* We failed to enqueue an error, reset the
398+
* list of errors and send OOM to userspace
399+
* pointing to the batch header.
400+
*/
401+
nfnl_err_reset(&err_list);
402+
netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM);
403+
success = false;
404+
goto done;
405+
}
344406
/* We don't stop processing the batch on errors, thus,
345407
* userspace gets all the errors that the batch
346408
* triggers.
347409
*/
348-
netlink_ack(skb, nlh, err);
349410
if (err)
350411
success = false;
351412
}
@@ -361,6 +422,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
361422
else
362423
ss->abort(skb);
363424

425+
nfnl_err_deliver(&err_list, oskb);
364426
nfnl_unlock(subsys_id);
365427
kfree_skb(nskb);
366428
}

0 commit comments

Comments
 (0)