diff --git a/sys/include/net/gnrc/ipv6/ext.h b/sys/include/net/gnrc/ipv6/ext.h index 6f38fc23c2..985eded734 100644 --- a/sys/include/net/gnrc/ipv6/ext.h +++ b/sys/include/net/gnrc/ipv6/ext.h @@ -46,8 +46,8 @@ extern "C" { * @param[in] pkt A packet. * @param[in] nh A protocol number (see @ref net_protnum). * - * @return true, on success. - * @return false, on failure. + * @return true, on success - continue packet processing. + * @return false, on failure - stop packet processing. */ bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh); diff --git a/sys/include/net/gnrc/rpl/srh.h b/sys/include/net/gnrc/rpl/srh.h index 892685782e..6a772922e6 100644 --- a/sys/include/net/gnrc/rpl/srh.h +++ b/sys/include/net/gnrc/rpl/srh.h @@ -23,6 +23,7 @@ #ifndef GNRC_RPL_SRH_H_ #define GNRC_RPL_SRH_H_ +#include "net/ipv6/hdr.h" #include "net/ipv6/addr.h" #ifdef __cplusplus @@ -48,17 +49,22 @@ typedef struct __attribute__((packed)) { uint8_t len; /**< length in 8 octets without first octet */ uint8_t type; /**< identifier of a particular routing header type */ uint8_t seg_left; /**< number of route segments remaining */ + uint8_t compr; /**< number of prefix octets (comprI and comprE) */ + uint8_t pad_resv; /**< padding and reserved */ + uint16_t resv; /**< reserved */ } gnrc_rpl_srh_t; /** - * @brief Extract next hop from the RPL source routing header. + * @brief Process the RPL source routing header. * + * @param[in,out] ipv6 The IPv6 header of the incoming packet. * @param[in] rh A RPL source routing header. * - * @return next hop, on success - * @return NULL, if not found. + * @return EXT_RH_CODE_ERROR + * @return EXT_RH_CODE_FORWARD + * @return EXT_RH_CODE_OK */ -ipv6_addr_t *gnrc_rpl_srh_next_hop(gnrc_rpl_srh_t *rh); +int gnrc_rpl_srh_process(ipv6_hdr_t *ipv6, gnrc_rpl_srh_t *rh); #ifdef __cplusplus } diff --git a/sys/include/net/ipv6/ext/rh.h b/sys/include/net/ipv6/ext/rh.h index a752e2134c..b26b97cc4f 100644 --- a/sys/include/net/ipv6/ext/rh.h +++ b/sys/include/net/ipv6/ext/rh.h @@ -23,12 +23,24 @@ #include #include "net/ipv6/addr.h" +#include "net/ipv6/ext.h" #include "net/ipv6/hdr.h" #ifdef __cplusplus extern "C" { #endif +/** + * @name Return codes for routing header processing + * @{ + */ +#define EXT_RH_CODE_ERROR (-1) +#define EXT_RH_CODE_FORWARD (0) +#define EXT_RH_CODE_OK (1) +/** + * @} + */ + /** * @brief IPv6 routing extension header. * @@ -46,14 +58,16 @@ typedef struct __attribute__((packed)) { } ipv6_ext_rh_t; /** - * @brief Extract next hop from the routing header of an IPv6 packet. + * @brief Process the routing header of an IPv6 packet. * - * @param[in] ipv6 An IPv6 packet. + * @param[in, out] ipv6 An IPv6 packet. + * @param[in] ext A routing header of @ipv6. * - * @return next hop on success, on success - * @return NULL, if not found. + * @return EXT_RH_CODE_ERROR + * @return EXT_RH_CODE_FORWARD + * @return EXT_RH_CODE_OK */ -ipv6_addr_t *ipv6_ext_rh_next_hop(ipv6_hdr_t *ipv6); +int ipv6_ext_rh_process(ipv6_hdr_t *ipv6, ipv6_ext_rh_t *ext); #ifdef __cplusplus } 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 b2a057f7c7..64dd90ca71 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 @@ -20,12 +20,17 @@ #include "net/gnrc/ipv6/ext.h" +#define ENABLE_DEBUG (0) +#include "debug.h" + bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh) { - gnrc_pktsnip_t *ext_snip; + gnrc_pktsnip_t *ext_snip, *tmp; ipv6_ext_t *ext; unsigned int offset = 0; + ipv6_hdr_t *hdr; + int res; ext = ((ipv6_ext_t *)(((uint8_t *)pkt->data) + sizeof(ipv6_hdr_t))); @@ -36,6 +41,35 @@ bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, case PROTNUM_IPV6_EXT_HOPOPT: case PROTNUM_IPV6_EXT_DST: case PROTNUM_IPV6_EXT_RH: + if ((tmp = gnrc_pktbuf_start_write(pkt)) == NULL) { + DEBUG("ipv6: could not get a copy of pkt\n"); + gnrc_pktbuf_release(pkt); + return false; + } + pkt = tmp; + hdr = pkt->data; + ext = (ipv6_ext_t *) (((uint8_t *) pkt->data) + sizeof(ipv6_hdr_t) + offset); + res = ipv6_ext_rh_process(hdr, (ipv6_ext_rh_t *)ext); + if (res == EXT_RH_CODE_ERROR) { + /* TODO: send ICMPv6 error codes */ + gnrc_pktbuf_release(pkt); + return false; + } + else if (res == EXT_RH_CODE_FORWARD) { + /* forward packet */ + if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, + pkt)) { + DEBUG("ipv6: could not dispatch packet to the ipv6 thread\n"); + gnrc_pktbuf_release(pkt); + } + return false; + } + else if (res == EXT_RH_CODE_OK) { + nh = ext->nh; + offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT); + ext = ipv6_ext_get_next((ipv6_ext_t *)ext); + } + break; case PROTNUM_IPV6_EXT_FRAG: case PROTNUM_IPV6_EXT_AH: case PROTNUM_IPV6_EXT_ESP: @@ -57,6 +91,7 @@ bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6); if (ext_snip == NULL) { + gnrc_pktbuf_release(pkt); return false; } diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index 2283c4cbd3..2cf446889a 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -123,8 +123,7 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh) case PROTNUM_IPV6_EXT_MOB: DEBUG("ipv6: handle extension header (nh = %u)\n", nh); if (!gnrc_ipv6_ext_demux(iface, pkt, nh)) { - DEBUG("ipv6: unable to parse extension headers.\n"); - gnrc_pktbuf_release(pkt); + DEBUG("ipv6: stop packet processing.\n"); return; } #endif diff --git a/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c b/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c index facab339eb..bb9ed06b44 100644 --- a/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c +++ b/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c @@ -63,14 +63,6 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, ipv6_addr_t *next_hop_ip = NULL, *prefix = NULL; bool dst_link_local = ipv6_addr_is_link_local(dst); -#ifdef MODULE_GNRC_IPV6_EXT_RH - ipv6_hdr_t *hdr; - gnrc_pktsnip_t *ipv6; - ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); - assert(ipv6); - hdr = ipv6->data; - next_hop_ip = ipv6_ext_rh_next_hop(hdr); -#endif #ifdef MODULE_FIB ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ /* don't look-up link local addresses in FIB */ diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c b/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c index a8653dd1e5..f3a7f7e33d 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c @@ -124,14 +124,6 @@ kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_ ipv6_addr_t *next_hop = NULL; gnrc_ipv6_nc_t *nc_entry = NULL; -#ifdef MODULE_GNRC_IPV6_EXT_RH - ipv6_hdr_t *hdr; - gnrc_pktsnip_t *ipv6; - ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); - assert(ipv6); - hdr = ipv6->data; - next_hop = ipv6_ext_rh_next_hop(hdr); -#endif #ifdef MODULE_FIB kernel_pid_t fib_iface; ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ diff --git a/sys/net/gnrc/routing/rpl/srh/Makefile b/sys/net/gnrc/routing/rpl/srh/Makefile new file mode 100644 index 0000000000..316990c418 --- /dev/null +++ b/sys/net/gnrc/routing/rpl/srh/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_rpl_srh + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c b/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c index b28b9dfd9b..0e09ff4ae2 100644 --- a/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c +++ b/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c @@ -12,13 +12,86 @@ * @file */ +#include +#include "net/gnrc/ipv6/netif.h" #include "net/gnrc/rpl/srh.h" -ipv6_addr_t *gnrc_rpl_srh_next_hop(gnrc_rpl_srh_t *rh) -{ - /* TODO */ +#define ENABLE_DEBUG (0) +#include "debug.h" - return NULL; +#if ENABLE_DEBUG +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +#define GNRC_RPL_SRH_PADDING(X) ((X & 0xF0) >> 4) +#define GNRC_RPL_SRH_COMPRE(X) (X & 0x0F) +#define GNRC_RPL_SRH_COMPRI(X) ((X & 0xF0) >> 4) + +int gnrc_rpl_srh_process(ipv6_hdr_t *ipv6, gnrc_rpl_srh_t *rh) +{ + if (rh->seg_left == 0) { + return EXT_RH_CODE_OK; + } + + uint8_t n = (((rh->len * 8) - GNRC_RPL_SRH_PADDING(rh->pad_resv) - + (16 - GNRC_RPL_SRH_COMPRE(rh->compr))) / + (16 - GNRC_RPL_SRH_COMPRI(rh->compr))) + 1; + ipv6_addr_t addr = ipv6->dst, tmp; + uint8_t i, pref_elided, tmp_pref_elided, addr_len, compri_addr_len, tmp_addr_len, found_pos = 0; + uint8_t *addr_vec = (uint8_t *) (rh + 1); + bool found = false; + + DEBUG("RPL SRH: %" PRIu8 " addresses in the routing header\n", n); + + if (rh->seg_left > n) { + DEBUG("RPL SRH: number of segments left > number of addresses - discard\n"); + /* TODO ICMP Parameter Problem - Code 0 */ + return EXT_RH_CODE_ERROR; + } + + rh->seg_left--; + i = n - rh->seg_left; + pref_elided = rh->seg_left ? GNRC_RPL_SRH_COMPRI(rh->compr) : GNRC_RPL_SRH_COMPRE(rh->compr); + compri_addr_len = sizeof(ipv6_addr_t) - GNRC_RPL_SRH_COMPRI(rh->compr); + addr_len = sizeof(ipv6_addr_t) - pref_elided; + memcpy(&addr.u8[pref_elided], &addr_vec[(i - 1) * compri_addr_len], addr_len); + + if (ipv6_addr_is_multicast(&ipv6->dst) || ipv6_addr_is_multicast(&addr)) { + DEBUG("RPL SRH: found a multicast address - discard\n"); + /* TODO discard the packet */ + return EXT_RH_CODE_ERROR; + } + + /* check if multiple addresses of my interface exist */ + tmp_pref_elided = GNRC_RPL_SRH_COMPRI(rh->compr); + tmp_addr_len = sizeof(ipv6_addr_t) - tmp_pref_elided; + tmp = ipv6->dst; + for (uint8_t k = 0; k < n; k++) { + if (k == n - 1) { + tmp_pref_elided = GNRC_RPL_SRH_COMPRE(rh->compr); + tmp_addr_len = sizeof(ipv6_addr_t) - tmp_pref_elided; + tmp = ipv6->dst; + } + memcpy(&tmp.u8[tmp_pref_elided], &addr_vec[k * compri_addr_len], tmp_addr_len); + if (gnrc_ipv6_netif_find_by_addr(NULL, &tmp) != KERNEL_PID_UNDEF) { + if (found && ((k - found_pos) > 1)) { + DEBUG("RPL SRH: found multiple addresses that belong to me - discard\n"); + /* TODO send an ICMP Parameter Problem (Code 0) and discard the packet */ + return EXT_RH_CODE_ERROR; + } + found_pos = k; + found = true; + } + } + + memcpy(&addr_vec[(i - 1) * compri_addr_len], &ipv6->dst.u8[pref_elided], addr_len); + + DEBUG("RPL SRH: Next hop: %s at position %d\n", + ipv6_addr_to_str(addr_str, &addr, sizeof(addr_str)), i); + + ipv6->dst = addr; + + return EXT_RH_CODE_FORWARD; } /** @} */ diff --git a/sys/net/network_layer/ipv6/ext/Makefile b/sys/net/network_layer/ipv6/ext/Makefile new file mode 100644 index 0000000000..35b590050e --- /dev/null +++ b/sys/net/network_layer/ipv6/ext/Makefile @@ -0,0 +1,3 @@ +MODULE = ipv6_ext + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ipv6/ext/rh/ipv6_ext_rh.c b/sys/net/network_layer/ipv6/ext/rh/ipv6_ext_rh.c index 341f624077..187a5c8056 100644 --- a/sys/net/network_layer/ipv6/ext/rh/ipv6_ext_rh.c +++ b/sys/net/network_layer/ipv6/ext/rh/ipv6_ext_rh.c @@ -15,48 +15,24 @@ #include #include "net/protnum.h" +#include "net/ipv6/ext.h" #include "net/ipv6/ext/rh.h" #include "net/gnrc/rpl/srh.h" -ipv6_addr_t *ipv6_ext_rh_next_hop(ipv6_hdr_t *ipv6) +int ipv6_ext_rh_process(ipv6_hdr_t *hdr, ipv6_ext_rh_t *ext) { - ipv6_ext_rh_t *ext = (ipv6_ext_rh_t *)(ipv6 + 1); - bool c = true; + (void) hdr; - while (c) { - switch (ext->type) { - case PROTNUM_IPV6_EXT_HOPOPT: - case PROTNUM_IPV6_EXT_DST: - case PROTNUM_IPV6_EXT_FRAG: - case PROTNUM_IPV6_EXT_AH: - case PROTNUM_IPV6_EXT_ESP: - case PROTNUM_IPV6_EXT_MOB: - ext = (ipv6_ext_rh_t *)ipv6_ext_get_next((ipv6_ext_t *)ext); - break; - - case PROTNUM_IPV6_EXT_RH: - c = false; - break; - - default: - c = false; - break; - } - } - - if (ipv6->nh == PROTNUM_IPV6_EXT_RH) { - switch (ext->type) { + switch (ext->type) { #ifdef MODULE_GNRC_RPL_SRH - case GNRC_RPL_SRH_TYPE: - return gnrc_rpl_srh_next_hop((gnrc_rpl_srh_t *)ext); + case GNRC_RPL_SRH_TYPE: + return gnrc_rpl_srh_process(hdr, (gnrc_rpl_srh_t *)ext); #endif - default: - break; - } + default: + break; } - - return NULL; + return EXT_RH_CODE_ERROR; } /** @} */