static enum gnrc_ipv6_ext_demux_status _handle_rh(gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt) { gnrc_pktsnip_t *ipv6; ipv6_ext_t *ext = (ipv6_ext_t *) current->data; size_t current_offset; ipv6_hdr_t *hdr; /* check seg_left early to avoid duplicating the packet */ if (((ipv6_ext_rh_t *)ext)->seg_left == 0) { return GNRC_IPV6_EXT_OK; } /* We cannot use `gnrc_pktbuf_start_write` since it duplicates only the head. `ipv6_ext_rh_process` modifies the IPv6 header as well as the extension header */ current_offset = gnrc_pkt_len_upto(current->next, GNRC_NETTYPE_IPV6); if (pkt->users != 1) { if ((ipv6 = gnrc_pktbuf_duplicate_upto(pkt, GNRC_NETTYPE_IPV6)) == NULL) { DEBUG("ipv6: could not get a copy of pkt\n"); gnrc_pktbuf_release(pkt); return GNRC_IPV6_EXT_ERROR; } pkt = ipv6; hdr = ipv6->data; ext = (ipv6_ext_t *)(((uint8_t *)ipv6->data) + current_offset); } else { ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); hdr = ipv6->data; } switch (ipv6_ext_rh_process(hdr, (ipv6_ext_rh_t *)ext)) { case EXT_RH_CODE_ERROR: /* TODO: send ICMPv6 error codes */ gnrc_pktbuf_release(pkt); return GNRC_IPV6_EXT_ERROR; case 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 GNRC_IPV6_EXT_FORWARDED; case EXT_RH_CODE_OK: /* this should not happen since we checked seg_left early */ gnrc_pktbuf_release(pkt); return GNRC_IPV6_EXT_ERROR; } return GNRC_IPV6_EXT_OK; }
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; }