/*---------------------------------------------------------------------------*/ void rpl_print_neighbor_list() { if(default_instance != NULL && default_instance->current_dag != NULL && default_instance->of != NULL && default_instance->of->calculate_rank != NULL) { int curr_dio_interval = default_instance->dio_intcurrent; int curr_rank = default_instance->current_dag->rank; rpl_parent_t *p = nbr_table_head(rpl_parents); clock_time_t now = clock_time(); printf("RPL: rank %u dioint %u, %u nbr(s)\n", curr_rank, curr_dio_interval, uip_ds6_nbr_num()); while(p != NULL) { uip_ds6_nbr_t *nbr = rpl_get_nbr(p); printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n", nbr_table_get_lladdr(rpl_parents, p)->u8[7], p->rank, nbr ? nbr->link_metric : 0, default_instance->of->calculate_rank(p, 0), default_instance->current_dag == p->dag ? 'd' : ' ', p == default_instance->current_dag->preferred_parent ? '*' : ' ', (unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND))); p = nbr_table_next(rpl_parents, p); } printf("RPL: end of list\n"); } }
static rpl_rank_t calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank) { rpl_rank_t new_rank; rpl_rank_t rank_increase; uip_ds6_nbr_t *nbr; if(p == NULL || (nbr = rpl_get_nbr(p)) == NULL) { if(base_rank == 0) { return INFINITE_RANK; } rank_increase = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR; } else { rank_increase = nbr->link_metric; if(base_rank == 0) { base_rank = p->rank; } } if(INFINITE_RANK - base_rank < rank_increase) { /* Reached the maximum rank. */ new_rank = INFINITE_RANK; } else { /* Calculate the rank based on the new rank information from DIO or stored otherwise. */ new_rank = base_rank + rank_increase; } return new_rank; }
/*---------------------------------------------------------------------------*/ static void res_rpl_link_metric_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { uint16_t length; rpl_instance_t *instance; uip_ds6_nbr_t *nbr; char message[REST_MAX_CHUNK_SIZE]; memset(message, 0, REST_MAX_CHUNK_SIZE); instance = rpl_get_instance(RPL_DEFAULT_INSTANCE); /* Print the link metric to parent */ if(instance != NULL && instance->current_dag != NULL && instance->current_dag->preferred_parent != NULL && (nbr = rpl_get_nbr(instance->current_dag->preferred_parent)) != NULL) { snprintf(message, sizeof(message) - 1, "Link Metric: %u.%02u\n", nbr->link_metric / RPL_DAG_MC_ETX_DIVISOR, (100 * (nbr->link_metric % RPL_DAG_MC_ETX_DIVISOR)) / RPL_DAG_MC_ETX_DIVISOR); } else { snprintf(message, sizeof(message) - 1, "No link metric yet\n"); } length = strlen(message); memcpy(buffer, message, length); REST.set_header_content_type(response, REST.type.TEXT_PLAIN); REST.set_header_etag(response, (uint8_t *)&length, 1); REST.set_response_payload(response, buffer, length); }
uint16_t create_parent_msg(char *buf,rpl_parent_t *parent, uint8_t preferred) { uint8_t n = 0; uip_ds6_nbr_t *nbr; uip_ipaddr_t * addr = rpl_get_parent_ipaddr(parent); n += sprintf(&(buf[n]), "{\"eui\":\"%04x%04x%04x%04x\",", UIP_HTONS(addr->u16[4]), UIP_HTONS(addr->u16[5]), UIP_HTONS(addr->u16[6]), UIP_HTONS(addr->u16[7])); n += sprintf(&(buf[n]), "\"pref\":"); if(preferred == 1) { n += sprintf(&(buf[n]), "true,"); }else { n += sprintf(&(buf[n]), "false,"); } nbr = rpl_get_nbr(parent); // Estimated Retransmission part (ETX) value n += sprintf(&(buf[n]), "\"etx\":%d}",nbr->link_metric); buf[n] = 0; // clear next buffer value PRINTF("buf: %s\n", buf); // print the buffer value return n; }
/* rpl_purge_parents is called with the periodic timer in rpl-timers.c */ void rpl_purge_parents(void) { rpl_parent_t *p; p = nbr_table_head(rpl_parents); while(p != NULL) { if(rpl_get_nbr(p) == NULL) { PRINTF("RPL: No NBR entry for parent - removing parent\n"); rpl_remove_parent(p); /* continue with the removal next periodic tick */ return; } p = nbr_table_next(rpl_parents, p); } }
static void neighbor_link_callback(rpl_parent_t *p, int status, int numtx) { uint16_t recorded_etx = 0; uint16_t packet_etx = numtx * RPL_DAG_MC_ETX_DIVISOR; uint16_t new_etx; uip_ds6_nbr_t *nbr = NULL; nbr = rpl_get_nbr(p); if(nbr == NULL) { /* No neighbor for this parent - something bad has occurred */ return; } recorded_etx = nbr->link_metric; /* Do not penalize the ETX when collisions or transmission errors occur. */ if(status == MAC_TX_OK || status == MAC_TX_NOACK) { if(status == MAC_TX_NOACK) { packet_etx = MAX_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR; } if(p->flags & RPL_PARENT_FLAG_LINK_METRIC_VALID) { /* We already have a valid link metric, use weighted moving average to update it */ new_etx = ((uint32_t)recorded_etx * ETX_ALPHA + (uint32_t)packet_etx * (ETX_SCALE - ETX_ALPHA)) / ETX_SCALE; } else { /* We don't have a valid link metric, set it to the current packet's ETX */ new_etx = packet_etx; /* Set link metric as valid */ p->flags |= RPL_PARENT_FLAG_LINK_METRIC_VALID; } PRINTF("RPL: ETX changed from %u to %u (packet ETX = %u)\n", (unsigned)(recorded_etx / RPL_DAG_MC_ETX_DIVISOR), (unsigned)(new_etx / RPL_DAG_MC_ETX_DIVISOR), (unsigned)(packet_etx / RPL_DAG_MC_ETX_DIVISOR)); /* update the link metric for this nbr */ nbr->link_metric = new_etx; } }
/*---------------------------------------------------------------------------*/ rpl_parent_t * rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr) { rpl_parent_t *p = NULL; /* Is the parent known by ds6? Drop this request if not. * Typically, the parent is added upon receiving a DIO. */ const uip_lladdr_t *lladdr = uip_ds6_nbr_lladdr_from_ipaddr(addr); PRINTF("RPL: rpl_add_parent lladdr %p ", lladdr); PRINTLLADDR(lladdr); PRINTF(" "); PRINT6ADDR(addr); PRINTF("\n"); if(lladdr != NULL) { /* Add parent in rpl_parents */ p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr); if(p == NULL) { PRINTF("RPL: rpl_add_parent p NULL\n"); } else { uip_ds6_nbr_t *nbr; nbr = rpl_get_nbr(p); p->dag = dag; p->rank = dio->rank; p->dtsn = dio->dtsn; /* Check whether we have a neighbor that has not gotten a link metric yet */ if(nbr != NULL && nbr->link_metric == 0) { nbr->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR; } #if RPL_DAG_MC != RPL_DAG_MC_NONE memcpy(&p->mc, &dio->mc, sizeof(p->mc)); #endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */ } } return p; }
static rpl_path_metric_t calculate_path_metric(rpl_parent_t *p) { uip_ds6_nbr_t *nbr; if(p == NULL) { return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR; } nbr = rpl_get_nbr(p); if(nbr == NULL) { return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR; } #if RPL_DAG_MC == RPL_DAG_MC_NONE { return p->rank + (uint16_t)nbr->link_metric; } #elif RPL_DAG_MC == RPL_DAG_MC_ETX return p->mc.obj.etx + (uint16_t)nbr->link_metric; #elif RPL_DAG_MC == RPL_DAG_MC_ENERGY return p->mc.obj.energy.energy_est + (uint16_t)nbr->link_metric; #else #error "Unsupported RPL_DAG_MC configured. See rpl.h." #endif /* RPL_DAG_MC */ }
/*---------------------------------------------------------------------------*/ void rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio) { rpl_instance_t *instance; rpl_dag_t *dag, *previous_dag; rpl_parent_t *p; #if RPL_CONF_MULTICAST /* If the root is advertising MOP 2 but we support MOP 3 we can still join * In that scenario, we suppress DAOs for multicast targets */ if(dio->mop < RPL_MOP_STORING_NO_MULTICAST) { #else if(dio->mop != RPL_MOP_DEFAULT) { #endif PRINTF("RPL: Ignoring a DIO with an unsupported MOP: %d\n", dio->mop); return; } dag = get_dag(dio->instance_id, &dio->dag_id); instance = rpl_get_instance(dio->instance_id); #if CETIC_6LBR if(!cetic_6lbr_dio_input_hook(from, instance, dag, dio)) { return; } #endif if(dag != NULL && instance != NULL) { if(lollipop_greater_than(dio->version, dag->version)) { if(dag->rank == ROOT_RANK(instance)) { PRINTF("RPL: Root received inconsistent DIO version number\n"); dag->version = dio->version; RPL_LOLLIPOP_INCREMENT(dag->version); rpl_reset_dio_timer(instance); #if CETIC_6LBR nvm_data.rpl_version_id = dag->version; store_nvm_config(); #endif } else { PRINTF("RPL: Global repair\n"); if(dio->prefix_info.length != 0) { if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) { PRINTF("RPL : Prefix announced in DIO\n"); rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length); } } global_repair(from, dag, dio); } return; } if(lollipop_greater_than(dag->version, dio->version)) { /* The DIO sender is on an older version of the DAG. */ PRINTF("RPL: old version received => inconsistency detected\n"); if(dag->joined) { rpl_reset_dio_timer(instance); return; } } } if(instance == NULL) { PRINTF("RPL: New instance detected: Joining...\n"); rpl_join_instance(from, dio); return; } if(instance->current_dag->rank == ROOT_RANK(instance) && instance->current_dag != dag) { PRINTF("RPL: Root ignored DIO for different DAG\n"); return; } if(dag == NULL) { #if RPL_MAX_DAG_PER_INSTANCE > 1 PRINTF("RPL: Adding new DAG to known instance.\n"); dag = rpl_add_dag(from, dio); if(dag == NULL) { PRINTF("RPL: Failed to add DAG.\n"); return; } #else /* RPL_MAX_DAG_PER_INSTANCE > 1 */ PRINTF("RPL: Only one instance supported.\n"); return; #endif /* RPL_MAX_DAG_PER_INSTANCE > 1 */ } if(dio->rank < ROOT_RANK(instance)) { PRINTF("RPL: Ignoring DIO with too low rank: %u\n", (unsigned)dio->rank); return; } else if(dio->rank == INFINITE_RANK && dag->joined) { rpl_reset_dio_timer(instance); } /* Prefix Information Option treated to add new prefix */ if(dio->prefix_info.length != 0) { if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) { PRINTF("RPL : Prefix announced in DIO\n"); rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length); } } if(dag->rank == ROOT_RANK(instance)) { if(dio->rank != INFINITE_RANK) { instance->dio_counter++; } return; } /* The DIO comes from a valid DAG, we can refresh its lifetime */ dag->lifetime = (1UL << (instance->dio_intmin + instance->dio_intdoubl)) * RPL_DAG_LIFETIME / 1000; PRINTF("Set dag "); PRINT6ADDR(&dag->dag_id); PRINTF(" lifetime to %ld\n", dag->lifetime); /* * At this point, we know that this DIO pertains to a DAG that * we are already part of. We consider the sender of the DIO to be * a candidate parent, and let rpl_process_parent_event decide * whether to keep it in the set. */ rpl_update_dag(instance, dag, dio); p = rpl_find_parent(dag, from); if(p == NULL) { previous_dag = find_parent_dag(instance, from); if(previous_dag == NULL) { /* Add the DIO sender as a candidate parent. */ p = rpl_add_parent(dag, dio, from); if(p == NULL) { PRINTF("RPL: Failed to add a new parent ("); PRINT6ADDR(from); PRINTF(")\n"); return; } PRINTF("RPL: New candidate parent with rank %u: ", (unsigned)p->rank); PRINT6ADDR(from); PRINTF("\n"); } else { p = rpl_find_parent(previous_dag, from); if(p != NULL) { rpl_move_parent(previous_dag, dag, p); } } } else { if(p->rank == dio->rank) { PRINTF("RPL: Received consistent DIO\n"); if(dag->joined) { instance->dio_counter++; } } } p->rank = dio->rank; /* Parent info has been updated, trigger rank recalculation */ p->flags |= RPL_PARENT_FLAG_UPDATED; PRINTF("RPL: preferred DAG "); PRINT6ADDR(&instance->current_dag->dag_id); PRINTF(", rank %u, min_rank %u, ", instance->current_dag->rank, instance->current_dag->min_rank); PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n", p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx); /* We have allocated a candidate parent; process the DIO further. */ #if RPL_DAG_MC != RPL_DAG_MC_NONE memcpy(&p->mc, &dio->mc, sizeof(p->mc)); #endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */ if(rpl_process_parent_event(instance, p) == 0) { PRINTF("RPL: The candidate parent is rejected\n"); return; } /* We don't use route control, so we can have only one official parent. */ if(dag->joined && p == dag->preferred_parent) { if(should_send_dao(instance, dio, p)) { RPL_LOLLIPOP_INCREMENT(instance->dtsn_out); rpl_schedule_dao(instance); } /* We received a new DIO from our preferred parent. * Call uip_ds6_defrt_add to set a fresh value for the lifetime counter */ uip_ds6_defrt_add(from, RPL_DEFAULT_ROUTE_INFINITE_LIFETIME ? 0 : RPL_LIFETIME(instance, instance->default_lifetime)); } p->dtsn = dio->dtsn; }
/*---------------------------------------------------------------------------*/ static void res_rpl_info_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { /*Return RPL information in JSON format */ uint16_t length = 0; rpl_instance_t *instance; uip_ds6_nbr_t *nbr; char message[REST_MAX_CHUNK_SIZE]; memset(message, 0, sizeof(message)); instance = rpl_get_instance(RPL_DEFAULT_INSTANCE); if(instance != NULL && instance->current_dag != NULL && instance->current_dag->preferred_parent != NULL) { nbr = rpl_get_nbr(instance->current_dag->preferred_parent); } else { nbr = NULL; } /* Write all RPL info in JSON format */ snprintf(message, sizeof(message) - 1, "{\"parent\":\""); length = strlen(message); if(instance != NULL && instance->current_dag != NULL && instance->current_dag->preferred_parent != NULL) { sprint_addr6(&message[length], rpl_get_parent_ipaddr(instance->current_dag->preferred_parent)); length = strlen(message); snprintf(&message[length], sizeof(message) - length - 1, "\""); } else { snprintf(&message[length], sizeof(message) - length - 1, "None\""); } length = strlen(message); snprintf(&message[length], sizeof(message) - length - 1, " ,\"rank\":\""); length = strlen(message); if(instance != NULL && instance->current_dag != NULL) { snprintf(&message[length], sizeof(message) - length - 1, "%u.%02u\"", (instance->current_dag->rank / RPL_DAG_MC_ETX_DIVISOR), (100 * (instance->current_dag->rank % RPL_DAG_MC_ETX_DIVISOR)) / RPL_DAG_MC_ETX_DIVISOR); } else { snprintf(&message[length], sizeof(message) - length - 1, "inf\""); } length = strlen(message); snprintf(&message[length], sizeof(message) - length - 1, " ,\"link-metric\":\""); length = strlen(message); if(nbr != NULL) { snprintf(&message[length], sizeof(message) - length - 1, "%u.%02u\"", nbr->link_metric / RPL_DAG_MC_ETX_DIVISOR, (100 * (nbr->link_metric % RPL_DAG_MC_ETX_DIVISOR)) / RPL_DAG_MC_ETX_DIVISOR); } else { snprintf(&message[length], sizeof(message) - length - 1, "inf\""); } length = strlen(message); snprintf(&message[length], sizeof(message) - length - 1, "}"); length = strlen(message); memcpy(buffer, message, length); REST.set_header_content_type(response, REST.type.APPLICATION_JSON); REST.set_header_etag(response, (uint8_t *)&length, 1); REST.set_response_payload(response, buffer, length); }