mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
Koen Zandberg
0c790c6638
The code originally assumed that the location of DIS struct is directly after the ICMPv6 struct. This is not necessarily true when both structs are individually allocated by pktbuf. This commit fixes this issue by directly accessing the location of the DIS struct.
1147 lines
39 KiB
C
1147 lines
39 KiB
C
/*
|
||
* Copyright (C) 2018 HAW Hamburg
|
||
* Copyright (C) 2015–2017 Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
|
||
* Copyright (C) 2013–2014 INRIA.
|
||
*
|
||
* 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.
|
||
*/
|
||
|
||
/**
|
||
* @{
|
||
*
|
||
* @file
|
||
*
|
||
* @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
|
||
*/
|
||
|
||
#include "net/af.h"
|
||
#include "net/icmpv6.h"
|
||
#include "net/ipv6/hdr.h"
|
||
#include "net/gnrc/icmpv6.h"
|
||
#include "net/gnrc/ipv6.h"
|
||
#include "net/gnrc/netif/internal.h"
|
||
#include "net/gnrc.h"
|
||
#include "net/eui64.h"
|
||
#include "gnrc_rpl_internal/globals.h"
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
#include "gnrc_rpl_internal/netstats.h"
|
||
#endif
|
||
|
||
#include "net/gnrc/rpl.h"
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
#include "gnrc_rpl_internal/validation.h"
|
||
#endif
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
#include "net/gnrc/rpl/p2p_structs.h"
|
||
#include "net/gnrc/rpl/p2p_dodag.h"
|
||
#include "net/gnrc/rpl/p2p.h"
|
||
#endif
|
||
|
||
#define ENABLE_DEBUG (0)
|
||
#include "debug.h"
|
||
|
||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||
|
||
#define GNRC_RPL_GROUNDED_SHIFT (7)
|
||
#define GNRC_RPL_MOP_SHIFT (3)
|
||
#define GNRC_RPL_OPT_TRANSIT_E_FLAG_SHIFT (7)
|
||
#define GNRC_RPL_OPT_TRANSIT_E_FLAG (1 << GNRC_RPL_OPT_TRANSIT_E_FLAG_SHIFT)
|
||
#define GNRC_RPL_SHIFTED_MOP_MASK (0x7)
|
||
#define GNRC_RPL_PRF_MASK (0x7)
|
||
#define GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT (1 << 6)
|
||
|
||
static gnrc_netif_t *_find_interface_with_rpl_mcast(void)
|
||
{
|
||
gnrc_netif_t *netif = NULL;
|
||
|
||
while ((netif = gnrc_netif_iter(netif))) {
|
||
for (unsigned i = 0; i < GNRC_NETIF_IPV6_GROUPS_NUMOF; i++) {
|
||
if (ipv6_addr_equal(&netif->ipv6.groups[i], &ipv6_addr_all_rpl_nodes)) {
|
||
return netif;
|
||
}
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
void gnrc_rpl_send(gnrc_pktsnip_t *pkt, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst,
|
||
ipv6_addr_t *dodag_id)
|
||
{
|
||
gnrc_netif_t *netif;
|
||
|
||
(void)dodag_id;
|
||
gnrc_pktsnip_t *hdr;
|
||
if (iface == KERNEL_PID_UNDEF) {
|
||
netif = _find_interface_with_rpl_mcast();
|
||
|
||
if (netif == NULL) {
|
||
DEBUG("RPL: no suitable interface found for this destination address\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
iface = netif->pid;
|
||
}
|
||
else {
|
||
netif = gnrc_netif_get_by_pid(iface);
|
||
}
|
||
|
||
if (dst == NULL) {
|
||
dst = (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes;
|
||
}
|
||
|
||
if (src == NULL) {
|
||
src = gnrc_netif_ipv6_addr_best_src(netif, dst, true);
|
||
|
||
if (src == NULL) {
|
||
DEBUG("RPL: no suitable src address found\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
}
|
||
|
||
hdr = gnrc_ipv6_hdr_build(pkt, src, dst);
|
||
|
||
if (hdr == NULL) {
|
||
DEBUG("RPL: Send - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
|
||
pkt = hdr;
|
||
|
||
hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
|
||
if (hdr == NULL) {
|
||
DEBUG("RPL: Send - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
((gnrc_netif_hdr_t *)hdr->data)->if_pid = iface;
|
||
LL_PREPEND(pkt, hdr);
|
||
|
||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
|
||
DEBUG("RPL: cannot send packet: no subscribers found.\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
}
|
||
}
|
||
|
||
gnrc_pktsnip_t *_dio_dodag_conf_build(gnrc_pktsnip_t *pkt, gnrc_rpl_dodag_t *dodag)
|
||
{
|
||
gnrc_rpl_opt_dodag_conf_t *dodag_conf;
|
||
gnrc_pktsnip_t *opt_snip;
|
||
if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_opt_dodag_conf_t),
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: BUILD DODAG CONF - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
dodag_conf = opt_snip->data;
|
||
dodag_conf->type = GNRC_RPL_OPT_DODAG_CONF;
|
||
dodag_conf->length = GNRC_RPL_OPT_DODAG_CONF_LEN;
|
||
dodag_conf->flags_a_pcs = 0;
|
||
dodag_conf->dio_int_doubl = dodag->dio_interval_doubl;
|
||
dodag_conf->dio_int_min = dodag->dio_min;
|
||
dodag_conf->dio_redun = dodag->dio_redun;
|
||
dodag_conf->max_rank_inc = byteorder_htons(dodag->instance->max_rank_inc);
|
||
dodag_conf->min_hop_rank_inc = byteorder_htons(dodag->instance->min_hop_rank_inc);
|
||
dodag_conf->ocp = byteorder_htons(dodag->instance->of->ocp);
|
||
dodag_conf->reserved = 0;
|
||
dodag_conf->default_lifetime = dodag->default_lifetime;
|
||
dodag_conf->lifetime_unit = byteorder_htons(dodag->lifetime_unit);
|
||
return opt_snip;
|
||
}
|
||
|
||
gnrc_pktsnip_t *_dis_solicited_opt_build(gnrc_pktsnip_t *pkt, gnrc_rpl_internal_opt_dis_solicited_t *opt)
|
||
{
|
||
gnrc_pktsnip_t *opt_snip;
|
||
size_t snip_size = sizeof(gnrc_rpl_opt_dis_solicited_t);
|
||
|
||
if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, snip_size,
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: BUILD SOLICITED OPT - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
|
||
gnrc_rpl_opt_dis_solicited_t* solicited_information;
|
||
solicited_information = opt_snip->data;
|
||
|
||
solicited_information->type = GNRC_RPL_OPT_SOLICITED_INFO;
|
||
solicited_information->length = GNRC_RPL_DIS_SOLICITED_INFO_LENGTH;
|
||
solicited_information->instance_id = opt->instance_id;
|
||
|
||
solicited_information->VID_flags = opt->VID_flags;
|
||
solicited_information->dodag_id = opt->dodag_id;
|
||
solicited_information->version_number = opt->version_number;
|
||
|
||
return opt_snip;
|
||
}
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_PIO
|
||
static bool _get_pl_entry(unsigned iface, ipv6_addr_t *pfx,
|
||
unsigned pfx_len, gnrc_ipv6_nib_pl_t *ple)
|
||
{
|
||
void *state = NULL;
|
||
|
||
while (gnrc_ipv6_nib_pl_iter(iface, &state, ple)) {
|
||
if (ipv6_addr_match_prefix(&ple->pfx, pfx) >= pfx_len) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
gnrc_pktsnip_t *_dio_prefix_info_build(gnrc_pktsnip_t *pkt, gnrc_rpl_dodag_t *dodag)
|
||
{
|
||
gnrc_ipv6_nib_pl_t ple;
|
||
gnrc_rpl_opt_prefix_info_t *prefix_info;
|
||
gnrc_pktsnip_t *opt_snip;
|
||
|
||
if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_opt_prefix_info_t),
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: BUILD PREFIX INFO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
prefix_info = opt_snip->data;
|
||
prefix_info->type = GNRC_RPL_OPT_PREFIX_INFO;
|
||
prefix_info->length = GNRC_RPL_OPT_PREFIX_INFO_LEN;
|
||
/* auto-address configuration */
|
||
prefix_info->LAR_flags = GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT;
|
||
prefix_info->prefix_len = 64;
|
||
if (_get_pl_entry(dodag->iface, &dodag->dodag_id, prefix_info->prefix_len,
|
||
&ple)) {
|
||
uint32_t now = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX;
|
||
uint32_t valid_ltime = (ple.valid_until < UINT32_MAX) ?
|
||
(ple.valid_until - now) / MS_PER_SEC : UINT32_MAX;
|
||
uint32_t pref_ltime = (ple.pref_until < UINT32_MAX) ?
|
||
(ple.pref_until - now) / MS_PER_SEC : UINT32_MAX;
|
||
|
||
prefix_info->valid_lifetime = byteorder_htonl(valid_ltime);
|
||
prefix_info->pref_lifetime = byteorder_htonl(pref_ltime);
|
||
}
|
||
else {
|
||
DEBUG("RPL: Prefix of DODAG-ID not in prefix list\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
prefix_info->reserved = 0;
|
||
|
||
memset(&prefix_info->prefix, 0, sizeof(prefix_info->prefix));
|
||
ipv6_addr_init_prefix(&prefix_info->prefix, &dodag->dodag_id,
|
||
prefix_info->prefix_len);
|
||
return opt_snip;
|
||
}
|
||
#endif
|
||
|
||
void gnrc_rpl_send_DIO(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination)
|
||
{
|
||
if (inst == NULL) {
|
||
DEBUG("RPL: Error - trying to send DIO without being part of a dodag.\n");
|
||
return;
|
||
}
|
||
|
||
gnrc_rpl_dodag_t *dodag = &inst->dodag;
|
||
gnrc_pktsnip_t *pkt = NULL, *tmp;
|
||
gnrc_rpl_dio_t *dio;
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
gnrc_rpl_p2p_ext_t *p2p_ext = gnrc_rpl_p2p_ext_get(dodag);
|
||
if (dodag->instance->mop == GNRC_RPL_P2P_MOP) {
|
||
if (!p2p_ext->for_me) {
|
||
if ((pkt = gnrc_rpl_p2p_rdo_build(pkt, p2p_ext)) == NULL) {
|
||
return;
|
||
}
|
||
}
|
||
dodag->dio_opts &= ~GNRC_RPL_REQ_DIO_OPT_PREFIX_INFO;
|
||
}
|
||
#endif
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_PIO
|
||
if (dodag->dio_opts & GNRC_RPL_REQ_DIO_OPT_PREFIX_INFO) {
|
||
if ((pkt = _dio_prefix_info_build(pkt, dodag)) == NULL) {
|
||
return;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (dodag->dio_opts & GNRC_RPL_REQ_DIO_OPT_DODAG_CONF) {
|
||
if ((pkt = _dio_dodag_conf_build(pkt, dodag)) == NULL) {
|
||
return;
|
||
}
|
||
dodag->dio_opts &= ~GNRC_RPL_REQ_DIO_OPT_DODAG_CONF;
|
||
}
|
||
|
||
if ((tmp = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_dio_t), GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DIO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
dio = pkt->data;
|
||
dio->instance_id = inst->id;
|
||
dio->version_number = dodag->version;
|
||
/* a leaf node announces an INFINITE_RANK */
|
||
dio->rank = ((dodag->node_status == GNRC_RPL_LEAF_NODE) ?
|
||
byteorder_htons(GNRC_RPL_INFINITE_RANK) : byteorder_htons(dodag->my_rank));
|
||
dio->g_mop_prf = (dodag->grounded << GNRC_RPL_GROUNDED_SHIFT) |
|
||
(inst->mop << GNRC_RPL_MOP_SHIFT) | dodag->prf;
|
||
dio->dtsn = dodag->dtsn;
|
||
dio->flags = 0;
|
||
dio->reserved = 0;
|
||
dio->dodag_id = dodag->dodag_id;
|
||
|
||
if ((tmp = gnrc_icmpv6_build(pkt, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIO,
|
||
sizeof(icmpv6_hdr_t))) == NULL) {
|
||
DEBUG("RPL: Send DIO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_tx_DIO(&gnrc_rpl_netstats, gnrc_pkt_len(pkt),
|
||
(destination && !ipv6_addr_is_multicast(destination)));
|
||
#endif
|
||
|
||
gnrc_rpl_send(pkt, dodag->iface, NULL, destination, &dodag->dodag_id);
|
||
}
|
||
|
||
void gnrc_rpl_send_DIS(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination,
|
||
gnrc_rpl_internal_opt_t **options, size_t num_opts)
|
||
{
|
||
gnrc_pktsnip_t *pkt = NULL, *tmp;
|
||
gnrc_rpl_dis_t *dis;
|
||
|
||
/* No options provided to be attached to the DIS, so we PadN 2 bytes */
|
||
if (options == NULL || num_opts == 0) {
|
||
assert(!options);
|
||
gnrc_pktsnip_t *opt_snip;
|
||
size_t snip_size = 0;
|
||
/* The DIS is too small so that wireshark complains about an incorrect
|
||
* ethernet frame check sequence.
|
||
* To trick it we PadN 2 additional bytes, i.e. 4 bytes in sum. */
|
||
uint8_t padding[] = {
|
||
GNRC_RPL_OPT_PADN, /* Option Type */
|
||
0x02, /* Number of extra padding bytes */
|
||
0x00, 0x00
|
||
};
|
||
|
||
snip_size = sizeof(padding);
|
||
if ((opt_snip = gnrc_pktbuf_add(NULL, NULL, snip_size,
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: BUILD PadN OPT - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
memcpy(opt_snip->data, padding, snip_size);
|
||
pkt = opt_snip;
|
||
}
|
||
else {
|
||
assert(options);
|
||
for (size_t i = 0; i < num_opts; ++i) {
|
||
if (options[i]->type == GNRC_RPL_OPT_SOLICITED_INFO) {
|
||
if ((pkt = _dis_solicited_opt_build(pkt,
|
||
(gnrc_rpl_internal_opt_dis_solicited_t*)options[i])) == NULL) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if ((tmp = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_dis_t), GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DIS - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
dis = (gnrc_rpl_dis_t *)pkt->data;
|
||
dis->flags = 0;
|
||
dis->reserved = 0;
|
||
|
||
if ((tmp = gnrc_icmpv6_build(pkt, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIS,
|
||
sizeof(icmpv6_hdr_t))) == NULL) {
|
||
DEBUG("RPL: Send DIS - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_tx_DIS(&gnrc_rpl_netstats, gnrc_pkt_len(pkt),
|
||
(destination && !ipv6_addr_is_multicast(destination)));
|
||
#endif
|
||
|
||
gnrc_rpl_send(pkt, KERNEL_PID_UNDEF, NULL, destination, (inst? &(inst->dodag.dodag_id) : NULL));
|
||
}
|
||
|
||
static inline uint32_t _sec_to_ms(uint32_t sec)
|
||
{
|
||
if (sec == UINT32_MAX) {
|
||
/* infinite stays infinite */
|
||
return UINT32_MAX;
|
||
}
|
||
else if (sec > ((UINT32_MAX - 1) / MS_PER_SEC)) {
|
||
/* truncate long intervals to largest possible value */
|
||
return UINT32_MAX - 1;
|
||
}
|
||
else {
|
||
return sec * MS_PER_SEC;
|
||
}
|
||
}
|
||
|
||
/** @todo allow target prefixes in target options to be of variable length */
|
||
bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt_t *opt, uint16_t len,
|
||
ipv6_addr_t *src, uint32_t *included_opts)
|
||
{
|
||
uint16_t l = 0;
|
||
gnrc_rpl_opt_target_t *first_target = NULL;
|
||
gnrc_rpl_dodag_t *dodag = &inst->dodag;
|
||
eui64_t iid;
|
||
*included_opts = 0;
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
if (!gnrc_rpl_validation_options(msg_type, inst, opt, len)) {
|
||
return false;
|
||
}
|
||
#else
|
||
(void) msg_type;
|
||
#endif
|
||
|
||
while(l < len) {
|
||
switch(opt->type) {
|
||
case (GNRC_RPL_OPT_PAD1):
|
||
DEBUG("RPL: PAD1 option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_PAD1;
|
||
l += 1;
|
||
opt = (gnrc_rpl_opt_t *) (((uint8_t *) opt) + 1);
|
||
continue;
|
||
|
||
case (GNRC_RPL_OPT_PADN):
|
||
DEBUG("RPL: PADN option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_PADN;
|
||
break;
|
||
|
||
case (GNRC_RPL_OPT_DODAG_CONF):
|
||
DEBUG("RPL: DODAG CONF DIO option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_DODAG_CONF;
|
||
dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF;
|
||
gnrc_rpl_opt_dodag_conf_t *dc = (gnrc_rpl_opt_dodag_conf_t *) opt;
|
||
gnrc_rpl_of_t *of = gnrc_rpl_get_of_for_ocp(byteorder_ntohs(dc->ocp));
|
||
if (of != NULL) {
|
||
inst->of = of;
|
||
}
|
||
else {
|
||
DEBUG("RPL: Unsupported OCP 0x%02x\n", byteorder_ntohs(dc->ocp));
|
||
inst->of = gnrc_rpl_get_of_for_ocp(GNRC_RPL_DEFAULT_OCP);
|
||
}
|
||
dodag->dio_interval_doubl = dc->dio_int_doubl;
|
||
dodag->dio_min = dc->dio_int_min;
|
||
dodag->dio_redun = dc->dio_redun;
|
||
inst->max_rank_inc = byteorder_ntohs(dc->max_rank_inc);
|
||
inst->min_hop_rank_inc = byteorder_ntohs(dc->min_hop_rank_inc);
|
||
dodag->default_lifetime = dc->default_lifetime;
|
||
dodag->lifetime_unit = byteorder_ntohs(dc->lifetime_unit);
|
||
dodag->trickle.Imin = (1 << dodag->dio_min);
|
||
dodag->trickle.Imax = dodag->dio_interval_doubl;
|
||
dodag->trickle.k = dodag->dio_redun;
|
||
break;
|
||
|
||
case (GNRC_RPL_OPT_PREFIX_INFO):
|
||
DEBUG("RPL: Prefix Information DIO option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_PREFIX_INFO;
|
||
#ifndef GNRC_RPL_WITHOUT_PIO
|
||
dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_PREFIX_INFO;
|
||
#endif
|
||
gnrc_rpl_opt_prefix_info_t *pi = (gnrc_rpl_opt_prefix_info_t *) opt;
|
||
/* check for the auto address-configuration flag */
|
||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(dodag->iface);
|
||
|
||
assert(netif != NULL);
|
||
if ((gnrc_netif_ipv6_get_iid(netif, &iid) < 0)
|
||
&& !(pi->LAR_flags & GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT)) {
|
||
break;
|
||
}
|
||
ipv6_addr_set_aiid(&pi->prefix, iid.uint8);
|
||
/* TODO: find a way to do this with DAD (i.e. state != VALID) */
|
||
gnrc_netif_ipv6_addr_add_internal(netif, &pi->prefix, pi->prefix_len,
|
||
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID);
|
||
/* set lifetimes */
|
||
gnrc_ipv6_nib_pl_set(netif->pid, &pi->prefix, pi->prefix_len,
|
||
_sec_to_ms(byteorder_ntohl(pi->valid_lifetime)),
|
||
_sec_to_ms(byteorder_ntohl(pi->pref_lifetime)));
|
||
|
||
break;
|
||
case (GNRC_RPL_OPT_SOLICITED_INFO):
|
||
DEBUG("RPL: RPL SOLICITED INFO option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_SOLICITED_INFO;
|
||
gnrc_rpl_opt_dis_solicited_t* sol = (gnrc_rpl_opt_dis_solicited_t *) opt;
|
||
|
||
/* check expected length */
|
||
if (sol->length != GNRC_RPL_DIS_SOLICITED_INFO_LENGTH) {
|
||
DEBUG("RPL: RPL SOLICITED INFO option, unexpected length: %d\n", sol->length);
|
||
return false;
|
||
}
|
||
|
||
/* check the DODAG Version */
|
||
if ((sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_V)
|
||
&& (sol->version_number != inst->dodag.version)) {
|
||
DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: DODAG Version mismatch\n");
|
||
return false;
|
||
}
|
||
|
||
/* check the Instance ID */
|
||
if ((sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_I)
|
||
&& (sol->instance_id != inst->id)) {
|
||
DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: InstanceID mismatch\n");
|
||
return false;
|
||
}
|
||
|
||
/* check the DODAG ID */
|
||
if (sol->VID_flags & GNRC_RPL_DIS_SOLICITED_INFO_FLAG_D) {
|
||
if (memcmp(&sol->dodag_id, &inst->dodag.dodag_id, sizeof(ipv6_addr_t)) != 0) {
|
||
DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: DODAGID mismatch\n");
|
||
return false;
|
||
}
|
||
}
|
||
break;
|
||
case (GNRC_RPL_OPT_TARGET):
|
||
DEBUG("RPL: RPL TARGET DAO option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_TARGET;
|
||
|
||
gnrc_rpl_opt_target_t *target = (gnrc_rpl_opt_target_t *) opt;
|
||
if (first_target == NULL) {
|
||
first_target = target;
|
||
}
|
||
|
||
DEBUG("RPL: adding FT entry %s/%d\n",
|
||
ipv6_addr_to_str(addr_str, &(target->target), (unsigned)sizeof(addr_str)),
|
||
target->prefix_length);
|
||
|
||
gnrc_ipv6_nib_ft_del(&(target->target), target->prefix_length);
|
||
gnrc_ipv6_nib_ft_add(&(target->target), target->prefix_length, src,
|
||
dodag->iface,
|
||
dodag->default_lifetime * dodag->lifetime_unit);
|
||
break;
|
||
|
||
case (GNRC_RPL_OPT_TRANSIT):
|
||
DEBUG("RPL: RPL TRANSIT INFO DAO option parsed\n");
|
||
*included_opts |= ((uint32_t) 1) << GNRC_RPL_OPT_TRANSIT;
|
||
gnrc_rpl_opt_transit_t *transit = (gnrc_rpl_opt_transit_t *) opt;
|
||
if (first_target == NULL) {
|
||
DEBUG("RPL: Encountered a RPL TRANSIT DAO option without "
|
||
"a preceding RPL TARGET DAO option\n");
|
||
break;
|
||
}
|
||
|
||
do {
|
||
DEBUG("RPL: updating FT entry %s/%d\n",
|
||
ipv6_addr_to_str(addr_str, &(first_target->target), sizeof(addr_str)),
|
||
first_target->prefix_length);
|
||
|
||
gnrc_ipv6_nib_ft_del(&(first_target->target),
|
||
first_target->prefix_length);
|
||
gnrc_ipv6_nib_ft_add(&(first_target->target),
|
||
first_target->prefix_length, src,
|
||
dodag->iface,
|
||
transit->path_lifetime * dodag->lifetime_unit);
|
||
|
||
first_target = (gnrc_rpl_opt_target_t *) (((uint8_t *) (first_target)) +
|
||
sizeof(gnrc_rpl_opt_t) + first_target->length);
|
||
}
|
||
while (first_target->type == GNRC_RPL_OPT_TARGET);
|
||
|
||
first_target = NULL;
|
||
break;
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
case (GNRC_RPL_P2P_OPT_RDO):
|
||
gnrc_rpl_p2p_rdo_parse((gnrc_rpl_p2p_opt_rdo_t *) opt, gnrc_rpl_p2p_ext_get(dodag));
|
||
break;
|
||
#endif
|
||
}
|
||
l += opt->length + sizeof(gnrc_rpl_opt_t);
|
||
opt = (gnrc_rpl_opt_t *) (((uint8_t *) (opt + 1)) + opt->length);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
void gnrc_rpl_recv_DIS(gnrc_rpl_dis_t *dis, kernel_pid_t iface, ipv6_addr_t *src,
|
||
ipv6_addr_t *dst, uint16_t len)
|
||
{
|
||
(void)iface;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_rx_DIS(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst)));
|
||
#endif
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
if (!gnrc_rpl_validation_DIS(dis, len)) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if (ipv6_addr_is_multicast(dst)) {
|
||
for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) {
|
||
if ((gnrc_rpl_instances[i].state != 0)
|
||
/* a leaf node should only react to unicast DIS */
|
||
&& (gnrc_rpl_instances[i].dodag.node_status != GNRC_RPL_LEAF_NODE)) {
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
if (gnrc_rpl_instances[i].mop == GNRC_RPL_P2P_MOP) {
|
||
DEBUG("RPL: Not responding to DIS for P2P-RPL DODAG\n");
|
||
continue;
|
||
}
|
||
#endif
|
||
trickle_reset_timer(&(gnrc_rpl_instances[i].dodag.trickle));
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) {
|
||
if (gnrc_rpl_instances[i].state != 0) {
|
||
|
||
uint32_t included_opts = 0;
|
||
if(!_parse_options(GNRC_RPL_ICMPV6_CODE_DIS, &gnrc_rpl_instances[i],
|
||
(gnrc_rpl_opt_t *)(dis + 1), len, src, &included_opts)) {
|
||
DEBUG("RPL: DIS option parsing error - skip processing the DIS\n");
|
||
continue;
|
||
}
|
||
gnrc_rpl_instances[i].dodag.dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF;
|
||
gnrc_rpl_send_DIO(&gnrc_rpl_instances[i], src);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst,
|
||
uint16_t len)
|
||
{
|
||
(void) dst;
|
||
gnrc_rpl_instance_t *inst = NULL;
|
||
gnrc_rpl_dodag_t *dodag = NULL;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_rx_DIO(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst)));
|
||
#endif
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
if (!gnrc_rpl_validation_DIO(dio, len)) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
len -= (sizeof(gnrc_rpl_dio_t) + sizeof(icmpv6_hdr_t));
|
||
|
||
if (gnrc_rpl_instance_add(dio->instance_id, &inst)) {
|
||
/* new instance and DODAG */
|
||
gnrc_netif_t *netif;
|
||
|
||
if (byteorder_ntohs(dio->rank) == GNRC_RPL_INFINITE_RANK) {
|
||
DEBUG("RPL: ignore INFINITE_RANK DIO when we are not yet part of this DODAG\n");
|
||
gnrc_rpl_instance_remove(inst);
|
||
return;
|
||
}
|
||
|
||
inst->mop = (dio->g_mop_prf >> GNRC_RPL_MOP_SHIFT) & GNRC_RPL_SHIFTED_MOP_MASK;
|
||
inst->of = gnrc_rpl_get_of_for_ocp(GNRC_RPL_DEFAULT_OCP);
|
||
|
||
if (iface == KERNEL_PID_UNDEF) {
|
||
netif = _find_interface_with_rpl_mcast();
|
||
}
|
||
else {
|
||
netif = gnrc_netif_get_by_pid(iface);
|
||
}
|
||
assert(netif != NULL);
|
||
|
||
gnrc_rpl_dodag_init(inst, &dio->dodag_id, netif->pid);
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
DEBUG("RPL: Joined DODAG (%s).\n",
|
||
ipv6_addr_to_str(addr_str, &dio->dodag_id, sizeof(addr_str)));
|
||
|
||
gnrc_rpl_parent_t *parent = NULL;
|
||
|
||
if (!gnrc_rpl_parent_add_by_addr(dodag, src, &parent) && (parent == NULL)) {
|
||
DEBUG("RPL: Could not allocate new parent.\n");
|
||
gnrc_rpl_instance_remove(inst);
|
||
return;
|
||
}
|
||
|
||
dodag->version = dio->version_number;
|
||
dodag->grounded = dio->g_mop_prf >> GNRC_RPL_GROUNDED_SHIFT;
|
||
dodag->prf = dio->g_mop_prf & GNRC_RPL_PRF_MASK;
|
||
|
||
parent->rank = byteorder_ntohs(dio->rank);
|
||
|
||
uint32_t included_opts = 0;
|
||
if(!_parse_options(GNRC_RPL_ICMPV6_CODE_DIO, inst, (gnrc_rpl_opt_t *)(dio + 1), len,
|
||
src, &included_opts)) {
|
||
DEBUG("RPL: Error encountered during DIO option parsing - remove DODAG\n");
|
||
gnrc_rpl_instance_remove(inst);
|
||
return;
|
||
}
|
||
|
||
if (!(included_opts & (((uint32_t) 1) << GNRC_RPL_OPT_DODAG_CONF))) {
|
||
#ifndef GNRC_RPL_DODAG_CONF_OPTIONAL_ON_JOIN
|
||
DEBUG("RPL: DIO without DODAG_CONF option - remove DODAG and request new DIO\n");
|
||
gnrc_rpl_instance_remove(inst);
|
||
gnrc_rpl_send_DIS(NULL, src, NULL, 0);
|
||
return;
|
||
#else
|
||
DEBUG("RPL: DIO without DODAG_CONF option - use default trickle parameters\n");
|
||
gnrc_rpl_send_DIS(NULL, src, NULL, 0);
|
||
#endif
|
||
}
|
||
|
||
/* if there was no address created manually or by a PIO on the interface,
|
||
* leave this DODAG */
|
||
if (gnrc_netif_ipv6_addr_match(netif, &dodag->dodag_id) < 0) {
|
||
DEBUG("RPL: no IPv6 address configured on interface %i to match the "
|
||
"given dodag id: %s\n", netif->pid,
|
||
ipv6_addr_to_str(addr_str, &(dodag->dodag_id), sizeof(addr_str)));
|
||
gnrc_rpl_instance_remove(inst);
|
||
return;
|
||
}
|
||
|
||
gnrc_rpl_delay_dao(dodag);
|
||
trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG,
|
||
(1 << dodag->dio_min), dodag->dio_interval_doubl,
|
||
dodag->dio_redun);
|
||
|
||
gnrc_rpl_parent_update(dodag, parent);
|
||
return;
|
||
}
|
||
else if (inst == NULL) {
|
||
DEBUG("RPL: Could not allocate a new instance.\n");
|
||
return;
|
||
}
|
||
else {
|
||
/* instance exists already */
|
||
/* ignore dodags with other dodag_id's for now */
|
||
/* TODO: choose DODAG with better rank */
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
if (memcmp(&dodag->dodag_id, &dio->dodag_id, sizeof(ipv6_addr_t)) != 0) {
|
||
DEBUG("RPL: DIO received from another DODAG, but same instance - ignore\n");
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (inst->mop != ((dio->g_mop_prf >> GNRC_RPL_MOP_SHIFT) & GNRC_RPL_SHIFTED_MOP_MASK)) {
|
||
DEBUG("RPL: invalid MOP for this instance.\n");
|
||
return;
|
||
}
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
gnrc_rpl_p2p_ext_t *p2p_ext = gnrc_rpl_p2p_ext_get(dodag);
|
||
if ((dodag->instance->mop == GNRC_RPL_P2P_MOP) && (p2p_ext->lifetime_sec <= 0)) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if (GNRC_RPL_COUNTER_GREATER_THAN(dio->version_number, dodag->version)) {
|
||
if (dodag->node_status == GNRC_RPL_ROOT_NODE) {
|
||
dodag->version = GNRC_RPL_COUNTER_INCREMENT(dio->version_number);
|
||
trickle_reset_timer(&dodag->trickle);
|
||
}
|
||
else {
|
||
dodag->version = dio->version_number;
|
||
gnrc_rpl_local_repair(dodag);
|
||
}
|
||
}
|
||
else if (GNRC_RPL_COUNTER_GREATER_THAN(dodag->version, dio->version_number)) {
|
||
trickle_reset_timer(&dodag->trickle);
|
||
return;
|
||
}
|
||
|
||
if (dodag->node_status == GNRC_RPL_ROOT_NODE) {
|
||
if (byteorder_ntohs(dio->rank) != GNRC_RPL_INFINITE_RANK) {
|
||
trickle_increment_counter(&dodag->trickle);
|
||
}
|
||
else {
|
||
trickle_reset_timer(&dodag->trickle);
|
||
}
|
||
return;
|
||
}
|
||
|
||
gnrc_rpl_parent_t *parent = NULL;
|
||
|
||
if (!gnrc_rpl_parent_add_by_addr(dodag, src, &parent) && (parent == NULL)) {
|
||
DEBUG("RPL: Could not allocate new parent.\n");
|
||
return;
|
||
}
|
||
else if (parent != NULL) {
|
||
trickle_increment_counter(&dodag->trickle);
|
||
}
|
||
|
||
/* gnrc_rpl_parent_add_by_addr should have set this already */
|
||
assert(parent != NULL);
|
||
|
||
parent->rank = byteorder_ntohs(dio->rank);
|
||
|
||
gnrc_rpl_parent_update(dodag, parent);
|
||
|
||
/* sender of incoming DIO is not a parent of mine (anymore) and has an INFINITE rank
|
||
and I have a rank != INFINITE_RANK */
|
||
if (parent->state == GNRC_RPL_PARENT_UNUSED) {
|
||
if ((byteorder_ntohs(dio->rank) == GNRC_RPL_INFINITE_RANK)
|
||
&& (dodag->my_rank != GNRC_RPL_INFINITE_RANK)) {
|
||
trickle_reset_timer(&dodag->trickle);
|
||
return;
|
||
}
|
||
}
|
||
/* incoming DIO is from pref. parent */
|
||
else if (parent == dodag->parents) {
|
||
if (parent->dtsn != dio->dtsn) {
|
||
gnrc_rpl_delay_dao(dodag);
|
||
}
|
||
parent->dtsn = dio->dtsn;
|
||
dodag->grounded = dio->g_mop_prf >> GNRC_RPL_GROUNDED_SHIFT;
|
||
dodag->prf = dio->g_mop_prf & GNRC_RPL_PRF_MASK;
|
||
uint32_t included_opts = 0;
|
||
if(!_parse_options(GNRC_RPL_ICMPV6_CODE_DIO, inst, (gnrc_rpl_opt_t *)(dio + 1), len,
|
||
src, &included_opts)) {
|
||
DEBUG("RPL: Error encountered during DIO option parsing - remove DODAG\n");
|
||
gnrc_rpl_instance_remove(inst);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
gnrc_pktsnip_t *_dao_target_build(gnrc_pktsnip_t *pkt, ipv6_addr_t *addr, uint8_t prefix_length)
|
||
{
|
||
gnrc_rpl_opt_target_t *target;
|
||
gnrc_pktsnip_t *opt_snip;
|
||
if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_opt_target_t),
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
target = opt_snip->data;
|
||
target->type = GNRC_RPL_OPT_TARGET;
|
||
target->length = sizeof(target->flags) + sizeof(target->prefix_length) + sizeof(target->target);
|
||
target->flags = 0;
|
||
target->prefix_length = prefix_length;
|
||
target->target = *addr;
|
||
return opt_snip;
|
||
}
|
||
|
||
gnrc_pktsnip_t *_dao_transit_build(gnrc_pktsnip_t *pkt, uint8_t lifetime, bool external)
|
||
{
|
||
gnrc_rpl_opt_transit_t *transit;
|
||
gnrc_pktsnip_t *opt_snip;
|
||
if ((opt_snip = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_opt_transit_t),
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return NULL;
|
||
}
|
||
transit = opt_snip->data;
|
||
transit->type = GNRC_RPL_OPT_TRANSIT;
|
||
transit->length = sizeof(transit->e_flags) + sizeof(transit->path_control) +
|
||
sizeof(transit->path_sequence) + sizeof(transit->path_lifetime);
|
||
transit->e_flags = (external) << GNRC_RPL_OPT_TRANSIT_E_FLAG_SHIFT;
|
||
transit->path_control = 0;
|
||
transit->path_sequence = 0;
|
||
transit->path_lifetime = lifetime;
|
||
return opt_snip;
|
||
}
|
||
|
||
void gnrc_rpl_send_DAO(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination, uint8_t lifetime)
|
||
{
|
||
gnrc_rpl_dodag_t *dodag;
|
||
|
||
if (inst == NULL) {
|
||
DEBUG("RPL: Error - trying to send DAO without being part of a dodag.\n");
|
||
return;
|
||
}
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
if (dodag->node_status == GNRC_RPL_ROOT_NODE) {
|
||
return;
|
||
}
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
if (dodag->instance->mop == GNRC_RPL_P2P_MOP) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if (destination == NULL) {
|
||
if (dodag->parents == NULL) {
|
||
DEBUG("RPL: dodag has no preferred parent\n");
|
||
return;
|
||
}
|
||
|
||
destination = &(dodag->parents->addr);
|
||
}
|
||
|
||
gnrc_pktsnip_t *pkt = NULL, *tmp = NULL;
|
||
gnrc_rpl_dao_t *dao;
|
||
|
||
/* find my address */
|
||
ipv6_addr_t *me = NULL;
|
||
gnrc_netif_t *netif = gnrc_netif_get_by_prefix(&dodag->dodag_id);
|
||
int idx;
|
||
|
||
if (netif == NULL) {
|
||
DEBUG("RPL: no address configured\n");
|
||
return;
|
||
}
|
||
idx = gnrc_netif_ipv6_addr_match(netif, &dodag->dodag_id);
|
||
me = &netif->ipv6.addrs[idx];
|
||
|
||
/* add external and RPL FT entries */
|
||
/* TODO: nib: dropped support for external transit options for now */
|
||
void *ft_state = NULL;
|
||
gnrc_ipv6_nib_ft_t fte;
|
||
while(gnrc_ipv6_nib_ft_iter(NULL, dodag->iface, &ft_state, &fte)) {
|
||
DEBUG("RPL: Send DAO - building transit option\n");
|
||
|
||
if ((pkt = _dao_transit_build(pkt, lifetime, false)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
return;
|
||
}
|
||
if (ipv6_addr_is_global(&fte.dst) &&
|
||
!ipv6_addr_is_unspecified(&fte.next_hop)) {
|
||
DEBUG("RPL: Send DAO - building target %s/%d\n",
|
||
ipv6_addr_to_str(addr_str, &fte.dst, sizeof(addr_str)), fte.dst_len);
|
||
|
||
if ((pkt = _dao_target_build(pkt, &fte.dst, fte.dst_len)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* add own address */
|
||
DEBUG("RPL: Send DAO - building target %s/128\n",
|
||
ipv6_addr_to_str(addr_str, me, sizeof(addr_str)));
|
||
if ((pkt = _dao_target_build(pkt, me, IPV6_ADDR_BIT_LEN)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
return;
|
||
}
|
||
|
||
bool local_instance = (inst->id & GNRC_RPL_INSTANCE_ID_MSB) ? true : false;
|
||
|
||
if (local_instance) {
|
||
if ((tmp = gnrc_pktbuf_add(pkt, &dodag->dodag_id, sizeof(ipv6_addr_t),
|
||
GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
}
|
||
|
||
if ((tmp = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_rpl_dao_t), GNRC_NETTYPE_UNDEF)) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
dao = pkt->data;
|
||
dao->instance_id = inst->id;
|
||
if (local_instance) {
|
||
/* set the D flag to indicate that a DODAG id is present */
|
||
dao->k_d_flags = GNRC_RPL_DAO_D_BIT;
|
||
}
|
||
else {
|
||
dao->k_d_flags = 0;
|
||
}
|
||
|
||
/* set the K flag to indicate that ACKs are required */
|
||
dao->k_d_flags |= GNRC_RPL_DAO_K_BIT;
|
||
dao->dao_sequence = dodag->dao_seq;
|
||
dao->reserved = 0;
|
||
|
||
if ((tmp = gnrc_icmpv6_build(pkt, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DAO,
|
||
sizeof(icmpv6_hdr_t))) == NULL) {
|
||
DEBUG("RPL: Send DAO - no space left in packet buffer\n");
|
||
gnrc_pktbuf_release(pkt);
|
||
return;
|
||
}
|
||
pkt = tmp;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_tx_DAO(&gnrc_rpl_netstats, gnrc_pkt_len(pkt),
|
||
(destination && !ipv6_addr_is_multicast(destination)));
|
||
#endif
|
||
|
||
gnrc_rpl_send(pkt, dodag->iface, NULL, destination, &dodag->dodag_id);
|
||
|
||
GNRC_RPL_COUNTER_INCREMENT(dodag->dao_seq);
|
||
}
|
||
|
||
void gnrc_rpl_send_DAO_ACK(gnrc_rpl_instance_t *inst, ipv6_addr_t *destination, uint8_t seq)
|
||
{
|
||
gnrc_rpl_dodag_t *dodag = NULL;
|
||
|
||
if (inst == NULL) {
|
||
DEBUG("RPL: Error - trying to send DAO-ACK without being part of a dodag.\n");
|
||
return;
|
||
}
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
gnrc_pktsnip_t *pkt;
|
||
icmpv6_hdr_t *icmp;
|
||
gnrc_rpl_dao_ack_t *dao_ack;
|
||
int size = sizeof(icmpv6_hdr_t) + sizeof(gnrc_rpl_dao_ack_t);
|
||
bool local_instance = (inst->id & GNRC_RPL_INSTANCE_ID_MSB) ? true : false;
|
||
|
||
if (local_instance) {
|
||
size += sizeof(ipv6_addr_t);
|
||
}
|
||
|
||
if ((pkt = gnrc_icmpv6_build(NULL, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DAO_ACK, size)) == NULL) {
|
||
DEBUG("RPL: Send DAOACK - no space left in packet buffer\n");
|
||
return;
|
||
}
|
||
|
||
icmp = (icmpv6_hdr_t *)pkt->data;
|
||
dao_ack = (gnrc_rpl_dao_ack_t *)(icmp + 1);
|
||
|
||
dao_ack->instance_id = inst->id;
|
||
if (local_instance) {
|
||
/* set the D flag to indicate that a DODAG id is present */
|
||
dao_ack->d_reserved = GNRC_RPL_DAO_ACK_D_BIT;
|
||
memcpy((dao_ack + 1), &dodag->dodag_id, sizeof(ipv6_addr_t));
|
||
}
|
||
else {
|
||
dao_ack->d_reserved = 0;
|
||
}
|
||
|
||
dao_ack->dao_sequence = seq;
|
||
dao_ack->status = 0;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_tx_DAO_ACK(&gnrc_rpl_netstats, gnrc_pkt_len(pkt),
|
||
(destination && !ipv6_addr_is_multicast(destination)));
|
||
#endif
|
||
|
||
gnrc_rpl_send(pkt, dodag->iface, NULL, destination, &dodag->dodag_id);
|
||
}
|
||
|
||
void gnrc_rpl_recv_DAO(gnrc_rpl_dao_t *dao, kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst,
|
||
uint16_t len)
|
||
{
|
||
(void)iface;
|
||
(void)dst;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_rx_DAO(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst)));
|
||
#endif
|
||
|
||
gnrc_rpl_instance_t *inst = NULL;
|
||
gnrc_rpl_dodag_t *dodag = NULL;
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
if (!gnrc_rpl_validation_DAO(dao, len)) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
gnrc_rpl_opt_t *opts = (gnrc_rpl_opt_t *) (dao + 1);
|
||
|
||
if ((inst = gnrc_rpl_instance_get(dao->instance_id)) == NULL) {
|
||
DEBUG("RPL: DAO with unknown instance id (%d) received\n", dao->instance_id);
|
||
return;
|
||
}
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
len -= (sizeof(gnrc_rpl_dao_t) + sizeof(icmpv6_hdr_t));
|
||
|
||
/* check if the D flag is set before accessing the DODAG id */
|
||
if ((dao->k_d_flags & GNRC_RPL_DAO_D_BIT)) {
|
||
if (memcmp(&dodag->dodag_id, (ipv6_addr_t *)(dao + 1), sizeof(ipv6_addr_t)) != 0) {
|
||
DEBUG("RPL: DAO with unknown DODAG id (%s)\n", ipv6_addr_to_str(addr_str,
|
||
(ipv6_addr_t *)(dao + 1), sizeof(addr_str)));
|
||
return;
|
||
}
|
||
opts = (gnrc_rpl_opt_t *)(((uint8_t *) opts) + sizeof(ipv6_addr_t));
|
||
len -= sizeof(ipv6_addr_t);
|
||
}
|
||
|
||
/* a leaf node should not parse DAOs */
|
||
if (dodag->node_status == GNRC_RPL_LEAF_NODE) {
|
||
return;
|
||
}
|
||
|
||
#ifdef MODULE_GNRC_RPL_P2P
|
||
if (dodag->instance->mop == GNRC_RPL_P2P_MOP) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
uint32_t included_opts = 0;
|
||
if(!_parse_options(GNRC_RPL_ICMPV6_CODE_DAO, inst, opts, len, src, &included_opts)) {
|
||
DEBUG("RPL: Error encountered during DAO option parsing - ignore DAO\n");
|
||
return;
|
||
}
|
||
|
||
/* send a DAO-ACK if K flag is set */
|
||
if (dao->k_d_flags & GNRC_RPL_DAO_K_BIT) {
|
||
gnrc_rpl_send_DAO_ACK(inst, src, dao->dao_sequence);
|
||
}
|
||
|
||
gnrc_rpl_delay_dao(dodag);
|
||
}
|
||
|
||
void gnrc_rpl_recv_DAO_ACK(gnrc_rpl_dao_ack_t *dao_ack, kernel_pid_t iface, ipv6_addr_t *src,
|
||
ipv6_addr_t *dst, uint16_t len)
|
||
{
|
||
(void)iface;
|
||
(void)src;
|
||
(void)dst;
|
||
(void)len;
|
||
|
||
gnrc_rpl_instance_t *inst = NULL;
|
||
gnrc_rpl_dodag_t *dodag = NULL;
|
||
|
||
#ifdef MODULE_NETSTATS_RPL
|
||
gnrc_rpl_netstats_rx_DAO_ACK(&gnrc_rpl_netstats, len, (dst && !ipv6_addr_is_multicast(dst)));
|
||
#endif
|
||
|
||
#ifndef GNRC_RPL_WITHOUT_VALIDATION
|
||
if (!gnrc_rpl_validation_DAO_ACK(dao_ack, len, dst)) {
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if ((inst = gnrc_rpl_instance_get(dao_ack->instance_id)) == NULL) {
|
||
DEBUG("RPL: DAO-ACK with unknown instance id (%d) received\n", dao_ack->instance_id);
|
||
return;
|
||
}
|
||
|
||
dodag = &inst->dodag;
|
||
|
||
/* check if the D flag is set before accessing the DODAG id */
|
||
if ((dao_ack->d_reserved & GNRC_RPL_DAO_ACK_D_BIT)) {
|
||
if (memcmp(&dodag->dodag_id, (ipv6_addr_t *)(dao_ack + 1), sizeof(ipv6_addr_t)) != 0) {
|
||
DEBUG("RPL: DAO-ACK with unknown DODAG id (%s)\n", ipv6_addr_to_str(addr_str,
|
||
(ipv6_addr_t *)(dao_ack + 1), sizeof(addr_str)));
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ((dao_ack->status != 0) && (dao_ack->dao_sequence != dodag->dao_seq)) {
|
||
DEBUG("RPL: DAO-ACK sequence (%d) does not match expected sequence (%d)\n",
|
||
dao_ack->dao_sequence, dodag->dao_seq);
|
||
return;
|
||
}
|
||
|
||
dodag->dao_ack_received = true;
|
||
gnrc_rpl_long_delay_dao(dodag);
|
||
}
|
||
|
||
/**
|
||
* @}
|
||
*/
|