diff --git a/sys/include/net/gnrc/ipv6/ext.h b/sys/include/net/gnrc/ipv6/ext.h index a52c88046f..668c9b720e 100644 --- a/sys/include/net/gnrc/ipv6/ext.h +++ b/sys/include/net/gnrc/ipv6/ext.h @@ -66,6 +66,33 @@ gnrc_pktsnip_t *gnrc_ipv6_ext_demux(gnrc_pktsnip_t *pkt, unsigned nh); gnrc_pktsnip_t *gnrc_ipv6_ext_build(gnrc_pktsnip_t *ipv6, gnrc_pktsnip_t *next, uint8_t nh, size_t size); +#if defined(MODULE_GNRC_IPV6_EXT) || defined(DOXYGEN) +/** + * @brief Processes a packet's extension headers. + * + * @param[in] pkt An IPv6 packet in receive order. + * @param[in,out] nh **In:** The @ref net_protnum of gnrc_pktsnip_t::data of + * @p pkt (i.e. the first extension header to be + * processed).
+ * **Out:** The @ref net_protnum of header in + * gnrc_pktsnip_t::data of @p pkt. The extension headers + * are now marked, so their data can be found in + * gnrc_pktsnip_t::next of @p pkt and the following.
+ * If the return value is NULL, the value of @p nh is + * undefined. + * + * @return @p pkt with all extension headers marked until the first + * non-extension header. + * @return NULL, if packet was consumed by the extension header handling. + * @return NULL, on error. @p pkt is released with EINVAL in that case. + */ +gnrc_pktsnip_t *gnrc_ipv6_ext_process_all(gnrc_pktsnip_t *pkt, + uint8_t *nh); +#else /* defined(MODULE_GNRC_IPV6_EXT) || defined(DOXYGEN) */ +/* NOPs to make the usage code more readable */ +#define gnrc_ipv6_ext_process_all(pkt, first_nh) (pkt); +#endif /* defined(MODULE_GNRC_IPV6_EXT) || defined(DOXYGEN) */ + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c b/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c index 0498f931c9..70cdf8985b 100644 --- a/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c +++ b/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c @@ -28,6 +28,38 @@ #define ENABLE_DEBUG (0) #include "debug.h" +gnrc_pktsnip_t *gnrc_ipv6_ext_process_all(gnrc_pktsnip_t *pkt, + uint8_t *nh) +{ + bool is_ext = true; + while (is_ext) { + switch (*nh) { + case PROTNUM_IPV6_EXT_DST: + case PROTNUM_IPV6_EXT_RH: + case PROTNUM_IPV6_EXT_FRAG: + case PROTNUM_IPV6_EXT_AH: + case PROTNUM_IPV6_EXT_ESP: + case PROTNUM_IPV6_EXT_MOB: { + ipv6_ext_t *ext_hdr; + + DEBUG("ipv6: handle extension header (nh = %u)\n", *nh); + ext_hdr = pkt->data; + if ((pkt = gnrc_ipv6_ext_demux(pkt, *nh)) == NULL) { + DEBUG("ipv6: packet was consumed by extension header " + "handling\n"); + return NULL; + } + *nh = ext_hdr->nh; + break; + } + default: + is_ext = false; + break; + } + } + return pkt; +} + #ifdef MODULE_GNRC_IPV6_EXT_RH /* unchecked precondition: hdr is gnrc_pktsnip_t::data of the * GNRC_NETTYPE_IPV6 snip within pkt */ diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index cd697f8df0..dc320f94cc 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -93,48 +93,18 @@ kernel_pid_t gnrc_ipv6_init(void) static void _dispatch_next_header(gnrc_pktsnip_t *pkt, unsigned nh, bool interested); +static inline bool _gnrc_ipv6_is_interested(unsigned nh) { +#ifdef MODULE_GNRC_ICMPV6 + return (nh == PROTNUM_ICMPV6); +#else /* MODULE_GNRC_ICMPV6 */ + return false; +#endif /* MODULE_GNRC_ICMPV6 */ +} + static void _demux(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, unsigned nh) { - bool interested; - -#ifdef MODULE_GNRC_IPV6_EXT - bool is_ext = true; - - while (is_ext) { - switch (nh) { - case PROTNUM_IPV6_EXT_HOPOPT: - case PROTNUM_IPV6_EXT_DST: - case PROTNUM_IPV6_EXT_RH: - case PROTNUM_IPV6_EXT_FRAG: - case PROTNUM_IPV6_EXT_AH: - case PROTNUM_IPV6_EXT_ESP: - case PROTNUM_IPV6_EXT_MOB: { - ipv6_ext_t *ext_hdr; - - DEBUG("ipv6: handle extension header (nh = %u)\n", nh); - ext_hdr = pkt->data; - if ((pkt = gnrc_ipv6_ext_demux(pkt, nh)) == NULL) { - DEBUG("ipv6: packet was consumed by extension header " - "handling\n"); - return; - } - nh = ext_hdr->nh; - break; - } - default: - is_ext = false; - break; - } - } -#endif /* MODULE_GNRC_IPV6_EXT */ - -#ifdef MODULE_GNRC_ICMPV6 - interested = (nh == PROTNUM_ICMPV6); -#else /* MODULE_GNRC_ICMPV6 */ - interested = false; -#endif /* MODULE_GNRC_ICMPV6 */ pkt->type = gnrc_nettype_from_protnum(nh); - _dispatch_next_header(pkt, nh, interested); + _dispatch_next_header(pkt, nh, _gnrc_ipv6_is_interested(nh)); switch (nh) { #ifdef MODULE_GNRC_ICMPV6 case PROTNUM_ICMPV6: @@ -661,6 +631,7 @@ static void _receive(gnrc_pktsnip_t *pkt) gnrc_netif_t *netif = NULL; gnrc_pktsnip_t *ipv6, *netif_hdr; ipv6_hdr_t *hdr; + uint8_t first_nh; assert(pkt != NULL); @@ -732,8 +703,9 @@ static void _receive(gnrc_pktsnip_t *pkt) } uint16_t ipv6_len = byteorder_ntohs(hdr->len); + first_nh = hdr->nh; - if ((ipv6_len == 0) && (hdr->nh != PROTNUM_IPV6_NONXT)) { + if ((ipv6_len == 0) && (first_nh != PROTNUM_IPV6_NONXT)) { /* this doesn't even make sense */ DEBUG("ipv6: payload length 0, but next header not NONXT\n"); gnrc_pktbuf_release(pkt); @@ -758,7 +730,7 @@ static void _receive(gnrc_pktsnip_t *pkt) ipv6_addr_to_str(addr_str, &(hdr->src), sizeof(addr_str))); DEBUG("dst = %s, next header = %u, length = %" PRIu16 ")\n", ipv6_addr_to_str(addr_str, &(hdr->dst), sizeof(addr_str)), - hdr->nh, byteorder_ntohs(hdr->len)); + first_nh, byteorder_ntohs(hdr->len)); if (_pkt_not_for_me(&netif, hdr)) { /* if packet is not for me */ DEBUG("ipv6: packet destination not this host\n"); @@ -827,8 +799,11 @@ static void _receive(gnrc_pktsnip_t *pkt) return; #endif /* MODULE_GNRC_IPV6_ROUTER */ } - - _demux(netif, pkt, hdr->nh); + if ((pkt = gnrc_ipv6_ext_process_all(pkt, &first_nh)) == NULL) { + DEBUG("ipv6: packet was consumed in extension header handling\n"); + return; + } + _demux(netif, pkt, first_nh); } /** @} */