static gnrc_rpl_dodag_t *_root_dodag_init(uint8_t instance_id, ipv6_addr_t *dodag_id, uint8_t mop) { if (gnrc_rpl_pid == KERNEL_PID_UNDEF) { DEBUG("RPL: RPL thread not started\n"); return NULL; } ipv6_addr_t *configured_addr; gnrc_ipv6_netif_addr_t *netif_addr = NULL; gnrc_rpl_instance_t *inst = NULL; gnrc_rpl_dodag_t *dodag = NULL; if (instance_id == 0) { DEBUG("RPL: instance id (%d) must be a positive number greater than zero\n", instance_id); return NULL; } if (gnrc_ipv6_netif_find_by_addr(&configured_addr, dodag_id) == KERNEL_PID_UNDEF) { DEBUG("RPL: no IPv6 address configured to match the given dodag id: %s\n", ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); return NULL; } if ((netif_addr = gnrc_ipv6_netif_addr_get(configured_addr)) == NULL) { DEBUG("RPL: no netif address found for %s\n", ipv6_addr_to_str(addr_str, configured_addr, sizeof(addr_str))); return NULL; } if (gnrc_rpl_instance_add(instance_id, &inst)) { inst->of = (gnrc_rpl_of_t *) gnrc_rpl_get_of_for_ocp(GNRC_RPL_DEFAULT_OCP); inst->mop = mop; inst->min_hop_rank_inc = GNRC_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; inst->max_rank_inc = GNRC_RPL_DEFAULT_MAX_RANK_INCREASE; } else if (inst == NULL) { DEBUG("RPL: could not allocate memory for a new instance with id %d", instance_id); return NULL; } else if (inst->mop != mop) { DEBUG("RPL: instance (%d) exists with another MOP", instance_id); return NULL; } if (!gnrc_rpl_dodag_add(inst, dodag_id, &dodag)) { DEBUG("RPL: DODAG with id %s exists or no memory left for a new DODAG", ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); return NULL; } dodag->prefix_len = netif_addr->prefix_len; dodag->addr_preferred = netif_addr->preferred; dodag->addr_valid = netif_addr->valid; return dodag; }
void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src, uint16_t len) { gnrc_rpl_instance_t *inst = NULL; gnrc_rpl_dodag_t *dodag = NULL; if (!_gnrc_rpl_check_DIO_validity(dio, len)) { return; } len -= (sizeof(gnrc_rpl_dio_t) + sizeof(icmpv6_hdr_t)); if (gnrc_rpl_instance_add(dio->instance_id, &inst)) { /* new instance and DODAG */ 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) { iface = gnrc_ipv6_netif_find_by_addr(NULL, &ipv6_addr_all_rpl_nodes); assert(iface != KERNEL_PID_UNDEF); } gnrc_rpl_dodag_init(inst, &dio->dodag_id, iface, NULL); 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 netif_addr created manually or by a PIO, then leave this DODAG */ if (!dodag->netif_addr) { ipv6_addr_t *configured_addr; if (!(configured_addr = gnrc_ipv6_netif_match_prefix(dodag->iface, &dodag->dodag_id))) { DEBUG("RPL: no IPv6 address configured to match the given dodag id: %s\n", ipv6_addr_to_str(addr_str, &(dodag->dodag_id), sizeof(addr_str))); gnrc_rpl_instance_remove(inst); return; } if (!(dodag->netif_addr = gnrc_ipv6_netif_addr_get(configured_addr))) { DEBUG("RPL: no netif address found for %s\n", ipv6_addr_to_str(addr_str, configured_addr, 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_INTERVAL, GNRC_RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << dodag->dio_min), dodag->dio_interval_doubl, dodag->dio_redun); gnrc_rpl_parent_update(dodag, parent); return; } else if (inst == NULL) {
/** @todo allow target prefixes in target options to be of variable length */ bool _parse_options(int msg_type, gnrc_rpl_dodag_t *dodag, gnrc_rpl_opt_t *opt, uint16_t len, ipv6_addr_t *src) { uint16_t l = 0; gnrc_rpl_opt_target_t *first_target = NULL; eui64_t iid; kernel_pid_t if_id = KERNEL_PID_UNDEF; if (!_gnrc_rpl_check_options_validity(msg_type, dodag, opt, len)) { return false; } while(l < len) { switch(opt->type) { case (GNRC_RPL_OPT_PAD1): DEBUG("RPL: PAD1 option parsed\n"); l += 1; opt = (gnrc_rpl_opt_t *) (((uint8_t *) opt) + 1); continue; case (GNRC_RPL_OPT_PADN): DEBUG("RPL: PADN option parsed\n"); break; case (GNRC_RPL_OPT_DODAG_CONF): DEBUG("RPL: DODAG CONF DIO option parsed\n"); 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) { dodag->instance->of = of; } else { DEBUG("RPL: Unsupported OCP 0x%02x\n", byteorder_ntohs(dc->ocp)); dodag->instance->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; dodag->instance->max_rank_inc = byteorder_ntohs(dc->max_rank_inc); dodag->instance->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"); gnrc_rpl_opt_prefix_info_t *pi = (gnrc_rpl_opt_prefix_info_t *) opt; ipv6_addr_t all_RPL_nodes = GNRC_RPL_ALL_NODES_ADDR; if_id = gnrc_ipv6_netif_find_by_addr(NULL, &all_RPL_nodes); /* check for the auto address-configuration flag */ if ((gnrc_netapi_get(if_id, NETOPT_IPV6_IID, 0, &iid, sizeof(eui64_t)) < 0) && !(pi->LAR_flags & GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT)) { break; } ipv6_addr_set_aiid(&pi->prefix, iid.uint8); gnrc_ipv6_netif_add_addr(if_id, &pi->prefix, pi->prefix_len, 0); break; case (GNRC_RPL_OPT_TARGET): DEBUG("RPL: RPL TARGET DAO option parsed\n"); if_id = gnrc_ipv6_netif_find_by_prefix(NULL, &dodag->dodag_id); if (if_id == KERNEL_PID_UNDEF) { DEBUG("RPL: no interface found for the configured DODAG id\n"); return false; } gnrc_rpl_opt_target_t *target = (gnrc_rpl_opt_target_t *) opt; if (first_target == NULL) { first_target = target; } fib_add_entry(gnrc_ipv6_fib_table, if_id, target->target.u8, sizeof(ipv6_addr_t), AF_INET6, src->u8, sizeof(ipv6_addr_t), AF_INET6, (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS); break; case (GNRC_RPL_OPT_TRANSIT): DEBUG("RPL: RPL TRANSIT INFO DAO option parsed\n"); 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 { fib_update_entry(gnrc_ipv6_fib_table, first_target->target.u8, sizeof(ipv6_addr_t), src->u8, sizeof(ipv6_addr_t), AF_INET6, (transit->path_lifetime * dodag->lifetime_unit * SEC_IN_MS)); 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; } l += opt->length + sizeof(gnrc_rpl_opt_t); opt = (gnrc_rpl_opt_t *) (((uint8_t *) (opt + 1)) + opt->length); }
/** @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; ipv6_addr_t *me; if (!_gnrc_rpl_check_options_validity(msg_type, inst, opt, len)) { return false; } 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 */ if ((gnrc_netapi_get(dodag->iface, NETOPT_IPV6_IID, 0, &iid, sizeof(eui64_t)) < 0) && !(pi->LAR_flags & GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT)) { break; } ipv6_addr_set_aiid(&pi->prefix, iid.uint8); me = gnrc_ipv6_netif_add_addr(dodag->iface, &pi->prefix, pi->prefix_len, 0); if (me) { dodag->netif_addr = gnrc_ipv6_netif_addr_get(me); } 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; } uint32_t fib_dst_flags = 0; if (target->prefix_length < IPV6_ADDR_BIT_LEN) { fib_dst_flags = ((uint32_t)(target->prefix_length) << FIB_FLAG_NET_PREFIX_SHIFT); } DEBUG("RPL: adding fib entry %s/%d 0x%" PRIx32 "\n", ipv6_addr_to_str(addr_str, &(target->target), sizeof(addr_str)), target->prefix_length, fib_dst_flags); fib_add_entry(&gnrc_ipv6_fib_table, dodag->iface, target->target.u8, sizeof(ipv6_addr_t), fib_dst_flags, src->u8, sizeof(ipv6_addr_t), FIB_FLAG_RPL_ROUTE, (dodag->default_lifetime * dodag->lifetime_unit) * SEC_IN_MS); 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 fib entry %s/%d\n", ipv6_addr_to_str(addr_str, &(first_target->target), sizeof(addr_str)), first_target->prefix_length); fib_update_entry(&gnrc_ipv6_fib_table, first_target->target.u8, sizeof(ipv6_addr_t), src->u8, sizeof(ipv6_addr_t), ((transit->e_flags & GNRC_RPL_OPT_TRANSIT_E_FLAG) ? 0x0 : FIB_FLAG_RPL_ROUTE), (transit->path_lifetime * dodag->lifetime_unit * SEC_IN_MS)); 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; }