mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
63cc5f6751
Two edge cases were previously missed: 1. Infinite should stay infinite. Prefixes with infinite lifetimes should be advertised as such. Likewise, should prefixes with infinite lifetimes be added to the prefix list as such 2. Prefixes with lifetimes > (UINT32_MAX - 1) / 1000 should have the longest possible lifetime below infinite (UINT32_MAX - 1) This fixes that.
1047 lines
35 KiB
C
1047 lines
35 KiB
C
/*
|
|
* Copyright (C) 2013 - 2014 INRIA.
|
|
* Copyright (C) 2015 Cenk Gündoğan <cnkgndgn@gmail.com>
|
|
*
|
|
* 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 <cnkgndgn@gmail.com>
|
|
*/
|
|
|
|
#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"
|
|
|
|
#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"
|
|
|
|
#if ENABLE_DEBUG
|
|
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
|
#endif
|
|
|
|
#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);
|
|
((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;
|
|
}
|
|
|
|
#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_pktsnip_t *pkt;
|
|
icmpv6_hdr_t *icmp;
|
|
gnrc_rpl_dis_t *dis;
|
|
|
|
/* TODO: Currently the DIS is too small so that wireshark complains about an incorrect
|
|
* ethernet frame check sequence. In order to prevent this, 4 PAD1 options are added.
|
|
* This will be addressed in follow-up PRs */
|
|
uint8_t padding[] = {
|
|
0x01, 0x02, 0x00, 0x00
|
|
};
|
|
|
|
int size = sizeof(icmpv6_hdr_t) + sizeof(gnrc_rpl_dis_t) + sizeof(padding);
|
|
|
|
if ((pkt = gnrc_icmpv6_build(NULL, ICMPV6_RPL_CTRL, GNRC_RPL_ICMPV6_CODE_DIS, size)) == NULL) {
|
|
DEBUG("RPL: Send DIS - no space left in packet buffer\n");
|
|
return;
|
|
}
|
|
|
|
icmp = (icmpv6_hdr_t *)pkt->data;
|
|
dis = (gnrc_rpl_dis_t *)(icmp + 1);
|
|
dis->flags = 0;
|
|
dis->reserved = 0;
|
|
|
|
/* TODO add padding may be removed if packet size grows */
|
|
memcpy((dis + 1), padding, sizeof(padding));
|
|
|
|
#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));
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* TODO handle Solicited Information Option */
|
|
(void)iface;
|
|
(void)dis;
|
|
(void)len;
|
|
|
|
#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) {
|
|
gnrc_rpl_instances[i].dodag.dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF;
|
|
gnrc_rpl_send_DIO(&gnrc_rpl_instances[i], src);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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(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_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 0x%" PRIx32 "\n",
|
|
ipv6_addr_to_str(addr_str, &(target->target), sizeof(addr_str)),
|
|
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_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_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);
|
|
return;
|
|
#else
|
|
DEBUG("RPL: DIO without DODAG_CONF option - use default trickle parameters\n");
|
|
gnrc_rpl_send_DIS(NULL, src);
|
|
#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 == 0) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|