From 9a989264d8faf68840380ad7ac0ed5510fb593e0 Mon Sep 17 00:00:00 2001 From: Martine Lenders <mail@martine-lenders.eu> Date: Mon, 13 Apr 2015 12:48:53 +0200 Subject: [PATCH] ng_sixlowpan: initial import of IP header compression --- Makefile.dep | 5 + sys/Makefile | 3 + sys/include/net/ng_sixlowpan.h | 1 + sys/include/net/ng_sixlowpan/iphc.h | 171 +++++ sys/include/net/ng_sixlowpan/netif.h | 5 + .../network_layer/ng_sixlowpan/iphc/Makefile | 3 + .../ng_sixlowpan/iphc/ng_sixlowpan_iphc.c | 697 ++++++++++++++++++ .../ng_sixlowpan/netif/ng_sixlowpan_netif.c | 3 + .../network_layer/ng_sixlowpan/ng_sixlowpan.c | 77 +- 9 files changed, 948 insertions(+), 17 deletions(-) create mode 100644 sys/include/net/ng_sixlowpan/iphc.h create mode 100644 sys/net/network_layer/ng_sixlowpan/iphc/Makefile create mode 100644 sys/net/network_layer/ng_sixlowpan/iphc/ng_sixlowpan_iphc.c diff --git a/Makefile.dep b/Makefile.dep index 143d9c6caf..1ff604b745 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -95,6 +95,11 @@ ifneq (,$(filter ng_sixlowpan_frag,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter ng_sixlowpan_iphc,$(USEMODULE))) + USEMODULE += ng_sixlowpan + USEMODULE += ng_sixlowpan_ctx +endif + ifneq (,$(filter ng_sixlowpan,$(USEMODULE))) USEMODULE += ng_ipv6 USEMODULE += ng_sixlowpan_netif diff --git a/sys/Makefile b/sys/Makefile index 71fdea57ae..41788ecee7 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -116,6 +116,9 @@ endif ifneq (,$(filter ng_sixlowpan_frag,$(USEMODULE))) DIRS += net/network_layer/ng_sixlowpan/frag endif +ifneq (,$(filter ng_sixlowpan_iphc,$(USEMODULE))) + DIRS += net/network_layer/ng_sixlowpan/iphc +endif ifneq (,$(filter ng_sixlowpan_netif,$(USEMODULE))) DIRS += net/network_layer/ng_sixlowpan/netif endif diff --git a/sys/include/net/ng_sixlowpan.h b/sys/include/net/ng_sixlowpan.h index d05524eaf4..b58bf1b8ab 100644 --- a/sys/include/net/ng_sixlowpan.h +++ b/sys/include/net/ng_sixlowpan.h @@ -25,6 +25,7 @@ #include "kernel_types.h" #include "net/ng_sixlowpan/frag.h" +#include "net/ng_sixlowpan/iphc.h" #ifdef __cplusplus extern "C" { diff --git a/sys/include/net/ng_sixlowpan/iphc.h b/sys/include/net/ng_sixlowpan/iphc.h new file mode 100644 index 0000000000..9f5f53309c --- /dev/null +++ b/sys/include/net/ng_sixlowpan/iphc.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_ng_sixlowpan_iphc IPv6 header compression (IPHC) + * @ingroup net_ng_sixlowpan + * @brief IPv6 header compression for 6LoWPAN. + * @{ + * + * @file + * @brief 6LoWPAN IPHC definitions + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef NG_SIXLOWPAN_IPHC_H_ +#define NG_SIXLOWPAN_IPHC_H_ + +#include <stdbool.h> + +#include "net/ng_pkt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Dispatch mask for LOWPAN_IPHC. + */ +#define NG_SIXLOWPAN_IPHC1_DISP_MASK (0xe0) + +/** + * @brief Dispatch for LOWPAN_IPHC. + */ +#define NG_SIXLOWPAN_IPHC1_DISP (0x60) + +/** + * @brief Flag for Traffic Class & Flow Label elision (part of first byte of + * LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC1_TF (0x18) + +/** + * @brief Flag for Next Header Compression (part of first byte of + * LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC1_NH (0x04) + +/** + * @brief Flag for Hop Limit elision (part of first byte of LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC1_HL (0x03) + +/** + * @brief Flag for Context Identifier Extention (part of second byte + * of LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_CID_EXT (0x80) + +/** + * @brief Flag for Source Address Compression (part of second byte + * of LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_SAC (0x40) + +/** + * @brief Bits for Source Address Mode (part of second byte of + * LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_SAM (0x30) + +/** + * @brief Flag for Destination Address Compression (part of second + * byte of LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_DAC (0x04) + +/** + * @brief Bits for Destination Address Mode (part of second byte of + * LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_DAM (0x03) + +/** + * @brief Flag for Multicast Compression (part of second byte of + * LOWPAN_IPHC). + * @see <a href="http://tools.ietf.org/html/rfc6282#section-3.1.1"> + * RFC 6282, section 3.1.1 + * </a> + */ +#define NG_SIXLOWPAN_IPHC2_M (0x08) + +/** + * @brief 6LoWPAN IPHC header length + */ +#define NG_SIXLOWPAN_IPHC_HDR_LEN (2) + +/** + * @brief 6LoWPAN context idendifier extension header length + */ +#define NG_SIXLOWPAN_IPHC_CID_EXT_LEN (1) + +/** + * @brief Checks if datagram is an IPHC datagram. + * + * @param[in] data Data of a datagram, may not be NULL. + * + * @return true, if datagram is an IPHC datagram. + * @return false, if datagram is not an IPHC datagram. + */ +static inline bool ng_sixlowpan_iphc_is(uint8_t *data) +{ + return ((*data & NG_SIXLOWPAN_IPHC1_DISP_MASK) == NG_SIXLOWPAN_IPHC1_DISP); +} + +/** + * @brief Decompresses a received 6LoWPAN IPHC frame. + * + * @param[in,out] pkt A received 6LoWPAN IPHC frame. Will be translated + * into an IPv6 packet. + * + * @return true, on success + * @return false, on error. + */ +bool ng_sixlowpan_iphc_decode(ng_pktsnip_t *pkt); + +/** + * @brief Compresses a 6LoWPAN for IPHC. + * + * @param[in,out] pkt A 6LoWPAN frame with an uncompressed IPv6 header to + * send. Will be translated to an 6LoWPAN IPHC frame. + * + * @return true, on success + * @return false, on error. + */ +bool ng_sixlowpan_iphc_encode(ng_pktsnip_t *pkt); + +#ifdef __cplusplus +} +#endif + +#endif /* NG_SIXLOWPAN_IPHC_H_ */ +/** @} */ diff --git a/sys/include/net/ng_sixlowpan/netif.h b/sys/include/net/ng_sixlowpan/netif.h index 11d845eb43..ce08ff9409 100644 --- a/sys/include/net/ng_sixlowpan/netif.h +++ b/sys/include/net/ng_sixlowpan/netif.h @@ -20,6 +20,8 @@ #ifndef NG_SIXLOWPAN_NETIF_H_ #define NG_SIXLOWPAN_NETIF_H_ +#include <stdbool.h> + #include "kernel_types.h" #ifdef __cplusplus @@ -32,6 +34,9 @@ extern "C" { typedef struct { kernel_pid_t pid; /**< PID of the interface */ uint16_t max_frag_size; /**< Maximum fragment size for this interface */ +#ifdef MODULE_NG_SIXLOWPAN_IPHC + bool iphc_enabled; /**< enable or disable IPHC */ +#endif } ng_sixlowpan_netif_t; /** diff --git a/sys/net/network_layer/ng_sixlowpan/iphc/Makefile b/sys/net/network_layer/ng_sixlowpan/iphc/Makefile new file mode 100644 index 0000000000..766032ebe7 --- /dev/null +++ b/sys/net/network_layer/ng_sixlowpan/iphc/Makefile @@ -0,0 +1,3 @@ +MODULE = ng_sixlowpan_iphc + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_sixlowpan/iphc/ng_sixlowpan_iphc.c b/sys/net/network_layer/ng_sixlowpan/iphc/ng_sixlowpan_iphc.c new file mode 100644 index 0000000000..babdfeb593 --- /dev/null +++ b/sys/net/network_layer/ng_sixlowpan/iphc/ng_sixlowpan_iphc.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ + +#include <stdbool.h> + +#include "byteorder.h" +#include "net/ng_ipv6/hdr.h" +#include "net/ng_netbase.h" +#include "net/ng_sixlowpan/ctx.h" +#include "utlist.h" + +#include "net/ng_sixlowpan/iphc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* dispatch byte definitions */ +#define IPHC1_IDX (0U) +#define IPHC2_IDX (1U) +#define CID_EXT_IDX (2U) + +/* compression values for traffic class and flow label */ +#define IPHC_TF_ECN_DSCP_FL (0x00) +#define IPHC_TF_ECN_FL (0x08) +#define IPHC_TF_ECN_DSCP (0x10) +#define IPHC_TF_ECN_ELIDE (0x18) + +/* compression values for hop limit */ +#define IPHC_HL_INLINE (0x00) +#define IPHC_HL_1 (0x01) +#define IPHC_HL_64 (0x02) +#define IPHC_HL_255 (0x03) + +/* compression values for source address */ +#define IPHC_SAC_SAM_FULL (0x00) +#define IPHC_SAC_SAM_64 (0x10) +#define IPHC_SAC_SAM_16 (0x20) +#define IPHC_SAC_SAM_L2 (0x30) +#define IPHC_SAC_SAM_UNSPEC (0x40) +#define IPHC_SAC_SAM_CTX_64 (0x50) +#define IPHC_SAC_SAM_CTX_16 (0x60) +#define IPHC_SAC_SAM_CTX_L2 (0x70) + +/* compression values for destination address */ +#define IPHC_M_DAC_DAM_U_FULL (0x00) +#define IPHC_M_DAC_DAM_U_64 (0x01) +#define IPHC_M_DAC_DAM_U_16 (0x02) +#define IPHC_M_DAC_DAM_U_L2 (0x03) +#define IPHC_M_DAC_DAM_U_UNSPEC (0x04) +#define IPHC_M_DAC_DAM_U_CTX_64 (0x05) +#define IPHC_M_DAC_DAM_U_CTX_16 (0x06) +#define IPHC_M_DAC_DAM_U_CTX_L2 (0x07) +#define IPHC_M_DAC_DAM_M_FULL (0x08) +#define IPHC_M_DAC_DAM_M_48 (0x09) +#define IPHC_M_DAC_DAM_M_32 (0x0a) +#define IPHC_M_DAC_DAM_M_8 (0x0b) +#define IPHC_M_DAC_DAM_M_UC_PREFIX (0x0c) + +static network_uint64_t _init_iid(uint8_t *l2addr, size_t l2addr_len) +{ + network_uint64_t res = { 0 }; + + if (l2addr_len <= 4) { + res = byteorder_htonll(0x000000fffe000000); + + if (l2addr_len == 1) { + res.u8[7] = l2addr[0]; + } + else if (l2addr_len == 2) { + res.u8[6] = l2addr[0]; + res.u8[7] = l2addr[1]; + } + else if (l2addr_len == 4) { + res.u8[0] = l2addr[0]; + res.u8[1] = l2addr[1]; + res.u8[6] = l2addr[2]; + res.u8[7] = l2addr[3]; + } + } + else if (l2addr_len == 8) { + network_uint64_t *l2addr_u64 = (network_uint64_t *)l2addr; + res = *l2addr_u64; + res.u8[0] ^= 0x02; /* swap local/universal bit */ + } + + return res; +} + +static inline bool _context_overlaps_iid(ng_sixlowpan_ctx_t *ctx, + ng_ipv6_addr_t *addr, + network_uint64_t *iid) +{ + uint8_t byte_mask[] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + + if (ctx == NULL) { + return false; + } + + return ((ctx->prefix_len == 128) || /* Full-length prefix overlaps IID in any case */ + ((ctx->prefix_len > 64) && /* otherwise, if bigger than 64-bit */ + /* compare bytes until prefix length with IID */ + (memcmp(&(addr->u8[(ctx->prefix_len / 8) + 1]), + &(iid->u8[(ctx->prefix_len / 8) - 7]), + sizeof(network_uint64_t) - ((ctx->prefix_len / 8) - 7)) == 0) && + /* compare bits at prefix length with IID */ + (addr->u8[(ctx->prefix_len / 8)] & byte_mask[ctx->prefix_len % 8]) == + (iid->u8[(ctx->prefix_len / 8) - 8] & byte_mask[ctx->prefix_len % 8]))); +} + +bool ng_sixlowpan_iphc_decode(ng_pktsnip_t *pkt) +{ + ng_netif_hdr_t *netif_hdr = pkt->next->data; + ng_ipv6_hdr_t *ipv6_hdr; + uint8_t *iphc_hdr = pkt->data; + uint16_t payload_offset = NG_SIXLOWPAN_IPHC_HDR_LEN; + ng_sixlowpan_ctx_t *ctx = NULL; + ng_pktsnip_t *payload; + ng_pktsnip_t *ipv6 = ng_pktbuf_add(NULL, NULL, sizeof(ng_ipv6_hdr_t), + NG_NETTYPE_IPV6); + + if (ipv6 == NULL) { + DEBUG("6lo iphc: error allocating ipv6 header space\n"); + return false; + } + + ipv6_hdr = ipv6->data; + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_CID_EXT) { + payload_offset++; + } + + ng_ipv6_hdr_set_version(ipv6_hdr); + + switch (iphc_hdr[IPHC1_IDX] & NG_SIXLOWPAN_IPHC1_TF) { + case IPHC_TF_ECN_DSCP_FL: + ng_ipv6_hdr_set_tc(ipv6_hdr, iphc_hdr[payload_offset++]); + ipv6_hdr->v_tc_fl.u8[1] |= iphc_hdr[payload_offset++] & 0x0f; + ipv6_hdr->v_tc_fl.u8[2] |= iphc_hdr[payload_offset++]; + ipv6_hdr->v_tc_fl.u8[3] |= iphc_hdr[payload_offset++]; + break; + + case IPHC_TF_ECN_FL: + ng_ipv6_hdr_set_tc_ecn(ipv6_hdr, iphc_hdr[payload_offset] >> 6); + ng_ipv6_hdr_set_tc_dscp(ipv6_hdr, 0); + ipv6_hdr->v_tc_fl.u8[1] |= iphc_hdr[payload_offset++] & 0x0f; + ipv6_hdr->v_tc_fl.u8[2] |= iphc_hdr[payload_offset++]; + ipv6_hdr->v_tc_fl.u8[3] |= iphc_hdr[payload_offset++]; + break; + + case IPHC_TF_ECN_DSCP: + ng_ipv6_hdr_set_tc(ipv6_hdr, iphc_hdr[payload_offset++]); + ng_ipv6_hdr_set_fl(ipv6_hdr, 0); + break; + + case IPHC_TF_ECN_ELIDE: + ng_ipv6_hdr_set_tc(ipv6_hdr, 0); + ng_ipv6_hdr_set_fl(ipv6_hdr, 0); + break; + } + + if (!(iphc_hdr[IPHC1_IDX] & NG_SIXLOWPAN_IPHC1_NH)) { + ipv6_hdr->nh = iphc_hdr[payload_offset++]; + } + + switch (iphc_hdr[IPHC1_IDX] & NG_SIXLOWPAN_IPHC1_HL) { + case IPHC_HL_INLINE: + ipv6_hdr->hl = iphc_hdr[payload_offset++]; + break; + + case IPHC_HL_1: + ipv6_hdr->hl = 1; + break; + + case IPHC_HL_64: + ipv6_hdr->hl = 64; + break; + + case IPHC_HL_255: + ipv6_hdr->hl = 255; + break; + } + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_SAC) { + uint8_t sci = 0; + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_CID_EXT) { + sci = iphc_hdr[CID_EXT_IDX] >> 4; + } + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_SAM) { + ctx = ng_sixlowpan_ctx_lookup_id(sci); + + if (ctx == NULL) { + DEBUG("6lo iphc: could not find source context\n"); + return false; + } + } + } + + switch (iphc_hdr[IPHC2_IDX] & (NG_SIXLOWPAN_IPHC2_SAC | NG_SIXLOWPAN_IPHC2_SAM)) { + case IPHC_SAC_SAM_FULL: + /* take full 128 from inline */ + memcpy(&(ipv6_hdr->src), iphc_hdr + payload_offset, 16); + payload_offset += 16; + break; + + case IPHC_SAC_SAM_64: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->src); + memcpy(ipv6_hdr->src.u8 + 8, iphc_hdr + payload_offset, 8); + payload_offset += 8; + break; + + case IPHC_SAC_SAM_16: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->src); + ipv6_hdr->src.u32[2] = byteorder_htonl(0x000000ff); + ipv6_hdr->src.u16[6] = byteorder_htons(0xfe00); + memcpy(ipv6_hdr->src.u8 + 14, iphc_hdr + payload_offset, 2); + payload_offset += 2; + break; + + case IPHC_SAC_SAM_L2: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->src); + ipv6_hdr->src.u64[1] = _init_iid(ng_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + break; + + case IPHC_SAC_SAM_UNSPEC: + ng_ipv6_addr_set_unspecified(&ipv6_hdr->src); + break; + + case IPHC_SAC_SAM_CTX_64: + memcpy(ipv6_hdr->src.u8 + 8, iphc_hdr + payload_offset, 8); + ng_ipv6_addr_init_prefix(&ipv6_hdr->src, &ctx->prefix, + ctx->prefix_len); + payload_offset += 8; + break; + + case IPHC_SAC_SAM_CTX_16: + ipv6_hdr->src.u32[2] = byteorder_htonl(0x000000ff); + ipv6_hdr->src.u16[6] = byteorder_htons(0xfe00); + memcpy(ipv6_hdr->src.u8 + 14, iphc_hdr + payload_offset, 2); + ng_ipv6_addr_init_prefix(&ipv6_hdr->src, &ctx->prefix, + ctx->prefix_len); + payload_offset += 2; + break; + + case IPHC_SAC_SAM_CTX_L2: + ipv6_hdr->src.u64[1] = _init_iid(ng_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + ng_ipv6_addr_init_prefix(&ipv6_hdr->src, &ctx->prefix, + ctx->prefix_len); + break; + } + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_DAC) { + uint8_t dci = 0; + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_CID_EXT) { + dci = iphc_hdr[CID_EXT_IDX] & 0x0f; + } + + if (iphc_hdr[IPHC2_IDX] & NG_SIXLOWPAN_IPHC2_DAM) { + ctx = ng_sixlowpan_ctx_lookup_id(dci); + + if (ctx == NULL) { + DEBUG("6lo iphc: could not find destination context\n"); + return false; + } + } + } + + switch (iphc_hdr[IPHC2_IDX] & (NG_SIXLOWPAN_IPHC2_M | NG_SIXLOWPAN_IPHC2_DAC | + NG_SIXLOWPAN_IPHC2_DAM)) { + case IPHC_M_DAC_DAM_U_FULL: + case IPHC_M_DAC_DAM_M_FULL: + memcpy(&(ipv6_hdr->dst.u8), iphc_hdr + payload_offset, 16); + payload_offset += 16; + break; + + case IPHC_M_DAC_DAM_U_64: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->dst); + memcpy(ipv6_hdr->dst.u8 + 8, iphc_hdr + payload_offset, 8); + payload_offset += 8; + break; + + case IPHC_M_DAC_DAM_U_16: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->dst); + ipv6_hdr->dst.u32[2] = byteorder_htonl(0x000000ff); + ipv6_hdr->dst.u16[6] = byteorder_htons(0xfe00); + memcpy(ipv6_hdr->dst.u8 + 14, iphc_hdr + payload_offset, 2); + payload_offset += 2; + break; + + case IPHC_M_DAC_DAM_U_L2: + ng_ipv6_addr_set_link_local_prefix(&ipv6_hdr->dst); + ipv6_hdr->dst.u64[1] = _init_iid(ng_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + break; + + case IPHC_M_DAC_DAM_U_CTX_64: + memcpy(ipv6_hdr->dst.u8 + 8, iphc_hdr + payload_offset, 8); + ng_ipv6_addr_init_prefix(&ipv6_hdr->dst, &ctx->prefix, + ctx->prefix_len); + payload_offset += 8; + break; + + case IPHC_M_DAC_DAM_U_CTX_16: + ipv6_hdr->dst.u32[2] = byteorder_htonl(0x000000ff); + ipv6_hdr->dst.u16[6] = byteorder_htons(0xfe00); + memcpy(ipv6_hdr->dst.u8 + 14, iphc_hdr + payload_offset, 2); + ng_ipv6_addr_init_prefix(&ipv6_hdr->dst, &ctx->prefix, + ctx->prefix_len); + payload_offset += 2; + break; + + case IPHC_M_DAC_DAM_U_CTX_L2: + ipv6_hdr->dst.u64[1] = _init_iid(ng_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + ng_ipv6_addr_init_prefix(&ipv6_hdr->dst, &ctx->prefix, + ctx->prefix_len); + break; + + case IPHC_M_DAC_DAM_M_48: + /* ffXX::00XX:XXXX:XXXX */ + ng_ipv6_addr_set_unspecified(&ipv6_hdr->dst); + ipv6_hdr->dst.u8[0] = 0xff; + ipv6_hdr->dst.u8[1] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[11] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[12] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[13] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[14] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[15] = iphc_hdr[payload_offset++]; + break; + + case IPHC_M_DAC_DAM_M_32: + /* ffXX::00XX:XXXX */ + ng_ipv6_addr_set_unspecified(&ipv6_hdr->dst); + ipv6_hdr->dst.u8[0] = 0xff; + ipv6_hdr->dst.u8[1] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[13] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[14] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[15] = iphc_hdr[payload_offset++]; + break; + + case IPHC_M_DAC_DAM_M_8: + /* ffXX::XX: */ + ng_ipv6_addr_set_unspecified(&ipv6_hdr->dst); + ipv6_hdr->dst.u8[0] = 0xff; + ipv6_hdr->dst.u8[1] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[15] = iphc_hdr[payload_offset++]; + break; + + case IPHC_M_DAC_DAM_M_UC_PREFIX: + do { + uint8_t orig_ctx_len = ctx->prefix_len; + + ng_ipv6_addr_set_unspecified(&ipv6_hdr->dst); + + if (ctx->prefix_len > 64) { + ctx->prefix_len = 64; + } + + ipv6_hdr->dst.u8[0] = 0xff; + ipv6_hdr->dst.u8[1] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[2] = iphc_hdr[payload_offset++]; + ipv6_hdr->dst.u8[3] = ctx->prefix_len; + ng_ipv6_addr_init_prefix((ng_ipv6_addr_t *)ipv6_hdr->dst.u8 + 4, + &ctx->prefix, ctx->prefix_len); + memcpy(ipv6_hdr->dst.u8 + 12, iphc_hdr + payload_offset + 2, 4); + + payload_offset += 4; + + ctx->prefix_len = orig_ctx_len; + } while (0); /* ANSI-C compatible block creation for orig_ctx_len allocation*/ + break; + + default: + DEBUG("6lo iphc: unspecified or reserved M, DAC, DAM combination\n"); + return false; + + } + + /* TODO: add next header decoding */ + + /* remove 6LoWPAN dispatch */ + payload = ng_pktbuf_add(pkt, pkt->data, payload_offset, NG_NETTYPE_SIXLOWPAN); + pkt = ng_pktbuf_remove_snip(pkt, payload); + + /* insert IPv6 header */ + ipv6->next = pkt->next; + pkt->next = ipv6; + + return true; +} + +bool ng_sixlowpan_iphc_encode(ng_pktsnip_t *pkt) +{ + ng_netif_hdr_t *netif_hdr = pkt->data; + ng_ipv6_hdr_t *ipv6_hdr = pkt->next->data; + uint8_t *iphc_hdr; + uint16_t inline_pos = NG_SIXLOWPAN_IPHC_HDR_LEN; + bool addr_comp = false; + ng_sixlowpan_ctx_t *src_ctx = NULL, *dst_ctx = NULL; + ng_pktsnip_t *dispatch = ng_pktbuf_add(NULL, NULL, pkt->next->size, + NG_NETTYPE_SIXLOWPAN); + + if (dispatch == NULL) { + DEBUG("6lo iphc: error allocating dispatch space\n"); + return false; + } + + iphc_hdr = dispatch->data; + + /* set initial dispatch value*/ + iphc_hdr[IPHC1_IDX] = NG_SIXLOWPAN_IPHC1_DISP; + iphc_hdr[2] = 0; + + /* check for available contexts */ + if (!ng_ipv6_addr_is_unspecified(&(ipv6_hdr->src))) { + src_ctx = ng_sixlowpan_ctx_lookup_addr(&(ipv6_hdr->src)); + } + + if (!ng_ipv6_addr_is_multicast(&ipv6_hdr->dst)) { + dst_ctx = ng_sixlowpan_ctx_lookup_addr(&(ipv6_hdr->dst)); + } + + /* if contexts available and both != 0 */ + /* since this moves inline_pos we have to do this ahead*/ + if (((src_ctx != NULL) && (src_ctx->id != 0)) || + ((dst_ctx != NULL) && (dst_ctx->id != 0))) { + /* add context identifier extension */ + iphc_hdr[IPHC2_IDX] |= NG_SIXLOWPAN_IPHC2_CID_EXT; + iphc_hdr[CID_EXT_IDX] = 0; + + /* move position to behind CID extension */ + inline_pos += NG_SIXLOWPAN_IPHC_CID_EXT_LEN; + } + + /* compress flow label and traffic class */ + if (ng_ipv6_hdr_get_fl(ipv6_hdr) == 0) { + if (ng_ipv6_hdr_get_tc(ipv6_hdr) == 0) { + /* elide both traffic class and flow label */ + iphc_hdr[IPHC1_IDX] |= IPHC_TF_ECN_ELIDE; + } + else { + /* elide flow label, traffic class (ECN + DSCP) inline (1 byte) */ + iphc_hdr[IPHC1_IDX] |= IPHC_TF_ECN_DSCP; + iphc_hdr[inline_pos++] = ng_ipv6_hdr_get_tc(ipv6_hdr); + } + } + else { + if (ng_ipv6_hdr_get_tc_dscp(ipv6_hdr) == 0) { + /* elide DSCP, ECN + 2-bit pad + flow label inline (3 byte) */ + iphc_hdr[IPHC1_IDX] |= IPHC_TF_ECN_FL; + iphc_hdr[inline_pos++] = (uint8_t)((ng_ipv6_hdr_get_tc_ecn(ipv6_hdr) << 6) | + ((ng_ipv6_hdr_get_fl(ipv6_hdr) & 0x000f0000) >> 16)); + } + else { + /* ECN + DSCP + 4-bit pad + flow label (4 bytes) */ + iphc_hdr[IPHC1_IDX] |= IPHC_TF_ECN_DSCP_FL; + iphc_hdr[inline_pos++] = ng_ipv6_hdr_get_tc(ipv6_hdr); + iphc_hdr[inline_pos++] = (uint8_t)((ng_ipv6_hdr_get_fl(ipv6_hdr) & 0x000f0000) >> 16); + } + + /* copy remaining byteos of flow label */ + iphc_hdr[inline_pos++] = (uint8_t)((ng_ipv6_hdr_get_fl(ipv6_hdr) & 0x0000ff00) >> 8); + iphc_hdr[inline_pos++] = (uint8_t)((ng_ipv6_hdr_get_fl(ipv6_hdr) & 0x000000ff) >> 8); + } + + /* compress next header */ + switch (ipv6_hdr->nh) { + /* TODO: add next header compression and set NH bit */ + default: + iphc_hdr[inline_pos++] = ipv6_hdr->nh; + break; + } + + /* compress hop limit */ + switch (ipv6_hdr->hl) { + case 1: + iphc_hdr[IPHC1_IDX] |= IPHC_HL_1; + break; + + case 64: + iphc_hdr[IPHC1_IDX] |= IPHC_HL_64; + break; + + case 255: + iphc_hdr[IPHC1_IDX] |= IPHC_HL_255; + break; + + default: + iphc_hdr[IPHC1_IDX] |= IPHC_HL_INLINE; + iphc_hdr[inline_pos++] = ipv6_hdr->hl; + break; + } + + if (ng_ipv6_addr_is_unspecified(&(ipv6_hdr->src))) { + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_UNSPEC; + } + else { + if (src_ctx != NULL) { + /* stateful source address compression */ + iphc_hdr[IPHC2_IDX] |= NG_SIXLOWPAN_IPHC2_SAC; + + if (src_ctx->id != 0) { /* context id is elided */ + iphc_hdr[CID_EXT_IDX] |= (src_ctx->id << 4); + } + } + + if ((src_ctx != NULL) || ng_ipv6_addr_is_link_local(&(ipv6_hdr->src))) { + uint16_t l2src_len = 0; + uint8_t l2src[8]; + + if (netif_hdr->src_l2addr_len > 0) { + l2src_len = netif_hdr->src_l2addr_len; + memcpy(l2src, ng_netif_hdr_get_src_addr(netif_hdr), l2src_len); + } + else { + bool try_long = false; + + if ((ng_netapi_get(netif_hdr->if_pid, NETCONF_OPT_ADDR_LEN, 0, + &l2src_len, sizeof(l2src_len)) >= 0) && + (l2src_len >= 8)) { + try_long = true; + } + + if ((!try_long) || (ng_netapi_get(netif_hdr->if_pid, + NETCONF_OPT_ADDRESS_LONG, 0, + &l2src, sizeof(l2src)) < 0)) { + if (ng_netapi_get(netif_hdr->if_pid, NETCONF_OPT_ADDRESS, + 0, &l2src, sizeof(l2src)) < 0) { + l2src_len = 0; + } + } + + } + + if (l2src_len > 0) { + network_uint64_t iid = _init_iid(l2src, l2src_len); + + if ((memcmp(ipv6_hdr->src.u64 + 1, &iid, + sizeof(network_uint64_t)) == 0) || + _context_overlaps_iid(src_ctx, &ipv6_hdr->src, &iid)) { + /* 0 bits. The address is derived from link-layer address */ + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_L2; + } + else if ((byteorder_ntohl(ipv6_hdr->src.u32[2]) == 0x000000ff) && + (byteorder_ntohs(ipv6_hdr->src.u16[6]) == 0xfe00)) { + /* 16 bits. The address is derived using 16 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_16; + memcpy(iphc_hdr + inline_pos, ipv6_hdr->src.u16 + 7, 2); + inline_pos += 2; + } + else { + /* 64 bits. The address is derived using 64 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_64; + memcpy(iphc_hdr + inline_pos, ipv6_hdr->src.u64 + 1, 8); + inline_pos += 8; + } + + addr_comp = true; + } + } + + if (!addr_comp) { + /* full address is carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_FULL; + memcpy(iphc_hdr + inline_pos, &ipv6_hdr->src, 16); + inline_pos += 16; + } + } + + addr_comp = false; + + /* M: Multicast compression */ + if (ng_ipv6_addr_is_multicast(&(ipv6_hdr->dst))) { + iphc_hdr[IPHC2_IDX] |= NG_SIXLOWPAN_IPHC2_M; + + /* if multicast address is of format ffXX::XXXX:XXXX:XXXX */ + if ((ipv6_hdr->dst.u16[1].u16 == 0) && + (ipv6_hdr->dst.u32[1].u32 == 0) && + (ipv6_hdr->dst.u16[4].u16 == 0)) { + /* if multicast address is of format ff02::XX */ + if ((ipv6_hdr->dst.u8[1] == 2) && + (ipv6_hdr->dst.u32[2].u32 == 0) && + (ipv6_hdr->dst.u16[6].u16 == 0) && + (ipv6_hdr->dst.u8[14] == 0)) { + /* 8 bits. The address is derived using 8 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_M_8; + iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[15]; + addr_comp = true; + } + /* if multicast address is of format ffXX::XX:XXXX */ + else if ((ipv6_hdr->dst.u16[5].u16 == 0) && + (ipv6_hdr->dst.u8[12] == 0)) { + /* 32 bits. The address is derived using 32 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_M_32; + iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[1]; + memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u8 + 13, 3); + inline_pos += 3; + addr_comp = true; + } + /* if multicast address is of format ffXX::XX:XXXX:XXXX */ + else if (ipv6_hdr->dst.u8[10] == 0) { + /* 32 bits. The address is derived using 32 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_M_32; + iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[1]; + memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u8 + 13, 3); + inline_pos += 3; + addr_comp = true; + } + } + /* try unicast prefix based compression */ + else { + ng_sixlowpan_ctx_t *ctx; + ng_ipv6_addr_t unicast_prefix; + unicast_prefix.u16[0] = ipv6_hdr->dst.u16[2]; + unicast_prefix.u16[1] = ipv6_hdr->dst.u16[3]; + unicast_prefix.u16[2] = ipv6_hdr->dst.u16[4]; + unicast_prefix.u16[3] = ipv6_hdr->dst.u16[5]; + + ctx = ng_sixlowpan_ctx_lookup_addr(&unicast_prefix); + + if ((ctx != NULL) && (ctx->prefix_len == ipv6_hdr->dst.u8[3])) { + /* Unicast prefix based IPv6 multicast address with given + * context for unicast prefix -> context based compression */ + iphc_hdr[IPHC2_IDX] |= NG_SIXLOWPAN_IPHC2_DAC; + iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[1]; + iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[2]; + memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u16 + 6, 4); + inline_pos += 4; + addr_comp = true; + } + } + } + else if (((dst_ctx != NULL) || ng_ipv6_addr_is_link_local(&ipv6_hdr->dst)) && + (netif_hdr->dst_l2addr_len > 0)) { + network_uint64_t iid = _init_iid(ng_netif_hdr_get_dst_addr(netif_hdr), + netif_hdr->dst_l2addr_len); + + if ((memcmp(&(ipv6_hdr->dst.u8[8]), &iid, sizeof(uint64_t)) == 0) || + _context_overlaps_iid(dst_ctx, &(ipv6_hdr->dst), &iid)) { + /* 0 bits. The address is derived using the link-layer address */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_U_L2; + addr_comp = true; + } + else if ((byteorder_ntohl(ipv6_hdr->dst.u32[2]) == 0x000000ff) && + (byteorder_ntohs(ipv6_hdr->dst.u16[6]) == 0xfe00)) { + /* 16 bits. The address is derived using 16 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_U_16; + memcpy(&(iphc_hdr[inline_pos]), &(ipv6_hdr->dst.u16[7]), 2); + inline_pos += 2; + addr_comp = true; + } + else { + /* 64 bits. The address is derived using 64 bits carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_U_64; + memcpy(&(iphc_hdr[inline_pos]), &(ipv6_hdr->dst.u8[8]), 8); + inline_pos += 8; + addr_comp = true; + } + } + + if (!addr_comp) { + /* full destination address is carried inline */ + iphc_hdr[IPHC2_IDX] |= IPHC_SAC_SAM_FULL; + memcpy(iphc_hdr + inline_pos, &ipv6_hdr->dst, 16); + inline_pos += 16; + } + + /* shrink dispatch allocation to final size */ + /* NOTE: Since this only shrinks the data nothing bad SHOULD happen ;-) */ + ng_pktbuf_realloc_data(dispatch, (size_t)inline_pos); + + /* remove IPv6 header */ + pkt = ng_pktbuf_remove_snip(pkt, pkt->next); + + /* insert dispatch into packet */ + dispatch->next = pkt->next; + pkt->next = dispatch; + + return true; +} + +/** @} */ diff --git a/sys/net/network_layer/ng_sixlowpan/netif/ng_sixlowpan_netif.c b/sys/net/network_layer/ng_sixlowpan/netif/ng_sixlowpan_netif.c index 03949a3cca..1fcbed08e7 100644 --- a/sys/net/network_layer/ng_sixlowpan/netif/ng_sixlowpan_netif.c +++ b/sys/net/network_layer/ng_sixlowpan/netif/ng_sixlowpan_netif.c @@ -49,6 +49,9 @@ void ng_sixlowpan_netif_add(kernel_pid_t pid, uint16_t max_frag_size) free_entry->pid = pid; free_entry->max_frag_size = max_frag_size; +#ifdef MODULE_NG_SIXLOWPAN_IPHC + free_entry->iphc_enabled = true; +#endif return; } diff --git a/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c b/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c index f008e8f744..af0a9a76fd 100644 --- a/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c +++ b/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c @@ -19,6 +19,7 @@ #include "net/ng_sixlowpan.h" #include "net/ng_sixlowpan/frag.h" +#include "net/ng_sixlowpan/iphc.h" #include "net/ng_sixlowpan/netif.h" #define ENABLE_DEBUG (0) @@ -101,6 +102,18 @@ void _receive(ng_pktsnip_t *pkt) return; } +#ifdef MODULE_NG_SIXLOWPAN_IPHC + if (ng_sixlowpan_iphc_is(payload->data)) { + if (!ng_sixlowpan_iphc_decode(pkt)) { + DEBUG("6lo: error on IPHC decoding\n"); + ng_pktbuf_release(pkt); + return; + } + LL_SEARCH_SCALAR(pkt, payload, type, NG_NETTYPE_IPV6); + + } +#endif + payload->type = NG_NETTYPE_IPV6; entry = ng_netreg_lookup(NG_NETTYPE_IPV6, NG_NETREG_DEMUX_CTX_ALL); @@ -151,6 +164,8 @@ void _send(ng_pktsnip_t *pkt) * length is the length of the IPv6 datagram + 6LoWPAN dispatches, * while the datagram size is the size of only the IPv6 datagram */ payload_len = ng_pkt_len(ipv6); + /* cppcheck: datagram_size will be read by ng_sixlowpan_frag implementation */ + /* cppcheck-suppress unreadVariable */ datagram_size = (uint16_t)payload_len; /* use sixlowpan packet snip as temporary one */ @@ -163,23 +178,6 @@ void _send(ng_pktsnip_t *pkt) } pkt = sixlowpan; - - DEBUG("6lo: Send uncompressed\n"); - - sixlowpan = ng_pktbuf_add(NULL, NULL, sizeof(uint8_t), NG_NETTYPE_SIXLOWPAN); - - if (sixlowpan == NULL) { - DEBUG("6lo: no space left in packet buffer\n"); - ng_pktbuf_release(pkt); - return; - } - - sixlowpan->next = ipv6; - pkt->next = sixlowpan; - disp = sixlowpan->data; - disp[0] = NG_SIXLOWPAN_UNCOMPRESSED; - payload_len++; - iface = ng_sixlowpan_netif_get(hdr->if_pid); if (iface == NULL) { @@ -192,11 +190,56 @@ void _send(ng_pktsnip_t *pkt) } ng_sixlowpan_netif_add(hdr->if_pid, max_frag_size); + iface = ng_sixlowpan_netif_get(hdr->if_pid); } else { max_frag_size = iface->max_frag_size; } +#ifdef MODULE_NG_SIXLOWPAN_IPHC + if (iface->iphc_enabled) { + if (!ng_sixlowpan_iphc_encode(pkt)) { + DEBUG("6lo: error on IPHC encoding\n"); + ng_pktbuf_release(pkt); + return; + } + } + else { + DEBUG("6lo: Send uncompressed\n"); + + sixlowpan = ng_pktbuf_add(NULL, NULL, sizeof(uint8_t), + NG_NETTYPE_SIXLOWPAN); + + if (sixlowpan == NULL) { + DEBUG("6lo: no space left in packet buffer\n"); + ng_pktbuf_release(pkt); + return; + } + + sixlowpan->next = ipv6; + pkt->next = sixlowpan; + disp = sixlowpan->data; + disp[0] = NG_SIXLOWPAN_UNCOMPRESSED; + payload_len++; + } +#else + DEBUG("6lo: Send uncompressed\n"); + + sixlowpan = ng_pktbuf_add(NULL, NULL, sizeof(uint8_t), NG_NETTYPE_SIXLOWPAN); + + if (sixlowpan == NULL) { + DEBUG("6lo: no space left in packet buffer\n"); + ng_pktbuf_release(pkt); + return; + } + + sixlowpan->next = ipv6; + pkt->next = sixlowpan; + disp = sixlowpan->data; + disp[0] = NG_SIXLOWPAN_UNCOMPRESSED; + payload_len++; +#endif + DEBUG("6lo: max_frag_size = %" PRIu16 " for interface %" PRIkernel_pid "\n", max_frag_size, hdr->if_pid); -- GitLab