| /**************************************************************************** |
| * net/sixlowpan/sixlowpan_hc1.c |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| * Copyright (C) 2017, Gregory Nutt, all rights reserved |
| * Author: Gregory Nutt <gnutt@nuttx.org> |
| * |
| * Derives from Contiki: |
| * |
| * Copyright (c) 2008, Swedish Institute of Computer Science. |
| * All rights reserved. |
| * Authors: Adam Dunkels <adam@sics.se> |
| * Nicolas Tsiftes <nvt@sics.se> |
| * Niclas Finne <nfi@sics.se> |
| * Mathilde Durvy <mdurvy@cisco.com> |
| * Julien Abeille <jabeille@cisco.com> |
| * Joakim Eriksson <joakime@sics.se> |
| * Joel Hoglund <joel@sics.se> |
| * |
| * 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 <nuttx/config.h> |
| |
| #include <string.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/mm/iob.h> |
| #include <nuttx/net/netdev.h> |
| #include <nuttx/net/radiodev.h> |
| #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 */ |