From c5d3687f6cfed185c2c0b29a5b33273ebd2c0781 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 03:49:01 +0000 Subject: 6lowpan: read data from skb safely Check if skb buffer can pull requested amount of bytes and return an error in opposite case. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 70 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 32 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 32eb4179e8fa..5c7bcf9663d3 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -291,25 +291,26 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) *hc06_ptr += 2; } -static u8 lowpan_fetch_skb_u8(struct sk_buff *skb) +static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) { - u8 ret; + if (unlikely(!pskb_may_pull(skb, 1))) + return -EINVAL; - ret = skb->data[0]; + *val = skb->data[0]; skb_pull(skb, 1); - return ret; + return 0; } -static u16 lowpan_fetch_skb_u16(struct sk_buff *skb) +static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) { - u16 ret; - - BUG_ON(!pskb_may_pull(skb, 2)); + if (unlikely(!pskb_may_pull(skb, 2))) + return -EINVAL; - ret = skb->data[0] | (skb->data[1] << 8); + *val = skb->data[0] | (skb->data[1] << 8); skb_pull(skb, 2); - return ret; + + return 0; } static int @@ -318,7 +319,8 @@ lowpan_uncompress_udp_header(struct sk_buff *skb) struct udphdr *uh = udp_hdr(skb); u8 tmp; - tmp = lowpan_fetch_skb_u8(skb); + if (lowpan_fetch_skb_u8(skb, &tmp)) + goto err; if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { pr_debug("(%s): UDP header uncompression\n", __func__); @@ -710,7 +712,9 @@ lowpan_process_data(struct sk_buff *skb) /* at least two bytes will be used for the encoding */ if (skb->len < 2) goto drop; - iphc0 = lowpan_fetch_skb_u8(skb); + + if (lowpan_fetch_skb_u8(skb, &iphc0)) + goto drop; /* fragments assembling */ switch (iphc0 & LOWPAN_DISPATCH_MASK) { @@ -722,8 +726,9 @@ lowpan_process_data(struct sk_buff *skb) u16 tag; bool found = false; - len = lowpan_fetch_skb_u8(skb); /* frame length */ - tag = lowpan_fetch_skb_u16(skb); + if (lowpan_fetch_skb_u8(skb, &len) || /* frame length */ + lowpan_fetch_skb_u16(skb, &tag)) /* fragment tag */ + goto drop; /* * check if frame assembling with the same tag is @@ -747,7 +752,8 @@ lowpan_process_data(struct sk_buff *skb) if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) goto unlock_and_drop; - offset = lowpan_fetch_skb_u8(skb); /* fetch offset */ + if (lowpan_fetch_skb_u8(skb, &offset)) /* fetch offset */ + goto unlock_and_drop; /* if payload fits buffer, copy it */ if (likely((offset * 8 + skb->len) <= frame->length)) @@ -769,7 +775,10 @@ lowpan_process_data(struct sk_buff *skb) dev_kfree_skb(skb); skb = frame->skb; kfree(frame); - iphc0 = lowpan_fetch_skb_u8(skb); + + if (lowpan_fetch_skb_u8(skb, &iphc0)) + goto unlock_and_drop; + break; } spin_unlock(&flist_lock); @@ -780,7 +789,8 @@ lowpan_process_data(struct sk_buff *skb) break; } - iphc1 = lowpan_fetch_skb_u8(skb); + if (lowpan_fetch_skb_u8(skb, &iphc1)) + goto drop; _saddr = mac_cb(skb)->sa.hwaddr; _daddr = mac_cb(skb)->da.hwaddr; @@ -791,9 +801,8 @@ lowpan_process_data(struct sk_buff *skb) if (iphc1 & LOWPAN_IPHC_CID) { pr_debug("(%s): CID flag is set, increase header with one\n", __func__); - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &num_context)) goto drop; - num_context = lowpan_fetch_skb_u8(skb); } hdr.version = 6; @@ -805,9 +814,9 @@ lowpan_process_data(struct sk_buff *skb) * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes) */ case 0: /* 00b */ - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &tmp)) goto drop; - tmp = lowpan_fetch_skb_u8(skb); + memcpy(&hdr.flow_lbl, &skb->data[0], 3); skb_pull(skb, 3); hdr.priority = ((tmp >> 2) & 0x0f); @@ -819,9 +828,9 @@ lowpan_process_data(struct sk_buff *skb) * ECN + DSCP (1 byte), Flow Label is elided */ case 1: /* 10b */ - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &tmp)) goto drop; - tmp = lowpan_fetch_skb_u8(skb); + hdr.priority = ((tmp >> 2) & 0x0f); hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30); hdr.flow_lbl[1] = 0; @@ -832,9 +841,9 @@ lowpan_process_data(struct sk_buff *skb) * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided */ case 2: /* 01b */ - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &tmp)) goto drop; - tmp = lowpan_fetch_skb_u8(skb); + hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30); memcpy(&hdr.flow_lbl[1], &skb->data[0], 2); skb_pull(skb, 2); @@ -853,9 +862,9 @@ lowpan_process_data(struct sk_buff *skb) /* Next Header */ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { /* Next header is carried inline */ - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr))) goto drop; - hdr.nexthdr = lowpan_fetch_skb_u8(skb); + pr_debug("(%s): NH flag is set, next header is carried " "inline: %02x\n", __func__, hdr.nexthdr); } @@ -864,9 +873,8 @@ lowpan_process_data(struct sk_buff *skb) if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03]; else { - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit))) goto drop; - hdr.hop_limit = lowpan_fetch_skb_u8(skb); } /* Extract SAM to the tmp variable */ @@ -894,10 +902,8 @@ lowpan_process_data(struct sk_buff *skb) pr_debug("(%s): destination address non-context-based" " multicast compression\n", __func__); if (0 < tmp && tmp < 3) { - if (!skb->len) + if (lowpan_fetch_skb_u8(skb, &prefix[1])) goto drop; - else - prefix[1] = lowpan_fetch_skb_u8(skb); } err = lowpan_uncompress_addr(skb, &hdr.daddr, prefix, -- cgit v1.2.3 From 5c00c0cba5d4e5371492dda8d642dd4d5788e905 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 03:49:02 +0000 Subject: 6lowpan: fix hop limit compression Add missing pointer shift for the 'default' case. Signed-off-by: Alexander Smirnov Cc: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 5c7bcf9663d3..b45e229bc42c 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -492,6 +492,7 @@ static int lowpan_header_create(struct sk_buff *skb, break; default: *hc06_ptr = hdr->hop_limit; + hc06_ptr += 1; break; } -- cgit v1.2.3 From e71094f989a48ba776ec117958481682c232ba0b Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 03:49:03 +0000 Subject: 6lowpan: remove excessive argument in pr_debug Remove excessive __func__ argument in pr_debug function and some excessive debug messages. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 94 +++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 57 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index b45e229bc42c..ad0c2264e537 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -240,8 +240,7 @@ lowpan_uncompress_addr(struct sk_buff *skb, struct in6_addr *ipaddr, lowpan_uip_ds6_set_addr_iid(ipaddr, lladdr); } - pr_debug("(%s): uncompressing %d + %d => ", __func__, prefcount, - postcount); + pr_debug("uncompressing %d + %d => ", prefcount, postcount); lowpan_raw_dump_inline(NULL, NULL, ipaddr->s6_addr, 16); return 0; @@ -252,13 +251,11 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) { struct udphdr *uh = udp_hdr(skb); - pr_debug("(%s): UDP header compression\n", __func__); - if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) == LOWPAN_NHC_UDP_4BIT_PORT) && ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) == LOWPAN_NHC_UDP_4BIT_PORT)) { - pr_debug("(%s): both ports compression to 4 bits\n", __func__); + pr_debug("UDP header: both ports compression to 4 bits\n"); **hc06_ptr = LOWPAN_NHC_UDP_CS_P_11; **(hc06_ptr + 1) = /* subtraction is faster */ (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) + @@ -266,20 +263,20 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) *hc06_ptr += 2; } else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) == LOWPAN_NHC_UDP_8BIT_PORT) { - pr_debug("(%s): remove 8 bits of dest\n", __func__); + pr_debug("UDP header: remove 8 bits of dest\n"); **hc06_ptr = LOWPAN_NHC_UDP_CS_P_01; memcpy(*hc06_ptr + 1, &uh->source, 2); **(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT); *hc06_ptr += 4; } else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) == LOWPAN_NHC_UDP_8BIT_PORT) { - pr_debug("(%s): remove 8 bits of source\n", __func__); + pr_debug("UDP header: remove 8 bits of source\n"); **hc06_ptr = LOWPAN_NHC_UDP_CS_P_10; memcpy(*hc06_ptr + 1, &uh->dest, 2); **(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT); *hc06_ptr += 4; } else { - pr_debug("(%s): can't compress header\n", __func__); + pr_debug("UDP header: can't compress\n"); **hc06_ptr = LOWPAN_NHC_UDP_CS_P_00; memcpy(*hc06_ptr + 1, &uh->source, 2); memcpy(*hc06_ptr + 3, &uh->dest, 2); @@ -323,7 +320,7 @@ lowpan_uncompress_udp_header(struct sk_buff *skb) goto err; if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { - pr_debug("(%s): UDP header uncompression\n", __func__); + pr_debug("UDP header uncompression\n"); switch (tmp & LOWPAN_NHC_UDP_CS_P_11) { case LOWPAN_NHC_UDP_CS_P_00: memcpy(&uh->source, &skb->data[0], 2); @@ -349,19 +346,19 @@ lowpan_uncompress_udp_header(struct sk_buff *skb) skb_pull(skb, 1); break; default: - pr_debug("(%s) ERROR: unknown UDP format\n", __func__); + pr_debug("ERROR: unknown UDP format\n"); goto err; break; } - pr_debug("(%s): uncompressed UDP ports: src = %d, dst = %d\n", - __func__, uh->source, uh->dest); + pr_debug("uncompressed UDP ports: src = %d, dst = %d\n", + uh->source, uh->dest); /* copy checksum */ memcpy(&uh->check, &skb->data[0], 2); skb_pull(skb, 2); } else { - pr_debug("(%s): ERROR: unsupported NH format\n", __func__); + pr_debug("ERROR: unsupported NH format\n"); goto err; } @@ -394,10 +391,9 @@ static int lowpan_header_create(struct sk_buff *skb, hdr = ipv6_hdr(skb); hc06_ptr = head + 2; - pr_debug("(%s): IPv6 header dump:\n\tversion = %d\n\tlength = %d\n" - "\tnexthdr = 0x%02x\n\thop_lim = %d\n", __func__, - hdr->version, ntohs(hdr->payload_len), hdr->nexthdr, - hdr->hop_limit); + pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n" + "\tnexthdr = 0x%02x\n\thop_lim = %d\n", hdr->version, + ntohs(hdr->payload_len), hdr->nexthdr, hdr->hop_limit); lowpan_raw_dump_table(__func__, "raw skb network header dump", skb_network_header(skb), sizeof(struct ipv6hdr)); @@ -498,23 +494,22 @@ static int lowpan_header_create(struct sk_buff *skb, /* source address compression */ if (is_addr_unspecified(&hdr->saddr)) { - pr_debug("(%s): source address is unspecified, setting SAC\n", - __func__); + pr_debug("source address is unspecified, setting SAC\n"); iphc1 |= LOWPAN_IPHC_SAC; /* TODO: context lookup */ } else if (is_addr_link_local(&hdr->saddr)) { - pr_debug("(%s): source address is link-local\n", __func__); + pr_debug("source address is link-local\n"); iphc1 |= lowpan_compress_addr_64(&hc06_ptr, LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr); } else { - pr_debug("(%s): send the full source address\n", __func__); + pr_debug("send the full source address\n"); memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16); hc06_ptr += 16; } /* destination address compression */ if (is_addr_mcast(&hdr->daddr)) { - pr_debug("(%s): destination address is multicast", __func__); + pr_debug("destination address is multicast: "); iphc1 |= LOWPAN_IPHC_M; if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) { pr_debug("compressed to 1 octet\n"); @@ -543,14 +538,13 @@ static int lowpan_header_create(struct sk_buff *skb, hc06_ptr += 16; } } else { - pr_debug("(%s): destination address is unicast: ", __func__); /* TODO: context lookup */ if (is_addr_link_local(&hdr->daddr)) { - pr_debug("destination address is link-local\n"); + pr_debug("dest address is unicast and link-local\n"); iphc1 |= lowpan_compress_addr_64(&hc06_ptr, LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr); } else { - pr_debug("using full address\n"); + pr_debug("dest address is unicast: using full one\n"); memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16); hc06_ptr += 16; } @@ -642,8 +636,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr) { struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr; - pr_debug("%s: timer expired for frame with tag %d\n", __func__, - entry->tag); + pr_debug("timer expired for frame with tag %d\n", entry->tag); spin_lock(&flist_lock); list_del(&entry->list); @@ -796,12 +789,11 @@ lowpan_process_data(struct sk_buff *skb) _saddr = mac_cb(skb)->sa.hwaddr; _daddr = mac_cb(skb)->da.hwaddr; - pr_debug("(%s): iphc0 = %02x, iphc1 = %02x\n", __func__, iphc0, iphc1); + pr_debug("iphc0 = %02x, iphc1 = %02x\n", iphc0, iphc1); /* another if the CID flag is set */ if (iphc1 & LOWPAN_IPHC_CID) { - pr_debug("(%s): CID flag is set, increase header with one\n", - __func__); + pr_debug("CID flag is set, increase header with one\n"); if (lowpan_fetch_skb_u8(skb, &num_context)) goto drop; } @@ -866,8 +858,8 @@ lowpan_process_data(struct sk_buff *skb) if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr))) goto drop; - pr_debug("(%s): NH flag is set, next header is carried " - "inline: %02x\n", __func__, hdr.nexthdr); + pr_debug("NH flag is set, next header carried inline: %02x\n", + hdr.nexthdr); } /* Hop Limit */ @@ -882,7 +874,7 @@ lowpan_process_data(struct sk_buff *skb) tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03; /* Source address uncompression */ - pr_debug("(%s): source address stateless compression\n", __func__); + pr_debug("source address stateless compression\n"); err = lowpan_uncompress_addr(skb, &hdr.saddr, lowpan_llprefix, lowpan_unc_llconf[tmp], skb->data); if (err) @@ -894,14 +886,12 @@ lowpan_process_data(struct sk_buff *skb) /* check for Multicast Compression */ if (iphc1 & LOWPAN_IPHC_M) { if (iphc1 & LOWPAN_IPHC_DAC) { - pr_debug("(%s): destination address context-based " - "multicast compression\n", __func__); + pr_debug("dest: context-based mcast compression\n"); /* TODO: implement this */ } else { u8 prefix[] = {0xff, 0x02}; - pr_debug("(%s): destination address non-context-based" - " multicast compression\n", __func__); + pr_debug("dest: non context-based mcast compression\n"); if (0 < tmp && tmp < 3) { if (lowpan_fetch_skb_u8(skb, &prefix[1])) goto drop; @@ -913,8 +903,7 @@ lowpan_process_data(struct sk_buff *skb) goto drop; } } else { - pr_debug("(%s): destination address stateless compression\n", - __func__); + pr_debug("dest: stateless compression\n"); err = lowpan_uncompress_addr(skb, &hdr.daddr, lowpan_llprefix, lowpan_unc_llconf[tmp], skb->data); if (err) @@ -929,11 +918,11 @@ lowpan_process_data(struct sk_buff *skb) /* Not fragmented package */ hdr.payload_len = htons(skb->len); - pr_debug("(%s): skb headroom size = %d, data length = %d\n", __func__, - skb_headroom(skb), skb->len); + pr_debug("skb headroom size = %d, data length = %d\n", + skb_headroom(skb), skb->len); - pr_debug("(%s): IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t" - "nexthdr = 0x%02x\n\thop_lim = %d\n", __func__, hdr.version, + pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t" + "nexthdr = 0x%02x\n\thop_lim = %d\n", hdr.version, ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit); lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, @@ -1035,11 +1024,11 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) { int err = -1; - pr_debug("(%s): package xmit\n", __func__); + pr_debug("package xmit\n"); skb->dev = lowpan_dev_info(dev)->real_dev; if (skb->dev == NULL) { - pr_debug("(%s) ERROR: no real wpan device found\n", __func__); + pr_debug("ERROR: no real wpan device found\n"); goto error; } @@ -1048,14 +1037,13 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) goto out; } - pr_debug("(%s): frame is too big, fragmentation is needed\n", - __func__); + pr_debug("frame is too big, fragmentation is needed\n"); err = lowpan_skb_fragmentation(skb); error: dev_kfree_skb(skb); out: if (err < 0) - pr_debug("(%s): ERROR: xmit failed\n", __func__); + pr_debug("ERROR: xmit failed\n"); return (err < 0 ? NETDEV_TX_BUSY : NETDEV_TX_OK); } @@ -1101,8 +1089,6 @@ static struct ieee802154_mlme_ops lowpan_mlme = { static void lowpan_setup(struct net_device *dev) { - pr_debug("(%s)\n", __func__); - dev->addr_len = IEEE802154_ADDR_LEN; memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); dev->type = ARPHRD_IEEE802154; @@ -1122,8 +1108,6 @@ static void lowpan_setup(struct net_device *dev) static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) { - pr_debug("(%s)\n", __func__); - if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) return -EINVAL; @@ -1164,7 +1148,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, struct net_device *real_dev; struct lowpan_dev_record *entry; - pr_debug("(%s)\n", __func__); + pr_debug("adding new link\n"); if (!tb[IFLA_LINK]) return -EINVAL; @@ -1259,8 +1243,6 @@ static int __init lowpan_init_module(void) { int err = 0; - pr_debug("(%s)\n", __func__); - err = lowpan_netlink_init(); if (err < 0) goto out; @@ -1272,8 +1254,6 @@ out: static void __exit lowpan_cleanup_module(void) { - pr_debug("(%s)\n", __func__); - lowpan_netlink_fini(); dev_remove_pack(&lowpan_packet_type); -- cgit v1.2.3 From 747cf6ed3dbf6200af761f5384893c3b621a484c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 26 Jun 2012 20:53:09 +0000 Subject: 6lowpan: double unlock on an error path We already unlocked a few lines earlier here, so we can go directly to drop without passing through unlock. This was introduced recently in c5d3687f6c ('6lowpan: read data from skb safely'). Signed-off-by: Dan Carpenter Acked-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index ad0c2264e537..cd5007f3a569 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -771,7 +771,7 @@ lowpan_process_data(struct sk_buff *skb) kfree(frame); if (lowpan_fetch_skb_u8(skb, &iphc0)) - goto unlock_and_drop; + goto drop; break; } -- cgit v1.2.3 From 42c3629551ab8b1613d27c8d046537f9f5b17cbb Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Sun, 1 Jul 2012 19:58:46 +0000 Subject: 6lowpan: revert 'reuse eth_mac_addr()' This reverts the commit cdf49c283e2e105da86ca575ad35b453f5ff24ea which replaces lowpan '.ndo_set_mac_address' method by ethernet's one. Accorind to the IEEE 802.15.4 standard, device has 8-byte length address, so this hook loses the last 2 bytes which may rise a compatibility problems with other IEEE 802.15.4 standard implementations. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index cd5007f3a569..f4070e54d1a1 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include @@ -936,6 +935,19 @@ drop: return -EINVAL; } +static int lowpan_set_address(struct net_device *dev, void *p) +{ + struct sockaddr *sa = p; + + if (netif_running(dev)) + return -EBUSY; + + /* TODO: validate addr */ + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + return 0; +} + static int lowpan_get_mac_header_length(struct sk_buff *skb) { /* @@ -1078,7 +1090,7 @@ static struct header_ops lowpan_header_ops = { static const struct net_device_ops lowpan_netdev_ops = { .ndo_start_xmit = lowpan_xmit, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = lowpan_set_address, }; static struct ieee802154_mlme_ops lowpan_mlme = { -- cgit v1.2.3 From 4d27de149ba17eb7643b6665ad3513d00d65b6c2 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Tue, 10 Jul 2012 21:22:42 +0000 Subject: 6lowpan: revert: add missing spin_lock_init() Revert the commit 768f7c7c121e80f458a9d013b2e8b169e5dfb1e5 to initialize spinlock in the more preferable way and make it static to avoid sparse warning. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index f4070e54d1a1..b872515f2137 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -123,7 +123,7 @@ struct lowpan_fragment { static unsigned short fragment_tag; static LIST_HEAD(lowpan_fragments); -spinlock_t flist_lock; +static DEFINE_SPINLOCK(flist_lock); static inline struct lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) @@ -1186,8 +1186,6 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, list_add_tail(&entry->list, &lowpan_devices); mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - spin_lock_init(&flist_lock); - register_netdevice(dev); return 0; -- cgit v1.2.3 From 79ff1db6d9661974186d51c1ef14d1cab521df49 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Tue, 10 Jul 2012 21:22:45 +0000 Subject: 6lowpan: get extra headroom in allocated frame Use netdev_alloc_skb_ip_align() instead of alloc_skb() to get some extra headroom in case we need to forward this frame in a tunnel or something else. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index b872515f2137..ae3f4eb02064 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -661,8 +661,8 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u8 iphc0, u8 len, u8 tag) frame->tag = tag; /* allocate buffer for frame assembling */ - frame->skb = alloc_skb(frame->length + - sizeof(struct ipv6hdr), GFP_ATOMIC); + frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length + + sizeof(struct ipv6hdr)); if (!frame->skb) goto skb_err; -- cgit v1.2.3 From abbee2effcbce55440accb0a1dd315562875efa2 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Tue, 10 Jul 2012 21:22:47 +0000 Subject: 6lowpan: fix tag variable size Function lowpan_alloc_new_frame() takes u8 tag as an argument. However, its only caller, lowpan_process_data() passes down a u16. Hence, the tag value can get corrupted. This prevent 6lowpan fragment reassembly of a message when the fragment tag value is over 256. Signed-off-by: Alexander Smirnov Cc: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index ae3f4eb02064..2e790fbe848d 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -646,7 +646,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr) } static struct lowpan_fragment * -lowpan_alloc_new_frame(struct sk_buff *skb, u8 iphc0, u8 len, u8 tag) +lowpan_alloc_new_frame(struct sk_buff *skb, u8 iphc0, u8 len, u16 tag) { struct lowpan_fragment *frame; -- cgit v1.2.3 From 33c34c5e9310622d5ed9a53d571f92824044020f Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Tue, 10 Jul 2012 21:22:48 +0000 Subject: 6lowpan: rework fragment-deleting routine 6lowpan module starts collecting incomming frames and fragments right after lowpan_module_init() therefor it will be better to clean unfinished fragments in lowpan_cleanup_module() function instead of doing it when link goes down. Changed spinlocks type to prevent deadlock with expired timer event and removed unused one. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 2e790fbe848d..6871ec1b30f8 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -113,7 +113,6 @@ struct lowpan_dev_record { struct lowpan_fragment { struct sk_buff *skb; /* skb to be assembled */ - spinlock_t lock; /* concurency lock */ u16 length; /* length to be assemled */ u32 bytes_rcv; /* bytes received */ u16 tag; /* current fragment tag */ @@ -637,10 +636,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr) pr_debug("timer expired for frame with tag %d\n", entry->tag); - spin_lock(&flist_lock); list_del(&entry->list); - spin_unlock(&flist_lock); - dev_kfree_skb(entry->skb); kfree(entry); } @@ -727,7 +723,7 @@ lowpan_process_data(struct sk_buff *skb) * check if frame assembling with the same tag is * already in progress */ - spin_lock(&flist_lock); + spin_lock_bh(&flist_lock); list_for_each_entry(frame, &lowpan_fragments, list) if (frame->tag == tag) { @@ -761,9 +757,9 @@ lowpan_process_data(struct sk_buff *skb) if ((frame->bytes_rcv == frame->length) && frame->timer.expires > jiffies) { /* if timer haven't expired - first of all delete it */ - del_timer(&frame->timer); + del_timer_sync(&frame->timer); list_del(&frame->list); - spin_unlock(&flist_lock); + spin_unlock_bh(&flist_lock); dev_kfree_skb(skb); skb = frame->skb; @@ -774,7 +770,7 @@ lowpan_process_data(struct sk_buff *skb) break; } - spin_unlock(&flist_lock); + spin_unlock_bh(&flist_lock); return kfree_skb(skb), 0; } @@ -929,7 +925,7 @@ lowpan_process_data(struct sk_buff *skb) return lowpan_skb_deliver(skb, &hdr); unlock_and_drop: - spin_unlock(&flist_lock); + spin_unlock_bh(&flist_lock); drop: kfree_skb(skb); return -EINVAL; @@ -1196,19 +1192,9 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head) struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); struct net_device *real_dev = lowpan_dev->real_dev; struct lowpan_dev_record *entry, *tmp; - struct lowpan_fragment *frame, *tframe; ASSERT_RTNL(); - spin_lock(&flist_lock); - list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { - del_timer(&frame->timer); - list_del(&frame->list); - dev_kfree_skb(frame->skb); - kfree(frame); - } - spin_unlock(&flist_lock); - mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { if (entry->ldev == dev) { @@ -1264,9 +1250,24 @@ out: static void __exit lowpan_cleanup_module(void) { + struct lowpan_fragment *frame, *tframe; + lowpan_netlink_fini(); dev_remove_pack(&lowpan_packet_type); + + /* Now 6lowpan packet_type is removed, so no new fragments are + * expected on RX, therefore that's the time to clean incomplete + * fragments. + */ + spin_lock_bh(&flist_lock); + list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { + del_timer_sync(&frame->timer); + list_del(&frame->list); + dev_kfree_skb(frame->skb); + kfree(frame); + } + spin_unlock_bh(&flist_lock); } module_init(lowpan_init_module); -- cgit v1.2.3 From d4787a15432384826a0bed42d189fc2a97dc73ea Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Wed, 11 Jul 2012 06:51:14 +0000 Subject: 6lowpan: Fix null pointer dereference in UDP uncompression function When a UDP packet gets fragmented, a crash will occur at reassembly time. This is because skb->transport_header is not set during earlier period of fragment reassembly. As a consequence, call to udp_hdr() return NULL and uh (which is NULL) gets dereferenced without much test. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 6871ec1b30f8..416a54d31fb2 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -314,6 +314,9 @@ lowpan_uncompress_udp_header(struct sk_buff *skb) struct udphdr *uh = udp_hdr(skb); u8 tmp; + if (!uh) + goto err; + if (lowpan_fetch_skb_u8(skb, &tmp)) goto err; -- cgit v1.2.3 From 4576039ffc04ffe672081159a11cf6e0b875a069 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Wed, 11 Jul 2012 06:51:15 +0000 Subject: 6lowpan: Change byte order when storing/accessing u16 tag The tag field should be stored and accessed using big endian byte order (as intended in the specs). Or else, when displayed with a trafic analyser, such a Wireshark, the field not properly displayed (e.g. 0x01 00 instead of 0x00 01, and so on). Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 416a54d31fb2..536c6e21b20e 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -302,7 +302,7 @@ static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) if (unlikely(!pskb_may_pull(skb, 2))) return -EINVAL; - *val = skb->data[0] | (skb->data[1] << 8); + *val = (skb->data[0] << 8) | skb->data[1]; skb_pull(skb, 2); return 0; @@ -1006,8 +1006,8 @@ lowpan_skb_fragmentation(struct sk_buff *skb) /* first fragment header */ head[0] = LOWPAN_DISPATCH_FRAG1 | (payload_length & 0x7); head[1] = (payload_length >> 3) & 0xff; - head[2] = tag & 0xff; - head[3] = tag >> 8; + head[2] = tag >> 8; + head[3] = tag & 0xff; err = lowpan_fragment_xmit(skb, head, header_length, 0, 0); -- cgit v1.2.3 From 5e96855fc505082389813afcf796d4c46301d4fe Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Wed, 11 Jul 2012 06:51:16 +0000 Subject: 6lowpan: Change byte order when storing/accessing to len field Lenght field should be encoded using big endian byte order, such as intend in the specs. As it is currently written, the len field would not be decoded properly on an implementation using the correct byte ordering. Hence, it could lead to interroperability issues. Also, I rewrote the code so that iphc0 argument of lowpan_alloc_new_frame could be removed. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'net/ieee802154/6lowpan.c') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 536c6e21b20e..6a095225148e 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -645,7 +645,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr) } static struct lowpan_fragment * -lowpan_alloc_new_frame(struct sk_buff *skb, u8 iphc0, u8 len, u16 tag) +lowpan_alloc_new_frame(struct sk_buff *skb, u8 len, u16 tag) { struct lowpan_fragment *frame; @@ -656,7 +656,7 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u8 iphc0, u8 len, u16 tag) INIT_LIST_HEAD(&frame->list); - frame->length = (iphc0 & 7) | (len << 3); + frame->length = len; frame->tag = tag; /* allocate buffer for frame assembling */ @@ -714,14 +714,18 @@ lowpan_process_data(struct sk_buff *skb) case LOWPAN_DISPATCH_FRAGN: { struct lowpan_fragment *frame; - u8 len, offset; - u16 tag; + /* slen stores the rightmost 8 bits of the 11 bits length */ + u8 slen, offset; + u16 len, tag; bool found = false; - if (lowpan_fetch_skb_u8(skb, &len) || /* frame length */ + if (lowpan_fetch_skb_u8(skb, &slen) || /* frame length */ lowpan_fetch_skb_u16(skb, &tag)) /* fragment tag */ goto drop; + /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */ + len = ((iphc0 & 7) << 8) | slen; + /* * check if frame assembling with the same tag is * already in progress @@ -736,7 +740,7 @@ lowpan_process_data(struct sk_buff *skb) /* alloc new frame structure */ if (!found) { - frame = lowpan_alloc_new_frame(skb, iphc0, len, tag); + frame = lowpan_alloc_new_frame(skb, len, tag); if (!frame) goto unlock_and_drop; } @@ -1004,8 +1008,8 @@ lowpan_skb_fragmentation(struct sk_buff *skb) tag = fragment_tag++; /* first fragment header */ - head[0] = LOWPAN_DISPATCH_FRAG1 | (payload_length & 0x7); - head[1] = (payload_length >> 3) & 0xff; + head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7); + head[1] = payload_length & 0xff; head[2] = tag >> 8; head[3] = tag & 0xff; -- cgit v1.2.3