Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / net / netfilter / nfnetlink_queue_core.c
index 5352b2d2d5bf644cffd04ed5a571924f623f65d5..2b8199f68785d1a8f17cc65049826976d921f7c6 100644 (file)
@@ -227,22 +227,23 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
        spin_unlock_bh(&queue->lock);
 }
 
-static void
+static int
 nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
 {
        int i, j = 0;
        int plen = 0; /* length of skb->head fragment */
+       int ret;
        struct page *page;
        unsigned int offset;
 
        /* dont bother with small payloads */
-       if (len <= skb_tailroom(to)) {
-               skb_copy_bits(from, 0, skb_put(to, len), len);
-               return;
-       }
+       if (len <= skb_tailroom(to))
+               return skb_copy_bits(from, 0, skb_put(to, len), len);
 
        if (hlen) {
-               skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+               ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+               if (unlikely(ret))
+                       return ret;
                len -= hlen;
        } else {
                plen = min_t(int, skb_headlen(from), len);
@@ -260,6 +261,11 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
        to->len += len + plen;
        to->data_len += len + plen;
 
+       if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) {
+               skb_tx_error(from);
+               return -ENOMEM;
+       }
+
        for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
                if (!len)
                        break;
@@ -270,6 +276,8 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
                j++;
        }
        skb_shinfo(to)->nr_frags = j;
+
+       return 0;
 }
 
 static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet)
@@ -355,13 +363,16 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
 
        skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid,
                                  GFP_ATOMIC);
-       if (!skb)
+       if (!skb) {
+               skb_tx_error(entskb);
                return NULL;
+       }
 
        nlh = nlmsg_put(skb, 0, 0,
                        NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
                        sizeof(struct nfgenmsg), 0);
        if (!nlh) {
+               skb_tx_error(entskb);
                kfree_skb(skb);
                return NULL;
        }
@@ -481,13 +492,15 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                nla->nla_type = NFQA_PAYLOAD;
                nla->nla_len = nla_attr_size(data_len);
 
-               nfqnl_zcopy(skb, entskb, data_len, hlen);
+               if (nfqnl_zcopy(skb, entskb, data_len, hlen))
+                       goto nla_put_failure;
        }
 
        nlh->nlmsg_len = skb->len;
        return skb;
 
 nla_put_failure:
+       skb_tx_error(entskb);
        kfree_skb(skb);
        net_err_ratelimited("nf_queue: error creating packet message\n");
        return NULL;