darling-xnu/bsd/net/sixxlowpan.c
2023-05-16 21:41:14 -07:00

904 lines
28 KiB
C

/*
* Copyright (c) 2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 2008, Swedish Institute of Computer Science.
* All rights reserved.
*
* 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Header file for the 6lowpan implementation
* (RFC4944 and draft-hui-6lowpan-hc-01)
* \author Adam Dunkels <adam@sics.se>
* \author Nicolas Tsiftes <nvt@sics.se>
* \author Niclas Finne <nfi@sics.se>
* \author Mathilde Durvy <mdurvy@cisco.com>
* \author Julien Abeille <jabeille@cisco.com>
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/domain.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <sys/errno.h>
#include <libkern/libkern.h>
#include <net/sixxlowpan.h>
#include <net/frame802154.h>
errno_t
compress_hdr_hc1(struct frame802154 *, u_int8_t *,
long *, size_t *, u_int8_t *);
errno_t
uncompress_hdr_hc1(struct frame802154 *, u_int8_t *,
uint16_t, long *, size_t *, u_int8_t *);
/**
* \addtogroup sicslowpan
* @{
*/
/**
* \name General sicslowpan defines
* @{
*/
/* Min and Max compressible UDP ports - HC06 */
#define SICSLOWPAN_UDP_PORT_MIN 0xF0B0
#define SICSLOWPAN_UDP_PORT_MAX 0xF0BF /* F0B0 + 15 */
/** @} */
/**
* \name 6lowpan compressions
* @{
*/
#define SICSLOWPAN_COMPRESSION_IPV6 0
#define SICSLOWPAN_COMPRESSION_HC1 1
#define SICSLOWPAN_COMPRESSION_HC06 2
/** @} */
/**
* \name 6lowpan dispatches
* @{
*/
#define SICSLOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
#define SICSLOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */
#define SICSLOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
#define SICSLOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */
#define SICSLOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */
/** @} */
/** \name HC1 encoding
* @{
*/
#define SICSLOWPAN_HC1_NH_UDP 0x02
#define SICSLOWPAN_HC1_NH_TCP 0x06
#define SICSLOWPAN_HC1_NH_ICMP6 0x04
/** @} */
/** \name HC_UDP encoding (works together with HC1)
* @{
*/
#define SICSLOWPAN_HC_UDP_ALL_C 0xE0
/** @} */
/**
* \name IPHC encoding
* @{
*/
/*
* Values of fields within the IPHC encoding first byte
* (C stands for compressed and I for inline)
*/
#define SICSLOWPAN_IPHC_FL_C 0x10
#define SICSLOWPAN_IPHC_TC_C 0x08
#define SICSLOWPAN_IPHC_NH_C 0x04
#define SICSLOWPAN_IPHC_TTL_1 0x01
#define SICSLOWPAN_IPHC_TTL_64 0x02
#define SICSLOWPAN_IPHC_TTL_255 0x03
#define SICSLOWPAN_IPHC_TTL_I 0x00
/* Values of fields within the IPHC encoding second byte */
#define SICSLOWPAN_IPHC_CID 0x80
#define SICSLOWPAN_IPHC_SAC 0x40
#define SICSLOWPAN_IPHC_SAM_00 0x00
#define SICSLOWPAN_IPHC_SAM_01 0x10
#define SICSLOWPAN_IPHC_SAM_10 0x20
#define SICSLOWPAN_IPHC_SAM_11 0x30
#define SICSLOWPAN_IPHC_SAM_BIT 4
#define SICSLOWPAN_IPHC_M 0x08
#define SICSLOWPAN_IPHC_DAC 0x04
#define SICSLOWPAN_IPHC_DAM_00 0x00
#define SICSLOWPAN_IPHC_DAM_01 0x01
#define SICSLOWPAN_IPHC_DAM_10 0x02
#define SICSLOWPAN_IPHC_DAM_11 0x03
#define SICSLOWPAN_IPHC_DAM_BIT 0
/* Link local context number */
#define SICSLOWPAN_IPHC_ADDR_CONTEXT_LL 0
/* 16-bit multicast addresses compression */
#define SICSLOWPAN_IPHC_MCAST_RANGE 0xA0
/** @} */
/* NHC_EXT_HDR */
#define SICSLOWPAN_NHC_MASK 0xF0
#define SICSLOWPAN_NHC_EXT_HDR 0xE0
/**
* \name LOWPAN_UDP encoding (works together with IPHC)
* @{
*/
/**
* \name LOWPAN_UDP encoding (works together with IPHC)
* @{
*/
#define SICSLOWPAN_NHC_UDP_MASK 0xF8
#define SICSLOWPAN_NHC_UDP_ID 0xF0
#define SICSLOWPAN_NHC_UDP_CHECKSUMC 0x04
#define SICSLOWPAN_NHC_UDP_CHECKSUMI 0x00
/* values for port compression, _with checksum_ ie bit 5 set to 0 */
#define SICSLOWPAN_NHC_UDP_CS_P_00 0xF0 /* all inline */
#define SICSLOWPAN_NHC_UDP_CS_P_01 0xF1 /* source 16bit inline, dest = 0xF0 + 8 bit inline */
#define SICSLOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
#define SICSLOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */
/** @} */
/**
* \name The 6lowpan "headers" length
* @{
*/
#define SICSLOWPAN_IPV6_HDR_LEN 1 /*one byte*/
#define SICSLOWPAN_HC1_HDR_LEN 3
#define SICSLOWPAN_HC1_HC_UDP_HDR_LEN 7
#define SICSLOWPAN_FRAG1_HDR_LEN 4
#define SICSLOWPAN_FRAGN_HDR_LEN 5
// Minimum size of the compressed 6LoWPAN header length
#define SICSLOWPAN_MIN_COMP_HDR_LEN 7
// Minimum size of the uncompressed IPv6 header length
#define SICSLOWPAN_MIN_UNCOMP_HDR_LEN 40
#define UIP_IPH_LEN 40
#define UIP_UDPH_LEN 8 /* Size of UDP header */
#define UIP_TCPH_LEN 20 /* Size of TCP header */
#define UIP_ICMPH_LEN 4 /* Size of ICMP header */
/** @} */
/**
* \brief The header for fragments
* \note We do not define different structures for FRAG1
* and FRAGN headers, which are different. For FRAG1, the
* offset field is just not used
*/
/* struct sicslowpan_frag_hdr { */
/* uint16_t dispatch_size; */
/* uint16_t tag; */
/* uint8_t offset; */
/* }; */
/**
* \brief The HC1 header when HC_UDP is not used
*
* When all fields are compressed and HC_UDP is not used,
* we use this structure. If HC_UDP is used, the ttl is
* in another spot, and we use the sicslowpan_hc1_hc_udp
* structure
*/
/* struct sicslowpan_hc1_hdr { */
/* uint8_t dispatch; */
/* uint8_t encoding; */
/* uint8_t ttl; */
/* }; */
/**
* \brief HC1 followed by HC_UDP
*/
/* struct sicslowpan_hc1_hc_udp_hdr { */
/* uint8_t dispatch; */
/* uint8_t hc1_encoding; */
/* uint8_t hc_udp_encoding; */
/* uint8_t ttl; */
/* uint8_t ports; */
/* uint16_t udpchksum; */
/* }; */
/**
* \brief An address context for IPHC address compression
* each context can have upto 8 bytes
*/
struct sicslowpan_addr_context {
uint8_t used; /* possibly use as prefix-length */
uint8_t number;
uint8_t prefix[8];
};
/**
* \name Address compressibility test functions
* @{
*/
/**
* \brief check whether we can compress the IID in
* address 'a' to 16 bits.
* This is used for unicast addresses only, and is true
* if the address is on the format \<PREFIX\>::0000:00ff:fe00:XXXX
* NOTE: we currently assume 64-bits prefixes
*/
#define sicslowpan_is_iid_16_bit_compressable(a) \
((((a)->u16[4]) == 0) && \
(((a)->u8[10]) == 0)&& \
(((a)->u8[11]) == 0xff)&& \
(((a)->u8[12]) == 0xfe)&& \
(((a)->u8[13]) == 0))
/**
* \brief check whether the 9-bit group-id of the
* compressed multicast address is known. It is true
* if the 9-bit group is the all nodes or all routers
* group.
* \param a is typed uint8_t *
*/
#define sicslowpan_is_mcast_addr_decompressable(a) \
(((*a & 0x01) == 0) && \
((*(a + 1) == 0x01) || (*(a + 1) == 0x02)))
/**
* \brief check whether the 112-bit group-id of the
* multicast address is mappable to a 9-bit group-id
* It is true if the group is the all nodes or all
* routers group.
*/
#define sicslowpan_is_mcast_addr_compressable(a) \
((((a)->u16[1]) == 0) && \
(((a)->u16[2]) == 0) && \
(((a)->u16[3]) == 0) && \
(((a)->u16[4]) == 0) && \
(((a)->u16[5]) == 0) && \
(((a)->u16[6]) == 0) && \
(((a)->u8[14]) == 0) && \
((((a)->u8[15]) == 1) || (((a)->u8[15]) == 2)))
/* FFXX::00XX:XXXX:XXXX */
#define sicslowpan_is_mcast_addr_compressable48(a) \
((((a)->u16[1]) == 0) && \
(((a)->u16[2]) == 0) && \
(((a)->u16[3]) == 0) && \
(((a)->u16[4]) == 0) && \
(((a)->u8[10]) == 0))
/* FFXX::00XX:XXXX */
#define sicslowpan_is_mcast_addr_compressable32(a) \
((((a)->u16[1]) == 0) && \
(((a)->u16[2]) == 0) && \
(((a)->u16[3]) == 0) && \
(((a)->u16[4]) == 0) && \
(((a)->u16[5]) == 0) && \
(((a)->u8[12]) == 0))
/* FF02::00XX */
#define sicslowpan_is_mcast_addr_compressable8(a) \
((((a)->u8[1]) == 2) && \
(((a)->u16[1]) == 0) && \
(((a)->u16[2]) == 0) && \
(((a)->u16[3]) == 0) && \
(((a)->u16[4]) == 0) && \
(((a)->u16[5]) == 0) && \
(((a)->u16[6]) == 0) && \
(((a)->u8[14]) == 0))
#define uip_is_addr_mac_addr_based(a, m) \
((((a)->s6_addr[8]) == (((m)[0]) ^ 0x02)) && \
(((a)->s6_addr[9]) == (m)[1]) && \
(((a)->s6_addr[10]) == (m)[2]) && \
(((a)->s6_addr[11]) == (m)[3]) && \
(((a)->s6_addr[12]) == (m)[4]) && \
(((a)->s6_addr[13]) == (m)[5]) && \
(((a)->s6_addr[14]) == (m)[6]) && \
(((a)->s6_addr[15]) == (m)[7]))
/**
* Construct an IPv6 address from eight 16-bit words.
*
* This function constructs an IPv6 address.
*
* \hideinitializer
*/
#define uip_ip6addr(addr, addr0, addr1, addr2, addr3, addr4, addr5, addr6, addr7) do {\
(addr)->s6_addr[0] = htons(addr0); \
(addr)->s6_addr[1] = htons(addr1); \
(addr)->s6_addr[2] = htons(addr2); \
(addr)->s6_addr[3] = htons(addr3); \
(addr)->s6_addr[4] = htons(addr4); \
(addr)->s6_addr[5] = htons(addr5); \
(addr)->s6_addr[6] = htons(addr6); \
(addr)->s6_addr[7] = htons(addr7); \
} while(0)
/**
* Construct an IPv6 address from sixteen 8-bit words.
*
* This function constructs an IPv6 address.
*
* \hideinitializer
*/
#define uip_ip6addr_u8(addr, addr0, addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8, addr9, addr10, addr11, addr12, addr13, addr14, addr15) do {\
(addr)->s6_addr[0] = addr0; \
(addr)->s6_addr[1] = addr1; \
(addr)->s6_addr[2] = addr2; \
(addr)->s6_addr[3] = addr3; \
(addr)->s6_addr[4] = addr4; \
(addr)->s6_addr[5] = addr5; \
(addr)->s6_addr[6] = addr6; \
(addr)->s6_addr[7] = addr7; \
(addr)->s6_addr[8] = addr8; \
(addr)->s6_addr[9] = addr9; \
(addr)->s6_addr[10] = addr10; \
(addr)->s6_addr[11] = addr11; \
(addr)->s6_addr[12] = addr12; \
(addr)->s6_addr[13] = addr13; \
(addr)->s6_addr[14] = addr14; \
(addr)->s6_addr[15] = addr15; \
} while(0)
/** \brief 16 bit 802.15.4 address */
typedef struct uip_802154_shortaddr {
uint8_t addr[2];
} uip_802154_shortaddr;
/** \brief 64 bit 802.15.4 address */
typedef struct uip_802154_longaddr {
uint8_t addr[8];
} uip_802154_longaddr;
/** \brief 802.11 address */
typedef struct uip_80211_addr {
uint8_t addr[6];
} uip_80211_addr;
/** \brief 802.3 address */
typedef struct uip_eth_addr {
uint8_t addr[6];
} uip_eth_addr;
typedef uip_802154_longaddr uip_lladdr_t;
#define UIP_802154_SHORTADDR_LEN 2
#define UIP_802154_LONGADDR_LEN 8
#define UIP_LLADDR_LEN UIP_802154_LONGADDR_LEN
#define GET16(ptr) (((uint16_t)(((u_int8_t *)ptr)[0] << 8)) | (((u_int8_t *)ptr)[1]))
#define SET16(ptr, value) do { \
((u_int8_t *)ptr)[0] = ((value) >> 8) & 0xff; \
((u_int8_t *)ptr)[1] = (value) & 0xff; \
} while(0)
/** \name Pointers in the packetbuf buffer
* @{
*/
#define PACKETBUF_FRAG_DISPATCH_SIZE 0 /* 16 bit */
#define PACKETBUF_FRAG_TAG 2 /* 16 bit */
#define PACKETBUF_FRAG_OFFSET 4 /* 8 bit */
#define PACKETBUF_HC1_DISPATCH 0 /* 8 bit */
#define PACKETBUF_HC1_ENCODING 1 /* 8 bit */
#define PACKETBUF_HC1_TTL 2 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_DISPATCH 0 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_HC1_ENCODING 1 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_UDP_ENCODING 2 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_TTL 3 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_PORTS 4 /* 8 bit */
#define PACKETBUF_HC1_HC_UDP_CHKSUM 5 /* 16 bit */
#define LINKADDR_SIZE 8
typedef union {
unsigned char u8[LINKADDR_SIZE];
uint16_t u16;
} linkaddr_t;
static void
uip_ds6_set_addr_iid(struct in6_addr *ipaddr, uip_lladdr_t *lladdr)
{
/* We consider only links with IEEE EUI-64 identifier or
* IEEE 48-bit MAC addresses */
#if (UIP_LLADDR_LEN == 8)
memcpy(ipaddr->s6_addr + 8, lladdr, UIP_LLADDR_LEN);
ipaddr->s6_addr[8] ^= 0x02;
#elif (UIP_LLADDR_LEN == 6)
memcpy(ipaddr->s6_addr + 8, lladdr, 3);
ipaddr->s6_addr[11] = 0xff;
ipaddr->s6_addr[12] = 0xfe;
memcpy(ipaddr->s6_addr + 13, (uint8_t *)lladdr + 3, 3);
ipaddr->s6_addr[8] ^= 0x02;
#else
#error uip-ds6.c cannot build interface address when UIP_LLADDR_LEN is not 6 or 8
#endif
}
static errno_t
compress_hdr_ipv6(__unused struct frame802154 *ieee02154hdr,
__unused u_int8_t *payload,
long *hdroffset, size_t *hdrlen, u_int8_t *hdrbuf)
{
/*
* Negative offset: 6LoWPAN header needs to ve prepended to the data
*/
*hdroffset = -SICSLOWPAN_IPV6_HDR_LEN;
*hdrlen = SICSLOWPAN_IPV6_HDR_LEN;
hdrbuf[0] = SICSLOWPAN_DISPATCH_IPV6;
return 0;
}
#if 0
/*--------------------------------------------------------------------*/
/** \name HC1 compression and uncompression functions
* @{ */
/*--------------------------------------------------------------------*/
/**
* \brief 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.\n
* 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\n\n
*
* Resulting header structure:
* - For ICMP, TCP, non compressed UDP\n
* HC1 encoding = 11111010 (UDP) 11111110 (TCP) 11111100 (ICMP)\n
* \verbatim
* 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|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \endverbatim
*
* - For compressed UDP
* HC1 encoding = 11111011, HC_UDP encoding = 11100000\n
* \verbatim
* 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...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \endverbatim
*
* \param link_destaddr L2 destination address, needed to compress the
* IP destination field
*/
#endif
errno_t
compress_hdr_hc1(struct frame802154 *ieee02154hdr, u_int8_t *payload,
long *hdroffset, size_t *hdrlen, u_int8_t *hdrbuf)
{
struct ip6_hdr *ip6 = (struct ip6_hdr *)(payload);
if (*hdrlen < SICSLOWPAN_MIN_COMP_HDR_LEN) {
return EINVAL;
}
*hdroffset = 0;
/*
* Check if all the assumptions for full compression
* are valid :
*/
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION ||
!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) ||
!uip_is_addr_mac_addr_based(&ip6->ip6_src, ieee02154hdr->src_addr) ||
!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) ||
!uip_is_addr_mac_addr_based(&ip6->ip6_dst,
ieee02154hdr->dest_addr) ||
(ip6->ip6_nxt != IPPROTO_ICMPV6 &&
ip6->ip6_nxt != IPPROTO_UDP &&
ip6->ip6_nxt != IPPROTO_TCP)) {
/*
* IPV6 DISPATCH
* Something cannot be compressed, use IPV6 DISPATCH,
* compress nothing, copy IPv6 header in packetbuf buffer
*/
return compress_hdr_ipv6(ieee02154hdr, payload, hdroffset, hdrlen, hdrbuf);
} else {
/*
* HC1 DISPATCH
* maximum compresssion:
* All fields in the IP header but Hop Limit are elided
* If next header is UDP, we compress UDP header using HC2
*/
hdrbuf[PACKETBUF_HC1_DISPATCH] = SICSLOWPAN_DISPATCH_HC1;
switch (ip6->ip6_nxt) {
case IPPROTO_ICMPV6:
/* HC1 encoding and ttl */
hdrbuf[PACKETBUF_HC1_ENCODING] = 0xFC;
hdrbuf[PACKETBUF_HC1_TTL] = ip6->ip6_hlim;
*hdrlen = SICSLOWPAN_HC1_HDR_LEN;
*hdroffset = sizeof(struct ip6_hdr);
break;
case IPPROTO_TCP:
/* HC1 encoding and ttl */
hdrbuf[PACKETBUF_HC1_ENCODING] = 0xFE;
hdrbuf[PACKETBUF_HC1_TTL] = ip6->ip6_hlim;
*hdrlen = SICSLOWPAN_HC1_HDR_LEN;
*hdroffset = sizeof(struct ip6_hdr);
break;
case IPPROTO_UDP: {
struct udphdr *udp = (struct udphdr *)(uintptr_t)(ip6 + 1);
/*
* try to compress UDP header (we do only full compression).
* This is feasible if both src and dest ports are between
* SICSLOWPAN_UDP_PORT_MIN and SICSLOWPAN_UDP_PORT_MIN + 15
*/
printf("source/remote ports %u/%u\n", ntohs(udp->uh_sport), ntohs(udp->uh_dport));
if (ntohs(udp->uh_sport) >= SICSLOWPAN_UDP_PORT_MIN &&
ntohs(udp->uh_sport) < SICSLOWPAN_UDP_PORT_MAX &&
ntohs(udp->uh_dport) >= SICSLOWPAN_UDP_PORT_MIN &&
ntohs(udp->uh_dport) < SICSLOWPAN_UDP_PORT_MAX) {
/* HC1 encoding */
hdrbuf[PACKETBUF_HC1_HC_UDP_HC1_ENCODING] = 0xFB;
/* HC_UDP encoding, ttl, src and dest ports, checksum */
hdrbuf[PACKETBUF_HC1_HC_UDP_UDP_ENCODING] = 0xE0;
hdrbuf[PACKETBUF_HC1_HC_UDP_TTL] = ip6->ip6_hlim;
hdrbuf[PACKETBUF_HC1_HC_UDP_PORTS] =
(uint8_t)((ntohs(udp->uh_sport) - SICSLOWPAN_UDP_PORT_MIN) << 4) +
(uint8_t)((ntohs(udp->uh_dport) - SICSLOWPAN_UDP_PORT_MIN));
memcpy(&hdrbuf[PACKETBUF_HC1_HC_UDP_CHKSUM], &udp->uh_sum, 2);
*hdrlen = SICSLOWPAN_HC1_HC_UDP_HDR_LEN;
*hdroffset = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
} else {
/* HC1 encoding and ttl */
hdrbuf[PACKETBUF_HC1_ENCODING] = 0xFA;
hdrbuf[PACKETBUF_HC1_TTL] = ip6->ip6_hlim;
*hdrlen = SICSLOWPAN_HC1_HDR_LEN;
*hdroffset = sizeof(struct ip6_hdr);
}
break;
}
}
}
return 0;
}
/*--------------------------------------------------------------------*/
/**
* \brief Uncompress HC1 (and HC_UDP) headers and put them in
* sicslowpan_buf
*
* This function is called by the input function when the dispatch is
* HC1.
* We %process the packet in the packetbuf buffer, uncompress the header
* fields, and copy the result in the sicslowpan buffer.
* At the end of the decompression, packetbuf_hdr_len and uncompressed_hdr_len
* are set to the appropriate values
*
* \param ip_len 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.
*/
errno_t
uncompress_hdr_hc1(struct frame802154 *frame, u_int8_t *payload,
uint16_t ip_len, long *hdroffset, size_t *hdrlen, u_int8_t *hdrbuf)
{
struct ip6_hdr *ip6 = (struct ip6_hdr *)hdrbuf;
if (payload[PACKETBUF_HC1_DISPATCH] == SICSLOWPAN_DISPATCH_IPV6) {
*hdroffset = -SICSLOWPAN_IPV6_HDR_LEN;
*hdrlen = SICSLOWPAN_IPV6_HDR_LEN;
return 0;
}
*hdroffset = 0;
/* version, traffic class, flow label */
ip6->ip6_flow = 0;
ip6->ip6_vfc = IPV6_VERSION;
/* src and dest ip addresses */
uip_ip6addr_u8(&ip6->ip6_src, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
uip_ds6_set_addr_iid(&ip6->ip6_src,
(uip_lladdr_t *)frame->src_addr);
uip_ip6addr_u8(&ip6->ip6_dst, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
uip_ds6_set_addr_iid(&ip6->ip6_dst,
(uip_lladdr_t *)frame->dest_addr);
*hdrlen = UIP_IPH_LEN;
/* Next header field */
switch (payload[PACKETBUF_HC1_ENCODING] & 0x06) {
case SICSLOWPAN_HC1_NH_ICMP6:
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
*hdroffset = SICSLOWPAN_HC1_HDR_LEN;
break;
case SICSLOWPAN_HC1_NH_TCP:
ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
*hdroffset = SICSLOWPAN_HC1_HDR_LEN;
break;
case SICSLOWPAN_HC1_NH_UDP:
ip6->ip6_nxt = IPPROTO_UDP;
if (payload[PACKETBUF_HC1_HC_UDP_HC1_ENCODING] & 0x01) {
struct udphdr *udp = (struct udphdr *)(uintptr_t)ip6;
/* UDP header is compressed with HC_UDP */
if (payload[PACKETBUF_HC1_HC_UDP_UDP_ENCODING] !=
SICSLOWPAN_HC_UDP_ALL_C) {
printf("sicslowpan (uncompress_hdr), packet not supported");
return EINVAL;
}
/* IP TTL */
ip6->ip6_hlim = payload[PACKETBUF_HC1_HC_UDP_TTL];
/* UDP ports, len, checksum */
udp->uh_sport =
htons(SICSLOWPAN_UDP_PORT_MIN + (payload[PACKETBUF_HC1_HC_UDP_PORTS] >> 4));
udp->uh_dport =
htons(SICSLOWPAN_UDP_PORT_MIN + (payload[PACKETBUF_HC1_HC_UDP_PORTS] & 0x0F));
memcpy(&udp->uh_sum, &payload[PACKETBUF_HC1_HC_UDP_CHKSUM], 2);
*hdrlen += UIP_UDPH_LEN;
*hdroffset = SICSLOWPAN_HC1_HC_UDP_HDR_LEN;
} else {
ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
*hdroffset = SICSLOWPAN_HC1_HDR_LEN;
}
break;
default:
/* this shouldn't happen, drop */
return EINVAL;
}
/* IP length field. */
if (ip_len == 0) {
size_t len = frame->payload_len - *hdroffset + *hdrlen - sizeof(struct ip6_hdr);
/* This is not a fragmented packet */
SET16(&ip6->ip6_plen, len);
} else {
/* This is a 1st fragment */
SET16(&ip6->ip6_plen, ip_len - UIP_IPH_LEN);
}
/* length field in UDP header */
if (ip6->ip6_nxt == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)(uintptr_t)ip6;
memcpy(&udp->uh_ulen, &ip6->ip6_plen, 2);
}
return 0;
}
errno_t
sixxlowpan_compress(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
long hdroffset;
size_t hdrlen;
u_int8_t hdrbuf[128];
errno_t error;
bzero(hdrbuf, sizeof(hdrbuf));
hdrlen = sizeof(hdrbuf);
error = compress_hdr_hc1(ieee02154hdr, payload,
&hdroffset, &hdrlen, hdrbuf);
if (error != 0) {
return error;
}
if (hdroffset < 0) {
/*
* hdroffset negative means that we have to add
* hdrlen of extra stuff
*/
memmove(&payload[hdrlen],
&payload[0],
ieee02154hdr->payload_len);
memcpy(&payload[0], hdrbuf, hdrlen);
ieee02154hdr->payload_len += hdrlen;
} else if (hdroffset > 0) {
/*
* hdroffset is the size of the compressed header
*
* hdrlen is the size of the data that has been compressed
* -- i.e. when the untouched data starts
*/
memmove(&payload[hdrlen],
&payload[hdroffset],
ieee02154hdr->payload_len - hdroffset);
memcpy(&payload[0], hdrbuf, hdrlen);
ieee02154hdr->payload_len += hdrlen - hdroffset;
}
return 0;
}
errno_t
sixxlowpan_uncompress(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
long hdroffset;
size_t hdrlen;
u_int8_t hdrbuf[128];
errno_t error;
bzero(hdrbuf, sizeof(hdrbuf));
hdrlen = sizeof(hdrbuf);
error = uncompress_hdr_hc1(ieee02154hdr, (u_int8_t *)payload,
0, &hdroffset, &hdrlen, hdrbuf);
if (error != 0) {
return error;
}
if (hdroffset < 0) {
/*
* hdroffset negative means that we have to remove
* hdrlen of extra stuff
*/
if (ieee02154hdr->payload_len < hdrlen) {
return EINVAL;
}
memmove(&payload[0],
&payload[hdrlen],
ieee02154hdr->payload_len - hdrlen);
ieee02154hdr->payload_len -= hdrlen;
} else {
/*
* hdroffset is the size of the compressed header
* -- i.e. when the untouched data starts
*
* hdrlen is the size of the decompressed header
* that takes the place of compressed header of size hdroffset
*/
if (ieee02154hdr->payload_len < hdroffset) {
return EINVAL;
}
memmove(payload + hdrlen,
payload + hdroffset,
ieee02154hdr->payload_len - hdroffset);
memcpy(payload, hdrbuf, hdrlen);
ieee02154hdr->payload_len += hdrlen - hdroffset;
}
return 0;
}
errno_t
sixxlowpan_output(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
errno_t error = 0;
error = sixxlowpan_compress(ieee02154hdr, payload);
if (error != 0) {
goto done;
}
/*
* TO DO: fragmentation
*/
done:
return error;
}
errno_t
sixxlowpan_input(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
errno_t error = 0;
error = sixxlowpan_uncompress(ieee02154hdr, payload);
if (error != 0) {
goto done;
}
/*
* TO DO: fragmentation
*/
done:
return error;
}