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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <alex.aring@xxxxxxxxx>
> ---
>  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
--
To unsubscribe from this list: send the line "unsubscribe linux-wpan" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux