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 NET_ASSERT(net_pkt_lladdr_src(pkt)->addr);
369
370 /* Address is fully elided */
371 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->src,
372 net_pkt_lladdr_src(pkt))) {
373 NET_DBG("SAM_11 src address is fully elided");
374
375 *iphc |= NET_6LO_IPHC_SAM_11;
376 return inline_ptr;
377 }
378
379 /* Following 64 bits are 0000:00ff:fe00:XXXX */
380 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->src)) {
381 NET_DBG("SAM_10 src addr 16 bit compressible");
382 *iphc |= NET_6LO_IPHC_SAM_10;
383
384 inline_ptr -= sizeof(uint16_t);
385 memmove(inline_ptr, &ipv6->src[14], sizeof(uint16_t));
386
387 return inline_ptr;
388 }
389
390 NET_DBG("SAM_01 src 64 bits are inlined");
391 /* Remaining 64 bits are in-line */
392 *iphc |= NET_6LO_IPHC_SAM_01;
393
394 inline_ptr -= 8U;
395 memmove(inline_ptr, &ipv6->src[8], 8U);
396
397 return inline_ptr;
398 }
399
set_sa_inline(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)400 static uint8_t *set_sa_inline(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
401 uint16_t *iphc)
402 {
403 *iphc |= NET_6LO_IPHC_SAM_00;
404 inline_ptr -= 16U;
405 memmove(inline_ptr, &ipv6->src[0], 16U);
406 return inline_ptr;
407 }
408
409 #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)410 static uint8_t *compress_sa_ctx(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
411 struct net_pkt *pkt, uint16_t *iphc,
412 struct net_6lo_context *src)
413 {
414 NET_ASSERT(net_pkt_lladdr_src(pkt)->addr);
415
416 NET_DBG("SAC_1 src address context based");
417 *iphc |= NET_6LO_IPHC_SAC_1;
418
419 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->src,
420 net_pkt_lladdr_src(pkt))) {
421 NET_DBG("SAM_11 src address is fully elided");
422
423 /* Address is fully elided */
424 *iphc |= NET_6LO_IPHC_SAM_11;
425 return inline_ptr;
426 }
427
428 /* Following 64 bits are 0000:00ff:fe00:XXXX */
429 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->src)) {
430 NET_DBG("SAM_10 src addr 16 bit compressible");
431
432 *iphc |= NET_6LO_IPHC_SAM_10;
433
434 inline_ptr -= sizeof(uint16_t);
435 memmove(inline_ptr, &ipv6->src[14], sizeof(uint16_t));
436 return inline_ptr;
437 }
438
439 NET_DBG("SAM_01 src remaining 64 bits are inlined");
440
441 /* Remaining 64 bits are in-line */
442 *iphc |= NET_6LO_IPHC_SAM_01;
443
444 inline_ptr -= 8U;
445 memmove(inline_ptr, &ipv6->src[8], 8U);
446
447 return inline_ptr;
448 }
449 #endif
450
451 /* Helpers to compress Destination Address */
compress_da_mcast(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)452 static uint8_t *compress_da_mcast(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
453 uint16_t *iphc)
454 {
455 *iphc |= NET_6LO_IPHC_M_1;
456
457 NET_DBG("M_1 dst is mcast");
458
459 if (net_6lo_maddr_8_bit_compressible((struct in6_addr *)ipv6->dst)) {
460 NET_DBG("DAM_11 dst maddr 8 bit compressible");
461
462 /* last byte */
463 *iphc |= NET_6LO_IPHC_DAM_11;
464
465 inline_ptr -= sizeof(uint8_t);
466 memmove(inline_ptr, &ipv6->dst[15], sizeof(uint8_t));
467
468 return inline_ptr;
469 }
470
471 if (net_6lo_maddr_32_bit_compressible((struct in6_addr *)ipv6->dst)) {
472 NET_DBG("DAM_10 4 bytes: 2nd byte + last three bytes");
473
474 /* 4 bytes: 2nd byte + last three bytes */
475 *iphc |= NET_6LO_IPHC_DAM_10;
476
477 inline_ptr -= 3U;
478 memmove(inline_ptr, &ipv6->dst[13], 3U);
479
480 inline_ptr -= sizeof(uint8_t);
481 memmove(inline_ptr, &ipv6->dst[1], sizeof(uint8_t));
482
483 return inline_ptr;
484 }
485
486 if (net_6lo_maddr_48_bit_compressible((struct in6_addr *)ipv6->dst)) {
487 NET_DBG("DAM_01 6 bytes: 2nd byte + last five bytes");
488
489 /* 6 bytes: 2nd byte + last five bytes */
490 *iphc |= NET_6LO_IPHC_DAM_01;
491
492 inline_ptr -= 5U;
493 memmove(inline_ptr, &ipv6->dst[11], 5U);
494
495 inline_ptr -= sizeof(uint8_t);
496 memmove(inline_ptr, &ipv6->dst[1], sizeof(uint8_t));
497
498 return inline_ptr;
499 }
500
501 NET_DBG("DAM_00 dst complete addr inlined");
502
503 /* complete address NET_6LO_IPHC_DAM_00 */
504 inline_ptr -= 16U;
505 memmove(inline_ptr, &ipv6->dst[0], 16U);
506
507 return inline_ptr;
508 }
509
compress_da(struct net_ipv6_hdr * ipv6,struct net_pkt * pkt,uint8_t * inline_ptr,uint16_t * iphc)510 static uint8_t *compress_da(struct net_ipv6_hdr *ipv6, struct net_pkt *pkt,
511 uint8_t *inline_ptr, uint16_t *iphc)
512 {
513 NET_ASSERT(net_pkt_lladdr_dst(pkt)->addr);
514
515 /* Address is fully elided */
516 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->dst,
517 net_pkt_lladdr_dst(pkt))) {
518 NET_DBG("DAM_11 dst addr fully elided");
519
520 *iphc |= NET_6LO_IPHC_DAM_11;
521 return inline_ptr;
522 }
523
524 /* Following 64 bits are 0000:00ff:fe00:XXXX */
525 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->dst)) {
526 NET_DBG("DAM_10 dst addr 16 bit compressible");
527
528 *iphc |= NET_6LO_IPHC_DAM_10;
529
530 inline_ptr -= sizeof(uint16_t);
531 memmove(inline_ptr, &ipv6->dst[14], sizeof(uint16_t));
532 return inline_ptr;
533 }
534
535 NET_DBG("DAM_01 remaining 64 bits are inlined");
536
537 /* Remaining 64 bits are in-line */
538 *iphc |= NET_6LO_IPHC_DAM_01;
539
540 inline_ptr -= 8U;
541 memmove(inline_ptr, &ipv6->dst[8], 8U);
542
543 return inline_ptr;
544 }
545
set_da_inline(struct net_ipv6_hdr * ipv6,uint8_t * inline_ptr,uint16_t * iphc)546 static uint8_t *set_da_inline(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
547 uint16_t *iphc)
548 {
549 *iphc |= NET_6LO_IPHC_DAM_00;
550 inline_ptr -= 16U;
551 memmove(inline_ptr, &ipv6->dst[0], 16U);
552 return inline_ptr;
553 }
554
555 #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)556 static uint8_t *compress_da_ctx(struct net_ipv6_hdr *ipv6, uint8_t *inline_ptr,
557 struct net_pkt *pkt, uint16_t *iphc,
558 struct net_6lo_context *dst)
559 {
560 *iphc |= NET_6LO_IPHC_DAC_1;
561
562 if (net_ipv6_addr_based_on_ll((struct in6_addr *)ipv6->dst,
563 net_pkt_lladdr_dst(pkt))) {
564 NET_DBG("DAM_11 dst addr fully elided");
565
566 *iphc |= NET_6LO_IPHC_DAM_11;
567 return inline_ptr;
568 }
569
570 /* Following 64 bits are 0000:00ff:fe00:XXXX */
571 if (net_6lo_addr_16_bit_compressible((struct in6_addr *)ipv6->dst)) {
572 NET_DBG("DAM_10 dst addr 16 bit compressible");
573
574 *iphc |= NET_6LO_IPHC_DAM_10;
575 inline_ptr -= sizeof(uint16_t);
576 memmove(inline_ptr, &ipv6->dst[14], sizeof(uint16_t));
577 return inline_ptr;
578 }
579
580 NET_DBG("DAM_01 remaining 64 bits are inlined");
581
582 /* Remaining 64 bits are in-line */
583 *iphc |= NET_6LO_IPHC_DAM_01;
584
585 inline_ptr -= 8U;
586 memmove(inline_ptr, &ipv6->dst[8], 8U);
587
588 return inline_ptr;
589 }
590 #endif
591
592 /* Helper to compress Next header UDP */
compress_nh_udp(struct net_udp_hdr * udp,uint8_t * inline_ptr,bool compress_checksum)593 static inline uint8_t *compress_nh_udp(struct net_udp_hdr *udp, uint8_t *inline_ptr,
594 bool compress_checksum)
595 {
596 uint8_t nhc = NET_6LO_NHC_UDP_BARE;
597 uint8_t *inline_ptr_udp = inline_ptr;
598 uint8_t tmp;
599
600 /* 4.3.3 UDP LOWPAN_NHC Format
601 * 0 1 2 3 4 5 6 7
602 * +---+---+---+---+---+---+---+---+
603 * | 1 | 1 | 1 | 1 | 0 | C | P |
604 * +---+---+---+---+---+---+---+---+
605 */
606
607 /* Port compression
608 * 00: All 16 bits for src and dst are inlined.
609 * 01: All 16 bits for src port inlined. First 8 bits of dst port is
610 * 0xf0 and elided. The remaining 8 bits of dst port inlined.
611 * 10: First 8 bits of src port 0xf0 and elided. The remaining 8 bits
612 * of src port inlined. All 16 bits of dst port inlined.
613 * 11: First 12 bits of both src and dst are 0xf0b and elided. The
614 * remaining 4 bits for each are inlined.
615 */
616
617 if (compress_checksum) {
618 nhc |= NET_6LO_NHC_UDP_CHECKSUM;
619 } else {
620 inline_ptr_udp -= sizeof(udp->chksum);
621 memmove(inline_ptr_udp, &udp->chksum, sizeof(udp->chksum));
622 }
623
624 if ((((htons(udp->src_port) >> 4) & 0xFFF) ==
625 NET_6LO_NHC_UDP_4_BIT_PORT) &&
626 (((htons(udp->dst_port) >> 4) & 0xFFF) ==
627 NET_6LO_NHC_UDP_4_BIT_PORT)) {
628
629 NET_DBG("UDP ports src and dst 4 bits inlined");
630 /** src: first 16 bits elided, next 4 bits inlined
631 * dst: first 16 bits elided, next 4 bits inlined
632 */
633 nhc |= NET_6LO_NHC_UDP_PORT_11;
634
635 tmp = (uint8_t)(htons(udp->src_port));
636 tmp = tmp << 4;
637
638 tmp |= (((uint8_t)(htons(udp->dst_port))) & 0x0F);
639 inline_ptr_udp -= sizeof(tmp);
640 *inline_ptr_udp = tmp;
641
642 } else if (((htons(udp->dst_port) >> 8) & 0xFF) ==
643 NET_6LO_NHC_UDP_8_BIT_PORT) {
644
645 NET_DBG("UDP ports src full, dst 8 bits inlined");
646 /* dst: first 8 bits elided, next 8 bits inlined
647 * src: fully carried inline
648 */
649 nhc |= NET_6LO_NHC_UDP_PORT_01;
650
651 inline_ptr_udp -= sizeof(uint8_t);
652 *inline_ptr_udp = (uint8_t)(htons(udp->dst_port));
653
654 inline_ptr_udp -= sizeof(udp->src_port);
655 memmove(inline_ptr_udp, &udp->src_port, sizeof(udp->src_port));
656
657 } else if (((htons(udp->src_port) >> 8) & 0xFF) ==
658 NET_6LO_NHC_UDP_8_BIT_PORT) {
659
660 NET_DBG("UDP ports src 8bits, dst full inlined");
661 /* src: first 8 bits elided, next 8 bits inlined
662 * dst: fully carried inline
663 */
664 nhc |= NET_6LO_NHC_UDP_PORT_10;
665
666 inline_ptr_udp -= sizeof(udp->dst_port);
667 memmove(inline_ptr_udp, &udp->dst_port, sizeof(udp->dst_port));
668
669 inline_ptr_udp -= sizeof(uint8_t);
670 *inline_ptr_udp = (uint8_t)(htons(udp->src_port));
671
672 } else {
673 NET_DBG("Can not compress ports, ports are inlined");
674
675 /* can not compress ports, ports are inlined */
676 inline_ptr_udp -= sizeof(udp->dst_port) + sizeof(udp->src_port);
677 memmove(inline_ptr_udp, &udp->src_port,
678 sizeof(udp->dst_port) + sizeof(udp->src_port));
679 }
680
681 inline_ptr_udp -= sizeof(nhc);
682 *inline_ptr_udp = nhc;
683
684 return inline_ptr_udp;
685 }
686
687 #if defined(CONFIG_NET_6LO_CONTEXT)
688
get_src_addr_ctx(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6)689 static struct net_6lo_context *get_src_addr_ctx(struct net_pkt *pkt,
690 struct net_ipv6_hdr *ipv6)
691 {
692 /* If compress flag is unset means use only in uncompression. */
693 struct net_6lo_context *src;
694
695 src = get_6lo_context_by_addr(net_pkt_iface(pkt),
696 (struct in6_addr *)ipv6->src);
697 if (!src || !src->compress) {
698 return NULL;
699 }
700
701 return src;
702 }
703
get_dst_addr_ctx(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6)704 static struct net_6lo_context *get_dst_addr_ctx(struct net_pkt *pkt,
705 struct net_ipv6_hdr *ipv6)
706 {
707 /* If compress flag is unset means use only in uncompression. */
708 struct net_6lo_context *dst;
709
710 dst = get_6lo_context_by_addr(net_pkt_iface(pkt),
711 (struct in6_addr *)ipv6->dst);
712 if (!dst || !dst->compress) {
713 return NULL;
714 }
715
716 return dst;
717 }
718 #endif /* CONFIG_NET_6LO_CONTEXT */
719
720 /* RFC 6282 LOWPAN IPHC Encoding format (3.1)
721 * Base Format
722 * 0 1
723 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
724 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
725 * | 0 | 1 | 1 | TF |NH | HLIM |CID|SAC| SAM | M |DAC| DAM |
726 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
727 */
compress_IPHC_header(struct net_pkt * pkt)728 static inline int compress_IPHC_header(struct net_pkt *pkt)
729 {
730 #if defined(CONFIG_NET_6LO_CONTEXT)
731 struct net_6lo_context *src_ctx = NULL;
732 struct net_6lo_context *dst_ctx = NULL;
733 #endif
734 uint8_t compressed = 0;
735 uint16_t iphc = (NET_6LO_DISPATCH_IPHC << 8);
736 struct net_ipv6_hdr *ipv6 = NET_IPV6_HDR(pkt);
737 struct net_udp_hdr *udp;
738 uint8_t *inline_pos;
739
740 if (pkt->frags->len < NET_IPV6H_LEN) {
741 NET_ERR("Invalid length %d, min %d",
742 pkt->frags->len, NET_IPV6H_LEN);
743 return -EINVAL;
744 }
745
746 if (ipv6->nexthdr == IPPROTO_UDP &&
747 pkt->frags->len < NET_IPV6UDPH_LEN) {
748 NET_ERR("Invalid length %d, min %d",
749 pkt->frags->len, NET_IPV6UDPH_LEN);
750 return -EINVAL;
751 }
752
753 inline_pos = pkt->buffer->data + NET_IPV6H_LEN;
754
755 if (ipv6->nexthdr == IPPROTO_UDP) {
756 udp = (struct net_udp_hdr *)inline_pos;
757 inline_pos += NET_UDPH_LEN;
758
759 inline_pos = compress_nh_udp(udp, inline_pos, false);
760 }
761
762 if (net_6lo_ll_prefix_padded_with_zeros((struct in6_addr *)ipv6->dst)) {
763 inline_pos = compress_da(ipv6, pkt, inline_pos, &iphc);
764 goto da_end;
765 }
766
767 if (net_ipv6_is_addr_mcast((struct in6_addr *)ipv6->dst)) {
768 inline_pos = compress_da_mcast(ipv6, inline_pos, &iphc);
769 goto da_end;
770 }
771
772 #if defined(CONFIG_NET_6LO_CONTEXT)
773 dst_ctx = get_dst_addr_ctx(pkt, ipv6);
774 if (dst_ctx) {
775 iphc |= NET_6LO_IPHC_CID_1;
776 inline_pos = compress_da_ctx(ipv6, inline_pos, pkt, &iphc,
777 dst_ctx);
778 goto da_end;
779 }
780 #endif
781 inline_pos = set_da_inline(ipv6, inline_pos, &iphc);
782 da_end:
783
784 if (net_6lo_ll_prefix_padded_with_zeros((struct in6_addr *)ipv6->src)) {
785 inline_pos = compress_sa(ipv6, pkt, inline_pos, &iphc);
786 goto sa_end;
787 }
788
789 if (net_ipv6_is_addr_unspecified((struct in6_addr *)ipv6->src)) {
790 NET_DBG("SAM_00, SAC_1 unspecified src address");
791
792 /* Unspecified IPv6 src address */
793 iphc |= NET_6LO_IPHC_SAC_1;
794 iphc |= NET_6LO_IPHC_SAM_00;
795 goto sa_end;
796 }
797
798 #if defined(CONFIG_NET_6LO_CONTEXT)
799 src_ctx = get_src_addr_ctx(pkt, ipv6);
800 if (src_ctx) {
801 inline_pos = compress_sa_ctx(ipv6, inline_pos, pkt, &iphc,
802 src_ctx);
803 iphc |= NET_6LO_IPHC_CID_1;
804 goto sa_end;
805 }
806 #endif
807 inline_pos = set_sa_inline(ipv6, inline_pos, &iphc);
808 sa_end:
809
810 inline_pos = compress_hoplimit(ipv6, inline_pos, &iphc);
811 inline_pos = compress_nh(ipv6, inline_pos, &iphc);
812 inline_pos = compress_tfl(ipv6, inline_pos, &iphc);
813
814 #if defined(CONFIG_NET_6LO_CONTEXT)
815 if (iphc & NET_6LO_IPHC_CID_1) {
816 inline_pos -= sizeof(uint8_t);
817 *inline_pos = 0;
818
819 if (src_ctx) {
820 *inline_pos = src_ctx->cid << 4;
821 }
822
823 if (dst_ctx) {
824 *inline_pos |= dst_ctx->cid & 0x0F;
825 }
826 }
827 #endif
828
829 inline_pos -= sizeof(iphc);
830 iphc = htons(iphc);
831 memmove(inline_pos, &iphc, sizeof(iphc));
832
833 compressed = inline_pos - pkt->buffer->data;
834
835 net_pkt_cursor_init(pkt);
836 net_pkt_pull(pkt, compressed);
837 net_pkt_compact(pkt);
838
839 return compressed;
840 }
841
842 /* Helper to uncompress Traffic class and Flow label */
uncompress_tfl(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)843 static inline uint8_t *uncompress_tfl(uint16_t iphc, uint8_t *cursor,
844 struct net_ipv6_hdr *ipv6)
845 {
846 uint8_t tcl;
847
848 /* Uncompress tcl and flow label */
849 switch (iphc & NET_6LO_IPHC_TF_11) {
850 case NET_6LO_IPHC_TF_00:
851 NET_DBG("ECN + DSCP + 4-bit Pad + Flow Label");
852
853 tcl = *cursor;
854 cursor++;
855 tcl = (tcl >> 6) | (tcl << 2);
856
857 ipv6->vtc |= ((tcl & 0xF0) >> 4);
858 ipv6->tcflow = ((tcl & 0x0F) << 4) | (*cursor & 0x0F);
859 cursor++;
860
861 memmove(&ipv6->flow, cursor, sizeof(ipv6->flow));
862 cursor += sizeof(ipv6->flow);
863 break;
864 case NET_6LO_IPHC_TF_01:
865 NET_DBG("ECN + 2-bit Pad + Flow Label, DSCP is elided");
866
867 tcl = ((*cursor & 0xF0) >> 6);
868 ipv6->tcflow = ((tcl & 0x0F) << 4) | (*cursor & 0x0F);
869 cursor++;
870
871 memmove(&ipv6->flow, cursor, sizeof(ipv6->flow));
872 cursor += sizeof(ipv6->flow);
873
874 break;
875 case NET_6LO_IPHC_TF_10:
876 NET_DBG("Flow label elided");
877
878 tcl = *cursor;
879 cursor++;
880 tcl = (tcl >> 6) | (tcl << 2);
881
882 ipv6->vtc |= ((tcl & 0xF0) >> 4);
883 ipv6->tcflow = (tcl & 0x0F) << 4;
884 ipv6->flow = 0U;
885
886 break;
887 case NET_6LO_IPHC_TF_11:
888 NET_DBG("Tcl and Flow label elided");
889
890 ipv6->tcflow = 0U;
891 ipv6->flow = 0U;
892
893 break;
894 }
895
896 return cursor;
897 }
898
899 /* Helper to uncompress Hoplimit */
uncompress_hoplimit(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)900 static inline uint8_t *uncompress_hoplimit(uint16_t iphc, uint8_t *cursor,
901 struct net_ipv6_hdr *ipv6)
902 {
903 switch (iphc & NET_6LO_IPHC_HLIM_MASK) {
904 case NET_6LO_IPHC_HLIM:
905 ipv6->hop_limit = *cursor;
906 cursor++;
907
908 break;
909 case NET_6LO_IPHC_HLIM1:
910 ipv6->hop_limit = 1U;
911
912 break;
913 case NET_6LO_IPHC_HLIM64:
914 ipv6->hop_limit = 64U;
915
916 break;
917 case NET_6LO_IPHC_HLIM255:
918 ipv6->hop_limit = 255U;
919
920 break;
921 }
922
923 return cursor;
924 }
925
926 /* Helper to uncompress Source Address */
uncompress_sa(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_pkt * pkt)927 static inline uint8_t *uncompress_sa(uint16_t iphc, uint8_t *cursor,
928 struct net_ipv6_hdr *ipv6,
929 struct net_pkt *pkt)
930 {
931 struct in6_addr src_ip;
932
933 NET_DBG("SAC_0");
934
935 net_ipv6_addr_copy_raw((uint8_t *)&src_ip, ipv6->src);
936
937 switch (iphc & NET_6LO_IPHC_SAM_MASK) {
938 case NET_6LO_IPHC_SAM_00:
939 NET_DBG("SAM_00 full src addr inlined");
940
941 memmove(src_ip.s6_addr, cursor, sizeof(src_ip.s6_addr));
942 cursor += sizeof(src_ip.s6_addr);
943
944 break;
945 case NET_6LO_IPHC_SAM_01:
946 NET_DBG("SAM_01 last 64 bits are inlined");
947
948 memmove(&src_ip.s6_addr[8], cursor, 8);
949 cursor += 8U;
950
951 src_ip.s6_addr32[0] = 0x00;
952 src_ip.s6_addr32[1] = 0x00;
953 src_ip.s6_addr[0] = 0xFE;
954 src_ip.s6_addr[1] = 0x80;
955
956 break;
957 case NET_6LO_IPHC_SAM_10:
958 NET_DBG("SAM_10 src addr 16 bit compressed");
959
960 memmove(&src_ip.s6_addr[14], cursor, 2);
961 cursor += 2U;
962 src_ip.s6_addr16[6] = 0x00;
963
964 src_ip.s6_addr32[0] = 0x00;
965 src_ip.s6_addr32[1] = 0x00;
966 src_ip.s6_addr32[2] = 0x00;
967 src_ip.s6_addr[0] = 0xFE;
968 src_ip.s6_addr[1] = 0x80;
969 src_ip.s6_addr[11] = 0xFF;
970 src_ip.s6_addr[12] = 0xFE;
971
972 break;
973 case NET_6LO_IPHC_SAM_11:
974 NET_DBG("SAM_11 generate src addr from ll");
975
976 net_ipv6_addr_create_iid(&src_ip, net_pkt_lladdr_src(pkt));
977
978 break;
979 }
980
981 net_ipv6_addr_copy_raw(ipv6->src, (uint8_t *)&src_ip);
982
983 return cursor;
984 }
985
986 #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)987 static inline uint8_t *uncompress_sa_ctx(uint16_t iphc, uint8_t *cursor,
988 struct net_ipv6_hdr *ipv6,
989 struct net_6lo_context *ctx,
990 struct net_pkt *pkt)
991 {
992 struct in6_addr src_ip;
993
994 net_ipv6_addr_copy_raw((uint8_t *)&src_ip, ipv6->src);
995
996 switch (iphc & NET_6LO_IPHC_SAM_MASK) {
997 case NET_6LO_IPHC_SAM_01:
998 NET_DBG("SAM_01 last 64 bits are inlined");
999
1000 /* First 8 bytes are from context */
1001 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1002
1003 /* And the rest are carried in-line*/
1004 memmove(&src_ip.s6_addr[8], cursor, 8);
1005 cursor += 8U;
1006
1007 break;
1008 case NET_6LO_IPHC_SAM_10:
1009 NET_DBG("SAM_10 src addr 16 bit compressed");
1010
1011 /* 16 bit carried in-line */
1012 memmove(&src_ip.s6_addr[14], cursor, 2);
1013 cursor += 2U;
1014
1015 /* First 8 bytes are from context */
1016 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1017
1018 src_ip.s6_addr32[2] = 0x00;
1019 src_ip.s6_addr16[6] = 0x00;
1020 src_ip.s6_addr[11] = 0xFF;
1021 src_ip.s6_addr[12] = 0xFE;
1022
1023 break;
1024 case NET_6LO_IPHC_SAM_11:
1025 NET_DBG("SAM_11 generate src addr from ll");
1026
1027 /* RFC 6282, 3.1.1. If SAC = 1 and SAM = 11
1028 * Derive addr using context information and
1029 * the encapsulating header.
1030 * (e.g., 802.15.4 or IPv6 source address).
1031 */
1032 net_ipv6_addr_create_iid(&src_ip, net_pkt_lladdr_src(pkt));
1033
1034 /* net_ipv6_addr_create_iid will copy first 8 bytes
1035 * as link local prefix.
1036 * Overwrite first 8 bytes from context prefix here.
1037 */
1038 memmove(&src_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1039 break;
1040 }
1041
1042 net_ipv6_addr_copy_raw(ipv6->src, (uint8_t *)&src_ip);
1043
1044 return cursor;
1045 }
1046 #endif
1047
1048 /* Helpers to uncompress Destination Address */
uncompress_da_mcast(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6)1049 static inline uint8_t *uncompress_da_mcast(uint16_t iphc, uint8_t *cursor,
1050 struct net_ipv6_hdr *ipv6)
1051 {
1052 struct in6_addr dst_ip;
1053
1054 NET_DBG("Dst is multicast");
1055
1056 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1057
1058 if (iphc & NET_6LO_IPHC_DAC_1) {
1059 NET_WARN("Unsupported DAM options");
1060 return 0;
1061 }
1062
1063 /* If M=1 and DAC=0:
1064 * 00: 128 bits, The full address is carried in-line.
1065 * 01: 48 bits, The address takes the form ffXX::00XX:XXXX:XXXX.
1066 * 10: 32 bits, The address takes the form ffXX::00XX:XXXX.
1067 * 11: 8 bits, The address takes the form ff02::00XX.
1068 */
1069
1070 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1071 case NET_6LO_IPHC_DAM_00:
1072 NET_DBG("DAM_00 full dst addr inlined");
1073
1074 memmove(&dst_ip.s6_addr[0], cursor,
1075 sizeof(dst_ip.s6_addr));
1076
1077 cursor += sizeof(dst_ip.s6_addr);
1078 break;
1079 case NET_6LO_IPHC_DAM_01:
1080 NET_DBG("DAM_01 2nd byte and last five bytes");
1081
1082 dst_ip.s6_addr[1] = *cursor;
1083 cursor++;
1084 memmove(&dst_ip.s6_addr[11], cursor, 5);
1085 cursor += 5U;
1086
1087
1088 dst_ip.s6_addr[0] = 0xFF;
1089 dst_ip.s6_addr16[1] = 0x00;
1090 dst_ip.s6_addr32[1] = 0x00;
1091 dst_ip.s6_addr[10] = 0x00;
1092 dst_ip.s6_addr16[4] = 0x00;
1093
1094 break;
1095 case NET_6LO_IPHC_DAM_10:
1096 NET_DBG("DAM_10 2nd byte and last three bytes");
1097
1098 dst_ip.s6_addr[1] = *cursor;
1099 cursor++;
1100 memmove(&dst_ip.s6_addr[13], cursor, 3);
1101 cursor += 3U;
1102
1103 dst_ip.s6_addr[0] = 0xFF;
1104 dst_ip.s6_addr16[1] = 0x00;
1105 dst_ip.s6_addr32[1] = 0x00;
1106 dst_ip.s6_addr32[2] = 0x00;
1107 dst_ip.s6_addr[12] = 0x00;
1108
1109 break;
1110 case NET_6LO_IPHC_DAM_11:
1111 NET_DBG("DAM_11 8 bit compressed");
1112
1113 dst_ip.s6_addr[15] = *cursor;
1114 cursor++;
1115 dst_ip.s6_addr[14] = 0x00;
1116
1117 dst_ip.s6_addr32[0] = 0x00;
1118 dst_ip.s6_addr32[1] = 0x00;
1119 dst_ip.s6_addr32[2] = 0x00;
1120 dst_ip.s6_addr16[6] = 0x00;
1121 dst_ip.s6_addr[0] = 0xFF;
1122 dst_ip.s6_addr[1] = 0x02;
1123
1124 break;
1125 }
1126
1127 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1128
1129 return cursor;
1130 }
1131
1132 /* Helper to uncompress Destination Address */
uncompress_da(uint16_t iphc,uint8_t * cursor,struct net_ipv6_hdr * ipv6,struct net_pkt * pkt)1133 static inline uint8_t *uncompress_da(uint16_t iphc, uint8_t *cursor,
1134 struct net_ipv6_hdr *ipv6,
1135 struct net_pkt *pkt)
1136 {
1137 struct in6_addr dst_ip;
1138
1139 NET_DBG("DAC_0");
1140
1141 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1142
1143 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1144 case NET_6LO_IPHC_DAM_00:
1145 NET_DBG("DAM_00 full dst addr inlined");
1146
1147 memmove(&dst_ip.s6_addr[0], cursor,
1148 sizeof(dst_ip.s6_addr));
1149 cursor += sizeof(dst_ip.s6_addr);
1150
1151 break;
1152 case NET_6LO_IPHC_DAM_01:
1153 NET_DBG("DAM_01 last 64 bits are inlined");
1154
1155 memmove(&dst_ip.s6_addr[8], cursor, 8);
1156 cursor += 8U;
1157
1158 dst_ip.s6_addr32[0] = 0x00;
1159 dst_ip.s6_addr32[1] = 0x00;
1160 dst_ip.s6_addr[0] = 0xFE;
1161 dst_ip.s6_addr[1] = 0x80;
1162
1163 break;
1164 case NET_6LO_IPHC_DAM_10:
1165 NET_DBG("DAM_10 dst addr 16 bit compressed");
1166
1167 memmove(&dst_ip.s6_addr[14], cursor, 2);
1168 cursor += 2U;
1169
1170 dst_ip.s6_addr32[0] = 0x00;
1171 dst_ip.s6_addr32[1] = 0x00;
1172 dst_ip.s6_addr32[2] = 0x00;
1173 dst_ip.s6_addr16[6] = 0x00;
1174 dst_ip.s6_addr[0] = 0xFE;
1175 dst_ip.s6_addr[1] = 0x80;
1176 dst_ip.s6_addr[11] = 0xFF;
1177 dst_ip.s6_addr[12] = 0xFE;
1178
1179 break;
1180 case NET_6LO_IPHC_DAM_11:
1181 NET_DBG("DAM_11 generate dst addr from ll");
1182
1183 net_ipv6_addr_create_iid(&dst_ip, net_pkt_lladdr_dst(pkt));
1184
1185 break;
1186 }
1187
1188 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1189
1190 return cursor;
1191 }
1192
1193 #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)1194 static inline uint8_t *uncompress_da_ctx(uint16_t iphc, uint8_t *cursor,
1195 struct net_ipv6_hdr *ipv6,
1196 struct net_6lo_context *ctx,
1197 struct net_pkt *pkt)
1198 {
1199 struct in6_addr dst_ip;
1200
1201 NET_DBG("DAC_1");
1202
1203 net_ipv6_addr_copy_raw((uint8_t *)&dst_ip, ipv6->dst);
1204
1205 switch (iphc & NET_6LO_IPHC_DAM_MASK) {
1206 case NET_6LO_IPHC_DAM_01:
1207 NET_DBG("DAM_01 last 64 bits are inlined");
1208
1209 /* Last 8 bytes carried in-line */
1210 memmove(&dst_ip.s6_addr[8], cursor, 8);
1211 cursor += 8U;
1212
1213 /* First 8 bytes are from context */
1214 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1215
1216 break;
1217 case NET_6LO_IPHC_DAM_10:
1218 NET_DBG("DAM_10 src addr 16 bit compressed");
1219
1220 /* 16 bit carried in-line */
1221 memmove(&dst_ip.s6_addr[14], cursor, 2);
1222 cursor += 2U;
1223
1224 /* First 8 bytes are from context */
1225 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1226
1227 dst_ip.s6_addr32[2] = 0x00;
1228 dst_ip.s6_addr16[6] = 0x00;
1229 dst_ip.s6_addr[11] = 0xFF;
1230 dst_ip.s6_addr[12] = 0xFE;
1231
1232 break;
1233 case NET_6LO_IPHC_DAM_11:
1234 NET_DBG("DAM_11 generate src addr from ll");
1235
1236 /* RFC 6282, 3.1.1. If SAC = 1 and SAM = 11
1237 * Derive addr using context information and
1238 * the encapsulating header.
1239 * (e.g., 802.15.4 or IPv6 source address).
1240 */
1241 net_ipv6_addr_create_iid(&dst_ip, net_pkt_lladdr_dst(pkt));
1242
1243 /* net_ipv6_addr_create_iid will copy first 8 bytes
1244 * as link local prefix.
1245 * Overwrite first 8 bytes from context prefix here.
1246 */
1247 memmove(&dst_ip.s6_addr[0], &ctx->prefix.s6_addr[0], 8);
1248
1249 break;
1250 }
1251
1252 net_ipv6_addr_copy_raw(ipv6->dst, (uint8_t *)&dst_ip);
1253
1254 return cursor;
1255 }
1256 #endif
1257
1258 /* Helper to uncompress NH UDP */
uncompress_nh_udp(uint8_t nhc,uint8_t * cursor,struct net_udp_hdr * udp)1259 static uint8_t *uncompress_nh_udp(uint8_t nhc, uint8_t *cursor,
1260 struct net_udp_hdr *udp)
1261 {
1262
1263 /* Port uncompression
1264 * 00: All 16 bits for src and dst are inlined
1265 * 01: src, 16 bits are lined, dst(0xf0) 8 bits are inlined
1266 * 10: dst, 16 bits are lined, src(0xf0) 8 bits are inlined
1267 * 11: src, dst (0xf0b) 4 bits are inlined
1268 */
1269
1270 /* UDP header uncompression */
1271 switch (nhc & NET_6LO_NHC_UDP_PORT_11) {
1272 case NET_6LO_NHC_UDP_PORT_00:
1273 NET_DBG("src and dst ports are inlined");
1274
1275 memmove(&udp->src_port, cursor, sizeof(udp->src_port));
1276 cursor += sizeof(udp->src_port);
1277 memmove(&udp->dst_port, cursor, sizeof(udp->dst_port));
1278 cursor += sizeof(udp->dst_port);
1279
1280 break;
1281 case NET_6LO_NHC_UDP_PORT_01:
1282 NET_DBG("src full, dst 8 bits inlined");
1283
1284 memmove(&udp->src_port, cursor, sizeof(udp->src_port));
1285 cursor += sizeof(udp->src_port);
1286 udp->dst_port = htons(((uint16_t)NET_6LO_NHC_UDP_8_BIT_PORT << 8) |
1287 *cursor);
1288 cursor++;
1289
1290 break;
1291 case NET_6LO_NHC_UDP_PORT_10:
1292 NET_DBG("src 8 bits, dst full inlined");
1293
1294 udp->src_port = htons(((uint16_t)NET_6LO_NHC_UDP_8_BIT_PORT << 8) |
1295 *cursor);
1296 cursor++;
1297 memmove(&udp->dst_port, cursor, sizeof(udp->dst_port));
1298 cursor += sizeof(udp->dst_port);
1299
1300 break;
1301 case NET_6LO_NHC_UDP_PORT_11:
1302 NET_DBG("src and dst 4 bits inlined");
1303
1304 udp->src_port = htons((NET_6LO_NHC_UDP_4_BIT_PORT << 4) |
1305 (*cursor >> 4));
1306
1307 udp->dst_port = htons((NET_6LO_NHC_UDP_4_BIT_PORT << 4) |
1308 (*cursor & 0x0F));
1309 cursor++;
1310
1311 break;
1312 }
1313
1314 if (!(nhc & NET_6LO_NHC_UDP_CHECKSUM)) {
1315 memmove(&udp->chksum, cursor, sizeof(udp->chksum));
1316 cursor += sizeof(udp->chksum);
1317 }
1318
1319 return cursor;
1320 }
1321
1322 #if defined(CONFIG_NET_6LO_CONTEXT)
1323 /* 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)1324 static inline void uncompress_cid(struct net_pkt *pkt, uint8_t cid,
1325 struct net_6lo_context **src,
1326 struct net_6lo_context **dst)
1327 {
1328 uint8_t cid_tmp;
1329
1330 /* Extract source and destination Context Index,
1331 * Either src or dest address is context based or both.
1332 */
1333 cid_tmp = (cid >> 4) & 0x0F;
1334 *src = get_6lo_context_by_cid(net_pkt_iface(pkt), cid_tmp);
1335 if (!(*src)) {
1336 NET_DBG("Unknown src cid %d", cid_tmp);
1337 }
1338
1339 cid_tmp = cid & 0x0F;
1340 *dst = get_6lo_context_by_cid(net_pkt_iface(pkt), cid_tmp);
1341 if (!(*dst)) {
1342 NET_DBG("Unknown dst cid %d", cid_tmp);
1343 }
1344 }
1345 #endif
1346
uncompress_IPHC_header(struct net_pkt * pkt)1347 static bool uncompress_IPHC_header(struct net_pkt *pkt)
1348 {
1349 struct net_udp_hdr *udp = NULL;
1350 struct net_buf *frag = NULL;
1351 uint8_t nhc = 0;
1352 int nhc_inline_size = 0;
1353 struct net_ipv6_hdr *ipv6;
1354 uint16_t len;
1355 uint16_t iphc;
1356 int inline_size, compressed_hdr_size;
1357 size_t diff;
1358 uint8_t *cursor;
1359 #if defined(CONFIG_NET_6LO_CONTEXT)
1360 struct net_6lo_context *src = NULL;
1361 struct net_6lo_context *dst = NULL;
1362 #endif
1363
1364 iphc = ntohs(UNALIGNED_GET((uint16_t *)pkt->buffer->data));
1365
1366 inline_size = get_ihpc_inlined_size(iphc);
1367 if (inline_size < 0) {
1368 return false;
1369 }
1370
1371 compressed_hdr_size = sizeof(iphc) + inline_size;
1372 diff = sizeof(struct net_ipv6_hdr) - compressed_hdr_size;
1373
1374 if (iphc & NET_6LO_IPHC_NH_MASK) {
1375 nhc = *(pkt->buffer->data + sizeof(iphc) + inline_size);
1376 if ((nhc & 0xF8) != NET_6LO_NHC_UDP_BARE) {
1377 NET_ERR("Unsupported next header");
1378 return false;
1379 }
1380
1381 nhc_inline_size = get_udp_nhc_inlined_size(nhc);
1382 compressed_hdr_size += sizeof(uint8_t) + nhc_inline_size;
1383 diff += sizeof(struct net_udp_hdr) - sizeof(uint8_t) -
1384 nhc_inline_size;
1385 }
1386
1387 if (pkt->buffer->len < compressed_hdr_size) {
1388 NET_ERR("Scattered compressed header?");
1389 return false;
1390 }
1391
1392 if (net_buf_tailroom(pkt->buffer) >= diff) {
1393 NET_DBG("Enough tailroom. Uncompress inplace");
1394 frag = pkt->buffer;
1395 net_buf_add(frag, diff);
1396 cursor = frag->data + diff;
1397 memmove(cursor, frag->data, frag->len - diff);
1398 } else {
1399 size_t frag_len = nhc ? NET_IPV6UDPH_LEN : NET_IPV6H_LEN;
1400
1401 NET_DBG("Not enough tailroom. Get new fragment");
1402 cursor = pkt->buffer->data;
1403 frag = net_pkt_get_frag(pkt, frag_len, NET_6LO_RX_PKT_TIMEOUT);
1404 if (!frag) {
1405 NET_ERR("Can't get frag for uncompression");
1406 return false;
1407 }
1408
1409 net_buf_pull(pkt->buffer, compressed_hdr_size);
1410 net_buf_add(frag, frag_len);
1411 }
1412
1413 ipv6 = (struct net_ipv6_hdr *)(frag->data);
1414 cursor += sizeof(iphc);
1415
1416 if (iphc & NET_6LO_IPHC_CID_1) {
1417 #if defined(CONFIG_NET_6LO_CONTEXT)
1418 uncompress_cid(pkt, *cursor, &src, &dst);
1419 cursor++;
1420 #else
1421 NET_ERR("Context based uncompression not enabled");
1422 return false;
1423 #endif
1424 }
1425
1426 /* Version is always 6 */
1427 ipv6->vtc = 0x60;
1428 net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN);
1429
1430 /* Uncompress Traffic class and Flow label */
1431 cursor = uncompress_tfl(iphc, cursor, ipv6);
1432
1433 if (!(iphc & NET_6LO_IPHC_NH_MASK)) {
1434 ipv6->nexthdr = *cursor;
1435 cursor++;
1436 }
1437
1438 /* Uncompress Hoplimit */
1439 cursor = uncompress_hoplimit(iphc, cursor, ipv6);
1440
1441 /* Uncompress Source Address */
1442 if (iphc & NET_6LO_IPHC_SAC_1) {
1443 NET_DBG("SAC_1");
1444
1445 if ((iphc & NET_6LO_IPHC_SAM_MASK) == NET_6LO_IPHC_SAM_00) {
1446 NET_DBG("SAM_00 unspecified address");
1447 memset(&ipv6->src[0], 0,
1448 sizeof(ipv6->src));
1449 } else if (IS_ENABLED(CONFIG_NET_6LO_CONTEXT)) {
1450 #if defined(CONFIG_NET_6LO_CONTEXT)
1451 if (!src) {
1452 NET_ERR("Src context doesn't exists");
1453 goto fail;
1454 }
1455
1456 cursor = uncompress_sa_ctx(iphc, cursor, ipv6, src, pkt);
1457 #endif
1458 } else {
1459 NET_ERR("Context based uncompression not enabled");
1460 goto fail;
1461 }
1462 } else {
1463 cursor = uncompress_sa(iphc, cursor, ipv6, pkt);
1464 }
1465
1466 /* Uncompress Destination Address */
1467 if (iphc & NET_6LO_IPHC_M_1) {
1468 if (iphc & NET_6LO_IPHC_DAC_1) {
1469 /* TODO: DAM00 Unicast-Prefix-based IPv6 Multicast
1470 * Addresses. DAM_01, DAM_10 and DAM_11 are reserved.
1471 */
1472 NET_ERR("DAC_1 and M_1 is not supported");
1473 goto fail;
1474 } else {
1475 cursor = uncompress_da_mcast(iphc, cursor, ipv6);
1476 }
1477 } else {
1478 if (iphc & NET_6LO_IPHC_DAC_1) {
1479 #if defined(CONFIG_NET_6LO_CONTEXT)
1480 if (!dst) {
1481 NET_ERR("Dst context doesn't exists");
1482 goto fail;
1483 }
1484
1485 cursor = uncompress_da_ctx(iphc, cursor, ipv6, dst, pkt);
1486 #else
1487 NET_ERR("Context based uncompression not enabled");
1488 goto fail;
1489 #endif
1490 } else {
1491 cursor = uncompress_da(iphc, cursor, ipv6, pkt);
1492 }
1493 }
1494
1495 if (iphc & NET_6LO_IPHC_NH_MASK) {
1496 ipv6->nexthdr = IPPROTO_UDP;
1497 udp = (struct net_udp_hdr *)(frag->data + NET_IPV6H_LEN);
1498 /* skip nhc */
1499 cursor++;
1500 cursor = uncompress_nh_udp(nhc, cursor, udp);
1501 }
1502
1503 if (frag != pkt->buffer) {
1504 /* Insert the fragment (this one holds uncompressed headers) */
1505 net_pkt_frag_insert(pkt, frag);
1506 }
1507
1508 /* Set IPv6 header and UDP (if next header is) length */
1509 len = net_pkt_get_len(pkt) - NET_IPV6H_LEN;
1510 ipv6->len = htons(len);
1511
1512 if (ipv6->nexthdr == IPPROTO_UDP && udp) {
1513 udp->len = htons(len);
1514
1515 if (nhc & NET_6LO_NHC_UDP_CHECKSUM) {
1516 udp->chksum = net_calc_chksum_udp(pkt);
1517 }
1518 }
1519
1520 net_pkt_cursor_init(pkt);
1521
1522 return true;
1523
1524 fail:
1525 if (frag != pkt->buffer) {
1526 net_pkt_frag_unref(frag);
1527 }
1528
1529 return false;
1530 }
1531
1532 /* Adds IPv6 dispatch as first byte and adjust fragments */
compress_ipv6_header(struct net_pkt * pkt)1533 static inline int compress_ipv6_header(struct net_pkt *pkt)
1534 {
1535 struct net_buf *buffer = pkt->buffer;
1536
1537 if (net_buf_tailroom(buffer) >= 1U) {
1538 memmove(buffer->data + 1U, buffer->data, buffer->len);
1539 net_buf_add(buffer, 1U);
1540 buffer->data[0] = NET_6LO_DISPATCH_IPV6;
1541 return 0;
1542 }
1543
1544 buffer = net_pkt_get_frag(pkt, 1, K_FOREVER);
1545 if (!buffer) {
1546 return -ENOBUFS;
1547 }
1548
1549 buffer->data[0] = NET_6LO_DISPATCH_IPV6;
1550 net_buf_add(buffer, 1);
1551
1552 net_pkt_frag_insert(pkt, buffer);
1553
1554 /* Compact the fragments, so that gaps will be filled */
1555 net_pkt_compact(pkt);
1556
1557 return 0;
1558 }
1559
uncompress_ipv6_header(struct net_pkt * pkt)1560 static inline bool uncompress_ipv6_header(struct net_pkt *pkt)
1561 {
1562 /* Pull off IPv6 dispatch header and adjust data and length */
1563 net_buf_pull(pkt->buffer, 1U);
1564 net_pkt_cursor_init(pkt);
1565
1566 return true;
1567 }
1568
net_6lo_compress(struct net_pkt * pkt,bool iphc)1569 int net_6lo_compress(struct net_pkt *pkt, bool iphc)
1570 {
1571 if (iphc) {
1572 return compress_IPHC_header(pkt);
1573 } else {
1574 return compress_ipv6_header(pkt);
1575 }
1576 }
1577
net_6lo_uncompress(struct net_pkt * pkt)1578 bool net_6lo_uncompress(struct net_pkt *pkt)
1579 {
1580 NET_ASSERT(pkt && pkt->frags);
1581
1582 if ((pkt->frags->data[0] & NET_6LO_DISPATCH_IPHC_MASK) ==
1583 NET_6LO_DISPATCH_IPHC) {
1584 /* Uncompress IPHC header */
1585 return uncompress_IPHC_header(pkt);
1586
1587 } else if (pkt->frags->data[0] == NET_6LO_DISPATCH_IPV6) {
1588 /* Uncompress IPv6 header, it has only IPv6 dispatch in the
1589 * beginning */
1590 return uncompress_ipv6_header(pkt);
1591 }
1592
1593 NET_DBG("pkt %p is not compressed", pkt);
1594
1595 return true;
1596 }
1597
net_6lo_uncompress_hdr_diff(struct net_pkt * pkt)1598 int net_6lo_uncompress_hdr_diff(struct net_pkt *pkt)
1599 {
1600 int inline_size, compressed_hdr_size, nhc_inline_size, diff;
1601 uint16_t iphc;
1602 uint8_t nhc;
1603
1604 if (pkt->frags->data[0] == NET_6LO_DISPATCH_IPV6) {
1605 return -1;
1606 }
1607
1608 if ((pkt->frags->data[0] & NET_6LO_DISPATCH_IPHC_MASK) !=
1609 NET_6LO_DISPATCH_IPHC) {
1610 return 0;
1611 }
1612
1613 iphc = ntohs(UNALIGNED_GET((uint16_t *)pkt->buffer->data));
1614
1615 inline_size = get_ihpc_inlined_size(iphc);
1616 if (inline_size < 0) {
1617 return INT_MAX;
1618 }
1619
1620 compressed_hdr_size = sizeof(iphc) + inline_size;
1621 diff = sizeof(struct net_ipv6_hdr) - compressed_hdr_size;
1622
1623 if (iphc & NET_6LO_IPHC_NH_MASK) {
1624 nhc = *(pkt->buffer->data + sizeof(iphc) + inline_size);
1625 if ((nhc & 0xF8) != NET_6LO_NHC_UDP_BARE) {
1626 NET_ERR("Unsupported next header");
1627 return INT_MAX;
1628 }
1629
1630 nhc_inline_size = get_udp_nhc_inlined_size(nhc);
1631 diff += sizeof(struct net_udp_hdr) - sizeof(uint8_t) -
1632 nhc_inline_size;
1633 }
1634
1635 return diff;
1636 }
1637