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 init_dag_from_dio(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_curr_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) {
232 init_dag_from_dio(dio);
240 if(curr_instance.used) {
241 LOG_WARN(
"local repair (%s)\n", str);
243 curr_instance.dag.state = DAG_INITIALIZED;
245 curr_instance.of->reset();
255 if(curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
256 return curr_instance.used && curr_instance.dag.state >= DAG_INITIALIZED;
258 return curr_instance.used && curr_instance.dag.state >= DAG_REACHABLE;
268 if(!curr_instance.used) {
272 old_rank = curr_instance.dag.rank;
276 if(curr_instance.dag.state == DAG_POISONING) {
278 curr_instance.dag.rank = RPL_INFINITE_RANK;
279 if(old_rank != RPL_INFINITE_RANK) {
281 LOG_WARN(
"poisoning and leaving after a delay\n");
286 rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent;
295 nbr = nbr_table_head(rpl_neighbors);
300 if(nbr->better_parent_since == 0) {
304 nbr->better_parent_since = 0;
306 nbr = nbr_table_next(rpl_neighbors, nbr);
309 if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) {
312 curr_instance.dag.lowest_rank = curr_instance.dag.rank;
316 if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK
317 && curr_instance.dag.rank != RPL_INFINITE_RANK
318 && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) {
319 LOG_WARN(
"significant rank update %u->%u\n",
320 curr_instance.dag.last_advertised_rank, curr_instance.dag.rank);
322 curr_instance.dag.last_advertised_rank = curr_instance.dag.rank;
327 if(curr_instance.dag.preferred_parent != old_parent) {
329 if(old_parent == NULL) {
330 curr_instance.dag.state = DAG_JOINED;
332 LOG_WARN(
"found parent: ");
334 LOG_WARN_(
", staying in DAG\n");
339 if(curr_instance.dag.preferred_parent != NULL) {
343 curr_instance.dag.state = DAG_INITIALIZED;
344 LOG_WARN(
"no parent, scheduling periodic DIS, will leave if no parent is found\n");
350 if(LOG_INFO_ENABLED) {
357 curr_instance.of->update_metric_container();
361 update_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
364 const uip_lladdr_t *lladdr;
371 lladdr = uip_ds6_nbr_lladdr_from_ipaddr(from);
377 nbr = nbr_table_add_lladdr(rpl_neighbors, (linkaddr_t *)lladdr,
378 NBR_TABLE_REASON_RPL_DIO, dio);
380 LOG_ERR(
"failed to add neighbor\n");
386 nbr->rank = dio->rank;
387 nbr->dtsn = dio->dtsn;
389 memcpy(&nbr->mc, &dio->mc,
sizeof(nbr->mc));
396 process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
421 if(dio->rank != RPL_INFINITE_RANK) {
422 curr_instance.dag.dio_counter++;
429 if(curr_instance.dag.rank ==
ROOT_RANK) {
431 LOG_ERR(
"inconsistent DIO version (current: %u, received: %u), initiate global repair\n",
432 curr_instance.dag.version, dio->version);
434 curr_instance.dag.version = dio->version;
437 LOG_WARN(
"new DIO version (current: %u, received: %u), apply global repair\n",
438 curr_instance.dag.version, dio->version);
439 global_repair_non_root(dio);
445 LOG_ERR(
"IPv6 cache full, dropping DIO\n");
451 last_dtsn = nbr != NULL ? nbr->dtsn : RPL_LOLLIPOP_INIT;
453 if(!update_nbr_from_dio(from, dio)) {
454 LOG_ERR(
"neighbor table full, dropping DIO\n");
459 if(curr_instance.dag.lifetime == 0 ||
460 (nbr != NULL && nbr == curr_instance.dag.preferred_parent)) {
461 LOG_INFO(
"refreshing lifetime\n");
462 curr_instance.dag.lifetime =
RPL_LIFETIME(RPL_DAG_LIFETIME);
467 if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
469 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
470 LOG_WARN(
"DTSN increment %u->%u, schedule new DAO with DTSN %u\n",
471 last_dtsn, dio->dtsn, curr_instance.dtsn_out);
478 init_dag(uint8_t instance_id, uip_ipaddr_t *dag_id, rpl_ocp_t ocp,
479 uip_ipaddr_t *prefix,
unsigned prefix_len, uint8_t prefix_flags)
483 memset(&curr_instance, 0,
sizeof(curr_instance));
486 of = find_objective_function(ocp);
488 LOG_ERR(
"ignoring DIO with an unsupported OF: %u\n", ocp);
494 LOG_ERR(
"failed to set prefix\n");
499 curr_instance.instance_id = instance_id;
500 curr_instance.of = of;
501 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
502 curr_instance.used = 1;
505 curr_instance.dag.rank = RPL_INFINITE_RANK;
506 curr_instance.dag.last_advertised_rank = RPL_INFINITE_RANK;
507 curr_instance.dag.lowest_rank = RPL_INFINITE_RANK;
508 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
509 curr_instance.dag.dao_last_acked_seqno = RPL_LOLLIPOP_INIT;
510 curr_instance.dag.dao_curr_seqno = RPL_LOLLIPOP_INIT;
511 memcpy(&curr_instance.dag.dag_id, dag_id,
sizeof(curr_instance.dag.dag_id));
517 init_dag_from_dio(rpl_dio_t *dio)
519 if(!init_dag(dio->instance_id, &dio->dag_id, dio->ocp,
520 &dio->prefix_info.prefix, dio->prefix_info.length, dio->prefix_info.flags)) {
525 curr_instance.mop = dio->mop;
526 curr_instance.mc.type = dio->mc.type;
527 curr_instance.mc.flags = dio->mc.flags;
528 curr_instance.mc.aggr = dio->mc.aggr;
529 curr_instance.mc.prec = dio->mc.prec;
530 curr_instance.max_rankinc = dio->dag_max_rankinc;
531 curr_instance.min_hoprankinc = dio->dag_min_hoprankinc;
532 curr_instance.dio_intdoubl = dio->dag_intdoubl;
533 curr_instance.dio_intmin = dio->dag_intmin;
534 curr_instance.dio_redundancy = dio->dag_redund;
535 curr_instance.default_lifetime = dio->default_lifetime;
536 curr_instance.lifetime_unit = dio->lifetime_unit;
539 curr_instance.dag.state = DAG_INITIALIZED;
540 curr_instance.dag.preference = dio->preference;
541 curr_instance.dag.grounded = dio->grounded;
542 curr_instance.dag.version = dio->version;
543 curr_instance.dag.dio_intcurrent = dio->dag_intmin;
549 process_dio_init_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
551 #ifdef RPL_VALIDATE_DIO_FUNC 552 if(!RPL_VALIDATE_DIO_FUNC(dio)) {
553 LOG_WARN(
"DIO validation failed\n");
559 if(dio->mop != RPL_MOP_NO_DOWNWARD_ROUTES && dio->mop != RPL_MOP_NON_STORING) {
560 LOG_WARN(
"ignoring DIO with an unsupported MOP: %d\n", dio->mop);
565 if(!init_dag_from_dio(dio)) {
566 LOG_WARN(
"failed to initialize DAG\n");
571 curr_instance.of->reset();
578 LOG_INFO(
"initialized DAG with instance ID %u, DAG ID ",
579 curr_instance.instance_id);
580 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
581 LOG_INFO_(
", prexix ");
582 LOG_INFO_6ADDR(&dio->prefix_info.prefix);
583 LOG_INFO_(
"/%u, rank %u\n", dio->prefix_info.length, curr_instance.dag.rank);
585 LOG_ANNOTATE(
"#A init=%u\n", curr_instance.dag.dag_id.u8[
sizeof(curr_instance.dag.dag_id) - 1]);
587 LOG_WARN(
"just joined, no parent yet, setting timer for leaving\n");
598 if(!process_dio_init_dag(from, dio)) {
599 LOG_WARN(
"failed to init DAG\n");
604 if(curr_instance.used
605 && curr_instance.instance_id == dio->instance_id
606 && uip_ipaddr_cmp(&curr_instance.dag.dag_id, &dio->dag_id)) {
607 process_dio_from_current_dag(from, dio);
620 LOG_INFO(
"unicast DIS, reply to sender\n");
629 if(dao->lifetime == 0) {
633 LOG_ERR(
"failed to add link on incoming DAO\n");
639 if(dao->flags & RPL_DAO_K_FLAG) {
651 curr_instance.dag.dao_last_acked_seqno = sequence;
654 if(sequence == curr_instance.dag.dao_last_seqno) {
655 int status_ok = status < RPL_DAO_ACK_UNABLE_TO_ACCEPT;
656 if(curr_instance.dag.state == DAG_JOINED && status_ok) {
657 curr_instance.dag.state = DAG_REACHABLE;
663 LOG_WARN(
"DAO-NACK received with seqno %u, status %u, poison and leave\n",
665 curr_instance.dag.state = DAG_POISONING;
677 if(rank_error_signaled) {
678 #if RPL_LOOP_ERROR_DROP 681 LOG_WARN(
"rank error and loop detected, dropping\n");
690 if(rank_error_signaled) {
694 sender->rank = sender_rank;
706 uip_ipaddr_t *prefix,
unsigned prefix_len, uint8_t prefix_flags)
708 uint8_t version = RPL_LOLLIPOP_INIT;
711 if(curr_instance.used) {
713 if(uip_ipaddr_cmp(&curr_instance.dag.dag_id, dag_id)) {
714 version = curr_instance.dag.version;
715 RPL_LOLLIPOP_INCREMENT(version);
721 init_dag(instance_id, dag_id, RPL_OF_OCP, prefix, prefix_len, prefix_flags);
724 curr_instance.mop = RPL_MOP_DEFAULT;
725 curr_instance.max_rankinc = RPL_MAX_RANKINC;
726 curr_instance.min_hoprankinc = RPL_MIN_HOPRANKINC;
727 curr_instance.dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
728 curr_instance.dio_intmin = RPL_DIO_INTERVAL_MIN;
729 curr_instance.dio_redundancy = RPL_DIO_REDUNDANCY;
730 curr_instance.default_lifetime = RPL_DEFAULT_LIFETIME;
731 curr_instance.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
734 curr_instance.dag.preference = RPL_PREFERENCE;
735 curr_instance.dag.grounded = RPL_GROUNDED;
736 curr_instance.dag.version = version;
738 curr_instance.dag.lifetime =
RPL_LIFETIME(RPL_INFINITE_LIFETIME);
739 curr_instance.dag.dio_intcurrent = RPL_DIO_INTERVAL_MIN;
740 curr_instance.dag.state = DAG_REACHABLE;
744 LOG_INFO(
"created DAG with instance ID %u, DAG ID ",
745 curr_instance.instance_id);
746 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
747 LOG_INFO_(
", rank %u\n", curr_instance.dag.rank);
749 LOG_ANNOTATE(
"#A root=%u\n", curr_instance.dag.dag_id.u8[
sizeof(curr_instance.dag.dag_id) - 1]);
755 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_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.
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()