summary refs log tree commit diff
path: root/net/6lowpan/iphc.c
diff options
context:
space:
mode:
authorMartin Townsend <mtownsend1973@gmail.com>2014-10-13 11:00:56 +0100
committerMarcel Holtmann <marcel@holtmann.org>2014-10-25 07:56:25 +0200
commit11e3ff7072789ad4585870cbdde1be10c45f1cc4 (patch)
tree0a8e62cbf7eaeb33ac5931f9cea2321abf730ce5 /net/6lowpan/iphc.c
parent4456c50d23d44352f4174a9a0cb75313d3150907 (diff)
downloadlinux-11e3ff7072789ad4585870cbdde1be10c45f1cc4.tar.gz
6lowpan: Use skb_cow in IPHC decompression.
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>
Diffstat (limited to 'net/6lowpan/iphc.c')
-rw-r--r--net/6lowpan/iphc.c47
1 files changed, 21 insertions, 26 deletions
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 142eef55c9e2..747b3ccfc4f8 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -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);