/**************************************************************************** * net/sixlowpan/sixlowpan_hc1.c * * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2017, Gregory Nutt, all rights reserved * Author: Gregory Nutt * * Derives from Contiki: * * Copyright (c) 2008, Swedish Institute of Computer Science. * All rights reserved. * Authors: Adam Dunkels * Nicolas Tsiftes * Niclas Finne * Mathilde Durvy * Julien Abeille * Joakim Eriksson * Joel Hoglund * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1 /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: sixlowpan_compresshdr_hc1 * * Description: * Compress IP/UDP header using HC1 and HC_UDP * * This function is called by the 6lowpan code to create a compressed * 6lowpan packet in the packetbuf buffer from a full IPv6 packet in the * uip_buf buffer. * * If we can compress everything, we use HC1 dispatch, if not we use * IPv6 dispatch. We can compress everything if: * * - IP version is * - Flow label and traffic class are 0 * - Both src and dest ip addresses are link local * - Both src and dest interface ID are recoverable from lower layer * header * - Next header is either ICMP, UDP or TCP * * Moreover, if next header is UDP, we try to compress it using HC_UDP. * This is feasible is both ports are between F0B0 and F0B0 + 15 * * Resulting header structure: * - For ICMP, TCP, non compressed UDP\n * HC1 encoding = 11111010 (UDP) 11111110 (TCP) 11111100 (ICMP) * 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | LoWPAN HC1 Dsp | HC1 encoding | IPv6 Hop limit| L4 hdr + data| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | ... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * - For compressed UDP * HC1 encoding = 11111011, HC_UDP encoding = 11100000 * 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | LoWPAN HC1 Dsp| HC1 encoding | HC_UDP encod.| IPv6 Hop limit| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | src p.| dst p.| UDP checksum | L4 data... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Input Parameters: * radio - A reference to a radio network device instance * ipv6 - The IPv6 header followed by TCP, UDP, or ICMPv6 header to be * compressed * destmac - L2 destination address, needed to compress the IP * destination field * fptr - Pointer to frame to be compressed. * * Returned Value: * On success the indications of the defines COMPRESS_HDR_* are returned. * A negated errno value is returned on failure. * ****************************************************************************/ int sixlowpan_compresshdr_hc1(FAR struct radio_driver_s *radio, FAR const struct ipv6_hdr_s *ipv6, FAR const struct netdev_varaddr_s *destmac, FAR uint8_t *fptr) { FAR uint8_t *hc1 = fptr + g_frame_hdrlen; int ret = COMPRESS_HDR_INLINE; /* Check if all the assumptions for full compression are valid */ if (ipv6->vtc != 0x60 || ipv6->tcf != 0 || ipv6->flow != 0 || !sixlowpan_islinklocal(ipv6->srcipaddr) || !sixlowpan_ismacbased(ipv6->srcipaddr, &radio->r_dev.d_mac.radio) || !sixlowpan_islinklocal(ipv6->destipaddr) || !sixlowpan_ismacbased(ipv6->destipaddr, destmac) || (1 #ifdef CONFIG_NET_TCP && ipv6->proto != IP_PROTO_TCP #endif #ifdef CONFIG_NET_UDP && ipv6->proto != IP_PROTO_UDP #endif #ifdef CONFIG_NET_ICMPv6 && ipv6->proto != IP_PROTO_ICMP6 #endif )) { /* IPV6 DISPATCH * Something cannot be compressed, use IPV6 DISPATCH, compress * nothing, copy IPv6 header into the frame buffer */ nwarn("WARNING: Fall back to IPv6 dispatch\n"); /* IPv6 dispatch header (1 byte) */ hc1[SIXLOWPAN_HC1_DISPATCH] = SIXLOWPAN_DISPATCH_IPV6; g_frame_hdrlen += SIXLOWPAN_IPV6_HDR_LEN; memcpy(fptr + g_frame_hdrlen, ipv6, IPv6_HDRLEN); g_frame_hdrlen += IPv6_HDRLEN; g_uncomp_hdrlen += IPv6_HDRLEN; } else { /* HC1 DISPATCH maximum compression: * All fields in the IP header but Hop Limit are elided. If next * header is UDP, we compress UDP header using HC2 */ hc1[SIXLOWPAN_HC1_DISPATCH] = SIXLOWPAN_DISPATCH_HC1; g_uncomp_hdrlen += IPv6_HDRLEN; switch (ipv6->proto) { #ifdef CONFIG_NET_ICMPv6 case IP_PROTO_ICMP6: { /* HC1 encoding and ttl */ hc1[SIXLOWPAN_HC1_ENCODING] = 0xfc; hc1[SIXLOWPAN_HC1_TTL] = ipv6->ttl; g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; } break; #endif #ifdef CONFIG_NET_TCP case IP_PROTO_TCP: { /* HC1 encoding and ttl */ hc1[SIXLOWPAN_HC1_ENCODING] = 0xfe; hc1[SIXLOWPAN_HC1_TTL] = ipv6->ttl; g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; } break; #endif #ifdef CONFIG_NET_UDP case IP_PROTO_UDP: { FAR struct udp_hdr_s *udp = &(((FAR struct ipv6udp_hdr_s *)ipv6)->udp); /* Try to compress UDP header (we do only full compression). * This is feasible if both src and dest ports are between * CONFIG_NET_6LOWPAN_MINPORT and CONFIG_NET_6LOWPAN_MINPORT + * 15 */ ninfo("local/remote port %04x/%04x\n", udp->srcport, udp->destport); if (NTOHS(udp->srcport) >= CONFIG_NET_6LOWPAN_MINPORT && NTOHS(udp->srcport) < (CONFIG_NET_6LOWPAN_MINPORT + 16) && NTOHS(udp->destport) >= CONFIG_NET_6LOWPAN_MINPORT && NTOHS(udp->destport) < (CONFIG_NET_6LOWPAN_MINPORT + 16)) { FAR uint8_t *hcudp = fptr + g_frame_hdrlen; /* HC1 encoding */ hcudp[SIXLOWPAN_HC1_HC_UDP_HC1_ENCODING] = 0xfb; /* HC_UDP encoding, ttl, src and dest ports, checksum */ hcudp[SIXLOWPAN_HC1_HC_UDP_UDP_ENCODING] = 0xe0; hcudp[SIXLOWPAN_HC1_HC_UDP_TTL] = ipv6->ttl; hcudp[SIXLOWPAN_HC1_HC_UDP_PORTS] = (uint8_t)((NTOHS(udp->srcport) - CONFIG_NET_6LOWPAN_MINPORT) << 4) + (uint8_t)((NTOHS(udp->destport) - CONFIG_NET_6LOWPAN_MINPORT)); memcpy(&hcudp[SIXLOWPAN_HC1_HC_UDP_CHKSUM], &udp->udpchksum, 2); g_frame_hdrlen += SIXLOWPAN_HC1_HC_UDP_HDR_LEN; g_uncomp_hdrlen += UDP_HDRLEN; } else { /* HC1 encoding and ttl */ hc1[SIXLOWPAN_HC1_ENCODING] = 0xfa; hc1[SIXLOWPAN_HC1_TTL] = ipv6->ttl; g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; } ret = COMPRESS_HDR_ELIDED; } break; #endif /* CONFIG_NET_UDP */ default: { /* Test above assures that this will never happen */ nerr("ERROR: Unhandled protocol\n"); DEBUGPANIC(); } break; } } return ret; } /**************************************************************************** * Name: sixlowpan_uncompresshdr_hc1 * * Description: * Uncompress HC1 (and HC_UDP) headers and put them in sixlowpan_buf * * This function is called by the input function when the dispatch is * HC1. It processes the frame in the IOB buffer, uncompresses the * header fields, and copies the result in the packet buffer. At the * end of the decompression, g_frame_hdrlen and uncompressed_hdr_len * are set to the appropriate values * * Input Parameters: * metadata - Obfuscated MAC metadata including node addressing * information. * iplen - Equal to 0 if the packet is not a fragment (IP length is * then inferred from the L2 length), non 0 if the packet is * a 1st fragment. * iob - Pointer to the IOB containing the received frame. * fptr - Pointer to frame to be uncompressed. * bptr - Output goes here. Normally this is a known offset into * d_buf, may be redirected to a "bitbucket" on the case of * FRAGN frames. * * Returned Value: * Zero (OK) is returned on success, on failure a negated errno value is * returned. * ****************************************************************************/ int sixlowpan_uncompresshdr_hc1(FAR struct radio_driver_s *radio, FAR const void *metadata, uint16_t iplen, FAR struct iob_s *iob, FAR uint8_t *fptr, FAR uint8_t *bptr) { FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)bptr; FAR uint8_t *hc1 = fptr + g_frame_hdrlen; struct netdev_varaddr_s addr; int ret; ninfo("fptr=%p g_frame_hdrlen=%u\n", fptr, g_frame_hdrlen); /* Format the IPv6 header in the device d_buf. * * Set version, traffic clase, and flow label. This assumes that Bit 4 is * set in HC1. */ ipv6->vtc = 0x60; /* Bits 0-3: version, bits 4-7: traffic class (MS) */ ipv6->tcf = 0; /* Bits 0-3: traffic class (LS), 4-bits: flow label (MS) */ ipv6->flow = 0; /* 16-bit flow label (LS) */ g_uncomp_hdrlen += IPv6_HDRLEN; /* len[], proto, and ttl depend on the encoding */ switch (hc1[SIXLOWPAN_HC1_ENCODING] & 0x06) { #ifdef CONFIG_NET_ICMPv6 case SIXLOWPAN_HC1_NH_ICMPv6: ipv6->proto = IP_PROTO_ICMP6; ipv6->ttl = hc1[SIXLOWPAN_HC1_TTL]; g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; break; #endif #ifdef CONFIG_NET_TCP case SIXLOWPAN_HC1_NH_TCP: ipv6->proto = IP_PROTO_TCP; ipv6->ttl = hc1[SIXLOWPAN_HC1_TTL]; g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; break; #endif #ifdef CONFIG_NET_UDP case SIXLOWPAN_HC1_NH_UDP: { FAR struct udp_hdr_s *udp = (FAR struct udp_hdr_s *)(bptr + IPv6_HDRLEN); FAR uint8_t *hcudp = fptr + g_frame_hdrlen; ipv6->proto = IP_PROTO_UDP; /* Check for HC_UDP encoding */ if ((hcudp[SIXLOWPAN_HC1_HC_UDP_HC1_ENCODING] & 0x01) != 0) { /* UDP header is compressed with HC_UDP */ if (hcudp[SIXLOWPAN_HC1_HC_UDP_UDP_ENCODING] != SIXLOWPAN_HC_UDP_ALL_C) { nwarn("WARNING: " "sixlowpan (uncompress_hdr), packet not supported"); return -EOPNOTSUPP; } /* IP TTL */ ipv6->ttl = hcudp[SIXLOWPAN_HC1_HC_UDP_TTL]; /* UDP ports, len, checksum */ udp->srcport = HTONS(CONFIG_NET_6LOWPAN_MINPORT + (hcudp[SIXLOWPAN_HC1_HC_UDP_PORTS] >> 4)); udp->destport = HTONS(CONFIG_NET_6LOWPAN_MINPORT + (hcudp[SIXLOWPAN_HC1_HC_UDP_PORTS] & 0x0f)); ninfo("UDP srcport=%04x destport=%04x\n", udp->srcport, udp->destport); memcpy(&udp->udpchksum, &hcudp[SIXLOWPAN_HC1_HC_UDP_CHKSUM], 2); g_uncomp_hdrlen += UDP_HDRLEN; g_frame_hdrlen += SIXLOWPAN_HC1_HC_UDP_HDR_LEN; } else { g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; } } break; #endif /* CONFIG_NET_UDP */ default: return -EPROTONOSUPPORT; } /* Re-create the link-local, mac-based IP address from src/dest node * addresses. * * PC: Prefix compressed (link-local prefix assumed) * IC: Interface identifier elided (derivable from the corresponding * link-layer address). */ if ((hc1[SIXLOWPAN_HC1_ENCODING] & SIXLOWPAN_HC1_SRCADDR_MASK) == SIXLOWPAN_HC1_SRCADDR_PCIC) { ret = sixlowpan_extract_srcaddr(radio, metadata, &addr); if (ret < 0) { nerr("ERROR: sixlowpan_extract_srcaddr failed: %d\n", ret); } else { sixlowpan_ipfromaddr(&addr, ipv6->srcipaddr); } } else { nwarn("HC1 srcipaddr encoding not supported: %02x\n", hc1[SIXLOWPAN_HC1_ENCODING]); } ninfo("srcipaddr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", NTOHS(ipv6->srcipaddr[0]), NTOHS(ipv6->srcipaddr[1]), NTOHS(ipv6->srcipaddr[2]), NTOHS(ipv6->srcipaddr[3]), NTOHS(ipv6->srcipaddr[4]), NTOHS(ipv6->srcipaddr[5]), NTOHS(ipv6->srcipaddr[6]), NTOHS(ipv6->srcipaddr[7])); if ((hc1[SIXLOWPAN_HC1_ENCODING] & SIXLOWPAN_HC1_DESTADDR_MASK) == SIXLOWPAN_HC1_DESTADDR_PCIC) { ret = sixlowpan_extract_srcaddr(radio, metadata, &addr); if (ret < 0) { nerr("ERROR: sixlowpan_extract_srcaddr failed: %d\n", ret); } else { sixlowpan_ipfromaddr(&addr, ipv6->destipaddr); } } else { nwarn("HC1 destipaddr encoding not supported: %02x\n", hc1[SIXLOWPAN_HC1_ENCODING]); } ninfo("destipaddr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", NTOHS(ipv6->destipaddr[0]), NTOHS(ipv6->destipaddr[1]), NTOHS(ipv6->destipaddr[2]), NTOHS(ipv6->destipaddr[3]), NTOHS(ipv6->destipaddr[4]), NTOHS(ipv6->destipaddr[5]), NTOHS(ipv6->destipaddr[6]), NTOHS(ipv6->destipaddr[7])); /* IP length field. */ if (iplen == 0) { /* This is not a fragmented packet */ ipv6->len[0] = 0; ipv6->len[1] = iob->io_len - g_frame_hdrlen + g_uncomp_hdrlen - IPv6_HDRLEN; } else { /* This is a 1st fragment */ ipv6->len[0] = (iplen - IPv6_HDRLEN) >> 8; ipv6->len[1] = (iplen - IPv6_HDRLEN) & 0x00ff; } ninfo("IPv6 len=%02x:%02x\n", ipv6->len[0], ipv6->len[1]); #ifdef CONFIG_NET_UDP /* Length field in UDP header */ if (ipv6->proto == IP_PROTO_UDP) { FAR struct udp_hdr_s *udp = (FAR struct udp_hdr_s *)(bptr + IPv6_HDRLEN); memcpy(&udp->udplen, &ipv6->len[0], 2); ninfo("IPv6 len=%04x\n", udp->udplen); } #endif return OK; } #endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC1 */