45 #include "net/routing/rpl-lite/rpl.h" 47 #include "net/nbr-table.h" 48 #include "net/link-stats.h" 52 #define LOG_MODULE "RPL" 53 #define LOG_LEVEL LOG_LEVEL_RPL 57 static rpl_of_t *
const objective_functions[] = RPL_SUPPORTED_OFS;
58 static int process_dio_init_dag(rpl_dio_t *dio);
66 #ifdef RPL_VALIDATE_DIO_FUNC 67 int RPL_VALIDATE_DIO_FUNC(rpl_dio_t *dio);
91 if(curr_instance.used && ipaddr != NULL) {
101 LOG_INFO(
"leaving DAG ");
102 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
103 LOG_INFO_(
", instance %u\n", curr_instance.instance_id);
107 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.dao_last_seqno);
122 if((curr_instance.dag.prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
127 curr_instance.used = 0;
133 curr_instance.dag.state = DAG_POISONING;
140 if(curr_instance.used) {
141 if(curr_instance.dag.lifetime !=
RPL_LIFETIME(RPL_INFINITE_LIFETIME)) {
142 curr_instance.dag.lifetime =
143 curr_instance.dag.lifetime > seconds ? curr_instance.dag.lifetime - seconds : 0;
144 if(curr_instance.dag.lifetime == 0) {
145 LOG_WARN(
"DAG expired, poison and leave\n");
146 curr_instance.dag.state = DAG_POISONING;
148 }
else if(curr_instance.dag.lifetime < 300 && curr_instance.dag.preferred_parent != NULL) {
150 LOG_WARN(
"DAG expiring in %u seconds, send DIS to preferred parent\n", (
unsigned)curr_instance.dag.lifetime);
160 return curr_instance.used
161 && uip_ipaddr_prefixcmp(&curr_instance.dag.dag_id, addr, curr_instance.dag.prefix_info.length);
167 return curr_instance.used ? &curr_instance : NULL;
173 return curr_instance.used ? &curr_instance.dag : NULL;
177 find_objective_function(rpl_ocp_t ocp)
180 for(i = 0; i <
sizeof(objective_functions) /
sizeof(objective_functions[0]); i++) {
181 if(objective_functions[i]->ocp == ocp) {
182 return objective_functions[i];
193 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
195 LOG_WARN(
"incremented DTSN (%s), current %u\n",
196 str, curr_instance.dtsn_out);
197 if(LOG_INFO_ENABLED) {
207 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.version);
208 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
210 LOG_WARN(
"initiating global repair (%s), version %u, rank %u\n",
211 str, curr_instance.dag.version, curr_instance.dag.rank);
212 if(LOG_INFO_ENABLED) {
222 global_repair_non_root(rpl_dio_t *dio)
225 LOG_WARN(
"participating in global repair, version %u, rank %u\n",
226 dio->version, curr_instance.dag.rank);
227 if(LOG_INFO_ENABLED) {
234 process_dio_init_dag(dio);
242 if(curr_instance.used) {
243 LOG_WARN(
"local repair (%s)\n", str);
245 curr_instance.dag.state = DAG_INITIALIZED;
247 curr_instance.of->reset();
257 if(curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
258 return curr_instance.used && curr_instance.dag.state >= DAG_INITIALIZED;
260 return curr_instance.used && curr_instance.dag.state >= DAG_REACHABLE;
270 if(!curr_instance.used) {
274 old_rank = curr_instance.dag.rank;
278 if(curr_instance.dag.state == DAG_POISONING) {
280 curr_instance.dag.rank = RPL_INFINITE_RANK;
281 if(old_rank != RPL_INFINITE_RANK) {
283 LOG_WARN(
"poisoning and leaving after a delay\n");
288 rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent;
297 nbr = nbr_table_head(rpl_neighbors);
302 if(nbr->better_parent_since == 0) {
306 nbr->better_parent_since = 0;
308 nbr = nbr_table_next(rpl_neighbors, nbr);
311 if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) {
314 curr_instance.dag.lowest_rank = curr_instance.dag.rank;
318 if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK
319 && curr_instance.dag.rank != RPL_INFINITE_RANK
320 && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) {
321 LOG_WARN(
"significant rank update %u->%u\n",
322 curr_instance.dag.last_advertised_rank, curr_instance.dag.rank);
324 curr_instance.dag.last_advertised_rank = curr_instance.dag.rank;
329 if(curr_instance.dag.unprocessed_parent_switch) {
331 if(old_parent == NULL) {
332 curr_instance.dag.state = DAG_JOINED;
334 LOG_WARN(
"found parent: ");
336 LOG_WARN_(
", staying in DAG\n");
341 if(curr_instance.dag.preferred_parent != NULL) {
345 curr_instance.dag.state = DAG_INITIALIZED;
346 LOG_WARN(
"no parent, scheduling periodic DIS, will leave if no parent is found\n");
352 if(LOG_INFO_ENABLED) {
357 curr_instance.dag.unprocessed_parent_switch =
false;
362 curr_instance.of->update_metric_container();
366 update_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
369 const uip_lladdr_t *lladdr;
382 nbr = nbr_table_add_lladdr(rpl_neighbors, (linkaddr_t *)lladdr,
383 NBR_TABLE_REASON_RPL_DIO, dio);
385 LOG_ERR(
"failed to add neighbor\n");
391 nbr->rank = dio->rank;
392 nbr->dtsn = dio->dtsn;
394 memcpy(&nbr->mc, &dio->mc,
sizeof(nbr->mc));
401 process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
426 if(dio->rank != RPL_INFINITE_RANK) {
427 curr_instance.dag.dio_counter++;
434 if(curr_instance.dag.rank ==
ROOT_RANK) {
436 LOG_ERR(
"inconsistent DIO version (current: %u, received: %u), initiate global repair\n",
437 curr_instance.dag.version, dio->version);
439 curr_instance.dag.version = dio->version;
442 LOG_WARN(
"new DIO version (current: %u, received: %u), apply global repair\n",
443 curr_instance.dag.version, dio->version);
444 global_repair_non_root(dio);
450 LOG_ERR(
"IPv6 cache full, dropping DIO\n");
455 last_dtsn = nbr != NULL ? nbr->dtsn : RPL_LOLLIPOP_INIT;
458 if(!update_nbr_from_dio(from, dio)) {
459 LOG_ERR(
"neighbor table full, dropping DIO\n");
464 if(curr_instance.dag.lifetime == 0 ||
465 (nbr != NULL && nbr == curr_instance.dag.preferred_parent)) {
466 LOG_INFO(
"refreshing lifetime\n");
467 curr_instance.dag.lifetime =
RPL_LIFETIME(RPL_DAG_LIFETIME);
472 if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
474 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
475 LOG_WARN(
"DTSN increment %u->%u, schedule new DAO with DTSN %u\n",
476 last_dtsn, dio->dtsn, curr_instance.dtsn_out);
483 init_dag(uint8_t instance_id, uip_ipaddr_t *dag_id, rpl_ocp_t ocp,
484 uip_ipaddr_t *prefix,
unsigned prefix_len, uint8_t prefix_flags)
488 memset(&curr_instance, 0,
sizeof(curr_instance));
491 of = find_objective_function(ocp);
493 LOG_ERR(
"ignoring DIO with an unsupported OF: %u\n", ocp);
499 LOG_ERR(
"failed to set prefix\n");
504 curr_instance.instance_id = instance_id;
505 curr_instance.of = of;
506 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
507 curr_instance.used = 1;
510 curr_instance.dag.rank = RPL_INFINITE_RANK;
511 curr_instance.dag.last_advertised_rank = RPL_INFINITE_RANK;
512 curr_instance.dag.lowest_rank = RPL_INFINITE_RANK;
513 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
514 curr_instance.dag.dao_last_acked_seqno = RPL_LOLLIPOP_INIT;
515 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
516 memcpy(&curr_instance.dag.dag_id, dag_id,
sizeof(curr_instance.dag.dag_id));
522 init_dag_from_dio(rpl_dio_t *dio)
524 if(!init_dag(dio->instance_id, &dio->dag_id, dio->ocp,
525 &dio->prefix_info.prefix, dio->prefix_info.length, dio->prefix_info.flags)) {
530 curr_instance.mop = dio->mop;
531 curr_instance.mc.type = dio->mc.type;
532 curr_instance.mc.flags = dio->mc.flags;
533 curr_instance.mc.aggr = dio->mc.aggr;
534 curr_instance.mc.prec = dio->mc.prec;
535 curr_instance.max_rankinc = dio->dag_max_rankinc;
536 curr_instance.min_hoprankinc = dio->dag_min_hoprankinc;
537 curr_instance.dio_intdoubl = dio->dag_intdoubl;
538 curr_instance.dio_intmin = dio->dag_intmin;
539 curr_instance.dio_redundancy = dio->dag_redund;
540 curr_instance.default_lifetime = dio->default_lifetime;
541 curr_instance.lifetime_unit = dio->lifetime_unit;
544 curr_instance.dag.state = DAG_INITIALIZED;
545 curr_instance.dag.preference = dio->preference;
546 curr_instance.dag.grounded = dio->grounded;
547 curr_instance.dag.version = dio->version;
549 curr_instance.dag.dio_intcurrent = 0;
555 process_dio_init_dag(rpl_dio_t *dio)
557 #ifdef RPL_VALIDATE_DIO_FUNC 558 if(!RPL_VALIDATE_DIO_FUNC(dio)) {
559 LOG_WARN(
"DIO validation failed\n");
565 if(dio->mop != RPL_MOP_NO_DOWNWARD_ROUTES && dio->mop != RPL_MOP_NON_STORING) {
566 LOG_WARN(
"ignoring DIO with an unsupported MOP: %d\n", dio->mop);
571 if(!init_dag_from_dio(dio)) {
572 LOG_WARN(
"failed to initialize DAG\n");
577 curr_instance.of->reset();
584 LOG_INFO(
"initialized DAG with instance ID %u, DAG ID ",
585 curr_instance.instance_id);
586 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
587 LOG_INFO_(
", prexix ");
588 LOG_INFO_6ADDR(&dio->prefix_info.prefix);
589 LOG_INFO_(
"/%u, rank %u\n", dio->prefix_info.length, curr_instance.dag.rank);
591 LOG_ANNOTATE(
"#A init=%u\n", curr_instance.dag.dag_id.u8[
sizeof(curr_instance.dag.dag_id) - 1]);
593 LOG_WARN(
"just joined, no parent yet, setting timer for leaving\n");
604 if(!process_dio_init_dag(dio)) {
605 LOG_WARN(
"failed to init DAG\n");
610 if(curr_instance.used
611 && curr_instance.instance_id == dio->instance_id
612 && uip_ipaddr_cmp(&curr_instance.dag.dag_id, &dio->dag_id)) {
613 process_dio_from_current_dag(from, dio);
626 LOG_INFO(
"unicast DIS, reply to sender\n");
635 if(dao->lifetime == 0) {
639 LOG_ERR(
"failed to add link on incoming DAO\n");
645 if(dao->flags & RPL_DAO_K_FLAG) {
657 curr_instance.dag.dao_last_acked_seqno = sequence;
660 if(sequence == curr_instance.dag.dao_last_seqno) {
661 int status_ok = status < RPL_DAO_ACK_UNABLE_TO_ACCEPT;
662 if(curr_instance.dag.state == DAG_JOINED && status_ok) {
663 curr_instance.dag.state = DAG_REACHABLE;
671 LOG_WARN(
"DAO-NACK received with seqno %u, status %u, poison and leave\n",
673 curr_instance.dag.state = DAG_POISONING;
685 if(rank_error_signaled) {
686 #if RPL_LOOP_ERROR_DROP 689 LOG_WARN(
"rank error and loop detected, dropping\n");
698 if(rank_error_signaled) {
702 sender->rank = sender_rank;
714 uip_ipaddr_t *prefix,
unsigned prefix_len, uint8_t prefix_flags)
716 uint8_t version = RPL_LOLLIPOP_INIT;
719 if(curr_instance.used) {
721 if(uip_ipaddr_cmp(&curr_instance.dag.dag_id, dag_id)) {
722 version = curr_instance.dag.version;
723 RPL_LOLLIPOP_INCREMENT(version);
729 init_dag(instance_id, dag_id, RPL_OF_OCP, prefix, prefix_len, prefix_flags);
732 curr_instance.mop = RPL_MOP_DEFAULT;
733 curr_instance.max_rankinc = RPL_MAX_RANKINC;
734 curr_instance.min_hoprankinc = RPL_MIN_HOPRANKINC;
735 curr_instance.dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
736 curr_instance.dio_intmin = RPL_DIO_INTERVAL_MIN;
737 curr_instance.dio_redundancy = RPL_DIO_REDUNDANCY;
738 curr_instance.default_lifetime = RPL_DEFAULT_LIFETIME;
739 curr_instance.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
742 curr_instance.dag.preference = RPL_PREFERENCE;
743 curr_instance.dag.grounded = RPL_GROUNDED;
744 curr_instance.dag.version = version;
746 curr_instance.dag.lifetime =
RPL_LIFETIME(RPL_INFINITE_LIFETIME);
748 curr_instance.dag.dio_intcurrent = 0;
749 curr_instance.dag.state = DAG_REACHABLE;
753 LOG_INFO(
"created DAG with instance ID %u, DAG ID ",
754 curr_instance.instance_id);
755 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
756 LOG_INFO_(
", rank %u\n", curr_instance.dag.rank);
758 LOG_ANNOTATE(
"#A root=%u\n", curr_instance.dag.dag_id.u8[
sizeof(curr_instance.dag.dag_id) - 1]);
764 memset(&curr_instance, 0,
sizeof(curr_instance));
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
void rpl_dag_poison_and_leave(void)
Start poisoning and leave the DAG after a delay.
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor's (link-local) IPv6 address.
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
void rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
Processes incoming DAO.
void rpl_timers_schedule_unicast_dio(rpl_nbr_t *target)
Schedule unicast DIO with no delay.
#define ROOT_RANK
Rank of a root node.
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
int rpl_lollipop_greater_than(int a, int b)
Greater-than function for a lollipop counter.
void rpl_timers_notify_dao_ack(void)
Let the rpl-timers module know that the last DAO was ACKed.
void rpl_global_repair(const char *str)
Triggers a RPL global repair.
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
rpl_nbr_t * rpl_neighbor_get_from_ipaddr(uip_ipaddr_t *addr)
Returns a neighbor from its link-local IPv6 address.
void uip_sr_free_all(void)
Deallocate all neighbors.
int rpl_is_addr_in_our_dag(const uip_ipaddr_t *addr)
Tells whether a given global IPv6 address is in our current DAG.
#define RPL_LIFETIME(lifetime)
Compute lifetime, accounting for the lifetime unit.
void rpl_dag_init_root(uint8_t instance_id, uip_ipaddr_t *dag_id, uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
Initializes DAG internal structure for a root node.
void uip_sr_expire_parent(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent)
Expires a given child-parent link.
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
void rpl_timers_schedule_dao_ack(uip_ipaddr_t *target, uint16_t sequence)
Schedule a DAO-ACK with no delay.
void rpl_icmp6_dao_output(uint8_t lifetime)
Creates an ICMPv6 DAO packet and sends it to the root, advertising the current preferred parent...
void rpl_process_dao_ack(uint8_t sequence, uint8_t status)
Processes incoming DAO-ACK.
API for RPL objective functions (OF)
const char * rpl_dag_state_to_str(enum rpl_dag_state state)
Returns a textual description of the current DAG state.
int rpl_set_prefix_from_addr(uip_ipaddr_t *addr, unsigned len, uint8_t flags)
Set prefix from an IPv6 address.
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
void rpl_timers_dio_reset(const char *str)
Reset DIO Trickle timer.
void rpl_timers_unschedule_leaving(void)
Cancel scheduled leaving if any.
void rpl_timers_stop_dag_timers(void)
Stop all timers related to the DAG.
void rpl_dag_leave(void)
Leaves the current DAG.
void rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
Processes incoming DIS.
rpl_dag_state
RPL DAG states.
All information related to a RPL neighbor.
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code)
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
clock_time_t clock_time(void)
Get the current clock time.
const uip_lladdr_t * uip_ds6_nbr_lladdr_from_ipaddr(const uip_ipaddr_t *ipaddr)
Get the link-layer address associated with a specified IPv6 address.
int rpl_dag_ready_to_advertise(void)
Tells whether RPL is ready to advertise the DAG.
void rpl_timers_schedule_periodic_dis(void)
Schedule periodic DIS with a random delay based on RPL_DIS_INTERVAL, until we join a DAG...
uip_ds6_nbr_t * rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
Updates IPv6 neighbor cache on incoming link-local RPL ICMPv6 messages.
void rpl_timers_schedule_dao(void)
Schedule a DAO with random delay based on RPL_DAO_DELAY.
void rpl_dag_periodic(unsigned seconds)
A function called periodically.
void rpl_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
void rpl_refresh_routes(const char *str)
Triggers a route fresh via DTSN increment.
void rpl_dag_update_state(void)
Updates RPL internal state: selects preferred parent, updates rank & metreic container, triggers control traffic accordingly and updates uIP6 internal state.
void rpl_timers_schedule_leaving(void)
Schedule leaving after RPL_DELAY_BEFORE_LEAVING.
int rpl_dag_get_root_ipaddr(uip_ipaddr_t *ipaddr)
Returns the IPv6 address of the RPL DAG root, if any.
uip_sr_node_t * uip_sr_update_node(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime)
Updates a child-parent link.
void rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
Processes incoming DIO.
int rpl_process_hbh(rpl_nbr_t *sender, uint16_t sender_rank, int loop_detected, int rank_error_signaled)
Processes Hop-by-Hop (HBH) Extension Header of a packet currently being forwrded. ...
void rpl_timers_unschedule_state_update(void)
Cancelled any scheduled state update.
rpl_rank_t rpl_neighbor_rank_via_nbr(rpl_nbr_t *nbr)
Returns our rank if selecting a given parent as preferred parent.
void rpl_local_repair(const char *str)
Triggers a RPL local repair.
void rpl_reset_prefix(rpl_prefix_t *last_prefix)
Removes current prefx.
void rpl_neighbor_set_preferred_parent(rpl_nbr_t *nbr)
Set current RPL preferred parent and update DS6 default route accordingly.
void rpl_icmp6_dio_output(uip_ipaddr_t *uc_addr)
Creates an ICMPv6 DIO packet and sends it.
Header file for the logging system
rpl_nbr_t * rpl_neighbor_select_best(void)
Returns the best candidate for preferred parent.
rpl_instance_t * rpl_get_default_instance(void)
Returns pointer to the default instance (for compatibility with legagy RPL code)
void rpl_dag_init(void)
Initializes rpl-dag module.
void rpl_icmp6_dis_output(uip_ipaddr_t *addr)
Creates an ICMPv6 DIS packet and sends it.
void rpl_schedule_probing(void)
Schedule probing with delay RPL_PROBING_DELAY_FUNC()