rpl nearly finished, rpl_test now with shell
This commit is contained in:
parent
8124389151
commit
f69fbfd686
@ -1,5 +1,5 @@
|
||||
SubDir TOP projects test_rpl ;
|
||||
|
||||
Module test_rpl : main.c : auto_init vtimer 6lowpan uart0 posix_io cc110x_ng rpl ;
|
||||
Module test_rpl : main.c : shell posix_io uart0 auto_init vtimer 6lowpan uart0 posix_io cc110x_ng rpl ;
|
||||
|
||||
UseModule test_rpl ;
|
||||
|
||||
@ -2,25 +2,118 @@
|
||||
#include <string.h>
|
||||
#include <vtimer.h>
|
||||
#include <thread.h>
|
||||
|
||||
#include <posix_io.h>
|
||||
#include <shell.h>
|
||||
#include <board_uart0.h>
|
||||
|
||||
#include "sys/net/sixlowpan/sixlowip.h"
|
||||
#include "sys/net/sixlowpan/sixlowpan.h"
|
||||
#include "sys/net/sixlowpan/sixlowerror.h"
|
||||
#include "sys/net/sixlowpan/rpl/rpl.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint16_t root = 0x0002;
|
||||
ipv6_addr_t std_addr;
|
||||
uint16_t r_addr = root;
|
||||
void init(char *str){
|
||||
char command;
|
||||
uint16_t r_addr;
|
||||
ipv6_addr_t std_addr;
|
||||
|
||||
int res = sscanf(str, "init %c %hu", &command, &r_addr);
|
||||
|
||||
if(res < 1){
|
||||
printf("Usage: init address\n");
|
||||
printf("\tr\tinitialize as root\n");
|
||||
printf("\tn\tinitialize as node router\n");
|
||||
printf("\taddress must be an 8 bit integer\n");
|
||||
}
|
||||
|
||||
ipv6_init_address(&std_addr, 0xABCD,0,0,0,0x1234,0xFFFF,0xFEDC,r_addr);
|
||||
uint8_t state = rpl_init(TRANSCEIVER_CC1100, &std_addr);
|
||||
if(state != SUCCESS){
|
||||
printf("Error initializing RPL\n");
|
||||
}
|
||||
if(root == 0x0001){
|
||||
rpl_init_root();
|
||||
}
|
||||
printf("RPL INIT FINISHED\n");
|
||||
while(1);
|
||||
uint8_t state;
|
||||
switch (command) {
|
||||
case 'r':
|
||||
printf("INFO: Initialize as root on address \n");
|
||||
ipv6_print_addr(&std_addr);
|
||||
if (r_addr > 255) {
|
||||
printf("ERROR: address not an 8 bit integer\n");
|
||||
return;
|
||||
}
|
||||
state = rpl_init(TRANSCEIVER_CC1100, &std_addr);
|
||||
if(state != SUCCESS){
|
||||
printf("Error initializing RPL\n");
|
||||
}
|
||||
rpl_init_root();
|
||||
break;
|
||||
case 'n':
|
||||
printf("INFO: Initialize as node on address \n");
|
||||
ipv6_print_addr(&std_addr);
|
||||
if (r_addr > 255) {
|
||||
printf("ERROR: address not an 8 bit integer\n");
|
||||
return;
|
||||
}
|
||||
state = rpl_init(TRANSCEIVER_CC1100, &std_addr);
|
||||
if(state != SUCCESS){
|
||||
printf("Error initializing RPL\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: Unknown command '%c'\n", command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void table(char *str){
|
||||
rpl_routing_entry_t * rtable;
|
||||
rtable = rpl_get_routing_table();
|
||||
printf("---------------------------\n");
|
||||
printf("OUTPUT\n");
|
||||
printf("---------------------------\n");
|
||||
for(int i=0;i<RPL_MAX_ROUTING_ENTRIES;i++){
|
||||
if(rtable[i].used){
|
||||
ipv6_print_addr(&rtable[i].address);
|
||||
printf("--------------\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shell_command_t shell_commands[] = {
|
||||
{"init", "", init},
|
||||
{"table", "", table},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
int main(void) {
|
||||
printf("RPL Test Application\n");
|
||||
vtimer_init();
|
||||
|
||||
posix_open(uart0_handler_pid, 0);
|
||||
|
||||
shell_t shell;
|
||||
shell_init(&shell, shell_commands, uart0_readc, uart0_putc);
|
||||
|
||||
shell_run(&shell);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int old_main(void)
|
||||
{
|
||||
timex_t mytime = timex_set(10,0);
|
||||
while(1){
|
||||
|
||||
rpl_routing_entry_t * rtable;
|
||||
rtable = rpl_get_routing_table();
|
||||
printf("---------------------------\n");
|
||||
printf("OUTPUT\n");
|
||||
printf("---------------------------\n");
|
||||
for(int i=0;i<RPL_MAX_ROUTING_ENTRIES;i++){
|
||||
if(rtable[i].used){
|
||||
ipv6_print_addr(&rtable[i].address);
|
||||
printf("--------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
vtimer_sleep(mytime);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "sys/net/sixlowpan/sixlowmac.h"
|
||||
#include "sys/net/sixlowpan/sixlowip.h"
|
||||
#include "sys/net/sixlowpan/sixlowpan.h"
|
||||
#include "sys/net/sixlowpan/sixlownd.h"
|
||||
#include "sys/net/sixlowpan/sixlowerror.h"
|
||||
|
||||
char rpl_process_buf[RPL_PROCESS_STACKSIZE];
|
||||
@ -152,6 +153,7 @@ void send_DIO(ipv6_addr_t* destination){
|
||||
|
||||
icmp_buf->type = ICMP_RPL_CONTROL;
|
||||
icmp_buf->code = ICMP_CODE_DIO;
|
||||
icmp_buf->checksum = ~icmpv6_csum(PROTO_NUM_ICMPV6);
|
||||
|
||||
rpl_dio_buf = get_rpl_dio_buf();
|
||||
memset(rpl_dio_buf, 0, sizeof(*rpl_dio_buf));
|
||||
@ -163,8 +165,8 @@ void send_DIO(ipv6_addr_t* destination){
|
||||
rpl_dio_buf->flags = 0;
|
||||
rpl_dio_buf->reserved = 0;
|
||||
rpl_dio_buf->dodagid = mydodag->dodag_id;
|
||||
printf("Send DIO with DODAGID: \n");
|
||||
ipv6_print_addr(&rpl_dio_buf->dodagid);
|
||||
//printf("Send DIO with DODAGID: \n");
|
||||
//ipv6_print_addr(&rpl_dio_buf->dodagid);
|
||||
|
||||
int opt_hdr_len = 0;
|
||||
//DODAG Configuration Option!
|
||||
@ -195,7 +197,9 @@ void send_DIS(ipv6_addr_t *destination){
|
||||
icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len);
|
||||
|
||||
icmp_buf->type = ICMP_RPL_CONTROL;
|
||||
icmp_buf->code = ICMP_CODE_DIO;
|
||||
icmp_buf->code = ICMP_CODE_DIO;
|
||||
icmp_buf->checksum = ~icmpv6_csum(PROTO_NUM_ICMPV6);
|
||||
|
||||
rpl_dis_buf = get_rpl_dis_buf();
|
||||
|
||||
uint16_t plen = ICMPV6_HDR_LEN + DIS_BASE_LEN;
|
||||
@ -203,10 +207,14 @@ void send_DIS(ipv6_addr_t *destination){
|
||||
}
|
||||
|
||||
void send_DAO(){
|
||||
if(i_am_root){
|
||||
return;
|
||||
}
|
||||
icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len);
|
||||
|
||||
icmp_buf->type = ICMP_RPL_CONTROL;
|
||||
icmp_buf->code = ICMP_CODE_DAO;
|
||||
icmp_buf->checksum = ~icmpv6_csum(PROTO_NUM_ICMPV6);
|
||||
|
||||
rpl_dodag_t * my_dodag;
|
||||
my_dodag = rpl_get_my_dodag();
|
||||
@ -217,25 +225,32 @@ void send_DAO(){
|
||||
memset(rpl_dao_buf,0,sizeof(*rpl_dao_buf));
|
||||
rpl_dao_buf->rpl_instanceid = my_dodag->instance->id;
|
||||
//rpl_dao_buf->k_d_flags = 0x00;
|
||||
//TODO:dao_sequence handling
|
||||
rpl_dao_buf->dao_sequence = 0x00;
|
||||
rpl_dao_buf->dao_sequence = my_dodag->dao_seq;
|
||||
uint16_t opt_len = 0;
|
||||
rpl_opt_target_buf = get_rpl_opt_target_buf(DAO_BASE_LEN);
|
||||
//Alle Ziele aus der Routing Tabelle als Target eintragen
|
||||
//TODO: Ausnahme default_route zu Parent
|
||||
for(uint8_t i=0; i<RPL_MAX_ROUTING_ENTRIES;i++){
|
||||
if(routing_table[i].used){
|
||||
rpl_opt_target_buf->type=RPL_OPT_TARGET;
|
||||
rpl_opt_target_buf->length=RPL_OPT_TARGET_LEN;
|
||||
rpl_opt_target_buf->flags=0x00;
|
||||
rpl_opt_target_buf->prefix_length=16;
|
||||
rpl_opt_target_buf->prefix_length= RPL_DODAG_ID_LEN;
|
||||
memcpy(&rpl_opt_target_buf->target,&routing_table[i].address,sizeof(ipv6_addr_t));
|
||||
opt_len += RPL_OPT_TARGET_LEN +2;
|
||||
rpl_opt_target_buf = get_rpl_opt_target_buf(DAO_BASE_LEN + opt_len);
|
||||
}
|
||||
}
|
||||
//Add own address
|
||||
rpl_opt_target_buf->type=RPL_OPT_TARGET;
|
||||
rpl_opt_target_buf->length=RPL_OPT_TARGET_LEN;
|
||||
rpl_opt_target_buf->flags=0x00;
|
||||
rpl_opt_target_buf->prefix_length= RPL_DODAG_ID_LEN;
|
||||
memcpy(&rpl_opt_target_buf->target,&my_address,sizeof(ipv6_addr_t));
|
||||
printf("Sending DAO with length %d\n",rpl_opt_target_buf->prefix_length);
|
||||
opt_len += RPL_OPT_TARGET_LEN +2;
|
||||
|
||||
uint16_t plen = ICMPV6_HDR_LEN + DAO_BASE_LEN + opt_len;
|
||||
printf("Sending DAO\n");
|
||||
rpl_send(&my_dodag->my_preferred_parent->addr,(uint8_t*)icmp_buf, plen, PROTO_NUM_ICMPV6, NULL);
|
||||
}
|
||||
|
||||
@ -397,9 +412,11 @@ void recv_rpl_dio(void){
|
||||
|
||||
if(rpl_equal_id(&my_dodag->dodag_id, &dio_dodag.dodag_id)){
|
||||
//Mein DODAG
|
||||
if( dio_dodag.version > my_dodag->version){
|
||||
if(RPL_COUNTER_GREATER_THAN(dio_dodag.version,my_dodag->version) ){
|
||||
printf("New Version of dodag\n");
|
||||
if(my_dodag->my_rank == ROOT_RANK){
|
||||
//Jemand hat ein DIO mit einer höheren Version als der richtigen gesendet
|
||||
//Wir erhöhen diese Version noch einmal, und machen sie zur neuen
|
||||
my_dodag->version = RPL_COUNTER_INCREMENT(dio_dodag.version);
|
||||
reset_trickletimer();
|
||||
}
|
||||
@ -408,7 +425,8 @@ void recv_rpl_dio(void){
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if( dio_dodag.version < my_dodag->version){
|
||||
else if( RPL_COUNTER_GREATER_THAN(my_dodag->version, dio_dodag.version) ){
|
||||
//ein Knoten hat noch eine kleinere Versionsnummer -> mehr DIOs senden
|
||||
reset_trickletimer();
|
||||
return;
|
||||
}
|
||||
@ -427,6 +445,7 @@ void recv_rpl_dio(void){
|
||||
parent = rpl_find_parent(&ipv6_buf->srcaddr);
|
||||
if(parent == NULL){
|
||||
//neuen Elternknoten hinzufuegen
|
||||
//TODO: Checken, ob der Knoten parent sein darf
|
||||
parent = rpl_new_parent(&dio_dodag, &ipv6_buf->srcaddr, rpl_dio_buf->rank);
|
||||
if(parent == NULL){
|
||||
return;
|
||||
@ -492,10 +511,16 @@ void recv_rpl_dis(void){
|
||||
}
|
||||
|
||||
void recv_rpl_dao(void){
|
||||
printf("Receiving DAO\n");
|
||||
rpl_dodag_t *my_dodag = rpl_get_my_dodag();
|
||||
if(my_dodag == NULL){
|
||||
printf("[Error] got DAO without beeing part of a Dodag\n");
|
||||
return;
|
||||
}
|
||||
ipv6_buf = get_ipv6_buf();
|
||||
|
||||
rpl_dao_buf = get_rpl_dao_buf();
|
||||
int len = DAO_BASE_LEN;
|
||||
uint8_t increment_seq = 0;
|
||||
while(len < (ipv6_buf->length - ICMPV6_HDR_LEN) ){
|
||||
rpl_opt_buf = get_rpl_opt_buf(len);
|
||||
switch(rpl_opt_buf->type){
|
||||
@ -513,6 +538,12 @@ void recv_rpl_dao(void){
|
||||
break;
|
||||
}
|
||||
case(RPL_OPT_TARGET):{
|
||||
rpl_opt_target_buf = get_rpl_opt_target_buf(len);
|
||||
if(rpl_opt_target_buf->prefix_length != RPL_DODAG_ID_LEN){
|
||||
printf("prefixes are not supported yet");
|
||||
}
|
||||
rpl_add_routing_entry(&rpl_opt_target_buf->target, &ipv6_buf->srcaddr);
|
||||
increment_seq = 1;
|
||||
len += rpl_opt_buf->length +2;
|
||||
break;
|
||||
}
|
||||
@ -529,6 +560,10 @@ void recv_rpl_dao(void){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(increment_seq){
|
||||
RPL_COUNTER_INCREMENT(my_dodag->dao_seq);
|
||||
delay_dao();
|
||||
}
|
||||
}
|
||||
|
||||
void rpl_send(ipv6_addr_t *destination, uint8_t *payload, uint16_t p_len, uint8_t next_header, void *tcp_socket){
|
||||
@ -573,13 +608,17 @@ void rpl_send(ipv6_addr_t *destination, uint8_t *payload, uint16_t p_len, uint8_
|
||||
ipv6_addr_t *next_hop = rpl_get_next_hop(&ipv6_buf->destaddr);
|
||||
if(next_hop == NULL){
|
||||
if(i_am_root){
|
||||
//oops... entweder bin ich root und weiß nicht wohin mit dem paket, oder es ist kein
|
||||
//preferred parent eingetragen, was nicht passieren sollte.
|
||||
//oops... ich bin root und weiß nicht wohin mit dem paketn
|
||||
printf("[Error] destination unknown\n");
|
||||
return;
|
||||
}
|
||||
else{
|
||||
next_hop = rpl_get_my_preferred_parent();
|
||||
if(next_hop == NULL){
|
||||
//kein preferred parent eingetragen, was nicht passieren sollte.
|
||||
printf("[Error] no preferred parent\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
lowpan_init((ieee_802154_long_t*)&(next_hop->uint16[4]),(uint8_t*)ipv6_buf);
|
||||
@ -597,11 +636,12 @@ ipv6_addr_t *rpl_get_next_hop(ipv6_addr_t * addr){
|
||||
}
|
||||
|
||||
void rpl_add_routing_entry(ipv6_addr_t *addr, ipv6_addr_t *next_hop){
|
||||
//TODO: if no free entry, delete worst parent
|
||||
for(uint8_t i=0; i<RPL_MAX_ROUTING_ENTRIES; i++){
|
||||
if(!routing_table[i].used){
|
||||
routing_table[i].address = *addr;
|
||||
routing_table[i].next_hop = *next_hop;
|
||||
routing_table[i].used = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -621,3 +661,8 @@ void rpl_clear_routing_table(){
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//This function is for debug output purpose only...
|
||||
rpl_routing_entry_t *rpl_get_routing_table(void){
|
||||
return routing_table;
|
||||
}
|
||||
|
||||
@ -24,3 +24,4 @@ ipv6_addr_t *rpl_get_next_hop(ipv6_addr_t * addr);
|
||||
void rpl_add_routing_entry(ipv6_addr_t *addr, ipv6_addr_t *next_hop);
|
||||
void rpl_del_routing_entry(ipv6_addr_t *addr);
|
||||
void rpl_clear_routing_table();
|
||||
rpl_routing_entry_t *rpl_get_routing_table(void);
|
||||
|
||||
@ -87,7 +87,9 @@ void rpl_del_dodag(rpl_dodag_t *dodag){
|
||||
void rpl_leave_dodag(rpl_dodag_t * dodag){
|
||||
dodag->joined = 0;
|
||||
dodag->my_preferred_parent = NULL;
|
||||
//TODO: parents aus Liste löschen?
|
||||
//parents aus Liste löschen
|
||||
rpl_delete_all_parents();
|
||||
//TODO: Poison mit INFINITE_RANK
|
||||
}
|
||||
|
||||
bool rpl_equal_id(ipv6_addr_t *id1, ipv6_addr_t *id2){
|
||||
@ -97,6 +99,7 @@ bool rpl_equal_id(ipv6_addr_t *id1, ipv6_addr_t *id2){
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
rpl_parent_t *rpl_new_parent(rpl_dodag_t *dodag, ipv6_addr_t *address, uint16_t rank){
|
||||
@ -113,7 +116,8 @@ rpl_parent_t *rpl_new_parent(rpl_dodag_t *dodag, ipv6_addr_t *address, uint16_t
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
rpl_delete_worst_parent();
|
||||
return rpl_new_parent(dodag, address, rank);
|
||||
}
|
||||
|
||||
rpl_parent_t *rpl_find_parent(ipv6_addr_t *address){
|
||||
@ -128,23 +132,55 @@ rpl_parent_t *rpl_find_parent(ipv6_addr_t *address){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rpl_delete_parent(ipv6_addr_t * address){
|
||||
void rpl_delete_parent(rpl_parent_t * parent){
|
||||
rpl_dodag_t * my_dodag = rpl_get_my_dodag();
|
||||
//TODO:check if this was the preferred parent, find new parent, if it was last parent leave dodag
|
||||
if(rpl_equal_id(&my_dodag->my_preferred_parent->addr,address)){
|
||||
//set_new_preferred_parent
|
||||
//check if this was the preferred parent, find new parent, if it was last parent leave dodag
|
||||
char new_preferred_parent = 0;
|
||||
if( (my_dodag != NULL) && rpl_equal_id(&my_dodag->my_preferred_parent->addr, &parent->addr) ){
|
||||
new_preferred_parent = 1;
|
||||
memset(parent,0,sizeof(*parent));
|
||||
}
|
||||
for(int i=0;i<RPL_MAX_PARENTS;i++){
|
||||
if( parents[i].used && (rpl_equal_id(address, &parents[i].addr)) ){
|
||||
memset(&parents[i], 0, sizeof(parents[i]));
|
||||
uint8_t best = 0xFF;
|
||||
uint16_t min_rank = 0xFFFF;
|
||||
if(new_preferred_parent){
|
||||
for(int i=0;i<RPL_MAX_PARENTS;i++){
|
||||
if(parents[i].rank < min_rank){
|
||||
best = i;
|
||||
min_rank = parents[i].rank;
|
||||
}
|
||||
}
|
||||
if(best == 0xFF){
|
||||
//we have no more parents for this dodag -> leave dodag;
|
||||
//TODO: Erst nach Ablauf eines Timers verlassen, siehe RPL draft 8.2.2.1 DODAG Version
|
||||
rpl_leave_dodag(my_dodag);
|
||||
}
|
||||
my_dodag->my_preferred_parent = &parents[best];
|
||||
}
|
||||
}
|
||||
|
||||
void rpl_delete_parents_for_dodag(ipv6_addr_t * dodag_id){
|
||||
void rpl_delete_worst_parent(void){
|
||||
uint8_t worst = 0xFF;
|
||||
uint16_t max_rank = 0x0000;
|
||||
for(int i=0;i<RPL_MAX_PARENTS;i++){
|
||||
if(parents[i].rank > max_rank){
|
||||
worst = i;
|
||||
max_rank = parents[i].rank;
|
||||
}
|
||||
}
|
||||
if(worst == 0xFF){
|
||||
//Fehler, keine parents -> sollte nicht passieren
|
||||
return;
|
||||
}
|
||||
rpl_delete_parent(&parents[worst]);
|
||||
|
||||
}
|
||||
|
||||
void rpl_delete_all_parents(void){
|
||||
for(int i=0;i<RPL_MAX_PARENTS;i++){
|
||||
memset(&parents[i],0,sizeof(parents[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void rpl_join_dodag(rpl_dodag_t *dodag, ipv6_addr_t *parent, uint16_t parent_rank){
|
||||
rpl_dodag_t *my_dodag;
|
||||
rpl_parent_t *preferred_parent;
|
||||
@ -174,6 +210,7 @@ void rpl_join_dodag(rpl_dodag_t *dodag, ipv6_addr_t *parent, uint16_t parent_ran
|
||||
my_dodag->joined = 1;
|
||||
my_dodag->my_preferred_parent = preferred_parent;
|
||||
my_dodag->my_rank = dodag->of->calc_rank(preferred_parent, dodag->my_rank);
|
||||
my_dodag->dao_seq = RPL_COUNTER_INIT;
|
||||
my_dodag->min_rank = my_dodag->my_rank;
|
||||
|
||||
start_trickle(my_dodag->dio_min, my_dodag->dio_interval_doubling, my_dodag->dio_redundancy);
|
||||
@ -186,6 +223,7 @@ void rpl_global_repair(rpl_dodag_t *dodag){
|
||||
printf("Error - no global repair possible, if not part of a DODAG\n");
|
||||
return;
|
||||
}
|
||||
//TODO: nachschauen - soll das wirklich so überschrieben werden?
|
||||
my_dodag->version = dodag->version;
|
||||
my_dodag->dtsn = dodag->dtsn;
|
||||
}
|
||||
|
||||
@ -15,3 +15,6 @@ void rpl_leave_dodag(rpl_dodag_t * dodag);
|
||||
bool rpl_equal_id(ipv6_addr_t *id1, ipv6_addr_t *id2);
|
||||
void rpl_global_repair(rpl_dodag_t *dodag);
|
||||
ipv6_addr_t *rpl_get_my_preferred_parent();
|
||||
void rpl_delete_parent(rpl_parent_t *parent);
|
||||
void rpl_delete_worst_parent(void);
|
||||
void rpl_delete_all_parents(void);
|
||||
|
||||
@ -68,6 +68,7 @@
|
||||
#define DEFAULT_MIN_HOP_RANK_INCREASE 256
|
||||
//DAO_DELAY is in seconds
|
||||
#define DEFAULT_DAO_DELAY 1
|
||||
#define RPL_DODAG_ID_LEN 16
|
||||
|
||||
//others
|
||||
|
||||
@ -197,6 +198,7 @@ typedef struct rpl_dodag_t {
|
||||
uint8_t version;
|
||||
uint8_t grounded;
|
||||
uint16_t my_rank;
|
||||
uint8_t dao_seq;
|
||||
uint16_t min_rank;
|
||||
uint8_t joined;
|
||||
rpl_parent_t *my_preferred_parent;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user