Hello everybody,
below is the patch which adds support for fragmentation in 6LoWPAN
point to point networks. This activity needs because of difference
in MTU: 1280 ipv6 and 128 ieee802.15.4
This patch is just a draft. Could anyone please look at
it and let me know your opinion.
The most doubtful moments for me are:
1. Should the list 'frag_list' and the mutex 'flist_lock' be
included into dev private data?
2. Can I use 'dev_queue_xmit' to send fragments to queue?
3. Creating new 'skb' instead of copying and modifying main one.
With best regards,
Alexander
>From 48472bae269b7b1a4047967ec21eadb217c4fd6d Mon Sep 17 00:00:00 2001
From: Alexander Smirnov <alex.bluesman.smir...@gmail.com>
Date: Thu, 20 Oct 2011 15:02:36 +0400
Subject: [PATCH] 6LoWPAN fragmentation support
Signed-off-by: Alexander Smirnov <alex.bluesman.smir...@gmail.com>
---
net/ieee802154/6lowpan.c | 286 +++++++++++++++++++++++++++++++++++++++++++++-
net/ieee802154/6lowpan.h | 3 +
2 files changed, 288 insertions(+), 1 deletions(-)
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c
index 96877bd..1923ec7 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan.c
@@ -113,6 +113,24 @@ struct lowpan_dev_record {
struct list_head list;
};
+struct lowpan_fragment {
+ u8 in_progress; /* assembling is in progress */
+ struct sk_buff *skb; /* skb to be assembled */
+ u8 *data; /* data to be stored */
+ struct mutex lock; /* concurency lock */
+ u16 length; /* frame length to be assemled */
+ u32 bytes_rcv; /* bytes received */
+ u16 tag; /* current fragment tag */
+ struct timer_list timer; /* assembling timer */
+ struct list_head list; /* fragments list handler */
+};
+
+static unsigned short fragment_tag;
+
+/* TODO: bind mutex and list to device */
+static LIST_HEAD(lowpan_fragments);
+struct mutex flist_lock;
+
static inline struct
lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
{
@@ -244,6 +262,18 @@ static u8 lowpan_fetch_skb_u8(struct sk_buff *skb)
return ret;
}
+static u16 lowpan_fetch_skb_u16(struct sk_buff *skb)
+{
+ u16 ret;
+
+ BUG_ON(skb->len < 2);
+
+ ret = skb->data[0] | (skb->data[1] << 8);
+ skb_pull(skb, 2);
+ return ret;
+}
+
+static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev);
static int lowpan_header_create(struct sk_buff *skb,
struct net_device *dev,
unsigned short type, const void *_daddr,
@@ -467,9 +497,102 @@ static int lowpan_header_create(struct sk_buff *skb,
memcpy(&(sa.hwaddr), saddr, 8);
mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+
+ /* frame fragmentation */
+
+ /*
+ * if payload + mac header doesn't fit MTU-sized frame
+ * we need to fragment it.
+ */
+ if (skb->len > (127 - 24)) { /* MTU - MAC_HEADER_LENGTH */
+ struct sk_buff *fr_skb;
+ u16 b_sent = 0;
+ unsigned short payload_len = skb->len;
+ int stat = 0;
+
+ pr_debug("%s: the frame is too big (0x%x),"
+ "fragmentation needed, using tag = 0x%x\n",
+ __func__, payload_len, fragment_tag);
+
+ fr_skb = skb_copy(skb, GFP_KERNEL);
+ if (!fr_skb)
+ goto error;
+
+ /* 40-bit - fragment dispatch size */
+ head = kzalloc(5, GFP_KERNEL);
+ if (!head)
+ goto error;
+
+ /* first fagment header */
+ head[0] = LOWPAN_DISPATCH_FRAG1 | (payload_len & 0x7);
+ head[1] = (payload_len >> 3) & 0xff;
+ head[2] = fragment_tag & 0xff;
+ head[3] = fragment_tag >> 8;
+
+
+ lowpan_raw_dump_inline(__func__, "first header",
+ head, 4);
+
+ memcpy(skb_push(fr_skb, 4), head, 4);
+ skb_trim(fr_skb, LOWPAN_FRAG_SIZE);
+
+ dev_hard_header(fr_skb, lowpan_dev_info(dev)->real_dev,
+ type, (void *)&da, (void *)&sa, fr_skb->len);
+
+ /* send fragment to dev queue */
+ dev_queue_xmit(fr_skb);
+
+ /* next fragments headers */
+ head[0] |= 0x20;
+
+ lowpan_raw_dump_inline(__func__, "next headers",
+ head, 5);
+
+ while (b_sent < payload_len) {
+ /* not the first fragment */
+ if (b_sent)
+ skb_pull(skb, LOWPAN_FRAG_SIZE);
+
+ pr_debug("%s: preparing fragment %d\n",
+ __func__, b_sent / LOWPAN_FRAG_SIZE);
+
+ /*
+ * create copy of current buffer and trim it
+ * down to fragment size
+ */
+ fr_skb = skb_copy(skb, GFP_KERNEL);
+ if (!fr_skb)
+ goto error;
+
+ skb_trim(fr_skb, LOWPAN_FRAG_SIZE);
+
+ /* add fragment header */
+ head[4] = b_sent / 8;
+ memcpy(skb_push(fr_skb, 5), head, 5);
+
+ b_sent += LOWPAN_FRAG_SIZE;
+
+ lowpan_raw_dump_table(__func__,
+ "fragment data", fr_skb->data, fr_skb->len);
+
+ stat = dev_hard_header(fr_skb,
+ lowpan_dev_info(dev)->real_dev, type,
+ (void *)&da, (void *)&sa, fr_skb->len);
+
+ dev_queue_xmit(fr_skb);
+ }
+
+ /* TODO: what's the correct way to skip default skb? */
+
+ fragment_tag++;
+ return stat;
+ } else
return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
type, (void *)&da, (void *)&sa, skb->len);
}
+error:
+ kfree_skb(skb);
+ return -ENOMEM;
}
static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr)
@@ -511,6 +634,23 @@ static int lowpan_skb_deliver(struct sk_buff *skb, struct
ipv6hdr *hdr)
return stat;
}
+static void lowpan_fragment_timer_expired(unsigned long tag)
+{
+ struct lowpan_fragment *entry, *tmp;
+
+ pr_debug("%s: timer expired for frame with tag %lu\n", __func__, tag);
+
+ mutex_lock(&flist_lock);
+ list_for_each_entry_safe(entry, tmp, &lowpan_fragments, list)
+ if (entry->tag == tag) {
+ list_del(&entry->list);
+ kfree(entry->data);
+ kfree(entry);
+ break;
+ }
+ mutex_unlock(&flist_lock);
+}
+
static int
lowpan_process_data(struct sk_buff *skb)
{
@@ -525,6 +665,139 @@ lowpan_process_data(struct sk_buff *skb)
if (skb->len < 2)
goto drop;
iphc0 = lowpan_fetch_skb_u8(skb);
+
+ /* fragments assmebling */
+ switch (iphc0 & 0xf8) {
+ /* first fragment of the frame */
+ case LOWPAN_DISPATCH_FRAG1:
+ {
+ struct lowpan_fragment *entry, *frame;
+ u16 tag;
+
+ lowpan_raw_dump_inline(__func__, "first frame fragment header",
+ skb->data, 3);
+
+ tmp = lowpan_fetch_skb_u8(skb);
+ tag = lowpan_fetch_skb_u16(skb);
+
+ /*
+ * check if frame assembling with the same tag is
+ * already in progress
+ */
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, &lowpan_fragments, list)
+ if (entry->tag == tag) {
+ pr_debug("%s ERROR: frame with this tag is"
+ "alredy in assembling", __func__);
+ goto drop_rcu;
+ }
+ rcu_read_unlock();
+
+ /* alloc new frame structure */
+ frame = kzalloc(sizeof(struct lowpan_fragment), GFP_KERNEL);
+ if (!frame)
+ goto drop;
+
+ INIT_LIST_HEAD(&frame->list);
+
+ frame->bytes_rcv = 0;
+ frame->length = (iphc0 & 7) | (tmp << 3);
+ frame->tag = tag;
+ /* allocate buffer for frame assembling */
+ frame->data = kzalloc(frame->length, GFP_KERNEL);
+ if (!frame->data) {
+ kfree(frame);
+ goto drop;
+ }
+
+ pr_debug("%s: frame to be assembled: length = 0x%x, "
+ "tag = 0x%x\n", __func__, frame->length, frame->tag);
+
+ init_timer(&frame->timer);
+ /* (number of fragments) * (fragment processing time-out) */
+ frame->timer.expires = jiffies +
+ (frame->length / LOWPAN_FRAG_SIZE + 1) * LOWPAN_FRAG_TIMEOUT;
+ frame->timer.data = tag;
+ frame->timer.function = lowpan_fragment_timer_expired;
+
+ add_timer(&frame->timer);
+
+ mutex_lock(&flist_lock);
+ list_add_tail(&frame->list, &lowpan_fragments);
+ mutex_unlock(&flist_lock);
+
+ return kfree_skb(skb), 0;
+ }
+ /* second and next fragment of the frame */
+ case LOWPAN_DISPATCH_FRAGN:
+ {
+ u16 tag;
+ struct lowpan_fragment *entry, *t;
+
+ lowpan_raw_dump_inline(__func__, "next fragment header",
+ skb->data, 4);
+
+ lowpan_fetch_skb_u8(skb); /* skip frame length byte */
+ tag = lowpan_fetch_skb_u16(skb);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, &lowpan_fragments, list)
+ if (entry->tag == tag)
+ break;
+ rcu_read_unlock();
+
+ if (entry->tag != tag) {
+ pr_debug("%s ERROR: no frame structure found for this"
+ "fragment", __func__);
+ goto drop;
+ }
+
+ tmp = lowpan_fetch_skb_u8(skb); /* fetch offset */
+
+ lowpan_raw_dump_table(__func__, "next fragment payload",
+ skb->data, skb->len);
+
+ /* if payload fits buffer, copy it */
+ if ((tmp * 8 + skb->len) <= entry->length) /* TODO: likely? */
+ memcpy(entry->data + tmp * 8, skb->data, skb->len);
+ else
+ goto drop;
+
+ entry->bytes_rcv += skb->len;
+
+ pr_debug("%s: frame length = 0x%x, bytes received = 0x%x\n",
+ __func__, entry->length, entry->bytes_rcv);
+
+ /* frame assembling complete */
+ if (entry->bytes_rcv == entry->length) {
+ struct sk_buff *tmp = skb;
+
+ mutex_lock(&flist_lock);
+ list_for_each_entry_safe(entry, t, &lowpan_fragments,
list)
+ if (entry->tag == tag) {
+ list_del(&entry->list);
+ /* copy and clear skb */
+ skb = skb_copy_expand(skb,
entry->length, skb_tailroom(skb), GFP_KERNEL);
+ skb_pull(skb, skb->len);
+ /* copy new data to skb */
+ memcpy(skb_push(skb, entry->length),
entry->data, entry->length);
+ kfree_skb(tmp);
+ del_timer(&entry->timer);
+ kfree(entry->data);
+ kfree(entry);
+
+ iphc0 = lowpan_fetch_skb_u8(skb);
+ break;
+ }
+ mutex_unlock(&flist_lock);
+ break;
+ }
+ return kfree_skb(skb), 0;
+ }
+ default:
+ break;
+ }
+
iphc1 = lowpan_fetch_skb_u8(skb);
_saddr = mac_cb(skb)->sa.hwaddr;
@@ -674,6 +947,8 @@ lowpan_process_data(struct sk_buff *skb)
lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
sizeof(hdr));
return lowpan_skb_deliver(skb, &hdr);
+drop_rcu:
+ rcu_read_unlock();
drop:
kfree(skb);
return -EINVAL;
@@ -765,8 +1040,15 @@ static int lowpan_rcv(struct sk_buff *skb, struct
net_device *dev,
goto drop;
/* check that it's our buffer */
- if ((skb->data[0] & 0xe0) == 0x60)
+ switch (skb->data[0] & 0xe0) {
+ case 0x60: /* ipv6 datagram */
+ case 0xc0: /* first fragment header */
+ case 0xe0: /* next fragments headers */
lowpan_process_data(skb);
+ break;
+ default:
+ break;
+ }
return NET_RX_SUCCESS;
@@ -793,6 +1075,8 @@ static int lowpan_newlink(struct net *src_net, struct
net_device *dev,
lowpan_dev_info(dev)->real_dev = real_dev;
mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
+ mutex_init(&flist_lock);
+
entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL);
if (!entry)
return -ENOMEM;
diff --git a/net/ieee802154/6lowpan.h b/net/ieee802154/6lowpan.h
index 5d8cf80..e8e57c4 100644
--- a/net/ieee802154/6lowpan.h
+++ b/net/ieee802154/6lowpan.h
@@ -159,6 +159,9 @@
#define LOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */
#define LOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */
+#define LOWPAN_FRAG_SIZE 40 /* fragment payload size */
+#define LOWPAN_FRAG_TIMEOUT (HZ * 2) /* processing time: 2 sec */
+
/*
* Values of fields within the IPHC encoding first byte
* (C stands for compressed and I for inline)
--
1.7.2.5
------------------------------------------------------------------------------
The demand for IT networking professionals continues to grow, and the
demand for specialized networking skills is growing even more rapidly.
Take a complimentary Learning@Ciosco Self-Assessment and learn
about Cisco certifications, training, and career opportunities.
http://p.sf.net/sfu/cisco-dev2dev
_______________________________________________
Linux-zigbee-devel mailing list
Linux-zigbee-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel