Skip to content

Commit

Permalink
6lowpan: iphc: add support for stateful compression
Browse files Browse the repository at this point in the history
This patch introduce support for IPHC stateful address compression. It
will offer the context table via one debugfs entry.
This debugfs has and directory for each cid entry for the context table.
Inside each cid directory there exists the following files:

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

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

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

Reviewed-by: Stefan Schmidt <stefan@osg.samsung.com>
Signed-off-by: Alexander Aring <aar@pengutronix.de>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Alexander Aring authored and holtmann committed Feb 23, 2016
1 parent aef00c1 commit 5609c18
Show file tree
Hide file tree
Showing 4 changed files with 674 additions and 58 deletions.
32 changes: 32 additions & 0 deletions include/net/6lowpan.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
LOWPAN_IPHC_MAX_HEADER_LEN + \
LOWPAN_NHC_MAX_HDR_LEN)
/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)

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

enum lowpan_iphc_ctx_flags {
LOWPAN_IPHC_CTX_FLAG_ACTIVE,
LOWPAN_IPHC_CTX_FLAG_COMPRESSION,
};

struct lowpan_iphc_ctx {
u8 id;
struct in6_addr pfx;
u8 plen;
unsigned long flags;
};

struct lowpan_iphc_ctx_table {
spinlock_t lock;
const struct lowpan_iphc_ctx_ops *ops;
struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
};

static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
{
return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
}

static inline bool
lowpan_iphc_ctx_is_compression(const struct lowpan_iphc_ctx *ctx)
{
return test_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
}

struct lowpan_priv {
enum lowpan_lltypes lltype;
struct dentry *iface_debugfs;
struct lowpan_iphc_ctx_table ctx;

/* must be last */
u8 priv[0] __aligned(sizeof(void *));
Expand Down
39 changes: 38 additions & 1 deletion net/6lowpan/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

spin_lock_init(&lowpan_priv(dev)->ctx.lock);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
lowpan_priv(dev)->ctx.table[i].id = i;

ret = register_netdevice(dev);
if (ret < 0)
return ret;
Expand Down Expand Up @@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(lowpan_unregister_netdev);

static int lowpan_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
int i;

if (dev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;

switch (event) {
case NETDEV_DOWN:
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
&lowpan_priv(dev)->ctx.table[i].flags);
break;
default:
return NOTIFY_DONE;
}

return NOTIFY_OK;
}

static struct notifier_block lowpan_notifier = {
.notifier_call = lowpan_event,
};

static int __init lowpan_module_init(void)
{
int ret;
Expand All @@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
if (ret < 0)
return ret;

ret = register_netdevice_notifier(&lowpan_notifier);
if (ret < 0) {
lowpan_debugfs_exit();
return ret;
}

request_module_nowait("ipv6");

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

module_init(lowpan_module_init);
Expand Down
247 changes: 247 additions & 0 deletions net/6lowpan/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,266 @@

#include "6lowpan_i.h"

#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8

static struct dentry *lowpan_debugfs;

static int lowpan_ctx_flag_active_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;

if (val != 0 && val != 1)
return -EINVAL;

if (val)
set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
else
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);

return 0;
}

static int lowpan_ctx_flag_active_get(void *data, u64 *val)
{
*val = lowpan_iphc_ctx_is_active(data);
return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
lowpan_ctx_flag_active_get,
lowpan_ctx_flag_active_set, "%llu\n");

static int lowpan_ctx_flag_c_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;

if (val != 0 && val != 1)
return -EINVAL;

if (val)
set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
else
clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);

return 0;
}

static int lowpan_ctx_flag_c_get(void *data, u64 *val)
{
*val = lowpan_iphc_ctx_is_compression(data);
return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
lowpan_ctx_flag_c_set, "%llu\n");

static int lowpan_ctx_plen_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);

if (val > 128)
return -EINVAL;

spin_lock_bh(&t->lock);
ctx->plen = val;
spin_unlock_bh(&t->lock);

return 0;
}

static int lowpan_ctx_plen_get(void *data, u64 *val)
{
struct lowpan_iphc_ctx *ctx = data;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);

spin_lock_bh(&t->lock);
*val = ctx->plen;
spin_unlock_bh(&t->lock);
return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
lowpan_ctx_plen_set, "%llu\n");

static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
{
struct lowpan_iphc_ctx *ctx = file->private;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);

spin_lock_bh(&t->lock);
seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
be16_to_cpu(ctx->pfx.s6_addr16[0]),
be16_to_cpu(ctx->pfx.s6_addr16[1]),
be16_to_cpu(ctx->pfx.s6_addr16[2]),
be16_to_cpu(ctx->pfx.s6_addr16[3]),
be16_to_cpu(ctx->pfx.s6_addr16[4]),
be16_to_cpu(ctx->pfx.s6_addr16[5]),
be16_to_cpu(ctx->pfx.s6_addr16[6]),
be16_to_cpu(ctx->pfx.s6_addr16[7]));
spin_unlock_bh(&t->lock);

return 0;
}

static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
{
return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
}

static ssize_t lowpan_ctx_pfx_write(struct file *fp,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
char buf[128] = {};
struct seq_file *file = fp->private_data;
struct lowpan_iphc_ctx *ctx = file->private;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
int status = count, n, i;
unsigned int addr[8];

if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
count))) {
status = -EFAULT;
goto out;
}

n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
&addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
&addr[5], &addr[6], &addr[7]);
if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
status = -EINVAL;
goto out;
}

spin_lock_bh(&t->lock);
for (i = 0; i < 8; i++)
ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
spin_unlock_bh(&t->lock);

out:
return status;
}

const struct file_operations lowpan_ctx_pfx_fops = {
.open = lowpan_ctx_pfx_open,
.read = seq_read,
.write = lowpan_ctx_pfx_write,
.llseek = seq_lseek,
.release = single_release,
};

static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
struct dentry *ctx, u8 id)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
struct dentry *dentry, *root;
char buf[32];

WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);

sprintf(buf, "%d", id);

root = debugfs_create_dir(buf, ctx);
if (!root)
return -EINVAL;

dentry = debugfs_create_file("active", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_flag_active_fops);
if (!dentry)
return -EINVAL;

dentry = debugfs_create_file("compression", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_flag_c_fops);
if (!dentry)
return -EINVAL;

dentry = debugfs_create_file("prefix", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_pfx_fops);
if (!dentry)
return -EINVAL;

dentry = debugfs_create_file("prefix_len", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_plen_fops);
if (!dentry)
return -EINVAL;

return 0;
}

static int lowpan_context_show(struct seq_file *file, void *offset)
{
struct lowpan_iphc_ctx_table *t = file->private;
int i;

seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
seq_puts(file, "-------------------------------------------------\n");

spin_lock_bh(&t->lock);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
if (!lowpan_iphc_ctx_is_active(&t->table[i]))
continue;

seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
&t->table[i].pfx, t->table[i].plen,
lowpan_iphc_ctx_is_compression(&t->table[i]));
}
spin_unlock_bh(&t->lock);

return 0;
}

static int lowpan_context_open(struct inode *inode, struct file *file)
{
return single_open(file, lowpan_context_show, inode->i_private);
}

const struct file_operations lowpan_context_fops = {
.open = lowpan_context_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
struct dentry *contexts, *dentry;
int ret, i;

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

contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
if (!contexts)
goto remove_root;

dentry = debugfs_create_file("show", 0644, contexts,
&lowpan_priv(dev)->ctx,
&lowpan_context_fops);
if (!dentry)
goto remove_root;

for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
if (ret < 0)
goto remove_root;
}

return 0;

remove_root:
lowpan_dev_debugfs_exit(dev);
fail:
return -EINVAL;
}
Expand Down

0 comments on commit 5609c18

Please sign in to comment.