summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Aring <aar@pengutronix.de>2016-02-22 03:13:54 -0500
committerMarcel Holtmann <marcel@holtmann.org>2016-02-23 14:29:40 -0500
commit5609c185f24dffca5f6a9c127106869da150be03 (patch)
tree709e278dc1463646bf0db64b2f03511e3e4ec934
parentaef00c15b8c503083a703900a755fdb1cf2436e0 (diff)
6lowpan: iphc: add support for stateful compression
This patch introduce support for IPHC stateful address compression. It will offer the context table via one debugfs entry. This debugfs has and directory for each cid entry for the context table. Inside each cid directory there exists the following files: - "active": If the entry is added or deleted. The context table is original a list implementation, this flag will indicate if the context is part of list or not. - "prefix": The ipv6 prefix. - "prefix_length": The prefix length for the prefix. - "compression": The compression flag according RFC6775. This part should be moved into sysfs after some testing time. Also the debugfs entry contains a "show" file which is a pretty-printout for the current context table information. Reviewed-by: Stefan Schmidt <stefan@osg.samsung.com> Signed-off-by: Alexander Aring <aar@pengutronix.de> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r--include/net/6lowpan.h32
-rw-r--r--net/6lowpan/core.c39
-rw-r--r--net/6lowpan/debugfs.c247
-rw-r--r--net/6lowpan/iphc.c414
4 files changed, 674 insertions, 58 deletions
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2f6a3f2233ed..da3a77d25fcb 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -75,6 +75,8 @@
75#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \ 75#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
76 LOWPAN_IPHC_MAX_HEADER_LEN + \ 76 LOWPAN_IPHC_MAX_HEADER_LEN + \
77 LOWPAN_NHC_MAX_HDR_LEN) 77 LOWPAN_NHC_MAX_HDR_LEN)
78/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
79#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)
78 80
79#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ 81#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
80#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ 82#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
@@ -98,9 +100,39 @@ enum lowpan_lltypes {
98 LOWPAN_LLTYPE_IEEE802154, 100 LOWPAN_LLTYPE_IEEE802154,
99}; 101};
100 102
103enum lowpan_iphc_ctx_flags {
104 LOWPAN_IPHC_CTX_FLAG_ACTIVE,
105 LOWPAN_IPHC_CTX_FLAG_COMPRESSION,
106};
107
108struct lowpan_iphc_ctx {
109 u8 id;
110 struct in6_addr pfx;
111 u8 plen;
112 unsigned long flags;
113};
114
115struct lowpan_iphc_ctx_table {
116 spinlock_t lock;
117 const struct lowpan_iphc_ctx_ops *ops;
118 struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
119};
120
121static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
122{
123 return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
124}
125
126static inline bool
127lowpan_iphc_ctx_is_compression(const struct lowpan_iphc_ctx *ctx)
128{
129 return test_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
130}
131
101struct lowpan_priv { 132struct lowpan_priv {
102 enum lowpan_lltypes lltype; 133 enum lowpan_lltypes lltype;
103 struct dentry *iface_debugfs; 134 struct dentry *iface_debugfs;
135 struct lowpan_iphc_ctx_table ctx;
104 136
105 /* must be last */ 137 /* must be last */
106 u8 priv[0] __aligned(sizeof(void *)); 138 u8 priv[0] __aligned(sizeof(void *));
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index faf65baed617..34e44c0c0836 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -20,7 +20,7 @@
20int lowpan_register_netdevice(struct net_device *dev, 20int lowpan_register_netdevice(struct net_device *dev,
21 enum lowpan_lltypes lltype) 21 enum lowpan_lltypes lltype)
22{ 22{
23 int ret; 23 int i, ret;
24 24
25 dev->addr_len = EUI64_ADDR_LEN; 25 dev->addr_len = EUI64_ADDR_LEN;
26 dev->type = ARPHRD_6LOWPAN; 26 dev->type = ARPHRD_6LOWPAN;
@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,
29 29
30 lowpan_priv(dev)->lltype = lltype; 30 lowpan_priv(dev)->lltype = lltype;
31 31
32 spin_lock_init(&lowpan_priv(dev)->ctx.lock);
33 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
34 lowpan_priv(dev)->ctx.table[i].id = i;
35
32 ret = register_netdevice(dev); 36 ret = register_netdevice(dev);
33 if (ret < 0) 37 if (ret < 0)
34 return ret; 38 return ret;
@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
68} 72}
69EXPORT_SYMBOL(lowpan_unregister_netdev); 73EXPORT_SYMBOL(lowpan_unregister_netdev);
70 74
75static int lowpan_event(struct notifier_block *unused,
76 unsigned long event, void *ptr)
77{
78 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
79 int i;
80
81 if (dev->type != ARPHRD_6LOWPAN)
82 return NOTIFY_DONE;
83
84 switch (event) {
85 case NETDEV_DOWN:
86 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
87 clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
88 &lowpan_priv(dev)->ctx.table[i].flags);
89 break;
90 default:
91 return NOTIFY_DONE;
92 }
93
94 return NOTIFY_OK;
95}
96
97static struct notifier_block lowpan_notifier = {
98 .notifier_call = lowpan_event,
99};
100
71static int __init lowpan_module_init(void) 101static int __init lowpan_module_init(void)
72{ 102{
73 int ret; 103 int ret;
@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
76 if (ret < 0) 106 if (ret < 0)
77 return ret; 107 return ret;
78 108
109 ret = register_netdevice_notifier(&lowpan_notifier);
110 if (ret < 0) {
111 lowpan_debugfs_exit();
112 return ret;
113 }
114
79 request_module_nowait("ipv6"); 115 request_module_nowait("ipv6");
80 116
81 request_module_nowait("nhc_dest"); 117 request_module_nowait("nhc_dest");
@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
92static void __exit lowpan_module_exit(void) 128static void __exit lowpan_module_exit(void)
93{ 129{
94 lowpan_debugfs_exit(); 130 lowpan_debugfs_exit();
131 unregister_netdevice_notifier(&lowpan_notifier);
95} 132}
96 133
97module_init(lowpan_module_init); 134module_init(lowpan_module_init);
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index 88eef84df0fc..aa49ff4ce6fd 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -16,19 +16,266 @@
16 16
17#include "6lowpan_i.h" 17#include "6lowpan_i.h"
18 18
19#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
20
19static struct dentry *lowpan_debugfs; 21static struct dentry *lowpan_debugfs;
20 22
23static int lowpan_ctx_flag_active_set(void *data, u64 val)
24{
25 struct lowpan_iphc_ctx *ctx = data;
26
27 if (val != 0 && val != 1)
28 return -EINVAL;
29
30 if (val)
31 set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
32 else
33 clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
34
35 return 0;
36}
37
38static int lowpan_ctx_flag_active_get(void *data, u64 *val)
39{
40 *val = lowpan_iphc_ctx_is_active(data);
41 return 0;
42}
43
44DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
45 lowpan_ctx_flag_active_get,
46 lowpan_ctx_flag_active_set, "%llu\n");
47
48static int lowpan_ctx_flag_c_set(void *data, u64 val)
49{
50 struct lowpan_iphc_ctx *ctx = data;
51
52 if (val != 0 && val != 1)
53 return -EINVAL;
54
55 if (val)
56 set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
57 else
58 clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
59
60 return 0;
61}
62
63static int lowpan_ctx_flag_c_get(void *data, u64 *val)
64{
65 *val = lowpan_iphc_ctx_is_compression(data);
66 return 0;
67}
68
69DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
70 lowpan_ctx_flag_c_set, "%llu\n");
71
72static int lowpan_ctx_plen_set(void *data, u64 val)
73{
74 struct lowpan_iphc_ctx *ctx = data;
75 struct lowpan_iphc_ctx_table *t =
76 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
77
78 if (val > 128)
79 return -EINVAL;
80
81 spin_lock_bh(&t->lock);
82 ctx->plen = val;
83 spin_unlock_bh(&t->lock);
84
85 return 0;
86}
87
88static int lowpan_ctx_plen_get(void *data, u64 *val)
89{
90 struct lowpan_iphc_ctx *ctx = data;
91 struct lowpan_iphc_ctx_table *t =
92 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
93
94 spin_lock_bh(&t->lock);
95 *val = ctx->plen;
96 spin_unlock_bh(&t->lock);
97 return 0;
98}
99
100DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
101 lowpan_ctx_plen_set, "%llu\n");
102
103static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
104{
105 struct lowpan_iphc_ctx *ctx = file->private;
106 struct lowpan_iphc_ctx_table *t =
107 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
108
109 spin_lock_bh(&t->lock);
110 seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
111 be16_to_cpu(ctx->pfx.s6_addr16[0]),
112 be16_to_cpu(ctx->pfx.s6_addr16[1]),
113 be16_to_cpu(ctx->pfx.s6_addr16[2]),
114 be16_to_cpu(ctx->pfx.s6_addr16[3]),
115 be16_to_cpu(ctx->pfx.s6_addr16[4]),
116 be16_to_cpu(ctx->pfx.s6_addr16[5]),
117 be16_to_cpu(ctx->pfx.s6_addr16[6]),
118 be16_to_cpu(ctx->pfx.s6_addr16[7]));
119 spin_unlock_bh(&t->lock);
120
121 return 0;
122}
123
124static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
125{
126 return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
127}
128
129static ssize_t lowpan_ctx_pfx_write(struct file *fp,
130 const char __user *user_buf, size_t count,
131 loff_t *ppos)
132{
133 char buf[128] = {};
134 struct seq_file *file = fp->private_data;
135 struct lowpan_iphc_ctx *ctx = file->private;
136 struct lowpan_iphc_ctx_table *t =
137 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
138 int status = count, n, i;
139 unsigned int addr[8];
140
141 if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
142 count))) {
143 status = -EFAULT;
144 goto out;
145 }
146
147 n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
148 &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
149 &addr[5], &addr[6], &addr[7]);
150 if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
151 status = -EINVAL;
152 goto out;
153 }
154
155 spin_lock_bh(&t->lock);
156 for (i = 0; i < 8; i++)
157 ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
158 spin_unlock_bh(&t->lock);
159
160out:
161 return status;
162}
163
164const struct file_operations lowpan_ctx_pfx_fops = {
165 .open = lowpan_ctx_pfx_open,
166 .read = seq_read,
167 .write = lowpan_ctx_pfx_write,
168 .llseek = seq_lseek,
169 .release = single_release,
170};
171
172static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
173 struct dentry *ctx, u8 id)
174{
175 struct lowpan_priv *lpriv = lowpan_priv(dev);
176 struct dentry *dentry, *root;
177 char buf[32];
178
179 WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
180
181 sprintf(buf, "%d", id);
182
183 root = debugfs_create_dir(buf, ctx);
184 if (!root)
185 return -EINVAL;
186
187 dentry = debugfs_create_file("active", 0644, root,
188 &lpriv->ctx.table[id],
189 &lowpan_ctx_flag_active_fops);
190 if (!dentry)
191 return -EINVAL;
192
193 dentry = debugfs_create_file("compression", 0644, root,
194 &lpriv->ctx.table[id],
195 &lowpan_ctx_flag_c_fops);
196 if (!dentry)
197 return -EINVAL;
198
199 dentry = debugfs_create_file("prefix", 0644, root,
200 &lpriv->ctx.table[id],
201 &lowpan_ctx_pfx_fops);
202 if (!dentry)
203 return -EINVAL;
204
205 dentry = debugfs_create_file("prefix_len", 0644, root,
206 &lpriv->ctx.table[id],
207 &lowpan_ctx_plen_fops);
208 if (!dentry)
209 return -EINVAL;
210
211 return 0;
212}
213
214static int lowpan_context_show(struct seq_file *file, void *offset)
215{
216 struct lowpan_iphc_ctx_table *t = file->private;
217 int i;
218
219 seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
220 seq_puts(file, "-------------------------------------------------\n");
221
222 spin_lock_bh(&t->lock);
223 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
224 if (!lowpan_iphc_ctx_is_active(&t->table[i]))
225 continue;
226
227 seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
228 &t->table[i].pfx, t->table[i].plen,
229 lowpan_iphc_ctx_is_compression(&t->table[i]));
230 }
231 spin_unlock_bh(&t->lock);
232
233 return 0;
234}
235
236static int lowpan_context_open(struct inode *inode, struct file *file)
237{
238 return single_open(file, lowpan_context_show, inode->i_private);
239}
240
241const struct file_operations lowpan_context_fops = {
242 .open = lowpan_context_open,
243 .read = seq_read,
244 .llseek = seq_lseek,
245 .release = single_release,
246};
247
21int lowpan_dev_debugfs_init(struct net_device *dev) 248int lowpan_dev_debugfs_init(struct net_device *dev)
22{ 249{
23 struct lowpan_priv *lpriv = lowpan_priv(dev); 250 struct lowpan_priv *lpriv = lowpan_priv(dev);
251 struct dentry *contexts, *dentry;
252 int ret, i;
24 253
25 /* creating the root */ 254 /* creating the root */
26 lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); 255 lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
27 if (!lpriv->iface_debugfs) 256 if (!lpriv->iface_debugfs)
28 goto fail; 257 goto fail;
29 258
259 contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
260 if (!contexts)
261 goto remove_root;
262
263 dentry = debugfs_create_file("show", 0644, contexts,
264 &lowpan_priv(dev)->ctx,
265 &lowpan_context_fops);
266 if (!dentry)
267 goto remove_root;
268
269 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
270 ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
271 if (ret < 0)
272 goto remove_root;
273 }
274
30 return 0; 275 return 0;
31 276
277remove_root:
278 lowpan_dev_debugfs_exit(dev);
32fail: 279fail:
33 return -EINVAL; 280 return -EINVAL;
34} 281}
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 346b5c1a9185..d2a565cde4f4 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -56,6 +56,7 @@
56/* special link-layer handling */ 56/* special link-layer handling */
57#include <net/mac802154.h> 57#include <net/mac802154.h>
58 58
59#include "6lowpan_i.h"
59#include "nhc.h" 60#include "nhc.h"
60 61
61/* Values of fields within the IPHC encoding first byte */ 62/* Values of fields within the IPHC encoding first byte */
@@ -147,6 +148,9 @@
147 (((a)->s6_addr16[6]) == 0) && \ 148 (((a)->s6_addr16[6]) == 0) && \
148 (((a)->s6_addr[14]) == 0)) 149 (((a)->s6_addr[14]) == 0))
149 150
151#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
152#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
153
150static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr, 154static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
151 const void *lladdr) 155 const void *lladdr)
152{ 156{
@@ -195,6 +199,98 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
195 } 199 }
196} 200}
197 201
202static struct lowpan_iphc_ctx *
203lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
204{
205 struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
206
207 if (!lowpan_iphc_ctx_is_active(ret))
208 return NULL;
209
210 return ret;
211}
212
213static struct lowpan_iphc_ctx *
214lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
215 const struct in6_addr *addr)
216{
217 struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
218 struct lowpan_iphc_ctx *ret = NULL;
219 struct in6_addr addr_pfx;
220 u8 addr_plen;
221 int i;
222
223 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
224 /* Check if context is valid. A context that is not valid
225 * MUST NOT be used for compression.
226 */
227 if (!lowpan_iphc_ctx_is_active(&table[i]) ||
228 !lowpan_iphc_ctx_is_compression(&table[i]))
229 continue;
230
231 ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
232
233 /* if prefix len < 64, the remaining bits until 64th bit is
234 * zero. Otherwise we use table[i]->plen.
235 */
236 if (table[i].plen < 64)
237 addr_plen = 64;
238 else
239 addr_plen = table[i].plen;
240
241 if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
242 /* remember first match */
243 if (!ret) {
244 ret = &table[i];
245 continue;
246 }
247
248 /* get the context with longest prefix len */
249 if (table[i].plen > ret->plen)
250 ret = &table[i];
251 }
252 }
253
254 return ret;
255}
256
257static struct lowpan_iphc_ctx *
258lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
259 const struct in6_addr *addr)
260{
261 struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
262 struct lowpan_iphc_ctx *ret = NULL;
263 struct in6_addr addr_mcast, network_pfx = {};
264 int i;
265
266 /* init mcast address with */
267 memcpy(&addr_mcast, addr, sizeof(*addr));
268
269 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
270 /* Check if context is valid. A context that is not valid
271 * MUST NOT be used for compression.
272 */
273 if (!lowpan_iphc_ctx_is_active(&table[i]) ||
274 !lowpan_iphc_ctx_is_compression(&table[i]))
275 continue;
276
277 /* setting plen */
278 addr_mcast.s6_addr[3] = table[i].plen;
279 /* get network prefix to copy into multicast address */
280 ipv6_addr_prefix(&network_pfx, &table[i].pfx,
281 table[i].plen);
282 /* setting network prefix */
283 memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
284
285 if (ipv6_addr_equal(addr, &addr_mcast)) {
286 ret = &table[i];
287 break;
288 }
289 }
290
291 return ret;
292}
293
198/* Uncompress address function for source and 294/* Uncompress address function for source and
199 * destination address(non-multicast). 295 * destination address(non-multicast).
200 * 296 *
@@ -259,30 +355,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
259/* Uncompress address function for source context 355/* Uncompress address function for source context
260 * based address(non-multicast). 356 * based address(non-multicast).
261 */ 357 */
262static int uncompress_context_based_src_addr(struct sk_buff *skb, 358static int uncompress_ctx_addr(struct sk_buff *skb,
263 struct in6_addr *ipaddr, 359 const struct net_device *dev,
264 u8 address_mode) 360 const struct lowpan_iphc_ctx *ctx,
361 struct in6_addr *ipaddr, u8 address_mode,
362 const void *lladdr)
265{ 363{
364 bool fail;
365
266 switch (address_mode) { 366 switch (address_mode) {
267 case LOWPAN_IPHC_SAM_00: 367 /* SAM and DAM are the same here */
268 /* unspec address :: 368 case LOWPAN_IPHC_DAM_00:
369 fail = false;
370 /* SAM_00 -> unspec address ::
269 * Do nothing, address is already :: 371 * Do nothing, address is already ::
372 *
373 * DAM 00 -> reserved should never occur.
270 */ 374 */
271 break; 375 break;
272 case LOWPAN_IPHC_SAM_01: 376 case LOWPAN_IPHC_SAM_01:
273 /* TODO */ 377 case LOWPAN_IPHC_DAM_01:
378 fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
379 ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
380 break;
274 case LOWPAN_IPHC_SAM_10: 381 case LOWPAN_IPHC_SAM_10:
275 /* TODO */ 382 case LOWPAN_IPHC_DAM_10:
383 ipaddr->s6_addr[11] = 0xFF;
384 ipaddr->s6_addr[12] = 0xFE;
385 fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
386 ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
387 break;
276 case LOWPAN_IPHC_SAM_11: 388 case LOWPAN_IPHC_SAM_11:
277 /* TODO */ 389 case LOWPAN_IPHC_DAM_11:
278 netdev_warn(skb->dev, "SAM value 0x%x not supported\n", 390 fail = false;
279 address_mode); 391 switch (lowpan_priv(dev)->lltype) {
280 return -EINVAL; 392 case LOWPAN_LLTYPE_IEEE802154:
393 iphc_uncompress_802154_lladdr(ipaddr, lladdr);
394 break;
395 default:
396 iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
397 break;
398 }
399 ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
400 break;
281 default: 401 default:
282 pr_debug("Invalid sam value: 0x%x\n", address_mode); 402 pr_debug("Invalid sam value: 0x%x\n", address_mode);
283 return -EINVAL; 403 return -EINVAL;
284 } 404 }
285 405
406 if (fail) {
407 pr_debug("Failed to fetch skb data\n");
408 return -EIO;
409 }
410
286 raw_dump_inline(NULL, 411 raw_dump_inline(NULL,
287 "Reconstructed context based ipv6 src addr is", 412 "Reconstructed context based ipv6 src addr is",
288 ipaddr->s6_addr, 16); 413 ipaddr->s6_addr, 16);
@@ -346,6 +471,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
346 return 0; 471 return 0;
347} 472}
348 473
474static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
475 struct lowpan_iphc_ctx *ctx,
476 struct in6_addr *ipaddr,
477 u8 address_mode)
478{
479 struct in6_addr network_pfx = {};
480 bool fail;
481
482 ipaddr->s6_addr[0] = 0xFF;
483 fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
484 fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
485 if (fail < 0)
486 return -EIO;
487
488 /* take prefix_len and network prefix from the context */
489 ipaddr->s6_addr[3] = ctx->plen;
490 /* get network prefix to copy into multicast address */
491 ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
492 /* setting network prefix */
493 memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
494
495 return 0;
496}
497
349/* get the ecn values from iphc tf format and set it to ipv6hdr */ 498/* get the ecn values from iphc tf format and set it to ipv6hdr */
350static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf) 499static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
351{ 500{
@@ -459,7 +608,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
459 const void *daddr, const void *saddr) 608 const void *daddr, const void *saddr)
460{ 609{
461 struct ipv6hdr hdr = {}; 610 struct ipv6hdr hdr = {};
462 u8 iphc0, iphc1; 611 struct lowpan_iphc_ctx *ci;
612 u8 iphc0, iphc1, cid = 0;
463 int err; 613 int err;
464 614
465 raw_dump_table(__func__, "raw skb data dump uncompressed", 615 raw_dump_table(__func__, "raw skb data dump uncompressed",
@@ -469,12 +619,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
469 lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1))) 619 lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
470 return -EINVAL; 620 return -EINVAL;
471 621
472 /* another if the CID flag is set */
473 if (iphc1 & LOWPAN_IPHC_CID)
474 return -ENOTSUPP;
475
476 hdr.version = 6; 622 hdr.version = 6;
477 623
624 /* default CID = 0, another if the CID flag is set */
625 if (iphc1 & LOWPAN_IPHC_CID) {
626 if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
627 return -EINVAL;
628 }
629
478 err = lowpan_iphc_tf_decompress(skb, &hdr, 630 err = lowpan_iphc_tf_decompress(skb, &hdr,
479 iphc0 & LOWPAN_IPHC_TF_MASK); 631 iphc0 & LOWPAN_IPHC_TF_MASK);
480 if (err < 0) 632 if (err < 0)
@@ -500,10 +652,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
500 } 652 }
501 653
502 if (iphc1 & LOWPAN_IPHC_SAC) { 654 if (iphc1 & LOWPAN_IPHC_SAC) {
503 /* Source address context based uncompression */ 655 spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
656 ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
657 if (!ci) {
658 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
659 return -EINVAL;
660 }
661
504 pr_debug("SAC bit is set. Handle context based source address.\n"); 662 pr_debug("SAC bit is set. Handle context based source address.\n");
505 err = uncompress_context_based_src_addr(skb, &hdr.saddr, 663 err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
506 iphc1 & LOWPAN_IPHC_SAM_MASK); 664 iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
665 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
507 } else { 666 } else {
508 /* Source address uncompression */ 667 /* Source address uncompression */
509 pr_debug("source address stateless compression\n"); 668 pr_debug("source address stateless compression\n");
@@ -515,27 +674,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
515 if (err) 674 if (err)
516 return -EINVAL; 675 return -EINVAL;
517 676
518 /* check for Multicast Compression */ 677 switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
519 if (iphc1 & LOWPAN_IPHC_M) { 678 case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
520 if (iphc1 & LOWPAN_IPHC_DAC) { 679 spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
521 pr_debug("dest: context-based mcast compression\n"); 680 ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
522 /* TODO: implement this */ 681 if (!ci) {
523 } else { 682 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
524 err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr, 683 return -EINVAL;
525 iphc1 & LOWPAN_IPHC_DAM_MASK); 684 }
526 685
527 if (err) 686 /* multicast with context */
528 return -EINVAL; 687 pr_debug("dest: context-based mcast compression\n");
688 err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
689 &hdr.daddr,
690 iphc1 & LOWPAN_IPHC_DAM_MASK);
691 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
692 break;
693 case LOWPAN_IPHC_M:
694 /* multicast */
695 err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
696 iphc1 & LOWPAN_IPHC_DAM_MASK);
697 break;
698 case LOWPAN_IPHC_DAC:
699 spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
700 ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
701 if (!ci) {
702 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
703 return -EINVAL;
529 } 704 }
530 } else { 705
706 /* Destination address context based uncompression */
707 pr_debug("DAC bit is set. Handle context based destination address.\n");
708 err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
709 iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
710 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
711 break;
712 default:
531 err = uncompress_addr(skb, dev, &hdr.daddr, 713 err = uncompress_addr(skb, dev, &hdr.daddr,
532 iphc1 & LOWPAN_IPHC_DAM_MASK, daddr); 714 iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
533 pr_debug("dest: stateless compression mode %d dest %pI6c\n", 715 pr_debug("dest: stateless compression mode %d dest %pI6c\n",
534 iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr); 716 iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
535 if (err) 717 break;
536 return -EINVAL;
537 } 718 }
538 719
720 if (err)
721 return -EINVAL;
722
539 /* Next header data uncompression */ 723 /* Next header data uncompression */
540 if (iphc0 & LOWPAN_IPHC_NH) { 724 if (iphc0 & LOWPAN_IPHC_NH) {
541 err = lowpan_nhc_do_uncompression(skb, dev, &hdr); 725 err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
@@ -585,6 +769,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
585 [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11, 769 [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
586}; 770};
587 771
772static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
773 const struct lowpan_iphc_ctx *ctx,
774 const unsigned char *lladdr, bool sam)
775{
776 struct in6_addr tmp = {};
777 u8 dam;
778
779 /* check for SAM/DAM = 11 */
780 memcpy(&tmp.s6_addr[8], lladdr, 8);
781 /* second bit-flip (Universe/Local) is done according RFC2464 */
782 tmp.s6_addr[8] ^= 0x02;
783 /* context information are always used */
784 ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
785 if (ipv6_addr_equal(&tmp, ipaddr)) {
786 dam = LOWPAN_IPHC_DAM_11;
787 goto out;
788 }
789
790 memset(&tmp, 0, sizeof(tmp));
791 /* check for SAM/DAM = 01 */
792 tmp.s6_addr[11] = 0xFF;
793 tmp.s6_addr[12] = 0xFE;
794 memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
795 /* context information are always used */
796 ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
797 if (ipv6_addr_equal(&tmp, ipaddr)) {
798 lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
799 dam = LOWPAN_IPHC_DAM_10;
800 goto out;
801 }
802
803 memset(&tmp, 0, sizeof(tmp));
804 /* check for SAM/DAM = 10, should always match */
805 memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
806 /* context information are always used */
807 ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
808 if (ipv6_addr_equal(&tmp, ipaddr)) {
809 lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
810 dam = LOWPAN_IPHC_DAM_01;
811 goto out;
812 }
813
814 WARN_ON_ONCE("context found but no address mode matched\n");
815 return -EINVAL;
816out:
817
818 if (sam)
819 return lowpan_iphc_dam_to_sam_value[dam];
820 else
821 return dam;
822}
823
588static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr, 824static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
589 const unsigned char *lladdr, bool sam) 825 const unsigned char *lladdr, bool sam)
590{ 826{
@@ -708,6 +944,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
708 return val; 944 return val;
709} 945}
710 946
947static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
948 const struct lowpan_iphc_ctx *ctx,
949 const struct in6_addr *ipaddr)
950{
951 u8 data[6];
952
953 /* flags/scope, reserved (RIID) */
954 memcpy(data, &ipaddr->s6_addr[1], 2);
955 /* group ID */
956 memcpy(&data[1], &ipaddr->s6_addr[11], 4);
957 lowpan_push_hc_data(hc_ptr, data, 6);
958
959 return LOWPAN_IPHC_DAM_00;
960}
961
711static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr, 962static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
712 const struct in6_addr *ipaddr) 963 const struct in6_addr *ipaddr)
713{ 964{
@@ -742,10 +993,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
742int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev, 993int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
743 const void *daddr, const void *saddr) 994 const void *daddr, const void *saddr)
744{ 995{
745 u8 iphc0, iphc1, *hc_ptr; 996 u8 iphc0, iphc1, *hc_ptr, cid = 0;
746 struct ipv6hdr *hdr; 997 struct ipv6hdr *hdr;
747 u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {}; 998 u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
748 int ret, addr_type; 999 struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
1000 int ret, ipv6_daddr_type, ipv6_saddr_type;
749 1001
750 if (skb->protocol != htons(ETH_P_IPV6)) 1002 if (skb->protocol != htons(ETH_P_IPV6))
751 return -EINVAL; 1003 return -EINVAL;
@@ -769,14 +1021,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
769 iphc0 = LOWPAN_DISPATCH_IPHC; 1021 iphc0 = LOWPAN_DISPATCH_IPHC;
770 iphc1 = 0; 1022 iphc1 = 0;
771 1023
772 /* TODO: context lookup */
773
774 raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN); 1024 raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
775 raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN); 1025 raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
776 1026
777 raw_dump_table(__func__, "sending raw skb network uncompressed packet", 1027 raw_dump_table(__func__, "sending raw skb network uncompressed packet",
778 skb->data, skb->len); 1028 skb->data, skb->len);
779 1029
1030 ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
1031 spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
1032 if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
1033 dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
1034 else
1035 dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
1036 if (dci) {
1037 memcpy(&dci_entry, dci, sizeof(*dci));
1038 cid |= dci->id;
1039 }
1040 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
1041
1042 spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
1043 sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
1044 if (sci) {
1045 memcpy(&sci_entry, sci, sizeof(*sci));
1046 cid |= (sci->id << 4);
1047 }
1048 spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
1049
1050 /* if cid is zero it will be compressed */
1051 if (cid) {
1052 iphc1 |= LOWPAN_IPHC_CID;
1053 lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
1054 }
1055
780 /* Traffic Class, Flow Label compression */ 1056 /* Traffic Class, Flow Label compression */
781 iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr); 1057 iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
782 1058
@@ -813,39 +1089,63 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
813 sizeof(hdr->hop_limit)); 1089 sizeof(hdr->hop_limit));
814 } 1090 }
815 1091
816 addr_type = ipv6_addr_type(&hdr->saddr); 1092 ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
817 /* source address compression */ 1093 /* source address compression */
818 if (addr_type == IPV6_ADDR_ANY) { 1094 if (ipv6_saddr_type == IPV6_ADDR_ANY) {
819 pr_debug("source address is unspecified, setting SAC\n"); 1095 pr_debug("source address is unspecified, setting SAC\n");
820 iphc1 |= LOWPAN_IPHC_SAC; 1096 iphc1 |= LOWPAN_IPHC_SAC;
821 } else { 1097 } else {
822 if (addr_type & IPV6_ADDR_LINKLOCAL) { 1098 if (sci) {
823 iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr, 1099 iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
824 saddr, true); 1100 &sci_entry, saddr,
825 pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n", 1101 true);
826 &hdr->saddr, iphc1); 1102 iphc1 |= LOWPAN_IPHC_SAC;
827 } else { 1103 } else {
828 pr_debug("send the full source address\n"); 1104 if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
829 lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16); 1105 iphc1 |= lowpan_compress_addr_64(&hc_ptr,
1106 &hdr->saddr,
1107 saddr, true);
1108 pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
1109 &hdr->saddr, iphc1);
1110 } else {
1111 pr_debug("send the full source address\n");
1112 lowpan_push_hc_data(&hc_ptr,
1113 hdr->saddr.s6_addr, 16);
1114 }
830 } 1115 }
831 } 1116 }
832 1117
833 addr_type = ipv6_addr_type(&hdr->daddr);
834 /* destination address compression */ 1118 /* destination address compression */
835 if (addr_type & IPV6_ADDR_MULTICAST) { 1119 if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
836 pr_debug("destination address is multicast: "); 1120 pr_debug("destination address is multicast: ");
837 iphc1 |= LOWPAN_IPHC_M; 1121 if (dci) {
838 iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr); 1122 iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
1123 &dci_entry,
1124 &hdr->daddr);
1125 } else {
1126 iphc1 |= LOWPAN_IPHC_M;
1127 iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
1128 &hdr->daddr);
1129 }
839 } else { 1130 } else {
840 if (addr_type & IPV6_ADDR_LINKLOCAL) { 1131 if (dci) {
841 /* TODO: context lookup */ 1132 iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
842 iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr, 1133 &dci_entry, daddr,
843 daddr, false); 1134 false);
844 pr_debug("dest address unicast link-local %pI6c " 1135 iphc1 |= LOWPAN_IPHC_DAC;
845 "iphc1 0x%02x\n", &hdr->daddr, iphc1);
846 } else { 1136 } else {
847 pr_debug("dest address unicast %pI6c\n", &hdr->daddr); 1137 if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
848 lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16); 1138 iphc1 |= lowpan_compress_addr_64(&hc_ptr,
1139 &hdr->daddr,
1140 daddr, false);
1141 pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
1142 &hdr->daddr, iphc1);
1143 } else {
1144 pr_debug("dest address unicast %pI6c\n",
1145 &hdr->daddr);
1146 lowpan_push_hc_data(&hc_ptr,
1147 hdr->daddr.s6_addr, 16);
1148 }
849 } 1149 }
850 } 1150 }
851 1151