2016-01-10 20:50:23

by Alexander Aring

[permalink] [raw]
Subject: [PATCH bluetooth-next 0/2] 6lowpan: stateful compression support

Hi,

this contains at first some small fix for the netdev notifier which I deteced
while hacking 6lowpan stateful compression support.

I rework the debugfs entry for the stateful compression. Now each context entry
for a 6LoWPAN contains has his own subdirectory (named as his CID value).
In this subdirectories there exists the following files for manipulating the
context entry:

- active: If the context belongs to the table or not.
- compression: the compression flag according rfc6775.
- prefix: ipv6 prefix in xxxx:xxxx:....:xxxx form.
- prefix_lenght: the length of the ipv6 prefix.

This offers a setting per property instead doing remember the full context and
doing echo "$FULL_CONTEXT_INFORMATION" > ctx_table.

These file should be prepared to move into the sysfs directory when it seems to
running fine.

There exist one file "show" inside $LOWPAN_IFACE/contexts/. This file is for
a "pretty-printout" for the current context table. I use the same format-string
like RIOT-OS, example:

cid|prefix |C
-------------------------------------------------
0| 2001::/64 |1
1| 2001::1/128|1
2| 2001::5/128|1
7| 2002::/64 |1


difference here is that RIOT shows the lifetime, but I currently see no sense to
handle the lifetime in kernelspace.

Also I added support to clean the context table when doing a "ifdown". The reason
is that IPv6 interface will also drop all address information while doing a
ifdown and a 6LoWPAN should not use previous context information after ifup again.

- Alex

Alexander Aring (2):
6lowpan: iphc: add support for stateful compression
ieee802154: 6lowpan: fix return of netdev notifier

include/net/6lowpan.h | 31 ++++
net/6lowpan/core.c | 39 +++-
net/6lowpan/debugfs.c | 247 +++++++++++++++++++++++++
net/6lowpan/iphc.c | 420 ++++++++++++++++++++++++++++++++++++------
net/ieee802154/6lowpan/core.c | 7 +-
5 files changed, 682 insertions(+), 62 deletions(-)

--
2.6.1



2016-01-19 09:12:28

by Lukasz Duda

[permalink] [raw]
Subject: RE: [PATCH bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

Hi Alex,

> This patch introduce support for IPHC stateful address compression. It will
> offer the context table via one debugfs entry.
> This debugfs has and directory for each cid entry for the context table.
> Inside each cid directory there exists the following files:
>
> - "active": If the entry is added or deleted. The context table is
> original a list implementation, this flag will indicate if the
> context is part of list or not.
> - "prefix": The ipv6 prefix.
> - "prefix_length": The prefix length for the prefix.
> - "compression": The compression flag according RFC6775.
>
> This part should be moved into sysfs after some testing time.
>
> Also the debugfs entry contains a "show" file which is a pretty-printout for the
> current context table information.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 31 ++++
> net/6lowpan/core.c | 39 ++++-
> net/6lowpan/debugfs.c | 247 +++++++++++++++++++++++++++++
> net/6lowpan/iphc.c | 420
> +++++++++++++++++++++++++++++++++++++++++++-------
> 4 files changed, 679 insertions(+), 58 deletions(-)
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index
> 2f6a3f2..e1e2e61 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -75,6 +75,8 @@
> #define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) +
> \
> LOWPAN_IPHC_MAX_HEADER_LEN +
> \
> LOWPAN_NHC_MAX_HDR_LEN)
> +/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
> +#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)
>
> #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
> #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
> @@ -98,9 +100,38 @@ enum lowpan_lltypes {
> LOWPAN_LLTYPE_IEEE802154,
> };
>
> +enum lowpan_iphc_ctx_flags {
> + LOWPAN_IPHC_CTX_FLAG_ACTIVE,
> + LOWPAN_IPHC_CTX_FLAG_C,
> +};
> +
> +struct lowpan_iphc_ctx {
> + u8 id;
> + struct in6_addr pfx;
> + u8 plen;
> + unsigned long flags;
> +};
> +
> +struct lowpan_iphc_ctx_table {
> + spinlock_t lock;
> + const struct lowpan_iphc_ctx_ops *ops;
> + struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
> +};
> +
> +static inline bool lowpan_iphc_ctx_is_active(const struct
> +lowpan_iphc_ctx *ctx) {
> + return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); }
> +
> +static inline bool lowpan_iphc_ctx_is_c(const struct lowpan_iphc_ctx
> +*ctx) {
> + return test_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags); }
> +
> struct lowpan_priv {
> enum lowpan_lltypes lltype;
> struct dentry *iface_debugfs;
> + struct lowpan_iphc_ctx_table ctx;
>
> /* must be last */
> u8 priv[0] __aligned(sizeof(void *));
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index faf65ba..34e44c0
> 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -20,7 +20,7 @@
> int lowpan_register_netdevice(struct net_device *dev,
> enum lowpan_lltypes lltype)
> {
> - int ret;
> + int i, ret;
>
> dev->addr_len = EUI64_ADDR_LEN;
> dev->type = ARPHRD_6LOWPAN;
> @@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device
> *dev,
>
> lowpan_priv(dev)->lltype = lltype;
>
> + spin_lock_init(&lowpan_priv(dev)->ctx.lock);
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> + lowpan_priv(dev)->ctx.table[i].id = i;
> +
> ret = register_netdevice(dev);
> if (ret < 0)
> return ret;
> @@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device
> *dev) } EXPORT_SYMBOL(lowpan_unregister_netdev);
>
> +static int lowpan_event(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
> + int i;
> +
> + if (dev->type != ARPHRD_6LOWPAN)
> + return NOTIFY_DONE;
> +
> + switch (event) {
> + case NETDEV_DOWN:
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
> + &lowpan_priv(dev)->ctx.table[i].flags);
> + break;
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block lowpan_notifier = {
> + .notifier_call = lowpan_event,
> +};
> +
> static int __init lowpan_module_init(void) {
> int ret;
> @@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
> if (ret < 0)
> return ret;
>
> + ret = register_netdevice_notifier(&lowpan_notifier);
> + if (ret < 0) {
> + lowpan_debugfs_exit();
> + return ret;
> + }
> +
> request_module_nowait("ipv6");
>
> request_module_nowait("nhc_dest");
> @@ -92,6 +128,7 @@ static int __init lowpan_module_init(void) static void
> __exit lowpan_module_exit(void) {
> lowpan_debugfs_exit();
> + unregister_netdevice_notifier(&lowpan_notifier);
> }
>
> module_init(lowpan_module_init);
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c index
> 88eef84..0a95848 100644
> --- a/net/6lowpan/debugfs.c
> +++ b/net/6lowpan/debugfs.c
> @@ -16,19 +16,266 @@
>
> #include "6lowpan_i.h"
>
> +#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
> +
> static struct dentry *lowpan_debugfs;
>
> +static int lowpan_ctx_flag_active_set(void *data, u64 val) {
> + struct lowpan_iphc_ctx *ctx = data;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + if (val)
> + set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
> + else
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_flag_active_get(void *data, u64 *val) {
> + *val = !!lowpan_iphc_ctx_is_active(data);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
> + lowpan_ctx_flag_active_get,
> + lowpan_ctx_flag_active_set, "%llu\n");
> +
> +static int lowpan_ctx_flag_c_set(void *data, u64 val) {
> + struct lowpan_iphc_ctx *ctx = data;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + if (val)
> + set_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
> + else
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_flag_c_get(void *data, u64 *val) {
> + *val = !!lowpan_iphc_ctx_is_c(data);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops,
> lowpan_ctx_flag_c_get,
> + lowpan_ctx_flag_c_set, "%llu\n");
> +
> +static int lowpan_ctx_plen_set(void *data, u64 val) {
> + struct lowpan_iphc_ctx *ctx = data;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx-
> >id]);
> +
> + if (val > 128)
> + return -EINVAL;
> +
> + spin_lock_bh(&t->lock);
> + ctx->plen = val;
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_plen_get(void *data, u64 *val) {
> + struct lowpan_iphc_ctx *ctx = data;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx-
> >id]);
> +
> + spin_lock_bh(&t->lock);
> + *val = ctx->plen;
> + spin_unlock_bh(&t->lock);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
> + lowpan_ctx_plen_set, "%llu\n");
> +
> +static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset) {
> + struct lowpan_iphc_ctx *ctx = file->private;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx-
> >id]);
> +
> + spin_lock_bh(&t->lock);
> + seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
> + be16_to_cpu(ctx->pfx.s6_addr16[0]),
> + be16_to_cpu(ctx->pfx.s6_addr16[1]),
> + be16_to_cpu(ctx->pfx.s6_addr16[2]),
> + be16_to_cpu(ctx->pfx.s6_addr16[3]),
> + be16_to_cpu(ctx->pfx.s6_addr16[4]),
> + be16_to_cpu(ctx->pfx.s6_addr16[5]),
> + be16_to_cpu(ctx->pfx.s6_addr16[6]),
> + be16_to_cpu(ctx->pfx.s6_addr16[7]));
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_ctx_pfx_show, inode->i_private); }
> +
> +static ssize_t lowpan_ctx_pfx_write(struct file *fp,
> + const char __user *user_buf, size_t count,
> + loff_t *ppos)
> +{
> + char buf[128] = {};
> + struct seq_file *file = fp->private_data;
> + struct lowpan_iphc_ctx *ctx = file->private;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx-
> >id]);
> + int status = count, n, i;
> + unsigned int addr[8];
> +
> + if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
> + count))) {
> + status = -EFAULT;
> + goto out;
> + }
> +
> + n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
> + &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
> + &addr[5], &addr[6], &addr[7]);
> + if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
> + status = -EINVAL;
> + goto out;
> + }
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < 8; i++)
> + ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
> + spin_unlock_bh(&t->lock);
> +
> +out:
> + return status;
> +}
> +
> +const struct file_operations lowpan_ctx_pfx_fops = {
> + .open = lowpan_ctx_pfx_open,
> + .read = seq_read,
> + .write = lowpan_ctx_pfx_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
> + struct dentry *ctx, u8 id)
> +{
> + struct lowpan_priv *lpriv = lowpan_priv(dev);
> + struct dentry *dentry, *root;
> + char buf[32];
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
> +
> + sprintf(buf, "%d", id);
> +
> + root = debugfs_create_dir(buf, ctx);
> + if (!root)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("active", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_flag_active_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("compression", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_flag_c_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("prefix", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_pfx_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("prefix_len", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_plen_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int lowpan_context_show(struct seq_file *file, void *offset) {
> + struct lowpan_iphc_ctx_table *t = file->private;
> + int i;
> +
> + seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
> + seq_puts(file, "-------------------------------------------------\n");
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + if (!lowpan_iphc_ctx_is_active(&t->table[i]))
> + continue;
> +
> + seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
> + &t->table[i].pfx, t->table[i].plen,
> + !!lowpan_iphc_ctx_is_active(&t->table[i]));

This function checks if context is active, however I assume that it should
have been lowpan_iphc_ctx_is_c(). Current code prints active state of the
entry, instead of compression state ('C'-column).

> + }
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_context_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_context_show, inode->i_private); }
> +
> +const struct file_operations lowpan_context_fops = {
> + .open = lowpan_context_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> int lowpan_dev_debugfs_init(struct net_device *dev) {
> struct lowpan_priv *lpriv = lowpan_priv(dev);
> + struct dentry *contexts, *dentry;
> + int ret, i;
>
> /* creating the root */
> lpriv->iface_debugfs = debugfs_create_dir(dev->name,
> lowpan_debugfs);
> if (!lpriv->iface_debugfs)
> goto fail;
>
> + contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
> + if (!contexts)
> + goto remove_root;
> +
> + dentry = debugfs_create_file("show", 0644, contexts,
> + &lowpan_priv(dev)->ctx,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
> + if (ret < 0)
> + goto remove_root;
> + }
> +
> return 0;
>
> +remove_root:
> + lowpan_dev_debugfs_exit(dev);
> fail:
> return -EINVAL;
> }
> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 346b5c1..aad7d77
> 100644
> --- a/net/6lowpan/iphc.c
> +++ b/net/6lowpan/iphc.c
> @@ -56,6 +56,7 @@
> /* special link-layer handling */
> #include <net/mac802154.h>
>
> +#include "6lowpan_i.h"
> #include "nhc.h"
>
> /* Values of fields within the IPHC encoding first byte */ @@ -147,6 +148,9
> @@
> (((a)->s6_addr16[6]) == 0) && \
> (((a)->s6_addr[14]) == 0))
>
> +#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
> +#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
> +
> static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
> const void *lladdr)
> {
> @@ -195,6 +199,104 @@ static inline void
> iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
> }
> }
>
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id) {
> + struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
> +
> + if (!lowpan_iphc_ctx_is_active(ret))
> + return NULL;
> +
> + /* Don't need to check the compression flag here. Context SHOULD
> be used
> + * in decompression case another compressor has not yet received
> the
> + * updated context information.
> + */
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_pfx;
> + u8 addr_plen;
> + int i;
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
> +
> + /* if prefix len < 64, the remaining bits until 64th bit is
> + * zero. Otherwise we use table[i]->plen.
> + */
> + if (table[i].plen < 64)
> + addr_plen = 64;
> + else
> + addr_plen = table[i].plen;
> +
> + if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
> + /* remember first match */
> + if (!ret) {
> + ret = &table[i];
> + continue;
> + }
> +
> + /* get the context with longest prefix len */
> + if (table[i].plen > ret->plen)
> + ret = &table[i];
> + }
> + }
> +
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_mcast, network_pfx = {};
> + int i;
> +
> + /* init mcast address with */
> + memcpy(&addr_mcast, addr, sizeof(*addr));
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + /* setting plen */
> + addr_mcast.s6_addr[3] = table[i].plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &table[i].pfx,
> + table[i].plen);
> + /* setting network prefix */
> + memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
> +
> + if (ipv6_addr_equal(addr, &addr_mcast)) {
> + ret = &table[i];
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> /* Uncompress address function for source and
> * destination address(non-multicast).
> *
> @@ -259,30 +361,59 @@ static int uncompress_addr(struct sk_buff *skb,
> const struct net_device *dev,
> /* Uncompress address function for source context
> * based address(non-multicast).
> */
> -static int uncompress_context_based_src_addr(struct sk_buff *skb,
> - struct in6_addr *ipaddr,
> - u8 address_mode)
> +static int uncompress_ctx_addr(struct sk_buff *skb,
> + const struct net_device *dev,
> + const struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr, u8 address_mode,
> + const void *lladdr)
> {
> + bool fail;
> +
> switch (address_mode) {
> - case LOWPAN_IPHC_SAM_00:
> - /* unspec address ::
> + /* SAM and DAM are the same here */
> + case LOWPAN_IPHC_DAM_00:
> + fail = false;
> + /* SAM_00 -> unspec address ::
> * Do nothing, address is already ::
> + *
> + * DAM 00 -> reserved should never occur.
> */
> break;
> case LOWPAN_IPHC_SAM_01:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_01:
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_10:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_10:
> + ipaddr->s6_addr[11] = 0xFF;
> + ipaddr->s6_addr[12] = 0xFE;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_11:
> - /* TODO */
> - netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
> - address_mode);
> - return -EINVAL;
> + case LOWPAN_IPHC_DAM_11:
> + fail = false;
> + switch (lowpan_priv(dev)->lltype) {
> + case LOWPAN_LLTYPE_IEEE802154:
> + iphc_uncompress_802154_lladdr(ipaddr, lladdr);
> + break;
> + default:
> + iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
> + break;
> + }
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> default:
> pr_debug("Invalid sam value: 0x%x\n", address_mode);
> return -EINVAL;
> }
>
> + if (fail) {
> + pr_debug("Failed to fetch skb data\n");
> + return -EIO;
> + }
> +
> raw_dump_inline(NULL,
> "Reconstructed context based ipv6 src addr is",
> ipaddr->s6_addr, 16);
> @@ -346,6 +477,30 @@ static int
> lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
> return 0;
> }
>
> +static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
> + struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr,
> + u8 address_mode)
> +{
> + struct in6_addr network_pfx = {};
> + bool fail;
> +
> + ipaddr->s6_addr[0] = 0xFF;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
> + fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
> + if (fail < 0)
> + return -EIO;
> +
> + /* take prefix_len and network prefix from the context */
> + ipaddr->s6_addr[3] = ctx->plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
> + /* setting network prefix */
> + memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
> +
> + return 0;
> +}
> +
> /* get the ecn values from iphc tf format and set it to ipv6hdr */ static inline
> void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf) { @@ -459,7
> +614,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct
> net_device *dev,
> const void *daddr, const void *saddr) {
> struct ipv6hdr hdr = {};
> - u8 iphc0, iphc1;
> + struct lowpan_iphc_ctx *ci;
> + u8 iphc0, iphc1, cid = 0;
> int err;
>
> raw_dump_table(__func__, "raw skb data dump uncompressed", @@
> -469,12 +625,14 @@ int lowpan_header_decompress(struct sk_buff *skb,
> const struct net_device *dev,
> lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
> return -EINVAL;
>
> - /* another if the CID flag is set */
> - if (iphc1 & LOWPAN_IPHC_CID)
> - return -ENOTSUPP;
> -
> hdr.version = 6;
>
> + /* default CID = 0, another if the CID flag is set */
> + if (iphc1 & LOWPAN_IPHC_CID) {
> + if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
> + return -EINVAL;
> + }
> +
> err = lowpan_iphc_tf_decompress(skb, &hdr,
> iphc0 & LOWPAN_IPHC_TF_MASK);
> if (err < 0)
> @@ -500,10 +658,17 @@ int lowpan_header_decompress(struct sk_buff
> *skb, const struct net_device *dev,
> }
>
> if (iphc1 & LOWPAN_IPHC_SAC) {
> - /* Source address context based uncompression */
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_SCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
> +
> pr_debug("SAC bit is set. Handle context based source
> address.\n");
> - err = uncompress_context_based_src_addr(skb, &hdr.saddr,
> - iphc1 &
> LOWPAN_IPHC_SAM_MASK);
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
> + iphc1 &
> LOWPAN_IPHC_SAM_MASK, saddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> } else {
> /* Source address uncompression */
> pr_debug("source address stateless compression\n"); @@ -
> 515,27 +680,52 @@ int lowpan_header_decompress(struct sk_buff *skb,
> const struct net_device *dev,
> if (err)
> return -EINVAL;
>
> - /* check for Multicast Compression */
> - if (iphc1 & LOWPAN_IPHC_M) {
> - if (iphc1 & LOWPAN_IPHC_DAC) {
> - pr_debug("dest: context-based mcast
> compression\n");
> - /* TODO: implement this */
> - } else {
> - err = lowpan_uncompress_multicast_daddr(skb,
> &hdr.daddr,
> - iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
> + case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
>
> - if (err)
> - return -EINVAL;
> + /* multicast with context */
> + pr_debug("dest: context-based mcast compression\n");
> + err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
> + &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + case LOWPAN_IPHC_M:
> + /* multicast */
> + err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + break;
> + case LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> }
> - } else {
> +
> + /* Destination address context based uncompression */
> + pr_debug("DAC bit is set. Handle context based destination
> address.\n");
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK, daddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + default:
> err = uncompress_addr(skb, dev, &hdr.daddr,
> iphc1 & LOWPAN_IPHC_DAM_MASK,
> daddr);
> pr_debug("dest: stateless compression mode %d dest
> %pI6c\n",
> iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
> - if (err)
> - return -EINVAL;
> + break;
> }
>
> + if (err)
> + return -EINVAL;
> +
> /* Next header data uncompression */
> if (iphc0 & LOWPAN_IPHC_NH) {
> err = lowpan_nhc_do_uncompression(skb, dev, &hdr); @@ -
> 585,6 +775,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
> [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11, };
>
> +static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr
> *ipaddr,
> + const struct lowpan_iphc_ctx *ctx,
> + const unsigned char *lladdr, bool sam) {
> + struct in6_addr tmp = {};
> + u8 dam;
> +
> + /* check for SAM/DAM = 11 */
> + memcpy(&tmp.s6_addr[8], lladdr, 8);
> + /* second bit-flip (Universe/Local) is done according RFC2464 */
> + tmp.s6_addr[8] ^= 0x02;
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + dam = LOWPAN_IPHC_DAM_11;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 01 */
> + tmp.s6_addr[11] = 0xFF;
> + tmp.s6_addr[12] = 0xFE;
> + memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
> + dam = LOWPAN_IPHC_DAM_10;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 10, should always match */
> + memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
> + dam = LOWPAN_IPHC_DAM_01;
> + goto out;
> + }
> +
> + WARN_ON_ONCE("context found but no address mode matched\n");
> + return -EINVAL;
> +out:
> +
> + if (sam)
> + return lowpan_iphc_dam_to_sam_value[dam];
> + else
> + return dam;
> +}
> +
> static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr
> *ipaddr,
> const unsigned char *lladdr, bool sam) {
> @@ -708,6 +950,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr,
> const struct ipv6hdr *hdr)
> return val;
> }
>
> +static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
> + const struct lowpan_iphc_ctx *ctx,
> + const struct in6_addr *ipaddr) {
> + u8 data[6];
> +
> + /* flags/scope, reserved (RIID) */
> + memcpy(data, &ipaddr->s6_addr[1], 2);
> + /* group ID */
> + memcpy(&data[1], &ipaddr->s6_addr[11], 4);
> + lowpan_push_hc_data(hc_ptr, data, 6);
> +
> + return LOWPAN_IPHC_DAM_00;
> +}
> +
> static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
> const struct in6_addr *ipaddr)
> {
> @@ -742,10 +999,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8
> **hc_ptr, int lowpan_header_compress(struct sk_buff *skb, const struct
> net_device *dev,
> const void *daddr, const void *saddr) {
> - u8 iphc0, iphc1, *hc_ptr;
> + u8 iphc0, iphc1, *hc_ptr, cid = 0;
> struct ipv6hdr *hdr;
> u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
> - int ret, addr_type;
> + struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
> + int ret, ipv6_daddr_type, ipv6_saddr_type;
>
> if (skb->protocol != htons(ETH_P_IPV6))
> return -EINVAL;
> @@ -769,14 +1027,38 @@ int lowpan_header_compress(struct sk_buff
> *skb, const struct net_device *dev,
> iphc0 = LOWPAN_DISPATCH_IPHC;
> iphc1 = 0;
>
> - /* TODO: context lookup */
> -
> raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
> raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
>
> raw_dump_table(__func__, "sending raw skb network uncompressed
> packet",
> skb->data, skb->len);
>
> + ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
> + dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr-
> >daddr);
> + else
> + dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
> + if (dci) {
> + memcpy(&dci_entry, dci, sizeof(*dci));
> + cid |= dci->id;
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
> + if (sci) {
> + memcpy(&sci_entry, sci, sizeof(*sci));
> + cid |= (sci->id << 4);
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + /* if cid is zero it will be compressed */
> + if (cid) {
> + iphc1 |= LOWPAN_IPHC_CID;
> + lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
> + }
> +
> /* Traffic Class, Flow Label compression */
> iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
>
> @@ -813,39 +1095,63 @@ int lowpan_header_compress(struct sk_buff
> *skb, const struct net_device *dev,
> sizeof(hdr->hop_limit));
> }
>
> - addr_type = ipv6_addr_type(&hdr->saddr);
> + ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
> /* source address compression */
> - if (addr_type == IPV6_ADDR_ANY) {
> + if (ipv6_saddr_type == IPV6_ADDR_ANY) {
> pr_debug("source address is unspecified, setting SAC\n");
> iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr-
> >saddr,
> - saddr, true);
> - pr_debug("source address unicast link-local %pI6c
> iphc1 0x%02x\n",
> - &hdr->saddr, iphc1);
> + if (sci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr-
> >saddr,
> + &sci_entry, saddr,
> + true);
> + iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - pr_debug("send the full source address\n");
> - lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr,
> 16);
> + if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |=
> lowpan_compress_addr_64(&hc_ptr,
> + &hdr->saddr,
> + saddr, true);
> + pr_debug("source address unicast link-local
> %pI6c iphc1 0x%02x\n",
> + &hdr->saddr, iphc1);
> + } else {
> + pr_debug("send the full source address\n");
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->saddr.s6_addr, 16);
> + }
> }
> }
>
> - addr_type = ipv6_addr_type(&hdr->daddr);
> /* destination address compression */
> - if (addr_type & IPV6_ADDR_MULTICAST) {
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
> pr_debug("destination address is multicast: ");
> - iphc1 |= LOWPAN_IPHC_M;
> - iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr-
> >daddr);
> + if (dci) {
> + iphc1 |=
> lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
> +
> &dci_entry,
> + &hdr-
> >daddr);
> + } else {
> + iphc1 |= LOWPAN_IPHC_M;
> + iphc1 |=
> lowpan_iphc_mcast_addr_compress(&hc_ptr,
> + &hdr-
> >daddr);
> + }
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - /* TODO: context lookup */
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr-
> >daddr,
> - daddr, false);
> - pr_debug("dest address unicast link-local %pI6c "
> - "iphc1 0x%02x\n", &hdr->daddr, iphc1);
> + if (dci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr-
> >daddr,
> + &dci_entry, daddr,
> + false);
> + iphc1 |= LOWPAN_IPHC_DAC;
> } else {
> - pr_debug("dest address unicast %pI6c\n", &hdr-
> >daddr);
> - lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr,
> 16);
> + if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |=
> lowpan_compress_addr_64(&hc_ptr,
> + &hdr->daddr,
> + daddr, false);
> + pr_debug("dest address unicast link-local
> %pI6c iphc1 0x%02x\n",
> + &hdr->daddr, iphc1);
> + } else {
> + pr_debug("dest address unicast %pI6c\n",
> + &hdr->daddr);
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->daddr.s6_addr, 16);
> + }
> }
> }
>
> --
> 2.6.1

Regards,
Lukasz

2016-01-19 09:05:59

by Lukasz Duda

[permalink] [raw]
Subject: RE: [PATCH bluetooth-next 0/2] 6lowpan: stateful compression support

Hi Alex,

> Hi,
>
> this contains at first some small fix for the netdev notifier which I deteced
> while hacking 6lowpan stateful compression support.
>
> I rework the debugfs entry for the stateful compression. Now each context
> entry for a 6LoWPAN contains has his own subdirectory (named as his CID
> value).
> In this subdirectories there exists the following files for manipulating the
> context entry:
>
> - active: If the context belongs to the table or not.
> - compression: the compression flag according rfc6775.
> - prefix: ipv6 prefix in xxxx:xxxx:....:xxxx form.
> - prefix_lenght: the length of the ipv6 prefix.
>
> This offers a setting per property instead doing remember the full context and
> doing echo "$FULL_CONTEXT_INFORMATION" > ctx_table.
>
> These file should be prepared to move into the sysfs directory when it seems
> to running fine.
>
> There exist one file "show" inside $LOWPAN_IFACE/contexts/. This file is for a
> "pretty-printout" for the current context table. I use the same format-string
> like RIOT-OS, example:
>
> cid|prefix |C
> -------------------------------------------------
> 0| 2001::/64 |1
> 1| 2001::1/128|1
> 2| 2001::5/128|1
> 7| 2002::/64 |1
>
>
> difference here is that RIOT shows the lifetime, but I currently see no sense to
> handle the lifetime in kernelspace.
>
> Also I added support to clean the context table when doing a "ifdown". The
> reason is that IPv6 interface will also drop all address information while doing
> a ifdown and a 6LoWPAN should not use previous context information after
> ifup again.
>
> - Alex
>
> Alexander Aring (2):
> 6lowpan: iphc: add support for stateful compression
> ieee802154: 6lowpan: fix return of netdev notifier
>
> include/net/6lowpan.h | 31 ++++
> net/6lowpan/core.c | 39 +++-
> net/6lowpan/debugfs.c | 247 +++++++++++++++++++++++++
> net/6lowpan/iphc.c | 420
> ++++++++++++++++++++++++++++++++++++------
> net/ieee802154/6lowpan/core.c | 7 +-
> 5 files changed, 682 insertions(+), 62 deletions(-)
>
> --
> 2.6.1

Thanks for the great work you have done!

I have tested the latest stateful compression module, and it works very
well! One note is that the "C" flag, in the debugfs entry "show", list
active state instead of compression state. However this is a minor and I
will highlight it in the PATCH 1/2 mail.

The context table was set up with 2 entries, using CID 0 and 15:

cid|prefix |C
-------------------------------------------------
0| 2001:db8::/64 |1
15| 2001:db8::1/128|1

Device setup:

Linux computer (bt0)
- Address: 2001:db8::1/64

Device A
- CID 0, compression ON
- CID 15, compression OFF
- Address: 2001:db8::MAC/64

Device B
- CID 0, compression ON
- CID 15, compression OFF
- Address: 2001:db8::MAC/64

Device C
- No CIDs
- Address: 2001:db8::MAC/64

Scenario 1:
ping6 <A> -s 0 -c 1
TX: Full compression of source and dest
RX: Full compression of source and dest

Scenario 2:
ping6 <B> -s 0 -c 1
TX: Full compression of source and dest
RX: Compression of source, half compression of dest (bt0)

Scenario 3:
- Disable CID
echo 0 > /sys/kernel/debug/6lowpan/bt0/contexts/0/active
echo 0 > /sys/kernel/debug/6lowpan/bt0/contexts/15/active

ping6 <C> -s 0 -c 1
TX: No compression
RX: No compression

Scenario 4:
- Adjust prefix length on CID 0 to prefix_len 32.
echo 32 > /sys/kernel/debug/6lowpan/bt0/contexts/0/prefix_len
echo 1 > /sys/kernel/debug/6lowpan/bt0/contexts/0/active
echo 1 > /sys/kernel/debug/6lowpan/bt0/contexts/15/active

ping6 <A> -s 0 -c 1
TX: Full compression of source and dest
RX: Full compression of source and dest

Regards,
Lukasz

2016-01-15 14:39:53

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCH bluetooth-next 2/2] ieee802154: 6lowpan: fix return of netdev notifier

Hello.

On 10/01/16 21:50, Alexander Aring wrote:
> This patch fixed the return value of netdev notifier. If the command is
> a don't care a NOTIFY_DONE should be returned. If the command matched a
> NOTIFY_OK should be returned.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> net/ieee802154/6lowpan/core.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
> index 737c87a..0023c90 100644
> --- a/net/ieee802154/6lowpan/core.c
> +++ b/net/ieee802154/6lowpan/core.c
> @@ -207,7 +207,7 @@ static int lowpan_device_event(struct notifier_block *unused,
> struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
>
> if (wdev->type != ARPHRD_IEEE802154)
> - goto out;
> + return NOTIFY_DONE;
>
> switch (event) {
> case NETDEV_UNREGISTER:
> @@ -219,11 +219,10 @@ static int lowpan_device_event(struct notifier_block *unused,
> lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
> break;
> default:
> - break;
> + return NOTIFY_DONE;
> }
>
> -out:
> - return NOTIFY_DONE;
> + return NOTIFY_OK;
> }
>
> static struct notifier_block lowpan_dev_notifier = {

Reviewed-by: Stefan Schmidt <[email protected]>

regards
Stefan Schmidt


2016-01-15 14:39:45

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCH bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

Hello.

On 10/01/16 21:50, Alexander Aring wrote:
> This patch introduce support for IPHC stateful address compression. It
> will offer the context table via one debugfs entry.
> This debugfs has and directory for each cid entry for the context table.
> Inside each cid directory there exists the following files:
>
> - "active": If the entry is added or deleted. The context table is
> original a list implementation, this flag will indicate if the
> context is part of list or not.
> - "prefix": The ipv6 prefix.
> - "prefix_length": The prefix length for the prefix.
> - "compression": The compression flag according RFC6775.
>
> This part should be moved into sysfs after some testing time.
>
> Also the debugfs entry contains a "show" file which is a pretty-printout
> for the current context table information.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 31 ++++
> net/6lowpan/core.c | 39 ++++-
> net/6lowpan/debugfs.c | 247 +++++++++++++++++++++++++++++
> net/6lowpan/iphc.c | 420 +++++++++++++++++++++++++++++++++++++++++++-------
> 4 files changed, 679 insertions(+), 58 deletions(-)
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index 2f6a3f2..e1e2e61 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -75,6 +75,8 @@
> #define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
> LOWPAN_IPHC_MAX_HEADER_LEN + \
> LOWPAN_NHC_MAX_HDR_LEN)
> +/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
> +#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)
>
> #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
> #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
> @@ -98,9 +100,38 @@ enum lowpan_lltypes {
> LOWPAN_LLTYPE_IEEE802154,
> };
>
> +enum lowpan_iphc_ctx_flags {
> + LOWPAN_IPHC_CTX_FLAG_ACTIVE,
> + LOWPAN_IPHC_CTX_FLAG_C,

the name FLAG_C is not really telling much FLAG_COMPRESSION?

> +};
> +
> +struct lowpan_iphc_ctx {
> + u8 id;
> + struct in6_addr pfx;
> + u8 plen;
> + unsigned long flags;
> +};
> +
> +struct lowpan_iphc_ctx_table {
> + spinlock_t lock;
> + const struct lowpan_iphc_ctx_ops *ops;
> + struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
> +};
> +
> +static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
> +{
> + return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
> +}
> +
> +static inline bool lowpan_iphc_ctx_is_c(const struct lowpan_iphc_ctx *ctx)

While the enum above might still be ok I really think that this function
name should change. Again, why not use lowpan_iphc_ctx_is_compressed()
> +{
> + return test_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
> +}
> +
> struct lowpan_priv {
> enum lowpan_lltypes lltype;
> struct dentry *iface_debugfs;
> + struct lowpan_iphc_ctx_table ctx;
>
> /* must be last */
> u8 priv[0] __aligned(sizeof(void *));
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index faf65ba..34e44c0 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -20,7 +20,7 @@
> int lowpan_register_netdevice(struct net_device *dev,
> enum lowpan_lltypes lltype)
> {
> - int ret;
> + int i, ret;
>
> dev->addr_len = EUI64_ADDR_LEN;
> dev->type = ARPHRD_6LOWPAN;
> @@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,
>
> lowpan_priv(dev)->lltype = lltype;
>
> + spin_lock_init(&lowpan_priv(dev)->ctx.lock);
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> + lowpan_priv(dev)->ctx.table[i].id = i;
> +
> ret = register_netdevice(dev);
> if (ret < 0)
> return ret;
> @@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
> }
> EXPORT_SYMBOL(lowpan_unregister_netdev);
>
> +static int lowpan_event(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
> + int i;
> +
> + if (dev->type != ARPHRD_6LOWPAN)
> + return NOTIFY_DONE;
> +
> + switch (event) {
> + case NETDEV_DOWN:
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
> + &lowpan_priv(dev)->ctx.table[i].flags);
> + break;
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block lowpan_notifier = {
> + .notifier_call = lowpan_event,
> +};
> +
> static int __init lowpan_module_init(void)
> {
> int ret;
> @@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
> if (ret < 0)
> return ret;
>
> + ret = register_netdevice_notifier(&lowpan_notifier);
> + if (ret < 0) {
> + lowpan_debugfs_exit();
> + return ret;
> + }
> +
> request_module_nowait("ipv6");
>
> request_module_nowait("nhc_dest");
> @@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
> static void __exit lowpan_module_exit(void)
> {
> lowpan_debugfs_exit();
> + unregister_netdevice_notifier(&lowpan_notifier);
> }
>
> module_init(lowpan_module_init);
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
> index 88eef84..0a95848 100644
> --- a/net/6lowpan/debugfs.c
> +++ b/net/6lowpan/debugfs.c
> @@ -16,19 +16,266 @@
>
> #include "6lowpan_i.h"
>
> +#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
> +
> static struct dentry *lowpan_debugfs;
>
> +static int lowpan_ctx_flag_active_set(void *data, u64 val)
> +{
> + struct lowpan_iphc_ctx *ctx = data;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + if (val)
> + set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
> + else
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_flag_active_get(void *data, u64 *val)
> +{
> + *val = !!lowpan_iphc_ctx_is_active(data);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
> + lowpan_ctx_flag_active_get,
> + lowpan_ctx_flag_active_set, "%llu\n");
> +
> +static int lowpan_ctx_flag_c_set(void *data, u64 val)
> +{
> + struct lowpan_iphc_ctx *ctx = data;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + if (val)
> + set_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
> + else
> + clear_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_flag_c_get(void *data, u64 *val)
> +{
> + *val = !!lowpan_iphc_ctx_is_c(data);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
> + lowpan_ctx_flag_c_set, "%llu\n");
> +
> +static int lowpan_ctx_plen_set(void *data, u64 val)
> +{
> + struct lowpan_iphc_ctx *ctx = data;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
> +
> + if (val > 128)
> + return -EINVAL;
> +
> + spin_lock_bh(&t->lock);
> + ctx->plen = val;
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_plen_get(void *data, u64 *val)
> +{
> + struct lowpan_iphc_ctx *ctx = data;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
> +
> + spin_lock_bh(&t->lock);
> + *val = ctx->plen;
> + spin_unlock_bh(&t->lock);
> + return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
> + lowpan_ctx_plen_set, "%llu\n");
> +
> +static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
> +{
> + struct lowpan_iphc_ctx *ctx = file->private;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
> +
> + spin_lock_bh(&t->lock);
> + seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
> + be16_to_cpu(ctx->pfx.s6_addr16[0]),
> + be16_to_cpu(ctx->pfx.s6_addr16[1]),
> + be16_to_cpu(ctx->pfx.s6_addr16[2]),
> + be16_to_cpu(ctx->pfx.s6_addr16[3]),
> + be16_to_cpu(ctx->pfx.s6_addr16[4]),
> + be16_to_cpu(ctx->pfx.s6_addr16[5]),
> + be16_to_cpu(ctx->pfx.s6_addr16[6]),
> + be16_to_cpu(ctx->pfx.s6_addr16[7]));
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
> +}
> +
> +static ssize_t lowpan_ctx_pfx_write(struct file *fp,
> + const char __user *user_buf, size_t count,
> + loff_t *ppos)
> +{
> + char buf[128] = {};
> + struct seq_file *file = fp->private_data;
> + struct lowpan_iphc_ctx *ctx = file->private;
> + struct lowpan_iphc_ctx_table *t =
> + container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
> + int status = count, n, i;
> + unsigned int addr[8];
> +
> + if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
> + count))) {
> + status = -EFAULT;
> + goto out;
> + }
> +
> + n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
> + &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
> + &addr[5], &addr[6], &addr[7]);
> + if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
> + status = -EINVAL;
> + goto out;
> + }
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < 8; i++)
> + ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
> + spin_unlock_bh(&t->lock);
> +
> +out:
> + return status;
> +}
> +
> +const struct file_operations lowpan_ctx_pfx_fops = {
> + .open = lowpan_ctx_pfx_open,
> + .read = seq_read,
> + .write = lowpan_ctx_pfx_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
> + struct dentry *ctx, u8 id)
> +{
> + struct lowpan_priv *lpriv = lowpan_priv(dev);
> + struct dentry *dentry, *root;
> + char buf[32];
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
> +
> + sprintf(buf, "%d", id);
> +
> + root = debugfs_create_dir(buf, ctx);
> + if (!root)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("active", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_flag_active_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("compression", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_flag_c_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("prefix", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_pfx_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + dentry = debugfs_create_file("prefix_len", 0644, root,
> + &lpriv->ctx.table[id],
> + &lowpan_ctx_plen_fops);
> + if (!dentry)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int lowpan_context_show(struct seq_file *file, void *offset)
> +{
> + struct lowpan_iphc_ctx_table *t = file->private;
> + int i;
> +
> + seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
> + seq_puts(file, "-------------------------------------------------\n");
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + if (!lowpan_iphc_ctx_is_active(&t->table[i]))
> + continue;
> +
> + seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
> + &t->table[i].pfx, t->table[i].plen,
> + !!lowpan_iphc_ctx_is_active(&t->table[i]));
> + }
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_context_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_context_show, inode->i_private);
> +}
> +
> +const struct file_operations lowpan_context_fops = {
> + .open = lowpan_context_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> int lowpan_dev_debugfs_init(struct net_device *dev)
> {
> struct lowpan_priv *lpriv = lowpan_priv(dev);
> + struct dentry *contexts, *dentry;
> + int ret, i;
>
> /* creating the root */
> lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
> if (!lpriv->iface_debugfs)
> goto fail;
>
> + contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
> + if (!contexts)
> + goto remove_root;
> +
> + dentry = debugfs_create_file("show", 0644, contexts,
> + &lowpan_priv(dev)->ctx,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
> + if (ret < 0)
> + goto remove_root;
> + }
> +
> return 0;
>
> +remove_root:
> + lowpan_dev_debugfs_exit(dev);
> fail:
> return -EINVAL;
> }
> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
> index 346b5c1..aad7d77 100644
> --- a/net/6lowpan/iphc.c
> +++ b/net/6lowpan/iphc.c
> @@ -56,6 +56,7 @@
> /* special link-layer handling */
> #include <net/mac802154.h>
>
> +#include "6lowpan_i.h"
> #include "nhc.h"
>
> /* Values of fields within the IPHC encoding first byte */
> @@ -147,6 +148,9 @@
> (((a)->s6_addr16[6]) == 0) && \
> (((a)->s6_addr[14]) == 0))
>
> +#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
> +#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
> +
> static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
> const void *lladdr)
> {
> @@ -195,6 +199,104 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
> }
> }
>
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
> +{
> + struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
> +
> + if (!lowpan_iphc_ctx_is_active(ret))
> + return NULL;
> +
> + /* Don't need to check the compression flag here. Context SHOULD be used
> + * in decompression case another compressor has not yet received the

it seems soem words are missing here. Do you mean "...SHOULD be used in
decompressions case even if another compressor has not yet..." ?

> + * updated context information.
> + */
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_pfx;
> + u8 addr_plen;
> + int i;
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
> +
> + /* if prefix len < 64, the remaining bits until 64th bit is
> + * zero. Otherwise we use table[i]->plen.
> + */
> + if (table[i].plen < 64)
> + addr_plen = 64;
> + else
> + addr_plen = table[i].plen;
> +
> + if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
> + /* remember first match */
> + if (!ret) {
> + ret = &table[i];
> + continue;
> + }
> +
> + /* get the context with longest prefix len */
> + if (table[i].plen > ret->plen)
> + ret = &table[i];
> + }
> + }
> +
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_mcast, network_pfx = {};
> + int i;
> +
> + /* init mcast address with */
> + memcpy(&addr_mcast, addr, sizeof(*addr));
> +
> + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + /* setting plen */
> + addr_mcast.s6_addr[3] = table[i].plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &table[i].pfx,
> + table[i].plen);
> + /* setting network prefix */
> + memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
> +
> + if (ipv6_addr_equal(addr, &addr_mcast)) {
> + ret = &table[i];
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> /* Uncompress address function for source and
> * destination address(non-multicast).
> *
> @@ -259,30 +361,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
> /* Uncompress address function for source context
> * based address(non-multicast).
> */
> -static int uncompress_context_based_src_addr(struct sk_buff *skb,
> - struct in6_addr *ipaddr,
> - u8 address_mode)
> +static int uncompress_ctx_addr(struct sk_buff *skb,
> + const struct net_device *dev,
> + const struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr, u8 address_mode,
> + const void *lladdr)
> {
> + bool fail;
> +
> switch (address_mode) {
> - case LOWPAN_IPHC_SAM_00:
> - /* unspec address ::
> + /* SAM and DAM are the same here */
> + case LOWPAN_IPHC_DAM_00:
> + fail = false;
> + /* SAM_00 -> unspec address ::
> * Do nothing, address is already ::
> + *
> + * DAM 00 -> reserved should never occur.
> */
> break;
> case LOWPAN_IPHC_SAM_01:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_01:
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_10:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_10:
> + ipaddr->s6_addr[11] = 0xFF;
> + ipaddr->s6_addr[12] = 0xFE;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_11:
> - /* TODO */
> - netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
> - address_mode);
> - return -EINVAL;
> + case LOWPAN_IPHC_DAM_11:
> + fail = false;
> + switch (lowpan_priv(dev)->lltype) {
> + case LOWPAN_LLTYPE_IEEE802154:
> + iphc_uncompress_802154_lladdr(ipaddr, lladdr);
> + break;
> + default:
> + iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
> + break;
> + }
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> default:
> pr_debug("Invalid sam value: 0x%x\n", address_mode);
> return -EINVAL;
> }
>
> + if (fail) {
> + pr_debug("Failed to fetch skb data\n");
> + return -EIO;
> + }
> +
> raw_dump_inline(NULL,
> "Reconstructed context based ipv6 src addr is",
> ipaddr->s6_addr, 16);
> @@ -346,6 +477,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
> return 0;
> }
>
> +static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
> + struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr,
> + u8 address_mode)
> +{
> + struct in6_addr network_pfx = {};
> + bool fail;
> +
> + ipaddr->s6_addr[0] = 0xFF;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
> + fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
> + if (fail < 0)
> + return -EIO;
> +
> + /* take prefix_len and network prefix from the context */
> + ipaddr->s6_addr[3] = ctx->plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
> + /* setting network prefix */
> + memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
> +
> + return 0;
> +}
> +
> /* get the ecn values from iphc tf format and set it to ipv6hdr */
> static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
> {
> @@ -459,7 +614,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
> const void *daddr, const void *saddr)
> {
> struct ipv6hdr hdr = {};
> - u8 iphc0, iphc1;
> + struct lowpan_iphc_ctx *ci;
> + u8 iphc0, iphc1, cid = 0;
> int err;
>
> raw_dump_table(__func__, "raw skb data dump uncompressed",
> @@ -469,12 +625,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
> lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
> return -EINVAL;
>
> - /* another if the CID flag is set */
> - if (iphc1 & LOWPAN_IPHC_CID)
> - return -ENOTSUPP;
> -
> hdr.version = 6;
>
> + /* default CID = 0, another if the CID flag is set */
> + if (iphc1 & LOWPAN_IPHC_CID) {
> + if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
> + return -EINVAL;
> + }
> +
> err = lowpan_iphc_tf_decompress(skb, &hdr,
> iphc0 & LOWPAN_IPHC_TF_MASK);
> if (err < 0)
> @@ -500,10 +658,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
> }
>
> if (iphc1 & LOWPAN_IPHC_SAC) {
> - /* Source address context based uncompression */
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
> +
> pr_debug("SAC bit is set. Handle context based source address.\n");
> - err = uncompress_context_based_src_addr(skb, &hdr.saddr,
> - iphc1 & LOWPAN_IPHC_SAM_MASK);
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
> + iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> } else {
> /* Source address uncompression */
> pr_debug("source address stateless compression\n");
> @@ -515,27 +680,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
> if (err)
> return -EINVAL;
>
> - /* check for Multicast Compression */
> - if (iphc1 & LOWPAN_IPHC_M) {
> - if (iphc1 & LOWPAN_IPHC_DAC) {
> - pr_debug("dest: context-based mcast compression\n");
> - /* TODO: implement this */
> - } else {
> - err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
> - iphc1 & LOWPAN_IPHC_DAM_MASK);
> + switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
> + case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
>
> - if (err)
> - return -EINVAL;
> + /* multicast with context */
> + pr_debug("dest: context-based mcast compression\n");
> + err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
> + &hdr.daddr,
> + iphc1 & LOWPAN_IPHC_DAM_MASK);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + case LOWPAN_IPHC_M:
> + /* multicast */
> + err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
> + iphc1 & LOWPAN_IPHC_DAM_MASK);
> + break;
> + case LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> }
> - } else {
> +
> + /* Destination address context based uncompression */
> + pr_debug("DAC bit is set. Handle context based destination address.\n");
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
> + iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + default:
> err = uncompress_addr(skb, dev, &hdr.daddr,
> iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
> pr_debug("dest: stateless compression mode %d dest %pI6c\n",
> iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
> - if (err)
> - return -EINVAL;
> + break;
> }
>
> + if (err)
> + return -EINVAL;
> +
> /* Next header data uncompression */
> if (iphc0 & LOWPAN_IPHC_NH) {
> err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
> @@ -585,6 +775,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
> [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
> };
>
> +static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
> + const struct lowpan_iphc_ctx *ctx,
> + const unsigned char *lladdr, bool sam)
> +{
> + struct in6_addr tmp = {};
> + u8 dam;
> +
> + /* check for SAM/DAM = 11 */
> + memcpy(&tmp.s6_addr[8], lladdr, 8);
> + /* second bit-flip (Universe/Local) is done according RFC2464 */
> + tmp.s6_addr[8] ^= 0x02;
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + dam = LOWPAN_IPHC_DAM_11;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 01 */
> + tmp.s6_addr[11] = 0xFF;
> + tmp.s6_addr[12] = 0xFE;
> + memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
> + dam = LOWPAN_IPHC_DAM_10;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 10, should always match */
> + memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
> + dam = LOWPAN_IPHC_DAM_01;
> + goto out;
> + }
> +
> + WARN_ON_ONCE("context found but no address mode matched\n");
> + return -EINVAL;
> +out:
> +
> + if (sam)
> + return lowpan_iphc_dam_to_sam_value[dam];
> + else
> + return dam;
> +}
> +
> static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
> const unsigned char *lladdr, bool sam)
> {
> @@ -708,6 +950,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
> return val;
> }
>
> +static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
> + const struct lowpan_iphc_ctx *ctx,
> + const struct in6_addr *ipaddr)
> +{
> + u8 data[6];
> +
> + /* flags/scope, reserved (RIID) */
> + memcpy(data, &ipaddr->s6_addr[1], 2);
> + /* group ID */
> + memcpy(&data[1], &ipaddr->s6_addr[11], 4);
> + lowpan_push_hc_data(hc_ptr, data, 6);
> +
> + return LOWPAN_IPHC_DAM_00;
> +}
> +
> static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
> const struct in6_addr *ipaddr)
> {
> @@ -742,10 +999,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
> int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> const void *daddr, const void *saddr)
> {
> - u8 iphc0, iphc1, *hc_ptr;
> + u8 iphc0, iphc1, *hc_ptr, cid = 0;
> struct ipv6hdr *hdr;
> u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
> - int ret, addr_type;
> + struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
> + int ret, ipv6_daddr_type, ipv6_saddr_type;
>
> if (skb->protocol != htons(ETH_P_IPV6))
> return -EINVAL;
> @@ -769,14 +1027,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> iphc0 = LOWPAN_DISPATCH_IPHC;
> iphc1 = 0;
>
> - /* TODO: context lookup */
> -
> raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
> raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
>
> raw_dump_table(__func__, "sending raw skb network uncompressed packet",
> skb->data, skb->len);
>
> + ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
> + dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
> + else
> + dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
> + if (dci) {
> + memcpy(&dci_entry, dci, sizeof(*dci));
> + cid |= dci->id;
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
> + if (sci) {
> + memcpy(&sci_entry, sci, sizeof(*sci));
> + cid |= (sci->id << 4);
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + /* if cid is zero it will be compressed */
> + if (cid) {
> + iphc1 |= LOWPAN_IPHC_CID;
> + lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
> + }
> +
> /* Traffic Class, Flow Label compression */
> iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
>
> @@ -813,39 +1095,63 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> sizeof(hdr->hop_limit));
> }
>
> - addr_type = ipv6_addr_type(&hdr->saddr);
> + ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
> /* source address compression */
> - if (addr_type == IPV6_ADDR_ANY) {
> + if (ipv6_saddr_type == IPV6_ADDR_ANY) {
> pr_debug("source address is unspecified, setting SAC\n");
> iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
> - saddr, true);
> - pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
> - &hdr->saddr, iphc1);
> + if (sci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
> + &sci_entry, saddr,
> + true);
> + iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - pr_debug("send the full source address\n");
> - lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
> + if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |= lowpan_compress_addr_64(&hc_ptr,
> + &hdr->saddr,
> + saddr, true);
> + pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
> + &hdr->saddr, iphc1);
> + } else {
> + pr_debug("send the full source address\n");
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->saddr.s6_addr, 16);
> + }
> }
> }
>
> - addr_type = ipv6_addr_type(&hdr->daddr);
> /* destination address compression */
> - if (addr_type & IPV6_ADDR_MULTICAST) {
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
> pr_debug("destination address is multicast: ");
> - iphc1 |= LOWPAN_IPHC_M;
> - iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
> + if (dci) {
> + iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
> + &dci_entry,
> + &hdr->daddr);
> + } else {
> + iphc1 |= LOWPAN_IPHC_M;
> + iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
> + &hdr->daddr);
> + }
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - /* TODO: context lookup */
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
> - daddr, false);
> - pr_debug("dest address unicast link-local %pI6c "
> - "iphc1 0x%02x\n", &hdr->daddr, iphc1);
> + if (dci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
> + &dci_entry, daddr,
> + false);
> + iphc1 |= LOWPAN_IPHC_DAC;
> } else {
> - pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
> - lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
> + if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |= lowpan_compress_addr_64(&hc_ptr,
> + &hdr->daddr,
> + daddr, false);
> + pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
> + &hdr->daddr, iphc1);
> + } else {
> + pr_debug("dest address unicast %pI6c\n",
> + &hdr->daddr);
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->daddr.s6_addr, 16);
> + }
> }
> }
>

Besides the renaming request and the one unclear comment I'm fine with
this patch.
I will see that I have some time next week to give it a go on my setup.

Reviewed-by: Stefan Schmidt <[email protected]>

regards
Stefan Schmidt


2016-01-10 20:50:25

by Alexander Aring

[permalink] [raw]
Subject: [PATCH bluetooth-next 2/2] ieee802154: 6lowpan: fix return of netdev notifier

This patch fixed the return value of netdev notifier. If the command is
a don't care a NOTIFY_DONE should be returned. If the command matched a
NOTIFY_OK should be returned.

Signed-off-by: Alexander Aring <[email protected]>
---
net/ieee802154/6lowpan/core.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 737c87a..0023c90 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -207,7 +207,7 @@ static int lowpan_device_event(struct notifier_block *unused,
struct net_device *wdev = netdev_notifier_info_to_dev(ptr);

if (wdev->type != ARPHRD_IEEE802154)
- goto out;
+ return NOTIFY_DONE;

switch (event) {
case NETDEV_UNREGISTER:
@@ -219,11 +219,10 @@ static int lowpan_device_event(struct notifier_block *unused,
lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
break;
default:
- break;
+ return NOTIFY_DONE;
}

-out:
- return NOTIFY_DONE;
+ return NOTIFY_OK;
}

static struct notifier_block lowpan_dev_notifier = {
--
2.6.1


2016-01-10 20:50:24

by Alexander Aring

[permalink] [raw]
Subject: [PATCH bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

This patch introduce support for IPHC stateful address compression. It
will offer the context table via one debugfs entry.
This debugfs has and directory for each cid entry for the context table.
Inside each cid directory there exists the following files:

- "active": If the entry is added or deleted. The context table is
original a list implementation, this flag will indicate if the
context is part of list or not.
- "prefix": The ipv6 prefix.
- "prefix_length": The prefix length for the prefix.
- "compression": The compression flag according RFC6775.

This part should be moved into sysfs after some testing time.

Also the debugfs entry contains a "show" file which is a pretty-printout
for the current context table information.

Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 31 ++++
net/6lowpan/core.c | 39 ++++-
net/6lowpan/debugfs.c | 247 +++++++++++++++++++++++++++++
net/6lowpan/iphc.c | 420 +++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 679 insertions(+), 58 deletions(-)

diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2f6a3f2..e1e2e61 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -75,6 +75,8 @@
#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
LOWPAN_IPHC_MAX_HEADER_LEN + \
LOWPAN_NHC_MAX_HDR_LEN)
+/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
+#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)

#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
@@ -98,9 +100,38 @@ enum lowpan_lltypes {
LOWPAN_LLTYPE_IEEE802154,
};

+enum lowpan_iphc_ctx_flags {
+ LOWPAN_IPHC_CTX_FLAG_ACTIVE,
+ LOWPAN_IPHC_CTX_FLAG_C,
+};
+
+struct lowpan_iphc_ctx {
+ u8 id;
+ struct in6_addr pfx;
+ u8 plen;
+ unsigned long flags;
+};
+
+struct lowpan_iphc_ctx_table {
+ spinlock_t lock;
+ const struct lowpan_iphc_ctx_ops *ops;
+ struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
+};
+
+static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
+{
+ return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+}
+
+static inline bool lowpan_iphc_ctx_is_c(const struct lowpan_iphc_ctx *ctx)
+{
+ return test_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
+}
+
struct lowpan_priv {
enum lowpan_lltypes lltype;
struct dentry *iface_debugfs;
+ struct lowpan_iphc_ctx_table ctx;

/* must be last */
u8 priv[0] __aligned(sizeof(void *));
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index faf65ba..34e44c0 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -20,7 +20,7 @@
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
- int ret;
+ int i, ret;

dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

+ spin_lock_init(&lowpan_priv(dev)->ctx.lock);
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
+ lowpan_priv(dev)->ctx.table[i].id = i;
+
ret = register_netdevice(dev);
if (ret < 0)
return ret;
@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(lowpan_unregister_netdev);

+static int lowpan_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ int i;
+
+ if (dev->type != ARPHRD_6LOWPAN)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_DOWN:
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
+ clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
+ &lowpan_priv(dev)->ctx.table[i].flags);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block lowpan_notifier = {
+ .notifier_call = lowpan_event,
+};
+
static int __init lowpan_module_init(void)
{
int ret;
@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
if (ret < 0)
return ret;

+ ret = register_netdevice_notifier(&lowpan_notifier);
+ if (ret < 0) {
+ lowpan_debugfs_exit();
+ return ret;
+ }
+
request_module_nowait("ipv6");

request_module_nowait("nhc_dest");
@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
static void __exit lowpan_module_exit(void)
{
lowpan_debugfs_exit();
+ unregister_netdevice_notifier(&lowpan_notifier);
}

module_init(lowpan_module_init);
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index 88eef84..0a95848 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -16,19 +16,266 @@

#include "6lowpan_i.h"

+#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
+
static struct dentry *lowpan_debugfs;

+static int lowpan_ctx_flag_active_set(void *data, u64 val)
+{
+ struct lowpan_iphc_ctx *ctx = data;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ if (val)
+ set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+ else
+ clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+
+ return 0;
+}
+
+static int lowpan_ctx_flag_active_get(void *data, u64 *val)
+{
+ *val = !!lowpan_iphc_ctx_is_active(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
+ lowpan_ctx_flag_active_get,
+ lowpan_ctx_flag_active_set, "%llu\n");
+
+static int lowpan_ctx_flag_c_set(void *data, u64 val)
+{
+ struct lowpan_iphc_ctx *ctx = data;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ if (val)
+ set_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
+ else
+ clear_bit(LOWPAN_IPHC_CTX_FLAG_C, &ctx->flags);
+
+ return 0;
+}
+
+static int lowpan_ctx_flag_c_get(void *data, u64 *val)
+{
+ *val = !!lowpan_iphc_ctx_is_c(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
+ lowpan_ctx_flag_c_set, "%llu\n");
+
+static int lowpan_ctx_plen_set(void *data, u64 val)
+{
+ struct lowpan_iphc_ctx *ctx = data;
+ struct lowpan_iphc_ctx_table *t =
+ container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+ if (val > 128)
+ return -EINVAL;
+
+ spin_lock_bh(&t->lock);
+ ctx->plen = val;
+ spin_unlock_bh(&t->lock);
+
+ return 0;
+}
+
+static int lowpan_ctx_plen_get(void *data, u64 *val)
+{
+ struct lowpan_iphc_ctx *ctx = data;
+ struct lowpan_iphc_ctx_table *t =
+ container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+ spin_lock_bh(&t->lock);
+ *val = ctx->plen;
+ spin_unlock_bh(&t->lock);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
+ lowpan_ctx_plen_set, "%llu\n");
+
+static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
+{
+ struct lowpan_iphc_ctx *ctx = file->private;
+ struct lowpan_iphc_ctx_table *t =
+ container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+ spin_lock_bh(&t->lock);
+ seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ be16_to_cpu(ctx->pfx.s6_addr16[0]),
+ be16_to_cpu(ctx->pfx.s6_addr16[1]),
+ be16_to_cpu(ctx->pfx.s6_addr16[2]),
+ be16_to_cpu(ctx->pfx.s6_addr16[3]),
+ be16_to_cpu(ctx->pfx.s6_addr16[4]),
+ be16_to_cpu(ctx->pfx.s6_addr16[5]),
+ be16_to_cpu(ctx->pfx.s6_addr16[6]),
+ be16_to_cpu(ctx->pfx.s6_addr16[7]));
+ spin_unlock_bh(&t->lock);
+
+ return 0;
+}
+
+static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
+}
+
+static ssize_t lowpan_ctx_pfx_write(struct file *fp,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ char buf[128] = {};
+ struct seq_file *file = fp->private_data;
+ struct lowpan_iphc_ctx *ctx = file->private;
+ struct lowpan_iphc_ctx_table *t =
+ container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+ int status = count, n, i;
+ unsigned int addr[8];
+
+ if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
+ count))) {
+ status = -EFAULT;
+ goto out;
+ }
+
+ n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
+ &addr[5], &addr[6], &addr[7]);
+ if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_bh(&t->lock);
+ for (i = 0; i < 8; i++)
+ ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
+ spin_unlock_bh(&t->lock);
+
+out:
+ return status;
+}
+
+const struct file_operations lowpan_ctx_pfx_fops = {
+ .open = lowpan_ctx_pfx_open,
+ .read = seq_read,
+ .write = lowpan_ctx_pfx_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
+ struct dentry *ctx, u8 id)
+{
+ struct lowpan_priv *lpriv = lowpan_priv(dev);
+ struct dentry *dentry, *root;
+ char buf[32];
+
+ WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
+
+ sprintf(buf, "%d", id);
+
+ root = debugfs_create_dir(buf, ctx);
+ if (!root)
+ return -EINVAL;
+
+ dentry = debugfs_create_file("active", 0644, root,
+ &lpriv->ctx.table[id],
+ &lowpan_ctx_flag_active_fops);
+ if (!dentry)
+ return -EINVAL;
+
+ dentry = debugfs_create_file("compression", 0644, root,
+ &lpriv->ctx.table[id],
+ &lowpan_ctx_flag_c_fops);
+ if (!dentry)
+ return -EINVAL;
+
+ dentry = debugfs_create_file("prefix", 0644, root,
+ &lpriv->ctx.table[id],
+ &lowpan_ctx_pfx_fops);
+ if (!dentry)
+ return -EINVAL;
+
+ dentry = debugfs_create_file("prefix_len", 0644, root,
+ &lpriv->ctx.table[id],
+ &lowpan_ctx_plen_fops);
+ if (!dentry)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int lowpan_context_show(struct seq_file *file, void *offset)
+{
+ struct lowpan_iphc_ctx_table *t = file->private;
+ int i;
+
+ seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
+ seq_puts(file, "-------------------------------------------------\n");
+
+ spin_lock_bh(&t->lock);
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+ if (!lowpan_iphc_ctx_is_active(&t->table[i]))
+ continue;
+
+ seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
+ &t->table[i].pfx, t->table[i].plen,
+ !!lowpan_iphc_ctx_is_active(&t->table[i]));
+ }
+ spin_unlock_bh(&t->lock);
+
+ return 0;
+}
+
+static int lowpan_context_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_context_show, inode->i_private);
+}
+
+const struct file_operations lowpan_context_fops = {
+ .open = lowpan_context_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
+ struct dentry *contexts, *dentry;
+ int ret, i;

/* creating the root */
lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
if (!lpriv->iface_debugfs)
goto fail;

+ contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
+ if (!contexts)
+ goto remove_root;
+
+ dentry = debugfs_create_file("show", 0644, contexts,
+ &lowpan_priv(dev)->ctx,
+ &lowpan_context_fops);
+ if (!dentry)
+ goto remove_root;
+
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+ ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
+ if (ret < 0)
+ goto remove_root;
+ }
+
return 0;

+remove_root:
+ lowpan_dev_debugfs_exit(dev);
fail:
return -EINVAL;
}
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 346b5c1..aad7d77 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -56,6 +56,7 @@
/* special link-layer handling */
#include <net/mac802154.h>

+#include "6lowpan_i.h"
#include "nhc.h"

/* Values of fields within the IPHC encoding first byte */
@@ -147,6 +148,9 @@
(((a)->s6_addr16[6]) == 0) && \
(((a)->s6_addr[14]) == 0))

+#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
+#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
+
static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
const void *lladdr)
{
@@ -195,6 +199,104 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
}
}

+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
+{
+ struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
+
+ WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
+
+ if (!lowpan_iphc_ctx_is_active(ret))
+ return NULL;
+
+ /* Don't need to check the compression flag here. Context SHOULD be used
+ * in decompression case another compressor has not yet received the
+ * updated context information.
+ */
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
+ const struct in6_addr *addr)
+{
+ struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+ struct lowpan_iphc_ctx *ret = NULL;
+ struct in6_addr addr_pfx;
+ u8 addr_plen;
+ int i;
+
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+ /* Check if context is valid. A context that is not valid
+ * MUST NOT be used for compression.
+ */
+ if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+ !lowpan_iphc_ctx_is_c(&table[i]))
+ continue;
+
+ ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
+
+ /* if prefix len < 64, the remaining bits until 64th bit is
+ * zero. Otherwise we use table[i]->plen.
+ */
+ if (table[i].plen < 64)
+ addr_plen = 64;
+ else
+ addr_plen = table[i].plen;
+
+ if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
+ /* remember first match */
+ if (!ret) {
+ ret = &table[i];
+ continue;
+ }
+
+ /* get the context with longest prefix len */
+ if (table[i].plen > ret->plen)
+ ret = &table[i];
+ }
+ }
+
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
+ const struct in6_addr *addr)
+{
+ struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+ struct lowpan_iphc_ctx *ret = NULL;
+ struct in6_addr addr_mcast, network_pfx = {};
+ int i;
+
+ /* init mcast address with */
+ memcpy(&addr_mcast, addr, sizeof(*addr));
+
+ for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+ /* Check if context is valid. A context that is not valid
+ * MUST NOT be used for compression.
+ */
+ if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+ !lowpan_iphc_ctx_is_c(&table[i]))
+ continue;
+
+ /* setting plen */
+ addr_mcast.s6_addr[3] = table[i].plen;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &table[i].pfx,
+ table[i].plen);
+ /* setting network prefix */
+ memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
+
+ if (ipv6_addr_equal(addr, &addr_mcast)) {
+ ret = &table[i];
+ break;
+ }
+ }
+
+ return ret;
+}
+
/* Uncompress address function for source and
* destination address(non-multicast).
*
@@ -259,30 +361,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
/* Uncompress address function for source context
* based address(non-multicast).
*/
-static int uncompress_context_based_src_addr(struct sk_buff *skb,
- struct in6_addr *ipaddr,
- u8 address_mode)
+static int uncompress_ctx_addr(struct sk_buff *skb,
+ const struct net_device *dev,
+ const struct lowpan_iphc_ctx *ctx,
+ struct in6_addr *ipaddr, u8 address_mode,
+ const void *lladdr)
{
+ bool fail;
+
switch (address_mode) {
- case LOWPAN_IPHC_SAM_00:
- /* unspec address ::
+ /* SAM and DAM are the same here */
+ case LOWPAN_IPHC_DAM_00:
+ fail = false;
+ /* SAM_00 -> unspec address ::
* Do nothing, address is already ::
+ *
+ * DAM 00 -> reserved should never occur.
*/
break;
case LOWPAN_IPHC_SAM_01:
- /* TODO */
+ case LOWPAN_IPHC_DAM_01:
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
case LOWPAN_IPHC_SAM_10:
- /* TODO */
+ case LOWPAN_IPHC_DAM_10:
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
case LOWPAN_IPHC_SAM_11:
- /* TODO */
- netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
- address_mode);
- return -EINVAL;
+ case LOWPAN_IPHC_DAM_11:
+ fail = false;
+ switch (lowpan_priv(dev)->lltype) {
+ case LOWPAN_LLTYPE_IEEE802154:
+ iphc_uncompress_802154_lladdr(ipaddr, lladdr);
+ break;
+ default:
+ iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
+ break;
+ }
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
default:
pr_debug("Invalid sam value: 0x%x\n", address_mode);
return -EINVAL;
}

+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
raw_dump_inline(NULL,
"Reconstructed context based ipv6 src addr is",
ipaddr->s6_addr, 16);
@@ -346,6 +477,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
return 0;
}

+static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
+ struct lowpan_iphc_ctx *ctx,
+ struct in6_addr *ipaddr,
+ u8 address_mode)
+{
+ struct in6_addr network_pfx = {};
+ bool fail;
+
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
+ fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
+ if (fail < 0)
+ return -EIO;
+
+ /* take prefix_len and network prefix from the context */
+ ipaddr->s6_addr[3] = ctx->plen;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
+ /* setting network prefix */
+ memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
+
+ return 0;
+}
+
/* get the ecn values from iphc tf format and set it to ipv6hdr */
static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
{
@@ -459,7 +614,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
struct ipv6hdr hdr = {};
- u8 iphc0, iphc1;
+ struct lowpan_iphc_ctx *ci;
+ u8 iphc0, iphc1, cid = 0;
int err;

raw_dump_table(__func__, "raw skb data dump uncompressed",
@@ -469,12 +625,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
return -EINVAL;

- /* another if the CID flag is set */
- if (iphc1 & LOWPAN_IPHC_CID)
- return -ENOTSUPP;
-
hdr.version = 6;

+ /* default CID = 0, another if the CID flag is set */
+ if (iphc1 & LOWPAN_IPHC_CID) {
+ if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
+ return -EINVAL;
+ }
+
err = lowpan_iphc_tf_decompress(skb, &hdr,
iphc0 & LOWPAN_IPHC_TF_MASK);
if (err < 0)
@@ -500,10 +658,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
}

if (iphc1 & LOWPAN_IPHC_SAC) {
- /* Source address context based uncompression */
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
+ }
+
pr_debug("SAC bit is set. Handle context based source address.\n");
- err = uncompress_context_based_src_addr(skb, &hdr.saddr,
- iphc1 & LOWPAN_IPHC_SAM_MASK);
+ err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
+ iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
} else {
/* Source address uncompression */
pr_debug("source address stateless compression\n");
@@ -515,27 +680,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
if (err)
return -EINVAL;

- /* check for Multicast Compression */
- if (iphc1 & LOWPAN_IPHC_M) {
- if (iphc1 & LOWPAN_IPHC_DAC) {
- pr_debug("dest: context-based mcast compression\n");
- /* TODO: implement this */
- } else {
- err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
- iphc1 & LOWPAN_IPHC_DAM_MASK);
+ switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
+ case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
+ }

- if (err)
- return -EINVAL;
+ /* multicast with context */
+ pr_debug("dest: context-based mcast compression\n");
+ err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
+ &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ break;
+ case LOWPAN_IPHC_M:
+ /* multicast */
+ err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK);
+ break;
+ case LOWPAN_IPHC_DAC:
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
}
- } else {
+
+ /* Destination address context based uncompression */
+ pr_debug("DAC bit is set. Handle context based destination address.\n");
+ err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ break;
+ default:
err = uncompress_addr(skb, dev, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
- if (err)
- return -EINVAL;
+ break;
}

+ if (err)
+ return -EINVAL;
+
/* Next header data uncompression */
if (iphc0 & LOWPAN_IPHC_NH) {
err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
@@ -585,6 +775,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
[LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
};

+static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
+ const struct lowpan_iphc_ctx *ctx,
+ const unsigned char *lladdr, bool sam)
+{
+ struct in6_addr tmp = {};
+ u8 dam;
+
+ /* check for SAM/DAM = 11 */
+ memcpy(&tmp.s6_addr[8], lladdr, 8);
+ /* second bit-flip (Universe/Local) is done according RFC2464 */
+ tmp.s6_addr[8] ^= 0x02;
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ dam = LOWPAN_IPHC_DAM_11;
+ goto out;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ /* check for SAM/DAM = 01 */
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
+ dam = LOWPAN_IPHC_DAM_10;
+ goto out;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ /* check for SAM/DAM = 10, should always match */
+ memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
+ dam = LOWPAN_IPHC_DAM_01;
+ goto out;
+ }
+
+ WARN_ON_ONCE("context found but no address mode matched\n");
+ return -EINVAL;
+out:
+
+ if (sam)
+ return lowpan_iphc_dam_to_sam_value[dam];
+ else
+ return dam;
+}
+
static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
const unsigned char *lladdr, bool sam)
{
@@ -708,6 +950,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
return val;
}

+static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
+ const struct lowpan_iphc_ctx *ctx,
+ const struct in6_addr *ipaddr)
+{
+ u8 data[6];
+
+ /* flags/scope, reserved (RIID) */
+ memcpy(data, &ipaddr->s6_addr[1], 2);
+ /* group ID */
+ memcpy(&data[1], &ipaddr->s6_addr[11], 4);
+ lowpan_push_hc_data(hc_ptr, data, 6);
+
+ return LOWPAN_IPHC_DAM_00;
+}
+
static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
const struct in6_addr *ipaddr)
{
@@ -742,10 +999,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
- u8 iphc0, iphc1, *hc_ptr;
+ u8 iphc0, iphc1, *hc_ptr, cid = 0;
struct ipv6hdr *hdr;
u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
- int ret, addr_type;
+ struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
+ int ret, ipv6_daddr_type, ipv6_saddr_type;

if (skb->protocol != htons(ETH_P_IPV6))
return -EINVAL;
@@ -769,14 +1027,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
iphc0 = LOWPAN_DISPATCH_IPHC;
iphc1 = 0;

- /* TODO: context lookup */
-
raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);

raw_dump_table(__func__, "sending raw skb network uncompressed packet",
skb->data, skb->len);

+ ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
+ dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
+ else
+ dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
+ if (dci) {
+ memcpy(&dci_entry, dci, sizeof(*dci));
+ cid |= dci->id;
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
+ if (sci) {
+ memcpy(&sci_entry, sci, sizeof(*sci));
+ cid |= (sci->id << 4);
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+ /* if cid is zero it will be compressed */
+ if (cid) {
+ iphc1 |= LOWPAN_IPHC_CID;
+ lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
+ }
+
/* Traffic Class, Flow Label compression */
iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);

@@ -813,39 +1095,63 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
sizeof(hdr->hop_limit));
}

- addr_type = ipv6_addr_type(&hdr->saddr);
+ ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
/* source address compression */
- if (addr_type == IPV6_ADDR_ANY) {
+ if (ipv6_saddr_type == IPV6_ADDR_ANY) {
pr_debug("source address is unspecified, setting SAC\n");
iphc1 |= LOWPAN_IPHC_SAC;
} else {
- if (addr_type & IPV6_ADDR_LINKLOCAL) {
- iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
- saddr, true);
- pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
- &hdr->saddr, iphc1);
+ if (sci) {
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
+ &sci_entry, saddr,
+ true);
+ iphc1 |= LOWPAN_IPHC_SAC;
} else {
- pr_debug("send the full source address\n");
- lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
+ if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ &hdr->saddr,
+ saddr, true);
+ pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
+ &hdr->saddr, iphc1);
+ } else {
+ pr_debug("send the full source address\n");
+ lowpan_push_hc_data(&hc_ptr,
+ hdr->saddr.s6_addr, 16);
+ }
}
}

- addr_type = ipv6_addr_type(&hdr->daddr);
/* destination address compression */
- if (addr_type & IPV6_ADDR_MULTICAST) {
+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
pr_debug("destination address is multicast: ");
- iphc1 |= LOWPAN_IPHC_M;
- iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
+ if (dci) {
+ iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
+ &dci_entry,
+ &hdr->daddr);
+ } else {
+ iphc1 |= LOWPAN_IPHC_M;
+ iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
+ &hdr->daddr);
+ }
} else {
- if (addr_type & IPV6_ADDR_LINKLOCAL) {
- /* TODO: context lookup */
- iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
- daddr, false);
- pr_debug("dest address unicast link-local %pI6c "
- "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+ if (dci) {
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
+ &dci_entry, daddr,
+ false);
+ iphc1 |= LOWPAN_IPHC_DAC;
} else {
- pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
- lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
+ if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ &hdr->daddr,
+ daddr, false);
+ pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
+ &hdr->daddr, iphc1);
+ } else {
+ pr_debug("dest address unicast %pI6c\n",
+ &hdr->daddr);
+ lowpan_push_hc_data(&hc_ptr,
+ hdr->daddr.s6_addr, 16);
+ }
}
}

--
2.6.1