mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #4654 from Yonezawa-T2/fix_ipv6_ext
ipv6_ext: fixed extension header handling
This commit is contained in:
commit
1724ab50fc
@ -223,7 +223,6 @@ ifneq (,$(filter ipv6_ext_rh,$(USEMODULE)))
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_ext,$(USEMODULE)))
|
||||
USEMODULE += ipv6_ext
|
||||
USEMODULE += gnrc_ipv6
|
||||
endif
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
@ -187,6 +190,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;
|
||||
|
@ -136,6 +136,31 @@ static inline size_t gnrc_pkt_len(gnrc_pktsnip_t *pkt)
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates length of a packet in byte upto (including) a snip with the given type.
|
||||
*
|
||||
* @param[in] pkt list of packet snips.
|
||||
* @param[in] type type of snip to stop calculation.
|
||||
*
|
||||
* @return length of the list of headers.
|
||||
*/
|
||||
static inline size_t gnrc_pkt_len_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (pkt) {
|
||||
len += pkt->size;
|
||||
|
||||
if (pkt->type == type) {
|
||||
break;
|
||||
}
|
||||
|
||||
pkt = pkt->next;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Count the numbers of snips in the given packet
|
||||
*
|
||||
|
@ -215,6 +215,56 @@ gnrc_pktsnip_t *gnrc_pktbuf_remove_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *sni
|
||||
*/
|
||||
gnrc_pktsnip_t *gnrc_pktbuf_replace_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *old, gnrc_pktsnip_t *add);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
gnrc_pktsnip_t *gnrc_pktbuf_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type);
|
||||
|
||||
#ifdef DEVELHELP
|
||||
/**
|
||||
* @brief Prints some statistics about the packet buffer to stdout.
|
||||
|
@ -23,81 +23,163 @@
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
|
||||
#ifdef MODULE_GNRC_RPL_SRH
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
#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;
|
||||
gnrc_pktsnip_t *ext_snip, *tmp, *next;
|
||||
ipv6_ext_t *ext;
|
||||
unsigned int offset = 0;
|
||||
ipv6_hdr_t *hdr;
|
||||
int res;
|
||||
size_t offset = 0;
|
||||
|
||||
ext = ((ipv6_ext_t *)(((uint8_t *)pkt->data) + sizeof(ipv6_hdr_t)));
|
||||
ext = (ipv6_ext_t *) current->data;
|
||||
|
||||
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) {
|
||||
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;
|
||||
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;
|
||||
DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh);
|
||||
offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
|
||||
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;
|
||||
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 */
|
||||
|
||||
ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6);
|
||||
next = NULL;
|
||||
|
||||
if (ext_snip == NULL) {
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return false;
|
||||
for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
|
||||
if (tmp->next == current) {
|
||||
next = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(next != NULL);
|
||||
}
|
||||
|
||||
gnrc_ipv6_demux(iface, pkt, nh); /* demultiplex next header */
|
||||
gnrc_ipv6_demux(iface, next, pkt, nh); /* demultiplex next header */
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
gnrc_pktsnip_t *gnrc_ipv6_ext_build(gnrc_pktsnip_t *ipv6, gnrc_pktsnip_t *next,
|
||||
|
@ -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)
|
||||
|
@ -217,9 +217,8 @@ void gnrc_pktbuf_hold(gnrc_pktsnip_t *pkt, unsigned int num)
|
||||
mutex_unlock(&_mutex);
|
||||
}
|
||||
|
||||
void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
|
||||
static void _release_error_locked(gnrc_pktsnip_t *pkt, uint32_t err)
|
||||
{
|
||||
mutex_lock(&_mutex);
|
||||
while (pkt) {
|
||||
gnrc_pktsnip_t *tmp;
|
||||
assert(_pktbuf_contains(pkt));
|
||||
@ -236,6 +235,12 @@ void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
|
||||
gnrc_neterr_report(pkt, err);
|
||||
pkt = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
|
||||
{
|
||||
mutex_lock(&_mutex);
|
||||
_release_error_locked(pkt, err);
|
||||
mutex_unlock(&_mutex);
|
||||
}
|
||||
|
||||
@ -526,4 +531,54 @@ gnrc_pktsnip_t *gnrc_pktbuf_replace_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *ol
|
||||
return pkt;
|
||||
}
|
||||
|
||||
gnrc_pktsnip_t *gnrc_pktbuf_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
|
||||
{
|
||||
mutex_lock(&_mutex);
|
||||
|
||||
bool is_shared = pkt->users > 1;
|
||||
size_t size = gnrc_pkt_len_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, type);
|
||||
gnrc_pktsnip_t *next = (target == NULL) ? NULL : target->next;
|
||||
gnrc_pktsnip_t *new = _create_snip(next, NULL, size, type);
|
||||
|
||||
if (new == NULL) {
|
||||
mutex_unlock(&_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy payloads */
|
||||
for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
|
||||
uint8_t *dest = ((uint8_t *)new->data) + (size - tmp->size);
|
||||
|
||||
memcpy(dest, tmp->data, tmp->size);
|
||||
|
||||
size -= tmp->size;
|
||||
|
||||
if (tmp->type == type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* decrements reference counters */
|
||||
|
||||
if (target != NULL) {
|
||||
target->next = NULL;
|
||||
}
|
||||
|
||||
_release_error_locked(pkt, GNRC_NETERR_SUCCESS);
|
||||
|
||||
if (is_shared && (target != NULL)) {
|
||||
target->next = next;
|
||||
}
|
||||
|
||||
mutex_unlock(&_mutex);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
@ -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) {
|
||||
|
43
tests/gnrc_ipv6_ext/Makefile
Normal file
43
tests/gnrc_ipv6_ext/Makefile
Normal file
@ -0,0 +1,43 @@
|
||||
# name of your application
|
||||
APPLICATION = gnrc_ipv6_ext
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := airfy-beacon chronos msb-430 msb-430h nrf51dongle \
|
||||
nrf6310 nucleo-f103 nucleo-f334 pca10000 pca10005 spark-core \
|
||||
stm32f0discovery telosb weio wsn430-v1_3b wsn430-v1_4 \
|
||||
yunjia-nrf51822 z1
|
||||
|
||||
# Include packages that pull up and auto-init the link layer.
|
||||
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
|
||||
USEMODULE += gnrc_netif_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
# Specify the mandatory networking modules for IPv6
|
||||
USEMODULE += gnrc_ipv6_router_default
|
||||
# IPv6 extension headers
|
||||
USEMODULE += gnrc_ipv6_ext
|
||||
USEMODULE += gnrc_rpl_srh
|
||||
# Add also the shell, some shell commands
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
CFLAGS += -DDEVELHELP
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# This requires ENABLE_DEBUG in gnrc_ipv6.c to be 1
|
||||
test:
|
||||
# `testrunner` calls `make term` recursively, results in duplicated `TERMFLAGS`.
|
||||
# So clears `TERMFLAGS` before run.
|
||||
TERMFLAGS= tests/01-run.py
|
43
tests/gnrc_ipv6_ext/README.md
Normal file
43
tests/gnrc_ipv6_ext/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# `gnrc_ipv6_ext` test
|
||||
|
||||
This test sends a packet to itself with extension headers. This is based on gnrc_networking example.
|
||||
|
||||
Enable debug output of `gnrc_ipv6.c` before run. When the test is run, it should show the following debug output:
|
||||
|
||||
```
|
||||
ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)
|
||||
ipv6: forward nh = 0 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 0)
|
||||
ipv6: forward nh = 43 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 43)
|
||||
ipv6: waiting for incoming message.
|
||||
ipv6: GNRC_NETAPI_MSG_TYPE_RCV received
|
||||
ipv6: Received (src = fd01::1, dst = fd01::3, next header = 0, length = 40)
|
||||
ipv6: forward nh = 0 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 0)
|
||||
ipv6: forward nh = 43 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 43)
|
||||
ipv6: waiting for incoming message.
|
||||
ipv6: GNRC_NETAPI_MSG_TYPE_RCV received
|
||||
ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)
|
||||
ipv6: forward nh = 0 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 0)
|
||||
ipv6: forward nh = 43 to other threads
|
||||
ipv6: unable to forward packet as no one is interested in it
|
||||
ipv6: handle extension header (nh = 43)
|
||||
ipv6: forward nh = 17 to other threads
|
||||
ipv6: Send receive command for 0x1060b8 to 5
|
||||
ipv6: waiting for incoming message.
|
||||
pkt->users: 0
|
||||
```
|
||||
|
||||
It configures the network interface with addresses fd01::02 and fd01::03. Then it sends a packet to fd01::02 with a routing extension header containing addresses fd01::03 and fd01::02. So the packet should be forwarded from fd01::02 to fd01::03, then to fd01::02 again.
|
||||
|
||||
The packet has a Hop-by-Hop extension header that should be ignored.
|
||||
|
||||
The test also asserts that the packet is released.
|
125
tests/gnrc_ipv6_ext/main.c
Normal file
125
tests/gnrc_ipv6_ext/main.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Tests extension header handling of gnrc stack.
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "msg.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/gnrc/ipv6/netif.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/netreg.h"
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/netif.h"
|
||||
|
||||
#define MAIN_QUEUE_SIZE (8)
|
||||
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
|
||||
|
||||
static void _send_packet(void) {
|
||||
kernel_pid_t ifs[GNRC_NETIF_NUMOF];
|
||||
ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED;
|
||||
|
||||
gnrc_netif_get(ifs);
|
||||
|
||||
addr.u8[0] = 0xfd;
|
||||
addr.u8[1] = 0x01;
|
||||
addr.u8[15] = 0x02;
|
||||
/* fd01::02 */
|
||||
gnrc_ipv6_netif_add_addr(ifs[0], &addr, 64, GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST);
|
||||
|
||||
addr.u8[15] = 0x03;
|
||||
/* fd01::03 */
|
||||
gnrc_ipv6_netif_add_addr(ifs[0], &addr, 64, GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST);
|
||||
|
||||
uint8_t data[] = {
|
||||
/* IPv6 Header */
|
||||
0x60, 0x00, 0x00, 0x00, /* version, traffic class, flow label */
|
||||
0x00, 0x28, /* payload length: 40 */
|
||||
0x00, /* next header: Hop-by-Hop Option */
|
||||
0x10, /* hop limit: 16 */
|
||||
/* source address: fd01::1 */
|
||||
0xfd, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
/* destination address: fd01::2 */
|
||||
0xfd, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
|
||||
/* Hop-by-Hop Options Header */
|
||||
/* https://tools.ietf.org/html/rfc6553 */
|
||||
0x2b, /* next header: IPv6-Route */
|
||||
0x00, /* hdr ext len: 0 * 8 + 8 = 8 */
|
||||
0x63, /* option type: RPL Option */
|
||||
0x04, /* opt data len: 4 */
|
||||
0x80, /* flags, Down: 1, Rank-Error: 0, Forwarding-Error: 0 */
|
||||
0x00, /* RPLInstanceID */
|
||||
0x80, 0x00, /* SenderRank */
|
||||
|
||||
/* RPL Routing Header */
|
||||
/* https://tools.ietf.org/html/rfc6554 */
|
||||
0x11, /* next header: UDP */
|
||||
0x02, /* hdr ext len: 2 * 8 + 8 = 24 */
|
||||
0x03, /* routing type: SRH */
|
||||
0x02, /* segments left: 2 */
|
||||
0xef, /* ComprI: 14, ComprE: 15 */
|
||||
0xd0, 0x00, 0x00, /* pad and reserved */
|
||||
/* address: fd01::3, fd01::2 */
|
||||
0x00, 0x03, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
/* UDP (ignored) */
|
||||
0x1f, 0x90, /* source port: 8080 */
|
||||
0x1f, 0x90, /* destination port: 8080 */
|
||||
0x00, 0x08, /* length: 8 */
|
||||
0xff, 0xff, /* checksum */
|
||||
};
|
||||
|
||||
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, data, sizeof(data), GNRC_NETTYPE_UNDEF);
|
||||
|
||||
gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt);
|
||||
|
||||
printf("pkt->users: %d\n", pkt->users);
|
||||
assert(pkt->users == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* we need a message queue for the thread running the shell in order to
|
||||
* receive potentially fast incoming networking packets */
|
||||
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
|
||||
puts("RIOT network stack example application");
|
||||
|
||||
_send_packet();
|
||||
|
||||
/* start shell */
|
||||
puts("All up, running the shell now");
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
|
||||
/* should be never reached */
|
||||
return 0;
|
||||
}
|
38
tests/gnrc_ipv6_ext/tests/01-run.py
Executable file
38
tests/gnrc_ipv6_ext/tests/01-run.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
|
||||
import testrunner
|
||||
|
||||
def testfunc(child):
|
||||
index = child.expect_exact([
|
||||
"ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)",
|
||||
"pkt->users: 0"
|
||||
])
|
||||
|
||||
if index == 1:
|
||||
# debug is disabled
|
||||
return
|
||||
|
||||
child.expect_exact("ipv6: handle extension header (nh = 0)")
|
||||
child.expect_exact("ipv6: handle extension header (nh = 43)")
|
||||
child.expect_exact("ipv6: Received (src = fd01::1, dst = fd01::3, next header = 0, length = 40)")
|
||||
child.expect_exact("ipv6: handle extension header (nh = 0)")
|
||||
child.expect_exact("ipv6: handle extension header (nh = 43)")
|
||||
child.expect_exact("ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)")
|
||||
child.expect_exact("ipv6: handle extension header (nh = 0)")
|
||||
child.expect_exact("ipv6: handle extension header (nh = 43)")
|
||||
child.expect_exact("ipv6: forward nh = 17 to other threads")
|
||||
child.expect_exact("pkt->users: 0")
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(testrunner.run(testfunc))
|
Loading…
Reference in New Issue
Block a user