From 70c3d2903632fa9e78463b782d522475cdbf70ba Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Wed, 10 Jun 2015 04:02:10 +0200 Subject: [PATCH] gnrc_icmpv6_error: initial import --- Makefile.dep | 4 + sys/include/net/gnrc/icmpv6/error.h | 54 +++++++- sys/include/net/icmpv6.h | 2 +- sys/net/gnrc/Makefile | 3 + .../gnrc/network_layer/icmpv6/error/Makefile | 3 + .../icmpv6/error/gnrc_icmpv6_error.c | 122 ++++++++++++++++++ 6 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 sys/net/gnrc/network_layer/icmpv6/error/Makefile create mode 100644 sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c diff --git a/Makefile.dep b/Makefile.dep index 50f6ad402c52..c0cb60f92c19 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -197,6 +197,10 @@ ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE))) USEMODULE += gnrc_icmpv6 endif +ifneq (,$(filter gnrc_icmpv6_error,$(USEMODULE))) + USEMODULE += gnrc_icmpv6 +endif + ifneq (,$(filter gnrc_icmpv6,$(USEMODULE))) USEMODULE += inet_csum USEMODULE += gnrc_ipv6 diff --git a/sys/include/net/gnrc/icmpv6/error.h b/sys/include/net/gnrc/icmpv6/error.h index 7774d9a54f64..798912368c56 100644 --- a/sys/include/net/gnrc/icmpv6/error.h +++ b/sys/include/net/gnrc/icmpv6/error.h @@ -16,16 +16,66 @@ * @brief ICMPv6 error message definitions * * @author Martine Lenders - * - * @todo implement build and handle functions */ #ifndef GNRC_ICMPV6_ERROR_H_ #define GNRC_ICMPV6_ERROR_H_ +#include + +#include "net/icmpv6.h" +#include "net/ipv6/hdr.h" +#include "net/gnrc/pkt.h" + #ifdef __cplusplus extern "C" { #endif +/** + * @brief Builds an ICMPv6 destination unreachable message for sending. + * + * @param[in] code The code for the message @see net/icmpv6.h. + * @param[in] orig_pkt The invoking packet. + * + * @return The destination unreachable message on success. + * @return NULL, on failure. + */ +gnrc_pktsnip_t *gnrc_icmpv6_error_dst_unr_build(uint8_t code, gnrc_pktsnip_t *orig_pkt); + +/** + * @brief Builds an ICMPv6 packet too big message for sending. + * + * @param[in] mtu The maximum transission unit of the next-hop link. + * @param[in] orig_pkt The invoking packet. + * + * @return The packet too big message on success. + * @return NULL, on failure. + */ +gnrc_pktsnip_t *gnrc_icmpv6_error_pkt_too_big_build(uint32_t mtu, gnrc_pktsnip_t *orig_pkt); + +/** + * @brief Builds an ICMPv6 time exceeded message for sending. + * + * @param[in] code The code for the message @see net/icmpv6.h. + * @param[in] orig_pkt The invoking packet. + * + * @return The time exceeded message on success. + * @return NULL, on failure. + */ +gnrc_pktsnip_t *gnrc_icmpv6_error_time_exc_build(uint8_t code, gnrc_pktsnip_t *orig_pkt); + +/** + * @brief Builds an ICMPv6 parameter problem message for sending. + * + * @param[in] code The code for the message @see net/icmpv6.h. + * @param[in] ptr Pointer to the errorneous octet in @p orig_pkt. + * @param[in] orig_pkt The invoking packet. + * + * @return The parameter problem message on success. + * @return NULL, on failure. + */ +gnrc_pktsnip_t *gnrc_icmpv6_error_param_prob_build(uint8_t code, void *ptr, + gnrc_pktsnip_t *orig_pkt); + #ifdef __cplusplus } #endif diff --git a/sys/include/net/icmpv6.h b/sys/include/net/icmpv6.h index d0b6da4317e5..256bf04c4fc2 100644 --- a/sys/include/net/icmpv6.h +++ b/sys/include/net/icmpv6.h @@ -38,7 +38,7 @@ extern "C" { * IANA, ICMPv6 "type" Numbers * */ -#define ICMPV6_DEST_UNR (1) /**< Destination unreachable message */ +#define ICMPV6_DST_UNR (1) /**< Destination unreachable message */ #define ICMPV6_PKT_TOO_BIG (2) /**< Packet Too Big message */ #define ICMPV6_TIME_EXC (3) /**< Time Exceeded message */ #define ICMPV6_PARAM_PROB (4) /**< Parameter Problem message */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 54fe9e810768..76c83ddd2d92 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -13,6 +13,9 @@ endif ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE))) DIRS += network_layer/icmpv6/echo endif +ifneq (,$(filter gnrc_icmpv6_error,$(USEMODULE))) + DIRS += network_layer/icmpv6/error +endif ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) DIRS += network_layer/ipv6 endif diff --git a/sys/net/gnrc/network_layer/icmpv6/error/Makefile b/sys/net/gnrc/network_layer/icmpv6/error/Makefile new file mode 100644 index 000000000000..3ad164ac181f --- /dev/null +++ b/sys/net/gnrc/network_layer/icmpv6/error/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_icmpv6_error + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c new file mode 100644 index 000000000000..80a6773bdd52 --- /dev/null +++ b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 "net/gnrc/pktbuf.h" + +#include "net/ipv6.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/gnrc/icmpv6/error.h" +#include "net/gnrc/icmpv6.h" + +/* all error messages are basically the same size and format */ +#define ICMPV6_ERROR_SZ (sizeof(icmpv6_error_dst_unr_t)) +#define ICMPV6_ERROR_SET_VALUE(data, value) \ + ((icmpv6_error_pkt_too_big_t *)(data))->mtu = byteorder_htonl(value) + +/* TODO: generalize and centralize (see https://github.com/RIOT-OS/RIOT/pull/3184) */ +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +static inline size_t _fit(gnrc_pktsnip_t *pkt) +{ + /* TODO: replace IPV6_MIN_MTU with known path MTU? */ + return MIN((gnrc_pkt_len(pkt) + ICMPV6_ERROR_SZ), IPV6_MIN_MTU); +} + +/* Build a generic error message */ +static gnrc_pktsnip_t *_icmpv6_error_build(uint8_t type, uint8_t code, + gnrc_pktsnip_t *orig_pkt, uint32_t value) +{ + gnrc_pktsnip_t *pkt = gnrc_icmpv6_build(NULL, type, code, _fit(orig_pkt)); + + /* copy as much of the originating packet into error message as fits the message's size */ + if (pkt != NULL) { + size_t offset = ICMPV6_ERROR_SZ; + uint8_t *data = pkt->data; + ICMPV6_ERROR_SET_VALUE(data, value); + while ((orig_pkt != NULL) && (offset < pkt->size)) { + memcpy(data + offset, orig_pkt->data, + MIN(pkt->size - offset, orig_pkt->size)); + offset += MIN(pkt->size - offset, orig_pkt->size); + orig_pkt = orig_pkt->next; + } + } + + return pkt; +} + +gnrc_pktsnip_t *gnrc_icmpv6_error_dst_unr_build(uint8_t code, gnrc_pktsnip_t *orig_pkt) +{ + return _icmpv6_error_build(ICMPV6_DST_UNR, code, orig_pkt, 0); +} + +gnrc_pktsnip_t *gnrc_icmpv6_error_pkt_too_big_build(uint32_t mtu, gnrc_pktsnip_t *orig_pkt) +{ + return _icmpv6_error_build(ICMPV6_PKT_TOO_BIG, 0, orig_pkt, mtu); +} + +gnrc_pktsnip_t *gnrc_icmpv6_error_time_exc_build(uint8_t code, gnrc_pktsnip_t *orig_pkt) +{ + return _icmpv6_error_build(ICMPV6_TIME_EXC, code, orig_pkt, 0); +} + +static inline bool _in_range(uint8_t *ptr, uint8_t *start, size_t sz) +{ + return (ptr >= start) && (ptr < (start + sz)); +} + +gnrc_pktsnip_t *gnrc_icmpv6_error_param_prob_build(uint8_t code, void *ptr, + gnrc_pktsnip_t *orig_pkt) +{ + gnrc_pktsnip_t *pkt = gnrc_icmpv6_build(NULL, ICMPV6_PARAM_PROB, code, + _fit(orig_pkt)); + + /* copy as much of the originating packet into error message and + * determine relative *ptr* offset */ + if (pkt != NULL) { + size_t offset = sizeof(icmpv6_error_param_prob_t); + uint8_t *data = pkt->data; + uint32_t ptr_offset = 0U; + bool found_offset = false; + + while (orig_pkt != NULL) { + /* copy as long as it fits into packet */ + if (offset < pkt->size) { + memcpy(data + offset, orig_pkt->data, + MIN(pkt->size - offset, orig_pkt->size)); + offset += MIN(pkt->size - offset, orig_pkt->size); + } + + if (_in_range(ptr, orig_pkt->data, orig_pkt->size)) { + ptr_offset += (uint32_t)(((uint8_t *)ptr) - ((uint8_t *)orig_pkt->data)); + found_offset = true; + } + else if (!found_offset) { + ptr_offset += (uint32_t)orig_pkt->size; + } + + orig_pkt = orig_pkt->next; + + if ((offset < pkt->size) && found_offset) { + break; + } + } + + /* set "pointer" field to relative pointer offset */ + ((icmpv6_error_param_prob_t *)data)->ptr = byteorder_htonl(ptr_offset); + } + + return pkt; +} + +/** @} */