Browse Source

rpl: RPL/Trickle with only *one* thread

This PR proposes an approach to reduce the thread count of RPL.

The current RPL/Trickle stack needs about 5 threads to handle tasks
like updating the trickle timer, routing entries and the transmission of
DAOs.

This PR modifies RPL to use only one thread with a looped `msg_recv()` call.
The message is then multiplexed to the right task.
dev/timer
Cenk Gündoğan 8 years ago
parent
commit
c4b01b87bc
  1. 1
      Makefile.dep
  2. 3
      sys/Makefile
  3. 114
      sys/include/trickle.h
  4. 20
      sys/net/include/rpl/rpl_config.h
  5. 2
      sys/net/include/rpl/rpl_dodag.h
  6. 12
      sys/net/include/rpl/rpl_structs.h
  7. 1
      sys/net/network_layer/sixlowpan/ip.c
  8. 299
      sys/net/routing/rpl/rpl.c
  9. 33
      sys/net/routing/rpl/rpl_dodag.c
  10. 20
      sys/net/routing/rpl/rpl_nonstoring/rpl_nonstoring.c
  11. 20
      sys/net/routing/rpl/rpl_storing/rpl_storing.c
  12. 288
      sys/net/routing/rpl/trickle.c
  13. 42
      sys/net/routing/rpl/trickle.h
  14. 3
      sys/trickle/Makefile
  15. 102
      sys/trickle/trickle.c

1
Makefile.dep

@ -32,6 +32,7 @@ ifneq (,$(filter sixlowborder,$(USEMODULE)))
endif
ifneq (,$(filter rpl,$(USEMODULE)))
USEMODULE += trickle
USEMODULE += routing
endif

3
sys/Makefile

@ -71,6 +71,9 @@ endif
ifneq (,$(filter netapi,$(USEMODULE)))
DIRS += net/crosslayer/netapi
endif
ifneq (,$(filter trickle,$(USEMODULE)))
DIRS += trickle
endif
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

114
sys/include/trickle.h

@ -0,0 +1,114 @@
/*
* Trickle constants and prototypes
*
* Copyright (C) 2013, 2014 INRIA.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_trickle Trickle Timer
* @ingroup sys
* @{
*/
/**
* @file trickle.h
* @brief Implementation of a generic Trickle Algorithm (RFC 6206)
*
* @author Eric Engel <eric.engel@fu-berlin.de>
* @author Cenk Gündoğan <cnkgndgn@gmail.com>
*/
#ifndef _TRICKLE_H
#define _TRICKLE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "vtimer.h"
#include "thread.h"
/** @brief a generic callback function with arguments that is called by trickle periodically */
typedef struct {
void (*func)(void *); /**< a generic callback function pointer */
void *args; /**< a generic parameter for the callback function pointer */
} trickle_callback_t;
/** @brief all state variables for a trickle timer */
typedef struct {
uint8_t k; /**< redundancy constant */
uint32_t Imin; /**< minimum interval size */
uint8_t Imax; /**< maximum interval size, described as a number of doublings */
uint32_t I; /**< current interval size */
uint32_t t; /**< time within the current interval */
uint16_t c; /**< counter */
kernel_pid_t pid; /**< pid of trickles target thread */
trickle_callback_t callback; /**< the callback function and parameter that trickle is calling
after each interval */
uint16_t interval_msg_type; /**< the msg_t.type that trickle should use after an interval */
timex_t msg_interval_time; /**< interval represented as timex_t */
vtimer_t msg_interval_timer; /**< vtimer to send a msg_t to the target thread for a new interval */
uint16_t callback_msg_type; /**< the msg_t.type that trickle should use after a callback */
timex_t msg_callback_time; /**< callback interval represented as timex_t */
vtimer_t msg_callback_timer; /**< vtimer to send a msg_t to the target thread for a callback */
} trickle_t;
/**
* @brief resets the trickle timer
*
* @param[in] trickle the trickle timer
*/
void trickle_reset_timer(trickle_t *trickle);
/**
* @brief start the trickle timer
*
* @param[in] pid target thread
* @param[in] trickle trickle timer
* @param[in] interval_msg_type msg_t.type for interval messages
* @param[in] callback_msg_type msg_t.type for callback messages
* @param[in] Imin minimum interval
* @param[in] Imax maximum interval
* @param[in] k redundancy constant
*/
void trickle_start(kernel_pid_t pid, trickle_t *trickle, uint16_t interval_msg_type,
uint16_t callback_msg_type, uint32_t Imin, uint8_t Imax, uint8_t k);
/**
* @brief stops the trickle timer
*
* @param[in] trickle trickle timer
*/
void trickle_stop(trickle_t *trickle);
/**
* @brief increments the counter by one
*
* @param[in] trickle trickle timer
*/
void trickle_increment_counter(trickle_t *trickle);
/**
* @brief is called after the interval is over and calculates the next interval
*
* @param[in] trickle trickle timer
*/
void trickle_interval(trickle_t *trickle);
/**
* @brief is called after the callback interval is over and calls the callback function
*
* @param[in] trickle trickle timer
*/
void trickle_callback(trickle_t *trickle);
#ifdef __cplusplus
}
#endif
#endif /* _TRICKLE_H */
/** @} */

20
sys/net/include/rpl/rpl_config.h

@ -32,12 +32,21 @@ extern "C" {
#define RPL_STORING_MODE_NO_MC 0x02
#define RPL_STORING_MODE_MC 0x03
/* ICMP type */
#define RPL_SEQUENCE_WINDOW 16
#define ICMP_CODE_DIS 0x00
#define ICMP_CODE_DIO 0x01
#define ICMP_CODE_DAO 0x02
#define ICMP_CODE_DAO_ACK 0x03
/* RPL Message type */
enum RPL_MSG_CODE {
ICMP_CODE_DIS = 0,
ICMP_CODE_DIO,
ICMP_CODE_DAO,
ICMP_CODE_DAO_ACK,
/* put all ICMP codes before the end marker */
ICMP_CODE_END,
RPL_MSG_TYPE_DAO_HANDLE,
RPL_MSG_TYPE_ROUTING_ENTRY_UPDATE,
RPL_MSG_TYPE_TRICKLE_INTERVAL,
RPL_MSG_TYPE_TRICKLE_CALLBACK
};
/* packet base lengths */
#define DIO_BASE_LEN 24
#define DIS_BASE_LEN 2
@ -147,6 +156,7 @@ static inline bool RPL_COUNTER_GREATER_THAN(uint8_t A, uint8_t B)
#define RPL_ROOT_RANK 256
#define RPL_DEFAULT_LIFETIME 0xff
#define RPL_LIFETIME_UNIT 2
#define RPL_LIFETIME_STEP 2
#define RPL_GROUNDED 1
#define RPL_PRF_MASK 0x7
#define RPL_MOP_SHIFT 3

2
sys/net/include/rpl/rpl_dodag.h

@ -26,6 +26,8 @@
extern "C" {
#endif
void rpl_dao_ack_received(rpl_dodag_t *dodag);
void rpl_delay_dao(rpl_dodag_t *dodag);
void rpl_instances_init(void);
rpl_instance_t *rpl_new_instance(uint8_t instanceid);
rpl_instance_t *rpl_get_instance(uint8_t instanceid);

12
sys/net/include/rpl/rpl_structs.h

@ -18,9 +18,6 @@
* @}
*/
#include <string.h>
#include "ipv6.h"
#ifndef RPL_STRUCTS_H_INCLUDED
#define RPL_STRUCTS_H_INCLUDED
@ -28,6 +25,10 @@
extern "C" {
#endif
#include <string.h>
#include "ipv6.h"
#include "trickle.h"
/* Modes of Operation */
/* DIO Base Object (RFC 6550 Fig. 14) */
@ -167,6 +168,11 @@ typedef struct rpl_dodag_t {
uint8_t joined;
rpl_parent_t *my_preferred_parent;
struct rpl_of_t *of;
trickle_t trickle;
bool ack_received;
uint8_t dao_counter;
timex_t dao_time;
vtimer_t dao_timer;
} rpl_dodag_t;
typedef struct rpl_of_t {

1
sys/net/network_layer/sixlowpan/ip.c

@ -282,6 +282,7 @@ int icmpv6_demultiplex(const icmpv6_hdr_t *hdr)
if (_rpl_process_pid != KERNEL_PID_UNDEF) {
msg_t m_send;
m_send.content.ptr = (char *) ipv6_buf;
m_send.type = ((icmpv6_hdr_t *)(m_send.content.ptr + IPV6_HDR_LEN))->code;
msg_send(&m_send, _rpl_process_pid);
}
else {

299
sys/net/routing/rpl/rpl.c

@ -45,8 +45,6 @@
#define ENABLE_DEBUG (0)
#if ENABLE_DEBUG
#undef TRICKLE_TIMER_STACKSIZE
#define TRICKLE_TIMER_STACKSIZE (KERNEL_CONF_STACKSIZE_MAIN)
char addr_str[IPV6_MAX_ADDR_STR_LEN];
#endif
#include "debug.h"
@ -57,6 +55,11 @@ mutex_t rpl_send_mutex = MUTEX_INIT;
msg_t rpl_msg_queue[RPL_PKT_RECV_BUF_SIZE];
char rpl_process_buf[RPL_PROCESS_STACKSIZE];
uint8_t rpl_buffer[BUFFER_SIZE - LL_HDR_LEN];
static timex_t rt_time;
static vtimer_t rt_timer;
static void _dao_handle_send(rpl_dodag_t *dodag);
static void _rpl_update_routing_table(void);
#if RPL_DEFAULT_MOP == RPL_NON_STORING_MODE
uint8_t srh_buffer[BUFFER_SIZE];
@ -74,7 +77,6 @@ ipv6_addr_t my_address;
/* IPv6 message buffer */
ipv6_hdr_t *ipv6_buf;
icmpv6_hdr_t *icmp_buf;
uint8_t rpl_init(int if_id)
{
@ -86,7 +88,6 @@ uint8_t rpl_init(int if_id)
rpl_clear_routing_table();
#endif
init_trickle();
rpl_process_pid = thread_create(rpl_process_buf, RPL_PROCESS_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
rpl_process, NULL, "rpl_process");
@ -106,6 +107,10 @@ uint8_t rpl_init(int if_id)
/* initialize objective function manager */
rpl_of_manager_init(&my_address);
rpl_init_mode(&my_address);
rt_time = timex_set(RPL_LIFETIME_STEP, 0);
vtimer_set_msg(&rt_timer, rt_time, rpl_process_pid, RPL_MSG_TYPE_ROUTING_ENTRY_UPDATE, NULL);
return SIXLOWERROR_SUCCESS;
}
@ -133,7 +138,9 @@ uint8_t rpl_is_root(void)
#if RPL_DEFAULT_MOP == RPL_NON_STORING_MODE
void internal_srh_process(ipv6_srh_t *srh_header)
{
/* modify it accordingly - the number of entries is not depending on padding, because there is none. */
/* modify it accordingly - the number of entries is not depending on padding,
* because there is none.
*/
uint8_t n = srh_header->hdrextlen / sizeof(ipv6_addr_t);
if (srh_header->segments_left > n) {
@ -146,7 +153,8 @@ void internal_srh_process(ipv6_srh_t *srh_header)
down_next_hop = &srh_header->route[n - segs];
srh_header->segments_left = segs - 1;
DEBUGF("Segments left after reduction: %d\n", srh_header->segments_left);
DEBUGF("Next hop is: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, down_next_hop));
DEBUGF("Next hop is: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, down_next_hop));
}
}
#endif
@ -158,81 +166,122 @@ void *rpl_process(void *arg)
msg_t m_recv;
msg_init_queue(rpl_msg_queue, RPL_PKT_RECV_BUF_SIZE);
rpl_dodag_t *dodag;
trickle_t *trickle;
while (1) {
msg_receive(&m_recv);
/* differentiate packet types */
ipv6_buf = ((ipv6_hdr_t *)m_recv.content.ptr);
memcpy(&rpl_buffer, ipv6_buf, NTOHS(ipv6_buf->length) + IPV6_HDR_LEN);
/* This is an RPL-related message. */
if (ipv6_buf->nextheader == IPV6_PROTO_NUM_ICMPV6) {
icmp_buf = ((icmpv6_hdr_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
/* get code for message-interpretation and process message */
DEBUGF("Received RPL information of type %04X and length %u\n", icmp_buf->code, NTOHS(ipv6_buf->length));
switch (icmp_buf->code) {
case (ICMP_CODE_DIS): {
rpl_recv_DIS();
if (m_recv.type > ICMP_CODE_END) {
switch (m_recv.type) {
case RPL_MSG_TYPE_DAO_HANDLE:
dodag = (rpl_dodag_t *) m_recv.content.ptr;
if (dodag->joined) {
_dao_handle_send(dodag);
}
break;
}
case (ICMP_CODE_DIO): {
rpl_recv_DIO();
case RPL_MSG_TYPE_ROUTING_ENTRY_UPDATE:
_rpl_update_routing_table();
break;
}
case (ICMP_CODE_DAO): {
rpl_recv_DAO();
case RPL_MSG_TYPE_TRICKLE_INTERVAL:
trickle = (trickle_t *) m_recv.content.ptr;
if (trickle->callback.func != NULL) {
trickle_interval(trickle);
}
break;
}
case (ICMP_CODE_DAO_ACK): {
rpl_recv_DAO_ACK();
case RPL_MSG_TYPE_TRICKLE_CALLBACK:
trickle = (trickle_t *) m_recv.content.ptr;
if (trickle->callback.func != NULL) {
trickle_callback(trickle);
}
break;
}
default:
break;
}
}
/* This is an RPL-related message. */
else {
/* differentiate packet types */
ipv6_buf = (ipv6_hdr_t *) m_recv.content.ptr;
memcpy(&rpl_buffer, ipv6_buf, NTOHS(ipv6_buf->length) + IPV6_HDR_LEN);
#if RPL_DEFAULT_MOP == RPL_NON_STORING_MODE
/* If the message is not RPL-type, it relates to non-storing mode */
else if (RPL_DEFAULT_MOP == RPL_NON_STORING_MODE) {
if (ipv6_buf->nextheader == IPV6_PROTO_NUM_SRH) {
srh_header = ((ipv6_srh_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
/* if there are no segments left, the routing is finished */
if (srh_header->segments_left == 0) {
DEBUGF("Source routing finished with next header: %02X.\n", srh_header->nextheader);
DEBUGF("Size of srh: %d\n", srh_header->hdrextlen);
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr + IPV6_HDR_LEN + sizeof(ipv6_srh_t)+srh_header->hdrextlen));
rpl_remove_srh_header(ipv6_buf, payload, srh_header->nextheader);
}
else {
internal_srh_process(srh_header);
if (down_next_hop != NULL) {
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
rpl_srh_sendto(payload, NTOHS(ipv6_buf->length), &ipv6_buf->srcaddr, down_next_hop, srh_header, 0);
if (ipv6_buf->nextheader == IPV6_PROTO_NUM_ICMPV6) {
/* get code for message-interpretation and process message */
DEBUGF("Received RPL information of type %04X and length %u\n",
m_recv.type, NTOHS(ipv6_buf->length));
switch (m_recv.type) {
case (ICMP_CODE_DIS): {
rpl_recv_DIS();
break;
}
case (ICMP_CODE_DIO): {
rpl_recv_DIO();
break;
}
case (ICMP_CODE_DAO): {
rpl_recv_DAO();
break;
}
case (ICMP_CODE_DAO_ACK): {
rpl_recv_DAO_ACK();
break;
}
default:
break;
}
}
#if RPL_DEFAULT_MOP == RPL_NON_STORING_MODE
/* If the message is not RPL-type, it relates to non-storing mode */
else if (RPL_DEFAULT_MOP == RPL_NON_STORING_MODE) {
if (ipv6_buf->nextheader == IPV6_PROTO_NUM_SRH) {
srh_header = ((ipv6_srh_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
/* if there are no segments left, the routing is finished */
if (srh_header->segments_left == 0) {
DEBUGF("Source routing finished with next header: %02X.\n",
srh_header->nextheader);
DEBUGF("Size of srh: %d\n", srh_header->hdrextlen);
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr +
IPV6_HDR_LEN + sizeof(ipv6_srh_t)+srh_header->hdrextlen));
rpl_remove_srh_header(ipv6_buf, payload, srh_header->nextheader);
}
else {
internal_srh_process(srh_header);
if (down_next_hop != NULL) {
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
rpl_srh_sendto(payload, NTOHS(ipv6_buf->length), &ipv6_buf->srcaddr,
down_next_hop, srh_header, 0);
}
}
}
#if RPL_MAX_ROUTING_ENTRIES != 0
else {
srh_header = rpl_get_srh_header(ipv6_buf);
else {
srh_header = rpl_get_srh_header(ipv6_buf);
if (srh_header != NULL) {
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
rpl_srh_sendto(payload, NTOHS(ipv6_buf->length), &ipv6_buf->srcaddr, &ipv6_buf->destaddr, srh_header, srh_header->hdrextlen + sizeof(ipv6_srh_t));
if (srh_header != NULL) {
uint8_t *payload = ((uint8_t *)(m_recv.content.ptr + IPV6_HDR_LEN));
rpl_srh_sendto(payload, NTOHS(ipv6_buf->length),
&ipv6_buf->srcaddr, &ipv6_buf->destaddr, srh_header,
srh_header->hdrextlen + sizeof(ipv6_srh_t));
}
}
#endif
}
#endif
}
#endif
}
}
@ -247,7 +296,8 @@ void rpl_send_DIO(ipv6_addr_t *destination)
mutex_unlock(&rpl_send_mutex);
}
void rpl_send_DAO(ipv6_addr_t *destination, uint8_t lifetime, bool default_lifetime, uint8_t start_index)
void rpl_send_DAO(ipv6_addr_t *destination, uint8_t lifetime,
bool default_lifetime, uint8_t start_index)
{
if (destination) {
DEBUGF("Send DAO to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, destination));
@ -272,7 +322,8 @@ void rpl_send_DIS(ipv6_addr_t *destination)
void rpl_send_DAO_ACK(ipv6_addr_t *destination)
{
if (destination) {
DEBUGF("Send DAO ACK to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, destination));
DEBUGF("Send DAO ACK to %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, destination));
}
mutex_lock(&rpl_send_mutex);
@ -309,26 +360,109 @@ void rpl_recv_DAO_ACK(void)
rpl_recv_dao_ack_mode();
}
void _rpl_update_routing_table(void) {
rpl_dodag_t *my_dodag = rpl_get_my_dodag();
rpl_routing_entry_t *rt;
if (my_dodag != NULL) {
rt = rpl_get_routing_table();
for (uint8_t i = 0; i < rpl_max_routing_entries; i++) {
if (rt[i].used) {
if (rt[i].lifetime <= 1) {
memset(&rt[i], 0, sizeof(rt[i]));
}
else {
rt[i].lifetime = rt[i].lifetime - RPL_LIFETIME_STEP;
}
}
}
/* Parent is NULL for root too */
if (my_dodag->my_preferred_parent != NULL) {
if (my_dodag->my_preferred_parent->lifetime <= 1) {
DEBUGF("parent lifetime timeout\n");
rpl_parent_update(NULL);
}
else {
my_dodag->my_preferred_parent->lifetime =
my_dodag->my_preferred_parent->lifetime - RPL_LIFETIME_STEP;
}
}
}
vtimer_remove(&rt_timer);
vtimer_set_msg(&rt_timer, rt_time, rpl_process_pid, RPL_MSG_TYPE_ROUTING_ENTRY_UPDATE, NULL);
}
void rpl_delay_dao(rpl_dodag_t *dodag)
{
dodag->dao_time = timex_set(DEFAULT_DAO_DELAY, 0);
dodag->dao_counter = 0;
dodag->ack_received = false;
vtimer_remove(&dodag->dao_timer);
vtimer_set_msg(&dodag->dao_timer, dodag->dao_time,
rpl_process_pid, RPL_MSG_TYPE_DAO_HANDLE, dodag);
}
/* This function is used for regular update of the routes.
* The Timer can be overwritten, as the normal delay_dao function gets called
*/
void long_delay_dao(rpl_dodag_t *dodag)
{
dodag->dao_time = timex_set(REGULAR_DAO_INTERVAL, 0);
dodag->dao_counter = 0;
dodag->ack_received = false;
vtimer_remove(&dodag->dao_timer);
vtimer_set_msg(&dodag->dao_timer, dodag->dao_time,
rpl_process_pid, RPL_MSG_TYPE_DAO_HANDLE, dodag);
}
void rpl_dao_ack_received(rpl_dodag_t *dodag)
{
dodag->ack_received = true;
long_delay_dao(dodag);
}
void _dao_handle_send(rpl_dodag_t *dodag) {
if ((dodag->ack_received == false) && (dodag->dao_counter < DAO_SEND_RETRIES)) {
dodag->dao_counter++;
rpl_send_DAO(NULL, 0, true, 0);
dodag->dao_time = timex_set(DEFAULT_WAIT_FOR_DAO_ACK, 0);
vtimer_remove(&dodag->dao_timer);
vtimer_set_msg(&dodag->dao_timer, dodag->dao_time,
rpl_process_pid, RPL_MSG_TYPE_DAO_HANDLE, dodag);
}
else if (dodag->ack_received == false) {
long_delay_dao(dodag);
}
}
ipv6_addr_t *rpl_get_next_hop(ipv6_addr_t *addr)
{
DEBUGF("Looking up the next hop to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, addr));
DEBUGF("Looking up the next hop to %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, addr));
#if RPL_MAX_ROUTING_ENTRIES != 0
for (uint8_t i = 0; i < rpl_max_routing_entries; i++) {
if (rpl_routing_table[i].used) {
DEBUGF("checking %d: %s\n", i, ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &rpl_routing_table[i].address));
DEBUGF("checking %d: %s\n", i,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &rpl_routing_table[i].address));
}
if ((RPL_DEFAULT_MOP == RPL_NON_STORING_MODE) && rpl_is_root()) {
if (rpl_routing_table[i].used && rpl_equal_id(&rpl_routing_table[i].address, addr)) {
DEBUGF("found %d: %s\n", i, ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &rpl_routing_table[i].address));
DEBUGF("found %d: %s\n", i,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN,
&rpl_routing_table[i].address));
return &rpl_routing_table[i].address;
}
}
else {
if (rpl_routing_table[i].used && rpl_equal_id(&rpl_routing_table[i].address, addr)) {
DEBUGF("found %d: %s\n", i, ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &rpl_routing_table[i].next_hop));
DEBUGF("found %d: %s\n", i,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN,
&rpl_routing_table[i].next_hop));
return &rpl_routing_table[i].next_hop;
}
}
@ -437,13 +571,16 @@ void rpl_add_srh_entry(ipv6_addr_t *child, ipv6_addr_t *parent, uint16_t lifetim
}
}
/* This maybe a bit confusing since the root also using the standard routing table, but in this case
* the code stays cleaner - especially for rt_over_timer from trickle.c. Just keep in mind that
* address is now child (unique, iteration variable) and parent is now next_hop. The whole routing table
* transforms to a list of children and their parents, so that route aggregation can be done properly.
/* This maybe a bit confusing since the root also using the standard routing table,
* but in this case the code stays cleaner - especially for rt_over_timer from trickle.c.
* Just keep in mind that address is now child (unique, iteration variable) and parent is
* now next_hop. The whole routing table transforms to a list of children and their parents,
* so that route aggregation can be done properly.
*/
DEBUGF("Adding source-routing entry child: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, child));
DEBUGF("Adding source-routing entry parent: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, parent));
DEBUGF("Adding source-routing entry child: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, child));
DEBUGF("Adding source-routing entry parent: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, parent));
for (uint8_t i = 0; i < rpl_max_routing_entries; i++) {
if (!rpl_routing_table[i].used) {
@ -486,13 +623,18 @@ ipv6_srh_t *rpl_get_srh_header(ipv6_hdr_t *act_ipv6_hdr)
DEBUGF("DESTINATION NODE: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, actual_node));
while (!(rpl_equal_id(actual_node, &my_address))) {
/* set check variable - this is reversed, if a child/parent-relation is found in one iteration of the routing table */
/* set check variable - this is reversed,
* if a child/parent-relation is found in one iteration of the routing table */
traceable = 0;
for (uint8_t i = 0; i < rpl_max_routing_entries; i++) {
if (rpl_routing_table[i].used && ipv6_suffix_is_equal(&rpl_routing_table[i].address, actual_node)) {
DEBUGF("[INFO] Found parent-child relation with P: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &rpl_routing_table[i].next_hop));
DEBUGF(" and C: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, actual_node));
if (rpl_routing_table[i].used
&& ipv6_suffix_is_equal(&rpl_routing_table[i].address, actual_node)) {
DEBUGF("[INFO] Found parent-child relation with P: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN,
&rpl_routing_table[i].next_hop));
DEBUGF(" and C: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, actual_node));
memcpy(&rev_route[counter], actual_node, sizeof(ipv6_addr_t));
actual_node = &rpl_routing_table[i].next_hop;
@ -514,7 +656,9 @@ ipv6_srh_t *rpl_get_srh_header(ipv6_hdr_t *act_ipv6_hdr)
}
}
/* build real route based on reversed route. After building it starts with the node next to destination */
/* build real route based on reversed route.
* After building it starts with the node next to destination
*/
if (counter > 1) {
for (uint8_t i = 0; i < counter-1; i++) {
memcpy(&srh_header->route[i], &rev_route[counter-i-2], sizeof(ipv6_addr_t));
@ -552,12 +696,14 @@ void rpl_remove_srh_header(ipv6_hdr_t *ipv6_header, const void *buf, uint8_t nex
temp_ipv6_header->nextheader = nextheader;
memcpy(payload, buf, msg_length);
DEBUGF("Source routing header extraction finished.\n");
DEBUGF("Dest is now: %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &temp_ipv6_header->destaddr));
DEBUGF("Dest is now: %s\n",
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &temp_ipv6_header->destaddr));
srh_m_send.content.ptr = (char *) srh_send_buffer;
msg_send_receive(&srh_m_send, &srh_m_recv, ip_process_pid);
}
int rpl_srh_sendto(const void *buf, uint16_t len, ipv6_addr_t *src, ipv6_addr_t *dest, ipv6_srh_t *srh_header, uint8_t srh_length)
int rpl_srh_sendto(const void *buf, uint16_t len,
ipv6_addr_t *src, ipv6_addr_t *dest, ipv6_srh_t *srh_header, uint8_t srh_length)
{
ipv6_hdr_t *temp_ipv6_header = ((ipv6_hdr_t *)(&srh_send_buffer));
ipv6_srh_t *current_packet = ((ipv6_srh_t *)(&srh_send_buffer[IPV6_HDR_LEN]));
@ -572,6 +718,7 @@ int rpl_srh_sendto(const void *buf, uint16_t len, ipv6_addr_t *src, ipv6_addr_t
DEBUGF("SRH-length: %d\n", current_packet->hdrextlen);
DEBUGF("My payload length: %d\n", plength);
return ipv6_sendto(&temp_ipv6_header->destaddr, IPV6_PROTO_NUM_SRH, (uint8_t *)current_packet, plength, &temp_ipv6_header->destaddr);
return ipv6_sendto(&temp_ipv6_header->destaddr, IPV6_PROTO_NUM_SRH,
(uint8_t *)current_packet, plength, &temp_ipv6_header->destaddr);
}
#endif

33
sys/net/routing/rpl/rpl_dodag.c

@ -31,10 +31,19 @@ char addr_str[IPV6_MAX_ADDR_STR_LEN];
#endif
#include "debug.h"
kernel_pid_t rpl_process_pid;
rpl_instance_t instances[RPL_MAX_INSTANCES];
rpl_dodag_t dodags[RPL_MAX_DODAGS];
rpl_parent_t parents[RPL_MAX_PARENTS];
void rpl_trickle_send_dio(void *args) {
(void) args;
ipv6_addr_t mcast;
ipv6_addr_set_all_nodes_addr(&mcast);
rpl_send_DIO(&mcast);
}
void rpl_instances_init(void)
{
memset(instances, 0, sizeof(rpl_instance_t) * RPL_MAX_INSTANCES);
@ -98,6 +107,9 @@ rpl_dodag_t *rpl_new_dodag(uint8_t instanceid, ipv6_addr_t *dodagid)
dodag->instance = inst;
dodag->my_rank = INFINITE_RANK;
dodag->used = 1;
dodag->ack_received = true;
dodag->dao_counter = 0;
dodag->trickle.callback.func = &rpl_trickle_send_dio;
memcpy(&dodag->dodag_id, dodagid, sizeof(*dodagid));
return dodag;
}
@ -129,6 +141,7 @@ rpl_dodag_t *rpl_get_my_dodag(void)
}
void rpl_del_dodag(rpl_dodag_t *dodag)
{
rpl_leave_dodag(dodag);
memset(dodag, 0, sizeof(*dodag));
}
@ -137,6 +150,8 @@ void rpl_leave_dodag(rpl_dodag_t *dodag)
dodag->joined = 0;
dodag->my_preferred_parent = NULL;
rpl_delete_all_parents();
trickle_stop(&dodag->trickle);
vtimer_remove(&dodag->dao_timer);
}
bool rpl_equal_id(ipv6_addr_t *id1, ipv6_addr_t *id2)
@ -279,10 +294,10 @@ rpl_parent_t *rpl_find_preferred_parent(void)
my_dodag->my_preferred_parent = best;
if (my_dodag->mop != RPL_NO_DOWNWARD_ROUTES) {
delay_dao();
rpl_delay_dao(my_dodag);
}
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
return best;
@ -315,7 +330,7 @@ void rpl_parent_update(rpl_parent_t *parent)
my_dodag->min_rank = my_dodag->my_rank;
}
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
}
@ -367,8 +382,10 @@ void rpl_join_dodag(rpl_dodag_t *dodag, ipv6_addr_t *parent, uint16_t parent_ran
DEBUG("\tmy_preferred_parent rank\t%02X\n", my_dodag->my_preferred_parent->rank);
DEBUG("\tmy_preferred_parent lifetime\t%04X\n", my_dodag->my_preferred_parent->lifetime);
start_trickle(my_dodag->dio_min, my_dodag->dio_interval_doubling, my_dodag->dio_redundancy);
delay_dao();
trickle_start(rpl_process_pid, &my_dodag->trickle, RPL_MSG_TYPE_TRICKLE_INTERVAL,
RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << my_dodag->dio_min), my_dodag->dio_interval_doubling,
my_dodag->dio_redundancy);
rpl_delay_dao(my_dodag);
}
void rpl_global_repair(rpl_dodag_t *dodag, ipv6_addr_t *p_addr, uint16_t rank)
@ -395,8 +412,8 @@ void rpl_global_repair(rpl_dodag_t *dodag, ipv6_addr_t *p_addr, uint16_t rank)
my_dodag->my_rank = my_dodag->of->calc_rank(my_dodag->my_preferred_parent,
my_dodag->my_rank);
my_dodag->min_rank = my_dodag->my_rank;
reset_trickletimer();
delay_dao();
trickle_reset_timer(&my_dodag->trickle);
rpl_delay_dao(my_dodag);
}
DEBUGF("Migrated to DODAG Version %d. My new Rank: %d\n", my_dodag->version,
@ -416,7 +433,7 @@ void rpl_local_repair(void)
my_dodag->my_rank = INFINITE_RANK;
my_dodag->dtsn++;
rpl_delete_all_parents();
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}

20
sys/net/routing/rpl/rpl_nonstoring/rpl_nonstoring.c

@ -213,8 +213,10 @@ void rpl_init_root_mode(void)
return;
}
trickle_start(rpl_process_pid, &dodag->trickle, RPL_MSG_TYPE_TRICKLE_INTERVAL,
RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << dodag->dio_min), dodag->dio_interval_doubling,
dodag->dio_redundancy);
DEBUGF("Root init finished.\n");
start_trickle(dodag->dio_min, dodag->dio_interval_doubling, dodag->dio_redundancy);
}
uint8_t rpl_is_root_mode(void)
@ -548,7 +550,7 @@ void rpl_recv_DIO_mode(void)
if (my_dodag->my_rank == ROOT_RANK) {
DEBUGF("[Warning] Inconsistent Dodag Version\n");
my_dodag->version = RPL_COUNTER_INCREMENT(dio_dodag.version);
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
else {
DEBUGF("my dodag has no preferred_parent yet - seems to be odd since I have a parent.\n");
@ -558,20 +560,20 @@ void rpl_recv_DIO_mode(void)
return;
}
else if (RPL_COUNTER_GREATER_THAN(my_dodag->version, dio_dodag.version)) {
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
return;
}
}
/* version matches, DODAG matches */
if (rpl_dio_buf->rank == INFINITE_RANK) {
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
/* We are root, all done!*/
if (my_dodag->my_rank == ROOT_RANK) {
if (rpl_dio_buf->rank != INFINITE_RANK) {
trickle_increment_counter();
trickle_increment_counter(&my_dodag->trickle);
}
return;
@ -592,7 +594,7 @@ void rpl_recv_DIO_mode(void)
}
else {
/* DIO OK */
trickle_increment_counter();
trickle_increment_counter(&my_dodag->trickle);
}
/* update parent rank */
@ -604,7 +606,7 @@ void rpl_recv_DIO_mode(void)
}
else if (rpl_equal_id(&parent->addr, &my_dodag->my_preferred_parent->addr) &&
(parent->dtsn != rpl_dio_buf->dtsn)) {
delay_dao();
rpl_delay_dao(my_dodag);
}
parent->dtsn = rpl_dio_buf->dtsn;
@ -699,7 +701,7 @@ void rpl_recv_DAO_mode(void)
if (increment_seq) {
RPL_COUNTER_INCREMENT(my_dodag->dao_seq);
delay_dao();
rpl_delay_dao(my_dodag);
}
}
@ -788,7 +790,7 @@ void rpl_recv_dao_ack_mode(void)
return;
}
dao_ack_received();
rpl_dao_ack_received(my_dodag);
}

20
sys/net/routing/rpl/rpl_storing/rpl_storing.c

@ -215,7 +215,9 @@ void rpl_init_root_mode(void)
}
i_am_root = 1;
start_trickle(dodag->dio_min, dodag->dio_interval_doubling, dodag->dio_redundancy);
trickle_start(rpl_process_pid, &dodag->trickle, RPL_MSG_TYPE_TRICKLE_INTERVAL,
RPL_MSG_TYPE_TRICKLE_CALLBACK, (1 << dodag->dio_min), dodag->dio_interval_doubling,
dodag->dio_redundancy);
DEBUGF("ROOT INIT FINISHED\n");
}
@ -570,7 +572,7 @@ void rpl_recv_DIO_mode(void)
if (my_dodag->my_rank == ROOT_RANK) {
DEBUGF("[Warning] Inconsistent Dodag Version\n");
my_dodag->version = RPL_COUNTER_INCREMENT(dio_dodag.version);
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
else {
DEBUGF("my dodag has no preferred_parent yet - seems to be odd since I have a parent.\n");
@ -581,20 +583,20 @@ void rpl_recv_DIO_mode(void)
}
else if (RPL_COUNTER_GREATER_THAN(my_dodag->version, dio_dodag.version)) {
/* lower version number detected -> send more DIOs */
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
return;
}
}
/* version matches, DODAG matches */
if (rpl_dio_buf->rank == INFINITE_RANK) {
reset_trickletimer();
trickle_reset_timer(&my_dodag->trickle);
}
/* We are root, all done!*/
if (my_dodag->my_rank == ROOT_RANK) {
if (rpl_dio_buf->rank != INFINITE_RANK) {
trickle_increment_counter();
trickle_increment_counter(&my_dodag->trickle);
}
return;
@ -615,7 +617,7 @@ void rpl_recv_DIO_mode(void)
}
else {
/* DIO OK */
trickle_increment_counter();
trickle_increment_counter(&my_dodag->trickle);
}
/* update parent rank */
@ -627,7 +629,7 @@ void rpl_recv_DIO_mode(void)
}
else if (rpl_equal_id(&parent->addr, &my_dodag->my_preferred_parent->addr) &&
(parent->dtsn != rpl_dio_buf->dtsn)) {
delay_dao();
rpl_delay_dao(my_dodag);
}
parent->dtsn = rpl_dio_buf->dtsn;
@ -722,7 +724,7 @@ void rpl_recv_DAO_mode(void)
if (increment_seq) {
RPL_COUNTER_INCREMENT(my_dodag->dao_seq);
delay_dao();
rpl_delay_dao(my_dodag);
}
}
@ -811,7 +813,7 @@ void rpl_recv_dao_ack_mode(void)
return;
}
dao_ack_received();
rpl_dao_ack_received(my_dodag);
}

288
sys/net/routing/rpl/trickle.c

@ -1,288 +0,0 @@
/**
* Copyright (C) 2013 INRIA.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup rpl
* @{
* @file trickle.c
* @brief Trickle
*
* Implementation of Trickle-Algorithm for RPL.
*
* @author Eric Engel <eric.engel@fu-berlin.de>
* @}
*/
#include <string.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "inttypes.h"
#include "trickle.h"
#include "rpl.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* thread stacks */
static char timer_over_buf[TRICKLE_TIMER_STACKSIZE];
static char interval_over_buf[TRICKLE_INTERVAL_STACKSIZE];
static char dao_delay_over_buf[DAO_DELAY_STACKSIZE];
static char routing_table_buf[RT_STACKSIZE];
kernel_pid_t timer_over_pid = KERNEL_PID_UNDEF;
kernel_pid_t interval_over_pid = KERNEL_PID_UNDEF;
kernel_pid_t dao_delay_over_pid = KERNEL_PID_UNDEF;
kernel_pid_t rt_timer_over_pid = KERNEL_PID_UNDEF;
bool ack_received;
uint8_t dao_counter;
uint8_t k;
uint32_t Imin;
uint8_t Imax;
uint32_t I;
uint32_t t;
uint16_t c;
vtimer_t trickle_t_timer;
vtimer_t trickle_I_timer;
vtimer_t dao_timer;
vtimer_t rt_timer;
timex_t t_time;
timex_t I_time;
timex_t dao_time;
timex_t rt_time;
static void *trickle_timer_over(void *arg);
static void *trickle_interval_over(void *arg);
static void *dao_delay_over(void *arg);
static void *rt_timer_over(void *arg);
void reset_trickletimer(void)
{
I = Imin;
c = 0;
/* start timer */
t = (I / 2) + (rand() % (I - (I / 2) + 1));
t_time = timex_set(0, t * 1000);
I_time = timex_set(0, I * 1000);
timex_normalize(&t_time);
timex_normalize(&I_time);
vtimer_remove(&trickle_t_timer);
vtimer_remove(&trickle_I_timer);
vtimer_set_wakeup(&trickle_t_timer, t_time, timer_over_pid);
vtimer_set_wakeup(&trickle_I_timer, I_time, interval_over_pid);
}
void init_trickle(void)
{
/* Create threads */
ack_received = true;
dao_counter = 0;
timer_over_pid = thread_create(timer_over_buf, TRICKLE_TIMER_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
trickle_timer_over, NULL, "trickle_timer_over");
interval_over_pid = thread_create(interval_over_buf, TRICKLE_INTERVAL_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
trickle_interval_over, NULL, "trickle_interval_over");
dao_delay_over_pid = thread_create(dao_delay_over_buf, DAO_DELAY_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
dao_delay_over, NULL, "dao_delay_over");
rt_timer_over_pid = thread_create(routing_table_buf, RT_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
rt_timer_over, NULL, "rt_timer_over");
}
void start_trickle(uint8_t DIOIntMin, uint8_t DIOIntDoubl,
uint8_t DIORedundancyConstant)
{
c = 0;
k = DIORedundancyConstant;
Imin = (1 << DIOIntMin);
Imax = DIOIntDoubl;
/* Eigentlich laut Spezifikation erste Bestimmung von I wie auskommentiert: */
/* I = Imin + ( rand() % ( (Imin << Imax) - Imin + 1 ) ); */
I = Imin + (rand() % (4 * Imin)) ;
t = (I / 2) + (rand() % (I - (I / 2) + 1));
t_time = timex_set(0, t * 1000);
timex_normalize(&t_time);
I_time = timex_set(0, I * 1000);
timex_normalize(&I_time);
vtimer_remove(&trickle_t_timer);
vtimer_remove(&trickle_I_timer);
vtimer_set_wakeup(&trickle_t_timer, t_time, timer_over_pid);
vtimer_set_wakeup(&trickle_I_timer, I_time, interval_over_pid);
}
void trickle_increment_counter(void)
{
/* call this function, when received DIO message */
c++;
}
static void *trickle_timer_over(void *arg)
{
(void) arg;
ipv6_addr_t mcast;
ipv6_addr_set_all_nodes_addr(&mcast);
while (1) {
thread_sleep();
/* Handle k=0 like k=infinity (according to RFC6206, section 6.5) */
if ((c < k) || (k == 0)) {
rpl_send_DIO(&mcast);
}
}
return NULL;
}
static void *trickle_interval_over(void *arg)
{
(void) arg;
while (1) {
thread_sleep();
I = I * 2;
DEBUG("TRICKLE new Interval %" PRIu32 "\n", I);
if (I == 0) {
DEBUGF("[WARNING] Interval was 0\n");
if (Imax == 0) {
DEBUGF("[WARNING] Imax == 0\n");
}
I = (Imin << Imax);
}
if (I > (Imin << Imax)) {
I = (Imin << Imax);
}
c = 0;
t = (I / 2) + (rand() % (I - (I / 2) + 1));
/* start timer */
t_time = timex_set(0, t * 1000);
timex_normalize(&t_time);
I_time = timex_set(0, I * 1000);
timex_normalize(&I_time);
vtimer_remove(&trickle_t_timer);
if (vtimer_set_wakeup(&trickle_t_timer, t_time, timer_over_pid) != 0) {
DEBUGF("[ERROR] setting Wakeup\n");
}
vtimer_remove(&trickle_I_timer);
if (vtimer_set_wakeup(&trickle_I_timer, I_time, interval_over_pid) != 0) {
DEBUGF("[ERROR] setting Wakeup\n");
}
}
return NULL;
}
void delay_dao(void)
{
dao_time = timex_set(DEFAULT_DAO_DELAY, 0);
dao_counter = 0;
ack_received = false;
vtimer_remove(&dao_timer);
vtimer_set_wakeup(&dao_timer, dao_time, dao_delay_over_pid);
}
/* This function is used for regular update of the routes. The Timer can be overwritten, as the normal delay_dao function gets called */
void long_delay_dao(void)
{
dao_time = timex_set(REGULAR_DAO_INTERVAL, 0);
dao_counter = 0;
ack_received = false;
vtimer_remove(&dao_timer);
vtimer_set_wakeup(&dao_timer, dao_time, dao_delay_over_pid);
}
static void *dao_delay_over(void *arg)
{
(void) arg;
while (1) {
thread_sleep();
if ((ack_received == false) && (dao_counter < DAO_SEND_RETRIES)) {
dao_counter++;
rpl_send_DAO(NULL, 0, true, 0);
dao_time = timex_set(DEFAULT_WAIT_FOR_DAO_ACK, 0);
vtimer_remove(&dao_timer);
vtimer_set_wakeup(&dao_timer, dao_time, dao_delay_over_pid);
}
else if (ack_received == false) {
long_delay_dao();
}
}
return NULL;
}
void dao_ack_received(void)
{
ack_received = true;
long_delay_dao();
}
static void *rt_timer_over(void *arg)
{
(void) arg;
#if RPL_MAX_ROUTING_ENTRIES != 0
rpl_routing_entry_t *rt;
#endif
while (1) {
rpl_dodag_t *my_dodag = rpl_get_my_dodag();
if (my_dodag != NULL) {
#if RPL_MAX_ROUTING_ENTRIES != 0
rt = rpl_get_routing_table();
for (uint8_t i = 0; i < rpl_max_routing_entries; i++) {
if (rt[i].used) {
if (rt[i].lifetime <= 1) {
memset(&rt[i], 0, sizeof(rt[i]));
}
else {
rt[i].lifetime--;
}
}
}
#endif
/* Parent is NULL for root too */
if (my_dodag->my_preferred_parent != NULL) {
if (my_dodag->my_preferred_parent->lifetime <= 1) {
DEBUGF("parent lifetime timeout\n");
rpl_parent_update(NULL);
}
else {
my_dodag->my_preferred_parent->lifetime--;
}
}
}
/* Wake up every second */
vtimer_usleep(1000000);
}
return NULL;
}

42
sys/net/routing/rpl/trickle.h

@ -1,42 +0,0 @@
/**
* Copyright (C) 2013 INRIA.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup rpl
* @{
* @file trickle.h
* @brief Trickle
*
* Header-file, which defines all Trickle constants and prototypes
*
* @author Eric Engel <eric.engel@fu-berlin.de>
* @}
*/
#include "vtimer.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TRICKLE_TIMER_STACKSIZE (KERNEL_CONF_STACKSIZE_MAIN)
#define TRICKLE_INTERVAL_STACKSIZE (KERNEL_CONF_STACKSIZE_MAIN)
#define DAO_DELAY_STACKSIZE (KERNEL_CONF_STACKSIZE_MAIN)
#define RT_STACKSIZE (KERNEL_CONF_STACKSIZE_DEFAULT)
void reset_trickletimer(void);
void init_trickle(void);
void start_trickle(uint8_t DIOINtMin, uint8_t DIOIntDoubl, uint8_t DIORedundancyConstatnt);
void trickle_increment_counter(void);