Changes in RPL implementation :

- Structural modification of RPL data storage.
- Support multiple gateways (multiple DODAG-ID with a unique InstanceID)
- Use Lollipop counters
- Add leaf-only configuration option for RPL
Bugfix :
- Correctly send "Grounded" flag in DIO
This commit is contained in:
Vincent Brillault 2011-07-11 15:50:51 +02:00
parent 2ac91d53b5
commit dc9cbe647d
15 changed files with 1497 additions and 555 deletions

View file

@ -72,7 +72,7 @@ static void dio_input(void);
static void dao_input(void);
static void dao_ack_input(void);
static uint8_t dao_sequence;
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
/*---------------------------------------------------------------------------*/
static int
get_global_addr(uip_ipaddr_t *addr)
@ -112,21 +112,27 @@ set32(uint8_t *buffer, int pos, uint32_t value)
static void
dis_input(void)
{
rpl_dag_t *dag;
rpl_instance_t *instance;
rpl_instance_t *end;
/* DAG Information Solicitation */
PRINTF("RPL: Received a DIS from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF("\n");
dag = rpl_get_dag(RPL_ANY_INSTANCE);
if(dag != NULL) {
if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
PRINTF("RPL: Multicast DIS => reset DIO timer\n");
rpl_reset_dio_timer(dag, 0);
} else {
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(dag, &UIP_IP_BUF->srcipaddr);
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)) {
#else /* !RPL_LEAF_ONLY */
if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
PRINTF("RPL: Multicast DIS => reset DIO timer\n");
rpl_reset_dio_timer(instance, 0);
} else {
#endif /* !RPL_LEAF_ONLY */
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(instance, &UIP_IP_BUF->srcipaddr);
}
}
}
}
@ -199,7 +205,7 @@ dio_input(void)
buffer_length = uip_len - uip_l2_l3_icmp_hdr_len;
#if RPL_CONF_ADJUST_LLH_LEN
buffer_length+=UIP_LLH_LEN; //Add jackdaw, minimal-net ethernet header
buffer_length += UIP_LLH_LEN; /* Add jackdaw, minimal-net ethernet header */
#endif
/* Process the DIO base option. */
@ -211,6 +217,7 @@ dio_input(void)
dio.rank = (buffer[i] << 8) | buffer[i + 1];
i += 2;
PRINTF("RPL: Incoming DIO InstanceID-Version %u-%u\n", (unsigned)dio.instance_id,(unsigned)dio.version);
PRINTF("RPL: Incoming DIO rank %u\n", (unsigned)dio.rank);
dio.grounded = buffer[i] & RPL_DIO_GROUNDED;
@ -224,6 +231,10 @@ dio_input(void)
memcpy(&dio.dag_id, buffer + i, sizeof(dio.dag_id));
i += sizeof(dio.dag_id);
PRINTF("RPL: Incoming DIO DODAG ");
PRINT6ADDR(&dio.dag_id);
PRINTF(" preference : %u\n",dio.preference);
/* Check if there are any DIO suboptions. */
for(; i < buffer_length; i += len) {
subopt_type = buffer[i];
@ -249,7 +260,6 @@ dio_input(void)
RPL_STAT(rpl_stats.malformed_msgs++);
return;
}
dio.mc.type = buffer[i + 2];
dio.mc.flags = buffer[i + 3] << 1;
dio.mc.flags |= buffer[i + 4] >> 7;
@ -347,30 +357,50 @@ dio_input(void)
}
/*---------------------------------------------------------------------------*/
void
dio_output(rpl_dag_t *dag, uip_ipaddr_t *uc_addr)
dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
{
unsigned char *buffer;
int pos;
#if !RPL_LEAF_ONLY
uip_ipaddr_t addr;
#endif /* !RPL_LEAF_ONLY */
#if RPL_LEAF_ONLY
/* only respond to unicast DIS */
if(uc_addr == NULL) {
return;
}
#endif /* RPL_LEAF_ONLY */
rpl_dag_t *dag = instance->current_dag;
/* DAG Information Object */
pos = 0;
buffer = UIP_ICMP_PAYLOAD;
buffer[pos++] = dag->instance_id;
buffer[pos++] = instance->instance_id;
buffer[pos++] = dag->version;
#if RPL_LEAF_ONLY
buffer[pos++] = INFINITE_RANK >> 8;
buffer[pos++] = INFINITE_RANK & 0xff;
#else /* RPL_LEAF_ONLY */
buffer[pos++] = dag->rank >> 8;
buffer[pos++] = dag->rank & 0xff;
#endif /* RPL_LEAF_ONLY */
buffer[pos] = 0;
if(dag->grounded) {
buffer[pos] |= RPL_DIO_GROUNDED;
}
buffer[pos] = dag->mop << RPL_DIO_MOP_SHIFT;
buffer[pos] |= instance->mop << RPL_DIO_MOP_SHIFT;
buffer[pos] |= dag->preference & RPL_DIO_PREFERENCE_MASK;
pos++;
buffer[pos++] = ++dag->dtsn_out;
buffer[pos++] = instance->dtsn_out;
if (RPL_LOLLIPOP_IS_INIT(instance->dtsn_out))
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
/* reserved 2 bytes */
buffer[pos++] = 0; /* flags */
@ -379,49 +409,51 @@ dio_output(rpl_dag_t *dag, uip_ipaddr_t *uc_addr)
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
pos += 16;
if(dag->mc.type != RPL_DAG_MC_NONE) {
dag->of->update_metric_container(dag);
#if !RPL_LEAF_ONLY
if(instance->mc.type != RPL_DAG_MC_NONE) {
instance->of->update_metric_container(instance);
buffer[pos++] = RPL_DIO_SUBOPT_DAG_METRIC_CONTAINER;
buffer[pos++] = 6;
buffer[pos++] = dag->mc.type;
buffer[pos++] = dag->mc.flags >> 1;
buffer[pos] = (dag->mc.flags & 1) << 7;
buffer[pos++] |= (dag->mc.aggr << 4) | dag->mc.prec;
if(dag->mc.type == RPL_DAG_MC_ETX) {
buffer[pos++] = instance->mc.type;
buffer[pos++] = instance->mc.flags >> 1;
buffer[pos] = (instance->mc.flags & 1) << 7;
buffer[pos++] |= (instance->mc.aggr << 4) | instance->mc.prec;
if(instance->mc.type == RPL_DAG_MC_ETX) {
buffer[pos++] = 2;
buffer[pos++] = dag->mc.obj.etx >> 8;
buffer[pos++] = dag->mc.obj.etx & 0xff;
} else if(dag->mc.type == RPL_DAG_MC_ENERGY) {
buffer[pos++] = instance->mc.obj.etx >> 8;
buffer[pos++] = instance->mc.obj.etx & 0xff;
} else if(instance->mc.type == RPL_DAG_MC_ENERGY) {
buffer[pos++] = 2;
buffer[pos++] = dag->mc.obj.energy.flags;
buffer[pos++] = dag->mc.obj.energy.energy_est;
buffer[pos++] = instance->mc.obj.energy.flags;
buffer[pos++] = instance->mc.obj.energy.energy_est;
} else {
PRINTF("RPL: Unable to send DIO because of unhandled DAG MC type %u\n",
(unsigned)dag->mc.type);
(unsigned)instance->mc.type);
return;
}
}
/* Always add a sub-option for DAG configuration. */
#endif /* RPL_LEAF_ONLY */
/* Always add a sub-option for DAG configuration */
buffer[pos++] = RPL_DIO_SUBOPT_DAG_CONF;
buffer[pos++] = 14;
buffer[pos++] = 0; /* No Auth, PCS = 0 */
buffer[pos++] = dag->dio_intdoubl;
buffer[pos++] = dag->dio_intmin;
buffer[pos++] = dag->dio_redundancy;
buffer[pos++] = dag->max_rankinc >> 8;
buffer[pos++] = dag->max_rankinc & 0xff;
buffer[pos++] = dag->min_hoprankinc >> 8;
buffer[pos++] = dag->min_hoprankinc & 0xff;
buffer[pos++] = instance->dio_intdoubl;
buffer[pos++] = instance->dio_intmin;
buffer[pos++] = instance->dio_redundancy;
buffer[pos++] = instance->max_rankinc >> 8;
buffer[pos++] = instance->max_rankinc & 0xff;
buffer[pos++] = instance->min_hoprankinc >> 8;
buffer[pos++] = instance->min_hoprankinc & 0xff;
/* OCP is in the DAG_CONF option */
buffer[pos++] = dag->of->ocp >> 8;
buffer[pos++] = dag->of->ocp & 0xff;
buffer[pos++] = instance->of->ocp >> 8;
buffer[pos++] = instance->of->ocp & 0xff;
buffer[pos++] = 0; /* reserved */
buffer[pos++] = dag->default_lifetime;
buffer[pos++] = dag->lifetime_unit >> 8;
buffer[pos++] = dag->lifetime_unit & 0xff;
buffer[pos++] = instance->default_lifetime;
buffer[pos++] = instance->lifetime_unit >> 8;
buffer[pos++] = instance->lifetime_unit & 0xff;
/* Check if we have a prefix to send also. */
if(dag->prefix_info.length > 0) {
@ -445,19 +477,27 @@ dio_output(rpl_dag_t *dag, uip_ipaddr_t *uc_addr)
dag->prefix_info.length);
}
#if RPL_LEAF_ONLY
PRINTF("RPL: Sending unicast-DIO with rank %u to ",
(unsigned)dag->rank);
PRINT6ADDR(uc_addr);
PRINTF("\n");
uip_icmp6_send(uc_addr, ICMP6_RPL, RPL_CODE_DIO, pos);
#else /* RPL_LEAF_ONLY */
/* Unicast requests get unicast replies! */
if(uc_addr == NULL) {
PRINTF("RPL: Sending a multicast-DIO with rank %u\n",
(unsigned)dag->rank);
(unsigned)instance->current_dag->rank);
uip_create_linklocal_rplnodes_mcast(&addr);
uip_icmp6_send(&addr, ICMP6_RPL, RPL_CODE_DIO, pos);
} else {
PRINTF("RPL: Sending unicast-DIO with rank %u to ",
(unsigned)dag->rank);
(unsigned)instance->current_dag->rank);
PRINT6ADDR(uc_addr);
PRINTF("\n");
uip_icmp6_send(uc_addr, ICMP6_RPL, RPL_CODE_DIO, pos);
}
#endif /* RPL_LEAF_ONLY */
}
/*---------------------------------------------------------------------------*/
static void
@ -465,10 +505,11 @@ dao_input(void)
{
uip_ipaddr_t dao_sender_addr;
rpl_dag_t *dag;
rpl_instance_t *instance;
unsigned char *buffer;
uint16_t sequence;
uint8_t instance_id;
rpl_lifetime_t lifetime;
uint8_t lifetime;
uint8_t prefixlen;
uint8_t flags;
uint8_t subopt_type;
@ -494,32 +535,37 @@ dao_input(void)
buffer = UIP_ICMP_PAYLOAD;
buffer_length = uip_len - uip_l2_l3_icmp_hdr_len;
#if RPL_CONF_ADJUST_LLH_LEN
buffer_length += UIP_LLH_LEN; /* Add jackdaw, minimal-net ethernet header */
#endif
pos = 0;
instance_id = buffer[pos++];
dag = rpl_get_dag(instance_id);
if(dag == NULL) {
PRINTF("RPL: Ignoring a DAO for a different RPL instance (%u)\n",
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
instance_id);
return;
}
lifetime = dag->default_lifetime;
lifetime = instance->default_lifetime;
flags = buffer[pos++];
/* reserved */
pos++;
sequence = buffer[pos++];
dag = instance->current_dag;
/* Is the DAGID present? */
if(flags & RPL_DAO_D_FLAG) {
/* Currently the DAG ID is ignored since we only use global
RPL Instance IDs. */
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
PRINTF("RPL: Ignoring a DAO for a DODAG different from ours\n");
return;
}
pos += 16;
} else {
/* Perhaps, there are verification to do but ... */
}
/* Check if there are any DIO sub-options. */
@ -529,7 +575,7 @@ dao_input(void)
if(subopt_type == RPL_DIO_SUBOPT_PAD1) {
len = 1;
} else {
/* Sub-option with a two-byte header and payload */
/* Sub-option with a two-byte header + payload */
len = 2 + buffer[i + 1];
}
@ -551,7 +597,7 @@ dao_input(void)
}
PRINTF("RPL: DAO lifetime: %u, prefix length: %u prefix: ",
(unsigned)lifetime, (unsigned)prefixlen);
(unsigned)lifetime, (unsigned)prefixlen);
PRINT6ADDR(&prefix);
PRINTF("\n");
@ -559,7 +605,7 @@ dao_input(void)
if(lifetime == ZERO_LIFETIME) {
/* No-Path DAO received; invoke the route purging routine. */
if(rep != NULL && rep->state.saved_lifetime == 0) {
if(rep != NULL && rep->state.saved_lifetime == 0 && rep->length==prefixlen) {
PRINTF("RPL: Setting expiration timer for prefix ");
PRINT6ADDR(&prefix);
PRINTF("\n");
@ -575,9 +621,11 @@ dao_input(void)
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
/* Check whether this is a DAO forwarding loop. */
p = rpl_find_parent(dag, &dao_sender_addr);
if(p != NULL && DAG_RANK(p->rank, dag) < DAG_RANK(dag->rank, dag)) {
/* check if this is a new DAO registration with an "illegal" rank */
/* if we already route to this node it is likely */
if(p != NULL && DAG_RANK(p->rank, instance) < DAG_RANK(dag->rank, instance)) {
PRINTF("RPL: Loop detected when receiving a unicast DAO from a node with a lower rank! (%u < %u)\n",
DAG_RANK(p->rank, dag), DAG_RANK(dag->rank, dag));
DAG_RANK(p->rank, instance), DAG_RANK(dag->rank, instance));
p->rank = INFINITE_RANK;
p->updated = 1;
return;
@ -593,7 +641,7 @@ dao_input(void)
}
}
rep->state.lifetime = RPL_LIFETIME(dag, lifetime);
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
rep->state.learned_from = learned_from;
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
@ -603,16 +651,18 @@ dao_input(void)
PRINTF("\n");
uip_icmp6_send(&dag->preferred_parent->addr,
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
} else if(flags & RPL_DAO_K_FLAG) {
dao_ack_output(dag, &dao_sender_addr, sequence);
}
if(flags & RPL_DAO_K_FLAG) {
dao_ack_output(instance, &dao_sender_addr, sequence);
}
}
}
/*---------------------------------------------------------------------------*/
void
dao_output(rpl_parent_t *n, rpl_lifetime_t lifetime)
dao_output(rpl_parent_t *n, uint8_t lifetime)
{
rpl_dag_t *dag;
rpl_instance_t *instance;
unsigned char *buffer;
uint8_t prefixlen;
uip_ipaddr_t addr;
@ -626,7 +676,7 @@ dao_output(rpl_parent_t *n, rpl_lifetime_t lifetime)
}
if(n == NULL) {
dag = rpl_get_dag(RPL_ANY_INSTANCE);
dag = rpl_get_any_dag();
if(dag == NULL) {
PRINTF("RPL: Did not join a DAG before sending DAO\n");
return;
@ -635,19 +685,28 @@ dao_output(rpl_parent_t *n, rpl_lifetime_t lifetime)
dag = n->dag;
}
instance = dag->instance;
buffer = UIP_ICMP_PAYLOAD;
++dao_sequence;
RPL_LOLLIPOP_INCREMENT(dao_sequence);
pos = 0;
buffer[pos++] = dag->instance_id;
buffer[pos++] = instance->instance_id;
buffer[pos] = 0;
#if RPL_DAO_SPECIFY_DODAG
buffer[pos] |= RPL_DAO_D_FLAG;
#endif /* RPL_DAO_SPECIFY_DODAG */
#if RPL_CONF_DAO_ACK
buffer[pos++] = RPL_DAO_K_FLAG; /* DAO ACK request, no DODAGID */
#else
buffer[pos++] = 0; /* No DAO ACK request, no DODAGID */
#endif
buffer[pos] |= RPL_DAO_K_FLAG;
#endif /* RPL_CONF_DAO_ACK */
++pos;
buffer[pos++] = 0; /* reserved */
buffer[pos++] = dao_sequence & 0xff;
buffer[pos++] = dao_sequence;
#if RPL_DAO_SPECIFY_DODAG
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
pos+=sizeof(dag->dag_id);
#endif /* RPL_DAO_SPECIFY_DODAG */
/* create target subopt */
prefixlen = sizeof(prefix) * CHAR_BIT;
@ -696,9 +755,6 @@ dao_ack_input(void)
buffer = UIP_ICMP_PAYLOAD;
buffer_length = uip_len - uip_l2_l3_icmp_hdr_len;
#if RPL_CONF_ADJUST_LLH_LEN
buffer_length+=UIP_LLH_LEN; //Add jackdaw, minimal-net ethernet header
#endif
instance_id = buffer[0];
sequence = buffer[2];
@ -711,7 +767,7 @@ dao_ack_input(void)
}
/*---------------------------------------------------------------------------*/
void
dao_ack_output(rpl_dag_t *dag, uip_ipaddr_t *dest, uint8_t sequence)
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence)
{
unsigned char *buffer;
@ -721,7 +777,7 @@ dao_ack_output(rpl_dag_t *dag, uip_ipaddr_t *dest, uint8_t sequence)
buffer = UIP_ICMP_PAYLOAD;
buffer[0] = dag->instance_id;
buffer[0] = instance->instance_id;
buffer[1] = 0;
buffer[2] = sequence;
buffer[3] = 0;