Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __6LOWPAN_I_H
#define __6LOWPAN_I_H
#include <linux/netdevice.h>
#include <net/6lowpan.h>
/* caller need to be sure it's dev->type is ARPHRD_6LOWPAN */
static inline bool lowpan_is_ll(const struct net_device *dev,
enum lowpan_lltypes lltype)
{
return lowpan_dev(dev)->lltype == lltype;
}
extern const struct ndisc_ops lowpan_ndisc_ops;
int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev);
#ifdef CONFIG_6LOWPAN_DEBUGFS
void lowpan_dev_debugfs_init(struct net_device *dev);
void lowpan_dev_debugfs_exit(struct net_device *dev);
void __init lowpan_debugfs_init(void);
void lowpan_debugfs_exit(void);
#else
static inline void lowpan_dev_debugfs_init(struct net_device *dev) { }
static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
static inline void __init lowpan_debugfs_init(void) { }
static inline void lowpan_debugfs_exit(void) { }
#endif /* CONFIG_6LOWPAN_DEBUGFS */
#endif /* __6LOWPAN_I_H */
+105
View File
@@ -0,0 +1,105 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig 6LOWPAN
tristate "6LoWPAN Support"
depends on IPV6
help
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
config 6LOWPAN_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 and Generic Header Compression Support"
depends on 6LOWPAN
default y
help
Support for next header and generic header compression defined in
RFC6282 and RFC7400.
if 6LOWPAN_NHC
config 6LOWPAN_NHC_DEST
tristate "Destination Options Header Support"
default y
help
6LoWPAN IPv6 Destination Options Header compression according to
RFC6282.
config 6LOWPAN_NHC_FRAGMENT
tristate "Fragment Header Support"
default y
help
6LoWPAN IPv6 Fragment Header compression according to RFC6282.
config 6LOWPAN_NHC_HOP
tristate "Hop-by-Hop Options Header Support"
default y
help
6LoWPAN IPv6 Hop-by-Hop Options Header compression according to
RFC6282.
config 6LOWPAN_NHC_IPV6
tristate "IPv6 Header Support"
default y
help
6LoWPAN IPv6 Header compression according to RFC6282.
config 6LOWPAN_NHC_MOBILITY
tristate "Mobility Header Support"
default y
help
6LoWPAN IPv6 Mobility Header compression according to RFC6282.
config 6LOWPAN_NHC_ROUTING
tristate "Routing Header Support"
default y
help
6LoWPAN IPv6 Routing Header compression according to RFC6282.
config 6LOWPAN_NHC_UDP
tristate "UDP Header Support"
default y
help
6LoWPAN IPv6 UDP Header compression according to RFC6282.
config 6LOWPAN_GHC_EXT_HDR_HOP
tristate "GHC Hop-by-Hop Options Header Support"
help
6LoWPAN IPv6 Hop-by-Hop option generic header compression according
to RFC7400.
config 6LOWPAN_GHC_UDP
tristate "GHC UDP Support"
help
6LoWPAN IPv6 UDP generic header compression according to RFC7400.
config 6LOWPAN_GHC_ICMPV6
tristate "GHC ICMPv6 Support"
help
6LoWPAN IPv6 ICMPv6 generic header compression according to RFC7400.
config 6LOWPAN_GHC_EXT_HDR_DEST
tristate "GHC Destination Options Header Support"
help
6LoWPAN IPv6 destination option generic header compression according
to RFC7400.
config 6LOWPAN_GHC_EXT_HDR_FRAG
tristate "GHC Fragmentation Options Header Support"
help
6LoWPAN IPv6 fragmentation option generic header compression
according to RFC7400.
config 6LOWPAN_GHC_EXT_HDR_ROUTE
tristate "GHC Routing Options Header Support"
help
6LoWPAN IPv6 routing option generic header compression according
to RFC7400.
endif
+22
View File
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_6LOWPAN) += 6lowpan.o
6lowpan-y := core.o iphc.o nhc.o ndisc.o
6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
#rfc6282 nhcs
obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
obj-$(CONFIG_6LOWPAN_NHC_FRAGMENT) += nhc_fragment.o
obj-$(CONFIG_6LOWPAN_NHC_HOP) += nhc_hop.o
obj-$(CONFIG_6LOWPAN_NHC_IPV6) += nhc_ipv6.o
obj-$(CONFIG_6LOWPAN_NHC_MOBILITY) += nhc_mobility.o
obj-$(CONFIG_6LOWPAN_NHC_ROUTING) += nhc_routing.o
obj-$(CONFIG_6LOWPAN_NHC_UDP) += nhc_udp.o
#rfc7400 ghcs
obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_HOP) += nhc_ghc_ext_hop.o
obj-$(CONFIG_6LOWPAN_GHC_UDP) += nhc_ghc_udp.o
obj-$(CONFIG_6LOWPAN_GHC_ICMPV6) += nhc_ghc_icmpv6.o
obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_DEST) += nhc_ghc_ext_dest.o
obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG) += nhc_ghc_ext_frag.o
obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE) += nhc_ghc_ext_route.o
+183
View File
@@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Authors:
* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
*/
#include <linux/if_arp.h>
#include <linux/module.h>
#include <net/6lowpan.h>
#include <net/addrconf.h>
#include "6lowpan_i.h"
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
int i, ret;
switch (lltype) {
case LOWPAN_LLTYPE_IEEE802154:
dev->addr_len = EUI64_ADDR_LEN;
break;
case LOWPAN_LLTYPE_BTLE:
dev->addr_len = ETH_ALEN;
break;
}
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
lowpan_dev(dev)->lltype = lltype;
spin_lock_init(&lowpan_dev(dev)->ctx.lock);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
lowpan_dev(dev)->ctx.table[i].id = i;
dev->ndisc_ops = &lowpan_ndisc_ops;
ret = register_netdevice(dev);
if (ret < 0)
return ret;
lowpan_dev_debugfs_init(dev);
return ret;
}
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);
lowpan_dev_debugfs_exit(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_unregister_netdev);
int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
{
struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
/* Set short_addr autoconfiguration if short_addr is present only */
if (!lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
return -1;
/* For either address format, all zero addresses MUST NOT be used */
if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
wpan_dev->short_addr == cpu_to_le16(0x0000))
return -1;
/* Alternatively, if no PAN ID is known, 16 zero bits may be used */
if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
memset(eui, 0, 2);
else
ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
/* The "Universal/Local" (U/L) bit shall be set to zero */
eui[0] &= ~2;
eui[2] = 0;
eui[3] = 0xFF;
eui[4] = 0xFE;
eui[5] = 0;
ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
return 0;
}
static int lowpan_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct inet6_dev *idev;
struct in6_addr addr;
int i;
if (dev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
idev = __in6_dev_get(dev);
if (!idev)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
case NETDEV_CHANGE:
/* (802.15.4 6LoWPAN short address slaac handling */
if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
__ipv6_addr_set_half(&addr.s6_addr32[0],
htonl(0xFE800000), 0);
addrconf_add_linklocal(idev, &addr, 0);
}
break;
case NETDEV_DOWN:
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
&lowpan_dev(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;
lowpan_debugfs_init();
ret = register_netdevice_notifier(&lowpan_notifier);
if (ret < 0) {
lowpan_debugfs_exit();
return ret;
}
request_module_nowait("nhc_dest");
request_module_nowait("nhc_fragment");
request_module_nowait("nhc_hop");
request_module_nowait("nhc_ipv6");
request_module_nowait("nhc_mobility");
request_module_nowait("nhc_routing");
request_module_nowait("nhc_udp");
return 0;
}
static void __exit lowpan_module_exit(void)
{
lowpan_debugfs_exit();
unregister_netdevice_notifier(&lowpan_notifier);
}
module_init(lowpan_module_init);
module_exit(lowpan_module_exit);
MODULE_DESCRIPTION("IPv6 over Low-Power Wireless Personal Area Network core module");
MODULE_LICENSE("GPL");
+278
View File
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Authors:
* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*/
#include <net/6lowpan.h>
#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_DEBUGFS_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_DEBUGFS_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_DEBUGFS_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;
}
static 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 void lowpan_dev_debugfs_ctx_init(struct net_device *dev,
struct dentry *ctx, u8 id)
{
struct lowpan_dev *ldev = lowpan_dev(dev);
struct dentry *root;
char buf[32];
if (WARN_ON_ONCE(id >= LOWPAN_IPHC_CTX_TABLE_SIZE))
return;
sprintf(buf, "%d", id);
root = debugfs_create_dir(buf, ctx);
debugfs_create_file("active", 0644, root, &ldev->ctx.table[id],
&lowpan_ctx_flag_active_fops);
debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id],
&lowpan_ctx_flag_c_fops);
debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id],
&lowpan_ctx_pfx_fops);
debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id],
&lowpan_ctx_plen_fops);
}
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;
}
DEFINE_SHOW_ATTRIBUTE(lowpan_context);
static int lowpan_short_addr_get(void *data, u64 *val)
{
struct wpan_dev *wdev = data;
rtnl_lock();
*val = le16_to_cpu(wdev->short_addr);
rtnl_unlock();
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL,
"0x%04llx\n");
static void lowpan_dev_debugfs_802154_init(const struct net_device *dev,
struct lowpan_dev *ldev)
{
struct dentry *root;
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
return;
root = debugfs_create_dir("ieee802154", ldev->iface_debugfs);
debugfs_create_file("short_addr", 0444, root,
lowpan_802154_dev(dev)->wdev->ieee802154_ptr,
&lowpan_short_addr_fops);
}
void lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_dev *ldev = lowpan_dev(dev);
struct dentry *contexts;
int i;
/* creating the root */
ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
contexts = debugfs_create_dir("contexts", ldev->iface_debugfs);
debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx,
&lowpan_context_fops);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
lowpan_dev_debugfs_ctx_init(dev, contexts, i);
lowpan_dev_debugfs_802154_init(dev, ldev);
}
void lowpan_dev_debugfs_exit(struct net_device *dev)
{
debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs);
}
void __init lowpan_debugfs_init(void)
{
lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
}
void lowpan_debugfs_exit(void)
{
debugfs_remove_recursive(lowpan_debugfs);
}
File diff suppressed because it is too large Load Diff
+223
View File
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Authors:
* (C) 2016 Pengutronix, Alexander Aring <aar@pengutronix.de>
*/
#include <net/6lowpan.h>
#include <net/addrconf.h>
#include <net/ndisc.h>
#include "6lowpan_i.h"
#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
#define NDISC_802154_SHORT_ADDR_LENGTH 1
static int lowpan_ndisc_parse_802154_options(const struct net_device *dev,
struct nd_opt_hdr *nd_opt,
struct ndisc_options *ndopts)
{
switch (nd_opt->nd_opt_len) {
case NDISC_802154_SHORT_ADDR_LENGTH:
if (ndopts->nd_802154_opt_array[nd_opt->nd_opt_type])
net_dbg_ratelimited("%s: duplicated short addr ND6 option found: type=%d\n",
__func__, nd_opt->nd_opt_type);
else
ndopts->nd_802154_opt_array[nd_opt->nd_opt_type] = nd_opt;
return 1;
default:
/* all others will be handled by ndisc IPv6 option parsing */
return 0;
}
}
static int lowpan_ndisc_parse_options(const struct net_device *dev,
struct nd_opt_hdr *nd_opt,
struct ndisc_options *ndopts)
{
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
return 0;
switch (nd_opt->nd_opt_type) {
case ND_OPT_SOURCE_LL_ADDR:
case ND_OPT_TARGET_LL_ADDR:
return lowpan_ndisc_parse_802154_options(dev, nd_opt, ndopts);
default:
return 0;
}
}
static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
u8 icmp6_type,
const struct ndisc_options *ndopts)
{
struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
u8 *lladdr_short = NULL;
switch (icmp6_type) {
case NDISC_ROUTER_SOLICITATION:
case NDISC_ROUTER_ADVERTISEMENT:
case NDISC_NEIGHBOUR_SOLICITATION:
if (ndopts->nd_802154_opts_src_lladdr) {
lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_src_lladdr,
IEEE802154_SHORT_ADDR_LEN, 0);
if (!lladdr_short) {
net_dbg_ratelimited("NA: invalid short link-layer address length\n");
return;
}
}
break;
case NDISC_REDIRECT:
case NDISC_NEIGHBOUR_ADVERTISEMENT:
if (ndopts->nd_802154_opts_tgt_lladdr) {
lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_tgt_lladdr,
IEEE802154_SHORT_ADDR_LEN, 0);
if (!lladdr_short) {
net_dbg_ratelimited("NA: invalid short link-layer address length\n");
return;
}
}
break;
default:
break;
}
write_lock_bh(&n->lock);
if (lladdr_short) {
ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr))
neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
}
write_unlock_bh(&n->lock);
}
static void lowpan_ndisc_update(const struct net_device *dev,
struct neighbour *n, u32 flags, u8 icmp6_type,
const struct ndisc_options *ndopts)
{
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
return;
/* react on overrides only. TODO check if this is really right. */
if (flags & NEIGH_UPDATE_F_OVERRIDE)
lowpan_ndisc_802154_update(n, flags, icmp6_type, ndopts);
}
static int lowpan_ndisc_opt_addr_space(const struct net_device *dev,
u8 icmp6_type, struct neighbour *neigh,
u8 *ha_buf, u8 **ha)
{
struct lowpan_802154_neigh *n;
struct wpan_dev *wpan_dev;
int addr_space = 0;
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
return 0;
switch (icmp6_type) {
case NDISC_REDIRECT:
n = lowpan_802154_neigh(neighbour_priv(neigh));
read_lock_bh(&neigh->lock);
if (lowpan_802154_is_valid_src_short_addr(n->short_addr)) {
memcpy(ha_buf, &n->short_addr,
IEEE802154_SHORT_ADDR_LEN);
read_unlock_bh(&neigh->lock);
addr_space += __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
*ha = ha_buf;
} else {
read_unlock_bh(&neigh->lock);
}
break;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
case NDISC_NEIGHBOUR_SOLICITATION:
case NDISC_ROUTER_SOLICITATION:
wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
addr_space = __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
break;
default:
break;
}
return addr_space;
}
static void lowpan_ndisc_fill_addr_option(const struct net_device *dev,
struct sk_buff *skb, u8 icmp6_type,
const u8 *ha)
{
struct wpan_dev *wpan_dev;
__be16 short_addr;
u8 opt_type;
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
return;
switch (icmp6_type) {
case NDISC_REDIRECT:
if (ha) {
ieee802154_le16_to_be16(&short_addr, ha);
__ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
&short_addr,
IEEE802154_SHORT_ADDR_LEN, 0);
}
return;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
opt_type = ND_OPT_TARGET_LL_ADDR;
break;
case NDISC_ROUTER_SOLICITATION:
case NDISC_NEIGHBOUR_SOLICITATION:
opt_type = ND_OPT_SOURCE_LL_ADDR;
break;
default:
return;
}
wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
ieee802154_le16_to_be16(&short_addr,
&wpan_dev->short_addr);
__ndisc_fill_addr_option(skb, opt_type, &short_addr,
IEEE802154_SHORT_ADDR_LEN, 0);
}
}
static void lowpan_ndisc_prefix_rcv_add_addr(struct net *net,
struct net_device *dev,
const struct prefix_info *pinfo,
struct inet6_dev *in6_dev,
struct in6_addr *addr,
int addr_type, u32 addr_flags,
bool sllao, bool tokenized,
__u32 valid_lft,
u32 prefered_lft,
bool dev_addr_generated)
{
int err;
/* generates short based address for RA PIO's */
if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) && dev_addr_generated &&
!addrconf_ifid_802154_6lowpan(addr->s6_addr + 8, dev)) {
err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
addr, addr_type, addr_flags,
sllao, tokenized, valid_lft,
prefered_lft);
if (err)
net_dbg_ratelimited("RA: could not add a short address based address for prefix: %pI6c\n",
&pinfo->prefix);
}
}
#endif
const struct ndisc_ops lowpan_ndisc_ops = {
#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
.parse_options = lowpan_ndisc_parse_options,
.update = lowpan_ndisc_update,
.opt_addr_space = lowpan_ndisc_opt_addr_space,
.fill_addr_option = lowpan_ndisc_fill_addr_option,
.prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr,
#endif
};
+169
View File
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN next header compression
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
*/
#include <linux/netdevice.h>
#include <net/ipv6.h>
#include "nhc.h"
static const struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1];
static DEFINE_SPINLOCK(lowpan_nhc_lock);
static const struct lowpan_nhc *lowpan_nhc_by_nhcid(struct sk_buff *skb)
{
const struct lowpan_nhc *nhc;
int i;
u8 id;
if (!pskb_may_pull(skb, 1))
return NULL;
id = *skb->data;
for (i = 0; i < NEXTHDR_MAX + 1; i++) {
nhc = lowpan_nexthdr_nhcs[i];
if (!nhc)
continue;
if ((id & nhc->idmask) == nhc->id)
return nhc;
}
return NULL;
}
int lowpan_nhc_check_compression(struct sk_buff *skb,
const struct ipv6hdr *hdr, u8 **hc_ptr)
{
const struct lowpan_nhc *nhc;
int ret = 0;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
if (!(nhc && nhc->compress))
ret = -ENOENT;
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
u8 **hc_ptr)
{
int ret;
const struct lowpan_nhc *nhc;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
/* check if the nhc module was removed in unlocked part.
* TODO: this is a workaround we should prevent unloading
* of nhc modules while unlocked part, this will always drop
* the lowpan packet but it's very unlikely.
*
* Solution isn't easy because we need to decide at
* lowpan_nhc_check_compression if we do a compression or not.
* Because the inline data which is added to skb, we can't move this
* handling.
*/
if (unlikely(!nhc || !nhc->compress)) {
ret = -EINVAL;
goto out;
}
/* In the case of RAW sockets the transport header is not set by
* the ip6 stack so we must set it ourselves
*/
if (skb->transport_header == skb->network_header)
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
ret = nhc->compress(skb, hc_ptr);
if (ret < 0)
goto out;
/* skip the transport header */
skb_pull(skb, nhc->nexthdrlen);
out:
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
int lowpan_nhc_do_uncompression(struct sk_buff *skb,
const struct net_device *dev,
struct ipv6hdr *hdr)
{
const struct lowpan_nhc *nhc;
int ret;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nhc_by_nhcid(skb);
if (nhc) {
if (nhc->uncompress) {
ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
nhc->nexthdrlen);
if (ret < 0) {
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
} else {
netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
nhc->name);
spin_unlock_bh(&lowpan_nhc_lock);
return -ENOTSUPP;
}
} else {
spin_unlock_bh(&lowpan_nhc_lock);
netdev_warn(dev, "received unknown nhc id which was not found.\n");
return -ENOENT;
}
hdr->nexthdr = nhc->nexthdr;
skb_reset_transport_header(skb);
raw_dump_table(__func__, "raw transport header dump",
skb_transport_header(skb), nhc->nexthdrlen);
spin_unlock_bh(&lowpan_nhc_lock);
return 0;
}
int lowpan_nhc_add(const struct lowpan_nhc *nhc)
{
int ret = 0;
spin_lock_bh(&lowpan_nhc_lock);
if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
ret = -EEXIST;
goto out;
}
lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
out:
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
EXPORT_SYMBOL(lowpan_nhc_add);
void lowpan_nhc_del(const struct lowpan_nhc *nhc)
{
spin_lock_bh(&lowpan_nhc_lock);
lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
spin_unlock_bh(&lowpan_nhc_lock);
synchronize_net();
}
EXPORT_SYMBOL(lowpan_nhc_del);
+133
View File
@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __6LOWPAN_NHC_H
#define __6LOWPAN_NHC_H
#include <linux/skbuff.h>
#include <linux/rbtree.h>
#include <linux/module.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
/**
* LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
*
* @__nhc: variable name of the lowpan_nhc struct.
* @_name: const char * of common header compression name.
* @_nexthdr: ipv6 nexthdr field for the header compression.
* @_nexthdrlen: ipv6 nexthdr len for the reserved space.
* @_id: one byte nhc id value.
* @_idmask: one byte nhc id mask value.
* @_uncompress: callback for uncompression call.
* @_compress: callback for compression call.
*/
#define LOWPAN_NHC(__nhc, _name, _nexthdr, \
_hdrlen, _id, _idmask, \
_uncompress, _compress) \
static const struct lowpan_nhc __nhc = { \
.name = _name, \
.nexthdr = _nexthdr, \
.nexthdrlen = _hdrlen, \
.id = _id, \
.idmask = _idmask, \
.uncompress = _uncompress, \
.compress = _compress, \
}
#define module_lowpan_nhc(__nhc) \
static int __init __nhc##_init(void) \
{ \
return lowpan_nhc_add(&(__nhc)); \
} \
module_init(__nhc##_init); \
static void __exit __nhc##_exit(void) \
{ \
lowpan_nhc_del(&(__nhc)); \
} \
module_exit(__nhc##_exit);
/**
* struct lowpan_nhc - hold 6lowpan next hdr compression ifnformation
*
* @name: name of the specific next header compression
* @nexthdr: next header value of the protocol which should be compressed.
* @nexthdrlen: ipv6 nexthdr len for the reserved space.
* @id: one byte nhc id value.
* @idmask: one byte nhc id mask value.
* @compress: callback to do the header compression.
* @uncompress: callback to do the header uncompression.
*/
struct lowpan_nhc {
const char *name;
u8 nexthdr;
size_t nexthdrlen;
u8 id;
u8 idmask;
int (*uncompress)(struct sk_buff *skb, size_t needed);
int (*compress)(struct sk_buff *skb, u8 **hc_ptr);
};
/**
* lowpan_nhc_by_nexthdr - return the 6lowpan nhc by ipv6 nexthdr.
*
* @nexthdr: ipv6 nexthdr value.
*/
struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr);
/**
* lowpan_nhc_check_compression - checks if we support compression format. If
* we support the nhc by nexthdr field, the function will return 0. If we
* don't support the nhc by nexthdr this function will return -ENOENT.
*
* @skb: skb of 6LoWPAN header to read nhc and replace header.
* @hdr: ipv6hdr to check the nexthdr value
* @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
* replaced header.
*/
int lowpan_nhc_check_compression(struct sk_buff *skb,
const struct ipv6hdr *hdr, u8 **hc_ptr);
/**
* lowpan_nhc_do_compression - calling compress callback for nhc
*
* @skb: skb of 6LoWPAN header to read nhc and replace header.
* @hdr: ipv6hdr to set the nexthdr value
* @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
* replaced header.
*/
int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
u8 **hc_ptr);
/**
* lowpan_nhc_do_uncompression - calling uncompress callback for nhc
*
* @nhc: 6LoWPAN nhc context, get by lowpan_nhc_by_ functions.
* @skb: skb of 6LoWPAN header, skb->data should be pointed to nhc id value.
* @dev: netdevice for print logging information.
* @hdr: ipv6hdr for setting nexthdr value.
*/
int lowpan_nhc_do_uncompression(struct sk_buff *skb,
const struct net_device *dev,
struct ipv6hdr *hdr);
/**
* lowpan_nhc_add - register a next header compression to framework
*
* @nhc: nhc which should be add.
*/
int lowpan_nhc_add(const struct lowpan_nhc *nhc);
/**
* lowpan_nhc_del - delete a next header compression from framework
*
* @nhc: nhc which should be delete.
*/
void lowpan_nhc_del(const struct lowpan_nhc *nhc);
/**
* lowpan_nhc_init - adding all default nhcs
*/
void lowpan_nhc_init(void);
#endif /* __6LOWPAN_NHC_H */
+17
View File
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Destination Options Header compression according to
* RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_DEST_ID_0 0xe6
#define LOWPAN_NHC_DEST_MASK_0 0xfe
LOWPAN_NHC(nhc_dest, "RFC6282 Destination Options", NEXTHDR_DEST, 0,
LOWPAN_NHC_DEST_ID_0, LOWPAN_NHC_DEST_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_dest);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Destination Options compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Fragment Header compression according to RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_FRAGMENT_ID_0 0xe4
#define LOWPAN_NHC_FRAGMENT_MASK_0 0xfe
LOWPAN_NHC(nhc_fragment, "RFC6282 Fragment", NEXTHDR_FRAGMENT, 0,
LOWPAN_NHC_FRAGMENT_ID_0, LOWPAN_NHC_FRAGMENT_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_fragment);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Fragment compression");
MODULE_LICENSE("GPL");
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN Extension Header compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_EXT_DEST_ID_0 0xb6
#define LOWPAN_GHC_EXT_DEST_MASK_0 0xfe
LOWPAN_NHC(ghc_ext_dest, "RFC7400 Destination Extension Header", NEXTHDR_DEST,
0, LOWPAN_GHC_EXT_DEST_ID_0, LOWPAN_GHC_EXT_DEST_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_ext_dest);
MODULE_DESCRIPTION("6LoWPAN generic header destination extension compression");
MODULE_LICENSE("GPL");
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN Extension Header compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_EXT_FRAG_ID_0 0xb4
#define LOWPAN_GHC_EXT_FRAG_MASK_0 0xfe
LOWPAN_NHC(ghc_ext_frag, "RFC7400 Fragmentation Extension Header",
NEXTHDR_FRAGMENT, 0, LOWPAN_GHC_EXT_FRAG_ID_0,
LOWPAN_GHC_EXT_FRAG_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_ext_frag);
MODULE_DESCRIPTION("6LoWPAN generic header fragmentation extension compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN Extension Header compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_EXT_HOP_ID_0 0xb0
#define LOWPAN_GHC_EXT_HOP_MASK_0 0xfe
LOWPAN_NHC(ghc_ext_hop, "RFC7400 Hop-by-Hop Extension Header", NEXTHDR_HOP, 0,
LOWPAN_GHC_EXT_HOP_ID_0, LOWPAN_GHC_EXT_HOP_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_ext_hop);
MODULE_DESCRIPTION("6LoWPAN generic header hop-by-hop extension compression");
MODULE_LICENSE("GPL");
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN Extension Header compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_EXT_ROUTE_ID_0 0xb2
#define LOWPAN_GHC_EXT_ROUTE_MASK_0 0xfe
LOWPAN_NHC(ghc_ext_route, "RFC7400 Routing Extension Header", NEXTHDR_ROUTING,
0, LOWPAN_GHC_EXT_ROUTE_ID_0, LOWPAN_GHC_EXT_ROUTE_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_ext_route);
MODULE_DESCRIPTION("6LoWPAN generic header routing extension compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN ICMPv6 compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_ICMPV6_ID_0 0xdf
#define LOWPAN_GHC_ICMPV6_MASK_0 0xff
LOWPAN_NHC(ghc_icmpv6, "RFC7400 ICMPv6", NEXTHDR_ICMP, 0,
LOWPAN_GHC_ICMPV6_ID_0, LOWPAN_GHC_ICMPV6_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_icmpv6);
MODULE_DESCRIPTION("6LoWPAN generic header ICMPv6 compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN UDP compression according to RFC7400
*/
#include "nhc.h"
#define LOWPAN_GHC_UDP_ID_0 0xd0
#define LOWPAN_GHC_UDP_MASK_0 0xf8
LOWPAN_NHC(ghc_udp, "RFC7400 UDP", NEXTHDR_UDP, 0,
LOWPAN_GHC_UDP_ID_0, LOWPAN_GHC_UDP_MASK_0, NULL, NULL);
module_lowpan_nhc(ghc_udp);
MODULE_DESCRIPTION("6LoWPAN generic header UDP compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Hop-by-Hop Options Header compression according to RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_HOP_ID_0 0xe0
#define LOWPAN_NHC_HOP_MASK_0 0xfe
LOWPAN_NHC(nhc_hop, "RFC6282 Hop-by-Hop Options", NEXTHDR_HOP, 0,
LOWPAN_NHC_HOP_ID_0, LOWPAN_NHC_HOP_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_hop);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Hop-by-Hop Options compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Header compression according to RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_IPV6_ID_0 0xee
#define LOWPAN_NHC_IPV6_MASK_0 0xfe
LOWPAN_NHC(nhc_ipv6, "RFC6282 IPv6", NEXTHDR_IPV6, 0, LOWPAN_NHC_IPV6_ID_0,
LOWPAN_NHC_IPV6_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_ipv6);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 IPv6 compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Mobility Header compression according to RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_MOBILITY_ID_0 0xe8
#define LOWPAN_NHC_MOBILITY_MASK_0 0xfe
LOWPAN_NHC(nhc_mobility, "RFC6282 Mobility", NEXTHDR_MOBILITY, 0,
LOWPAN_NHC_MOBILITY_ID_0, LOWPAN_NHC_MOBILITY_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_mobility);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Mobility compression");
MODULE_LICENSE("GPL");
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 Routing Header compression according to RFC6282
*/
#include "nhc.h"
#define LOWPAN_NHC_ROUTING_ID_0 0xe2
#define LOWPAN_NHC_ROUTING_MASK_0 0xfe
LOWPAN_NHC(nhc_routing, "RFC6282 Routing", NEXTHDR_ROUTING, 0,
LOWPAN_NHC_ROUTING_ID_0, LOWPAN_NHC_ROUTING_MASK_0, NULL, NULL);
module_lowpan_nhc(nhc_routing);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Routing compression");
MODULE_LICENSE("GPL");
+176
View File
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6LoWPAN IPv6 UDP compression according to RFC6282
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
*
* Original written by:
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
* Jon Smirl <jonsmirl@gmail.com>
*/
#include "nhc.h"
#define LOWPAN_NHC_UDP_MASK 0xF8
#define LOWPAN_NHC_UDP_ID 0xF0
#define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
#define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
#define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
#define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
/* values for port compression, _with checksum_ ie bit 5 set to 0 */
/* all inline */
#define LOWPAN_NHC_UDP_CS_P_00 0xF0
/* source 16bit inline, dest = 0xF0 + 8 bit inline */
#define LOWPAN_NHC_UDP_CS_P_01 0xF1
/* source = 0xF0 + 8bit inline, dest = 16 bit inline */
#define LOWPAN_NHC_UDP_CS_P_10 0xF2
/* source & dest = 0xF0B + 4bit inline */
#define LOWPAN_NHC_UDP_CS_P_11 0xF3
/* checksum elided */
#define LOWPAN_NHC_UDP_CS_C 0x04
static int udp_uncompress(struct sk_buff *skb, size_t needed)
{
u8 tmp = 0, val = 0;
struct udphdr uh;
bool fail;
int err;
fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
pr_debug("UDP header uncompression\n");
switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
case LOWPAN_NHC_UDP_CS_P_00:
fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
break;
case LOWPAN_NHC_UDP_CS_P_01:
fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
break;
case LOWPAN_NHC_UDP_CS_P_10:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
break;
case LOWPAN_NHC_UDP_CS_P_11:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
break;
default:
BUG();
}
pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
ntohs(uh.source), ntohs(uh.dest));
/* checksum */
if (tmp & LOWPAN_NHC_UDP_CS_C) {
pr_debug_ratelimited("checksum elided currently not supported\n");
fail = true;
} else {
fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
}
if (fail)
return -EINVAL;
/* UDP length needs to be inferred from the lower layers
* here, we obtain the hint from the remaining size of the
* frame
*/
switch (lowpan_dev(skb->dev)->lltype) {
case LOWPAN_LLTYPE_IEEE802154:
if (lowpan_802154_cb(skb)->d_size)
uh.len = htons(lowpan_802154_cb(skb)->d_size -
sizeof(struct ipv6hdr));
else
uh.len = htons(skb->len + sizeof(struct udphdr));
break;
default:
uh.len = htons(skb->len + sizeof(struct udphdr));
break;
}
pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
err = skb_cow(skb, needed);
if (unlikely(err))
return err;
skb_push(skb, sizeof(struct udphdr));
skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
return 0;
}
static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
{
const struct udphdr *uh = udp_hdr(skb);
u8 tmp;
if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT) &&
((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT)) {
pr_debug("UDP header: both ports compression to 4 bits\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_11;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source and destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of dest\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_01;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of source\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_10;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
} else {
pr_debug("UDP header: can't compress\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_00;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
}
/* checksum is always inline */
lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
return 0;
}
LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
LOWPAN_NHC_UDP_ID, LOWPAN_NHC_UDP_MASK, udp_uncompress, udp_compress);
module_lowpan_nhc(nhc_udp);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
MODULE_LICENSE("GPL");