diff --git a/core/net/rpl/rpl-dag.c b/core/net/rpl/rpl-dag.c index f7d37a6e5..8f228e37b 100644 --- a/core/net/rpl/rpl-dag.c +++ b/core/net/rpl/rpl-dag.c @@ -78,10 +78,16 @@ NBR_TABLE(rpl_parent_t, rpl_parents); rpl_instance_t instance_table[RPL_MAX_INSTANCES]; rpl_instance_t *default_instance; /*---------------------------------------------------------------------------*/ +static void +nbr_callback(void *ptr) +{ + rpl_remove_parent(ptr); +} + void rpl_dag_init(void) { - nbr_table_register(rpl_parents, (nbr_table_callback *)rpl_remove_parent); + nbr_table_register(rpl_parents, (nbr_table_callback *)nbr_callback); } /*---------------------------------------------------------------------------*/ rpl_rank_t @@ -178,7 +184,7 @@ nullify_parents(rpl_dag_t *dag, rpl_rank_t minimum_rank) { rpl_parent_t *p; - PRINTF("RPL: Removing parents (minimum rank %u)\n", + PRINTF("RPL: Nullifying parents (minimum rank %u)\n", minimum_rank); p = nbr_table_head(rpl_parents); @@ -541,17 +547,23 @@ rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr) * 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\n", lladdr); + PRINTF("RPL: rpl_add_parent lladdr %p ", lladdr); + PRINT6ADDR(addr); + PRINTF("\n"); if(lladdr != NULL) { /* Add parent in rpl_parents */ p = nbr_table_add_lladdr(rpl_parents, (rimeaddr_t *)lladdr); - p->dag = dag; - p->rank = dio->rank; - p->dtsn = dio->dtsn; - p->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR; + if(p == NULL) { + PRINTF("RPL: rpl_add_parent p NULL\n"); + } else { + p->dag = dag; + p->rank = dio->rank; + p->dtsn = dio->dtsn; + p->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)); + memcpy(&p->mc, &dio->mc, sizeof(p->mc)); #endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */ + } } return p; @@ -689,8 +701,8 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p) return best_dag; } /*---------------------------------------------------------------------------*/ -rpl_parent_t * -rpl_select_parent(rpl_dag_t *dag) +static rpl_parent_t * +best_parent(rpl_dag_t *dag) { rpl_parent_t *p, *best; @@ -708,6 +720,14 @@ rpl_select_parent(rpl_dag_t *dag) p = nbr_table_next(rpl_parents, p); } + return best; +} +/*---------------------------------------------------------------------------*/ +rpl_parent_t * +rpl_select_parent(rpl_dag_t *dag) +{ + rpl_parent_t *best = best_parent(dag); + if(best != NULL) { rpl_set_preferred_parent(dag, best); } @@ -776,9 +796,7 @@ rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent) PRINT6ADDR(rpl_get_parent_ipaddr(parent)); PRINTF("\n"); - list_remove(dag_src->parents, parent); parent->dag = dag_dst; - list_add(dag_dst->parents, parent); } /*---------------------------------------------------------------------------*/ rpl_dag_t * @@ -1192,7 +1210,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio) } 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) { @@ -1277,4 +1295,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio) p->dtsn = dio->dtsn; } /*---------------------------------------------------------------------------*/ +void +rpl_lock_parent(rpl_parent_t *p) +{ + nbr_table_lock(rpl_parents, p); +} +/*---------------------------------------------------------------------------*/ #endif /* UIP_CONF_IPV6 */ diff --git a/core/net/rpl/rpl-ext-header.c b/core/net/rpl/rpl-ext-header.c index e2293c8ec..310c056cb 100644 --- a/core/net/rpl/rpl-ext-header.c +++ b/core/net/rpl/rpl-ext-header.c @@ -69,6 +69,7 @@ rpl_verify_header(int uip_ext_opt_offset) rpl_instance_t *instance; int down; uint8_t sender_closer; + uip_ds6_route_t *route; if(UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) { PRINTF("RPL: Bad header option! (wrong length)\n"); @@ -83,11 +84,11 @@ rpl_verify_header(int uip_ext_opt_offset) } if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_FWD_ERR) { + PRINTF("RPL: Forward error!\n"); /* We should try to repair it by removing the neighbor that caused the packet to be forwareded in the first place. We drop any routes that go through the neighbor that sent the packet to us. */ - uip_ds6_route_t *route; route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr); if(route != NULL) { uip_ds6_route_rm(route); diff --git a/core/net/rpl/rpl-icmp6.c b/core/net/rpl/rpl-icmp6.c index 72aaa461b..f1d5a5e04 100644 --- a/core/net/rpl/rpl-icmp6.c +++ b/core/net/rpl/rpl-icmp6.c @@ -146,7 +146,8 @@ dis_input(void) PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINTF("\n"); - for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) { + for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; + instance < end; ++instance) { if(instance->used == 1) { #if RPL_LEAF_ONLY if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) { @@ -578,6 +579,7 @@ dao_input(void) int i; int learned_from; rpl_parent_t *p; + uip_ds6_nbr_t *nbr; prefixlen = 0; @@ -667,6 +669,20 @@ dao_input(void) PRINTF("\n"); rep->state.nopath_received = 1; rep->state.lifetime = DAO_EXPIRATION_TIMEOUT; + + /* We forward the incoming no-path DAO to our parent, if we have + one. */ + if(dag->preferred_parent != NULL && + rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) { + PRINTF("RPL: Forwarding no-path DAO to parent "); + PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent)); + PRINTF("\n"); + uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent), + ICMP6_RPL, RPL_CODE_DAO, buffer_length); + } + if(flags & RPL_DAO_K_FLAG) { + dao_ack_output(instance, &dao_sender_addr, sequence); + } } return; } @@ -700,6 +716,32 @@ dao_input(void) } PRINTF("RPL: adding DAO route\n"); + + if((nbr = uip_ds6_nbr_lookup(&dao_sender_addr)) == NULL) { + if((nbr = uip_ds6_nbr_add(&dao_sender_addr, + (uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER), + 0, NBR_REACHABLE)) != NULL) { + /* set reachable timer */ + stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000); + PRINTF("RPL: Neighbor added to neighbor cache "); + PRINT6ADDR(&dao_sender_addr); + PRINTF(", "); + PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER)); + PRINTF("\n"); + } else { + PRINTF("RPL: Out of Memory, dropping DAO from "); + PRINT6ADDR(&dao_sender_addr); + PRINTF(", "); + PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER)); + PRINTF("\n"); + return; + } + } else { + PRINTF("RPL: Neighbor already in neighbor cache\n"); + } + + rpl_lock_parent(p); + rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr); if(rep == NULL) { RPL_STAT(rpl_stats.mem_overflows++); @@ -751,6 +793,11 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime) /* Destination Advertisement Object */ + /* If we are in feather mode, we should not send any DAOs */ + if(rpl_get_mode() == RPL_MODE_FEATHER) { + return; + } + if(parent == NULL) { PRINTF("RPL dao_output_target error parent NULL\n"); return; diff --git a/core/net/rpl/rpl-private.h b/core/net/rpl/rpl-private.h index bc307e459..0aaee680e 100644 --- a/core/net/rpl/rpl-private.h +++ b/core/net/rpl/rpl-private.h @@ -304,11 +304,17 @@ uip_ds6_route_t *rpl_add_route(rpl_dag_t *dag, uip_ipaddr_t *prefix, int prefix_len, uip_ipaddr_t *next_hop); void rpl_purge_routes(void); +/* Lock a parent in the neighbor cache. */ +void rpl_lock_parent(rpl_parent_t *p); + /* Objective function. */ rpl_of_t *rpl_find_of(rpl_ocp_t); /* Timer functions. */ void rpl_schedule_dao(rpl_instance_t *); +void rpl_schedule_dao_immediately(rpl_instance_t *); +void rpl_cancel_dao(rpl_instance_t *instance); + void rpl_reset_dio_timer(rpl_instance_t *); void rpl_reset_periodic_timer(void); diff --git a/core/net/rpl/rpl-timers.c b/core/net/rpl/rpl-timers.c index 51f440f3f..acc1badf0 100644 --- a/core/net/rpl/rpl-timers.c +++ b/core/net/rpl/rpl-timers.c @@ -194,6 +194,28 @@ rpl_reset_dio_timer(rpl_instance_t *instance) #endif /* RPL_LEAF_ONLY */ } /*---------------------------------------------------------------------------*/ +static void handle_dao_timer(void *ptr); +static void +set_dao_lifetime_timer(rpl_instance_t *instance) +{ + if(rpl_get_mode() == RPL_MODE_FEATHER) { + return; + } + + /* Set up another DAO within half the expiration time, if such a + time has been configured */ + if(instance->lifetime_unit != 0xffff && instance->default_lifetime != 0xff) { + clock_time_t expiration_time; + expiration_time = (clock_time_t)instance->default_lifetime * + (clock_time_t)instance->lifetime_unit * + CLOCK_SECOND / 2; + PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n", + (unsigned)expiration_time); + ctimer_set(&instance->dao_lifetime_timer, expiration_time, + handle_dao_timer, instance); + } +} +/*---------------------------------------------------------------------------*/ static void handle_dao_timer(void *ptr) { @@ -215,26 +237,60 @@ handle_dao_timer(void *ptr) } else { PRINTF("RPL: No suitable DAO parent\n"); } + ctimer_stop(&instance->dao_timer); + + if(etimer_expired(&instance->dao_lifetime_timer.etimer)) { + set_dao_lifetime_timer(instance); + } } /*---------------------------------------------------------------------------*/ -void -rpl_schedule_dao(rpl_instance_t *instance) +static void +schedule_dao(rpl_instance_t *instance, clock_time_t latency) { clock_time_t expiration_time; + if(rpl_get_mode() == RPL_MODE_FEATHER) { + return; + } + expiration_time = etimer_expiration_time(&instance->dao_timer.etimer); if(!etimer_expired(&instance->dao_timer.etimer)) { PRINTF("RPL: DAO timer already scheduled\n"); } else { - expiration_time = RPL_DAO_LATENCY / 2 + - (random_rand() % (RPL_DAO_LATENCY)); + if(latency != 0) { + expiration_time = latency / 2 + + (random_rand() % (latency)); + } else { + expiration_time = 0; + } PRINTF("RPL: Scheduling DAO timer %u ticks in the future\n", (unsigned)expiration_time); ctimer_set(&instance->dao_timer, expiration_time, handle_dao_timer, instance); + + set_dao_lifetime_timer(instance); } } /*---------------------------------------------------------------------------*/ +void +rpl_schedule_dao(rpl_instance_t *instance) +{ + schedule_dao(instance, RPL_DAO_LATENCY); +} +/*---------------------------------------------------------------------------*/ +void +rpl_schedule_dao_immediately(rpl_instance_t *instance) +{ + schedule_dao(instance, 0); +} +/*---------------------------------------------------------------------------*/ +void +rpl_cancel_dao(rpl_instance_t *instance) +{ + ctimer_stop(&instance->dao_timer); + ctimer_stop(&instance->dao_lifetime_timer); +} +/*---------------------------------------------------------------------------*/ #endif /* UIP_CONF_IPV6 */ diff --git a/core/net/rpl/rpl.c b/core/net/rpl/rpl.c index 4ff9b1c6a..7e3111d65 100644 --- a/core/net/rpl/rpl.c +++ b/core/net/rpl/rpl.c @@ -57,6 +57,47 @@ rpl_stats_t rpl_stats; #endif +static enum rpl_mode mode = RPL_MODE_MESH; +/*---------------------------------------------------------------------------*/ +enum rpl_mode +rpl_get_mode(void) +{ + return mode; +} +/*---------------------------------------------------------------------------*/ +enum rpl_mode +rpl_set_mode(enum rpl_mode m) +{ + enum rpl_mode oldmode = mode; + + /* We need to do different things depending on what mode we are + switching to. */ + if(m == RPL_MODE_MESH) { + + /* If we switcht to mesh mode, we should send out a DAO message to + inform our parent that we now are reachable. Before we do this, + we must set the mode variable, since DAOs will not be send if + we are in feather mode. */ + PRINTF("RPL: switching to mesh mode\n"); + mode = m; + + if(default_instance != NULL) { + rpl_schedule_dao_immediately(default_instance); + } + } else if(m == RPL_MODE_FEATHER) { + + PRINTF("RPL: switching to feather mode\n"); + mode = m; + if(default_instance != NULL) { + rpl_cancel_dao(default_instance); + } + + } else { + mode = m; + } + + return oldmode; +} /*---------------------------------------------------------------------------*/ void rpl_purge_routes(void) diff --git a/core/net/rpl/rpl.h b/core/net/rpl/rpl.h index b83e140ed..7d2a5ee70 100644 --- a/core/net/rpl/rpl.h +++ b/core/net/rpl/rpl.h @@ -45,9 +45,6 @@ #include "net/uip-ds6.h" #include "sys/ctimer.h" -/*---------------------------------------------------------------------------*/ -/* The amount of parents that this node has in a particular DAG. */ -#define RPL_PARENT_COUNT(dag) list_length((dag)->parents) /*---------------------------------------------------------------------------*/ typedef uint16_t rpl_rank_t; typedef uint16_t rpl_ocp_t; @@ -142,7 +139,6 @@ struct rpl_dag { rpl_parent_t *preferred_parent; rpl_rank_t rank; struct rpl_instance *instance; - LIST_STRUCT(parents); rpl_prefix_t prefix_info; }; typedef struct rpl_dag rpl_dag_t; @@ -225,6 +221,7 @@ struct rpl_instance { clock_time_t dio_next_delay; /* delay for completion of dio interval */ struct ctimer dio_timer; struct ctimer dao_timer; + struct ctimer dao_lifetime_timer; }; /*---------------------------------------------------------------------------*/ @@ -247,5 +244,38 @@ uip_ipaddr_t *rpl_get_parent_ipaddr(rpl_parent_t *nbr); rpl_rank_t rpl_get_parent_rank(uip_lladdr_t *addr); uint16_t rpl_get_parent_link_metric(const uip_lladdr_t *addr); void rpl_dag_init(void); + + +/** + * RPL modes + * + * The RPL module can be in either of three modes: mesh mode + * (RPL_MODE_MESH), feater mode (RPL_MODE_FEATHER), and leaf mode + * (RPL_MODE_LEAF). In mesh mode, nodes forward data for other nodes, + * and are reachable by others. In feather mode, nodes can forward + * data for other nodes, but are not reachable themselves. In leaf + * mode, nodes do not forward data for others, but are reachable by + * others. */ +enum rpl_mode { + RPL_MODE_MESH = 0, + RPL_MODE_FEATHER = 1, + RPL_MODE_LEAF = 2, +}; + +/** + * Set the RPL mode + * + * \param mode The new RPL mode + * \retval The previous RPL mode + */ +enum rpl_mode rpl_set_mode(enum rpl_mode mode); + +/** + * Get the RPL mode + * + * \retval The RPL mode + */ +enum rpl_mode rpl_get_mode(void); + /*---------------------------------------------------------------------------*/ #endif /* RPL_H */ diff --git a/examples/ipv6/rpl-collect/udp-sender.c b/examples/ipv6/rpl-collect/udp-sender.c index 75d63e806..cc8ed3bc1 100644 --- a/examples/ipv6/rpl-collect/udp-sender.c +++ b/examples/ipv6/rpl-collect/udp-sender.c @@ -142,7 +142,7 @@ collect_common_send(void) } rtmetric = dag->rank; beacon_interval = (uint16_t) ((2L << dag->instance->dio_intcurrent) / 1000); - num_neighbors = RPL_PARENT_COUNT(dag); + num_neighbors = uip_ds6_nbr_num(); } else { rtmetric = 0; beacon_interval = 0; diff --git a/regression-tests/11-ipv6/03-sky-ipv6-udp-fragmentation.csc b/regression-tests/11-ipv6/x03-sky-ipv6-udp-fragmentation.csc similarity index 100% rename from regression-tests/11-ipv6/03-sky-ipv6-udp-fragmentation.csc rename to regression-tests/11-ipv6/x03-sky-ipv6-udp-fragmentation.csc