1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

ipv6_ext: fixed extension header handling

This commit is contained in:
Yonezawa-T2 2016-01-20 19:25:58 +09:00
parent 93bce6291c
commit bc5b0c3140
7 changed files with 377 additions and 96 deletions

View File

@ -223,7 +223,6 @@ ifneq (,$(filter ipv6_ext_rh,$(USEMODULE)))
endif
ifneq (,$(filter gnrc_ipv6_ext,$(USEMODULE)))
USEMODULE += ipv6_ext
USEMODULE += gnrc_ipv6
endif

View File

@ -117,11 +117,22 @@ kernel_pid_t gnrc_ipv6_init(void);
* **Do not use outside this module or its submodules!!!**
* Public access needed for Extension Headers.
*
* About `current` and `pkt`:
*
* current pkt
* | |
* v v
* IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
*
* This situation may happen when the packet has a source routing extension
* header (RFC 6554), and the packet is forwarded from an interface to another.
*
* @param[in] iface The receiving interface.
* @param[in] current A snip to process.
* @param[in] pkt A packet.
* @param[in] nh A protocol number (see @ref net_protnum).
* @param[in] nh A protocol number (see @ref net_protnum) of the current snip.
*/
void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh);
void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt, uint8_t nh);
#ifdef __cplusplus
}

View File

@ -40,16 +40,26 @@ extern "C" {
/**
* @brief Demultiplex extension headers according to @p nh.
*
* About `current` and `pkt`:
*
* current pkt
* | |
* v v
* IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
*
* This situation may happen when the packet has a source routing extension
* header (RFC 6554), and the packet is forwarded from an interface to another.
*
* @internal
*
* @param[in] iface The receiving interface.
* @param[in] pkt A packet.
* @param[in] nh A protocol number (see @ref net_protnum).
*
* @return true, on success - continue packet processing.
* @return false, on failure - stop packet processing.
* @param[in] iface The receiving interface.
* @param[in] current A snip to process.
* @param[in] pkt A packet.
* @param[in] nh A protocol number (see @ref net_protnum) of the current snip.
*/
bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
void gnrc_ipv6_ext_demux(kernel_pid_t iface,
gnrc_pktsnip_t *current,
gnrc_pktsnip_t *pkt,
uint8_t nh);
/**

View File

@ -63,6 +63,9 @@ typedef enum {
#ifdef MODULE_GNRC_IPV6
GNRC_NETTYPE_IPV6, /**< Protocol is IPv6 */
#endif
#ifdef MODULE_GNRC_IPV6_EXT
GNRC_NETTYPE_IPV6_EXT, /**< Protocol is IPv6 extension header */
#endif
#ifdef MODULE_GNRC_ICMPV6
GNRC_NETTYPE_ICMPV6, /**< Protocol is ICMPv6 */
#endif
@ -184,6 +187,16 @@ static inline gnrc_nettype_t gnrc_nettype_from_protnum(uint8_t num)
#ifdef MODULE_GNRC_UDP
case PROTNUM_UDP:
return GNRC_NETTYPE_UDP;
#endif
#ifdef MODULE_GNRC_IPV6_EXT
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:
return GNRC_NETTYPE_IPV6_EXT;
#endif
default:
return GNRC_NETTYPE_UNDEF;

View File

@ -23,81 +23,271 @@
#define ENABLE_DEBUG (0)
#include "debug.h"
bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
uint8_t nh)
#ifdef MODULE_GNRC_RPL_SRH
static size_t _cumulate_size_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
{
gnrc_pktsnip_t *ext_snip, *tmp;
ipv6_ext_t *ext;
unsigned int offset = 0;
ipv6_hdr_t *hdr;
int res;
size_t sum = 0;
ext = ((ipv6_ext_t *)(((uint8_t *)pkt->data) + sizeof(ipv6_hdr_t)));
for (; pkt != NULL; pkt = pkt->next) {
sum += pkt->size;
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;
if (pkt->type == type) {
break;
}
}
ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6);
return sum;
}
if (ext_snip == NULL) {
gnrc_pktbuf_release(pkt);
return false;
/**
* @brief duplicates pktsnip chain upto (including) a snip with the given type
* as a continuous snip.
*
* Example:
* Input:
* buffer
* +---------------------------+ +------+
* | size = 8 | data +-------->| |
* | type = NETTYPE_IPV6_EXT |------------+ +------+
* +---------------------------+ . .
* | next . .
* v . .
* +---------------------------+ +------+
* | size = 40 | data +----------->| |
* | type = NETTYPE_IPV6 |---------+ +------+
* +---------------------------+ . .
* | next . .
* v
* +---------------------------+ +------+
* | size = 14 | data +-------------->| |
* | type = NETTYPE_NETIF |------+ +------+
* +---------------------------+ . .
*
*
* Output:
* buffer
* +---------------------------+ +------+
* | size = 48 | data +-------->| |
* | type = NETTYPE_IPV6 |------------+ | |
* +---------------------------+ | |
* | +------+
* | . .
* | next . .
* v
* +---------------------------+ +------+
* | size = 14 | data +-------------->| |
* | type = NETTYPE_NETIF |------+ +------+
* +---------------------------+ . .
*
* The original snip is keeped as is except `users` decremented.
*
* @param[in,out] pkt The snip to duplicate.
* @param[in] type The type of snip to stop duplication.
*
* @return The duplicated snip, if succeeded.
* @return NULL, if no space is left in the packet buffer.
*/
static gnrc_pktsnip_t *_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
{
bool is_shared = pkt->users > 1;
size_t size = _cumulate_size_upto(pkt, type);
DEBUG("ipv6_ext: duplicating %d octets\n", (int) size);
gnrc_pktsnip_t *tmp;
gnrc_pktsnip_t *target = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
gnrc_pktsnip_t *next = (target == NULL) ? NULL : target->next;
gnrc_pktsnip_t *new = gnrc_pktbuf_add(next, NULL, size, type);
if (new == NULL) {
return NULL;
}
gnrc_ipv6_demux(iface, pkt, nh); /* demultiplex next header */
/* copy payloads */
for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
uint8_t *dest = ((uint8_t *)new->data) + (size - tmp->size);
return true;
memcpy(dest, tmp->data, tmp->size);
size -= tmp->size;
if (tmp->type == type) {
break;
}
}
/* decrements reference counters */
if (target != NULL) {
target->next = NULL;
}
gnrc_pktbuf_release(pkt);
if (is_shared && (target != NULL)) {
target->next = next;
}
return new;
}
enum gnrc_ipv6_ext_demux_status {
GNRC_IPV6_EXT_OK,
GNRC_IPV6_EXT_FORWARDED,
GNRC_IPV6_EXT_ERROR,
};
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 = _cumulate_size_upto(current->next, GNRC_NETTYPE_IPV6);
if (pkt->users != 1) {
if ((ipv6 = _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;
}
#endif
/*
* 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)
{
gnrc_pktsnip_t *ext_snip, *tmp, *next;
ipv6_ext_t *ext;
size_t offset = 0;
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);
offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
break;
case GNRC_IPV6_EXT_ERROR:
/* already released by _handle_rh, so no release here */
return;
case GNRC_IPV6_EXT_FORWARDED:
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);
offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
break;
default:
DEBUG("ipv6_ext: unknown next header: %" PRIu8 "\n", nh);
gnrc_pktbuf_release(pkt);
return;
}
if (current == pkt) {
if ((tmp = gnrc_pktbuf_start_write(pkt)) == NULL) {
DEBUG("ipv6: could not get a copy of pkt\n");
gnrc_pktbuf_release(pkt);
return;
}
pkt = tmp;
ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6_EXT);
next = pkt;
if (ext_snip == NULL) {
gnrc_pktbuf_release(pkt);
return;
}
}
else {
/* the header is already marked */
next = NULL;
for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
if (tmp->next == current) {
next = tmp;
break;
}
}
assert(next != NULL);
}
gnrc_ipv6_demux(iface, next, pkt, nh); /* demultiplex next header */
return;
}
gnrc_pktsnip_t *gnrc_ipv6_ext_build(gnrc_pktsnip_t *ipv6, gnrc_pktsnip_t *next,

View File

@ -100,17 +100,24 @@ kernel_pid_t gnrc_ipv6_init(void)
return gnrc_ipv6_pid;
}
void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
/*
* current pkt
* | |
* v v
* IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
*/
void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt, uint8_t nh)
{
int receiver_num;
bool interested = false;
pkt->type = gnrc_nettype_from_protnum(nh);
switch (nh) {
#ifdef MODULE_GNRC_ICMPV6
case PROTNUM_ICMPV6:
DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
gnrc_icmpv6_demux(iface, pkt);
assert(current == pkt);
interested = true;
break;
#endif
#ifdef MODULE_GNRC_IPV6_EXT
@ -121,15 +128,14 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
case PROTNUM_IPV6_EXT_AH:
case PROTNUM_IPV6_EXT_ESP:
case PROTNUM_IPV6_EXT_MOB:
DEBUG("ipv6: handle extension header (nh = %u)\n", nh);
if (!gnrc_ipv6_ext_demux(iface, pkt, nh)) {
DEBUG("ipv6: stop packet processing.\n");
return;
}
interested = true;
break;
#endif
case PROTNUM_IPV6:
DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
_decapsulate(pkt);
assert(current == pkt);
interested = true;
break;
default:
(void)iface;
@ -142,17 +148,64 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
if (receiver_num == 0) {
DEBUG("ipv6: unable to forward packet as no one is interested in it\n");
gnrc_pktbuf_release(pkt);
return;
if (!interested) {
assert(current == pkt);
gnrc_pktbuf_release(pkt);
return;
}
}
else {
if (!interested) {
assert(current == pkt);
/* IPv6 is not interested anymore so `- 1` */
receiver_num--;
}
gnrc_pktbuf_hold(current, receiver_num);
/* XXX can't use gnrc_netapi_dispatch_receive() twice here since a call to that function
* implicitly hands all rights to the packet to one of the receiving threads. As a
* result, the second call to gnrc_netapi_dispatch_receive() would be invalid */
_dispatch_rcv_pkt(current->type, GNRC_NETREG_DEMUX_CTX_ALL, current);
_dispatch_rcv_pkt(GNRC_NETTYPE_IPV6, nh, current);
if (!interested) {
return;
}
}
gnrc_pktbuf_hold(pkt, receiver_num - 1); /* IPv6 is not interested anymore so `- 1` */
switch (nh) {
#ifdef MODULE_GNRC_ICMPV6
case PROTNUM_ICMPV6:
DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
gnrc_icmpv6_demux(iface, pkt);
break;
#endif
#ifdef MODULE_GNRC_IPV6_EXT
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:
DEBUG("ipv6: handle extension header (nh = %u)\n", nh);
/* XXX can't use gnrc_netapi_dispatch_receive() twice here since a call to that function
* implicitly hands all rights to the packet to one of the receiving threads. As a result,
* the second call to gnrc_netapi_dispatch_receive() would be invalid */
_dispatch_rcv_pkt(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt);
_dispatch_rcv_pkt(GNRC_NETTYPE_IPV6, nh, pkt);
gnrc_ipv6_ext_demux(iface, current, pkt, nh);
return;
#endif
case PROTNUM_IPV6:
DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
_decapsulate(pkt);
return;
default:
break;
}
assert(current == pkt);
gnrc_pktbuf_release(pkt);
}
/* internal functions */
@ -704,7 +757,7 @@ static void _dispatch_rcv_pkt(gnrc_nettype_t type, uint32_t demux_ctx,
static void _receive(gnrc_pktsnip_t *pkt)
{
kernel_pid_t iface = KERNEL_PID_UNDEF;
gnrc_pktsnip_t *ipv6, *netif;
gnrc_pktsnip_t *ipv6, *netif, *first_ext;
ipv6_hdr_t *hdr;
assert(pkt != NULL);
@ -715,11 +768,15 @@ static void _receive(gnrc_pktsnip_t *pkt)
iface = ((gnrc_netif_hdr_t *)netif->data)->if_pid;
}
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) {
@ -755,6 +812,8 @@ static void _receive(gnrc_pktsnip_t *pkt)
ipv6 = gnrc_pktbuf_mark(pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6);
first_ext = pkt;
if (ipv6 == NULL) {
DEBUG("ipv6: error marking IPv6 header, dropping packet\n");
gnrc_pktbuf_release(pkt);
@ -863,7 +922,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
}
/* IPv6 internal demuxing (ICMPv6, Extension headers etc.) */
gnrc_ipv6_demux(iface, pkt, hdr->nh);
gnrc_ipv6_demux(iface, first_ext, pkt, hdr->nh);
}
static void _decapsulate(gnrc_pktsnip_t *pkt)

View File

@ -70,7 +70,6 @@ int gnrc_rpl_srh_process(ipv6_hdr_t *ipv6, gnrc_rpl_srh_t *rh)
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) {