aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nfnetlink.c64
1 files changed, 63 insertions, 1 deletions
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index c138b8fbe280..f37f0716a9fc 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -222,6 +222,51 @@ replay:
222 } 222 }
223} 223}
224 224
225struct nfnl_err {
226 struct list_head head;
227 struct nlmsghdr *nlh;
228 int err;
229};
230
231static 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
246static void nfnl_err_del(struct nfnl_err *nfnl_err)
247{
248 list_del(&nfnl_err->head);
249 kfree(nfnl_err);
250}
251
252static 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
260static 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
225static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, 270static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
226 u_int16_t subsys_id) 271 u_int16_t subsys_id)
227{ 272{
@@ -230,6 +275,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
230 const struct nfnetlink_subsystem *ss; 275 const struct nfnetlink_subsystem *ss;
231 const struct nfnl_callback *nc; 276 const struct nfnl_callback *nc;
232 bool success = true, done = false; 277 bool success = true, done = false;
278 static LIST_HEAD(err_list);
233 int err; 279 int err;
234 280
235 if (subsys_id >= NFNL_SUBSYS_COUNT) 281 if (subsys_id >= NFNL_SUBSYS_COUNT)
@@ -287,6 +333,7 @@ replay:
287 type = nlh->nlmsg_type; 333 type = nlh->nlmsg_type;
288 if (type == NFNL_MSG_BATCH_BEGIN) { 334 if (type == NFNL_MSG_BATCH_BEGIN) {
289 /* Malformed: Batch begin twice */ 335 /* Malformed: Batch begin twice */
336 nfnl_err_reset(&err_list);
290 success = false; 337 success = false;
291 goto done; 338 goto done;
292 } else if (type == NFNL_MSG_BATCH_END) { 339 } else if (type == NFNL_MSG_BATCH_END) {
@@ -333,6 +380,7 @@ replay:
333 * original skb. 380 * original skb.
334 */ 381 */
335 if (err == -EAGAIN) { 382 if (err == -EAGAIN) {
383 nfnl_err_reset(&err_list);
336 ss->abort(skb); 384 ss->abort(skb);
337 nfnl_unlock(subsys_id); 385 nfnl_unlock(subsys_id);
338 kfree_skb(nskb); 386 kfree_skb(nskb);
@@ -341,11 +389,24 @@ replay:
341 } 389 }
342ack: 390ack:
343 if (nlh->nlmsg_flags & NLM_F_ACK || err) { 391 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 }
344 /* We don't stop processing the batch on errors, thus, 406 /* We don't stop processing the batch on errors, thus,
345 * userspace gets all the errors that the batch 407 * userspace gets all the errors that the batch
346 * triggers. 408 * triggers.
347 */ 409 */
348 netlink_ack(skb, nlh, err);
349 if (err) 410 if (err)
350 success = false; 411 success = false;
351 } 412 }
@@ -361,6 +422,7 @@ done:
361 else 422 else
362 ss->abort(skb); 423 ss->abort(skb);
363 424
425 nfnl_err_deliver(&err_list, oskb);
364 nfnl_unlock(subsys_id); 426 nfnl_unlock(subsys_id);
365 kfree_skb(nskb); 427 kfree_skb(nskb);
366} 428}