1195972f6Sopenharmony_ci/** 2195972f6Sopenharmony_ci * @file 3195972f6Sopenharmony_ci * 4195972f6Sopenharmony_ci * Common 6LowPAN routines for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units. 5195972f6Sopenharmony_ci * 6195972f6Sopenharmony_ci * This implementation aims to conform to IEEE 802.15.4(-2015), RFC 4944 and RFC 6282. 7195972f6Sopenharmony_ci * @todo: RFC 6775. 8195972f6Sopenharmony_ci */ 9195972f6Sopenharmony_ci 10195972f6Sopenharmony_ci/* 11195972f6Sopenharmony_ci * Copyright (c) 2015 Inico Technologies Ltd. 12195972f6Sopenharmony_ci * All rights reserved. 13195972f6Sopenharmony_ci * 14195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification, 15195972f6Sopenharmony_ci * are permitted provided that the following conditions are met: 16195972f6Sopenharmony_ci * 17195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice, 18195972f6Sopenharmony_ci * this list of conditions and the following disclaimer. 19195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice, 20195972f6Sopenharmony_ci * this list of conditions and the following disclaimer in the documentation 21195972f6Sopenharmony_ci * and/or other materials provided with the distribution. 22195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products 23195972f6Sopenharmony_ci * derived from this software without specific prior written permission. 24195972f6Sopenharmony_ci * 25195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 26195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 28195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 30195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 33195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 34195972f6Sopenharmony_ci * OF SUCH DAMAGE. 35195972f6Sopenharmony_ci * 36195972f6Sopenharmony_ci * This file is part of the lwIP TCP/IP stack. 37195972f6Sopenharmony_ci * 38195972f6Sopenharmony_ci * Author: Ivan Delamer <delamer@inicotech.com> 39195972f6Sopenharmony_ci * 40195972f6Sopenharmony_ci * 41195972f6Sopenharmony_ci * Please coordinate changes and requests with Ivan Delamer 42195972f6Sopenharmony_ci * <delamer@inicotech.com> 43195972f6Sopenharmony_ci */ 44195972f6Sopenharmony_ci 45195972f6Sopenharmony_ci/** 46195972f6Sopenharmony_ci * @defgroup sixlowpan 6LoWPAN (RFC4944) 47195972f6Sopenharmony_ci * @ingroup netifs 48195972f6Sopenharmony_ci * 6LowPAN netif implementation 49195972f6Sopenharmony_ci */ 50195972f6Sopenharmony_ci 51195972f6Sopenharmony_ci#include "netif/lowpan6_common.h" 52195972f6Sopenharmony_ci 53195972f6Sopenharmony_ci#if LWIP_IPV6 54195972f6Sopenharmony_ci 55195972f6Sopenharmony_ci#include "lwip/ip.h" 56195972f6Sopenharmony_ci#include "lwip/pbuf.h" 57195972f6Sopenharmony_ci#include "lwip/ip_addr.h" 58195972f6Sopenharmony_ci#include "lwip/netif.h" 59195972f6Sopenharmony_ci#include "lwip/udp.h" 60195972f6Sopenharmony_ci 61195972f6Sopenharmony_ci#include <string.h> 62195972f6Sopenharmony_ci 63195972f6Sopenharmony_ci/* Determine compression mode for unicast address. */ 64195972f6Sopenharmony_cis8_t 65195972f6Sopenharmony_cilowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct lowpan6_link_addr *mac_addr) 66195972f6Sopenharmony_ci{ 67195972f6Sopenharmony_ci if (mac_addr->addr_len == 2) { 68195972f6Sopenharmony_ci if ((ip6addr->addr[2] == (u32_t)PP_HTONL(0x000000ff)) && 69195972f6Sopenharmony_ci ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000))) { 70195972f6Sopenharmony_ci if ((ip6addr->addr[3] & PP_HTONL(0x0000ffff)) == lwip_ntohl((mac_addr->addr[0] << 8) | mac_addr->addr[1])) { 71195972f6Sopenharmony_ci return 3; 72195972f6Sopenharmony_ci } 73195972f6Sopenharmony_ci } 74195972f6Sopenharmony_ci } else if (mac_addr->addr_len == 8) { 75195972f6Sopenharmony_ci if ((ip6addr->addr[2] == lwip_ntohl(((mac_addr->addr[0] ^ 2) << 24) | (mac_addr->addr[1] << 16) | mac_addr->addr[2] << 8 | mac_addr->addr[3])) && 76195972f6Sopenharmony_ci (ip6addr->addr[3] == lwip_ntohl((mac_addr->addr[4] << 24) | (mac_addr->addr[5] << 16) | mac_addr->addr[6] << 8 | mac_addr->addr[7]))) { 77195972f6Sopenharmony_ci return 3; 78195972f6Sopenharmony_ci } 79195972f6Sopenharmony_ci } 80195972f6Sopenharmony_ci 81195972f6Sopenharmony_ci if ((ip6addr->addr[2] == PP_HTONL(0x000000ffUL)) && 82195972f6Sopenharmony_ci ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000UL))) { 83195972f6Sopenharmony_ci return 2; 84195972f6Sopenharmony_ci } 85195972f6Sopenharmony_ci 86195972f6Sopenharmony_ci return 1; 87195972f6Sopenharmony_ci} 88195972f6Sopenharmony_ci 89195972f6Sopenharmony_ci#if LWIP_6LOWPAN_IPHC 90195972f6Sopenharmony_ci 91195972f6Sopenharmony_ci/* Determine compression mode for multicast address. */ 92195972f6Sopenharmony_cistatic s8_t 93195972f6Sopenharmony_cilowpan6_get_address_mode_mc(const ip6_addr_t *ip6addr) 94195972f6Sopenharmony_ci{ 95195972f6Sopenharmony_ci if ((ip6addr->addr[0] == PP_HTONL(0xff020000)) && 96195972f6Sopenharmony_ci (ip6addr->addr[1] == 0) && 97195972f6Sopenharmony_ci (ip6addr->addr[2] == 0) && 98195972f6Sopenharmony_ci ((ip6addr->addr[3] & PP_HTONL(0xffffff00)) == 0)) { 99195972f6Sopenharmony_ci return 3; 100195972f6Sopenharmony_ci } else if (((ip6addr->addr[0] & PP_HTONL(0xff00ffff)) == PP_HTONL(0xff000000)) && 101195972f6Sopenharmony_ci (ip6addr->addr[1] == 0)) { 102195972f6Sopenharmony_ci if ((ip6addr->addr[2] == 0) && 103195972f6Sopenharmony_ci ((ip6addr->addr[3] & PP_HTONL(0xff000000)) == 0)) { 104195972f6Sopenharmony_ci return 2; 105195972f6Sopenharmony_ci } else if ((ip6addr->addr[2] & PP_HTONL(0xffffff00)) == 0) { 106195972f6Sopenharmony_ci return 1; 107195972f6Sopenharmony_ci } 108195972f6Sopenharmony_ci } 109195972f6Sopenharmony_ci 110195972f6Sopenharmony_ci return 0; 111195972f6Sopenharmony_ci} 112195972f6Sopenharmony_ci 113195972f6Sopenharmony_ci#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 114195972f6Sopenharmony_cistatic s8_t 115195972f6Sopenharmony_cilowpan6_context_lookup(const ip6_addr_t *lowpan6_contexts, const ip6_addr_t *ip6addr) 116195972f6Sopenharmony_ci{ 117195972f6Sopenharmony_ci s8_t i; 118195972f6Sopenharmony_ci 119195972f6Sopenharmony_ci for (i = 0; i < LWIP_6LOWPAN_NUM_CONTEXTS; i++) { 120195972f6Sopenharmony_ci if (ip6_addr_netcmp(&lowpan6_contexts[i], ip6addr)) { 121195972f6Sopenharmony_ci return i; 122195972f6Sopenharmony_ci } 123195972f6Sopenharmony_ci } 124195972f6Sopenharmony_ci return -1; 125195972f6Sopenharmony_ci} 126195972f6Sopenharmony_ci#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */ 127195972f6Sopenharmony_ci 128195972f6Sopenharmony_ci/* 129195972f6Sopenharmony_ci * Compress IPv6 and/or UDP headers. 130195972f6Sopenharmony_ci * */ 131195972f6Sopenharmony_cierr_t 132195972f6Sopenharmony_cilowpan6_compress_headers(struct netif *netif, u8_t *inbuf, size_t inbuf_size, u8_t *outbuf, size_t outbuf_size, 133195972f6Sopenharmony_ci u8_t *lowpan6_header_len_out, u8_t *hidden_header_len_out, ip6_addr_t *lowpan6_contexts, 134195972f6Sopenharmony_ci const struct lowpan6_link_addr *src, const struct lowpan6_link_addr *dst) 135195972f6Sopenharmony_ci{ 136195972f6Sopenharmony_ci u8_t *buffer, *inptr; 137195972f6Sopenharmony_ci u8_t lowpan6_header_len; 138195972f6Sopenharmony_ci u8_t hidden_header_len = 0; 139195972f6Sopenharmony_ci s8_t i; 140195972f6Sopenharmony_ci struct ip6_hdr *ip6hdr; 141195972f6Sopenharmony_ci ip_addr_t ip6src, ip6dst; 142195972f6Sopenharmony_ci 143195972f6Sopenharmony_ci LWIP_ASSERT("netif != NULL", netif != NULL); 144195972f6Sopenharmony_ci LWIP_ASSERT("inbuf != NULL", inbuf != NULL); 145195972f6Sopenharmony_ci LWIP_ASSERT("outbuf != NULL", outbuf != NULL); 146195972f6Sopenharmony_ci LWIP_ASSERT("lowpan6_header_len_out != NULL", lowpan6_header_len_out != NULL); 147195972f6Sopenharmony_ci LWIP_ASSERT("hidden_header_len_out != NULL", hidden_header_len_out != NULL); 148195972f6Sopenharmony_ci 149195972f6Sopenharmony_ci /* Perform 6LowPAN IPv6 header compression according to RFC 6282 */ 150195972f6Sopenharmony_ci buffer = outbuf; 151195972f6Sopenharmony_ci inptr = inbuf; 152195972f6Sopenharmony_ci 153195972f6Sopenharmony_ci if (inbuf_size < IP6_HLEN) { 154195972f6Sopenharmony_ci /* input buffer too short */ 155195972f6Sopenharmony_ci return ERR_VAL; 156195972f6Sopenharmony_ci } 157195972f6Sopenharmony_ci if (outbuf_size < IP6_HLEN) { 158195972f6Sopenharmony_ci /* output buffer too short for worst case */ 159195972f6Sopenharmony_ci return ERR_MEM; 160195972f6Sopenharmony_ci } 161195972f6Sopenharmony_ci 162195972f6Sopenharmony_ci /* Point to ip6 header and align copies of src/dest addresses. */ 163195972f6Sopenharmony_ci ip6hdr = (struct ip6_hdr *)inptr; 164195972f6Sopenharmony_ci ip_addr_copy_from_ip6_packed(ip6dst, ip6hdr->dest); 165195972f6Sopenharmony_ci ip6_addr_assign_zone(ip_2_ip6(&ip6dst), IP6_UNKNOWN, netif); 166195972f6Sopenharmony_ci ip_addr_copy_from_ip6_packed(ip6src, ip6hdr->src); 167195972f6Sopenharmony_ci ip6_addr_assign_zone(ip_2_ip6(&ip6src), IP6_UNKNOWN, netif); 168195972f6Sopenharmony_ci 169195972f6Sopenharmony_ci /* Basic length of 6LowPAN header, set dispatch and clear fields. */ 170195972f6Sopenharmony_ci lowpan6_header_len = 2; 171195972f6Sopenharmony_ci buffer[0] = 0x60; 172195972f6Sopenharmony_ci buffer[1] = 0; 173195972f6Sopenharmony_ci 174195972f6Sopenharmony_ci /* Determine whether there will be a Context Identifier Extension byte or not. 175195972f6Sopenharmony_ci * If so, set it already. */ 176195972f6Sopenharmony_ci#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 177195972f6Sopenharmony_ci buffer[2] = 0; 178195972f6Sopenharmony_ci 179195972f6Sopenharmony_ci i = lowpan6_context_lookup(lowpan6_contexts, ip_2_ip6(&ip6src)); 180195972f6Sopenharmony_ci if (i >= 0) { 181195972f6Sopenharmony_ci /* Stateful source address compression. */ 182195972f6Sopenharmony_ci buffer[1] |= 0x40; 183195972f6Sopenharmony_ci buffer[2] |= (i & 0x0f) << 4; 184195972f6Sopenharmony_ci } 185195972f6Sopenharmony_ci 186195972f6Sopenharmony_ci i = lowpan6_context_lookup(lowpan6_contexts, ip_2_ip6(&ip6dst)); 187195972f6Sopenharmony_ci if (i >= 0) { 188195972f6Sopenharmony_ci /* Stateful destination address compression. */ 189195972f6Sopenharmony_ci buffer[1] |= 0x04; 190195972f6Sopenharmony_ci buffer[2] |= i & 0x0f; 191195972f6Sopenharmony_ci } 192195972f6Sopenharmony_ci 193195972f6Sopenharmony_ci if (buffer[2] != 0x00) { 194195972f6Sopenharmony_ci /* Context identifier extension byte is appended. */ 195195972f6Sopenharmony_ci buffer[1] |= 0x80; 196195972f6Sopenharmony_ci lowpan6_header_len++; 197195972f6Sopenharmony_ci } 198195972f6Sopenharmony_ci#else /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */ 199195972f6Sopenharmony_ci LWIP_UNUSED_ARG(lowpan6_contexts); 200195972f6Sopenharmony_ci#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */ 201195972f6Sopenharmony_ci 202195972f6Sopenharmony_ci /* Determine TF field: Traffic Class, Flow Label */ 203195972f6Sopenharmony_ci if (IP6H_FL(ip6hdr) == 0) { 204195972f6Sopenharmony_ci /* Flow label is elided. */ 205195972f6Sopenharmony_ci buffer[0] |= 0x10; 206195972f6Sopenharmony_ci if (IP6H_TC(ip6hdr) == 0) { 207195972f6Sopenharmony_ci /* Traffic class (ECN+DSCP) elided too. */ 208195972f6Sopenharmony_ci buffer[0] |= 0x08; 209195972f6Sopenharmony_ci } else { 210195972f6Sopenharmony_ci /* Traffic class (ECN+DSCP) appended. */ 211195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_TC(ip6hdr); 212195972f6Sopenharmony_ci } 213195972f6Sopenharmony_ci } else { 214195972f6Sopenharmony_ci if (((IP6H_TC(ip6hdr) & 0x3f) == 0)) { 215195972f6Sopenharmony_ci /* DSCP portion of Traffic Class is elided, ECN and FL are appended (3 bytes) */ 216195972f6Sopenharmony_ci buffer[0] |= 0x08; 217195972f6Sopenharmony_ci 218195972f6Sopenharmony_ci buffer[lowpan6_header_len] = IP6H_TC(ip6hdr) & 0xc0; 219195972f6Sopenharmony_ci buffer[lowpan6_header_len++] |= (IP6H_FL(ip6hdr) >> 16) & 0x0f; 220195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff; 221195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff; 222195972f6Sopenharmony_ci } else { 223195972f6Sopenharmony_ci /* Traffic class and flow label are appended (4 bytes) */ 224195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_TC(ip6hdr); 225195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 16) & 0x0f; 226195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff; 227195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff; 228195972f6Sopenharmony_ci } 229195972f6Sopenharmony_ci } 230195972f6Sopenharmony_ci 231195972f6Sopenharmony_ci /* Compress NH? 232195972f6Sopenharmony_ci * Only if UDP for now. @todo support other NH compression. */ 233195972f6Sopenharmony_ci if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) { 234195972f6Sopenharmony_ci buffer[0] |= 0x04; 235195972f6Sopenharmony_ci } else { 236195972f6Sopenharmony_ci /* append nexth. */ 237195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_NEXTH(ip6hdr); 238195972f6Sopenharmony_ci } 239195972f6Sopenharmony_ci 240195972f6Sopenharmony_ci /* Compress hop limit? */ 241195972f6Sopenharmony_ci if (IP6H_HOPLIM(ip6hdr) == 255) { 242195972f6Sopenharmony_ci buffer[0] |= 0x03; 243195972f6Sopenharmony_ci } else if (IP6H_HOPLIM(ip6hdr) == 64) { 244195972f6Sopenharmony_ci buffer[0] |= 0x02; 245195972f6Sopenharmony_ci } else if (IP6H_HOPLIM(ip6hdr) == 1) { 246195972f6Sopenharmony_ci buffer[0] |= 0x01; 247195972f6Sopenharmony_ci } else { 248195972f6Sopenharmony_ci /* append hop limit */ 249195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = IP6H_HOPLIM(ip6hdr); 250195972f6Sopenharmony_ci } 251195972f6Sopenharmony_ci 252195972f6Sopenharmony_ci /* Compress source address */ 253195972f6Sopenharmony_ci if (((buffer[1] & 0x40) != 0) || 254195972f6Sopenharmony_ci (ip6_addr_islinklocal(ip_2_ip6(&ip6src)))) { 255195972f6Sopenharmony_ci /* Context-based or link-local source address compression. */ 256195972f6Sopenharmony_ci i = lowpan6_get_address_mode(ip_2_ip6(&ip6src), src); 257195972f6Sopenharmony_ci buffer[1] |= (i & 0x03) << 4; 258195972f6Sopenharmony_ci if (i == 1) { 259195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 16, 8); 260195972f6Sopenharmony_ci lowpan6_header_len += 8; 261195972f6Sopenharmony_ci } else if (i == 2) { 262195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 22, 2); 263195972f6Sopenharmony_ci lowpan6_header_len += 2; 264195972f6Sopenharmony_ci } 265195972f6Sopenharmony_ci } else if (ip6_addr_isany(ip_2_ip6(&ip6src))) { 266195972f6Sopenharmony_ci /* Special case: mark SAC and leave SAM=0 */ 267195972f6Sopenharmony_ci buffer[1] |= 0x40; 268195972f6Sopenharmony_ci } else { 269195972f6Sopenharmony_ci /* Append full address. */ 270195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 8, 16); 271195972f6Sopenharmony_ci lowpan6_header_len += 16; 272195972f6Sopenharmony_ci } 273195972f6Sopenharmony_ci 274195972f6Sopenharmony_ci /* Compress destination address */ 275195972f6Sopenharmony_ci if (ip6_addr_ismulticast(ip_2_ip6(&ip6dst))) { 276195972f6Sopenharmony_ci /* @todo support stateful multicast address compression */ 277195972f6Sopenharmony_ci 278195972f6Sopenharmony_ci buffer[1] |= 0x08; 279195972f6Sopenharmony_ci 280195972f6Sopenharmony_ci i = lowpan6_get_address_mode_mc(ip_2_ip6(&ip6dst)); 281195972f6Sopenharmony_ci buffer[1] |= i & 0x03; 282195972f6Sopenharmony_ci if (i == 0) { 283195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 24, 16); 284195972f6Sopenharmony_ci lowpan6_header_len += 16; 285195972f6Sopenharmony_ci } else if (i == 1) { 286195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[25]; 287195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 35, 5); 288195972f6Sopenharmony_ci lowpan6_header_len += 5; 289195972f6Sopenharmony_ci } else if (i == 2) { 290195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[25]; 291195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 37, 3); 292195972f6Sopenharmony_ci lowpan6_header_len += 3; 293195972f6Sopenharmony_ci } else if (i == 3) { 294195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = (inptr)[39]; 295195972f6Sopenharmony_ci } 296195972f6Sopenharmony_ci } else if (((buffer[1] & 0x04) != 0) || 297195972f6Sopenharmony_ci (ip6_addr_islinklocal(ip_2_ip6(&ip6dst)))) { 298195972f6Sopenharmony_ci /* Context-based or link-local destination address compression. */ 299195972f6Sopenharmony_ci i = lowpan6_get_address_mode(ip_2_ip6(&ip6dst), dst); 300195972f6Sopenharmony_ci buffer[1] |= i & 0x03; 301195972f6Sopenharmony_ci if (i == 1) { 302195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 32, 8); 303195972f6Sopenharmony_ci lowpan6_header_len += 8; 304195972f6Sopenharmony_ci } else if (i == 2) { 305195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 38, 2); 306195972f6Sopenharmony_ci lowpan6_header_len += 2; 307195972f6Sopenharmony_ci } 308195972f6Sopenharmony_ci } else { 309195972f6Sopenharmony_ci /* Append full address. */ 310195972f6Sopenharmony_ci MEMCPY(buffer + lowpan6_header_len, inptr + 24, 16); 311195972f6Sopenharmony_ci lowpan6_header_len += 16; 312195972f6Sopenharmony_ci } 313195972f6Sopenharmony_ci 314195972f6Sopenharmony_ci /* Move to payload. */ 315195972f6Sopenharmony_ci inptr += IP6_HLEN; 316195972f6Sopenharmony_ci hidden_header_len += IP6_HLEN; 317195972f6Sopenharmony_ci 318195972f6Sopenharmony_ci#if LWIP_UDP 319195972f6Sopenharmony_ci /* Compress UDP header? */ 320195972f6Sopenharmony_ci if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) { 321195972f6Sopenharmony_ci /* @todo support optional checksum compression */ 322195972f6Sopenharmony_ci 323195972f6Sopenharmony_ci if (inbuf_size < IP6_HLEN + UDP_HLEN) { 324195972f6Sopenharmony_ci /* input buffer too short */ 325195972f6Sopenharmony_ci return ERR_VAL; 326195972f6Sopenharmony_ci } 327195972f6Sopenharmony_ci if (outbuf_size < (size_t)(hidden_header_len + 7)) { 328195972f6Sopenharmony_ci /* output buffer too short for worst case */ 329195972f6Sopenharmony_ci return ERR_MEM; 330195972f6Sopenharmony_ci } 331195972f6Sopenharmony_ci 332195972f6Sopenharmony_ci buffer[lowpan6_header_len] = 0xf0; 333195972f6Sopenharmony_ci 334195972f6Sopenharmony_ci /* determine port compression mode. */ 335195972f6Sopenharmony_ci if ((inptr[0] == 0xf0) && ((inptr[1] & 0xf0) == 0xb0) && 336195972f6Sopenharmony_ci (inptr[2] == 0xf0) && ((inptr[3] & 0xf0) == 0xb0)) { 337195972f6Sopenharmony_ci /* Compress source and dest ports. */ 338195972f6Sopenharmony_ci buffer[lowpan6_header_len++] |= 0x03; 339195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = ((inptr[1] & 0x0f) << 4) | (inptr[3] & 0x0f); 340195972f6Sopenharmony_ci } else if (inptr[0] == 0xf0) { 341195972f6Sopenharmony_ci /* Compress source port. */ 342195972f6Sopenharmony_ci buffer[lowpan6_header_len++] |= 0x02; 343195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[1]; 344195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[2]; 345195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[3]; 346195972f6Sopenharmony_ci } else if (inptr[2] == 0xf0) { 347195972f6Sopenharmony_ci /* Compress dest port. */ 348195972f6Sopenharmony_ci buffer[lowpan6_header_len++] |= 0x01; 349195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[0]; 350195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[1]; 351195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[3]; 352195972f6Sopenharmony_ci } else { 353195972f6Sopenharmony_ci /* append full ports. */ 354195972f6Sopenharmony_ci lowpan6_header_len++; 355195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[0]; 356195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[1]; 357195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[2]; 358195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[3]; 359195972f6Sopenharmony_ci } 360195972f6Sopenharmony_ci 361195972f6Sopenharmony_ci /* elide length and copy checksum */ 362195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[6]; 363195972f6Sopenharmony_ci buffer[lowpan6_header_len++] = inptr[7]; 364195972f6Sopenharmony_ci 365195972f6Sopenharmony_ci hidden_header_len += UDP_HLEN; 366195972f6Sopenharmony_ci } 367195972f6Sopenharmony_ci#endif /* LWIP_UDP */ 368195972f6Sopenharmony_ci 369195972f6Sopenharmony_ci *lowpan6_header_len_out = lowpan6_header_len; 370195972f6Sopenharmony_ci *hidden_header_len_out = hidden_header_len; 371195972f6Sopenharmony_ci 372195972f6Sopenharmony_ci return ERR_OK; 373195972f6Sopenharmony_ci} 374195972f6Sopenharmony_ci 375195972f6Sopenharmony_ci/** Decompress IPv6 and UDP headers compressed according to RFC 6282 376195972f6Sopenharmony_ci * 377195972f6Sopenharmony_ci * @param lowpan6_buffer compressed headers, first byte is the dispatch byte 378195972f6Sopenharmony_ci * @param lowpan6_bufsize size of lowpan6_buffer (may include data after headers) 379195972f6Sopenharmony_ci * @param decomp_buffer buffer where the decompressed headers are stored 380195972f6Sopenharmony_ci * @param decomp_bufsize size of decomp_buffer 381195972f6Sopenharmony_ci * @param hdr_size_comp returns the size of the compressed headers (skip to get to data) 382195972f6Sopenharmony_ci * @param hdr_size_decomp returns the size of the decompressed headers (IPv6 + UDP) 383195972f6Sopenharmony_ci * @param datagram_size datagram size from fragments or 0 if unfragmented 384195972f6Sopenharmony_ci * @param compressed_size compressed datagram size (for unfragmented rx) 385195972f6Sopenharmony_ci * @param lowpan6_contexts context addresses 386195972f6Sopenharmony_ci * @param src source address of the outer layer, used for address compression 387195972f6Sopenharmony_ci * @param dest destination address of the outer layer, used for address compression 388195972f6Sopenharmony_ci * @return ERR_OK if decompression succeeded, an error otherwise 389195972f6Sopenharmony_ci */ 390195972f6Sopenharmony_cistatic err_t 391195972f6Sopenharmony_cilowpan6_decompress_hdr(u8_t *lowpan6_buffer, size_t lowpan6_bufsize, 392195972f6Sopenharmony_ci u8_t *decomp_buffer, size_t decomp_bufsize, 393195972f6Sopenharmony_ci u16_t *hdr_size_comp, u16_t *hdr_size_decomp, 394195972f6Sopenharmony_ci u16_t datagram_size, u16_t compressed_size, 395195972f6Sopenharmony_ci ip6_addr_t *lowpan6_contexts, 396195972f6Sopenharmony_ci struct lowpan6_link_addr *src, struct lowpan6_link_addr *dest) 397195972f6Sopenharmony_ci{ 398195972f6Sopenharmony_ci u16_t lowpan6_offset; 399195972f6Sopenharmony_ci struct ip6_hdr *ip6hdr; 400195972f6Sopenharmony_ci s8_t i; 401195972f6Sopenharmony_ci u32_t header_temp; 402195972f6Sopenharmony_ci u16_t ip6_offset = IP6_HLEN; 403195972f6Sopenharmony_ci 404195972f6Sopenharmony_ci LWIP_ASSERT("lowpan6_buffer != NULL", lowpan6_buffer != NULL); 405195972f6Sopenharmony_ci LWIP_ASSERT("decomp_buffer != NULL", decomp_buffer != NULL); 406195972f6Sopenharmony_ci LWIP_ASSERT("src != NULL", src != NULL); 407195972f6Sopenharmony_ci LWIP_ASSERT("dest != NULL", dest != NULL); 408195972f6Sopenharmony_ci LWIP_ASSERT("hdr_size_comp != NULL", hdr_size_comp != NULL); 409195972f6Sopenharmony_ci LWIP_ASSERT("dehdr_size_decompst != NULL", hdr_size_decomp != NULL); 410195972f6Sopenharmony_ci 411195972f6Sopenharmony_ci ip6hdr = (struct ip6_hdr *)decomp_buffer; 412195972f6Sopenharmony_ci if (decomp_bufsize < IP6_HLEN) { 413195972f6Sopenharmony_ci return ERR_MEM; 414195972f6Sopenharmony_ci } 415195972f6Sopenharmony_ci 416195972f6Sopenharmony_ci /* output the full compressed packet, if set in @see lowpan6_opts.h */ 417195972f6Sopenharmony_ci#if LWIP_LOWPAN6_IP_COMPRESSED_DEBUG 418195972f6Sopenharmony_ci { 419195972f6Sopenharmony_ci u16_t j; 420195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("lowpan6_decompress_hdr: IP6 payload (compressed): \n")); 421195972f6Sopenharmony_ci for (j = 0; j < lowpan6_bufsize; j++) { 422195972f6Sopenharmony_ci if ((j % 4) == 0) { 423195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("\n")); 424195972f6Sopenharmony_ci } 425195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("%2X ", lowpan6_buffer[j])); 426195972f6Sopenharmony_ci } 427195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("\np->len: %d", lowpan6_bufsize)); 428195972f6Sopenharmony_ci } 429195972f6Sopenharmony_ci#endif 430195972f6Sopenharmony_ci 431195972f6Sopenharmony_ci /* offset for inline IP headers (RFC 6282 ch3)*/ 432195972f6Sopenharmony_ci lowpan6_offset = 2; 433195972f6Sopenharmony_ci /* if CID is set (context identifier), the context byte 434195972f6Sopenharmony_ci * follows immediately after the header, so other IPHC fields are @+3 */ 435195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x80) { 436195972f6Sopenharmony_ci lowpan6_offset++; 437195972f6Sopenharmony_ci } 438195972f6Sopenharmony_ci 439195972f6Sopenharmony_ci /* Set IPv6 version, traffic class and flow label. (RFC6282, ch 3.1.1.)*/ 440195972f6Sopenharmony_ci if ((lowpan6_buffer[0] & 0x18) == 0x00) { 441195972f6Sopenharmony_ci header_temp = ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | \ 442195972f6Sopenharmony_ci (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3]; 443195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 00, ECN: 0x%"X8_F", Flowlabel+DSCP: 0x%8"X32_F"\n", \ 444195972f6Sopenharmony_ci lowpan6_buffer[lowpan6_offset],header_temp)); 445195972f6Sopenharmony_ci IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], header_temp); 446195972f6Sopenharmony_ci /* increase offset, processed 4 bytes here: 447195972f6Sopenharmony_ci * TF=00: ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)*/ 448195972f6Sopenharmony_ci lowpan6_offset += 4; 449195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x18) == 0x08) { 450195972f6Sopenharmony_ci header_temp = ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2]; 451195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 01, ECN: 0x%"X8_F", Flowlabel: 0x%2"X32_F", DSCP ignored\n", \ 452195972f6Sopenharmony_ci lowpan6_buffer[lowpan6_offset] & 0xc0,header_temp)); 453195972f6Sopenharmony_ci IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, header_temp); 454195972f6Sopenharmony_ci /* increase offset, processed 3 bytes here: 455195972f6Sopenharmony_ci * TF=01: ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided.*/ 456195972f6Sopenharmony_ci lowpan6_offset += 3; 457195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x18) == 0x10) { 458195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 10, DCSP+ECN: 0x%"X8_F", Flowlabel ignored\n", lowpan6_buffer[lowpan6_offset])); 459195972f6Sopenharmony_ci IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0); 460195972f6Sopenharmony_ci /* increase offset, processed 1 byte here: 461195972f6Sopenharmony_ci * ECN + DSCP (1 byte), Flow Label is elided.*/ 462195972f6Sopenharmony_ci lowpan6_offset += 1; 463195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x18) == 0x18) { 464195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 11, DCSP/ECN & Flowlabel ignored\n")); 465195972f6Sopenharmony_ci /* don't increase offset, no bytes processed here */ 466195972f6Sopenharmony_ci IP6H_VTCFL_SET(ip6hdr, 6, 0, 0); 467195972f6Sopenharmony_ci } 468195972f6Sopenharmony_ci 469195972f6Sopenharmony_ci /* Set Next Header (NH) */ 470195972f6Sopenharmony_ci if ((lowpan6_buffer[0] & 0x04) == 0x00) { 471195972f6Sopenharmony_ci /* 0: full next header byte carried inline (increase offset)*/ 472195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NH: 0x%2X\n", lowpan6_buffer[lowpan6_offset+1])); 473195972f6Sopenharmony_ci IP6H_NEXTH_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]); 474195972f6Sopenharmony_ci } else { 475195972f6Sopenharmony_ci /* 1: NH compression, LOWPAN_NHC (RFC6282, ch 4.1) */ 476195972f6Sopenharmony_ci /* We should fill this later with NHC decoding */ 477195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NH: skipped, later done with NHC\n")); 478195972f6Sopenharmony_ci IP6H_NEXTH_SET(ip6hdr, 0); 479195972f6Sopenharmony_ci } 480195972f6Sopenharmony_ci 481195972f6Sopenharmony_ci /* Set Hop Limit, either carried inline or 3 different hops (1,64,255) */ 482195972f6Sopenharmony_ci if ((lowpan6_buffer[0] & 0x03) == 0x00) { 483195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: full value: %d\n", lowpan6_buffer[lowpan6_offset+1])); 484195972f6Sopenharmony_ci IP6H_HOPLIM_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]); 485195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x03) == 0x01) { 486195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 1\n")); 487195972f6Sopenharmony_ci IP6H_HOPLIM_SET(ip6hdr, 1); 488195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x03) == 0x02) { 489195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 64\n")); 490195972f6Sopenharmony_ci IP6H_HOPLIM_SET(ip6hdr, 64); 491195972f6Sopenharmony_ci } else if ((lowpan6_buffer[0] & 0x03) == 0x03) { 492195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 255\n")); 493195972f6Sopenharmony_ci IP6H_HOPLIM_SET(ip6hdr, 255); 494195972f6Sopenharmony_ci } 495195972f6Sopenharmony_ci 496195972f6Sopenharmony_ci /* Source address decoding. */ 497195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x40) == 0x00) { 498195972f6Sopenharmony_ci /* Source address compression (SAC) = 0 -> stateless compression */ 499195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAC == 0, no context byte\n")); 500195972f6Sopenharmony_ci /* Stateless compression */ 501195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x30) == 0x00) { 502195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 00, no src compression, fetching 128bits inline\n")); 503195972f6Sopenharmony_ci /* copy full address, increase offset by 16 Bytes */ 504195972f6Sopenharmony_ci MEMCPY(&ip6hdr->src.addr[0], lowpan6_buffer + lowpan6_offset, 16); 505195972f6Sopenharmony_ci lowpan6_offset += 16; 506195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x30) == 0x10) { 507195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 01, src compression, 64bits inline\n")); 508195972f6Sopenharmony_ci /* set 64 bits to link local */ 509195972f6Sopenharmony_ci ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); 510195972f6Sopenharmony_ci ip6hdr->src.addr[1] = 0; 511195972f6Sopenharmony_ci /* copy 8 Bytes, increase offset */ 512195972f6Sopenharmony_ci MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8); 513195972f6Sopenharmony_ci lowpan6_offset += 8; 514195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x30) == 0x20) { 515195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 10, src compression, 16bits inline\n")); 516195972f6Sopenharmony_ci /* set 96 bits to link local */ 517195972f6Sopenharmony_ci ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); 518195972f6Sopenharmony_ci ip6hdr->src.addr[1] = 0; 519195972f6Sopenharmony_ci ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); 520195972f6Sopenharmony_ci /* extract remaining 16bits from inline bytes, increase offset */ 521195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | 522195972f6Sopenharmony_ci lowpan6_buffer[lowpan6_offset + 1]); 523195972f6Sopenharmony_ci lowpan6_offset += 2; 524195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x30) == 0x30) { 525195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 11, src compression, 0bits inline, using other headers\n")); 526195972f6Sopenharmony_ci /* no information avalaible, using other layers, see RFC6282 ch 3.2.2 */ 527195972f6Sopenharmony_ci ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); 528195972f6Sopenharmony_ci ip6hdr->src.addr[1] = 0; 529195972f6Sopenharmony_ci if (src->addr_len == 2) { 530195972f6Sopenharmony_ci ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); 531195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]); 532195972f6Sopenharmony_ci } else if (src->addr_len == 8) { 533195972f6Sopenharmony_ci ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | 534195972f6Sopenharmony_ci (src->addr[2] << 8) | src->addr[3]); 535195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | 536195972f6Sopenharmony_ci (src->addr[6] << 8) | src->addr[7]); 537195972f6Sopenharmony_ci } else { 538195972f6Sopenharmony_ci /* invalid source address length */ 539195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid source address length\n")); 540195972f6Sopenharmony_ci return ERR_VAL; 541195972f6Sopenharmony_ci } 542195972f6Sopenharmony_ci } 543195972f6Sopenharmony_ci } else { 544195972f6Sopenharmony_ci /* Source address compression (SAC) = 1 -> stateful/context-based compression */ 545195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAC == 1, additional context byte\n")); 546195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x30) == 0x00) { 547195972f6Sopenharmony_ci /* SAM=00, address=> :: (ANY) */ 548195972f6Sopenharmony_ci ip6hdr->src.addr[0] = 0; 549195972f6Sopenharmony_ci ip6hdr->src.addr[1] = 0; 550195972f6Sopenharmony_ci ip6hdr->src.addr[2] = 0; 551195972f6Sopenharmony_ci ip6hdr->src.addr[3] = 0; 552195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 00, context compression, ANY (::)\n")); 553195972f6Sopenharmony_ci } else { 554195972f6Sopenharmony_ci /* Set prefix from context info */ 555195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x80) { 556195972f6Sopenharmony_ci i = (lowpan6_buffer[2] >> 4) & 0x0f; 557195972f6Sopenharmony_ci } else { 558195972f6Sopenharmony_ci i = 0; 559195972f6Sopenharmony_ci } 560195972f6Sopenharmony_ci if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) { 561195972f6Sopenharmony_ci /* Error */ 562195972f6Sopenharmony_ci return ERR_VAL; 563195972f6Sopenharmony_ci } 564195972f6Sopenharmony_ci#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 565195972f6Sopenharmony_ci ip6hdr->src.addr[0] = lowpan6_contexts[i].addr[0]; 566195972f6Sopenharmony_ci ip6hdr->src.addr[1] = lowpan6_contexts[i].addr[1]; 567195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == xx, context compression found @%d: %8"X32_F", %8"X32_F"\n", (int)i, ip6hdr->src.addr[0], ip6hdr->src.addr[1])); 568195972f6Sopenharmony_ci#else 569195972f6Sopenharmony_ci LWIP_UNUSED_ARG(lowpan6_contexts); 570195972f6Sopenharmony_ci#endif 571195972f6Sopenharmony_ci } 572195972f6Sopenharmony_ci 573195972f6Sopenharmony_ci /* determine further address bits */ 574195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x30) == 0x10) { 575195972f6Sopenharmony_ci /* SAM=01, load additional 64bits */ 576195972f6Sopenharmony_ci MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8); 577195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 01, context compression, 64bits inline\n")); 578195972f6Sopenharmony_ci lowpan6_offset += 8; 579195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x30) == 0x20) { 580195972f6Sopenharmony_ci /* SAM=01, load additional 16bits */ 581195972f6Sopenharmony_ci ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); 582195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]); 583195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 10, context compression, 16bits inline\n")); 584195972f6Sopenharmony_ci lowpan6_offset += 2; 585195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x30) == 0x30) { 586195972f6Sopenharmony_ci /* SAM=11, address is fully elided, load from other layers */ 587195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 11, context compression, 0bits inline, using other headers\n")); 588195972f6Sopenharmony_ci if (src->addr_len == 2) { 589195972f6Sopenharmony_ci ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); 590195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]); 591195972f6Sopenharmony_ci } else if (src->addr_len == 8) { 592195972f6Sopenharmony_ci ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | (src->addr[2] << 8) | src->addr[3]); 593195972f6Sopenharmony_ci ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | (src->addr[6] << 8) | src->addr[7]); 594195972f6Sopenharmony_ci } else { 595195972f6Sopenharmony_ci /* invalid source address length */ 596195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid source address length\n")); 597195972f6Sopenharmony_ci return ERR_VAL; 598195972f6Sopenharmony_ci } 599195972f6Sopenharmony_ci } 600195972f6Sopenharmony_ci } 601195972f6Sopenharmony_ci 602195972f6Sopenharmony_ci /* Destination address decoding. */ 603195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x08) { 604195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("M=1: multicast\n")); 605195972f6Sopenharmony_ci /* Multicast destination */ 606195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x04) { 607195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_DBG_ON,("DAC == 1, context multicast: unsupported!!!\n")); 608195972f6Sopenharmony_ci /* @todo support stateful multicast addressing */ 609195972f6Sopenharmony_ci return ERR_VAL; 610195972f6Sopenharmony_ci } 611195972f6Sopenharmony_ci 612195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x03) == 0x00) { 613195972f6Sopenharmony_ci /* DAM = 00, copy full address (128bits) */ 614195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 00, no dst compression, fetching 128bits inline\n")); 615195972f6Sopenharmony_ci MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16); 616195972f6Sopenharmony_ci lowpan6_offset += 16; 617195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x01) { 618195972f6Sopenharmony_ci /* DAM = 01, copy 4 bytes (32bits) */ 619195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst address form (48bits): ffXX::00XX:XXXX:XXXX\n")); 620195972f6Sopenharmony_ci ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16)); 621195972f6Sopenharmony_ci ip6hdr->dest.addr[1] = 0; 622195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]); 623195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 24) | (lowpan6_buffer[lowpan6_offset + 1] << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset + 3]); 624195972f6Sopenharmony_ci lowpan6_offset += 4; 625195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x02) { 626195972f6Sopenharmony_ci /* DAM = 10, copy 3 bytes (24bits) */ 627195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 10, dst address form (32bits): ffXX::00XX:XXXX\n")); 628195972f6Sopenharmony_ci ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16)); 629195972f6Sopenharmony_ci ip6hdr->dest.addr[1] = 0; 630195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = 0; 631195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset + 2]); 632195972f6Sopenharmony_ci lowpan6_offset += 3; 633195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x03) { 634195972f6Sopenharmony_ci /* DAM = 11, copy 1 byte (8bits) */ 635195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 11, dst address form (8bits): ff02::00XX\n")); 636195972f6Sopenharmony_ci ip6hdr->dest.addr[0] = PP_HTONL(0xff020000UL); 637195972f6Sopenharmony_ci ip6hdr->dest.addr[1] = 0; 638195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = 0; 639195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]); 640195972f6Sopenharmony_ci } 641195972f6Sopenharmony_ci 642195972f6Sopenharmony_ci } else { 643195972f6Sopenharmony_ci /* no Multicast (M=0) */ 644195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x04) { 645195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAC == 1, stateful compression\n")); 646195972f6Sopenharmony_ci /* Stateful destination compression */ 647195972f6Sopenharmony_ci /* Set prefix from context info */ 648195972f6Sopenharmony_ci if (lowpan6_buffer[1] & 0x80) { 649195972f6Sopenharmony_ci i = lowpan6_buffer[2] & 0x0f; 650195972f6Sopenharmony_ci } else { 651195972f6Sopenharmony_ci i = 0; 652195972f6Sopenharmony_ci } 653195972f6Sopenharmony_ci if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) { 654195972f6Sopenharmony_ci /* Error */ 655195972f6Sopenharmony_ci return ERR_VAL; 656195972f6Sopenharmony_ci } 657195972f6Sopenharmony_ci#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 658195972f6Sopenharmony_ci ip6hdr->dest.addr[0] = lowpan6_contexts[i].addr[0]; 659195972f6Sopenharmony_ci ip6hdr->dest.addr[1] = lowpan6_contexts[i].addr[1]; 660195972f6Sopenharmony_ci#endif 661195972f6Sopenharmony_ci } else { 662195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAC == 0, stateless compression, setting link local prefix\n")); 663195972f6Sopenharmony_ci /* Link local address compression */ 664195972f6Sopenharmony_ci ip6hdr->dest.addr[0] = PP_HTONL(0xfe800000UL); 665195972f6Sopenharmony_ci ip6hdr->dest.addr[1] = 0; 666195972f6Sopenharmony_ci } 667195972f6Sopenharmony_ci 668195972f6Sopenharmony_ci /* M=0, DAC=0, determining destination address length via DAM=xx */ 669195972f6Sopenharmony_ci if ((lowpan6_buffer[1] & 0x03) == 0x00) { 670195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 00, no dst compression, fetching 128bits inline")); 671195972f6Sopenharmony_ci /* DAM=00, copy full address */ 672195972f6Sopenharmony_ci MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16); 673195972f6Sopenharmony_ci lowpan6_offset += 16; 674195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x01) { 675195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst compression, 64bits inline\n")); 676195972f6Sopenharmony_ci /* DAM=01, copy 64 inline bits, increase offset */ 677195972f6Sopenharmony_ci MEMCPY(&ip6hdr->dest.addr[2], lowpan6_buffer + lowpan6_offset, 8); 678195972f6Sopenharmony_ci lowpan6_offset += 8; 679195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x02) { 680195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst compression, 16bits inline\n")); 681195972f6Sopenharmony_ci /* DAM=10, copy 16 inline bits, increase offset */ 682195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL); 683195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]); 684195972f6Sopenharmony_ci lowpan6_offset += 2; 685195972f6Sopenharmony_ci } else if ((lowpan6_buffer[1] & 0x03) == 0x03) { 686195972f6Sopenharmony_ci /* DAM=11, no bits available, use other headers (not done here) */ 687195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG,("DAM == 01, dst compression, 0bits inline, using other headers\n")); 688195972f6Sopenharmony_ci if (dest->addr_len == 2) { 689195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL); 690195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (dest->addr[0] << 8) | dest->addr[1]); 691195972f6Sopenharmony_ci } else if (dest->addr_len == 8) { 692195972f6Sopenharmony_ci ip6hdr->dest.addr[2] = lwip_htonl(((dest->addr[0] ^ 2) << 24) | (dest->addr[1] << 16) | dest->addr[2] << 8 | dest->addr[3]); 693195972f6Sopenharmony_ci ip6hdr->dest.addr[3] = lwip_htonl((dest->addr[4] << 24) | (dest->addr[5] << 16) | dest->addr[6] << 8 | dest->addr[7]); 694195972f6Sopenharmony_ci } else { 695195972f6Sopenharmony_ci /* invalid destination address length */ 696195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid destination address length\n")); 697195972f6Sopenharmony_ci return ERR_VAL; 698195972f6Sopenharmony_ci } 699195972f6Sopenharmony_ci } 700195972f6Sopenharmony_ci } 701195972f6Sopenharmony_ci 702195972f6Sopenharmony_ci 703195972f6Sopenharmony_ci /* Next Header Compression (NHC) decoding? */ 704195972f6Sopenharmony_ci if (lowpan6_buffer[0] & 0x04) { 705195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NHC decoding\n")); 706195972f6Sopenharmony_ci#if LWIP_UDP 707195972f6Sopenharmony_ci if ((lowpan6_buffer[lowpan6_offset] & 0xf8) == 0xf0) { 708195972f6Sopenharmony_ci /* NHC: UDP */ 709195972f6Sopenharmony_ci struct udp_hdr *udphdr; 710195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NHC: UDP\n")); 711195972f6Sopenharmony_ci 712195972f6Sopenharmony_ci /* UDP compression */ 713195972f6Sopenharmony_ci IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_UDP); 714195972f6Sopenharmony_ci udphdr = (struct udp_hdr *)((u8_t *)decomp_buffer + ip6_offset); 715195972f6Sopenharmony_ci if (decomp_bufsize < IP6_HLEN + UDP_HLEN) { 716195972f6Sopenharmony_ci return ERR_MEM; 717195972f6Sopenharmony_ci } 718195972f6Sopenharmony_ci 719195972f6Sopenharmony_ci /* Checksum decompression */ 720195972f6Sopenharmony_ci if (lowpan6_buffer[lowpan6_offset] & 0x04) { 721195972f6Sopenharmony_ci /* @todo support checksum decompress */ 722195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_DBG_ON, ("NHC: UDP chechsum decompression UNSUPPORTED\n")); 723195972f6Sopenharmony_ci return ERR_VAL; 724195972f6Sopenharmony_ci } 725195972f6Sopenharmony_ci 726195972f6Sopenharmony_ci /* Decompress ports, according to RFC4944 */ 727195972f6Sopenharmony_ci i = lowpan6_buffer[lowpan6_offset++] & 0x03; 728195972f6Sopenharmony_ci if (i == 0) { 729195972f6Sopenharmony_ci udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); 730195972f6Sopenharmony_ci udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 2] << 8 | lowpan6_buffer[lowpan6_offset + 3]); 731195972f6Sopenharmony_ci lowpan6_offset += 4; 732195972f6Sopenharmony_ci } else if (i == 0x01) { 733195972f6Sopenharmony_ci udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); 734195972f6Sopenharmony_ci udphdr->dest = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset + 2]); 735195972f6Sopenharmony_ci lowpan6_offset += 3; 736195972f6Sopenharmony_ci } else if (i == 0x02) { 737195972f6Sopenharmony_ci udphdr->src = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset]); 738195972f6Sopenharmony_ci udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 1] << 8 | lowpan6_buffer[lowpan6_offset + 2]); 739195972f6Sopenharmony_ci lowpan6_offset += 3; 740195972f6Sopenharmony_ci } else if (i == 0x03) { 741195972f6Sopenharmony_ci udphdr->src = lwip_htons(0xf0b0 | ((lowpan6_buffer[lowpan6_offset] >> 4) & 0x0f)); 742195972f6Sopenharmony_ci udphdr->dest = lwip_htons(0xf0b0 | (lowpan6_buffer[lowpan6_offset] & 0x0f)); 743195972f6Sopenharmony_ci lowpan6_offset += 1; 744195972f6Sopenharmony_ci } 745195972f6Sopenharmony_ci 746195972f6Sopenharmony_ci udphdr->chksum = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); 747195972f6Sopenharmony_ci lowpan6_offset += 2; 748195972f6Sopenharmony_ci ip6_offset += UDP_HLEN; 749195972f6Sopenharmony_ci if (datagram_size == 0) { 750195972f6Sopenharmony_ci datagram_size = compressed_size - lowpan6_offset + ip6_offset; 751195972f6Sopenharmony_ci } 752195972f6Sopenharmony_ci udphdr->len = lwip_htons(datagram_size - IP6_HLEN); 753195972f6Sopenharmony_ci 754195972f6Sopenharmony_ci } else 755195972f6Sopenharmony_ci#endif /* LWIP_UDP */ 756195972f6Sopenharmony_ci { 757195972f6Sopenharmony_ci LWIP_DEBUGF(LWIP_DBG_ON,("NHC: unsupported protocol!\n")); 758195972f6Sopenharmony_ci /* @todo support NHC other than UDP */ 759195972f6Sopenharmony_ci return ERR_VAL; 760195972f6Sopenharmony_ci } 761195972f6Sopenharmony_ci } 762195972f6Sopenharmony_ci if (datagram_size == 0) { 763195972f6Sopenharmony_ci datagram_size = compressed_size - lowpan6_offset + ip6_offset; 764195972f6Sopenharmony_ci } 765195972f6Sopenharmony_ci /* Infer IPv6 payload length for header */ 766195972f6Sopenharmony_ci IP6H_PLEN_SET(ip6hdr, datagram_size - IP6_HLEN); 767195972f6Sopenharmony_ci 768195972f6Sopenharmony_ci if (lowpan6_offset > lowpan6_bufsize) { 769195972f6Sopenharmony_ci /* input buffer overflow */ 770195972f6Sopenharmony_ci return ERR_VAL; 771195972f6Sopenharmony_ci } 772195972f6Sopenharmony_ci *hdr_size_comp = lowpan6_offset; 773195972f6Sopenharmony_ci *hdr_size_decomp = ip6_offset; 774195972f6Sopenharmony_ci 775195972f6Sopenharmony_ci return ERR_OK; 776195972f6Sopenharmony_ci} 777195972f6Sopenharmony_ci 778195972f6Sopenharmony_cistruct pbuf * 779195972f6Sopenharmony_cilowpan6_decompress(struct pbuf *p, u16_t datagram_size, ip6_addr_t *lowpan6_contexts, 780195972f6Sopenharmony_ci struct lowpan6_link_addr *src, struct lowpan6_link_addr *dest) 781195972f6Sopenharmony_ci{ 782195972f6Sopenharmony_ci struct pbuf *q; 783195972f6Sopenharmony_ci u16_t lowpan6_offset, ip6_offset; 784195972f6Sopenharmony_ci err_t err; 785195972f6Sopenharmony_ci 786195972f6Sopenharmony_ci#if LWIP_UDP 787195972f6Sopenharmony_ci#define UDP_HLEN_ALLOC UDP_HLEN 788195972f6Sopenharmony_ci#else 789195972f6Sopenharmony_ci#define UDP_HLEN_ALLOC 0 790195972f6Sopenharmony_ci#endif 791195972f6Sopenharmony_ci 792195972f6Sopenharmony_ci /* Allocate a buffer for decompression. This buffer will be too big and will be 793195972f6Sopenharmony_ci trimmed once the final size is known. */ 794195972f6Sopenharmony_ci q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN_ALLOC, PBUF_POOL); 795195972f6Sopenharmony_ci if (q == NULL) { 796195972f6Sopenharmony_ci pbuf_free(p); 797195972f6Sopenharmony_ci return NULL; 798195972f6Sopenharmony_ci } 799195972f6Sopenharmony_ci if (q->len < IP6_HLEN + UDP_HLEN_ALLOC) { 800195972f6Sopenharmony_ci /* The headers need to fit into the first pbuf */ 801195972f6Sopenharmony_ci pbuf_free(p); 802195972f6Sopenharmony_ci pbuf_free(q); 803195972f6Sopenharmony_ci return NULL; 804195972f6Sopenharmony_ci } 805195972f6Sopenharmony_ci 806195972f6Sopenharmony_ci /* Decompress the IPv6 (and possibly UDP) header(s) into the new pbuf */ 807195972f6Sopenharmony_ci err = lowpan6_decompress_hdr((u8_t *)p->payload, p->len, (u8_t *)q->payload, q->len, 808195972f6Sopenharmony_ci &lowpan6_offset, &ip6_offset, datagram_size, p->tot_len, lowpan6_contexts, src, dest); 809195972f6Sopenharmony_ci if (err != ERR_OK) { 810195972f6Sopenharmony_ci pbuf_free(p); 811195972f6Sopenharmony_ci pbuf_free(q); 812195972f6Sopenharmony_ci return NULL; 813195972f6Sopenharmony_ci } 814195972f6Sopenharmony_ci 815195972f6Sopenharmony_ci /* Now we copy leftover contents from p to q, so we have all L2 and L3 headers 816195972f6Sopenharmony_ci (and L4?) in a single pbuf: */ 817195972f6Sopenharmony_ci 818195972f6Sopenharmony_ci /* Hide the compressed headers in p */ 819195972f6Sopenharmony_ci pbuf_remove_header(p, lowpan6_offset); 820195972f6Sopenharmony_ci /* Temporarily hide the headers in q... */ 821195972f6Sopenharmony_ci pbuf_remove_header(q, ip6_offset); 822195972f6Sopenharmony_ci /* ... copy the rest of p into q... */ 823195972f6Sopenharmony_ci pbuf_copy(q, p); 824195972f6Sopenharmony_ci /* ... and reveal the headers again... */ 825195972f6Sopenharmony_ci pbuf_add_header_force(q, ip6_offset); 826195972f6Sopenharmony_ci /* ... trim the pbuf to its correct size... */ 827195972f6Sopenharmony_ci pbuf_realloc(q, ip6_offset + p->len); 828195972f6Sopenharmony_ci /* ... and cat possibly remaining (data-only) pbufs */ 829195972f6Sopenharmony_ci if (p->next != NULL) { 830195972f6Sopenharmony_ci pbuf_cat(q, p->next); 831195972f6Sopenharmony_ci } 832195972f6Sopenharmony_ci /* the original (first) pbuf can now be freed */ 833195972f6Sopenharmony_ci p->next = NULL; 834195972f6Sopenharmony_ci pbuf_free(p); 835195972f6Sopenharmony_ci 836195972f6Sopenharmony_ci /* all done */ 837195972f6Sopenharmony_ci return q; 838195972f6Sopenharmony_ci} 839195972f6Sopenharmony_ci 840195972f6Sopenharmony_ci#endif /* LWIP_6LOWPAN_IPHC */ 841195972f6Sopenharmony_ci#endif /* LWIP_IPV6 */ 842