1 /** @file
2 * @brief 6lopan related functions
3 */
4
5 /*
6 * Copyright (c) 2016 Intel Corporation
7 * Copyright (c) 2019 Alexander Wachter
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(net_6lo, CONFIG_NET_6LO_LOG_LEVEL);
14
15 #include <errno.h>
16 #include <zephyr/net/net_core.h>
17 #include <zephyr/net/net_if.h>
18 #include <zephyr/net/net_stats.h>
19 #include <zephyr/net/udp.h>
20
21 #include "net_private.h"
22 #include "6lo.h"
23 #include "6lo_private.h"
24
25 #if defined(CONFIG_NET_6LO_CONTEXT)
26 struct net_6lo_context {
27 struct in6_addr prefix;
28 struct net_if *iface;
29 uint16_t lifetime;
30 uint8_t is_used : 1;
31 uint8_t compress : 1;
32 uint8_t cid : 4;
33 uint8_t unused : 2;
34 };
35
get_6co_compress(struct net_icmpv6_nd_opt_6co * opt)36 static inline uint8_t get_6co_compress(struct net_icmpv6_nd_opt_6co *opt)
37 {
38 return (opt->flag & 0x10) >> 4;
39 }
40
get_6co_cid(struct net_icmpv6_nd_opt_6co * opt)41 static inline uint8_t get_6co_cid(struct net_icmpv6_nd_opt_6co *opt)
42 {
43 return opt->flag & 0x0F;
44 }
45
46 static struct net_6lo_context ctx_6co[CONFIG_NET_MAX_6LO_CONTEXTS];
47 #endif
48
49 static const uint8_t udp_nhc_inline_size_table[] = {4, 3, 3, 1};
50
51 static const uint8_t tf_inline_size_table[] = {4, 3, 1, 0};
52 /* The first bit of the index is SAC | SAC=0 | SAC=1 |*/
53 static const uint8_t sa_inline_size_table[] = {16, 8, 2, 0, 0, 8, 2, 0};
54
55 /* The first bit is M, the second DAC
56 * | M=0 DAC=0 | M=0 DAC=1 | M=1 DAC=0 | M=1 DAC=1 (DAM always 00)
57 */
58 static const uint8_t da_inline_size_table[] = {
59 16, 8, 2, 0, 0, 8, 2, 0, 16, 6, 4, 1, 6
60 };
61
get_udp_nhc_inlined_size(uint8_t nhc)62 static int get_udp_nhc_inlined_size(uint8_t nhc)
63 {
64 int size = 0;
65
66 if ((nhc & 0xF8) != NET_6LO_NHC_UDP_BARE) {
67 NET_DBG("UDP NHC dispatch doesn't match");
68 return 0;
69 }
70
71 if (!(nhc & NET_6LO_NHC_UDP_CHECKSUM)) {
72 size += 2U;
73 }
74
75 size += udp_nhc_inline_size_table[(nhc & NET_6LO_NHC_UDP_PORT_MASK)];
76
77 NET_DBG("Size of inlined UDP HDR data: %d", size);
78
79 return size;
80 }
81
get_ihpc_inlined_size(uint16_t iphc)82 static int get_ihpc_inlined_size(uint16_t iphc)
83 {
84 int size = 0;
85
86 if (((iphc >> 8) & NET_6LO_DISPATCH_IPHC_MASK) !=
87 NET_6LO_DISPATCH_IPHC) {
88 NET_DBG("IPHC dispatch doesn't match");
89 return -1;
90 }
91
92 size += tf_inline_size_table[(iphc & NET_6LO_IPHC_TF_MASK) >>
93 NET_6LO_IPHC_TF_POS];
94
95 if (!(iphc & NET_6LO_IPHC_NH_MASK)) {
96 size += 1U;
97 }
98
99 if (!(iphc & NET_6LO_IPHC_HLIM_MASK)) {
100 size += 1U;
101 }
102
103 if (iphc & NET_6LO_IPHC_CID_MASK) {
104 size += 1U;
105 }
106
107 size += sa_inline_size_table[(iphc & NET_6LO_IPHC_SA_MASK) >>
108 NET_6LO_IPHC_SAM_POS];
109
110 size += da_inline_size_table[(iphc & NET_6LO_IPHC_DA_MASK) >>
111 NET_6LO_IPHC_DAM_POS];
112
113 NET_DBG("Size of inlined IP HDR data: %d", size);
114
115 return size;
116 }
117
118 /* TODO: Unicast-Prefix based IPv6 Multicast(dst) address compression
119 * Mesh header compression
120 */
121
net_6lo_ll_prefix_padded_with_zeros(struct in6_addr * addr)122 static inline bool net_6lo_ll_prefix_padded_with_zeros(struct in6_addr *addr)
123 {
124 return (net_ipv6_is_ll_addr(addr) &&
125 (UNALIGNED_GET(&addr->s6_addr16[1]) == 0x00) &&
126 (UNALIGNED_GET(&addr->s6_addr32[1]) == 0x00));
127 }
128
net_6lo_addr_16_bit_compressible(struct in6_addr * addr)129 static inline bool net_6lo_addr_16_bit_compressible(struct in6_addr *addr)
130 {
131 return ((UNALIGNED_GET(&addr->s6_addr32[2]) == htonl(0xFF)) &&
132 (UNALIGNED_GET(&addr->s6_addr16[6]) == htons(0xFE00)));
133 }
134
net_6lo_maddr_8_bit_compressible(struct in6_addr * addr)135 static inline bool net_6lo_maddr_8_bit_compressible(struct in6_addr *addr)
136 {
137 return ((addr->s6_addr[1] == 0x02) &&
138 (UNALIGNED_GET(&addr->s6_addr16[1]) == 0x00) &&
139 (UNALIGNED_GET(&addr->s6_addr32[1]) == 0x00) &&
140 (UNALIGNED_GET(&addr->s6_addr32[2]) == 0x00) &&
141 (addr->s6_addr[14] == 0x00));
142 }
143
net_6lo_maddr_32_bit_compressible(struct in6_addr * addr)144 static inline bool net_6lo_maddr_32_bit_compressible(struct in6_addr *addr)
145 {
146 return ((UNALIGNED_GET(&addr->s6_addr32[1]) == 0x00) &&
147 (UNALIGNED_GET(&addr->s6_addr32[2]) == 0x00) &&
148 (addr->s6_addr[12] == 0x00));
149 }
150
net_6lo_maddr_48_bit_compressible(struct in6_addr * addr)151 static inline bool net_6lo_maddr_48_bit_compressible(struct in6_addr *addr)
152 {
153 return ((UNALIGNED_GET(&addr->s6_addr32[1]) == 0x00) &&
154 (UNALIGNED_GET(&addr->s6_addr16[4]) == 0x00) &&
155 (addr->s6_addr[10] == 0x00));
156 }
157
158 #if defined(CONFIG_NET_6LO_CONTEXT)
159 /* RFC 6775, 4.2, 5.4.2, 5.4.3 and 7.2*/
set_6lo_context(struct net_if * iface,uint8_t index,struct net_icmpv6_nd_opt_6co * context)160 static inline void set_6lo_context(struct net_if *iface, uint8_t index,
161 struct net_icmpv6_nd_opt_6co *context)
162
163 {
164 ctx_6co[index].is_used = true;
165 ctx_6co[index].iface = iface;
166
167 /*TODO: Start timer */
168 ctx_6co[index].lifetime = context->lifetime;
169 ctx_6co[index].compress = get_6co_compress(context);
170 ctx_6co[index].cid = get_6co_cid(context);
171
172 net_ipv6_addr_copy_raw((uint8_t *)&ctx_6co[index].prefix, context->prefix);
173 }
174
net_6lo_set_context(struct net_if * iface,struct net_icmpv6_nd_opt_6co * context)175 void net_6lo_set_context(struct net_if *iface,
176 struct net_icmpv6_nd_opt_6co *context)
177 {
178 int unused = -1;
179 uint8_t i;
180
181 /* If the context information already exists, update or remove
182 * as per data.
183 */
184 for (i = 0U; i < CONFIG_NET_MAX_6LO_CONTEXTS; i++) {
185 if (!ctx_6co[i].is_used) {
186 unused = i;
187 continue;
188 }
189
190 if (ctx_6co[i].iface == iface &&
191 ctx_6co[i].cid == get_6co_cid(context)) {
192 /* Remove if lifetime is zero */
193 if (!context->lifetime) {
194 ctx_6co[i].is_used = false;
195 return;
196 }
197
198 /* Update the context */
199 set_6lo_context(iface, i, context);
200 return;
201 }
202 }
203
204 /* Cache the context information. */
205 if (unused != -1) {
206 set_6lo_context(iface, unused, context);
207 return;
208 }
209
210 NET_DBG("Either no free slots in the table or exceeds limit");
211 }
212
213 /* Get the context by matching cid */
214 static inline struct net_6lo_context *
get_6lo_context_by_cid(struct net_if * iface,uint8_t cid)215 get_6lo_context_by_cid(struct net_if *iface, uint8_t cid)
216 {
217 uint8_t i;
218
219 for (i = 0U; i < CONFIG_NET_MAX_6LO_CONTEXTS; i++) {
220 if (!ctx_6co[i].is_used) {
221 continue;
222 }
223
224 if (ctx_6co[i].iface == iface && ctx_6co[i].cid == cid) {
225 return &ctx_6co[i];
226 }
227 }
228
229 return NULL;
230 }
231
232 /* Get the context by addr */
233 static inline struct net_6lo_context *
get_6lo_context_by_addr(struct net_if * iface,struct in6_addr * addr)234 get_6lo_context_by_addr(struct net_if *iface, struct in6_addr *addr)
235 {
236 uint8_t i;
237
238 for (i = 0U; i < CONFIG_NET_MAX_6LO_CONTEXTS; i++) {
239 if (!ctx_6co[i].is_used) {
240 continue;
241 }
242
243 if (ctx_6co[i].iface == iface &&
244 !memcmp(ctx_6co[i].prefix.s6_addr, addr->s6_addr, 8)) {
245 return &ctx_6co[i];
246 }
247 }
248
249 return NULL;
250 }
251
252 #endif
253
254 /* Helper routine to compress Traffic class and Flow label */
255 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256 * |Version| Traffic Class | Flow Label |
257 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
258 * version: 4 bits, Traffic Class: 8 bits, Flow label: 20 bits
259 * The Traffic Class field in the IPv6 header comprises 6 bits of
260 * Diffserv extension [RFC2474] and 2 bits of Explicit Congestion
261 * Notification (ECN) [RFC3168]
262 */
263
264 /* IPHC (compressed) format of traffic class is ECN, DSCP but original
265 * IPv6 traffic class format is DSCP, ECN.
266 * DSCP(6), ECN(2).
267 */
compress_tfl(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)268 static uint8_t *compress_tfl(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
269 uint16_t *iphc)
270 {
271 uint8_t tcl;
272
273 tcl = ((ipv6->vtc & 0x0F) << 4) | ((ipv6->tcflow & 0xF0) >> 4);
274 tcl = (tcl << 6) | (tcl >> 2); /* ECN(2), DSCP(6) */
275
276 if (((ipv6->tcflow & 0x0F) == 0U) && (ipv6->flow == 0U)) {
277 if (((ipv6->vtc & 0x0F) == 0U) && ((ipv6->tcflow & 0xF0) == 0U)) {
278 NET_DBG("Traffic class and Flow label elided");
279
280 /* Traffic class and Flow label elided */
281 *iphc |= NET_6LO_IPHC_TF_11;
282 } else {
283 NET_DBG("Flow label elided");
284
285 /* Flow label elided */
286 *iphc |= NET_6LO_IPHC_TF_10;
287
288 inline_ptr -= sizeof(tcl);
289 *inline_ptr = tcl;
290 }
291 } else {
292 if (((ipv6->vtc & 0x0F) == 0U) && (ipv6->tcflow & 0x30)) {
293 NET_DBG("ECN + 2-bit Pad + Flow Label, DSCP is elided");
294
295 /* ECN + 2-bit Pad + Flow Label, DSCP is elided.*/
296 *iphc |= NET_6LO_IPHC_TF_01;
297
298 inline_ptr -= sizeof(ipv6->flow);
299 memmove(inline_ptr, &ipv6->flow, sizeof(ipv6->flow));
300
301 inline_ptr -= sizeof(uint8_t);
302 *inline_ptr = (tcl & 0xC0) | (ipv6->tcflow & 0x0F);
303 } else {
304 NET_DBG("ECN + DSCP + 4-bit Pad + Flow Label");
305
306 /* ECN + DSCP + 4-bit Pad + Flow Label */
307 *iphc |= NET_6LO_IPHC_TF_00;
308
309 inline_ptr -= sizeof(ipv6->flow);
310 memmove(inline_ptr, &ipv6->flow, sizeof(ipv6->flow));
311
312 inline_ptr -= sizeof(uint8_t);
313 *inline_ptr = ipv6->tcflow & 0x0F;
314 inline_ptr -= sizeof(tcl);
315 *inline_ptr = tcl;
316 }
317 }
318
319 return inline_ptr;
320 }
321
322 /* Helper to compress Hop limit */
compress_hoplimit(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)323 static uint8_t *compress_hoplimit(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
324 uint16_t *iphc)
325 {
326 /* Hop Limit */
327 switch (ipv6->hop_limit) {
328 case 1:
329 NET_DBG("HLIM compressed (1)");
330 *iphc |= NET_6LO_IPHC_HLIM1;
331 break;
332 case 64:
333 NET_DBG("HLIM compressed (64)");
334 *iphc |= NET_6LO_IPHC_HLIM64;
335 break;
336 case 255:
337 NET_DBG("HLIM compressed (255)");
338 *iphc |= NET_6LO_IPHC_HLIM255;
339 break;
340 default:
341 inline_ptr -= sizeof(ipv6->hop_limit);
342 *inline_ptr = ipv6->hop_limit;
343 break;
344 }
345
346 return inline_ptr;
347 }
348
349 /* Helper to compress Next header */
compress_nh(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)350 static uint8_t *compress_nh(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
351 uint16_t *iphc)
352 {
353 /* Next header */
354 if (ipv6->nexthdr == IPPROTO_UDP) {
355 *iphc |= NET_6LO_IPHC_NH_1;
356 } else {
357 inline_ptr -= sizeof(ipv6->nexthdr);
358 *inline_ptr = ipv6->nexthdr;
359 }
360
361 return inline_ptr;
362 }
363
364 /* Helpers to compress Source Address */
compress_sa(struct net_ipv6_hdr * ipv6,struct net_pkt * pkt,uint8_t * inline_ptr,uint16_t * iphc)365 static uint8_t *compress_sa(struct net_ipv6_hdr *ipv6, struct net_pkt *pkt,
366 uint8_t *inline_ptr, uint16_t *iphc)
367 {
368 /* Address is fully elided */
369 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->src,
370 net_pkt_lladdr_src(pkt))) {
371 NET_DBG("SAM_11 src address is fully elided");
372
373 *iphc |= NET_6LO_IPHC_SAM_11;
374 return inline_ptr;
375 }
376
377 /* Following 64 bits are 0000:00ff:fe00:XXXX */
378 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->src)) {
379 NET_DBG("SAM_10 src addr 16 bit compressible");
380 *iphc |= NET_6LO_IPHC_SAM_10;
381
382 inline_ptr -= sizeof(uint16_t);
383 memmove(inline_ptr, &ipv6->src[14], sizeof(uint16_t));
384
385 return inline_ptr;
386 }
387
388 NET_DBG("SAM_01 src 64 bits are inlined");
389 /* Remaining 64 bits are in-line */
390 *iphc |= NET_6LO_IPHC_SAM_01;
391
392 inline_ptr -= 8U;
393 memmove(inline_ptr, &ipv6->src[8], 8U);
394
395 return inline_ptr;
396 }
397
set_sa_inline(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)398 static uint8_t *set_sa_inline(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
399 uint16_t *iphc)
400 {
401 *iphc |= NET_6LO_IPHC_SAM_00;
402 inline_ptr -= 16U;
403 memmove(inline_ptr, &ipv6->src[0], 16U);
404 return inline_ptr;
405 }
406
407 #if defined(CONFIG_NET_6LO_CONTEXT)
compress_sa_ctx(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,struct net_pkt * pkt,uint16_t * iphc,struct net_6lo_context * src)408 static uint8_t *compress_sa_ctx(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
409 struct net_pkt *pkt, uint16_t *iphc,
410 struct net_6lo_context *src)
411 {
412 NET_DBG("SAC_1 src address context based");
413 *iphc |= NET_6LO_IPHC_SAC_1;
414
415 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->src,
416 net_pkt_lladdr_src(pkt))) {
417 NET_DBG("SAM_11 src address is fully elided");
418
419 /* Address is fully elided */
420 *iphc |= NET_6LO_IPHC_SAM_11;
421 return inline_ptr;
422 }
423
424 /* Following 64 bits are 0000:00ff:fe00:XXXX */
425 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->src)) {
426 NET_DBG("SAM_10 src addr 16 bit compressible");
427
428 *iphc |= NET_6LO_IPHC_SAM_10;
429
430 inline_ptr -= sizeof(uint16_t);
431 memmove(inline_ptr, &ipv6->src[14], sizeof(uint16_t));
432 return inline_ptr;
433 }
434
435 NET_DBG("SAM_01 src remaining 64 bits are inlined");
436
437 /* Remaining 64 bits are in-line */
438 *iphc |= NET_6LO_IPHC_SAM_01;
439
440 inline_ptr -= 8U;
441 memmove(inline_ptr, &ipv6->src[8], 8U);
442
443 return inline_ptr;
444 }
445 #endif
446
447 /* Helpers to compress Destination Address */
compress_da_mcast(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)448 static uint8_t *compress_da_mcast(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
449 uint16_t *iphc)
450 {
451 *iphc |= NET_6LO_IPHC_M_1;
452
453 NET_DBG("M_1 dst is mcast");
454
455 if (net_6lo_maddr_8_bit_compressible((struct in6_addr *)ipv6->dst)) {
456 NET_DBG("DAM_11 dst maddr 8 bit compressible");
457
458 /* last byte */
459 *iphc |= NET_6LO_IPHC_DAM_11;
460
461 inline_ptr -= sizeof(uint8_t);
462 memmove(inline_ptr, &ipv6->dst[15], sizeof(uint8_t));
463
464 return inline_ptr;
465 }
466
467 if (net_6lo_maddr_32_bit_compressible((struct in6_addr *)ipv6->dst)) {
468 NET_DBG("DAM_10 4 bytes: 2nd byte + last three bytes");
469
470 /* 4 bytes: 2nd byte + last three bytes */
471 *iphc |= NET_6LO_IPHC_DAM_10;
472
473 inline_ptr -= 3U;
474 memmove(inline_ptr, &ipv6->dst[13], 3U);
475
476 inline_ptr -= sizeof(uint8_t);
477 memmove(inline_ptr, &ipv6->dst[1], sizeof(uint8_t));
478
479 return inline_ptr;
480 }
481
482 if (net_6lo_maddr_48_bit_compressible((struct in6_addr *)ipv6->dst)) {
483 NET_DBG("DAM_01 6 bytes: 2nd byte + last five bytes");
484
485 /* 6 bytes: 2nd byte + last five bytes */
486 *iphc |= NET_6LO_IPHC_DAM_01;
487
488 inline_ptr -= 5U;
489 memmove(inline_ptr, &ipv6->dst[11], 5U);
490
491 inline_ptr -= sizeof(uint8_t);
492 memmove(inline_ptr, &ipv6->dst[1], sizeof(uint8_t));
493
494 return inline_ptr;
495 }
496
497 NET_DBG("DAM_00 dst complete addr inlined");
498
499 /* complete address NET_6LO_IPHC_DAM_00 */
500 inline_ptr -= 16U;
501 memmove(inline_ptr, &ipv6->dst[0], 16U);
502
503 return inline_ptr;
504 }
505
compress_da(struct net_ipv6_hdr * ipv6,struct net_pkt * pkt,uint8_t * inline_ptr,uint16_t * iphc)506 static uint8_t *compress_da(struct net_ipv6_hdr *ipv6, struct net_pkt *pkt,
507 uint8_t *inline_ptr, uint16_t *iphc)
508 {
509 /* Address is fully elided */
510 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->dst,
511 net_pkt_lladdr_dst(pkt))) {
512 NET_DBG("DAM_11 dst addr fully elided");
513
514 *iphc |= NET_6LO_IPHC_DAM_11;
515 return inline_ptr;
516 }
517
518 /* Following 64 bits are 0000:00ff:fe00:XXXX */
519 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->dst)) {
520 NET_DBG("DAM_10 dst addr 16 bit compressible");
521
522 *iphc |= NET_6LO_IPHC_DAM_10;
523
524 inline_ptr -= sizeof(uint16_t);
525 memmove(inline_ptr, &ipv6->dst[14], sizeof(uint16_t));
526 return inline_ptr;
527 }
528
529 NET_DBG("DAM_01 remaining 64 bits are inlined");
530
531 /* Remaining 64 bits are in-line */
532 *iphc |= NET_6LO_IPHC_DAM_01;
533
534 inline_ptr -= 8U;
535 memmove(inline_ptr, &ipv6->dst[8], 8U);
536
537 return inline_ptr;
538 }
539
set_da_inline(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)540 static uint8_t *set_da_inline(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
541 uint16_t *iphc)
542 {
543 *iphc |= NET_6LO_IPHC_DAM_00;
544 inline_ptr -= 16U;
545 memmove(inline_ptr, &ipv6->dst[0], 16U);
546 return inline_ptr;
547 }
548
549 #if defined(CONFIG_NET_6LO_CONTEXT)
compress_da_ctx(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,struct net_pkt * pkt,uint16_t * iphc,struct net_6lo_context * dst)550 static uint8_t *compress_da_ctx(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
551 struct net_pkt *pkt, uint16_t *iphc,
552 struct net_6lo_context *dst)
553 {
554 *iphc |= NET_6LO_IPHC_DAC_1;
555
556 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->dst,
557 net_pkt_lladdr_dst(pkt))) {
558 NET_DBG("DAM_11 dst addr fully elided");
559
560 *iphc |= NET_6LO_IPHC_DAM_11;
561 return inline_ptr;
562 }
563
564 /* Following 64 bits are 0000:00ff:fe00:XXXX */
565 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->dst)) {
566 NET_DBG("DAM_10 dst addr 16 bit compressible");
567
568 *iphc |= NET_6LO_IPHC_DAM_10;
569 inline_ptr -= sizeof(uint16_t);
570 memmove(inline_ptr, &ipv6->dst[14], sizeof(uint16_t));
571 return inline_ptr;
572 }
573
574 NET_DBG("DAM_01 remaining 64 bits are inlined");
575
576 /* Remaining 64 bits are in-line */
577 *iphc |= NET_6LO_IPHC_DAM_01;
578
579 inline_ptr -= 8U;
580 memmove(inline_ptr, &ipv6->dst[8], 8U);
581
582 return inline_ptr;
583 }
584 #endif
585
586 /* Helper to compress Next header UDP */
compress_nh_udp(struct net_udp_hdr * udp,uint8_t * inline_ptr,bool compress_checksum)587 static inline uint8_t *compress_nh_udp(struct net_udp_hdr *udp, uint8_t *inline_ptr,
588 bool compress_checksum)
589 {
590 uint8_t nhc = NET_6LO_NHC_UDP_BARE;
591 uint8_t *inline_ptr_udp = inline_ptr;
592 uint8_t tmp;
593
594 /* 4.3.3 UDP LOWPAN_NHC Format
595 * 0 1 2 3 4 5 6 7
596 * +---+---+---+---+---+---+---+---+
597 * | 1 | 1 | 1 | 1 | 0 | C | P |
598 * +---+---+---+---+---+---+---+---+
599 */
600
601 /* Port compression
602 * 00: All 16 bits for src and dst are inlined.
603 * 01: All 16 bits for src port inlined. First 8 bits of dst port is
604 * 0xf0 and elided. The remaining 8 bits of dst port inlined.
605 * 10: First 8 bits of src port 0xf0 and elided. The remaining 8 bits
606 * of src port inlined. All 16 bits of dst port inlined.
607 * 11: First 12 bits of both src and dst are 0xf0b and elided. The
608 * remaining 4 bits for each are inlined.
609 */
610
611 if (compress_checksum) {
612 nhc |= NET_6LO_NHC_UDP_CHECKSUM;
613 } else {
614 inline_ptr_udp -= sizeof(udp->chksum);
615 memmove(inline_ptr_udp, &udp->chksum, sizeof(udp->chksum));
616 }
617
618 if ((((htons(udp->src_port) >> 4) & 0xFFF) ==
619 NET_6LO_NHC_UDP_4_BIT_PORT) &&
620 (((htons(udp->dst_port) >> 4) & 0xFFF) ==
621 NET_6LO_NHC_UDP_4_BIT_PORT)) {
622
623 NET_DBG("UDP ports src and dst 4 bits inlined");
624 /** src: first 16 bits elided, next 4 bits inlined
625 * dst: first 16 bits elided, next 4 bits inlined
626 */
627 nhc |= NET_6LO_NHC_UDP_PORT_11;
628
629 tmp = (uint8_t)(htons(udp->src_port));
630 tmp = tmp << 4;
631
632 tmp |= (((uint8_t)(htons(udp->dst_port))) & 0x0F);
633 inline_ptr_udp -= sizeof(tmp);
634 *inline_ptr_udp = tmp;
635
636 } else if (((htons(udp->dst_port) >> 8) & 0xFF) ==
637 NET_6LO_NHC_UDP_8_BIT_PORT) {
638
639 NET_DBG("UDP ports src full, dst 8 bits inlined");
640 /* dst: first 8 bits elided, next 8 bits inlined
641 * src: fully carried inline
642 */
643 nhc |= NET_6LO_NHC_UDP_PORT_01;
644
645 inline_ptr_udp -= sizeof(uint8_t);
646 *inline_ptr_udp = (uint8_t)(htons(udp->dst_port));
647
648 inline_ptr_udp -= sizeof(udp->src_port);
649 memmove(inline_ptr_udp, &udp->src_port, sizeof(udp->src_port));
650
651 } else if (((htons(udp->src_port) >> 8) & 0xFF) ==
652 NET_6LO_NHC_UDP_8_BIT_PORT) {
653
654 NET_DBG("UDP ports src 8bits, dst full inlined");
655 /* src: first 8 bits elided, next 8 bits inlined
656 * dst: fully carried inline
657 */
658 nhc |= NET_6LO_NHC_UDP_PORT_10;
659
660 inline_ptr_udp -= sizeof(udp->dst_port);
661 memmove(inline_ptr_udp, &udp->dst_port, sizeof(udp->dst_port));
662
663 inline_ptr_udp -= sizeof(uint8_t);
664 *inline_ptr_udp = (uint8_t)(htons(udp->src_port));
665
666 } else {
667 NET_DBG("Can not compress ports, ports are inlined");
668
669 /* can not compress ports, ports are inlined */
670 inline_ptr_udp -= sizeof(udp->dst_port) + sizeof(udp->src_port);
671 memmove(inline_ptr_udp, &udp->src_port,
672 sizeof(udp->dst_port) + sizeof(udp->src_port));
673 }
674
675 inline_ptr_udp -= sizeof(nhc);
676 *inline_ptr_udp = nhc;
677
678 return inline_ptr_udp;
679 }
680
681 #if defined(CONFIG_NET_6LO_CONTEXT)
682
get_src_addr_ctx(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6)683 static struct net_6lo_context *get_src_addr_ctx(struct net_pkt *pkt,
684 struct net_ipv6_hdr *ipv6)
685 {
686 /* If compress flag is unset means use only in uncompression. */
687 struct net_6lo_context *src;
688
689 src = get_6lo_context_by_addr(net_pkt_iface(pkt),
690 (struct in6_addr *)ipv6->src);
691 if (!src || !src->compress) {
692 return NULL;
693 }
694
695 return src;
696 }
697
get_dst_addr_ctx(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6)698 static struct net_6lo_context *get_dst_addr_ctx(struct net_pkt *pkt,
699 struct net_ipv6_hdr *ipv6)
700 {
701 /* If compress flag is unset means use only in uncompression. */
702 struct net_6lo_context *dst;
703
704 dst = get_6lo_context_by_addr(net_pkt_iface(pkt),
705 (struct in6_addr *)ipv6->dst);
706 if (!dst || !dst->compress) {
707 return NULL;
708 }
709
710 return dst;
711 }
712 #endif /* CONFIG_NET_6LO_CONTEXT */
713
714 /* RFC 6282 LOWPAN IPHC Encoding format (3.1)
715 * Base Format
716 * 0 1
717 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
718 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
719 * | 0 | 1 | 1 | TF |NH | HLIM |CID|SAC| SAM | M |DAC| DAM |
720 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
721 */
compress_IPHC_header(struct net_pkt * pkt)722 static inline int compress_IPHC_header(struct net_pkt *pkt)
723 {
724 #if defined(CONFIG_NET_6LO_CONTEXT)
725 struct net_6lo_context *src_ctx = NULL;
726 struct net_6lo_context *dst_ctx = NULL;
727 #endif
728 uint8_t compressed = 0;
729 uint16_t iphc = (NET_6LO_DISPATCH_IPHC << 8);
730 struct net_ipv6_hdr *ipv6 = NET_IPV6_HDR(pkt);
731 struct net_udp_hdr *udp;
732 uint8_t *inline_pos;
733
734 if (pkt->frags->len < NET_IPV6H_LEN) {
735 NET_ERR("Invalid length %d, min %d",
736 pkt->frags->len, NET_IPV6H_LEN);
737 return -EINVAL;
738 }
739
740 if (ipv6->nexthdr == IPPROTO_UDP &&
741 pkt->frags->len < NET_IPV6UDPH_LEN) {
742 NET_ERR("Invalid length %d, min %d",
743 pkt->frags->len, NET_IPV6UDPH_LEN);
744 return -EINVAL;
745 }
746
747 inline_pos = pkt->buffer->data + NET_IPV6H_LEN;
748
749 if (ipv6->nexthdr == IPPROTO_UDP) {
750 udp = (struct net_udp_hdr *)inline_pos;
751 inline_pos += NET_UDPH_LEN;
752
753 inline_pos = compress_nh_udp(udp, inline_pos, false);
754 }
755
756 if (net_6lo_ll_prefix_padded_with_zeros((struct in6_addr *)ipv6->dst)) {
757 inline_pos = compress_da(ipv6, pkt, inline_pos, &iphc);
758 goto da_end;
759 }
760
761 if (net_ipv6_is_addr_mcast((struct in6_addr *)ipv6->dst)) {
762 inline_pos = compress_da_mcast(ipv6, inline_pos, &iphc);
763 goto da_end;
764 }
765
766 #if defined(CONFIG_NET_6LO_CONTEXT)
767 dst_ctx = get_dst_addr_ctx(pkt, ipv6);
768 if (dst_ctx) {
769 iphc |= NET_6LO_IPHC_CID_1;
770 inline_pos = compress_da_ctx(ipv6, inline_pos, pkt, &iphc,
771 dst_ctx);
772 goto da_end;
773 }
774 #endif
775 inline_pos = set_da_inline(ipv6, inline_pos, &iphc);
776 da_end:
777
778 if (net_6lo_ll_prefix_padded_with_zeros((struct in6_addr *)ipv6->src)) {
779 inline_pos = compress_sa(ipv6, pkt, inline_pos, &iphc);
780 goto sa_end;
781 }
782
783 if (net_ipv6_is_addr_unspecified((struct in6_addr *)ipv6->src)) {
784 NET_DBG("SAM_00, SAC_1 unspecified src address");
785
786 /* Unspecified IPv6 src address */
787 iphc |= NET_6LO_IPHC_SAC_1;
788 iphc |= NET_6LO_IPHC_SAM_00;
789 goto sa_end;
790 }
791
792 #if defined(CONFIG_NET_6LO_CONTEXT)
793 src_ctx = get_src_addr_ctx(pkt, ipv6);
794 if (src_ctx) {
795 inline_pos = compress_sa_ctx(ipv6, inline_pos, pkt, &iphc,
796 src_ctx);
797 iphc |= NET_6LO_IPHC_CID_1;
798 goto sa_end;
799 }
800 #endif
801 inline_pos = set_sa_inline(ipv6, inline_pos, &iphc);
802 sa_end:
803
804 inline_pos = compress_hoplimit(ipv6, inline_pos, &iphc);
805 inline_pos = compress_nh(ipv6, inline_pos, &iphc);
806 inline_pos = compress_tfl(ipv6, inline_pos, &iphc);
807
808 #if defined(CONFIG_NET_6LO_CONTEXT)
809 if (iphc & NET_6LO_IPHC_CID_1) {
810 inline_pos -= sizeof(uint8_t);
811 *inline_pos = 0;
812
813 if (src_ctx) {
814 *inline_pos = src_ctx->cid << 4;
815 }
816
817 if (dst_ctx) {
818 *inline_pos |= dst_ctx->cid & 0x0F;
819 }
820 }
821 #endif
822
823 inline_pos -= sizeof(iphc);
824 iphc = htons(iphc);
825 memmove(inline_pos, &iphc, sizeof(iphc));
826
827 compressed = inline_pos - pkt->buffer->data;
828
829 net_pkt_cursor_init(pkt);
830 net_pkt_pull(pkt, compressed);
831 net_pkt_compact(pkt);
832
833 return compressed;
834 }
835
836 /* Helper to uncompress Traffic class and Flow label */
uncompress_tfl(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)837 static inline uint8_t *uncompress_tfl(uint16_t iphc, uint8_t *cursor,
838 struct net_ipv6_hdr *ipv6)
839 {
840 uint8_t tcl;
841
842 /* Uncompress tcl and flow label */
843 switch (iphc & NET_6LO_IPHC_TF_11) {
844 case NET_6LO_IPHC_TF_00:
845 NET_DBG("ECN + DSCP + 4-bit Pad + Flow Label");
846
847 tcl = *cursor;
848 cursor++;
849 tcl = (tcl >> 6) | (tcl << 2);
850
851 ipv6->vtc |= ((tcl & 0xF0) >> 4);
852 ipv6->tcflow = ((tcl & 0x0F) << 4) | (*cursor & 0x0F);
853 cursor++;
854
855 memmove(&ipv6->flow, cursor, sizeof(ipv6->flow));
856 cursor += sizeof(ipv6->flow);
857 break;
858 case NET_6LO_IPHC_TF_01:
859 NET_DBG("ECN + 2-bit Pad + Flow Label, DSCP is elided");
860
861 tcl = ((*cursor & 0xF0) >> 6);
862 ipv6->tcflow = ((tcl & 0x0F) << 4) | (*cursor & 0x0F);
863 cursor++;
864
865 memmove(&ipv6->flow, cursor, sizeof(ipv6->flow));
866 cursor += sizeof(ipv6->flow);
867
868 break;
869 case NET_6LO_IPHC_TF_10:
870 NET_DBG("Flow label elided");
871
872 tcl = *cursor;
873 cursor++;
874 tcl = (tcl >> 6) | (tcl << 2);
875
876 ipv6->vtc |= ((tcl & 0xF0) >> 4);
877 ipv6->tcflow = (tcl & 0x0F) << 4;
878 ipv6->flow = 0U;
879
880 break;
881 case NET_6LO_IPHC_TF_11:
882 NET_DBG("Tcl and Flow label elided");
883
884 ipv6->tcflow = 0U;
885 ipv6->flow = 0U;
886
887 break;
888 }
889
890 return cursor;
891 }
892
893 /* Helper to uncompress Hoplimit */
uncompress_hoplimit(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)894 static inline uint8_t *uncompress_hoplimit(uint16_t iphc, uint8_t *cursor,
895 struct net_ipv6_hdr *ipv6)
896 {
897 switch (iphc & NET_6LO_IPHC_HLIM_MASK) {
898 case NET_6LO_IPHC_HLIM:
899 ipv6->hop_limit = *cursor;
900 cursor++;
901
902 break;
903 case NET_6LO_IPHC_HLIM1:
904 ipv6->hop_limit = 1U;
905
906 break;
907 case NET_6LO_IPHC_HLIM64:
908 ipv6->hop_limit = 64U;
909
910 break;
911 case NET_6LO_IPHC_HLIM255:
912 ipv6->hop_limit = 255U;
913
914 break;
915 }
916
917 return cursor;
918 }
919
920 /* Helper to uncompress Source Address */
uncompress_sa(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_pkt * pkt)921 static inline uint8_t *uncompress_sa(uint16_t iphc, uint8_t *cursor,
922 struct net_ipv6_hdr *ipv6,
923 struct net_pkt *pkt)
924 {
925 struct in6_addr src_ip;
926
927 NET_DBG("SAC_0");
928
929 net_ipv6_addr_copy_raw((uint8_t *)&src_ip, ipv6->src);
930
931 switch (iphc & NET_6LO_IPHC_SAM_MASK) {
932 case NET_6LO_IPHC_SAM_00:
933 NET_DBG("SAM_00 full src addr inlined");
934
935 memmove(src_ip.s6_addr, cursor, sizeof(src_ip.s6_addr));
936 cursor += sizeof(src_ip.s6_addr);
937
938 break;
939 case NET_6LO_IPHC_SAM_01:
940 NET_DBG("SAM_01 last 64 bits are inlined");
941
942 memmove(&src_ip.s6_addr[8], cursor, 8);
943 cursor += 8U;
944
945 src_ip.s6_addr32[0] = 0x00;
946 src_ip.s6_addr32[1] = 0x00;
947 src_ip.s6_addr[0] = 0xFE;
948 src_ip.s6_addr[1] = 0x80;
949
950 break;
951 case NET_6LO_IPHC_SAM_10:
952 NET_DBG("SAM_10 src addr 16 bit compressed");
953
954 memmove(&src_ip.s6_addr[14], cursor, 2);
955 cursor += 2U;
956 src_ip.s6_addr16[6] = 0x00;
957
958 src_ip.s6_addr32[0] = 0x00;
959 src_ip.s6_addr32[1] = 0x00;
960 src_ip.s6_addr32[2] = 0x00;
961 src_ip.s6_addr[0] = 0xFE;
962 src_ip.s6_addr[1] = 0x80;
963 src_ip.s6_addr[11] = 0xFF;
964 src_ip.s6_addr[12] = 0xFE;
965
966 break;
967 case NET_6LO_IPHC_SAM_11:
968 NET_DBG("SAM_11 generate src addr from ll");
969
970 net_ipv6_addr_create_iid(&src_ip, net_pkt_lladdr_src(pkt));
971
972 break;
973 }
974
975 net_ipv6_addr_copy_raw(ipv6->src, (uint8_t *)&src_ip);
976
977 return cursor;
978 }
979
980 #if defined(CONFIG_NET_6LO_CONTEXT)
uncompress_sa_ctx(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_6lo_context * ctx,struct net_pkt * pkt)981 static inline uint8_t *uncompress_sa_ctx(uint16_t iphc, uint8_t *cursor,
982 struct net_ipv6_hdr *ipv6,
983 struct net_6lo_context *ctx,
984 struct net_pkt *pkt)
985 {
986 struct in6_addr src_ip;
987
988 net_ipv6_addr_copy_raw((uint8_t *)&src_ip, ipv6->src);
989
990 switch (iphc & NET_6LO_IPHC_SAM_MASK) {
991 case NET_6LO_IPHC_SAM_01:
992 NET_DBG("SAM_01 last 64 bits are inlined");
993
994 /* First 8 bytes are from context */
995 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
996
997 /* And the rest are carried in-line*/
998 memmove(&src_ip.s6_addr[8], cursor, 8);
999 cursor += 8U;
1000
1001 break;
1002 case NET_6LO_IPHC_SAM_10:
1003 NET_DBG("SAM_10 src addr 16 bit compressed");
1004
1005 /* 16 bit carried in-line */
1006 memmove(&src_ip.s6_addr[14], cursor, 2);
1007 cursor += 2U;
1008
1009 /* First 8 bytes are from context */
1010 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1011
1012 src_ip.s6_addr32[2] = 0x00;
1013 src_ip.s6_addr16[6] = 0x00;
1014 src_ip.s6_addr[11] = 0xFF;
1015 src_ip.s6_addr[12] = 0xFE;
1016
1017 break;
1018 case NET_6LO_IPHC_SAM_11:
1019 NET_DBG("SAM_11 generate src addr from ll");
1020
1021 /* RFC 6282, 3.1.1. If SAC = 1 and SAM = 11
1022 * Derive addr using context information and
1023 * the encapsulating header.
1024 * (e.g., 802.15.4 or IPv6 source address).
1025 */
1026 net_ipv6_addr_create_iid(&src_ip, net_pkt_lladdr_src(pkt));
1027
1028 /* net_ipv6_addr_create_iid will copy first 8 bytes
1029 * as link local prefix.
1030 * Overwrite first 8 bytes from context prefix here.
1031 */
1032 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1033 break;
1034 }
1035
1036 net_ipv6_addr_copy_raw(ipv6->src, (uint8_t *)&src_ip);
1037
1038 return cursor;
1039 }
1040 #endif
1041
1042 /* Helpers to uncompress Destination Address */
uncompress_da_mcast(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)1043 static inline uint8_t *uncompress_da_mcast(uint16_t iphc, uint8_t *cursor,
1044 struct net_ipv6_hdr *ipv6)
1045 {
1046 struct in6_addr dst_ip;
1047
1048 NET_DBG("Dst is multicast");
1049
1050 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1051
1052 if (iphc & NET_6LO_IPHC_DAC_1) {
1053 NET_WARN("Unsupported DAM options");
1054 return 0;
1055 }
1056
1057 /* If M=1 and DAC=0:
1058 * 00: 128 bits, The full address is carried in-line.
1059 * 01: 48 bits, The address takes the form ffXX::00XX:XXXX:XXXX.
1060 * 10: 32 bits, The address takes the form ffXX::00XX:XXXX.
1061 * 11: 8 bits, The address takes the form ff02::00XX.
1062 */
1063
1064 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1065 case NET_6LO_IPHC_DAM_00:
1066 NET_DBG("DAM_00 full dst addr inlined");
1067
1068 memmove(&dst_ip.s6_addr[0], cursor,
1069 sizeof(dst_ip.s6_addr));
1070
1071 cursor += sizeof(dst_ip.s6_addr);
1072 break;
1073 case NET_6LO_IPHC_DAM_01:
1074 NET_DBG("DAM_01 2nd byte and last five bytes");
1075
1076 dst_ip.s6_addr[1] = *cursor;
1077 cursor++;
1078 memmove(&dst_ip.s6_addr[11], cursor, 5);
1079 cursor += 5U;
1080
1081
1082 dst_ip.s6_addr[0] = 0xFF;
1083 dst_ip.s6_addr16[1] = 0x00;
1084 dst_ip.s6_addr32[1] = 0x00;
1085 dst_ip.s6_addr[10] = 0x00;
1086 dst_ip.s6_addr16[4] = 0x00;
1087
1088 break;
1089 case NET_6LO_IPHC_DAM_10:
1090 NET_DBG("DAM_10 2nd byte and last three bytes");
1091
1092 dst_ip.s6_addr[1] = *cursor;
1093 cursor++;
1094 memmove(&dst_ip.s6_addr[13], cursor, 3);
1095 cursor += 3U;
1096
1097 dst_ip.s6_addr[0] = 0xFF;
1098 dst_ip.s6_addr16[1] = 0x00;
1099 dst_ip.s6_addr32[1] = 0x00;
1100 dst_ip.s6_addr32[2] = 0x00;
1101 dst_ip.s6_addr[12] = 0x00;
1102
1103 break;
1104 case NET_6LO_IPHC_DAM_11:
1105 NET_DBG("DAM_11 8 bit compressed");
1106
1107 dst_ip.s6_addr[15] = *cursor;
1108 cursor++;
1109 dst_ip.s6_addr[14] = 0x00;
1110
1111 dst_ip.s6_addr32[0] = 0x00;
1112 dst_ip.s6_addr32[1] = 0x00;
1113 dst_ip.s6_addr32[2] = 0x00;
1114 dst_ip.s6_addr16[6] = 0x00;
1115 dst_ip.s6_addr[0] = 0xFF;
1116 dst_ip.s6_addr[1] = 0x02;
1117
1118 break;
1119 }
1120
1121 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1122
1123 return cursor;
1124 }
1125
1126 /* Helper to uncompress Destination Address */
uncompress_da(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_pkt * pkt)1127 static inline uint8_t *uncompress_da(uint16_t iphc, uint8_t *cursor,
1128 struct net_ipv6_hdr *ipv6,
1129 struct net_pkt *pkt)
1130 {
1131 struct in6_addr dst_ip;
1132
1133 NET_DBG("DAC_0");
1134
1135 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1136
1137 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1138 case NET_6LO_IPHC_DAM_00:
1139 NET_DBG("DAM_00 full dst addr inlined");
1140
1141 memmove(&dst_ip.s6_addr[0], cursor,
1142 sizeof(dst_ip.s6_addr));
1143 cursor += sizeof(dst_ip.s6_addr);
1144
1145 break;
1146 case NET_6LO_IPHC_DAM_01:
1147 NET_DBG("DAM_01 last 64 bits are inlined");
1148
1149 memmove(&dst_ip.s6_addr[8], cursor, 8);
1150 cursor += 8U;
1151
1152 dst_ip.s6_addr32[0] = 0x00;
1153 dst_ip.s6_addr32[1] = 0x00;
1154 dst_ip.s6_addr[0] = 0xFE;
1155 dst_ip.s6_addr[1] = 0x80;
1156
1157 break;
1158 case NET_6LO_IPHC_DAM_10:
1159 NET_DBG("DAM_10 dst addr 16 bit compressed");
1160
1161 memmove(&dst_ip.s6_addr[14], cursor, 2);
1162 cursor += 2U;
1163
1164 dst_ip.s6_addr32[0] = 0x00;
1165 dst_ip.s6_addr32[1] = 0x00;
1166 dst_ip.s6_addr32[2] = 0x00;
1167 dst_ip.s6_addr16[6] = 0x00;
1168 dst_ip.s6_addr[0] = 0xFE;
1169 dst_ip.s6_addr[1] = 0x80;
1170 dst_ip.s6_addr[11] = 0xFF;
1171 dst_ip.s6_addr[12] = 0xFE;
1172
1173 break;
1174 case NET_6LO_IPHC_DAM_11:
1175 NET_DBG("DAM_11 generate dst addr from ll");
1176
1177 net_ipv6_addr_create_iid(&dst_ip, net_pkt_lladdr_dst(pkt));
1178
1179 break;
1180 }
1181
1182 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1183
1184 return cursor;
1185 }
1186
1187 #if defined(CONFIG_NET_6LO_CONTEXT)
uncompress_da_ctx(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_6lo_context * ctx,struct net_pkt * pkt)1188 static inline uint8_t *uncompress_da_ctx(uint16_t iphc, uint8_t *cursor,
1189 struct net_ipv6_hdr *ipv6,
1190 struct net_6lo_context *ctx,
1191 struct net_pkt *pkt)
1192 {
1193 struct in6_addr dst_ip;
1194
1195 NET_DBG("DAC_1");
1196
1197 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1198
1199 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1200 case NET_6LO_IPHC_DAM_01:
1201 NET_DBG("DAM_01 last 64 bits are inlined");
1202
1203 /* Last 8 bytes carried in-line */
1204 memmove(&dst_ip.s6_addr[8], cursor, 8);
1205 cursor += 8U;
1206
1207 /* First 8 bytes are from context */
1208 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1209
1210 break;
1211 case NET_6LO_IPHC_DAM_10:
1212 NET_DBG("DAM_10 src addr 16 bit compressed");
1213
1214 /* 16 bit carried in-line */
1215 memmove(&dst_ip.s6_addr[14], cursor, 2);
1216 cursor += 2U;
1217
1218 /* First 8 bytes are from context */
1219 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1220
1221 dst_ip.s6_addr32[2] = 0x00;
1222 dst_ip.s6_addr16[6] = 0x00;
1223 dst_ip.s6_addr[11] = 0xFF;
1224 dst_ip.s6_addr[12] = 0xFE;
1225
1226 break;
1227 case NET_6LO_IPHC_DAM_11:
1228 NET_DBG("DAM_11 generate src addr from ll");
1229
1230 /* RFC 6282, 3.1.1. If SAC = 1 and SAM = 11
1231 * Derive addr using context information and
1232 * the encapsulating header.
1233 * (e.g., 802.15.4 or IPv6 source address).
1234 */
1235 net_ipv6_addr_create_iid(&dst_ip, net_pkt_lladdr_dst(pkt));
1236
1237 /* net_ipv6_addr_create_iid will copy first 8 bytes
1238 * as link local prefix.
1239 * Overwrite first 8 bytes from context prefix here.
1240 */
1241 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1242
1243 break;
1244 }
1245
1246 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1247
1248 return cursor;
1249 }
1250 #endif
1251
1252 /* Helper to uncompress NH UDP */
uncompress_nh_udp(uint8_t nhc,uint8_t * cursor,struct net_udp_hdr * udp)1253 static uint8_t *uncompress_nh_udp(uint8_t nhc, uint8_t *cursor,
1254 struct net_udp_hdr *udp)
1255 {
1256
1257 /* Port uncompression
1258 * 00: All 16 bits for src and dst are inlined
1259 * 01: src, 16 bits are lined, dst(0xf0) 8 bits are inlined
1260 * 10: dst, 16 bits are lined, src(0xf0) 8 bits are inlined
1261 * 11: src, dst (0xf0b) 4 bits are inlined
1262 */
1263
1264 /* UDP header uncompression */
1265 switch (nhc & NET_6LO_NHC_UDP_PORT_11) {
1266 case NET_6LO_NHC_UDP_PORT_00:
1267 NET_DBG("src and dst ports are inlined");
1268
1269 memmove(&udp->src_port, cursor, sizeof(udp->src_port));
1270 cursor += sizeof(udp->src_port);
1271 memmove(&udp->dst_port, cursor, sizeof(udp->dst_port));
1272 cursor += sizeof(udp->dst_port);
1273
1274 break;
1275 case NET_6LO_NHC_UDP_PORT_01:
1276 NET_DBG("src full, dst 8 bits inlined");
1277
1278 memmove(&udp->src_port, cursor, sizeof(udp->src_port));
1279 cursor += sizeof(udp->src_port);
1280 udp->dst_port = htons(((uint16_t)NET_6LO_NHC_UDP_8_BIT_PORT << 8) |
1281 *cursor);
1282 cursor++;
1283
1284 break;
1285 case NET_6LO_NHC_UDP_PORT_10:
1286 NET_DBG("src 8 bits, dst full inlined");
1287
1288 udp->src_port = htons(((uint16_t)NET_6LO_NHC_UDP_8_BIT_PORT << 8) |
1289 *cursor);
1290 cursor++;
1291 memmove(&udp->dst_port, cursor, sizeof(udp->dst_port));
1292 cursor += sizeof(udp->dst_port);
1293
1294 break;
1295 case NET_6LO_NHC_UDP_PORT_11:
1296 NET_DBG("src and dst 4 bits inlined");
1297
1298 udp->src_port = htons((NET_6LO_NHC_UDP_4_BIT_PORT << 4) |
1299 (*cursor >> 4));
1300
1301 udp->dst_port = htons((NET_6LO_NHC_UDP_4_BIT_PORT << 4) |
1302 (*cursor & 0x0F));
1303 cursor++;
1304
1305 break;
1306 }
1307
1308 if (!(nhc & NET_6LO_NHC_UDP_CHECKSUM)) {
1309 memmove(&udp->chksum, cursor, sizeof(udp->chksum));
1310 cursor += sizeof(udp->chksum);
1311 }
1312
1313 return cursor;
1314 }
1315
1316 #if defined(CONFIG_NET_6LO_CONTEXT)
1317 /* Helper function to uncompress src and dst contexts */
uncompress_cid(struct net_pkt * pkt,uint8_t cid,struct net_6lo_context ** src,struct net_6lo_context ** dst)1318 static inline void uncompress_cid(struct net_pkt *pkt, uint8_t cid,
1319 struct net_6lo_context **src,
1320 struct net_6lo_context **dst)
1321 {
1322 uint8_t cid_tmp;
1323
1324 /* Extract source and destination Context Index,
1325 * Either src or dest address is context based or both.
1326 */
1327 cid_tmp = (cid >> 4) & 0x0F;
1328 *src = get_6lo_context_by_cid(net_pkt_iface(pkt), cid_tmp);
1329 if (!(*src)) {
1330 NET_DBG("Unknown src cid %d", cid_tmp);
1331 }
1332
1333 cid_tmp = cid & 0x0F;
1334 *dst = get_6lo_context_by_cid(net_pkt_iface(pkt), cid_tmp);
1335 if (!(*dst)) {
1336 NET_DBG("Unknown dst cid %d", cid_tmp);
1337 }
1338 }
1339 #endif
1340
uncompress_IPHC_header(struct net_pkt * pkt)1341 static bool uncompress_IPHC_header(struct net_pkt *pkt)
1342 {
1343 struct net_udp_hdr *udp = NULL;
1344 struct net_buf *frag = NULL;
1345 uint8_t nhc = 0;
1346 int nhc_inline_size = 0;
1347 struct net_ipv6_hdr *ipv6;
1348 uint16_t len;
1349 uint16_t iphc;
1350 int inline_size, compressed_hdr_size;
1351 size_t diff;
1352 uint8_t *cursor;
1353 #if defined(CONFIG_NET_6LO_CONTEXT)
1354 struct net_6lo_context *src = NULL;
1355 struct net_6lo_context *dst = NULL;
1356 #endif
1357
1358 iphc = ntohs(UNALIGNED_GET((uint16_t *)pkt->buffer->data));
1359
1360 inline_size = get_ihpc_inlined_size(iphc);
1361 if (inline_size < 0) {
1362 return false;
1363 }
1364
1365 compressed_hdr_size = sizeof(iphc) + inline_size;
1366 diff = sizeof(struct net_ipv6_hdr) - compressed_hdr_size;
1367
1368 if (iphc & NET_6LO_IPHC_NH_MASK) {
1369 nhc = *(pkt->buffer->data + sizeof(iphc) + inline_size);
1370 if ((nhc & 0xF8) != NET_6LO_NHC_UDP_BARE) {
1371 NET_ERR("Unsupported next header");
1372 return false;
1373 }
1374
1375 nhc_inline_size = get_udp_nhc_inlined_size(nhc);
1376 compressed_hdr_size += sizeof(uint8_t) + nhc_inline_size;
1377 diff += sizeof(struct net_udp_hdr) - sizeof(uint8_t) -
1378 nhc_inline_size;
1379 }
1380
1381 if (pkt->buffer->len < compressed_hdr_size) {
1382 NET_ERR("Scattered compressed header?");
1383 return false;
1384 }
1385
1386 if (net_buf_tailroom(pkt->buffer) >= diff) {
1387 NET_DBG("Enough tailroom. Uncompress inplace");
1388 frag = pkt->buffer;
1389 net_buf_add(frag, diff);
1390 cursor = frag->data + diff;
1391 memmove(cursor, frag->data, frag->len - diff);
1392 } else {
1393 size_t frag_len = nhc ? NET_IPV6UDPH_LEN : NET_IPV6H_LEN;
1394
1395 NET_DBG("Not enough tailroom. Get new fragment");
1396 cursor = pkt->buffer->data;
1397 frag = net_pkt_get_frag(pkt, frag_len, NET_6LO_RX_PKT_TIMEOUT);
1398 if (!frag) {
1399 NET_ERR("Can't get frag for uncompression");
1400 return false;
1401 }
1402
1403 net_buf_pull(pkt->buffer, compressed_hdr_size);
1404 net_buf_add(frag, frag_len);
1405 }
1406
1407 ipv6 = (struct net_ipv6_hdr *)(frag->data);
1408 cursor += sizeof(iphc);
1409
1410 if (iphc & NET_6LO_IPHC_CID_1) {
1411 #if defined(CONFIG_NET_6LO_CONTEXT)
1412 uncompress_cid(pkt, *cursor, &src, &dst);
1413 cursor++;
1414 #else
1415 NET_ERR("Context based uncompression not enabled");
1416 return false;
1417 #endif
1418 }
1419
1420 /* Version is always 6 */
1421 ipv6->vtc = 0x60;
1422 net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN);
1423
1424 /* Uncompress Traffic class and Flow label */
1425 cursor = uncompress_tfl(iphc, cursor, ipv6);
1426
1427 if (!(iphc & NET_6LO_IPHC_NH_MASK)) {
1428 ipv6->nexthdr = *cursor;
1429 cursor++;
1430 }
1431
1432 /* Uncompress Hoplimit */
1433 cursor = uncompress_hoplimit(iphc, cursor, ipv6);
1434
1435 /* Uncompress Source Address */
1436 if (iphc & NET_6LO_IPHC_SAC_1) {
1437 NET_DBG("SAC_1");
1438
1439 if ((iphc & NET_6LO_IPHC_SAM_MASK) == NET_6LO_IPHC_SAM_00) {
1440 NET_DBG("SAM_00 unspecified address");
1441 memset(&ipv6->src[0], 0,
1442 sizeof(ipv6->src));
1443 } else if (IS_ENABLED(CONFIG_NET_6LO_CONTEXT)) {
1444 #if defined(CONFIG_NET_6LO_CONTEXT)
1445 if (!src) {
1446 NET_ERR("Src context doesn't exists");
1447 goto fail;
1448 }
1449
1450 cursor = uncompress_sa_ctx(iphc, cursor, ipv6, src, pkt);
1451 #endif
1452 } else {
1453 NET_ERR("Context based uncompression not enabled");
1454 goto fail;
1455 }
1456 } else {
1457 cursor = uncompress_sa(iphc, cursor, ipv6, pkt);
1458 }
1459
1460 /* Uncompress Destination Address */
1461 if (iphc & NET_6LO_IPHC_M_1) {
1462 if (iphc & NET_6LO_IPHC_DAC_1) {
1463 /* TODO: DAM00 Unicast-Prefix-based IPv6 Multicast
1464 * Addresses. DAM_01, DAM_10 and DAM_11 are reserved.
1465 */
1466 NET_ERR("DAC_1 and M_1 is not supported");
1467 goto fail;
1468 } else {
1469 cursor = uncompress_da_mcast(iphc, cursor, ipv6);
1470 }
1471 } else {
1472 if (iphc & NET_6LO_IPHC_DAC_1) {
1473 #if defined(CONFIG_NET_6LO_CONTEXT)
1474 if (!dst) {
1475 NET_ERR("Dst context doesn't exists");
1476 goto fail;
1477 }
1478
1479 cursor = uncompress_da_ctx(iphc, cursor, ipv6, dst, pkt);
1480 #else
1481 NET_ERR("Context based uncompression not enabled");
1482 goto fail;
1483 #endif
1484 } else {
1485 cursor = uncompress_da(iphc, cursor, ipv6, pkt);
1486 }
1487 }
1488
1489 if (iphc & NET_6LO_IPHC_NH_MASK) {
1490 ipv6->nexthdr = IPPROTO_UDP;
1491 udp = (struct net_udp_hdr *)(frag->data + NET_IPV6H_LEN);
1492 /* skip nhc */
1493 cursor++;
1494 cursor = uncompress_nh_udp(nhc, cursor, udp);
1495 }
1496
1497 if (frag != pkt->buffer) {
1498 /* Insert the fragment (this one holds uncompressed headers) */
1499 net_pkt_frag_insert(pkt, frag);
1500 }
1501
1502 /* Set IPv6 header and UDP (if next header is) length */
1503 len = net_pkt_get_len(pkt) - NET_IPV6H_LEN;
1504 ipv6->len = htons(len);
1505
1506 if (ipv6->nexthdr == IPPROTO_UDP && udp) {
1507 udp->len = htons(len);
1508
1509 if (nhc & NET_6LO_NHC_UDP_CHECKSUM) {
1510 udp->chksum = net_calc_chksum_udp(pkt);
1511 }
1512 }
1513
1514 net_pkt_cursor_init(pkt);
1515
1516 return true;
1517
1518 fail:
1519 if (frag != pkt->buffer) {
1520 net_pkt_frag_unref(frag);
1521 }
1522
1523 return false;
1524 }
1525
1526 /* Adds IPv6 dispatch as first byte and adjust fragments */
compress_ipv6_header(struct net_pkt * pkt)1527 static inline int compress_ipv6_header(struct net_pkt *pkt)
1528 {
1529 struct net_buf *buffer = pkt->buffer;
1530
1531 if (net_buf_tailroom(buffer) >= 1U) {
1532 memmove(buffer->data + 1U, buffer->data, buffer->len);
1533 net_buf_add(buffer, 1U);
1534 buffer->data[0] = NET_6LO_DISPATCH_IPV6;
1535 return 0;
1536 }
1537
1538 buffer = net_pkt_get_frag(pkt, 1, K_FOREVER);
1539 if (!buffer) {
1540 return -ENOBUFS;
1541 }
1542
1543 buffer->data[0] = NET_6LO_DISPATCH_IPV6;
1544 net_buf_add(buffer, 1);
1545
1546 net_pkt_frag_insert(pkt, buffer);
1547
1548 /* Compact the fragments, so that gaps will be filled */
1549 net_pkt_compact(pkt);
1550
1551 return 0;
1552 }
1553
uncompress_ipv6_header(struct net_pkt * pkt)1554 static inline bool uncompress_ipv6_header(struct net_pkt *pkt)
1555 {
1556 /* Pull off IPv6 dispatch header and adjust data and length */
1557 net_buf_pull(pkt->buffer, 1U);
1558 net_pkt_cursor_init(pkt);
1559
1560 return true;
1561 }
1562
net_6lo_compress(struct net_pkt * pkt,bool iphc)1563 int net_6lo_compress(struct net_pkt *pkt, bool iphc)
1564 {
1565 if (iphc) {
1566 return compress_IPHC_header(pkt);
1567 } else {
1568 return compress_ipv6_header(pkt);
1569 }
1570 }
1571
net_6lo_uncompress(struct net_pkt * pkt)1572 bool net_6lo_uncompress(struct net_pkt *pkt)
1573 {
1574 NET_ASSERT(pkt && pkt->frags);
1575
1576 if ((pkt->frags->data[0] & NET_6LO_DISPATCH_IPHC_MASK) ==
1577 NET_6LO_DISPATCH_IPHC) {
1578 /* Uncompress IPHC header */
1579 return uncompress_IPHC_header(pkt);
1580
1581 } else if (pkt->frags->data[0] == NET_6LO_DISPATCH_IPV6) {
1582 /* Uncompress IPv6 header, it has only IPv6 dispatch in the
1583 * beginning */
1584 return uncompress_ipv6_header(pkt);
1585 }
1586
1587 NET_DBG("pkt %p is not compressed", pkt);
1588
1589 return true;
1590 }
1591
net_6lo_uncompress_hdr_diff(struct net_pkt * pkt)1592 int net_6lo_uncompress_hdr_diff(struct net_pkt *pkt)
1593 {
1594 int inline_size, compressed_hdr_size, nhc_inline_size, diff;
1595 uint16_t iphc;
1596 uint8_t nhc;
1597
1598 if (pkt->frags->data[0] == NET_6LO_DISPATCH_IPV6) {
1599 return -1;
1600 }
1601
1602 if ((pkt->frags->data[0] & NET_6LO_DISPATCH_IPHC_MASK) !=
1603 NET_6LO_DISPATCH_IPHC) {
1604 return 0;
1605 }
1606
1607 iphc = ntohs(UNALIGNED_GET((uint16_t *)pkt->buffer->data));
1608
1609 inline_size = get_ihpc_inlined_size(iphc);
1610 if (inline_size < 0) {
1611 return INT_MAX;
1612 }
1613
1614 compressed_hdr_size = sizeof(iphc) + inline_size;
1615 diff = sizeof(struct net_ipv6_hdr) - compressed_hdr_size;
1616
1617 if (iphc & NET_6LO_IPHC_NH_MASK) {
1618 nhc = *(pkt->buffer->data + sizeof(iphc) + inline_size);
1619 if ((nhc & 0xF8) != NET_6LO_NHC_UDP_BARE) {
1620 NET_ERR("Unsupported next header");
1621 return INT_MAX;
1622 }
1623
1624 nhc_inline_size = get_udp_nhc_inlined_size(nhc);
1625 diff += sizeof(struct net_udp_hdr) - sizeof(uint8_t) -
1626 nhc_inline_size;
1627 }
1628
1629 return diff;
1630 }
1631