6lowpan: Use skb_cow in IPHC decompression.
authorMartin Townsend <mtownsend1973@gmail.com>
Mon, 13 Oct 2014 10:00:56 +0000 (11:00 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Sat, 25 Oct 2014 05:56:25 +0000 (07:56 +0200)
Currently there are potentially 2 skb_copy_expand calls in IPHC
decompression.  This patch replaces this with one call to
skb_cow which will check to see if there is enough headroom
first to ensure it's only done if necessary and will handle
alignment issues for cache.
As skb_cow uses pskb_expand_head we ensure the skb isn't shared from
bluetooth and ieee802.15.4 code that use the IPHC decompression.

Signed-off-by: Martin Townsend <martin.townsend@xsilon.com>
Acked-by: Alexander Aring <alex.aring@gmail.com>
Acked-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/6lowpan/iphc.c
net/bluetooth/6lowpan.c

index 142eef55c9e2d92c180786d9919ded72536d94ba..747b3ccfc4f811424f65855950e8fec09c5b565b 100644 (file)
@@ -174,30 +174,22 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
 static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
                       struct net_device *dev, skb_delivery_cb deliver_skb)
 {
-       struct sk_buff *new;
        int stat;
 
-       new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
-                             GFP_ATOMIC);
-       kfree_skb(skb);
-
-       if (!new)
-               return -ENOMEM;
-
-       skb_push(new, sizeof(struct ipv6hdr));
-       skb_reset_network_header(new);
-       skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+       skb_push(skb, sizeof(struct ipv6hdr));
+       skb_reset_network_header(skb);
+       skb_copy_to_linear_data(skb, hdr, sizeof(struct ipv6hdr));
 
-       new->protocol = htons(ETH_P_IPV6);
-       new->pkt_type = PACKET_HOST;
-       new->dev = dev;
+       skb->protocol = htons(ETH_P_IPV6);
+       skb->pkt_type = PACKET_HOST;
+       skb->dev = dev;
 
        raw_dump_table(__func__, "raw skb data dump before receiving",
-                      new->data, new->len);
+                      skb->data, skb->len);
 
-       stat = deliver_skb(new, dev);
+       stat = deliver_skb(skb, dev);
 
-       kfree_skb(new);
+       consume_skb(skb);
 
        return stat;
 }
@@ -460,7 +452,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
        /* UDP data uncompression */
        if (iphc0 & LOWPAN_IPHC_NH_C) {
                struct udphdr uh;
-               struct sk_buff *new;
+               const int needed = sizeof(struct udphdr) + sizeof(hdr);
 
                if (uncompress_udp_header(skb, &uh))
                        goto drop;
@@ -468,14 +460,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
                /* replace the compressed UDP head by the uncompressed UDP
                 * header
                 */
-               new = skb_copy_expand(skb, sizeof(struct udphdr),
-                                     skb_tailroom(skb), GFP_ATOMIC);
-               kfree_skb(skb);
-
-               if (!new)
-                       return -ENOMEM;
-
-               skb = new;
+               err = skb_cow(skb, needed);
+               if (unlikely(err)) {
+                       kfree_skb(skb);
+                       return err;
+               }
 
                skb_push(skb, sizeof(struct udphdr));
                skb_reset_transport_header(skb);
@@ -485,6 +474,12 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
                               (u8 *)&uh, sizeof(uh));
 
                hdr.nexthdr = UIP_PROTO_UDP;
+       } else {
+               err = skb_cow(skb, sizeof(hdr));
+               if (unlikely(err)) {
+                       kfree_skb(skb);
+                       return err;
+               }
        }
 
        hdr.payload_len = htons(skb->len);
index 9b5c89b18718dd3902155e3dd62f20da4c2957d5..6c5c2eff45bd88e782a8b33fa44c7fe35e21349f 100644 (file)
@@ -309,6 +309,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
        if (dev->type != ARPHRD_6LOWPAN)
                goto drop;
 
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               goto drop;
+
        /* check that it's our buffer */
        if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
                /* Copy the packet so that the IPv6 header is