2014-11-29 21:14:41

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 0/2] 6lowpan: introduce nhc framework

This patch series introduce the next header compression framework. Currently
we support udp compression/uncompression only. This framework allow to add new
next header compression formats easily.

If somebody wants to add a new header compression format and some information
are missing while calling compression and uncompression callbacks. Please
feel free to make framework changes according these callbacks.

Note:

If building 6lowpan_udp as module please make sure it's loaded before running
rfc6282 udp connection. If it's not loaded we send 6lowpan with raw udp header
and next header compression bit isn't set. Nevertheless if you use rfc6282
connection and 6lowpan_udp isn't loaded following will be printed:

"ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."

changes since v2:
- make udp nhc as module as suggested by Marcel Holtmann
- fix comment header in nhc_udp.c

I didn't make the lowpan_nhc declaration "const" because this will occur
issues with rb_node, id and idmask array. Which will manipulated during
runtime.

Cc: Jukka Rissanen <[email protected]>
Cc: Martin Townsend <[email protected]>
Cc: Marcel Holtmann <[email protected]>

Alexander Aring (2):
6lowpan: add generic nhc layer interface
6lowpan: add udp compression via nhc layer

net/6lowpan/Kconfig | 7 ++
net/6lowpan/Makefile | 6 +-
net/6lowpan/iphc.c | 176 ++++----------------------------------------------
net/6lowpan/nhc.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++
net/6lowpan/nhc.h | 130 +++++++++++++++++++++++++++++++++++++
net/6lowpan/nhc_udp.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 497 insertions(+), 166 deletions(-)
create mode 100644 net/6lowpan/nhc.c
create mode 100644 net/6lowpan/nhc.h
create mode 100644 net/6lowpan/nhc_udp.c

--
2.1.3


2014-11-29 21:14:42

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 1/2] 6lowpan: add generic nhc layer interface

This patch adds a generic next header compression layer interface. There
exists various methods to do a header compression after 6LoWPAN header
to save payload. This introduce a generic nhc header which allow a
simple adding of a new header compression format instead of a static
implementation inside the 6LoWPAN header compression and uncompression
function.

Signed-off-by: Alexander Aring <[email protected]>
Cc: Jukka Rissanen <[email protected]>
Cc: Martin Townsend <[email protected]>
---
net/6lowpan/Makefile | 2 +-
net/6lowpan/nhc.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++
net/6lowpan/nhc.h | 130 ++++++++++++++++++++++++++++++++++++++
3 files changed, 306 insertions(+), 1 deletion(-)
create mode 100644 net/6lowpan/nhc.c
create mode 100644 net/6lowpan/nhc.h

diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index 415886b..4215602 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_6LOWPAN) := 6lowpan.o

-6lowpan-y := iphc.o
+6lowpan-y := iphc.o nhc.o
diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
new file mode 100644
index 0000000..f64b244
--- /dev/null
+++ b/net/6lowpan/nhc.c
@@ -0,0 +1,175 @@
+/*
+ * 6LoWPAN next header compression
+ *
+ *
+ * Authors:
+ * Alexander Aring <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/netdevice.h>
+
+#include <net/ipv6.h>
+
+#include "nhc.h"
+
+static struct rb_root rb_root = RB_ROOT;
+static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX];
+
+static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
+{
+ struct rb_node **new = &rb_root.rb_node, *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct lowpan_nhc *this = container_of(*new, struct lowpan_nhc,
+ node);
+ int result, len_dif, len;
+
+ len_dif = nhc->idlen - this->idlen;
+
+ if (nhc->idlen < this->idlen)
+ len = nhc->idlen;
+ else
+ len = this->idlen;
+
+ result = memcmp(nhc->id, this->id, len);
+ if (!result)
+ result = len_dif;
+
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&nhc->node, parent, new);
+ rb_insert_color(&nhc->node, &rb_root);
+
+ return 0;
+}
+
+static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
+{
+ rb_erase(&nhc->node, &rb_root);
+}
+
+struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
+{
+ struct rb_node *node = rb_root.rb_node;
+ const u8 *nhcid_skb_ptr = skb->data;
+
+ while (node) {
+ struct lowpan_nhc *nhc = container_of(node, struct lowpan_nhc,
+ node);
+ u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
+ int result, i;
+
+ if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
+ return NULL;
+
+ /* copy and mask afterwards the nhid value from skb */
+ memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
+ for (i = 0; i < nhc->idlen; i++)
+ nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
+
+ result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return nhc;
+ }
+
+ return NULL;
+}
+
+struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr)
+{
+ return lowpan_nexthdr_nhcs[nexthdr];
+}
+
+int lowpan_nhc_do_compression(struct lowpan_nhc *nhc, struct sk_buff *skb,
+ u8 **hc_ptr, u8 *iphc0)
+{
+ int ret;
+
+ if (!nhc)
+ return 0;
+
+ ret = nhc->compress(skb, hc_ptr);
+ if (ret == 0)
+ *iphc0 |= LOWPAN_IPHC_NH_C;
+
+ return ret;
+}
+
+int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct ipv6hdr *hdr)
+{
+ struct lowpan_nhc *nhc;
+ /* default dropping if nothing found */
+ int ret = 0;
+
+ nhc = lowpan_nhc_by_nhcid(skb);
+ if (nhc) {
+ ret = nhc->uncompress(skb, sizeof(*hdr) + nhc->nexthdrlen);
+ if (ret == 0) {
+ hdr->nexthdr = nhc->nexthdr;
+ skb_reset_transport_header(skb);
+ raw_dump_table(__func__, "raw transport header dump",
+ skb_transport_header(skb),
+ nhc->nexthdrlen);
+ } else if (ret == -ENOTSUPP) {
+ netdev_warn(skb->dev, "received %s which is not supported for uncompression.\n",
+ nhc->name);
+ }
+ } else {
+ netdev_warn(skb->dev, "received nhc which is not supported. Dropping.\n");
+ }
+
+ return ret;
+}
+
+int lowpan_nhc_add(struct lowpan_nhc *nhc)
+{
+ int ret;
+
+ if (!nhc->idlen || !nhc->idsetup || !nhc->compress || !nhc->uncompress)
+ return -EINVAL;
+
+ WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
+ "LOWPAN_NHC_MAX_ID_LEN should be updated to %d.\n",
+ nhc->idlen);
+
+ nhc->idsetup(nhc);
+
+ if (lowpan_nexthdr_nhcs[nhc->nexthdr])
+ return -EEXIST;
+
+ ret = lowpan_nhc_insert(nhc);
+ if (ret < 0)
+ goto out;
+
+ lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
+out:
+ return ret;
+}
+EXPORT_SYMBOL(lowpan_nhc_add);
+
+void lowpan_nhc_del(struct lowpan_nhc *nhc)
+{
+ lowpan_nhc_remove(nhc);
+ lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
+
+ synchronize_net();
+}
+EXPORT_SYMBOL(lowpan_nhc_del);
diff --git a/net/6lowpan/nhc.h b/net/6lowpan/nhc.h
new file mode 100644
index 0000000..416a85f
--- /dev/null
+++ b/net/6lowpan/nhc.h
@@ -0,0 +1,130 @@
+#ifndef __6LOWPAN_NHC_H
+#define __6LOWPAN_NHC_H
+
+#include <linux/skbuff.h>
+#include <linux/rbtree.h>
+
+#include <net/6lowpan.h>
+#include <net/ipv6.h>
+
+#define LOWPAN_NHC_MAX_ID_LEN 1
+
+/**
+ * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
+ *
+ * @__nhc: variable name of the lowpan_nhc struct.
+ * @_name: const char * of common header compression name.
+ * @_nexthdr: ipv6 nexthdr field for the header compression.
+ * @_nexthdrlen: ipv6 nexthdr len for the reserved space.
+ * @_idsetup: callback to setup id and mask values.
+ * @_idlen: len for the next header id and mask, should be always the same.
+ * @_uncompress: callback for uncompression call.
+ * @_compress: callback for compression call.
+ */
+#define LOWPAN_NHC(__nhc, _name, _nexthdr, \
+ _nexthdrlen, _idsetup, \
+ _idlen, _uncompress, \
+ _compress) \
+static u8 __nhc##_val[_idlen]; \
+static u8 __nhc##_mask[_idlen]; \
+static struct lowpan_nhc __nhc = { \
+ .name = _name, \
+ .nexthdr = _nexthdr, \
+ .nexthdrlen = _nexthdrlen, \
+ .id = __nhc##_val, \
+ .idmask = __nhc##_mask, \
+ .idlen = _idlen, \
+ .idsetup = _idsetup, \
+ .uncompress = _uncompress, \
+ .compress = _compress, \
+}
+
+#define module_lowpan_nhc(__nhc) \
+static int __init __nhc##_init(void) \
+{ \
+ return lowpan_nhc_add(&(__nhc)); \
+} \
+module_init(__nhc##_init); \
+static void __exit __nhc##_exit(void) \
+{ \
+ lowpan_nhc_del(&(__nhc)); \
+} \
+module_exit(__nhc##_exit);
+
+/**
+ * struct lowpan_nhc - hold 6lowpan next hdr compression ifnformation
+ *
+ * @node: holder for the rbtree.
+ * @name: name of the specific next header compression
+ * @nexthdr: next header value of the protocol which should be compressed.
+ * @nexthdrlen: ipv6 nexthdr len for the reserved space.
+ * @id: array for nhc id. Note this need to be in network byteorder.
+ * @mask: array for nhc id mask. Note this need to be in network byteorder.
+ * @len: the length of the next header id and mask.
+ * @setup: callback to setup fill the next header id value and mask.
+ * @compress: callback to do the header compression.
+ * @uncompress: callback to do the header uncompression.
+ */
+struct lowpan_nhc {
+ struct rb_node node;
+ const char *name;
+ const u8 nexthdr;
+ const size_t nexthdrlen;
+ u8 *id;
+ u8 *idmask;
+ const size_t idlen;
+
+ void (*idsetup)(struct lowpan_nhc *nhc);
+ int (*uncompress)(struct sk_buff *skb, size_t needed);
+ int (*compress)(struct sk_buff *skb, u8 **hc_ptr);
+};
+
+/**
+ * lowpan_nhc_by_nhcid - returns the 6lowpan nhc by nhcid
+ *
+ * @skb: skb with skb->data which is pointed to 6lowpan nhc id.
+ */
+struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb);
+
+/**
+ * lowpan_nhc_by_nexthdr - return the 6lowpan nhc by ipv6 nexthdr.
+ *
+ * @nexthdr: ipv6 nexthdr value.
+ */
+struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr);
+
+/**
+ * lowpan_nhc_do_compression - wrapper for calling compress callback
+ *
+ * @nhc: 6LoWPAN nhc context, get by lowpan_search_nhc_*.
+ * @skb: skb of 6LoWPAN header to read nhc and replace header.
+ * @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
+ * replaced header.
+ * @iphc0: First iphc byte, to set NHC bit.
+ */
+int lowpan_nhc_do_compression(struct lowpan_nhc *nhc, struct sk_buff *skb,
+ u8 **hc_ptr, u8 *iphc0);
+
+/**
+ * lowpan_nhc_do_uncompression - wrapper for calling uncompress callback
+ *
+ * @skb: skb of 6LoWPAN header, skb->data should be pointed to nhc id value.
+ * @hdr: ipv6 header to set the according nexthdr value.
+ */
+int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct ipv6hdr *hdr);
+
+/**
+ * lowpan_nhc_add - register a next header compression to framework
+ *
+ * @nhc: nhc which should be add.
+ */
+int lowpan_nhc_add(struct lowpan_nhc *nhc);
+
+/**
+ * lowpan_nhc_del - delete a next header compression from framework
+ *
+ * @nhc: nhc which should be delete.
+ */
+void lowpan_nhc_del(struct lowpan_nhc *nhc);
+
+#endif /* __6LOWPAN_NHC_H */
--
2.1.3


2014-11-29 21:14:43

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 2/2] 6lowpan: add udp compression via nhc layer

This patch move UDP header compression and uncompression into the
generic 6LoWPAN nhc header compression layer. Moreover this patch
activates the nhc layer compression in iphc compression and
uncompression functions.

Signed-off-by: Alexander Aring <[email protected]>
Cc: Jukka Rissanen <[email protected]>
Cc: Martin Townsend <[email protected]>
---
net/6lowpan/Kconfig | 7 ++
net/6lowpan/Makefile | 4 +-
net/6lowpan/iphc.c | 176 ++++----------------------------------------------
net/6lowpan/nhc_udp.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 191 insertions(+), 165 deletions(-)
create mode 100644 net/6lowpan/nhc_udp.c

diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
index e4a02ef..cb037e0 100644
--- a/net/6lowpan/Kconfig
+++ b/net/6lowpan/Kconfig
@@ -4,3 +4,10 @@ config 6LOWPAN
---help---
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
+
+config 6LOWPAN_UDP
+ tristate "6LoWPAN UDP compression"
+ depends on 6LOWPAN
+ default y
+ ---help---
+ This enables support for 6LoWPAN UDP compression according RFC6282.
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index 4215602..24fc27f 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,3 +1,5 @@
-obj-$(CONFIG_6LOWPAN) := 6lowpan.o
+obj-$(CONFIG_6LOWPAN) += 6lowpan.o
+obj-$(CONFIG_6LOWPAN_UDP) += 6lowpan_udp.o

6lowpan-y := iphc.o nhc.o
+6lowpan_udp-y := nhc_udp.o
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index aced97d..dd029ef 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -57,6 +57,8 @@
#include <net/ipv6.h>
#include <net/af_ieee802154.h>

+#include "nhc.h"
+
/* Uncompress address function for source and
* destination address(non-multicast).
*
@@ -227,77 +229,6 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
return 0;
}

-static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
-{
- bool fail;
- u8 tmp = 0, val = 0;
-
- fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
-
- if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
- pr_debug("UDP header uncompression\n");
- switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
- case LOWPAN_NHC_UDP_CS_P_00:
- fail |= lowpan_fetch_skb(skb, &uh->source,
- sizeof(uh->source));
- fail |= lowpan_fetch_skb(skb, &uh->dest,
- sizeof(uh->dest));
- break;
- case LOWPAN_NHC_UDP_CS_P_01:
- fail |= lowpan_fetch_skb(skb, &uh->source,
- sizeof(uh->source));
- fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
- uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
- break;
- case LOWPAN_NHC_UDP_CS_P_10:
- fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
- uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
- fail |= lowpan_fetch_skb(skb, &uh->dest,
- sizeof(uh->dest));
- break;
- case LOWPAN_NHC_UDP_CS_P_11:
- fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
- uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
- (val >> 4));
- uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
- (val & 0x0f));
- break;
- default:
- pr_debug("ERROR: unknown UDP format\n");
- goto err;
- }
-
- pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
- ntohs(uh->source), ntohs(uh->dest));
-
- /* checksum */
- if (tmp & LOWPAN_NHC_UDP_CS_C) {
- pr_debug_ratelimited("checksum elided currently not supported\n");
- goto err;
- } else {
- fail |= lowpan_fetch_skb(skb, &uh->check,
- sizeof(uh->check));
- }
-
- /* UDP length needs to be infered from the lower layers
- * here, we obtain the hint from the remaining size of the
- * frame
- */
- uh->len = htons(skb->len + sizeof(struct udphdr));
- pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
- } else {
- pr_debug("ERROR: unsupported NH format\n");
- goto err;
- }
-
- if (fail)
- goto err;
-
- return 0;
-err:
- return -EINVAL;
-}
-
/* TTL uncompression values */
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };

@@ -430,27 +361,9 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,

/* UDP data uncompression */
if (iphc0 & LOWPAN_IPHC_NH_C) {
- struct udphdr uh;
- const int needed = sizeof(struct udphdr) + sizeof(hdr);
-
- if (uncompress_udp_header(skb, &uh))
- return -EINVAL;
-
- /* replace the compressed UDP head by the uncompressed UDP
- * header
- */
- err = skb_cow(skb, needed);
- if (unlikely(err))
+ err = lowpan_nhc_do_uncompression(skb, &hdr);
+ if (err < 0)
return err;
-
- skb_push(skb, sizeof(struct udphdr));
- skb_reset_transport_header(skb);
- skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
-
- raw_dump_table(__func__, "raw UDP header dump",
- (u8 *)&uh, sizeof(uh));
-
- hdr.nexthdr = UIP_PROTO_UDP;
} else {
err = skb_cow(skb, sizeof(hdr));
if (unlikely(err))
@@ -503,79 +416,15 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
return rol8(val, shift);
}

-static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
-{
- struct udphdr *uh;
- u8 tmp;
-
- /* In the case of RAW sockets the transport header is not set by
- * the ip6 stack so we must set it ourselves
- */
- if (skb->transport_header == skb->network_header)
- skb_set_transport_header(skb, sizeof(struct ipv6hdr));
-
- uh = udp_hdr(skb);
-
- if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
- LOWPAN_NHC_UDP_4BIT_PORT) &&
- ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
- LOWPAN_NHC_UDP_4BIT_PORT)) {
- pr_debug("UDP header: both ports compression to 4 bits\n");
- /* compression value */
- tmp = LOWPAN_NHC_UDP_CS_P_11;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- /* source and destination port */
- tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
- ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
- LOWPAN_NHC_UDP_8BIT_PORT) {
- pr_debug("UDP header: remove 8 bits of dest\n");
- /* compression value */
- tmp = LOWPAN_NHC_UDP_CS_P_01;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- /* source port */
- lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
- /* destination port */
- tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
- LOWPAN_NHC_UDP_8BIT_PORT) {
- pr_debug("UDP header: remove 8 bits of source\n");
- /* compression value */
- tmp = LOWPAN_NHC_UDP_CS_P_10;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- /* source port */
- tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- /* destination port */
- lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
- } else {
- pr_debug("UDP header: can't compress\n");
- /* compression value */
- tmp = LOWPAN_NHC_UDP_CS_P_00;
- lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
- /* source port */
- lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
- /* destination port */
- lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
- }
-
- /* checksum is always inline */
- lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
-
- /* skip the UDP header */
- skb_pull(skb, sizeof(struct udphdr));
-}
-
int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
{
u8 tmp, iphc0, iphc1, *hc_ptr;
+ struct lowpan_nhc *nhc;
struct ipv6hdr *hdr;
u8 head[100] = {};
- int addr_type;
+ int ret, addr_type;

if (type != ETH_P_IPV6)
return -EINVAL;
@@ -652,11 +501,10 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,

/* NOTE: payload length is always compressed */

- /* Next Header is compress if UDP */
- if (hdr->nexthdr == UIP_PROTO_UDP)
+ nhc = lowpan_nhc_by_nexthdr(hdr->nexthdr);
+ if (nhc)
iphc0 |= LOWPAN_IPHC_NH_C;
-
- if ((iphc0 & LOWPAN_IPHC_NH_C) == 0)
+ else
lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
sizeof(hdr->nexthdr));

@@ -744,9 +592,9 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
}
}

- /* UDP header compression */
- if (hdr->nexthdr == UIP_PROTO_UDP)
- compress_udp_header(&hc_ptr, skb);
+ ret = lowpan_nhc_do_compression(nhc, skb, &hc_ptr, &iphc0);
+ if (ret < 0)
+ return ret;

head[0] = iphc0;
head[1] = iphc1;
diff --git a/net/6lowpan/nhc_udp.c b/net/6lowpan/nhc_udp.c
new file mode 100644
index 0000000..0f6de5fc
--- /dev/null
+++ b/net/6lowpan/nhc_udp.c
@@ -0,0 +1,169 @@
+/*
+ * 6LoWPAN IPv6 UDP compression
+ *
+ *
+ * Authors:
+ * Alexander Aring <[email protected]>
+ *
+ * Orignal written by:
+ * Alexander Smirnov <[email protected]>
+ * Jon Smirl <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+
+#include "nhc.h"
+
+#define LOWPAN_NHC_UDP_IDLEN 1
+
+static int udp_uncompress(struct sk_buff *skb, size_t needed)
+{
+ u8 tmp = 0, val = 0;
+ struct udphdr uh;
+ bool fail;
+ int err;
+
+ fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
+
+ pr_debug("UDP header uncompression\n");
+ switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+ case LOWPAN_NHC_UDP_CS_P_00:
+ fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
+ fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
+ break;
+ case LOWPAN_NHC_UDP_CS_P_01:
+ fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_10:
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+ fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
+ break;
+ case LOWPAN_NHC_UDP_CS_P_11:
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
+ uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
+ break;
+ default:
+ BUG();
+ }
+
+ pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+ ntohs(uh.source), ntohs(uh.dest));
+
+ /* checksum */
+ if (tmp & LOWPAN_NHC_UDP_CS_C) {
+ pr_debug_ratelimited("checksum elided currently not supported\n");
+ fail = true;
+ } else {
+ fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
+ }
+
+ if (fail)
+ return -EINVAL;
+
+ /* UDP length needs to be infered from the lower layers
+ * here, we obtain the hint from the remaining size of the
+ * frame
+ */
+ uh.len = htons(skb->len + sizeof(struct udphdr));
+ pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
+
+ /* replace the compressed UDP head by the uncompressed UDP
+ * header
+ */
+ err = skb_cow(skb, needed);
+ if (unlikely(err))
+ return err;
+
+ skb_push(skb, sizeof(struct udphdr));
+ skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+ return 0;
+}
+
+static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
+{
+ struct udphdr *uh;
+ u8 tmp;
+
+ /* In the case of RAW sockets the transport header is not set by
+ * the ip6 stack so we must set it ourselves
+ */
+ if (skb->transport_header == skb->network_header)
+ skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+ uh = udp_hdr(skb);
+
+ if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT) &&
+ ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT)) {
+ pr_debug("UDP header: both ports compression to 4 bits\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_11;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source and destination port */
+ tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+ ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of dest\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_01;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+ /* destination port */
+ tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of source\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_10;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* destination port */
+ lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+ } else {
+ pr_debug("UDP header: can't compress\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_00;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+ /* destination port */
+ lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+ }
+
+ /* checksum is always inline */
+ lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
+
+ /* skip the UDP header */
+ skb_pull(skb, sizeof(struct udphdr));
+
+ return 0;
+}
+
+static void udp_nhid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_NHC_UDP_ID;
+ nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
+}
+
+LOWPAN_NHC(udp_nhc, "IPv6 UDP Header", NEXTHDR_UDP, sizeof(struct udphdr),
+ udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
+
+module_lowpan_nhc(udp_nhc);
+MODULE_LICENSE("GPL");
--
2.1.3


2014-12-02 10:12:50

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 0/2] 6lowpan: introduce nhc framework

On Tue, Dec 02, 2014 at 11:23:36AM +0200, Jukka Rissanen wrote:
...
>
> I forgot to mention earlier that I think we need to support the case
> where some clients are using different compression method than the other
> (at the same time). What do you think?
>
>

_Currently_ I would to do the following:


Sending side:

We have a mapping from nexthdr IPv6 field to the configurated (at the
moment the defaults compression methods) next header compression.

lowpan_compress -> nexthdr to nhc struct -> do nhc compression

This is a 1:1 mapping for nexthdr as UDP we have RFC6282 UDP.

_Maybe_ we can do some socket options settings in the future to have not
always a 1:1 mapping. Sending sockopts to 6LoWPAN layer seems to be
difficult, I don't know how much possible that is and the IPv6 agree
with that. Otherwise we need some great idea to make allow different
compression methods per socket. Currently a 1:1 seems to be okay.


Receiving side:

There we need to support everything what we can support. The next header
compression id is a variable length of bitstring (that's why there is a
complicated rbtree datastructure inside the nhc framework).

This number is unique to each compression methods. It's some kind of n:1
mapping e.g. (UDP RFC6282) to UDP and (UDP FOOBAR) to UDP.



So sending side _can_ be optionally and receiving _should_ be implemented.

I have a new series which makes both optionally. The reason is that we
can register the NHC ID and print a warning which NHC ID isn't
supported. So this beahviour is more "We know this NHC but we don't
support compression and uncompression". This will print a warning and
users have two options:

1. Implement the nhc.

2. Change network configuration to not do this nhc on other nodes.

- Alex

2014-12-02 09:23:36

by Jukka Rissanen

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 0/2] 6lowpan: introduce nhc framework

Hi Alex,

On ma, 2014-12-01 at 16:01 +0100, Alexander Aring wrote:
> Hi Jukka,
>
> On Mon, Dec 01, 2014 at 02:33:03PM +0200, Jukka Rissanen wrote:
> > Hi Alex,
> >
> > On la, 2014-11-29 at 22:14 +0100, Alexander Aring wrote:
> > > This patch series introduce the next header compression framework. Currently
> > > we support udp compression/uncompression only. This framework allow to add new
> > > next header compression formats easily.
> > >
> > > If somebody wants to add a new header compression format and some information
> > > are missing while calling compression and uncompression callbacks. Please
> > > feel free to make framework changes according these callbacks.
> > >
> > > Note:
> > >
> > > If building 6lowpan_udp as module please make sure it's loaded before running
> > > rfc6282 udp connection. If it's not loaded we send 6lowpan with raw udp header
> > > and next header compression bit isn't set. Nevertheless if you use rfc6282
> > > connection and 6lowpan_udp isn't loaded following will be printed:
> > >
> > > "ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."
> > >
> > > changes since v2:
> > > - make udp nhc as module as suggested by Marcel Holtmann
> > > - fix comment header in nhc_udp.c
> > >
> > > I didn't make the lowpan_nhc declaration "const" because this will occur
> > > issues with rb_node, id and idmask array. Which will manipulated during
> > > runtime.
> > >
> >
> >
> > Tested it between two BT 6lowpan connections. If both ends load the
> > 6lowpan_udp module, then no issues. If only the other end has loaded the
> > module, the other end (receiving end) crashed.
> >
> >
> > [ 132.417733] (NULL net_device): received nhc which is not supported.
> > Dropping.
> > [ 132.722432] skbuff: skb_under_panic: text:d19cb60c len:55 put:40
> > head:cd923000 data:cd922fec tail:0xcd923023 end:0xcd923740 dev:<NULL>
> > [ 133.536256] ------------[ cut here ]------------
> > [ 133.537233] kernel BUG at 3.16+gitAUTOINC
> > +f6af675ef5-r215/linux/net/core/skbuff.c:100!
> > [ 133.537233] invalid opcode: 0000 [#1] PREEMPT SMP
> > [ 133.537233] Modules linked in: bluetooth_6lowpan 6lowpan nfc ecb
> > btusb bluetooth rfkill snd_intel8x0 ohci_pci snd_ac97_codec ac97_bus
> > parport_pc parport uvesafb
> > [ 133.537233] CPU: 0 PID: 327 Comm: kworker/u3:0 Not tainted
> > 3.17.0-bt6lowpan #1
> > [ 133.537233] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
> > VirtualBox 12/01/2006
> > [ 133.537233] Workqueue: hci0 hci_rx_work [bluetooth]
> > [ 133.537233] task: c0170000 ti: cc7d0000 task.ti: cc7d0000
> > [ 133.537233] EIP: 0060:[<c1779838>] EFLAGS: 00010292 CPU: 0
> > [ 133.537233] EIP is at skb_panic+0x68/0x70
> > [ 133.537233] EAX: 0000007a EBX: cd923000 ECX: 00000000 EDX: 00000001
> > [ 133.537233] ESI: c1b2a11b EDI: 00000004 EBP: cc7d1c18 ESP: cc7d1be8
> > [ 133.537233] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
> > [ 133.537233] CR0: 8005003b CR2: b76d9000 CR3: 0e1e5000 CR4: 000006d0
> > [ 133.537233] Stack:
> > [ 133.537233] c1ba8748 c1982e68 d19cb60c 00000037 00000028 cd923000
> > cd922fec cd923023
> > [ 133.537233] cd923740 c1b2a11b cd914300 00000003 cc7d1c24 c1779881
> > c1982e68 cc7d1c94
> > [ 133.537233] d19cb60c c0304c48 00000003 00000008 cc7d1c40 00000003
> > 017d1c64 00000003
> > [ 133.537233] Call Trace:
> > [ 133.537233] [<d19cb60c>] ? lowpan_header_decompress+0x26c/0x980
> > [6lowpan]
> > [ 133.537233] [<c1779881>] skb_push+0x41/0x50
> > [ 133.537233] [<d19cb60c>] lowpan_header_decompress+0x26c/0x980
> > [6lowpan]
> > [ 133.537233] [<d19daeb4>] chan_recv_cb+0x244/0x3f0
> > [bluetooth_6lowpan]
> > [ 133.537233] [<d1738711>] l2cap_data_channel+0x8e1/0x980 [bluetooth]
> > [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> > [ 133.537233] [<d173aa8f>] l2cap_recv_frame+0xbf/0x2ee0 [bluetooth]
> > [ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
> > [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> > [ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
> > [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> > [ 133.537233] [<c107770f>] ? sched_clock_cpu+0x10f/0x160
> > [ 133.537233] [<c10729cb>] ? get_parent_ip+0xb/0x40
> > [ 133.537233] [<c1072a4b>] ? preempt_count_add+0x4b/0xa0
> > [ 133.537233] [<c13e0112>] ? debug_smp_processor_id+0x12/0x20
> > [ 133.537233] [<c108e584>] ? get_lock_stats+0x24/0x40
> > [ 133.537233] [<c1090a34>] ? mark_held_locks+0x64/0x90
> > [ 133.537233] [<c189dbfd>] ? __mutex_unlock_slowpath+0xcd/0x1b0
> > [ 133.537233] [<c13e012f>] ? __this_cpu_preempt_check+0xf/0x20
> > [ 133.537233] [<c1090b54>] ? trace_hardirqs_on_caller+0xf4/0x240
> > [ 133.537233] [<c108e326>] ? trace_hardirqs_off_caller+0xb6/0x160
> > [ 133.537233] [<d173ee19>] l2cap_recv_acldata+0x2f9/0x340 [bluetooth]
> > [ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
> > [ 133.537233] [<d1709ce9>] hci_rx_work+0x369/0x4a0 [bluetooth]
> > [ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
> > [ 133.537233] [<c1063203>] process_one_work+0x1b3/0x4e0
> > [ 133.537233] [<c1063163>] ? process_one_work+0x113/0x4e0
> > [ 133.537233] [<c10638c9>] worker_thread+0x39/0x440
> > [ 133.537233] [<c1063890>] ? init_pwq+0xc0/0xc0
> > [ 133.537233] [<c1067ce8>] kthread+0xa8/0xc0
> > [ 133.537233] [<c1090cab>] ? trace_hardirqs_on+0xb/0x10
> > [ 133.537233] [<c18a1741>] ret_from_kernel_thread+0x21/0x30
> > [ 133.537233] [<c1067c40>] ? kthread_create_on_node+0x160/0x160
> > [ 133.537233] Code: 98 a8 00 00 00 89 54 24 10 89 5c 24 14 8b 40 5c 89
> > 4c 24 08 c7 04 24 48 87 ba c1 89 44 24 0c 8b 45 08 89 44 24 04 e8 b0 db
> > 11 00 <0f> 0b 8d b6 00 00 00 00 55 89 e5 83 ec 04 3e 8d 74 26 00 89 c1
> > [ 133.537233] EIP: [<c1779838>] skb_panic+0x68/0x70 SS:ESP
> > 0068:cc7d1be8
> >
>
> Ok I send now a correction for patch PATCH 1/2 for use "-ENOENT" as ret
> initialization. Can you test that, please? :-)

Sure, I will do that, will send status about that later.

>
> >
> > Just wondering about how it works now. If the device does not have
> > 6lowpan_nhc module loaded, then UDP compressed packets are dropped. This
> > feels wrong and is a kind of regression to the previous way of working,
> > at least we need to auto-load the compression module.
>
> It works now in this way (example UDP, RAW UDP is a normal 8 byte UDP header):
>
> If no 6lowpan_nhc_udp is loaded:
>
> sending side:
>
> We don't set the NHC bit in 6LoWPAN header, we putting the RAW UDP header.
>
> receiving side:
>
> If we get a 6LoWPAN header which set the NHC bit:
> We print a warning and drop the packet, because we don't know the NHC ID.
> "ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."
> Maybe this can look a little bit better. :-)
>
> If we get a 6LoWPAN header which does not set the NHC bit and contains
> the raw UDP header then this will go into IPv6 layer.
>
>
>
>
> If 6lowpan_nhc_udp is loaded:
>
> sending side:
> If we get nexthdr == UDP then automatically we do a udp compression
> algorithm for RFC6282.
>
> receiving side:
>
> We can now accept boths methods, NHC bit is set and UDP compression
> (rfc6282) then magic 6lowpan_nhc_udp module function do an
> uncompression.
>
> If NHC bit isn't set we accept also a RAW udp without compression.
>
> >
> > How the system works if there would be another way to compress UDP
> > packets. User would need to know somehow what kind of packets it is
> > suppose to send/receive and load corresponding module. I wonder if this
> > kind of knowledge is really possible in practice. Should we always
> > support the NHC defined in https://tools.ietf.org/html/rfc6282 and then
> > have a way to load correct compression "plugin" if we get a packet with
> > different compression scheme.
> >
> > If the system does not support https://tools.ietf.org/html/rfc6775 (like
> > at the moment) there is no way to figure out what compression system the
> > other end is using/supporting. In this respect, it would be nice if the
> > current way of working would be automatically supported and user would
> > not need to remember to select 6lowpan_nhc option when building the
> > kernel.
> >
>
> Yes, we should support automatically and not by user if he loads the
> corresponding module.
>
> For configuration the compression methods I would expect some kind of
> netlink interface. On the userspace side we need some kind of tool to
> make it configurable. What do you think here?

Yes, that sounds good.

>
> Supporting a new userspace tool will make a high working load and we
> need some kind of new 6lowpan repo for releasing such tool. I _could_ try
> to implement such netlink interface for nhc into net/6lowpan and greating
> a userspace tool. Don't know if we do this effort and making a 6lowpan_udp
> userspace tool for configuration, but please make first such framework
> mainline and the netlink interface can we add later. :-)
>
>
> btw: which part of rfc6775 describes how to detect the compression
> methods on the other end (using/supporting)?

My bad, I remembered incorrectly, there was nothing like that mentioned
in rfc6775.

>
> > I like the way to support different compression algorithms, it just
> > needs some fine tuning.
> >
>
> Yea, this should mainly _first_ bring any framework for next header
> compression mainline. I didn't program such framework in my life and
> don't know what 100% we need. For adding new compression methods I think
> this framework will grow automatically by history of hacking new
> compression methods and I am sure lot of things can be do better than
> right now. At the moment it's better than the current behaviour that we
> don't have such framework for adding new compression methods.
>
> - Alex

I forgot to mention earlier that I think we need to support the case
where some clients are using different compression method than the other
(at the same time). What do you think?


Cheers,
Jukka

2014-12-01 15:01:35

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 0/2] 6lowpan: introduce nhc framework

Hi Jukka,

On Mon, Dec 01, 2014 at 02:33:03PM +0200, Jukka Rissanen wrote:
> Hi Alex,
>
> On la, 2014-11-29 at 22:14 +0100, Alexander Aring wrote:
> > This patch series introduce the next header compression framework. Currently
> > we support udp compression/uncompression only. This framework allow to add new
> > next header compression formats easily.
> >
> > If somebody wants to add a new header compression format and some information
> > are missing while calling compression and uncompression callbacks. Please
> > feel free to make framework changes according these callbacks.
> >
> > Note:
> >
> > If building 6lowpan_udp as module please make sure it's loaded before running
> > rfc6282 udp connection. If it's not loaded we send 6lowpan with raw udp header
> > and next header compression bit isn't set. Nevertheless if you use rfc6282
> > connection and 6lowpan_udp isn't loaded following will be printed:
> >
> > "ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."
> >
> > changes since v2:
> > - make udp nhc as module as suggested by Marcel Holtmann
> > - fix comment header in nhc_udp.c
> >
> > I didn't make the lowpan_nhc declaration "const" because this will occur
> > issues with rb_node, id and idmask array. Which will manipulated during
> > runtime.
> >
>
>
> Tested it between two BT 6lowpan connections. If both ends load the
> 6lowpan_udp module, then no issues. If only the other end has loaded the
> module, the other end (receiving end) crashed.
>
>
> [ 132.417733] (NULL net_device): received nhc which is not supported.
> Dropping.
> [ 132.722432] skbuff: skb_under_panic: text:d19cb60c len:55 put:40
> head:cd923000 data:cd922fec tail:0xcd923023 end:0xcd923740 dev:<NULL>
> [ 133.536256] ------------[ cut here ]------------
> [ 133.537233] kernel BUG at 3.16+gitAUTOINC
> +f6af675ef5-r215/linux/net/core/skbuff.c:100!
> [ 133.537233] invalid opcode: 0000 [#1] PREEMPT SMP
> [ 133.537233] Modules linked in: bluetooth_6lowpan 6lowpan nfc ecb
> btusb bluetooth rfkill snd_intel8x0 ohci_pci snd_ac97_codec ac97_bus
> parport_pc parport uvesafb
> [ 133.537233] CPU: 0 PID: 327 Comm: kworker/u3:0 Not tainted
> 3.17.0-bt6lowpan #1
> [ 133.537233] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
> VirtualBox 12/01/2006
> [ 133.537233] Workqueue: hci0 hci_rx_work [bluetooth]
> [ 133.537233] task: c0170000 ti: cc7d0000 task.ti: cc7d0000
> [ 133.537233] EIP: 0060:[<c1779838>] EFLAGS: 00010292 CPU: 0
> [ 133.537233] EIP is at skb_panic+0x68/0x70
> [ 133.537233] EAX: 0000007a EBX: cd923000 ECX: 00000000 EDX: 00000001
> [ 133.537233] ESI: c1b2a11b EDI: 00000004 EBP: cc7d1c18 ESP: cc7d1be8
> [ 133.537233] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
> [ 133.537233] CR0: 8005003b CR2: b76d9000 CR3: 0e1e5000 CR4: 000006d0
> [ 133.537233] Stack:
> [ 133.537233] c1ba8748 c1982e68 d19cb60c 00000037 00000028 cd923000
> cd922fec cd923023
> [ 133.537233] cd923740 c1b2a11b cd914300 00000003 cc7d1c24 c1779881
> c1982e68 cc7d1c94
> [ 133.537233] d19cb60c c0304c48 00000003 00000008 cc7d1c40 00000003
> 017d1c64 00000003
> [ 133.537233] Call Trace:
> [ 133.537233] [<d19cb60c>] ? lowpan_header_decompress+0x26c/0x980
> [6lowpan]
> [ 133.537233] [<c1779881>] skb_push+0x41/0x50
> [ 133.537233] [<d19cb60c>] lowpan_header_decompress+0x26c/0x980
> [6lowpan]
> [ 133.537233] [<d19daeb4>] chan_recv_cb+0x244/0x3f0
> [bluetooth_6lowpan]
> [ 133.537233] [<d1738711>] l2cap_data_channel+0x8e1/0x980 [bluetooth]
> [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> [ 133.537233] [<d173aa8f>] l2cap_recv_frame+0xbf/0x2ee0 [bluetooth]
> [ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
> [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> [ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
> [ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
> [ 133.537233] [<c107770f>] ? sched_clock_cpu+0x10f/0x160
> [ 133.537233] [<c10729cb>] ? get_parent_ip+0xb/0x40
> [ 133.537233] [<c1072a4b>] ? preempt_count_add+0x4b/0xa0
> [ 133.537233] [<c13e0112>] ? debug_smp_processor_id+0x12/0x20
> [ 133.537233] [<c108e584>] ? get_lock_stats+0x24/0x40
> [ 133.537233] [<c1090a34>] ? mark_held_locks+0x64/0x90
> [ 133.537233] [<c189dbfd>] ? __mutex_unlock_slowpath+0xcd/0x1b0
> [ 133.537233] [<c13e012f>] ? __this_cpu_preempt_check+0xf/0x20
> [ 133.537233] [<c1090b54>] ? trace_hardirqs_on_caller+0xf4/0x240
> [ 133.537233] [<c108e326>] ? trace_hardirqs_off_caller+0xb6/0x160
> [ 133.537233] [<d173ee19>] l2cap_recv_acldata+0x2f9/0x340 [bluetooth]
> [ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
> [ 133.537233] [<d1709ce9>] hci_rx_work+0x369/0x4a0 [bluetooth]
> [ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
> [ 133.537233] [<c1063203>] process_one_work+0x1b3/0x4e0
> [ 133.537233] [<c1063163>] ? process_one_work+0x113/0x4e0
> [ 133.537233] [<c10638c9>] worker_thread+0x39/0x440
> [ 133.537233] [<c1063890>] ? init_pwq+0xc0/0xc0
> [ 133.537233] [<c1067ce8>] kthread+0xa8/0xc0
> [ 133.537233] [<c1090cab>] ? trace_hardirqs_on+0xb/0x10
> [ 133.537233] [<c18a1741>] ret_from_kernel_thread+0x21/0x30
> [ 133.537233] [<c1067c40>] ? kthread_create_on_node+0x160/0x160
> [ 133.537233] Code: 98 a8 00 00 00 89 54 24 10 89 5c 24 14 8b 40 5c 89
> 4c 24 08 c7 04 24 48 87 ba c1 89 44 24 0c 8b 45 08 89 44 24 04 e8 b0 db
> 11 00 <0f> 0b 8d b6 00 00 00 00 55 89 e5 83 ec 04 3e 8d 74 26 00 89 c1
> [ 133.537233] EIP: [<c1779838>] skb_panic+0x68/0x70 SS:ESP
> 0068:cc7d1be8
>

Ok I send now a correction for patch PATCH 1/2 for use "-ENOENT" as ret
initialization. Can you test that, please? :-)

>
> Just wondering about how it works now. If the device does not have
> 6lowpan_nhc module loaded, then UDP compressed packets are dropped. This
> feels wrong and is a kind of regression to the previous way of working,
> at least we need to auto-load the compression module.

It works now in this way (example UDP, RAW UDP is a normal 8 byte UDP header):

If no 6lowpan_nhc_udp is loaded:

sending side:

We don't set the NHC bit in 6LoWPAN header, we putting the RAW UDP header.

receiving side:

If we get a 6LoWPAN header which set the NHC bit:
We print a warning and drop the packet, because we don't know the NHC ID.
"ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."
Maybe this can look a little bit better. :-)

If we get a 6LoWPAN header which does not set the NHC bit and contains
the raw UDP header then this will go into IPv6 layer.




If 6lowpan_nhc_udp is loaded:

sending side:
If we get nexthdr == UDP then automatically we do a udp compression
algorithm for RFC6282.

receiving side:

We can now accept boths methods, NHC bit is set and UDP compression
(rfc6282) then magic 6lowpan_nhc_udp module function do an
uncompression.

If NHC bit isn't set we accept also a RAW udp without compression.

>
> How the system works if there would be another way to compress UDP
> packets. User would need to know somehow what kind of packets it is
> suppose to send/receive and load corresponding module. I wonder if this
> kind of knowledge is really possible in practice. Should we always
> support the NHC defined in https://tools.ietf.org/html/rfc6282 and then
> have a way to load correct compression "plugin" if we get a packet with
> different compression scheme.
>
> If the system does not support https://tools.ietf.org/html/rfc6775 (like
> at the moment) there is no way to figure out what compression system the
> other end is using/supporting. In this respect, it would be nice if the
> current way of working would be automatically supported and user would
> not need to remember to select 6lowpan_nhc option when building the
> kernel.
>

Yes, we should support automatically and not by user if he loads the
corresponding module.

For configuration the compression methods I would expect some kind of
netlink interface. On the userspace side we need some kind of tool to
make it configurable. What do you think here?

Supporting a new userspace tool will make a high working load and we
need some kind of new 6lowpan repo for releasing such tool. I _could_ try
to implement such netlink interface for nhc into net/6lowpan and greating
a userspace tool. Don't know if we do this effort and making a 6lowpan_udp
userspace tool for configuration, but please make first such framework
mainline and the netlink interface can we add later. :-)


btw: which part of rfc6775 describes how to detect the compression
methods on the other end (using/supporting)?

> I like the way to support different compression algorithms, it just
> needs some fine tuning.
>

Yea, this should mainly _first_ bring any framework for next header
compression mainline. I didn't program such framework in my life and
don't know what 100% we need. For adding new compression methods I think
this framework will grow automatically by history of hacking new
compression methods and I am sure lot of things can be do better than
right now. At the moment it's better than the current behaviour that we
don't have such framework for adding new compression methods.

- Alex

2014-12-01 14:34:45

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 1/2] 6lowpan: add generic nhc layer interface

On Sat, Nov 29, 2014 at 10:14:42PM +0100, Alexander Aring wrote:
> This patch adds a generic next header compression layer interface. There
> exists various methods to do a header compression after 6LoWPAN header
> to save payload. This introduce a generic nhc header which allow a
> simple adding of a new header compression format instead of a static
> implementation inside the 6LoWPAN header compression and uncompression
> function.
>
> Signed-off-by: Alexander Aring <[email protected]>
> Cc: Jukka Rissanen <[email protected]>
> Cc: Martin Townsend <[email protected]>
> ---
> net/6lowpan/Makefile | 2 +-
> net/6lowpan/nhc.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++
> net/6lowpan/nhc.h | 130 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 306 insertions(+), 1 deletion(-)
> create mode 100644 net/6lowpan/nhc.c
> create mode 100644 net/6lowpan/nhc.h
>
> diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
> index 415886b..4215602 100644
> --- a/net/6lowpan/Makefile
> +++ b/net/6lowpan/Makefile
> @@ -1,3 +1,3 @@
> obj-$(CONFIG_6LOWPAN) := 6lowpan.o
>
> -6lowpan-y := iphc.o
> +6lowpan-y := iphc.o nhc.o
> diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
> new file mode 100644
> index 0000000..f64b244
> --- /dev/null
> +++ b/net/6lowpan/nhc.c
> @@ -0,0 +1,175 @@
> +/*
> + * 6LoWPAN next header compression
> + *
> + *
> + * Authors:
> + * Alexander Aring <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/netdevice.h>
> +
> +#include <net/ipv6.h>
> +
> +#include "nhc.h"
> +
> +static struct rb_root rb_root = RB_ROOT;
> +static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX];
> +
> +static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
> +{
> + struct rb_node **new = &rb_root.rb_node, *parent = NULL;
> +
> + /* Figure out where to put new node */
> + while (*new) {
> + struct lowpan_nhc *this = container_of(*new, struct lowpan_nhc,
> + node);
> + int result, len_dif, len;
> +
> + len_dif = nhc->idlen - this->idlen;
> +
> + if (nhc->idlen < this->idlen)
> + len = nhc->idlen;
> + else
> + len = this->idlen;
> +
> + result = memcmp(nhc->id, this->id, len);
> + if (!result)
> + result = len_dif;
> +
> + parent = *new;
> + if (result < 0)
> + new = &((*new)->rb_left);
> + else if (result > 0)
> + new = &((*new)->rb_right);
> + else
> + return -EEXIST;
> + }
> +
> + /* Add new node and rebalance tree. */
> + rb_link_node(&nhc->node, parent, new);
> + rb_insert_color(&nhc->node, &rb_root);
> +
> + return 0;
> +}
> +
> +static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
> +{
> + rb_erase(&nhc->node, &rb_root);
> +}
> +
> +struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
> +{
> + struct rb_node *node = rb_root.rb_node;
> + const u8 *nhcid_skb_ptr = skb->data;
> +
> + while (node) {
> + struct lowpan_nhc *nhc = container_of(node, struct lowpan_nhc,
> + node);
> + u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
> + int result, i;
> +
> + if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
> + return NULL;
> +
> + /* copy and mask afterwards the nhid value from skb */
> + memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
> + for (i = 0; i < nhc->idlen; i++)
> + nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
> +
> + result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
> + if (result < 0)
> + node = node->rb_left;
> + else if (result > 0)
> + node = node->rb_right;
> + else
> + return nhc;
> + }
> +
> + return NULL;
> +}
> +
> +struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr)
> +{
> + return lowpan_nexthdr_nhcs[nexthdr];
> +}
> +
> +int lowpan_nhc_do_compression(struct lowpan_nhc *nhc, struct sk_buff *skb,
> + u8 **hc_ptr, u8 *iphc0)
> +{
> + int ret;
> +
> + if (!nhc)
> + return 0;
> +
> + ret = nhc->compress(skb, hc_ptr);
> + if (ret == 0)
> + *iphc0 |= LOWPAN_IPHC_NH_C;
> +
> + return ret;
> +}
> +
> +int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct ipv6hdr *hdr)
> +{
> + struct lowpan_nhc *nhc;
> + /* default dropping if nothing found */
> + int ret = 0;
> +

this should be ret = -ENOENT;

To avoid sending garbage to next higher layer. I changed this is here
from -EINVAL to 0. Sorry some mistake is happend here, now it's real
dropped afterwards.

> + nhc = lowpan_nhc_by_nhcid(skb);
> + if (nhc) {
> + ret = nhc->uncompress(skb, sizeof(*hdr) + nhc->nexthdrlen);
> + if (ret == 0) {
> + hdr->nexthdr = nhc->nexthdr;
> + skb_reset_transport_header(skb);
> + raw_dump_table(__func__, "raw transport header dump",
> + skb_transport_header(skb),
> + nhc->nexthdrlen);
> + } else if (ret == -ENOTSUPP) {
> + netdev_warn(skb->dev, "received %s which is not supported for uncompression.\n",
> + nhc->name);
> + }
> + } else {
> + netdev_warn(skb->dev, "received nhc which is not supported. Dropping.\n");
> + }
> +
> + return ret;
> +}
> +

- Alex

2014-12-01 12:33:03

by Jukka Rissanen

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 0/2] 6lowpan: introduce nhc framework

Hi Alex,

On la, 2014-11-29 at 22:14 +0100, Alexander Aring wrote:
> This patch series introduce the next header compression framework. Currently
> we support udp compression/uncompression only. This framework allow to add new
> next header compression formats easily.
>
> If somebody wants to add a new header compression format and some information
> are missing while calling compression and uncompression callbacks. Please
> feel free to make framework changes according these callbacks.
>
> Note:
>
> If building 6lowpan_udp as module please make sure it's loaded before running
> rfc6282 udp connection. If it's not loaded we send 6lowpan with raw udp header
> and next header compression bit isn't set. Nevertheless if you use rfc6282
> connection and 6lowpan_udp isn't loaded following will be printed:
>
> "ieee802154 wpan-phy0 wpan0: received nhc which is not supported. Dropping."
>
> changes since v2:
> - make udp nhc as module as suggested by Marcel Holtmann
> - fix comment header in nhc_udp.c
>
> I didn't make the lowpan_nhc declaration "const" because this will occur
> issues with rb_node, id and idmask array. Which will manipulated during
> runtime.
>


Tested it between two BT 6lowpan connections. If both ends load the
6lowpan_udp module, then no issues. If only the other end has loaded the
module, the other end (receiving end) crashed.


[ 132.417733] (NULL net_device): received nhc which is not supported.
Dropping.
[ 132.722432] skbuff: skb_under_panic: text:d19cb60c len:55 put:40
head:cd923000 data:cd922fec tail:0xcd923023 end:0xcd923740 dev:<NULL>
[ 133.536256] ------------[ cut here ]------------
[ 133.537233] kernel BUG at 3.16+gitAUTOINC
+f6af675ef5-r215/linux/net/core/skbuff.c:100!
[ 133.537233] invalid opcode: 0000 [#1] PREEMPT SMP
[ 133.537233] Modules linked in: bluetooth_6lowpan 6lowpan nfc ecb
btusb bluetooth rfkill snd_intel8x0 ohci_pci snd_ac97_codec ac97_bus
parport_pc parport uvesafb
[ 133.537233] CPU: 0 PID: 327 Comm: kworker/u3:0 Not tainted
3.17.0-bt6lowpan #1
[ 133.537233] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
VirtualBox 12/01/2006
[ 133.537233] Workqueue: hci0 hci_rx_work [bluetooth]
[ 133.537233] task: c0170000 ti: cc7d0000 task.ti: cc7d0000
[ 133.537233] EIP: 0060:[<c1779838>] EFLAGS: 00010292 CPU: 0
[ 133.537233] EIP is at skb_panic+0x68/0x70
[ 133.537233] EAX: 0000007a EBX: cd923000 ECX: 00000000 EDX: 00000001
[ 133.537233] ESI: c1b2a11b EDI: 00000004 EBP: cc7d1c18 ESP: cc7d1be8
[ 133.537233] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
[ 133.537233] CR0: 8005003b CR2: b76d9000 CR3: 0e1e5000 CR4: 000006d0
[ 133.537233] Stack:
[ 133.537233] c1ba8748 c1982e68 d19cb60c 00000037 00000028 cd923000
cd922fec cd923023
[ 133.537233] cd923740 c1b2a11b cd914300 00000003 cc7d1c24 c1779881
c1982e68 cc7d1c94
[ 133.537233] d19cb60c c0304c48 00000003 00000008 cc7d1c40 00000003
017d1c64 00000003
[ 133.537233] Call Trace:
[ 133.537233] [<d19cb60c>] ? lowpan_header_decompress+0x26c/0x980
[6lowpan]
[ 133.537233] [<c1779881>] skb_push+0x41/0x50
[ 133.537233] [<d19cb60c>] lowpan_header_decompress+0x26c/0x980
[6lowpan]
[ 133.537233] [<d19daeb4>] chan_recv_cb+0x244/0x3f0
[bluetooth_6lowpan]
[ 133.537233] [<d1738711>] l2cap_data_channel+0x8e1/0x980 [bluetooth]
[ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
[ 133.537233] [<d173aa8f>] l2cap_recv_frame+0xbf/0x2ee0 [bluetooth]
[ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
[ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
[ 133.537233] [<c100a7a8>] ? sched_clock+0x8/0x10
[ 133.537233] [<c10772d9>] ? sched_clock_local+0x49/0x180
[ 133.537233] [<c107770f>] ? sched_clock_cpu+0x10f/0x160
[ 133.537233] [<c10729cb>] ? get_parent_ip+0xb/0x40
[ 133.537233] [<c1072a4b>] ? preempt_count_add+0x4b/0xa0
[ 133.537233] [<c13e0112>] ? debug_smp_processor_id+0x12/0x20
[ 133.537233] [<c108e584>] ? get_lock_stats+0x24/0x40
[ 133.537233] [<c1090a34>] ? mark_held_locks+0x64/0x90
[ 133.537233] [<c189dbfd>] ? __mutex_unlock_slowpath+0xcd/0x1b0
[ 133.537233] [<c13e012f>] ? __this_cpu_preempt_check+0xf/0x20
[ 133.537233] [<c1090b54>] ? trace_hardirqs_on_caller+0xf4/0x240
[ 133.537233] [<c108e326>] ? trace_hardirqs_off_caller+0xb6/0x160
[ 133.537233] [<d173ee19>] l2cap_recv_acldata+0x2f9/0x340 [bluetooth]
[ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
[ 133.537233] [<d1709ce9>] hci_rx_work+0x369/0x4a0 [bluetooth]
[ 133.537233] [<d1709a93>] ? hci_rx_work+0x113/0x4a0 [bluetooth]
[ 133.537233] [<c1063203>] process_one_work+0x1b3/0x4e0
[ 133.537233] [<c1063163>] ? process_one_work+0x113/0x4e0
[ 133.537233] [<c10638c9>] worker_thread+0x39/0x440
[ 133.537233] [<c1063890>] ? init_pwq+0xc0/0xc0
[ 133.537233] [<c1067ce8>] kthread+0xa8/0xc0
[ 133.537233] [<c1090cab>] ? trace_hardirqs_on+0xb/0x10
[ 133.537233] [<c18a1741>] ret_from_kernel_thread+0x21/0x30
[ 133.537233] [<c1067c40>] ? kthread_create_on_node+0x160/0x160
[ 133.537233] Code: 98 a8 00 00 00 89 54 24 10 89 5c 24 14 8b 40 5c 89
4c 24 08 c7 04 24 48 87 ba c1 89 44 24 0c 8b 45 08 89 44 24 04 e8 b0 db
11 00 <0f> 0b 8d b6 00 00 00 00 55 89 e5 83 ec 04 3e 8d 74 26 00 89 c1
[ 133.537233] EIP: [<c1779838>] skb_panic+0x68/0x70 SS:ESP
0068:cc7d1be8


Just wondering about how it works now. If the device does not have
6lowpan_nhc module loaded, then UDP compressed packets are dropped. This
feels wrong and is a kind of regression to the previous way of working,
at least we need to auto-load the compression module.

How the system works if there would be another way to compress UDP
packets. User would need to know somehow what kind of packets it is
suppose to send/receive and load corresponding module. I wonder if this
kind of knowledge is really possible in practice. Should we always
support the NHC defined in https://tools.ietf.org/html/rfc6282 and then
have a way to load correct compression "plugin" if we get a packet with
different compression scheme.

If the system does not support https://tools.ietf.org/html/rfc6775 (like
at the moment) there is no way to figure out what compression system the
other end is using/supporting. In this respect, it would be nice if the
current way of working would be automatically supported and user would
not need to remember to select 6lowpan_nhc option when building the
kernel.

I like the way to support different compression algorithms, it just
needs some fine tuning.


Cheers,
Jukka