diff --git a/sys/include/net/gnrc/rpl.h b/sys/include/net/gnrc/rpl.h index bfba745e07fa..f1c7e4d5a8bc 100644 --- a/sys/include/net/gnrc/rpl.h +++ b/sys/include/net/gnrc/rpl.h @@ -446,11 +446,27 @@ static inline bool GNRC_RPL_COUNTER_GREATER_THAN(uint8_t A, uint8_t B) #define GNRC_RPL_OPT_DODAG_CONF (4) #define GNRC_RPL_OPT_TARGET (5) #define GNRC_RPL_OPT_TRANSIT (6) +#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_OPT_SOLICITED_INFO (7) #define GNRC_RPL_OPT_PREFIX_INFO (8) +#define GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT (1 << 6) #define GNRC_RPL_OPT_TARGET_DESC (9) /** @} */ +/** + * @name RPL DIO Base Object fields + * @see + * DODAG Information Object (DIO) + * + * @{ + */ +#define GNRC_RPL_GROUNDED_SHIFT (7) +#define GNRC_RPL_MOP_SHIFT (3) +#define GNRC_RPL_SHIFTED_MOP_MASK (0x7) +#define GNRC_RPL_PRF_MASK (0x7) + + /** * @brief Rank of the root node */ diff --git a/sys/include/net/gnrc/rpl/dodag.h b/sys/include/net/gnrc/rpl/dodag.h index 69a209fbd767..e23c543748d3 100644 --- a/sys/include/net/gnrc/rpl/dodag.h +++ b/sys/include/net/gnrc/rpl/dodag.h @@ -76,15 +76,19 @@ bool gnrc_rpl_instance_add(uint8_t instance_id, gnrc_rpl_instance_t **inst); */ bool gnrc_rpl_instance_remove_by_id(uint8_t instance_id); +/** + * @brief Remove a RPL DODAG with the pointer @p dodag from its instance. + * + * @param[in] dodag Pointer to the RPL DODAG to remove. + */ +void gnrc_rpl_dodag_remove(gnrc_rpl_dodag_t *dodag); + /** * @brief Remove a RPL instance with the pointer @p inst. * * @param[in] inst Pointer to the RPL instance to remove. - * - * @return true, on success. - * @return false, otherwise. */ -bool gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst); +void gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst); /** * @brief Get the RPL instance with the id @p instance_id. diff --git a/sys/include/net/gnrc/rpl/structs.h b/sys/include/net/gnrc/rpl/structs.h index cc6c86359a70..fc80056834eb 100644 --- a/sys/include/net/gnrc/rpl/structs.h +++ b/sys/include/net/gnrc/rpl/structs.h @@ -286,7 +286,7 @@ typedef struct { * @return Negative, if the first parent is preferred. */ int (*parent_cmp)(gnrc_rpl_parent_t *parent1, gnrc_rpl_parent_t *parent2); - gnrc_rpl_dodag_t *(*which_dodag)(gnrc_rpl_dodag_t *, gnrc_rpl_dodag_t *); /**< compare for dodags */ + int (*which_dodag)(gnrc_rpl_dodag_t *, gnrc_rpl_dio_t *); /**< compare for dodags */ /** * @brief Reset the state of the objective function. diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c index 52dd845935d4..c2ab0c7f3f63 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c @@ -35,6 +35,7 @@ #include "net/gnrc.h" #include "net/eui64.h" #include "gnrc_rpl_internal/globals.h" +#include "of0.h" #ifdef MODULE_NETSTATS_RPL #include "gnrc_rpl_internal/netstats.h" @@ -49,19 +50,11 @@ #include "net/gnrc/rpl/p2p.h" #endif -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG 1 #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) - /** * @brief Checks validity of DIO control messages * @@ -628,7 +621,7 @@ static bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt /* 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) { + if (!ipv6_addr_equal(&sol->dodag_id, &inst->dodag.dodag_id)) { DEBUG("RPL: RPL SOLICITED INFO option, ignore DIS cause: DODAGID mismatch\n"); return false; } @@ -905,6 +898,42 @@ void _recv_DIO_for_new_dodag(gnrc_rpl_instance_t *inst, gnrc_rpl_dio_t *dio, ker } +/** + * @brief Handles a received DIO message for a DODAG that is different from the + * one we currently participate in. + * + * @param[in] inst The @p RPL instance of the current DODAG. + * @param[in] dio The @p DIO packet for the other DODAG. + * @param[in] src The address of the sender. + * @param[in] len The length of the DIO packet. + */ +static void _recv_DIO_for_different_dodag(gnrc_rpl_instance_t *inst, gnrc_rpl_dio_t *dio, + kernel_pid_t iface, + ipv6_addr_t *src, uint16_t len) +{ + /* DIO received from a different DODAG */ + DEBUG("RPL: DIO received from another DODAG, but same instance.\n"); + + gnrc_rpl_dodag_t *dodag = &inst->dodag; + + /* clear parent from old dodag if present */ + gnrc_rpl_parent_t *parent = dodag->parents; + while (parent) { + if (ipv6_addr_equal(&parent->addr, src)) { + gnrc_rpl_parent_remove(parent); + break; + } + parent = parent->next; + } + + /* decide between old and new dodag */ + if (gnrc_rpl_get_of0()->which_dodag(dodag, dio) > 0) { + DEBUG("RPL: switch to new DODAG.\n"); + gnrc_rpl_dodag_remove(dodag); + _recv_DIO_for_new_dodag(inst, dio, iface, src, len); + } +} + /** * @brief Handles a received DIO message for an existing DODAG. * @@ -914,17 +943,11 @@ void _recv_DIO_for_new_dodag(gnrc_rpl_instance_t *inst, gnrc_rpl_dio_t *dio, ker * @param[in] len The length of the DIO packet. */ static void _recv_DIO_for_existing_dodag(gnrc_rpl_instance_t *inst, gnrc_rpl_dio_t *dio, - ipv6_addr_t *src, uint16_t len) + ipv6_addr_t *src, + uint16_t len) { gnrc_rpl_dodag_t *dodag = &inst->dodag; - /* ignore dodags with other dodag_id's for now */ - /* TODO: choose DODAG with better rank */ - 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; @@ -937,21 +960,6 @@ static void _recv_DIO_for_existing_dodag(gnrc_rpl_instance_t *inst, gnrc_rpl_dio } #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); @@ -996,6 +1004,11 @@ void gnrc_rpl_recv_DIO(gnrc_rpl_dio_t *dio, kernel_pid_t iface, ipv6_addr_t *src else if (inst == NULL) { DEBUG("RPL: Could not allocate a new instance.\n"); } + else if (!ipv6_addr_equal(&inst->dodag.dodag_id, &dio->dodag_id) + || (inst->dodag.version != dio->version_number)) { + + _recv_DIO_for_different_dodag(inst, dio, iface, src, len); + } else { _recv_DIO_for_existing_dodag(inst, dio, src, len); } @@ -1256,7 +1269,7 @@ void gnrc_rpl_recv_DAO(gnrc_rpl_dao_t *dao, kernel_pid_t iface, ipv6_addr_t *src /* 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) { + if (!ipv6_addr_equal(&dodag->dodag_id, (ipv6_addr_t *)(dao + 1))) { DEBUG("RPL: DAO with unknown DODAG id (%s)\n", _ip_addr_str((ipv6_addr_t *)(dao + 1))); return; } @@ -1319,7 +1332,7 @@ void gnrc_rpl_recv_DAO_ACK(gnrc_rpl_dao_ack_t *dao_ack, kernel_pid_t iface, ipv6 /* 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) { + if (!ipv6_addr_equal(&dodag->dodag_id, (ipv6_addr_t *)(dao_ack + 1))) { DEBUG("RPL: DAO-ACK with unknown DODAG id (%s)\n", _ip_addr_str((ipv6_addr_t *)(dao_ack + 1))); return; diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c index 8734b7218437..e0bf6ad39673 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c @@ -115,24 +115,29 @@ bool gnrc_rpl_instance_remove_by_id(uint8_t instance_id) { for(uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { if (gnrc_rpl_instances[i].id == instance_id) { - return gnrc_rpl_instance_remove(&gnrc_rpl_instances[i]); + gnrc_rpl_instance_remove(&gnrc_rpl_instances[i]); + return true; } } return false; } -bool gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst) +void gnrc_rpl_dodag_remove(gnrc_rpl_dodag_t *dodag) { - gnrc_rpl_dodag_t *dodag = &inst->dodag; #ifdef MODULE_GNRC_RPL_P2P gnrc_rpl_p2p_ext_remove(dodag); #endif gnrc_rpl_dodag_remove_all_parents(dodag); trickle_stop(&dodag->trickle); evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->dao_event); - evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&inst->cleanup_event); + evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->instance->cleanup_event); + +} + +void gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst) +{ + gnrc_rpl_dodag_remove(&inst->dodag); memset(inst, 0, sizeof(gnrc_rpl_instance_t)); - return true; } gnrc_rpl_instance_t *gnrc_rpl_instance_get(uint8_t instance_id) diff --git a/sys/net/gnrc/routing/rpl/of0.c b/sys/net/gnrc/routing/rpl/of0.c index 282964d5cd82..3dc5bae68bfd 100644 --- a/sys/net/gnrc/routing/rpl/of0.c +++ b/sys/net/gnrc/routing/rpl/of0.c @@ -25,18 +25,18 @@ static uint16_t calc_rank(gnrc_rpl_dodag_t *, uint16_t); static int parent_cmp(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); -static gnrc_rpl_dodag_t *which_dodag(gnrc_rpl_dodag_t *, gnrc_rpl_dodag_t *); +static int which_dodag(gnrc_rpl_dodag_t *, gnrc_rpl_dio_t *); static void reset(gnrc_rpl_dodag_t *); static gnrc_rpl_of_t gnrc_rpl_of0 = { - .ocp = 0x0, - .calc_rank = calc_rank, - .parent_cmp = parent_cmp, - .which_dodag = which_dodag, - .reset = reset, + .ocp = 0x0, + .calc_rank = calc_rank, + .parent_cmp = parent_cmp, + .which_dodag = which_dodag, + .reset = reset, .parent_state_callback = NULL, - .init = NULL, - .process_dio = NULL + .init = NULL, + .process_dio = NULL }; gnrc_rpl_of_t *gnrc_rpl_get_of0(void) @@ -87,9 +87,57 @@ int parent_cmp(gnrc_rpl_parent_t *parent1, gnrc_rpl_parent_t *parent2) return 0; } -/* Not used yet */ -gnrc_rpl_dodag_t *which_dodag(gnrc_rpl_dodag_t *d1, gnrc_rpl_dodag_t *d2) +int which_dodag(gnrc_rpl_dodag_t *d1, gnrc_rpl_dio_t *dio) { - (void) d2; - return d1; + /* parent set must not be empty */ + if ((d1->node_status != GNRC_RPL_ROOT_NODE) && !d1->parents) { + return 1; + } + + /* prefer grounded dodag */ + int dio_grounded = dio->g_mop_prf >> GNRC_RPL_GROUNDED_SHIFT; + if (d1->grounded > dio_grounded) { + return -1; + } + else if (dio_grounded > d1->grounded) { + return 1; + } + + int dio_prf = dio->g_mop_prf & GNRC_RPL_PRF_MASK; + + /* prefer dodag with more preferable root */ + if (d1->prf > dio_prf) { + return -1; + } + else if (dio_prf > d1->prf) { + return 1; + } + + /* prefer DODAG with more recent version */ + if (memcmp(&d1->dodag_id, &dio->dodag_id, sizeof(ipv6_addr_t)) != 0) { + if (GNRC_RPL_COUNTER_GREATER_THAN(d1->version, dio->version_number)) { + return -1; + } + else if (GNRC_RPL_COUNTER_GREATER_THAN(dio->version_number, d1->version)) { + return 1; + } + } + + /* prefer dodag with lesser resulting rank */ + /* TODO: calc rank properly */ + int d1_rank = d1->parents->rank; + int d2_rank = byteorder_ntohs(dio->rank); + if (d1_rank < d2_rank) { + return -1; + } + else if (d2_rank < d1_rank) { + return 1; + } + + /* prefer DODAG for which there is an alternate parent */ + if (d1->parents->next) { + return -1; + } + + return 0; } diff --git a/sys/shell/cmds/gnrc_rpl.c b/sys/shell/cmds/gnrc_rpl.c index a4707f68bbdb..e3fa12d802ae 100644 --- a/sys/shell/cmds/gnrc_rpl.c +++ b/sys/shell/cmds/gnrc_rpl.c @@ -106,10 +106,7 @@ int _gnrc_rpl_instance_remove(char *arg1) return 1; } - if (gnrc_rpl_instance_remove(inst) == false) { - printf("error: could not remove instance (%d)\n", instance_id); - return 1; - } + gnrc_rpl_instance_remove(inst); printf("success: removed instance (%d)\n", instance_id); return 0;