2015-11-29 11:34:38

by Alexander Aring

[permalink] [raw]
Subject: [RFCv3 bluetooth-next 0/4] 6lowpan: debugfs and stateful compression support

Hi,

This patch based on the work of Lukasz Duda. It doesn't contain any interface
lookup methods since we have a generic private dataroom per 6lowpan interface.

Also I dropped the list implementation and do a simple array one. It also
contains support for multicast stateful compression.

To access the context we supporting a complete callback structure for
operations, the include/net/6lowpan.h header contains static inline functions
to call these operations. With such behaviour we don't get any module
dependencies if we need upcomming runtime decisions inside the IPv6 stack.
The alternative would be to do everything inside static inline functions, but
there are few different to handling unicast and multicast context id's, so
it's maybe better to do this as "ops callback structure".

Few things which I am not sure about if it's correct:
- I implement the dci multicast context table and dci unicast as different
table. I don't know if when I have a context id "6" which is multicast
then, I don't can have a unicast context id which is "6". I think they are
different and we can indicate which table we need to use by the IPHC M bit
(receiving side) or the multicast address scope (transmit side).
- Unicast context prefix length can be shorter than 64 bits. Yes this can be,
but if somebody wants to do that he usually means "expand the bits until 64
with zeros". I forbid to add prefix below 64 bits for now.
Each stateful compression has cases for the IID bits (last 8 byte, use it as
inline data/do compression, etc.). When I have a prefix 2000:: with prefix
length 16, then I we hit the case of "Any remaining bits are zero." of
stateful compression, this means it can be converted by "2000::" prefix
length 64, then we don't need to care about that when doing a lookup of
context.
- I tried to get wireshark know about the available context without success.
What I successful get working is tell prefix length exact 64 bits long. I
don't know if wireshark do a correct context handling here, but it's also
possible to have a prefix length above 64 (In my opinion). E.g. unicast
stateful compression in case of SAM/DAM 01:
"Bits covered by context information are always used. Any IID bits not
covered by context information are taken directly from the corresponding
bits carried in-line.

This patch series do following handling for this case:
1. generate ipv6 address with inline IID data.
2. copy the prefix bits from context to the address, which _maybe_ overwrite
the IID (last eight bytes) data which was carried as inline data.

This means, if my context is a full address 2001::1/128, then it will be
the case of SAM/DAM = 11, which means the address is fully elided. (Don't
care about the IID bits, which will be reconstructed from encapsulating
header.

All others SAM/DAM cases has a similar handling.

Nevertheless I don't know how I can tell that wireshark, I can tell prefix which
are 64 bit's long only.

Any comments are welcome.

- Alex

changes since RFCv3:
- add patch for introduce lowpan register/unregister functions
- change name from ipv6_addr_prefix_cpy to ipv6_addr_prefix_copy
- move depends on DEBUG_FS to Patch 1/4 (reported by Stefan Schmidt)
- add "static inline" to lowpan_dev_debugfs_exit if
CONFIG_6LOWPAN_DEBUGFS is not defined. (reported by some bot)
- change "enum state" to simple bool and keyword "enabled"
- change formatstring of prefix-length from "%-03d" to "%-3d", not filled
with zeros anymore.
- Add LOWPAN_DEBUGFS_CTX_NUM_ARGS to avoid magic numbers.
- change name "uninit" to "exit".

changes since RFCv2:
- fix cleanup for 6LoWPAN per interface debugfs
- add ipv6_addr_prefix_cpy
- replace lowpan_iphc_bitcpy with ipv6_addr_prefix_cpy (unicast)
replace lowpan_iphc_bitcpy with ipv6_addr_prefix and memcpy (multicast)
- add netdev mailinglist into cc

Alexander Aring (4):
6lowpan: add lowpan dev register helpers
6lowpan: add debugfs support
ipv6: add ipv6_addr_prefix_copy
6lowpan: iphc: add support for stateful compression

include/net/6lowpan.h | 64 +++++-
include/net/ipv6.h | 15 ++
net/6lowpan/6lowpan_i.h | 31 +++
net/6lowpan/Kconfig | 8 +
net/6lowpan/Makefile | 1 +
net/6lowpan/core.c | 74 ++++++-
net/6lowpan/debugfs.c | 166 +++++++++++++++
net/6lowpan/iphc.c | 479 +++++++++++++++++++++++++++++++++++++-----
net/bluetooth/6lowpan.c | 8 +-
net/ieee802154/6lowpan/core.c | 6 +-
10 files changed, 783 insertions(+), 69 deletions(-)
create mode 100644 net/6lowpan/6lowpan_i.h
create mode 100644 net/6lowpan/debugfs.c

--
2.6.1



2015-11-29 11:34:42

by Alexander Aring

[permalink] [raw]
Subject: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

This patch introduce support for IPHC stateful address compression. It
will offer three debugfs per interface entries, which are:

- dci_table: destination context indentifier table
- sci_table: source context indentifier table
- mcast_sci_table: multicast context identifier table

Example to setup a context id:

A "cat /sys/kernel/debug/6lowpan/lowpan0/dci_table" will display all
contexts which are available. Example:

ID ipv6-address/prefix-length enabled
0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
15 0000:0000:0000:0000:0000:0000:0000:0000/0 0

For setting a context e.g. context id 0, context 2001::, prefix-length
64.

Hint: Simple copy one line and then maniuplate it.

echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 1" >
/sys/kernel/debug/6lowpan/lowpan0/dci_table

The enabled column will display currently if the context is disabled(0) or
enabled(1).

On transmit side:

The IPHC code will automatically search for a context which would be
match for the address. Then it will be use the context with the
best compression method. Means the longest prefix which match will be
used.

Example:

2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the
last bit of the address which has the prefix 2001::/127 is the same like
the IID from the Encapsulating Header. A context ID can also be a
2001::1/128, which is then a full ipv6 address.

On Receive side:

If there is a context defined (when CID not available then it's the
default context 0) then it will be used, if the header doesn't set
SAC or DAC bit thens, it will be dropped.

Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 54 ++++++
net/6lowpan/6lowpan_i.h | 3 +
net/6lowpan/core.c | 17 +-
net/6lowpan/debugfs.c | 113 ++++++++++++
net/6lowpan/iphc.c | 479 ++++++++++++++++++++++++++++++++++++++++++------
5 files changed, 608 insertions(+), 58 deletions(-)

diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2f6a3f2..dec5427 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_CI_TABLE_SIZE (1 << 4)

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

+struct lowpan_iphc_ctx {
+ u8 id;
+ struct in6_addr addr;
+ u8 prefix_len;
+ bool enabled;
+};
+
+struct lowpan_iphc_ctx_ops {
+ bool (*valid_prefix)(const struct lowpan_iphc_ctx *ctx);
+ int (*update)(struct lowpan_iphc_ctx *table,
+ const struct lowpan_iphc_ctx *ctx);
+ struct lowpan_iphc_ctx *(*get_by_id)(const struct net_device *dev,
+ struct lowpan_iphc_ctx *table,
+ u8 id);
+ struct lowpan_iphc_ctx *(*get_by_addr)(const struct net_device *dev,
+ struct lowpan_iphc_ctx *table,
+ const struct in6_addr *addr);
+};
+
+struct lowpan_iphc_ctx_table {
+ spinlock_t lock;
+ const struct lowpan_iphc_ctx_ops *ops;
+ struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE];
+};
+
struct lowpan_priv {
enum lowpan_lltypes lltype;
struct dentry *iface_debugfs;
+ struct lowpan_iphc_ctx_table iphc_dci;
+ struct lowpan_iphc_ctx_table iphc_sci;
+ struct lowpan_iphc_ctx_table iphc_mcast_dci;

/* must be last */
u8 priv[0] __aligned(sizeof(void *));
@@ -125,6 +155,30 @@ struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
return (struct lowpan_802154_cb *)skb->cb;
}

+static inline int lowpan_ctx_update(struct lowpan_iphc_ctx_table *t,
+ const struct lowpan_iphc_ctx *ctx)
+{
+ if (!t->ops->valid_prefix(ctx))
+ return -EINVAL;
+
+ return t->ops->update(t->table, ctx);
+}
+
+static inline struct lowpan_iphc_ctx *
+lowpan_ctx_by_id(const struct net_device *dev, struct lowpan_iphc_ctx_table *t,
+ u8 id)
+{
+ return t->ops->get_by_id(dev, t->table, id);
+}
+
+static inline struct lowpan_iphc_ctx *
+lowpan_ctx_by_addr(const struct net_device *dev,
+ struct lowpan_iphc_ctx_table *t,
+ const struct in6_addr *addr)
+{
+ return t->ops->get_by_addr(dev, t->table, addr);
+}
+
#ifdef DEBUG
/* print data in line */
static inline void raw_dump_inline(const char *caller, char *msg,
diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
index d16bb4b..2c275be 100644
--- a/net/6lowpan/6lowpan_i.h
+++ b/net/6lowpan/6lowpan_i.h
@@ -3,6 +3,9 @@

#include <linux/netdevice.h>

+extern const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops;
+extern const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops;
+
#ifdef CONFIG_6LOWPAN_DEBUGFS
int lowpan_dev_debugfs_init(struct net_device *dev);
void lowpan_dev_debugfs_exit(struct net_device *dev);
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index c7f06f5..f3dbd18 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,21 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

+ spin_lock_init(&lowpan_priv(dev)->iphc_dci.lock);
+ lowpan_priv(dev)->iphc_dci.ops = &iphc_ctx_unicast_ops;
+
+ spin_lock_init(&lowpan_priv(dev)->iphc_sci.lock);
+ lowpan_priv(dev)->iphc_sci.ops = &iphc_ctx_unicast_ops;
+
+ spin_lock_init(&lowpan_priv(dev)->iphc_mcast_dci.lock);
+ lowpan_priv(dev)->iphc_mcast_dci.ops = &iphc_ctx_mcast_ops;
+
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ lowpan_priv(dev)->iphc_dci.table[i].id = i;
+ lowpan_priv(dev)->iphc_sci.table[i].id = i;
+ lowpan_priv(dev)->iphc_mcast_dci.table[i].id = i;
+ }
+
ret = lowpan_dev_debugfs_init(dev);
if (ret < 0)
return ret;
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index 88eef84..24f94ac 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -16,19 +16,132 @@

#include "6lowpan_i.h"

+#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
+
static struct dentry *lowpan_debugfs;

+static int lowpan_context_show(struct seq_file *file, void *offset)
+{
+ struct lowpan_iphc_ctx_table *t = file->private;
+ int i;
+
+ seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
+ "enabled");
+
+ spin_lock_bh(&t->lock);
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ seq_printf(file,
+ "%-2d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %d\n",
+ t->table[i].id,
+ be16_to_cpu(t->table[i].addr.s6_addr16[0]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[1]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[2]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[3]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[4]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[5]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[6]),
+ be16_to_cpu(t->table[i].addr.s6_addr16[7]),
+ t->table[i].prefix_len, t->table[i].enabled);
+ }
+ spin_unlock_bh(&t->lock);
+
+ return 0;
+}
+
+static int lowpan_context_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_context_show, inode->i_private);
+}
+
+static ssize_t lowpan_context_dbgfs_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_table *t = file->private;
+ struct lowpan_iphc_ctx ctx;
+ int status = count, n, id, enabled, i, prefix_len, ret;
+ 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, "%d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %d",
+ &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
+ &addr[5], &addr[6], &addr[7], &prefix_len, &enabled);
+ if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
+ status = -EIO;
+ goto out;
+ }
+
+ if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 ||
+ (enabled != 0 && enabled != 1) || prefix_len > 128) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ ctx.id = id;
+ ctx.enabled = enabled;
+ ctx.prefix_len = prefix_len;
+
+ for (i = 0; i < 8; i++)
+ ctx.addr.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
+
+ spin_lock_bh(&t->lock);
+ ret = lowpan_ctx_update(t, &ctx);
+ if (ret < 0)
+ status = ret;
+ spin_unlock_bh(&t->lock);
+
+out:
+ return status;
+}
+
+const struct file_operations lowpan_context_fops = {
+ .open = lowpan_context_dbgfs_open,
+ .read = seq_read,
+ .write = lowpan_context_dbgfs_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
+ static struct dentry *dentry;

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

+ dentry = debugfs_create_file("dci_table", 0664, lpriv->iface_debugfs,
+ &lowpan_priv(dev)->iphc_dci,
+ &lowpan_context_fops);
+ if (!dentry)
+ goto remove_root;
+
+ dentry = debugfs_create_file("sci_table", 0664, lpriv->iface_debugfs,
+ &lowpan_priv(dev)->iphc_sci,
+ &lowpan_context_fops);
+ if (!dentry)
+ goto remove_root;
+
+ dentry = debugfs_create_file("mcast_dci_table", 0664,
+ lpriv->iface_debugfs,
+ &lowpan_priv(dev)->iphc_mcast_dci,
+ &lowpan_context_fops);
+ if (!dentry)
+ 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..2c5004b 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)
{
@@ -259,30 +263,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->addr, ctx->prefix_len);
+ 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->addr, ctx->prefix_len);
+ 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->addr, ctx->prefix_len);
+ 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 +379,33 @@ 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;
+
+ if (address_mode != LOWPAN_IPHC_DAM_00)
+ return -EINVAL;
+
+ 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);
+ /* take prefix_len and network prefix from the context */
+ ipaddr->s6_addr[3] = ctx->prefix_len;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &ctx->addr, ctx->prefix_len);
+ /* setting network prefix */
+ memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
+
+ if (fail < 0)
+ return -EIO;
+
+ 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 +519,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 +530,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 +563,18 @@ 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)->iphc_sci.lock);
+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_sci,
+ LOWPAN_IPHC_CID_SCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.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)->iphc_sci.lock);
} else {
/* Source address uncompression */
pr_debug("source address stateless compression\n");
@@ -515,27 +586,54 @@ 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)->iphc_mcast_dci.lock);
+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_mcast_dci,
+ LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.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)->iphc_mcast_dci.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)->iphc_dci.lock);
+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_dci,
+ LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.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)->iphc_dci.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 +683,60 @@ 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->addr, ctx->prefix_len);
+ 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->addr, ctx->prefix_len);
+ 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->addr, ctx->prefix_len);
+ 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 +860,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 +909,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 +937,47 @@ 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);
+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
+ spin_lock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
+ dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_mcast_dci,
+ &hdr->daddr);
+ if (dci) {
+ memcpy(&dci_entry, dci, sizeof(*dci));
+ cid |= dci->id;
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
+ } else {
+ spin_lock_bh(&lowpan_priv(dev)->iphc_dci.lock);
+ dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_dci,
+ &hdr->daddr);
+ if (dci) {
+ memcpy(&dci_entry, dci, sizeof(*dci));
+ cid |= dci->id;
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.lock);
+ }
+
+ spin_lock_bh(&lowpan_priv(dev)->iphc_sci.lock);
+ sci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_sci,
+ &hdr->saddr);
+ if (sci) {
+ memcpy(&sci_entry, sci, sizeof(*sci));
+ cid |= (sci->id << 4);
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.lock);
+
+ if (sci || dci) {
+ 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 +1014,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);
+ }
}
}

@@ -871,3 +1096,143 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
return 0;
}
EXPORT_SYMBOL_GPL(lowpan_header_compress);
+
+static bool lowpan_iphc_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
+{
+ /* prefix which are smaller than 64 bits are not valid, users
+ * may mean than a prefix which is filled with zero until 64th bit.
+ * Refere rfc6282 "Any remaining bits are zero." The remaining bits
+ * in this case where are the prefix(< 64) ends until IID starts.
+ */
+ if (ctx->prefix_len < 64)
+ return false;
+
+ return true;
+}
+
+static bool
+lowpan_iphc_mcast_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
+{
+ /* network prefix for multicast is at maximum 64 bits long */
+ if (ctx->prefix_len > 64)
+ return false;
+
+ return true;
+}
+
+static int lowpan_iphc_ctx_update(struct lowpan_iphc_ctx *table,
+ const struct lowpan_iphc_ctx *ctx)
+{
+ int ret = 0, i;
+
+ if (ctx->enabled) {
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ if (ctx->prefix_len != table[i].prefix_len)
+ continue;
+
+ if (ipv6_prefix_equal(&ctx->addr, &table[i].addr,
+ ctx->prefix_len)) {
+ if (table[i].enabled) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+ }
+ }
+
+ memcpy(&table[ctx->id], ctx, sizeof(*ctx));
+
+out:
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_id(const struct net_device *dev,
+ struct lowpan_iphc_ctx *table, u8 id)
+{
+ struct lowpan_iphc_ctx *ret = NULL;
+
+ WARN_ON_ONCE(id > LOWPAN_IPHC_CI_TABLE_SIZE);
+
+ if (table[id].enabled)
+ ret = &table[id];
+
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
+ struct lowpan_iphc_ctx *table,
+ const struct in6_addr *addr)
+{
+ struct lowpan_iphc_ctx *ret = NULL;
+ struct in6_addr addr_prefix;
+ int i;
+
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ ipv6_addr_prefix(&addr_prefix, addr, table[i].prefix_len);
+
+ if (ipv6_prefix_equal(&addr_prefix, &table[i].addr,
+ table[i].prefix_len)) {
+ if (table[i].enabled) {
+ /* remember first match */
+ if (!ret) {
+ ret = &table[i];
+ continue;
+ }
+
+ /* get the context with longest prefix_len */
+ if (table[i].prefix_len > ret->prefix_len)
+ ret = &table[i];
+ }
+ }
+ }
+
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
+ struct lowpan_iphc_ctx *table,
+ const struct in6_addr *addr)
+{
+ 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_CI_TABLE_SIZE; i++) {
+ /* setting plen */
+ addr_mcast.s6_addr[3] = table[i].prefix_len;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &table[i].addr,
+ table[i].prefix_len);
+ /* setting network prefix */
+ memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
+
+ if (ipv6_addr_equal(addr, &addr_mcast)) {
+ if (table[i].enabled) {
+ ret = &table[i];
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops = {
+ .valid_prefix = lowpan_iphc_ctx_valid_prefix,
+ .update = lowpan_iphc_ctx_update,
+ .get_by_id = lowpan_iphc_ctx_get_by_id,
+ .get_by_addr = lowpan_iphc_ctx_get_by_addr,
+};
+
+const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops = {
+ .valid_prefix = lowpan_iphc_mcast_ctx_valid_prefix,
+ .update = lowpan_iphc_ctx_update,
+ .get_by_id = lowpan_iphc_ctx_get_by_id,
+ .get_by_addr = lowpan_iphc_ctx_get_by_mcast_addr,
+};
--
2.6.1


2015-11-29 11:34:40

by Alexander Aring

[permalink] [raw]
Subject: [RFCv3 bluetooth-next 2/4] 6lowpan: add debugfs support

This patch will introduce a 6lowpan entry into the debugfs if enabled.
Inside this 6lowpan directory we create a subdirectories of all 6lowpan
interfaces to offer a per interface debugfs support.

Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 3 +++
net/6lowpan/6lowpan_i.h | 28 ++++++++++++++++++++++++++
net/6lowpan/Kconfig | 8 ++++++++
net/6lowpan/Makefile | 1 +
net/6lowpan/core.c | 28 +++++++++++++++++++++++++-
net/6lowpan/debugfs.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 120 insertions(+), 1 deletion(-)
create mode 100644 net/6lowpan/6lowpan_i.h
create mode 100644 net/6lowpan/debugfs.c

diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 730211f..2f6a3f2 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -53,6 +53,8 @@
#ifndef __6LOWPAN_H__
#define __6LOWPAN_H__

+#include <linux/debugfs.h>
+
#include <net/ipv6.h>
#include <net/net_namespace.h>

@@ -98,6 +100,7 @@ enum lowpan_lltypes {

struct lowpan_priv {
enum lowpan_lltypes lltype;
+ struct dentry *iface_debugfs;

/* must be last */
u8 priv[0] __aligned(sizeof(void *));
diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
new file mode 100644
index 0000000..d16bb4b
--- /dev/null
+++ b/net/6lowpan/6lowpan_i.h
@@ -0,0 +1,28 @@
+#ifndef __6LOWPAN_I_H
+#define __6LOWPAN_I_H
+
+#include <linux/netdevice.h>
+
+#ifdef CONFIG_6LOWPAN_DEBUGFS
+int lowpan_dev_debugfs_init(struct net_device *dev);
+void lowpan_dev_debugfs_exit(struct net_device *dev);
+
+int __init lowpan_debugfs_init(void);
+void lowpan_debugfs_exit(void);
+#else
+static inline int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ return 0;
+}
+
+static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
+
+static inline int __init lowpan_debugfs_init(void)
+{
+ return 0;
+}
+
+static inline void lowpan_debugfs_exit(void) { }
+#endif /* CONFIG_6LOWPAN_DEBUGFS */
+
+#endif /* __6LOWPAN_I_H */
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
index 7fa0f38..7ecedd7 100644
--- a/net/6lowpan/Kconfig
+++ b/net/6lowpan/Kconfig
@@ -5,6 +5,14 @@ menuconfig 6LOWPAN
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.

+config 6LOWPAN_DEBUGFS
+ bool "6LoWPAN debugfs support"
+ depends on 6LOWPAN
+ depends on DEBUG_FS
+ ---help---
+ This enables 6LoWPAN debugfs support. For example to manipulate
+ IPHC context information at runtime.
+
menuconfig 6LOWPAN_NHC
tristate "Next Header Compression Support"
depends on 6LOWPAN
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index c6ffc55..54cad8d 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_6LOWPAN) += 6lowpan.o

6lowpan-y := core.o iphc.o nhc.o
+6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o

#rfc6282 nhcs
obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 80fc509..c7f06f5 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -15,9 +15,13 @@

#include <net/6lowpan.h>

+#include "6lowpan_i.h"
+
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
+ int ret;
+
dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
@@ -25,7 +29,15 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

- return register_netdevice(dev);
+ ret = lowpan_dev_debugfs_init(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = register_netdevice(dev);
+ if (ret < 0)
+ lowpan_dev_debugfs_exit(dev);
+
+ return ret;
}
EXPORT_SYMBOL(lowpan_register_netdevice);

@@ -44,6 +56,7 @@ EXPORT_SYMBOL(lowpan_register_netdev);
void lowpan_unregister_netdevice(struct net_device *dev)
{
unregister_netdevice(dev);
+ lowpan_dev_debugfs_exit(dev);
}
EXPORT_SYMBOL(lowpan_unregister_netdevice);

@@ -57,6 +70,12 @@ EXPORT_SYMBOL(lowpan_unregister_netdev);

static int __init lowpan_module_init(void)
{
+ int ret;
+
+ ret = lowpan_debugfs_init();
+ if (ret < 0)
+ return ret;
+
request_module_nowait("ipv6");

request_module_nowait("nhc_dest");
@@ -69,6 +88,13 @@ static int __init lowpan_module_init(void)

return 0;
}
+
+static void __exit lowpan_module_exit(void)
+{
+ lowpan_debugfs_exit();
+}
+
module_init(lowpan_module_init);
+module_exit(lowpan_module_exit);

MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
new file mode 100644
index 0000000..88eef84
--- /dev/null
+++ b/net/6lowpan/debugfs.c
@@ -0,0 +1,53 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * (C) 2015 Pengutronix, Alexander Aring <[email protected]>
+ * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
+ */
+
+#include <net/6lowpan.h>
+
+#include "6lowpan_i.h"
+
+static struct dentry *lowpan_debugfs;
+
+int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ struct lowpan_priv *lpriv = lowpan_priv(dev);
+
+ /* creating the root */
+ lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
+ if (!lpriv->iface_debugfs)
+ goto fail;
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+void lowpan_dev_debugfs_exit(struct net_device *dev)
+{
+ debugfs_remove_recursive(lowpan_priv(dev)->iface_debugfs);
+}
+
+int __init lowpan_debugfs_init(void)
+{
+ lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
+ if (!lowpan_debugfs)
+ return -EINVAL;
+
+ return 0;
+}
+
+void lowpan_debugfs_exit(void)
+{
+ debugfs_remove_recursive(lowpan_debugfs);
+}
--
2.6.1


2015-11-29 11:34:41

by Alexander Aring

[permalink] [raw]
Subject: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy

This patch adds a static inline function ipv6_addr_prefix_copy which
copies a ipv6 address prefix(argument pfx) into the ipv6 address prefix.
The prefix len is given by plen as bits. This function mainly based on
ipv6_addr_prefix which copies one address prefix from address into a new
ipv6 address destination and zero all other address bits.

The difference is that ipv6_addr_prefix_copy don't get a prefix from an
ipv6 address, it sets a prefix to an ipv6 address with keeping other
address bits. The use case is for context based address compression
inside 6LoWPAN IPHC header which keeping ipv6 prefixes inside a context
table to lookup address-bits without sending them.

Cc: David S. Miller <[email protected]>
Cc: Alexey Kuznetsov <[email protected]>
Cc: James Morris <[email protected]>
Cc: Hideaki YOSHIFUJI <[email protected]>
Cc: Patrick McHardy <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/ipv6.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e1a10b0..cd3881e6 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -382,6 +382,21 @@ static inline void ipv6_addr_prefix(struct in6_addr *pfx,
pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
}

+static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
+ const struct in6_addr *pfx,
+ int plen)
+{
+ /* caller must guarantee 0 <= plen <= 128 */
+ int o = plen >> 3,
+ b = plen & 0x7;
+
+ memcpy(addr->s6_addr, pfx, o);
+ if (b != 0) {
+ addr->s6_addr[o] &= ~(0xff00 >> b);
+ addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
+ }
+}
+
static inline void __ipv6_addr_set_half(__be32 *addr,
__be32 wh, __be32 wl)
{
--
2.6.1


2015-11-29 11:34:39

by Alexander Aring

[permalink] [raw]
Subject: [RFCv3 bluetooth-next 1/4] 6lowpan: add lowpan dev register helpers

This patch introduces register and unregister functionality for lowpan
interfaces. While register a lowpan interface there are several things
which need to be initialize by the 6lowpan subsystem. Upcoming
functionality need to register/unregister per interface components e.g.
debugfs entry.

Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 7 ++++++-
net/6lowpan/core.c | 33 +++++++++++++++++++++++++++++++--
net/bluetooth/6lowpan.c | 8 +++-----
net/ieee802154/6lowpan/core.c | 6 ++----
4 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index cf3bc56..730211f 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -185,7 +185,12 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
*hc_ptr += len;
}

-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+void lowpan_unregister_netdevice(struct net_device *dev);
+void lowpan_unregister_netdev(struct net_device *dev);

/**
* lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 83b19e0..80fc509 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -15,7 +15,8 @@

#include <net/6lowpan.h>

-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype)
{
dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
@@ -23,8 +24,36 @@ void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
dev->priv_flags |= IFF_NO_QUEUE;

lowpan_priv(dev)->lltype = lltype;
+
+ return register_netdevice(dev);
+}
+EXPORT_SYMBOL(lowpan_register_netdevice);
+
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype)
+{
+ int ret;
+
+ rtnl_lock();
+ ret = lowpan_register_netdevice(dev, lltype);
+ rtnl_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(lowpan_register_netdev);
+
+void lowpan_unregister_netdevice(struct net_device *dev)
+{
+ unregister_netdevice(dev);
+}
+EXPORT_SYMBOL(lowpan_unregister_netdevice);
+
+void lowpan_unregister_netdev(struct net_device *dev)
+{
+ rtnl_lock();
+ lowpan_unregister_netdevice(dev);
+ rtnl_unlock();
}
-EXPORT_SYMBOL(lowpan_netdev_setup);
+EXPORT_SYMBOL(lowpan_unregister_netdev);

static int __init lowpan_module_init(void)
{
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 9e9cca3..d040365 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -825,9 +825,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
spin_unlock(&devices_lock);

- lowpan_netdev_setup(netdev, LOWPAN_LLTYPE_BTLE);
-
- err = register_netdev(netdev);
+ err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
if (err < 0) {
BT_INFO("register_netdev failed %d", err);
spin_lock(&devices_lock);
@@ -890,7 +888,7 @@ static void delete_netdev(struct work_struct *work)
struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
delete_netdev);

- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);

/* The entry pointer is deleted by the netdev destructor. */
}
@@ -1348,7 +1346,7 @@ static void disconnect_devices(void)
ifdown(entry->netdev);
BT_DBG("Unregistering netdev %s %p",
entry->netdev->name, entry->netdev);
- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);
kfree(entry);
}
}
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 20c49c7..737c87a 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -161,9 +161,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;

- lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
-
- ret = register_netdevice(ldev);
+ ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
return ret;
@@ -180,7 +178,7 @@ static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
ASSERT_RTNL();

wdev->ieee802154_ptr->lowpan_dev = NULL;
- unregister_netdevice(ldev);
+ lowpan_unregister_netdevice(ldev);
dev_put(wdev);
}

--
2.6.1


2015-12-11 19:49:23

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

On Fri, Dec 11, 2015 at 08:11:42PM +0100, Stefan Schmidt wrote:
> Hello.
>
> On 11/12/15 18:05, Alexander Aring wrote:
> >Hi,
> >
> >status update here.
> >
> >I implemented successful basic 6CO RA messages at sending and
> >processing inside radvd.
> >
> >There might also the question if processing 6CO inside kernel or
> >userspace? I put the basic functionality inside userspace for now.
>
> How are you feeding the entries your receive back into the kernel? The
> debugfs interface right now?
> If we want to go with userspace handling for 6CO we need something better at
> some point.
>

yes. For testing it's fine, I think we should add first support this way. If
we see it seems to be working well then making better UAPI interface.

I currently complete ignore the lifetime thing and handle it like
lifetime is non-zero.

For the lifetime handling, I think we need more than just a "bool", we
need flags. Because we need indicate "somehow" the "C flag" and if the
entry is "added" or "not added" (some active/enabled flag). See [0], or
we do that by check on "::" address/prefix, but I would not do that so far
nobody says that's an invalid prefix.

Then also the question is if doing the lifetime of a context inside the
kernel or in userspace (handled by $USERSPACE_PROGRAM, e.g. radvd). I think
radvd can handle this. See [1]:

The prefix information is timed out as specified in [RFC4861]. When
the Valid Lifetime for a context table entry expires, the entry is
placed in a receive-only mode, which is the equivalent of
receiving a 6CO for that context with C=0. The entry is held in
receive-only mode for a period of twice the default Router Lifetime,
after which the entry is removed.

For this we need the "C" flag and "active" flag (indicate remove). That
the entry is held could be very simple by a timer in kernel, but
otherwise $USERSPACE_PROGRAM could also do this job by handle the
lifetime only in userspace, when lifetime expires -> manipulating the table.

I am not sure what's the best option here is.

BTW:

One bad news is, that radvd needs now to run under "root" with this
changes and it seems that it can running without root right before. This
might be getting a problem when trying to send such feature uptream to
radvd.

Testing:

I already test the new stateful compression patch (manipulating entries in
RIOT manually (no RA messages processing on RIOT).
It works fine under RIOT by manipulating RIOT's context table.

- Alex

[0] https://tools.ietf.org/html/rfc6775#section-5.4.2
[1] https://tools.ietf.org/html/rfc6775#section-5.4.3

2015-12-11 19:11:42

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

Hello.

On 11/12/15 18:05, Alexander Aring wrote:
> Hi,
>
> status update here.
>
> I implemented successful basic 6CO RA messages at sending and
> processing inside radvd.
>
> There might also the question if processing 6CO inside kernel or
> userspace? I put the basic functionality inside userspace for now.

How are you feeding the entries your receive back into the kernel? The
debugfs interface right now?
If we want to go with userspace handling for 6CO we need something
better at some point.

> I will send patches for this in the next days.

Great.

>
> Anyway I figured some things out which was completely wrong inside this
> patch.
>
> - There exist one context based table only.
>
> In rfc6282 stands:
> The specification enables a node to use up to 16 contexts. The
> context used to encode the source address does not have to be the
> same as the context used to encode the destination address.
>
> and I thought it was $SOME_TABLE[DCI] != $DIFFERENT_TABLE[SCI].
> But it means in general simple that it can be: DCI != SCI.
>
> With this knowledge the code gets much smaller.

Interesting point. I also overlooked this during the first review rounds.

regards
Stefan Schmidt

2015-12-11 17:13:53

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

On Fri, Dec 11, 2015 at 06:05:41PM +0100, Alexander Aring wrote:
> Hi,
>
> status update here.
>
> I implemented successful basic 6CO RA messages at sending and
> processing inside radvd.
>
> There might also the question if processing 6CO inside kernel or
> userspace? I put the basic functionality inside userspace for now.
>
> I will send patches for this in the next days.
>
> Anyway I figured some things out which was completely wrong inside this
> patch.
>
> - There exist one context based table only.
>
> In rfc6282 stands:
> The specification enables a node to use up to 16 contexts. The
> context used to encode the source address does not have to be the
> same as the context used to encode the destination address.
>
> and I thought it was $SOME_TABLE[DCI] != $DIFFERENT_TABLE[SCI].

in case of SCI == DCI.

> But it means in general simple that it can be: DCI != SCI.
>
> With this knowledge the code gets much smaller.
>
> - I also remove the validation check for contexts, it can be any
> prefic/address. The lookup functionality will deal with that if the
> context looks not correct or need additional changes.
>

- Also I renamed the "enabled" to "C" which is the compression flag of
6CO.

- Alex

2015-12-11 17:05:46

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

Hi,

status update here.

I implemented successful basic 6CO RA messages at sending and
processing inside radvd.

There might also the question if processing 6CO inside kernel or
userspace? I put the basic functionality inside userspace for now.

I will send patches for this in the next days.

Anyway I figured some things out which was completely wrong inside this
patch.

- There exist one context based table only.

In rfc6282 stands:
The specification enables a node to use up to 16 contexts. The
context used to encode the source address does not have to be the
same as the context used to encode the destination address.

and I thought it was $SOME_TABLE[DCI] != $DIFFERENT_TABLE[SCI].
But it means in general simple that it can be: DCI != SCI.

With this knowledge the code gets much smaller.

- I also remove the validation check for contexts, it can be any
prefic/address. The lookup functionality will deal with that if the
context looks not correct or need additional changes.

The table is very KISS for now. Not much additional functionality is
there, if somebody needs that it can handled by
$USERSPACE_RA_6CO_handler.

- Alex

2015-12-04 12:13:02

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

Hello.

On 03/12/15 15:22, Alexander Aring wrote:
> Hi,
>
> On Wed, Dec 02, 2015 at 03:18:06PM +0100, Stefan Schmidt wrote:
>> Hello.
>>
>> A bit more review here. Still haven't tested the code.
>>
>> On 29/11/15 12:34, Alexander Aring wrote:
>>> This patch introduce support for IPHC stateful address compression. It
>>> will offer three debugfs per interface entries, which are:
>>>
>>> - dci_table: destination context indentifier table
>>> - sci_table: source context indentifier table
>>> - mcast_sci_table: multicast context identifier table
>>>
>>> Example to setup a context id:
>>>
>>> A "cat /sys/kernel/debug/6lowpan/lowpan0/dci_table" will display all
>>> contexts which are available. Example:
>>>
>>> ID ipv6-address/prefix-length enabled
>>> 0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>> 15 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>>>
>>> For setting a context e.g. context id 0, context 2001::, prefix-length
>>> 64.
>>>
>>> Hint: Simple copy one line and then maniuplate it.
>>>
>>> echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 1" >
>>> /sys/kernel/debug/6lowpan/lowpan0/dci_table
>>>
>>> The enabled column will display currently if the context is disabled(0) or
>>> enabled(1).
>>>
>>> On transmit side:
>>>
>>> The IPHC code will automatically search for a context which would be
>>> match for the address. Then it will be use the context with the
>>> best compression method. Means the longest prefix which match will be
>>> used.
>>>
>>> Example:
>>>
>>> 2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the
>>> last bit of the address which has the prefix 2001::/127 is the same like
>>> the IID from the Encapsulating Header. A context ID can also be a
>>> 2001::1/128, which is then a full ipv6 address.
>>>
>>> On Receive side:
>>>
>>> If there is a context defined (when CID not available then it's the
>>> default context 0) then it will be used, if the header doesn't set
>>> SAC or DAC bit thens, it will be dropped.
>>>
>>> Signed-off-by: Alexander Aring <[email protected]>
>>> ---
>>> include/net/6lowpan.h | 54 ++++++
>>> net/6lowpan/6lowpan_i.h | 3 +
>>> net/6lowpan/core.c | 17 +-
>>> net/6lowpan/debugfs.c | 113 ++++++++++++
>>> net/6lowpan/iphc.c | 479 ++++++++++++++++++++++++++++++++++++++++++------
>>> 5 files changed, 608 insertions(+), 58 deletions(-)
>>>
>>> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
>>> index 2f6a3f2..dec5427 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_CI_TABLE_SIZE (1 << 4)
>>> #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
>>> #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
>>> @@ -98,9 +100,37 @@ enum lowpan_lltypes {
>>> LOWPAN_LLTYPE_IEEE802154,
>>> };
>>> +struct lowpan_iphc_ctx {
>>> + u8 id;
>>> + struct in6_addr addr;
>>> + u8 prefix_len;
>>> + bool enabled;
>>> +};
>>> +
>>> +struct lowpan_iphc_ctx_ops {
>>> + bool (*valid_prefix)(const struct lowpan_iphc_ctx *ctx);
>>> + int (*update)(struct lowpan_iphc_ctx *table,
>>> + const struct lowpan_iphc_ctx *ctx);
>>> + struct lowpan_iphc_ctx *(*get_by_id)(const struct net_device *dev,
>>> + struct lowpan_iphc_ctx *table,
>>> + u8 id);
>>> + struct lowpan_iphc_ctx *(*get_by_addr)(const struct net_device *dev,
>>> + struct lowpan_iphc_ctx *table,
>>> + const struct in6_addr *addr);
>>> +};
>>> +
>>> +struct lowpan_iphc_ctx_table {
>>> + spinlock_t lock;
>>> + const struct lowpan_iphc_ctx_ops *ops;
>>> + struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE];
>>> +};
>>> +
>>> struct lowpan_priv {
>>> enum lowpan_lltypes lltype;
>>> struct dentry *iface_debugfs;
>>> + struct lowpan_iphc_ctx_table iphc_dci;
>>> + struct lowpan_iphc_ctx_table iphc_sci;
>>> + struct lowpan_iphc_ctx_table iphc_mcast_dci;
>>> /* must be last */
>>> u8 priv[0] __aligned(sizeof(void *));
>>> @@ -125,6 +155,30 @@ struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
>>> return (struct lowpan_802154_cb *)skb->cb;
>>> }
>>> +static inline int lowpan_ctx_update(struct lowpan_iphc_ctx_table *t,
>>> + const struct lowpan_iphc_ctx *ctx)
>>> +{
>>> + if (!t->ops->valid_prefix(ctx))
>>> + return -EINVAL;
>>> +
>>> + return t->ops->update(t->table, ctx);
>>> +}
>>> +
>>> +static inline struct lowpan_iphc_ctx *
>>> +lowpan_ctx_by_id(const struct net_device *dev, struct lowpan_iphc_ctx_table *t,
>>> + u8 id)
>>> +{
>>> + return t->ops->get_by_id(dev, t->table, id);
>>> +}
>>> +
>>> +static inline struct lowpan_iphc_ctx *
>>> +lowpan_ctx_by_addr(const struct net_device *dev,
>>> + struct lowpan_iphc_ctx_table *t,
>>> + const struct in6_addr *addr)
>>> +{
>>> + return t->ops->get_by_addr(dev, t->table, addr);
>>> +}
>>> +
>>> #ifdef DEBUG
>>> /* print data in line */
>>> static inline void raw_dump_inline(const char *caller, char *msg,
>>> diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
>>> index d16bb4b..2c275be 100644
>>> --- a/net/6lowpan/6lowpan_i.h
>>> +++ b/net/6lowpan/6lowpan_i.h
>>> @@ -3,6 +3,9 @@
>>> #include <linux/netdevice.h>
>>> +extern const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops;
>>> +extern const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops;
>>> +
>>> #ifdef CONFIG_6LOWPAN_DEBUGFS
>>> int lowpan_dev_debugfs_init(struct net_device *dev);
>>> void lowpan_dev_debugfs_exit(struct net_device *dev);
>>> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
>>> index c7f06f5..f3dbd18 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,21 @@ int lowpan_register_netdevice(struct net_device *dev,
>>> lowpan_priv(dev)->lltype = lltype;
>>> + spin_lock_init(&lowpan_priv(dev)->iphc_dci.lock);
>>> + lowpan_priv(dev)->iphc_dci.ops = &iphc_ctx_unicast_ops;
>>> +
>>> + spin_lock_init(&lowpan_priv(dev)->iphc_sci.lock);
>>> + lowpan_priv(dev)->iphc_sci.ops = &iphc_ctx_unicast_ops;
>>> +
>>> + spin_lock_init(&lowpan_priv(dev)->iphc_mcast_dci.lock);
>>> + lowpan_priv(dev)->iphc_mcast_dci.ops = &iphc_ctx_mcast_ops;
>>> +
>>> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
>>> + lowpan_priv(dev)->iphc_dci.table[i].id = i;
>>> + lowpan_priv(dev)->iphc_sci.table[i].id = i;
>>> + lowpan_priv(dev)->iphc_mcast_dci.table[i].id = i;
>>> + }
>>> +
>>> ret = lowpan_dev_debugfs_init(dev);
>>> if (ret < 0)
>>> return ret;
>>> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
>>> index 88eef84..24f94ac 100644
>>> --- a/net/6lowpan/debugfs.c
>>> +++ b/net/6lowpan/debugfs.c
>>> @@ -16,19 +16,132 @@
>>> #include "6lowpan_i.h"
>>> +#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
>>> +
>>> static struct dentry *lowpan_debugfs;
>>> +static int lowpan_context_show(struct seq_file *file, void *offset)
>>> +{
>>> + struct lowpan_iphc_ctx_table *t = file->private;
>>> + int i;
>>> +
>>> + seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
>>> + "enabled");
>>> +
>>> + spin_lock_bh(&t->lock);
>>> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
>>> + seq_printf(file,
>>> + "%-2d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %d\n",
>>> + t->table[i].id,
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[0]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[1]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[2]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[3]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[4]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[5]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[6]),
>>> + be16_to_cpu(t->table[i].addr.s6_addr16[7]),
>>> + t->table[i].prefix_len, t->table[i].enabled);
>>> + }
>>> + spin_unlock_bh(&t->lock);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int lowpan_context_dbgfs_open(struct inode *inode, struct file *file)
>>> +{
>>> + return single_open(file, lowpan_context_show, inode->i_private);
>>> +}
>>> +
>>> +static ssize_t lowpan_context_dbgfs_write(struct file *fp,
>>> + const char __user *user_buf,
>>> + size_t count, loff_t *ppos)
>>> +{
>>> + char buf[128] = { };
>> Do we enforce the 128 char limit somewhere?
>>
>>> + struct seq_file *file = fp->private_data;
>>> + struct lowpan_iphc_ctx_table *t = file->private;
>>> + struct lowpan_iphc_ctx ctx;
>>> + int status = count, n, id, enabled, i, prefix_len, ret;
>>> + unsigned int addr[8];
>>> +
>>> + if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
>>> + count))) {
>> I see we do here, ok.
>>
>>> + status = -EFAULT;
>>> + goto out;
>>> + }
>>> +
>>> + n = sscanf(buf, "%d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %d",
>>> + &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
>>> + &addr[5], &addr[6], &addr[7], &prefix_len, &enabled);
>>> + if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
>>> + status = -EIO;
>>> + goto out;
>>> + }
>>> +
>>> + if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 ||
>>> + (enabled != 0 && enabled != 1) || prefix_len > 128) {
>> Hmm, if you would use a bool for enabled here instead of an int the check on
>> enabled would not be needed.
>>
> This is because sscanf format string excepts an int here to parsing the
> "%d" argument.
>
> I adapt the same mechanism here what we have for "bools" for nl802154.
>
> When I change it to bool, I will get:
>
> net/6lowpan/debugfs.c: In function 'lowpan_context_dbgfs_write':
> net/6lowpan/debugfs.c:76:6: warning: format '%d' expects argument of
> type 'int *', but argument 13 has type 'bool *' [-Wformat=]
> &addr[5], &addr[6], &addr[7], &prefix_len, &enabled);

Ah, right, scanf has no support for bool. :( Guess we keep it as int.

>
>>> + status = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + ctx.id = id;
>>> + ctx.enabled = enabled;
> conversion to bool is here, I could do "!!enabled" and remove the check
> above, but then is 1 <-> MAX_INT and -1 <-> MIN_INT also true.

Nah, leave it as is. I was under the impüression we could use scanf for
bool directly. That not being the case we can keep it as is.

>
>>> + ctx.prefix_len = prefix_len;
>>> +
>>> + for (i = 0; i < 8; i++)
>>> + ctx.addr.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
>>> +
>>> + spin_lock_bh(&t->lock);
>>> + ret = lowpan_ctx_update(t, &ctx);
>>> + if (ret < 0)
>>> + status = ret;
>>> + spin_unlock_bh(&t->lock);
>>> +
>>> +out:
>>> + return status;
>>> +}
>>> +
>>> +const struct file_operations lowpan_context_fops = {
>>> + .open = lowpan_context_dbgfs_open,
>>> + .read = seq_read,
>>> + .write = lowpan_context_dbgfs_write,
>>> + .llseek = seq_lseek,
>>> + .release = single_release,
>>> +};
>>> +
>>> int lowpan_dev_debugfs_init(struct net_device *dev)
>>> {
>>> struct lowpan_priv *lpriv = lowpan_priv(dev);
>>> + static struct dentry *dentry;
>>> /* creating the root */
>>> lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
>>> if (!lpriv->iface_debugfs)
>>> goto fail;
>>> + dentry = debugfs_create_file("dci_table", 0664, lpriv->iface_debugfs,
>> Any special reason you want the group have write permissions? I would expect
>> a 644 here. Same for the other two.
>>
> No, I will change it to 0644.

Great, thanks.

>
>>> + &lowpan_priv(dev)->iphc_dci,
>>> + &lowpan_context_fops);
>>> + if (!dentry)
>>> + goto remove_root;
>>> +
>>> + dentry = debugfs_create_file("sci_table", 0664, lpriv->iface_debugfs,
>>> + &lowpan_priv(dev)->iphc_sci,
>>> + &lowpan_context_fops);
>>> + if (!dentry)
>>> + goto remove_root;
>>> +
>>> + dentry = debugfs_create_file("mcast_dci_table", 0664,
>>> + lpriv->iface_debugfs,
>>> + &lowpan_priv(dev)->iphc_mcast_dci,
>>> + &lowpan_context_fops);
>>> + if (!dentry)
>>> + goto remove_root;
>>> +
>>> return 0;
>>> +remove_root:
>>> + lowpan_dev_debugfs_exit(dev);
>> Just to check here. This one is calling debugfs_remove_recursive(). This
>> will cleanup states where we might have been able to create dci_table and
>> sci_table but not mcast_dci_table, right?
> Yes, should. I will remove the whole interface related debugfs entry
> inclusive all sub-entries which was created under the interface related
> directory entry which is in this case sci/dci/mcast_dci tables.

OK

>>> fail:
>>> return -EINVAL;
>>> }
>>> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
>>> index 346b5c1..2c5004b 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)
>>> {
>>> @@ -259,30 +263,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->addr, ctx->prefix_len);
>>> + 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->addr, ctx->prefix_len);
>>> + 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->addr, ctx->prefix_len);
>>> + 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 +379,33 @@ 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;
>>> +
>>> + if (address_mode != LOWPAN_IPHC_DAM_00)
>>> + return -EINVAL;
>>> +
>>> + 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);
>>> + /* take prefix_len and network prefix from the context */
>>> + ipaddr->s6_addr[3] = ctx->prefix_len;
>>> + /* get network prefix to copy into multicast address */
>>> + ipv6_addr_prefix(&network_pfx, &ctx->addr, ctx->prefix_len);
>>> + /* setting network prefix */
>>> + memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
>>> +
>>> + if (fail < 0)
>>> + return -EIO;
>>> +
>>> + 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 +519,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 +530,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 +563,18 @@ 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)->iphc_sci.lock);
>>> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_sci,
>>> + LOWPAN_IPHC_CID_SCI(cid));
>>> + if (!ci) {
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.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)->iphc_sci.lock);
>>> } else {
>>> /* Source address uncompression */
>>> pr_debug("source address stateless compression\n");
>>> @@ -515,27 +586,54 @@ 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)->iphc_mcast_dci.lock);
>>> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_mcast_dci,
>>> + LOWPAN_IPHC_CID_DCI(cid));
>>> + if (!ci) {
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.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)->iphc_mcast_dci.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)->iphc_dci.lock);
>>> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_dci,
>>> + LOWPAN_IPHC_CID_DCI(cid));
>>> + if (!ci) {
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.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)->iphc_dci.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 +683,60 @@ 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
>>> + */
>> Maybe put this comment on line. It should be short enough.
>>
> ok.
>
>>> + tmp.s6_addr[8] ^= 0x02;
>>> + /* context information are always used */
>>> + ipv6_addr_prefix_copy(&tmp, &ctx->addr, ctx->prefix_len);
>>> + 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->addr, ctx->prefix_len);
>>> + 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->addr, ctx->prefix_len);
>>> + 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 +860,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 +909,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 +937,47 @@ 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);
>>> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
>>> + spin_lock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
>>> + dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_mcast_dci,
>>> + &hdr->daddr);
>>> + if (dci) {
>>> + memcpy(&dci_entry, dci, sizeof(*dci));
>>> + cid |= dci->id;
>>> + }
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
>>> + } else {
>>> + spin_lock_bh(&lowpan_priv(dev)->iphc_dci.lock);
>>> + dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_dci,
>>> + &hdr->daddr);
>>> + if (dci) {
>>> + memcpy(&dci_entry, dci, sizeof(*dci));
>>> + cid |= dci->id;
>>> + }
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.lock);
>>> + }
>>> +
>>> + spin_lock_bh(&lowpan_priv(dev)->iphc_sci.lock);
>>> + sci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_sci,
>>> + &hdr->saddr);
>>> + if (sci) {
>>> + memcpy(&sci_entry, sci, sizeof(*sci));
>>> + cid |= (sci->id << 4);
>>> + }
>>> + spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.lock);
>>> +
>>> + if (sci || dci) {
>>> + 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 +1014,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);
>>> + }
>>> }
>>> }
>>> @@ -871,3 +1096,143 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
>>> return 0;
>>> }
>>> EXPORT_SYMBOL_GPL(lowpan_header_compress);
>>> +
>>> +static bool lowpan_iphc_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
>>> +{
>>> + /* prefix which are smaller than 64 bits are not valid, users
>>> + * may mean than a prefix which is filled with zero until 64th bit.
>>> + * Refere rfc6282 "Any remaining bits are zero." The remaining bits
>>> + * in this case where are the prefix(< 64) ends until IID starts.
>>> + */
>>> + if (ctx->prefix_len < 64)
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
> I will remove this check, the prefix_len should be any length, from 0
> until 128. We need to care about the zero bits if prefix_len < 64 during
> lookup.
>
> I think a "valid" test would be to check on multicast address scope
> which should not a valid prefix for dci/sci table. There might be more
> addresses which should not valid here. At the moment I would say a
> multicast address should be invalid. I will add this as well.
>
>>> +static bool
>>> +lowpan_iphc_mcast_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
>>> +{
>>> + /* network prefix for multicast is at maximum 64 bits long */
>>> + if (ctx->prefix_len > 64)
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
>>> +static int lowpan_iphc_ctx_update(struct lowpan_iphc_ctx *table,
>>> + const struct lowpan_iphc_ctx *ctx)
>>> +{
>>> + int ret = 0, i;
>>> +
>>> + if (ctx->enabled) {
>>> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
>>> + if (ctx->prefix_len != table[i].prefix_len)
>>> + continue;
>>> +
>>> + if (ipv6_prefix_equal(&ctx->addr, &table[i].addr,
>>> + ctx->prefix_len)) {
>>> + if (table[i].enabled) {
>>> + ret = -EEXIST;
>>> + goto out;
>>> + }
>>> + }
>>> + }
>>> + }
>>> +
>>> + memcpy(&table[ctx->id], ctx, sizeof(*ctx));
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>> In what cases should the update succeed? I'm wondering about some corner
>> cases here:
>>
>> o If the new ctx is disabled it would update the table on its cid no matter
>> what.
>> o If the new ctx is enabled we would update it only if we do not find the
>> same prefix in an enabled state already.
>>
>> To put it differently, the only case we do not update the table with the
>> context is if it already exists and is enabled. This is how you want it?
>>
> yes. If we don't check if it's enabled (inside the table), then we could
> not simple copy one line (by doing cat and echo) with a context which
> is disabled, then simple replace the disabled(0) to enabled(1). Then it
> would fail because the same prefix is already there, but when it's
> disabled then we ignore if the prefix is already exists. It will not
> used then anyway.

ok

regards
Stefan Schmidt


2015-12-03 14:22:59

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

Hi,

On Wed, Dec 02, 2015 at 03:18:06PM +0100, Stefan Schmidt wrote:
> Hello.
>
> A bit more review here. Still haven't tested the code.
>
> On 29/11/15 12:34, Alexander Aring wrote:
> >This patch introduce support for IPHC stateful address compression. It
> >will offer three debugfs per interface entries, which are:
> >
> > - dci_table: destination context indentifier table
> > - sci_table: source context indentifier table
> > - mcast_sci_table: multicast context identifier table
> >
> >Example to setup a context id:
> >
> >A "cat /sys/kernel/debug/6lowpan/lowpan0/dci_table" will display all
> >contexts which are available. Example:
> >
> >ID ipv6-address/prefix-length enabled
> >0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >15 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> >
> >For setting a context e.g. context id 0, context 2001::, prefix-length
> >64.
> >
> >Hint: Simple copy one line and then maniuplate it.
> >
> >echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 1" >
> >/sys/kernel/debug/6lowpan/lowpan0/dci_table
> >
> >The enabled column will display currently if the context is disabled(0) or
> >enabled(1).
> >
> >On transmit side:
> >
> >The IPHC code will automatically search for a context which would be
> >match for the address. Then it will be use the context with the
> >best compression method. Means the longest prefix which match will be
> >used.
> >
> >Example:
> >
> >2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the
> >last bit of the address which has the prefix 2001::/127 is the same like
> >the IID from the Encapsulating Header. A context ID can also be a
> >2001::1/128, which is then a full ipv6 address.
> >
> >On Receive side:
> >
> >If there is a context defined (when CID not available then it's the
> >default context 0) then it will be used, if the header doesn't set
> >SAC or DAC bit thens, it will be dropped.
> >
> >Signed-off-by: Alexander Aring <[email protected]>
> >---
> > include/net/6lowpan.h | 54 ++++++
> > net/6lowpan/6lowpan_i.h | 3 +
> > net/6lowpan/core.c | 17 +-
> > net/6lowpan/debugfs.c | 113 ++++++++++++
> > net/6lowpan/iphc.c | 479 ++++++++++++++++++++++++++++++++++++++++++------
> > 5 files changed, 608 insertions(+), 58 deletions(-)
> >
> >diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> >index 2f6a3f2..dec5427 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_CI_TABLE_SIZE (1 << 4)
> > #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
> > #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
> >@@ -98,9 +100,37 @@ enum lowpan_lltypes {
> > LOWPAN_LLTYPE_IEEE802154,
> > };
> >+struct lowpan_iphc_ctx {
> >+ u8 id;
> >+ struct in6_addr addr;
> >+ u8 prefix_len;
> >+ bool enabled;
> >+};
> >+
> >+struct lowpan_iphc_ctx_ops {
> >+ bool (*valid_prefix)(const struct lowpan_iphc_ctx *ctx);
> >+ int (*update)(struct lowpan_iphc_ctx *table,
> >+ const struct lowpan_iphc_ctx *ctx);
> >+ struct lowpan_iphc_ctx *(*get_by_id)(const struct net_device *dev,
> >+ struct lowpan_iphc_ctx *table,
> >+ u8 id);
> >+ struct lowpan_iphc_ctx *(*get_by_addr)(const struct net_device *dev,
> >+ struct lowpan_iphc_ctx *table,
> >+ const struct in6_addr *addr);
> >+};
> >+
> >+struct lowpan_iphc_ctx_table {
> >+ spinlock_t lock;
> >+ const struct lowpan_iphc_ctx_ops *ops;
> >+ struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE];
> >+};
> >+
> > struct lowpan_priv {
> > enum lowpan_lltypes lltype;
> > struct dentry *iface_debugfs;
> >+ struct lowpan_iphc_ctx_table iphc_dci;
> >+ struct lowpan_iphc_ctx_table iphc_sci;
> >+ struct lowpan_iphc_ctx_table iphc_mcast_dci;
> > /* must be last */
> > u8 priv[0] __aligned(sizeof(void *));
> >@@ -125,6 +155,30 @@ struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
> > return (struct lowpan_802154_cb *)skb->cb;
> > }
> >+static inline int lowpan_ctx_update(struct lowpan_iphc_ctx_table *t,
> >+ const struct lowpan_iphc_ctx *ctx)
> >+{
> >+ if (!t->ops->valid_prefix(ctx))
> >+ return -EINVAL;
> >+
> >+ return t->ops->update(t->table, ctx);
> >+}
> >+
> >+static inline struct lowpan_iphc_ctx *
> >+lowpan_ctx_by_id(const struct net_device *dev, struct lowpan_iphc_ctx_table *t,
> >+ u8 id)
> >+{
> >+ return t->ops->get_by_id(dev, t->table, id);
> >+}
> >+
> >+static inline struct lowpan_iphc_ctx *
> >+lowpan_ctx_by_addr(const struct net_device *dev,
> >+ struct lowpan_iphc_ctx_table *t,
> >+ const struct in6_addr *addr)
> >+{
> >+ return t->ops->get_by_addr(dev, t->table, addr);
> >+}
> >+
> > #ifdef DEBUG
> > /* print data in line */
> > static inline void raw_dump_inline(const char *caller, char *msg,
> >diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
> >index d16bb4b..2c275be 100644
> >--- a/net/6lowpan/6lowpan_i.h
> >+++ b/net/6lowpan/6lowpan_i.h
> >@@ -3,6 +3,9 @@
> > #include <linux/netdevice.h>
> >+extern const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops;
> >+extern const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops;
> >+
> > #ifdef CONFIG_6LOWPAN_DEBUGFS
> > int lowpan_dev_debugfs_init(struct net_device *dev);
> > void lowpan_dev_debugfs_exit(struct net_device *dev);
> >diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> >index c7f06f5..f3dbd18 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,21 @@ int lowpan_register_netdevice(struct net_device *dev,
> > lowpan_priv(dev)->lltype = lltype;
> >+ spin_lock_init(&lowpan_priv(dev)->iphc_dci.lock);
> >+ lowpan_priv(dev)->iphc_dci.ops = &iphc_ctx_unicast_ops;
> >+
> >+ spin_lock_init(&lowpan_priv(dev)->iphc_sci.lock);
> >+ lowpan_priv(dev)->iphc_sci.ops = &iphc_ctx_unicast_ops;
> >+
> >+ spin_lock_init(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> >+ lowpan_priv(dev)->iphc_mcast_dci.ops = &iphc_ctx_mcast_ops;
> >+
> >+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> >+ lowpan_priv(dev)->iphc_dci.table[i].id = i;
> >+ lowpan_priv(dev)->iphc_sci.table[i].id = i;
> >+ lowpan_priv(dev)->iphc_mcast_dci.table[i].id = i;
> >+ }
> >+
> > ret = lowpan_dev_debugfs_init(dev);
> > if (ret < 0)
> > return ret;
> >diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
> >index 88eef84..24f94ac 100644
> >--- a/net/6lowpan/debugfs.c
> >+++ b/net/6lowpan/debugfs.c
> >@@ -16,19 +16,132 @@
> > #include "6lowpan_i.h"
> >+#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
> >+
> > static struct dentry *lowpan_debugfs;
> >+static int lowpan_context_show(struct seq_file *file, void *offset)
> >+{
> >+ struct lowpan_iphc_ctx_table *t = file->private;
> >+ int i;
> >+
> >+ seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
> >+ "enabled");
> >+
> >+ spin_lock_bh(&t->lock);
> >+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> >+ seq_printf(file,
> >+ "%-2d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %d\n",
> >+ t->table[i].id,
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[0]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[1]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[2]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[3]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[4]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[5]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[6]),
> >+ be16_to_cpu(t->table[i].addr.s6_addr16[7]),
> >+ t->table[i].prefix_len, t->table[i].enabled);
> >+ }
> >+ spin_unlock_bh(&t->lock);
> >+
> >+ return 0;
> >+}
> >+
> >+static int lowpan_context_dbgfs_open(struct inode *inode, struct file *file)
> >+{
> >+ return single_open(file, lowpan_context_show, inode->i_private);
> >+}
> >+
> >+static ssize_t lowpan_context_dbgfs_write(struct file *fp,
> >+ const char __user *user_buf,
> >+ size_t count, loff_t *ppos)
> >+{
> >+ char buf[128] = { };
>
> Do we enforce the 128 char limit somewhere?
>
> >+ struct seq_file *file = fp->private_data;
> >+ struct lowpan_iphc_ctx_table *t = file->private;
> >+ struct lowpan_iphc_ctx ctx;
> >+ int status = count, n, id, enabled, i, prefix_len, ret;
> >+ unsigned int addr[8];
> >+
> >+ if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
> >+ count))) {
>
> I see we do here, ok.
>
> >+ status = -EFAULT;
> >+ goto out;
> >+ }
> >+
> >+ n = sscanf(buf, "%d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %d",
> >+ &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
> >+ &addr[5], &addr[6], &addr[7], &prefix_len, &enabled);
> >+ if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
> >+ status = -EIO;
> >+ goto out;
> >+ }
> >+
> >+ if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 ||
> >+ (enabled != 0 && enabled != 1) || prefix_len > 128) {
>
> Hmm, if you would use a bool for enabled here instead of an int the check on
> enabled would not be needed.
>

This is because sscanf format string excepts an int here to parsing the
"%d" argument.

I adapt the same mechanism here what we have for "bools" for nl802154.

When I change it to bool, I will get:

net/6lowpan/debugfs.c: In function 'lowpan_context_dbgfs_write':
net/6lowpan/debugfs.c:76:6: warning: format '%d' expects argument of
type 'int *', but argument 13 has type 'bool *' [-Wformat=]
&addr[5], &addr[6], &addr[7], &prefix_len, &enabled);

> >+ status = -EINVAL;
> >+ goto out;
> >+ }
> >+
> >+ ctx.id = id;
> >+ ctx.enabled = enabled;

conversion to bool is here, I could do "!!enabled" and remove the check
above, but then is 1 <-> MAX_INT and -1 <-> MIN_INT also true.

> >+ ctx.prefix_len = prefix_len;
> >+
> >+ for (i = 0; i < 8; i++)
> >+ ctx.addr.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
> >+
> >+ spin_lock_bh(&t->lock);
> >+ ret = lowpan_ctx_update(t, &ctx);
> >+ if (ret < 0)
> >+ status = ret;
> >+ spin_unlock_bh(&t->lock);
> >+
> >+out:
> >+ return status;
> >+}
> >+
> >+const struct file_operations lowpan_context_fops = {
> >+ .open = lowpan_context_dbgfs_open,
> >+ .read = seq_read,
> >+ .write = lowpan_context_dbgfs_write,
> >+ .llseek = seq_lseek,
> >+ .release = single_release,
> >+};
> >+
> > int lowpan_dev_debugfs_init(struct net_device *dev)
> > {
> > struct lowpan_priv *lpriv = lowpan_priv(dev);
> >+ static struct dentry *dentry;
> > /* creating the root */
> > lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
> > if (!lpriv->iface_debugfs)
> > goto fail;
> >+ dentry = debugfs_create_file("dci_table", 0664, lpriv->iface_debugfs,
>
> Any special reason you want the group have write permissions? I would expect
> a 644 here. Same for the other two.
>

No, I will change it to 0644.

> >+ &lowpan_priv(dev)->iphc_dci,
> >+ &lowpan_context_fops);
> >+ if (!dentry)
> >+ goto remove_root;
> >+
> >+ dentry = debugfs_create_file("sci_table", 0664, lpriv->iface_debugfs,
> >+ &lowpan_priv(dev)->iphc_sci,
> >+ &lowpan_context_fops);
> >+ if (!dentry)
> >+ goto remove_root;
> >+
> >+ dentry = debugfs_create_file("mcast_dci_table", 0664,
> >+ lpriv->iface_debugfs,
> >+ &lowpan_priv(dev)->iphc_mcast_dci,
> >+ &lowpan_context_fops);
> >+ if (!dentry)
> >+ goto remove_root;
> >+
> > return 0;
> >+remove_root:
> >+ lowpan_dev_debugfs_exit(dev);
>
> Just to check here. This one is calling debugfs_remove_recursive(). This
> will cleanup states where we might have been able to create dci_table and
> sci_table but not mcast_dci_table, right?

Yes, should. I will remove the whole interface related debugfs entry
inclusive all sub-entries which was created under the interface related
directory entry which is in this case sci/dci/mcast_dci tables.

> > fail:
> > return -EINVAL;
> > }
> >diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
> >index 346b5c1..2c5004b 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)
> > {
> >@@ -259,30 +263,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->addr, ctx->prefix_len);
> >+ 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->addr, ctx->prefix_len);
> >+ 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->addr, ctx->prefix_len);
> >+ 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 +379,33 @@ 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;
> >+
> >+ if (address_mode != LOWPAN_IPHC_DAM_00)
> >+ return -EINVAL;
> >+
> >+ 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);
> >+ /* take prefix_len and network prefix from the context */
> >+ ipaddr->s6_addr[3] = ctx->prefix_len;
> >+ /* get network prefix to copy into multicast address */
> >+ ipv6_addr_prefix(&network_pfx, &ctx->addr, ctx->prefix_len);
> >+ /* setting network prefix */
> >+ memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
> >+
> >+ if (fail < 0)
> >+ return -EIO;
> >+
> >+ 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 +519,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 +530,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 +563,18 @@ 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)->iphc_sci.lock);
> >+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_sci,
> >+ LOWPAN_IPHC_CID_SCI(cid));
> >+ if (!ci) {
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.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)->iphc_sci.lock);
> > } else {
> > /* Source address uncompression */
> > pr_debug("source address stateless compression\n");
> >@@ -515,27 +586,54 @@ 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)->iphc_mcast_dci.lock);
> >+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_mcast_dci,
> >+ LOWPAN_IPHC_CID_DCI(cid));
> >+ if (!ci) {
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.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)->iphc_mcast_dci.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)->iphc_dci.lock);
> >+ ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_dci,
> >+ LOWPAN_IPHC_CID_DCI(cid));
> >+ if (!ci) {
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.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)->iphc_dci.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 +683,60 @@ 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
> >+ */
> Maybe put this comment on line. It should be short enough.
>

ok.

> >+ tmp.s6_addr[8] ^= 0x02;
> >+ /* context information are always used */
> >+ ipv6_addr_prefix_copy(&tmp, &ctx->addr, ctx->prefix_len);
> >+ 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->addr, ctx->prefix_len);
> >+ 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->addr, ctx->prefix_len);
> >+ 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 +860,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 +909,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 +937,47 @@ 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);
> >+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
> >+ spin_lock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> >+ dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_mcast_dci,
> >+ &hdr->daddr);
> >+ if (dci) {
> >+ memcpy(&dci_entry, dci, sizeof(*dci));
> >+ cid |= dci->id;
> >+ }
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> >+ } else {
> >+ spin_lock_bh(&lowpan_priv(dev)->iphc_dci.lock);
> >+ dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_dci,
> >+ &hdr->daddr);
> >+ if (dci) {
> >+ memcpy(&dci_entry, dci, sizeof(*dci));
> >+ cid |= dci->id;
> >+ }
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.lock);
> >+ }
> >+
> >+ spin_lock_bh(&lowpan_priv(dev)->iphc_sci.lock);
> >+ sci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_sci,
> >+ &hdr->saddr);
> >+ if (sci) {
> >+ memcpy(&sci_entry, sci, sizeof(*sci));
> >+ cid |= (sci->id << 4);
> >+ }
> >+ spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.lock);
> >+
> >+ if (sci || dci) {
> >+ 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 +1014,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);
> >+ }
> > }
> > }
> >@@ -871,3 +1096,143 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> > return 0;
> > }
> > EXPORT_SYMBOL_GPL(lowpan_header_compress);
> >+
> >+static bool lowpan_iphc_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
> >+{
> >+ /* prefix which are smaller than 64 bits are not valid, users
> >+ * may mean than a prefix which is filled with zero until 64th bit.
> >+ * Refere rfc6282 "Any remaining bits are zero." The remaining bits
> >+ * in this case where are the prefix(< 64) ends until IID starts.
> >+ */
> >+ if (ctx->prefix_len < 64)
> >+ return false;
> >+
> >+ return true;
> >+}
> >+

I will remove this check, the prefix_len should be any length, from 0
until 128. We need to care about the zero bits if prefix_len < 64 during
lookup.

I think a "valid" test would be to check on multicast address scope
which should not a valid prefix for dci/sci table. There might be more
addresses which should not valid here. At the moment I would say a
multicast address should be invalid. I will add this as well.

> >+static bool
> >+lowpan_iphc_mcast_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
> >+{
> >+ /* network prefix for multicast is at maximum 64 bits long */
> >+ if (ctx->prefix_len > 64)
> >+ return false;
> >+
> >+ return true;
> >+}
> >+
> >+static int lowpan_iphc_ctx_update(struct lowpan_iphc_ctx *table,
> >+ const struct lowpan_iphc_ctx *ctx)
> >+{
> >+ int ret = 0, i;
> >+
> >+ if (ctx->enabled) {
> >+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> >+ if (ctx->prefix_len != table[i].prefix_len)
> >+ continue;
> >+
> >+ if (ipv6_prefix_equal(&ctx->addr, &table[i].addr,
> >+ ctx->prefix_len)) {
> >+ if (table[i].enabled) {
> >+ ret = -EEXIST;
> >+ goto out;
> >+ }
> >+ }
> >+ }
> >+ }
> >+
> >+ memcpy(&table[ctx->id], ctx, sizeof(*ctx));
> >+
> >+out:
> >+ return ret;
> >+}
> >+
>
> In what cases should the update succeed? I'm wondering about some corner
> cases here:
>
> o If the new ctx is disabled it would update the table on its cid no matter
> what.
> o If the new ctx is enabled we would update it only if we do not find the
> same prefix in an enabled state already.
>
> To put it differently, the only case we do not update the table with the
> context is if it already exists and is enabled. This is how you want it?
>

yes. If we don't check if it's enabled (inside the table), then we could
not simple copy one line (by doing cat and echo) with a context which
is disabled, then simple replace the disabled(0) to enabled(1). Then it
would fail because the same prefix is already there, but when it's
disabled then we ignore if the prefix is already exists. It will not
used then anyway.

- Alex

2015-12-02 14:18:06

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 4/4] 6lowpan: iphc: add support for stateful compression

Hello.

A bit more review here. Still haven't tested the code.

On 29/11/15 12:34, Alexander Aring wrote:
> This patch introduce support for IPHC stateful address compression. It
> will offer three debugfs per interface entries, which are:
>
> - dci_table: destination context indentifier table
> - sci_table: source context indentifier table
> - mcast_sci_table: multicast context identifier table
>
> Example to setup a context id:
>
> A "cat /sys/kernel/debug/6lowpan/lowpan0/dci_table" will display all
> contexts which are available. Example:
>
> ID ipv6-address/prefix-length enabled
> 0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 15 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>
> For setting a context e.g. context id 0, context 2001::, prefix-length
> 64.
>
> Hint: Simple copy one line and then maniuplate it.
>
> echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 1" >
> /sys/kernel/debug/6lowpan/lowpan0/dci_table
>
> The enabled column will display currently if the context is disabled(0) or
> enabled(1).
>
> On transmit side:
>
> The IPHC code will automatically search for a context which would be
> match for the address. Then it will be use the context with the
> best compression method. Means the longest prefix which match will be
> used.
>
> Example:
>
> 2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the
> last bit of the address which has the prefix 2001::/127 is the same like
> the IID from the Encapsulating Header. A context ID can also be a
> 2001::1/128, which is then a full ipv6 address.
>
> On Receive side:
>
> If there is a context defined (when CID not available then it's the
> default context 0) then it will be used, if the header doesn't set
> SAC or DAC bit thens, it will be dropped.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 54 ++++++
> net/6lowpan/6lowpan_i.h | 3 +
> net/6lowpan/core.c | 17 +-
> net/6lowpan/debugfs.c | 113 ++++++++++++
> net/6lowpan/iphc.c | 479 ++++++++++++++++++++++++++++++++++++++++++------
> 5 files changed, 608 insertions(+), 58 deletions(-)
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index 2f6a3f2..dec5427 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_CI_TABLE_SIZE (1 << 4)
>
> #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
> #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
> @@ -98,9 +100,37 @@ enum lowpan_lltypes {
> LOWPAN_LLTYPE_IEEE802154,
> };
>
> +struct lowpan_iphc_ctx {
> + u8 id;
> + struct in6_addr addr;
> + u8 prefix_len;
> + bool enabled;
> +};
> +
> +struct lowpan_iphc_ctx_ops {
> + bool (*valid_prefix)(const struct lowpan_iphc_ctx *ctx);
> + int (*update)(struct lowpan_iphc_ctx *table,
> + const struct lowpan_iphc_ctx *ctx);
> + struct lowpan_iphc_ctx *(*get_by_id)(const struct net_device *dev,
> + struct lowpan_iphc_ctx *table,
> + u8 id);
> + struct lowpan_iphc_ctx *(*get_by_addr)(const struct net_device *dev,
> + struct lowpan_iphc_ctx *table,
> + const struct in6_addr *addr);
> +};
> +
> +struct lowpan_iphc_ctx_table {
> + spinlock_t lock;
> + const struct lowpan_iphc_ctx_ops *ops;
> + struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE];
> +};
> +
> struct lowpan_priv {
> enum lowpan_lltypes lltype;
> struct dentry *iface_debugfs;
> + struct lowpan_iphc_ctx_table iphc_dci;
> + struct lowpan_iphc_ctx_table iphc_sci;
> + struct lowpan_iphc_ctx_table iphc_mcast_dci;
>
> /* must be last */
> u8 priv[0] __aligned(sizeof(void *));
> @@ -125,6 +155,30 @@ struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
> return (struct lowpan_802154_cb *)skb->cb;
> }
>
> +static inline int lowpan_ctx_update(struct lowpan_iphc_ctx_table *t,
> + const struct lowpan_iphc_ctx *ctx)
> +{
> + if (!t->ops->valid_prefix(ctx))
> + return -EINVAL;
> +
> + return t->ops->update(t->table, ctx);
> +}
> +
> +static inline struct lowpan_iphc_ctx *
> +lowpan_ctx_by_id(const struct net_device *dev, struct lowpan_iphc_ctx_table *t,
> + u8 id)
> +{
> + return t->ops->get_by_id(dev, t->table, id);
> +}
> +
> +static inline struct lowpan_iphc_ctx *
> +lowpan_ctx_by_addr(const struct net_device *dev,
> + struct lowpan_iphc_ctx_table *t,
> + const struct in6_addr *addr)
> +{
> + return t->ops->get_by_addr(dev, t->table, addr);
> +}
> +
> #ifdef DEBUG
> /* print data in line */
> static inline void raw_dump_inline(const char *caller, char *msg,
> diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
> index d16bb4b..2c275be 100644
> --- a/net/6lowpan/6lowpan_i.h
> +++ b/net/6lowpan/6lowpan_i.h
> @@ -3,6 +3,9 @@
>
> #include <linux/netdevice.h>
>
> +extern const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops;
> +extern const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops;
> +
> #ifdef CONFIG_6LOWPAN_DEBUGFS
> int lowpan_dev_debugfs_init(struct net_device *dev);
> void lowpan_dev_debugfs_exit(struct net_device *dev);
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index c7f06f5..f3dbd18 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,21 @@ int lowpan_register_netdevice(struct net_device *dev,
>
> lowpan_priv(dev)->lltype = lltype;
>
> + spin_lock_init(&lowpan_priv(dev)->iphc_dci.lock);
> + lowpan_priv(dev)->iphc_dci.ops = &iphc_ctx_unicast_ops;
> +
> + spin_lock_init(&lowpan_priv(dev)->iphc_sci.lock);
> + lowpan_priv(dev)->iphc_sci.ops = &iphc_ctx_unicast_ops;
> +
> + spin_lock_init(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> + lowpan_priv(dev)->iphc_mcast_dci.ops = &iphc_ctx_mcast_ops;
> +
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + lowpan_priv(dev)->iphc_dci.table[i].id = i;
> + lowpan_priv(dev)->iphc_sci.table[i].id = i;
> + lowpan_priv(dev)->iphc_mcast_dci.table[i].id = i;
> + }
> +
> ret = lowpan_dev_debugfs_init(dev);
> if (ret < 0)
> return ret;
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
> index 88eef84..24f94ac 100644
> --- a/net/6lowpan/debugfs.c
> +++ b/net/6lowpan/debugfs.c
> @@ -16,19 +16,132 @@
>
> #include "6lowpan_i.h"
>
> +#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
> +
> static struct dentry *lowpan_debugfs;
>
> +static int lowpan_context_show(struct seq_file *file, void *offset)
> +{
> + struct lowpan_iphc_ctx_table *t = file->private;
> + int i;
> +
> + seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
> + "enabled");
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + seq_printf(file,
> + "%-2d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %d\n",
> + t->table[i].id,
> + be16_to_cpu(t->table[i].addr.s6_addr16[0]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[1]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[2]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[3]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[4]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[5]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[6]),
> + be16_to_cpu(t->table[i].addr.s6_addr16[7]),
> + t->table[i].prefix_len, t->table[i].enabled);
> + }
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_context_dbgfs_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_context_show, inode->i_private);
> +}
> +
> +static ssize_t lowpan_context_dbgfs_write(struct file *fp,
> + const char __user *user_buf,
> + size_t count, loff_t *ppos)
> +{
> + char buf[128] = { };

Do we enforce the 128 char limit somewhere?

> + struct seq_file *file = fp->private_data;
> + struct lowpan_iphc_ctx_table *t = file->private;
> + struct lowpan_iphc_ctx ctx;
> + int status = count, n, id, enabled, i, prefix_len, ret;
> + unsigned int addr[8];
> +
> + if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
> + count))) {

I see we do here, ok.

> + status = -EFAULT;
> + goto out;
> + }
> +
> + n = sscanf(buf, "%d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %d",
> + &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
> + &addr[5], &addr[6], &addr[7], &prefix_len, &enabled);
> + if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
> + status = -EIO;
> + goto out;
> + }
> +
> + if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 ||
> + (enabled != 0 && enabled != 1) || prefix_len > 128) {

Hmm, if you would use a bool for enabled here instead of an int the
check on enabled would not be needed.

> + status = -EINVAL;
> + goto out;
> + }
> +
> + ctx.id = id;
> + ctx.enabled = enabled;
> + ctx.prefix_len = prefix_len;
> +
> + for (i = 0; i < 8; i++)
> + ctx.addr.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
> +
> + spin_lock_bh(&t->lock);
> + ret = lowpan_ctx_update(t, &ctx);
> + if (ret < 0)
> + status = ret;
> + spin_unlock_bh(&t->lock);
> +
> +out:
> + return status;
> +}
> +
> +const struct file_operations lowpan_context_fops = {
> + .open = lowpan_context_dbgfs_open,
> + .read = seq_read,
> + .write = lowpan_context_dbgfs_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> int lowpan_dev_debugfs_init(struct net_device *dev)
> {
> struct lowpan_priv *lpriv = lowpan_priv(dev);
> + static struct dentry *dentry;
>
> /* creating the root */
> lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
> if (!lpriv->iface_debugfs)
> goto fail;
>
> + dentry = debugfs_create_file("dci_table", 0664, lpriv->iface_debugfs,

Any special reason you want the group have write permissions? I would
expect a 644 here. Same for the other two.

> + &lowpan_priv(dev)->iphc_dci,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> + dentry = debugfs_create_file("sci_table", 0664, lpriv->iface_debugfs,
> + &lowpan_priv(dev)->iphc_sci,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> + dentry = debugfs_create_file("mcast_dci_table", 0664,
> + lpriv->iface_debugfs,
> + &lowpan_priv(dev)->iphc_mcast_dci,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> return 0;
>
> +remove_root:
> + lowpan_dev_debugfs_exit(dev);

Just to check here. This one is calling debugfs_remove_recursive(). This
will cleanup states where we might have been able to create dci_table
and sci_table but not mcast_dci_table, right?
> fail:
> return -EINVAL;
> }
> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
> index 346b5c1..2c5004b 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)
> {
> @@ -259,30 +263,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->addr, ctx->prefix_len);
> + 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->addr, ctx->prefix_len);
> + 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->addr, ctx->prefix_len);
> + 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 +379,33 @@ 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;
> +
> + if (address_mode != LOWPAN_IPHC_DAM_00)
> + return -EINVAL;
> +
> + 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);
> + /* take prefix_len and network prefix from the context */
> + ipaddr->s6_addr[3] = ctx->prefix_len;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &ctx->addr, ctx->prefix_len);
> + /* setting network prefix */
> + memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
> +
> + if (fail < 0)
> + return -EIO;
> +
> + 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 +519,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 +530,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 +563,18 @@ 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)->iphc_sci.lock);
> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_sci,
> + LOWPAN_IPHC_CID_SCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.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)->iphc_sci.lock);
> } else {
> /* Source address uncompression */
> pr_debug("source address stateless compression\n");
> @@ -515,27 +586,54 @@ 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)->iphc_mcast_dci.lock);
> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_mcast_dci,
> + LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.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)->iphc_mcast_dci.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)->iphc_dci.lock);
> + ci = lowpan_ctx_by_id(dev, &lowpan_priv(dev)->iphc_dci,
> + LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.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)->iphc_dci.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 +683,60 @@ 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
> + */
Maybe put this comment on line. It should be short enough.

> + tmp.s6_addr[8] ^= 0x02;
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->addr, ctx->prefix_len);
> + 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->addr, ctx->prefix_len);
> + 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->addr, ctx->prefix_len);
> + 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 +860,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 +909,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 +937,47 @@ 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);
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
> + spin_lock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> + dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_mcast_dci,
> + &hdr->daddr);
> + if (dci) {
> + memcpy(&dci_entry, dci, sizeof(*dci));
> + cid |= dci->id;
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_mcast_dci.lock);
> + } else {
> + spin_lock_bh(&lowpan_priv(dev)->iphc_dci.lock);
> + dci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_dci,
> + &hdr->daddr);
> + if (dci) {
> + memcpy(&dci_entry, dci, sizeof(*dci));
> + cid |= dci->id;
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_dci.lock);
> + }
> +
> + spin_lock_bh(&lowpan_priv(dev)->iphc_sci.lock);
> + sci = lowpan_ctx_by_addr(dev, &lowpan_priv(dev)->iphc_sci,
> + &hdr->saddr);
> + if (sci) {
> + memcpy(&sci_entry, sci, sizeof(*sci));
> + cid |= (sci->id << 4);
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->iphc_sci.lock);
> +
> + if (sci || dci) {
> + 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 +1014,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);
> + }
> }
> }
>
> @@ -871,3 +1096,143 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> return 0;
> }
> EXPORT_SYMBOL_GPL(lowpan_header_compress);
> +
> +static bool lowpan_iphc_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
> +{
> + /* prefix which are smaller than 64 bits are not valid, users
> + * may mean than a prefix which is filled with zero until 64th bit.
> + * Refere rfc6282 "Any remaining bits are zero." The remaining bits
> + * in this case where are the prefix(< 64) ends until IID starts.
> + */
> + if (ctx->prefix_len < 64)
> + return false;
> +
> + return true;
> +}
> +
> +static bool
> +lowpan_iphc_mcast_ctx_valid_prefix(const struct lowpan_iphc_ctx *ctx)
> +{
> + /* network prefix for multicast is at maximum 64 bits long */
> + if (ctx->prefix_len > 64)
> + return false;
> +
> + return true;
> +}
> +
> +static int lowpan_iphc_ctx_update(struct lowpan_iphc_ctx *table,
> + const struct lowpan_iphc_ctx *ctx)
> +{
> + int ret = 0, i;
> +
> + if (ctx->enabled) {
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + if (ctx->prefix_len != table[i].prefix_len)
> + continue;
> +
> + if (ipv6_prefix_equal(&ctx->addr, &table[i].addr,
> + ctx->prefix_len)) {
> + if (table[i].enabled) {
> + ret = -EEXIST;
> + goto out;
> + }
> + }
> + }
> + }
> +
> + memcpy(&table[ctx->id], ctx, sizeof(*ctx));
> +
> +out:
> + return ret;
> +}
> +

In what cases should the update succeed? I'm wondering about some corner
cases here:

o If the new ctx is disabled it would update the table on its cid no
matter what.
o If the new ctx is enabled we would update it only if we do not find
the same prefix in an enabled state already.

To put it differently, the only case we do not update the table with
the context is if it already exists and is enabled. This is how you want it?


> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_id(const struct net_device *dev,
> + struct lowpan_iphc_ctx *table, u8 id)
> +{
> + struct lowpan_iphc_ctx *ret = NULL;
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CI_TABLE_SIZE);
> +
> + if (table[id].enabled)
> + ret = &table[id];
> +
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
> + struct lowpan_iphc_ctx *table,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_prefix;
> + int i;
> +
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + ipv6_addr_prefix(&addr_prefix, addr, table[i].prefix_len);
> +
> + if (ipv6_prefix_equal(&addr_prefix, &table[i].addr,
> + table[i].prefix_len)) {
> + if (table[i].enabled) {
> + /* remember first match */
> + if (!ret) {
> + ret = &table[i];
> + continue;
> + }
> +
> + /* get the context with longest prefix_len */
> + if (table[i].prefix_len > ret->prefix_len)
> + ret = &table[i];
> + }
> + }
> + }
> +
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
> + struct lowpan_iphc_ctx *table,
> + const struct in6_addr *addr)
> +{
> + 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_CI_TABLE_SIZE; i++) {
> + /* setting plen */
> + addr_mcast.s6_addr[3] = table[i].prefix_len;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &table[i].addr,
> + table[i].prefix_len);
> + /* setting network prefix */
> + memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
> +
> + if (ipv6_addr_equal(addr, &addr_mcast)) {
> + if (table[i].enabled) {
> + ret = &table[i];
> + break;
> + }
> + }
> + }
> +
> + return ret;
> +}
> +
> +const struct lowpan_iphc_ctx_ops iphc_ctx_unicast_ops = {
> + .valid_prefix = lowpan_iphc_ctx_valid_prefix,
> + .update = lowpan_iphc_ctx_update,
> + .get_by_id = lowpan_iphc_ctx_get_by_id,
> + .get_by_addr = lowpan_iphc_ctx_get_by_addr,
> +};
> +
> +const struct lowpan_iphc_ctx_ops iphc_ctx_mcast_ops = {
> + .valid_prefix = lowpan_iphc_mcast_ctx_valid_prefix,
> + .update = lowpan_iphc_ctx_update,
> + .get_by_id = lowpan_iphc_ctx_get_by_id,
> + .get_by_addr = lowpan_iphc_ctx_get_by_mcast_addr,
> +};

regards
Stefan Schmidt

2015-12-01 20:50:11

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 2/4] 6lowpan: add debugfs support

Hello.

On 29/11/15 12:34, Alexander Aring wrote:
> This patch will introduce a 6lowpan entry into the debugfs if enabled.
> Inside this 6lowpan directory we create a subdirectories of all 6lowpan
> interfaces to offer a per interface debugfs support.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 3 +++
> net/6lowpan/6lowpan_i.h | 28 ++++++++++++++++++++++++++
> net/6lowpan/Kconfig | 8 ++++++++
> net/6lowpan/Makefile | 1 +
> net/6lowpan/core.c | 28 +++++++++++++++++++++++++-
> net/6lowpan/debugfs.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 120 insertions(+), 1 deletion(-)
> create mode 100644 net/6lowpan/6lowpan_i.h
> create mode 100644 net/6lowpan/debugfs.c
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index 730211f..2f6a3f2 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -53,6 +53,8 @@
> #ifndef __6LOWPAN_H__
> #define __6LOWPAN_H__
>
> +#include <linux/debugfs.h>
> +
> #include <net/ipv6.h>
> #include <net/net_namespace.h>
>
> @@ -98,6 +100,7 @@ enum lowpan_lltypes {
>
> struct lowpan_priv {
> enum lowpan_lltypes lltype;
> + struct dentry *iface_debugfs;
>
> /* must be last */
> u8 priv[0] __aligned(sizeof(void *));
> diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
> new file mode 100644
> index 0000000..d16bb4b
> --- /dev/null
> +++ b/net/6lowpan/6lowpan_i.h
> @@ -0,0 +1,28 @@
> +#ifndef __6LOWPAN_I_H
> +#define __6LOWPAN_I_H
> +
> +#include <linux/netdevice.h>
> +
> +#ifdef CONFIG_6LOWPAN_DEBUGFS
> +int lowpan_dev_debugfs_init(struct net_device *dev);
> +void lowpan_dev_debugfs_exit(struct net_device *dev);
> +
> +int __init lowpan_debugfs_init(void);
> +void lowpan_debugfs_exit(void);
> +#else
> +static inline int lowpan_dev_debugfs_init(struct net_device *dev)
> +{
> + return 0;
> +}
> +
> +static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
> +
> +static inline int __init lowpan_debugfs_init(void)
> +{
> + return 0;
> +}
> +
> +static inline void lowpan_debugfs_exit(void) { }
> +#endif /* CONFIG_6LOWPAN_DEBUGFS */
> +
> +#endif /* __6LOWPAN_I_H */
> diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
> index 7fa0f38..7ecedd7 100644
> --- a/net/6lowpan/Kconfig
> +++ b/net/6lowpan/Kconfig
> @@ -5,6 +5,14 @@ menuconfig 6LOWPAN
> This enables IPv6 over Low power Wireless Personal Area Network -
> "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
>
> +config 6LOWPAN_DEBUGFS
> + bool "6LoWPAN debugfs support"
> + depends on 6LOWPAN
> + depends on DEBUG_FS
> + ---help---
> + This enables 6LoWPAN debugfs support. For example to manipulate
> + IPHC context information at runtime.
> +
> menuconfig 6LOWPAN_NHC
> tristate "Next Header Compression Support"
> depends on 6LOWPAN
> diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
> index c6ffc55..54cad8d 100644
> --- a/net/6lowpan/Makefile
> +++ b/net/6lowpan/Makefile
> @@ -1,6 +1,7 @@
> obj-$(CONFIG_6LOWPAN) += 6lowpan.o
>
> 6lowpan-y := core.o iphc.o nhc.o
> +6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
>
> #rfc6282 nhcs
> obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index 80fc509..c7f06f5 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -15,9 +15,13 @@
>
> #include <net/6lowpan.h>
>
> +#include "6lowpan_i.h"
> +
> int lowpan_register_netdevice(struct net_device *dev,
> enum lowpan_lltypes lltype)
> {
> + int ret;
> +
> dev->addr_len = EUI64_ADDR_LEN;
> dev->type = ARPHRD_6LOWPAN;
> dev->mtu = IPV6_MIN_MTU;
> @@ -25,7 +29,15 @@ int lowpan_register_netdevice(struct net_device *dev,
>
> lowpan_priv(dev)->lltype = lltype;
>
> - return register_netdevice(dev);
> + ret = lowpan_dev_debugfs_init(dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = register_netdevice(dev);
> + if (ret < 0)
> + lowpan_dev_debugfs_exit(dev);
> +
> + return ret;
> }
> EXPORT_SYMBOL(lowpan_register_netdevice);
>
> @@ -44,6 +56,7 @@ EXPORT_SYMBOL(lowpan_register_netdev);
> void lowpan_unregister_netdevice(struct net_device *dev)
> {
> unregister_netdevice(dev);
> + lowpan_dev_debugfs_exit(dev);
> }
> EXPORT_SYMBOL(lowpan_unregister_netdevice);
>
> @@ -57,6 +70,12 @@ EXPORT_SYMBOL(lowpan_unregister_netdev);
>
> static int __init lowpan_module_init(void)
> {
> + int ret;
> +
> + ret = lowpan_debugfs_init();
> + if (ret < 0)
> + return ret;
> +
> request_module_nowait("ipv6");
>
> request_module_nowait("nhc_dest");
> @@ -69,6 +88,13 @@ static int __init lowpan_module_init(void)
>
> return 0;
> }
> +
> +static void __exit lowpan_module_exit(void)
> +{
> + lowpan_debugfs_exit();
> +}
> +
> module_init(lowpan_module_init);
> +module_exit(lowpan_module_exit);
>
> MODULE_LICENSE("GPL");
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
> new file mode 100644
> index 0000000..88eef84
> --- /dev/null
> +++ b/net/6lowpan/debugfs.c
> @@ -0,0 +1,53 @@
> +/* This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * Authors:
> + * (C) 2015 Pengutronix, Alexander Aring <[email protected]>
> + * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
> + */
> +
> +#include <net/6lowpan.h>
> +
> +#include "6lowpan_i.h"
> +
> +static struct dentry *lowpan_debugfs;
> +
> +int lowpan_dev_debugfs_init(struct net_device *dev)
> +{
> + struct lowpan_priv *lpriv = lowpan_priv(dev);
> +
> + /* creating the root */
> + lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
> + if (!lpriv->iface_debugfs)
> + goto fail;
> +
> + return 0;
> +
> +fail:
> + return -EINVAL;
> +}
> +
> +void lowpan_dev_debugfs_exit(struct net_device *dev)
> +{
> + debugfs_remove_recursive(lowpan_priv(dev)->iface_debugfs);
> +}
> +
> +int __init lowpan_debugfs_init(void)
> +{
> + lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
> + if (!lowpan_debugfs)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +void lowpan_debugfs_exit(void)
> +{
> + debugfs_remove_recursive(lowpan_debugfs);
> +}

With the Kconfig fix merged in here from v2 there is nothing wrong with
this from my side. Like the first patch I think this is ready to be
submitted as PATCH and applied.

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

regards
Stefan Schmidt

2015-12-01 20:38:26

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 1/4] 6lowpan: add lowpan dev register helpers

Hello.

On 29/11/15 12:34, Alexander Aring wrote:
> This patch introduces register and unregister functionality for lowpan
> interfaces. While register a lowpan interface there are several things
> which need to be initialize by the 6lowpan subsystem. Upcoming
> functionality need to register/unregister per interface components e.g.
> debugfs entry.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 7 ++++++-
> net/6lowpan/core.c | 33 +++++++++++++++++++++++++++++++--
> net/bluetooth/6lowpan.c | 8 +++-----
> net/ieee802154/6lowpan/core.c | 6 ++----
> 4 files changed, 42 insertions(+), 12 deletions(-)
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index cf3bc56..730211f 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -185,7 +185,12 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
> *hc_ptr += len;
> }
>
> -void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
> +int lowpan_register_netdevice(struct net_device *dev,
> + enum lowpan_lltypes lltype);
> +int lowpan_register_netdev(struct net_device *dev,
> + enum lowpan_lltypes lltype);
> +void lowpan_unregister_netdevice(struct net_device *dev);
> +void lowpan_unregister_netdev(struct net_device *dev);
>
> /**
> * lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index 83b19e0..80fc509 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -15,7 +15,8 @@
>
> #include <net/6lowpan.h>
>
> -void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
> +int lowpan_register_netdevice(struct net_device *dev,
> + enum lowpan_lltypes lltype)
> {
> dev->addr_len = EUI64_ADDR_LEN;
> dev->type = ARPHRD_6LOWPAN;
> @@ -23,8 +24,36 @@ void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
> dev->priv_flags |= IFF_NO_QUEUE;
>
> lowpan_priv(dev)->lltype = lltype;
> +
> + return register_netdevice(dev);
> +}
> +EXPORT_SYMBOL(lowpan_register_netdevice);
> +
> +int lowpan_register_netdev(struct net_device *dev,
> + enum lowpan_lltypes lltype)
> +{
> + int ret;
> +
> + rtnl_lock();
> + ret = lowpan_register_netdevice(dev, lltype);
> + rtnl_unlock();
> + return ret;
> +}
> +EXPORT_SYMBOL(lowpan_register_netdev);
> +
> +void lowpan_unregister_netdevice(struct net_device *dev)
> +{
> + unregister_netdevice(dev);
> +}
> +EXPORT_SYMBOL(lowpan_unregister_netdevice);
> +
> +void lowpan_unregister_netdev(struct net_device *dev)
> +{
> + rtnl_lock();
> + lowpan_unregister_netdevice(dev);
> + rtnl_unlock();
> }
> -EXPORT_SYMBOL(lowpan_netdev_setup);
> +EXPORT_SYMBOL(lowpan_unregister_netdev);
>
> static int __init lowpan_module_init(void)
> {
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index 9e9cca3..d040365 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -825,9 +825,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
> list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
> spin_unlock(&devices_lock);
>
> - lowpan_netdev_setup(netdev, LOWPAN_LLTYPE_BTLE);
> -
> - err = register_netdev(netdev);
> + err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
> if (err < 0) {
> BT_INFO("register_netdev failed %d", err);
> spin_lock(&devices_lock);
> @@ -890,7 +888,7 @@ static void delete_netdev(struct work_struct *work)
> struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
> delete_netdev);
>
> - unregister_netdev(entry->netdev);
> + lowpan_unregister_netdev(entry->netdev);
>
> /* The entry pointer is deleted by the netdev destructor. */
> }
> @@ -1348,7 +1346,7 @@ static void disconnect_devices(void)
> ifdown(entry->netdev);
> BT_DBG("Unregistering netdev %s %p",
> entry->netdev->name, entry->netdev);
> - unregister_netdev(entry->netdev);
> + lowpan_unregister_netdev(entry->netdev);
> kfree(entry);
> }
> }
> diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
> index 20c49c7..737c87a 100644
> --- a/net/ieee802154/6lowpan/core.c
> +++ b/net/ieee802154/6lowpan/core.c
> @@ -161,9 +161,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
> wdev->needed_headroom;
> ldev->needed_tailroom = wdev->needed_tailroom;
>
> - lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
> -
> - ret = register_netdevice(ldev);
> + ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
> if (ret < 0) {
> dev_put(wdev);
> return ret;
> @@ -180,7 +178,7 @@ static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
> ASSERT_RTNL();
>
> wdev->ieee802154_ptr->lowpan_dev = NULL;
> - unregister_netdevice(ldev);
> + lowpan_unregister_netdevice(ldev);
> dev_put(wdev);
> }
>

That is better compared to what you had in v2. I think this one is ready
for a real PATCH and applying.

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

regards
Stefan Schmidt

2015-12-01 11:51:25

by Lukasz Duda

[permalink] [raw]
Subject: RE: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy


> -----Original Message-----
> From: Alexander Aring [mailto:[email protected]]
> Sent: Sunday, November 29, 2015 12:35
> To: [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; Duda, Lukasz;
> [email protected]; Alexander Aring; David S . Miller; Alexey
> Kuznetsov; James Morris; Hideaki YOSHIFUJI; Patrick McHardy
> Subject: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy
>
> This patch adds a static inline function ipv6_addr_prefix_copy which copies a
> ipv6 address prefix(argument pfx) into the ipv6 address prefix.
> The prefix len is given by plen as bits. This function mainly based on
> ipv6_addr_prefix which copies one address prefix from address into a new
> ipv6 address destination and zero all other address bits.
>
> The difference is that ipv6_addr_prefix_copy don't get a prefix from an
> ipv6 address, it sets a prefix to an ipv6 address with keeping other address
> bits. The use case is for context based address compression inside 6LoWPAN
> IPHC header which keeping ipv6 prefixes inside a context table to lookup
> address-bits without sending them.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/ipv6.h | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e1a10b0..cd3881e6
> 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -382,6 +382,21 @@ static inline void ipv6_addr_prefix(struct in6_addr
> *pfx,
> pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); }
>
> +static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
> + const struct in6_addr *pfx,
> + int plen)
> +{
> + /* caller must guarantee 0 <= plen <= 128 */
> + int o = plen >> 3,
> + b = plen & 0x7;
> +
> + memcpy(addr->s6_addr, pfx, o);
> + if (b != 0) {
> + addr->s6_addr[o] &= ~(0xff00 >> b);
> + addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
> + }
> +}
> +
> static inline void __ipv6_addr_set_half(__be32 *addr,
> __be32 wh, __be32 wl)
> {
> --
> 2.6.1

Acked-by: ?ukasz Duda <[email protected]>

2015-12-01 11:38:01

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy

Hello.

On 29/11/15 12:34, Alexander Aring wrote:
> This patch adds a static inline function ipv6_addr_prefix_copy which
> copies a ipv6 address prefix(argument pfx) into the ipv6 address prefix.
> The prefix len is given by plen as bits. This function mainly based on
> ipv6_addr_prefix which copies one address prefix from address into a new
> ipv6 address destination and zero all other address bits.
>
> The difference is that ipv6_addr_prefix_copy don't get a prefix from an
> ipv6 address, it sets a prefix to an ipv6 address with keeping other
> address bits. The use case is for context based address compression
> inside 6LoWPAN IPHC header which keeping ipv6 prefixes inside a context
> table to lookup address-bits without sending them.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/ipv6.h | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index e1a10b0..cd3881e6 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -382,6 +382,21 @@ static inline void ipv6_addr_prefix(struct in6_addr *pfx,
> pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
> }
>
> +static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
> + const struct in6_addr *pfx,
> + int plen)
> +{
> + /* caller must guarantee 0 <= plen <= 128 */
> + int o = plen >> 3,
> + b = plen & 0x7;
> +
> + memcpy(addr->s6_addr, pfx, o);
> + if (b != 0) {
> + addr->s6_addr[o] &= ~(0xff00 >> b);
> + addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
> + }
> +}
> +
> static inline void __ipv6_addr_set_half(__be32 *addr,
> __be32 wh, __be32 wl)
> {

Thanks for taking up the review comments. You can add my r-b now:

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

regards
Stefan Schmidt

2015-12-01 11:17:19

by Hideaki Yoshifuji

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy

Hannes Frederic Sowa wrote:
>
>
> On Sun, Nov 29, 2015, at 12:34, Alexander Aring wrote:
>> This patch adds a static inline function ipv6_addr_prefix_copy which
>> copies a ipv6 address prefix(argument pfx) into the ipv6 address prefix.
>> The prefix len is given by plen as bits. This function mainly based on
>> ipv6_addr_prefix which copies one address prefix from address into a new
>> ipv6 address destination and zero all other address bits.
>>
>> The difference is that ipv6_addr_prefix_copy don't get a prefix from an
>> ipv6 address, it sets a prefix to an ipv6 address with keeping other
>> address bits. The use case is for context based address compression
>> inside 6LoWPAN IPHC header which keeping ipv6 prefixes inside a context
>> table to lookup address-bits without sending them.
>>
>> Cc: David S. Miller <[email protected]>
>> Cc: Alexey Kuznetsov <[email protected]>
>> Cc: James Morris <[email protected]>
>> Cc: Hideaki YOSHIFUJI <[email protected]>
>> Cc: Patrick McHardy <[email protected]>
>> Signed-off-by: Alexander Aring <[email protected]>
>> ---
>> include/net/ipv6.h | 15 +++++++++++++++
>> 1 file changed, 15 insertions(+)
>>
>> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
>> index e1a10b0..cd3881e6 100644
>> --- a/include/net/ipv6.h
>> +++ b/include/net/ipv6.h
>> @@ -382,6 +382,21 @@ static inline void ipv6_addr_prefix(struct in6_addr
>> *pfx,
>> pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
>> }
>>
>> +static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
>> + const struct in6_addr *pfx,
>> + int plen)
>> +{
>> + /* caller must guarantee 0 <= plen <= 128 */
>> + int o = plen >> 3,
>> + b = plen & 0x7;
>> +
>> + memcpy(addr->s6_addr, pfx, o);
>> + if (b != 0) {
>> + addr->s6_addr[o] &= ~(0xff00 >> b);
>> + addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
>> + }
>> +}
>> +
>
> Acked-by: Hannes Frederic Sowa <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>


> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

--
$B5HF#1QL@(B <[email protected]>
$B%_%i%/%k!&%j%J%C%/%93t<02q<R(B $B5;=QK\It(B $B%5%]!<%HIt(B

2015-12-01 10:56:26

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [RFCv3 bluetooth-next 3/4] ipv6: add ipv6_addr_prefix_copy



On Sun, Nov 29, 2015, at 12:34, Alexander Aring wrote:
> This patch adds a static inline function ipv6_addr_prefix_copy which
> copies a ipv6 address prefix(argument pfx) into the ipv6 address prefix.
> The prefix len is given by plen as bits. This function mainly based on
> ipv6_addr_prefix which copies one address prefix from address into a new
> ipv6 address destination and zero all other address bits.
>
> The difference is that ipv6_addr_prefix_copy don't get a prefix from an
> ipv6 address, it sets a prefix to an ipv6 address with keeping other
> address bits. The use case is for context based address compression
> inside 6LoWPAN IPHC header which keeping ipv6 prefixes inside a context
> table to lookup address-bits without sending them.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/ipv6.h | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index e1a10b0..cd3881e6 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -382,6 +382,21 @@ static inline void ipv6_addr_prefix(struct in6_addr
> *pfx,
> pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
> }
>
> +static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
> + const struct in6_addr *pfx,
> + int plen)
> +{
> + /* caller must guarantee 0 <= plen <= 128 */
> + int o = plen >> 3,
> + b = plen & 0x7;
> +
> + memcpy(addr->s6_addr, pfx, o);
> + if (b != 0) {
> + addr->s6_addr[o] &= ~(0xff00 >> b);
> + addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
> + }
> +}
> +

Acked-by: Hannes Frederic Sowa <[email protected]>