bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh) { 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))); bool c = true; while (c) { switch (nh) { 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: case PROTNUM_IPV6_EXT_MOB: /* TODO: add handling of types */ nh = ext->nh; offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT); ext = ipv6_ext_get_next((ipv6_ext_t *)ext); break; default: c = false; offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT); ext = ipv6_ext_get_next((ipv6_ext_t *)ext); break; } } ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6); if (ext_snip == NULL) { gnrc_pktbuf_release(pkt); return false; } gnrc_ipv6_demux(iface, pkt, nh); /* demultiplex next header */ return true; }
static void _receive(gnrc_pktsnip_t *pkt) { kernel_pid_t iface = KERNEL_PID_UNDEF; gnrc_pktsnip_t *ipv6, *netif; ipv6_hdr_t *hdr; assert(pkt != NULL); LL_SEARCH_SCALAR(pkt, netif, type, GNRC_NETTYPE_NETIF); if (netif != NULL) { iface = ((gnrc_netif_hdr_t *)netif->data)->if_pid; } if ((pkt->next != NULL) && (pkt->next->type == GNRC_NETTYPE_IPV6) && (pkt->next->size == sizeof(ipv6_hdr_t))) { /* IP header was already marked. Take it. */ ipv6 = pkt->next; if (!ipv6_hdr_is(ipv6->data)) { DEBUG("ipv6: Received packet was not IPv6, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } } else { if (!ipv6_hdr_is(pkt->data)) { DEBUG("ipv6: Received packet was not IPv6, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } /* seize ipv6 as a temporary variable */ ipv6 = gnrc_pktbuf_start_write(pkt); if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to packet, drop it\n"); gnrc_pktbuf_release(pkt); return; } pkt = ipv6; /* reset pkt from temporary variable */ ipv6 = gnrc_pktbuf_mark(pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6); if (ipv6 == NULL) { DEBUG("ipv6: error marking IPv6 header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } } /* extract header */ hdr = (ipv6_hdr_t *)ipv6->data; /* if available, remove any padding that was added by lower layers * to fulfill their minimum size requirements (e.g. ethernet) */ if (byteorder_ntohs(hdr->len) < pkt->size) { gnrc_pktbuf_realloc_data(pkt, byteorder_ntohs(hdr->len)); } DEBUG("ipv6: Received (src = %s, ", ipv6_addr_to_str(addr_str, &(hdr->src), sizeof(addr_str))); DEBUG("dst = %s, next header = %" PRIu8 ", length = %" PRIu16 ")\n", ipv6_addr_to_str(addr_str, &(hdr->dst), sizeof(addr_str)), hdr->nh, byteorder_ntohs(hdr->len)); if (_pkt_not_for_me(&iface, hdr)) { /* if packet is not for me */ DEBUG("ipv6: packet destination not this host\n"); #ifdef MODULE_GNRC_IPV6_ROUTER /* only routers redirect */ /* redirect to next hop */ DEBUG("ipv6: decrement hop limit to %" PRIu8 "\n", hdr->hl - 1); /* RFC 4291, section 2.5.6 states: "Routers must not forward any * packets with Link-Local source or destination addresses to other * links." */ if ((ipv6_addr_is_link_local(&(hdr->src))) || (ipv6_addr_is_link_local(&(hdr->dst)))) { DEBUG("ipv6: do not forward packets with link-local source or"\ " destination address\n"); gnrc_pktbuf_release(pkt); return; } /* TODO: check if receiving interface is router */ else if (--(hdr->hl) > 0) { /* drop packets that *reach* Hop Limit 0 */ gnrc_pktsnip_t *tmp = pkt; DEBUG("ipv6: forward packet to next hop\n"); /* pkt might not be writable yet, if header was given above */ pkt = gnrc_pktbuf_start_write(tmp); ipv6 = gnrc_pktbuf_start_write(ipv6); if ((ipv6 == NULL) || (pkt == NULL)) { DEBUG("ipv6: unable to get write access to packet: dropping it\n"); gnrc_pktbuf_release(tmp); return; } gnrc_pktbuf_release(ipv6->next); /* remove headers around IPV6 */ ipv6->next = pkt; /* reorder for sending */ pkt->next = NULL; _send(ipv6, false); return; } else { DEBUG("ipv6: hop limit reached 0: drop packet\n"); gnrc_pktbuf_release(pkt); return; } #else /* MODULE_GNRC_IPV6_ROUTER */ DEBUG("ipv6: dropping packet\n"); /* non rounting hosts just drop the packet */ gnrc_pktbuf_release(pkt); return; #endif /* MODULE_GNRC_IPV6_ROUTER */ } /* IPv6 internal demuxing (ICMPv6, Extension headers etc.) */ gnrc_ipv6_demux(iface, pkt, hdr->nh); }
static void _receive(gnrc_pktsnip_t *pkt) { kernel_pid_t iface = KERNEL_PID_UNDEF; gnrc_pktsnip_t *ipv6, *netif, *first_ext; ipv6_hdr_t *hdr; assert(pkt != NULL); netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); if (netif != NULL) { iface = ((gnrc_netif_hdr_t *)netif->data)->if_pid; #ifdef MODULE_NETSTATS_IPV6 assert(iface); netstats_t *stats = gnrc_ipv6_netif_get_stats(iface); stats->rx_count++; stats->rx_bytes += (gnrc_pkt_len(pkt) - netif->size); #endif } first_ext = pkt; for (ipv6 = pkt; ipv6 != NULL; ipv6 = ipv6->next) { /* find IPv6 header if already marked */ if ((ipv6->type == GNRC_NETTYPE_IPV6) && (ipv6->size == sizeof(ipv6_hdr_t)) && (ipv6_hdr_is(ipv6->data))) { break; } first_ext = ipv6; } if (ipv6 == NULL) { if (!ipv6_hdr_is(pkt->data)) { DEBUG("ipv6: Received packet was not IPv6, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } #ifdef MODULE_GNRC_IPV6_WHITELIST if (!gnrc_ipv6_whitelisted(&((ipv6_hdr_t *)(pkt->data))->src)) { DEBUG("ipv6: Source address not whitelisted, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } #endif #ifdef MODULE_GNRC_IPV6_BLACKLIST if (gnrc_ipv6_blacklisted(&((ipv6_hdr_t *)(pkt->data))->src)) { DEBUG("ipv6: Source address blacklisted, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } #endif /* seize ipv6 as a temporary variable */ ipv6 = gnrc_pktbuf_start_write(pkt); if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to packet, drop it\n"); gnrc_pktbuf_release(pkt); return; } pkt = ipv6; /* reset pkt from temporary variable */ ipv6 = gnrc_pktbuf_mark(pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6); first_ext = pkt; pkt->type = GNRC_NETTYPE_UNDEF; /* snip is no longer IPv6 */ if (ipv6 == NULL) { DEBUG("ipv6: error marking IPv6 header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } } #ifdef MODULE_GNRC_IPV6_WHITELIST else if (!gnrc_ipv6_whitelisted(&((ipv6_hdr_t *)(ipv6->data))->src)) { /* if ipv6 header already marked*/ DEBUG("ipv6: Source address not whitelisted, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } #endif #ifdef MODULE_GNRC_IPV6_BLACKLIST else if (gnrc_ipv6_blacklisted(&((ipv6_hdr_t *)(ipv6->data))->src)) { /* if ipv6 header already marked*/ DEBUG("ipv6: Source address blacklisted, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } #endif /* extract header */ hdr = (ipv6_hdr_t *)ipv6->data; /* if available, remove any padding that was added by lower layers * to fulfill their minimum size requirements (e.g. ethernet) */ if (byteorder_ntohs(hdr->len) < pkt->size) { gnrc_pktbuf_realloc_data(pkt, byteorder_ntohs(hdr->len)); } else if (byteorder_ntohs(hdr->len) > (gnrc_pkt_len_upto(pkt, GNRC_NETTYPE_IPV6) - sizeof(ipv6_hdr_t))) { DEBUG("ipv6: invalid payload length: %d, actual: %d, dropping packet\n", (int) byteorder_ntohs(hdr->len), (int) (gnrc_pkt_len_upto(pkt, GNRC_NETTYPE_IPV6) - sizeof(ipv6_hdr_t))); gnrc_pktbuf_release(pkt); return; } DEBUG("ipv6: Received (src = %s, ", 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)); if (_pkt_not_for_me(&iface, hdr)) { /* if packet is not for me */ DEBUG("ipv6: packet destination not this host\n"); #ifdef MODULE_GNRC_IPV6_ROUTER /* only routers redirect */ /* redirect to next hop */ DEBUG("ipv6: decrement hop limit to %u\n", (uint8_t) (hdr->hl - 1)); /* RFC 4291, section 2.5.6 states: "Routers must not forward any * packets with Link-Local source or destination addresses to other * links." */ if ((ipv6_addr_is_link_local(&(hdr->src))) || (ipv6_addr_is_link_local(&(hdr->dst)))) { DEBUG("ipv6: do not forward packets with link-local source or" " destination address\n"); gnrc_pktbuf_release(pkt); return; } /* TODO: check if receiving interface is router */ else if (--(hdr->hl) > 0) { /* drop packets that *reach* Hop Limit 0 */ gnrc_pktsnip_t *reversed_pkt = NULL, *ptr = pkt; DEBUG("ipv6: forward packet to next hop\n"); /* pkt might not be writable yet, if header was given above */ ipv6 = gnrc_pktbuf_start_write(ipv6); if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to packet: dropping it\n"); gnrc_pktbuf_release(pkt); return; } /* remove L2 headers around IPV6 */ netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); if (netif != NULL) { gnrc_pktbuf_remove_snip(pkt, netif); } /* reverse packet snip list order */ while (ptr != NULL) { gnrc_pktsnip_t *next; ptr = gnrc_pktbuf_start_write(ptr); /* duplicate if not already done */ if (ptr == NULL) { DEBUG("ipv6: unable to get write access to packet: dropping it\n"); gnrc_pktbuf_release(reversed_pkt); gnrc_pktbuf_release(pkt); return; } next = ptr->next; ptr->next = reversed_pkt; reversed_pkt = ptr; ptr = next; } _send(reversed_pkt, false); return; } else { DEBUG("ipv6: hop limit reached 0: drop packet\n"); gnrc_pktbuf_release(pkt); return; } #else /* MODULE_GNRC_IPV6_ROUTER */ DEBUG("ipv6: dropping packet\n"); /* non rounting hosts just drop the packet */ gnrc_pktbuf_release(pkt); return; #endif /* MODULE_GNRC_IPV6_ROUTER */ } /* IPv6 internal demuxing (ICMPv6, Extension headers etc.) */ gnrc_ipv6_demux(iface, first_ext, pkt, hdr->nh); }
/* * current pkt * | | * v v * IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF */ void gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt, uint8_t nh) { ipv6_ext_t *ext; while (true) { ext = (ipv6_ext_t *) current->data; switch (nh) { case PROTNUM_IPV6_EXT_RH: #ifdef MODULE_GNRC_RPL_SRH switch (_handle_rh(current, pkt)) { case GNRC_IPV6_EXT_OK: /* We are the final destination. So proceeds like normal packet. */ nh = ext->nh; DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh); if ((current = _mark_extension_header(current, &pkt)) == NULL) { return; } gnrc_ipv6_demux(iface, current, pkt, nh); /* demultiplex next header */ return; case GNRC_IPV6_EXT_ERROR: /* already released by _handle_rh, so no release here */ return; case GNRC_IPV6_EXT_FORWARDED: /* the packet is forwarded and released. finish processing */ return; } break; #endif 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: /* TODO: add handling of types */ nh = ext->nh; DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh); if ((current = _mark_extension_header(current, &pkt)) == NULL) { return; } gnrc_pktbuf_hold(pkt, 1); /* don't release on next dispatch */ if (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, nh, pkt) == 0) { gnrc_pktbuf_release(pkt); } break; default: gnrc_ipv6_demux(iface, current, pkt, nh); /* demultiplex next header */ return; } } assert(false); /* never reaches here */ }