diff --git a/core/net/ipv6/multicast/roll-tm.c b/core/net/ipv6/multicast/roll-tm.c new file mode 100644 index 000000000..37f14f457 --- /dev/null +++ b/core/net/ipv6/multicast/roll-tm.c @@ -0,0 +1,1435 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * This file implements IPv6 MCAST forwarding according to the + * algorithm described in the "MCAST Forwarding Using Trickle" + * internet draft. + * + * The current version of the draft can always be found in + * http://tools.ietf.org/html/draft-ietf-roll-trickle-mcast + * + * This implementation is based on the draft version stored in + * ROLL_TM_VER + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" +#include "net/ipv6/multicast/uip-mcast6.h" +#include "net/ipv6/multicast/roll-tm.h" +#include "dev/watchdog.h" +#include + +#define DEBUG DEBUG_NONE +#include "net/ip/uip-debug.h" + +#define TRICKLE_VERBOSE 0 + +#if DEBUG && TRICKLE_VERBOSE +#define VERBOSE_PRINTF(...) PRINTF(__VA_ARGS__) +#define VERBOSE_PRINT_SEED(s) PRINT_SEED(s) +#else +#define VERBOSE_PRINTF(...) +#define VERBOSE_PRINT_SEED(...) +#endif + +#if UIP_CONF_IPV6 +/*---------------------------------------------------------------------------*/ +/* Data Representation */ +/*---------------------------------------------------------------------------*/ +#if ROLL_TM_SHORT_SEEDS +typedef union seed_id_u { + uint8_t u8[2]; + uint16_t id; /* Big Endian */ +} seed_id_t; + +#define seed_is_null(s) ((s)->id == 0) +#define PRINT_SEED(s) PRINTF("0x%02x%02x", (s)->u8[0], (s)->u8[1]) +#else /* ROLL_TM_SHORT_SEEDS */ +typedef uip_ip6addr_t seed_id_t; + +#define seed_is_null(s) uip_is_addr_unspecified(s) +#define PRINT_SEED(s) PRINT6ADDR(s) +#endif /* ROLL_TM_SHORT_SEEDS */ +#define seed_id_cmp(a, b) (memcmp((a), (b), sizeof(seed_id_t)) == 0) +#define seed_id_cpy(a, b) (memcpy((a), (b), sizeof(seed_id_t))) + +/* Trickle Timers */ +struct trickle_param { + clock_time_t i_min; /* Clock ticks */ + clock_time_t t_start; /* Start of the interval (absolute clock_time) */ + clock_time_t t_end; /* End of the interval (absolute clock_time) */ + clock_time_t t_next; /* Clock ticks, randomised in [I/2, I) */ + clock_time_t t_last_trigger; + struct ctimer ct; + uint8_t i_current; /* Current doublings from i_min */ + uint8_t i_max; /* Max number of doublings */ + uint8_t k; /* Redundancy Constant */ + uint8_t t_active; /* Units of Imax */ + uint8_t t_dwell; /* Units of Imax */ + uint8_t c; /* Consistency Counter */ + uint8_t inconsistency; +}; + +/** + * \brief Convert a timer to a sane clock_time_t value after d doublings + * m is a value of Imin, d is a number of doublings + * Careful of overflows + */ +#define TRICKLE_TIME(m, d) ((clock_time_t)((m) << (d))) + +/** + * \brief Convert Imax from number of doublings to clock_time_t units for + * trickle_param t. Again, watch out for overflows */ +#define TRICKLE_IMAX(t) ((uint32_t)((t)->i_min << (t)->i_max)) + +/** + * \brief Convert Tactive for a trickle timer to a sane clock_time_t value + * t is a pointer to the timer + * Careful of overflows + */ +#define TRICKLE_ACTIVE(t) ((uint32_t)(TRICKLE_IMAX(t) * t->t_active)) + +/** + * \brief Convert Tdwell for a trickle timer to a sane clock_time_t value + * t is a pointer to the timer + * Careful of overflows + */ +#define TRICKLE_DWELL(t) ((uint32_t)(TRICKLE_IMAX(t) * t->t_dwell)) + +/** + * \brief Check if suppression is enabled for trickle_param t + * t is a pointer to the timer + */ +#define SUPPRESSION_ENABLED(t) ((t)->k != ROLL_TM_INFINITE_REDUNDANCY) + +/** + * \brief Check if suppression is disabled for trickle_param t + * t is a pointer to the timer + */ +#define SUPPRESSION_DISABLED(t) ((t)->k == ROLL_TM_INFINITE_REDUNDANCY) + +/** + * \brief Init trickle_timer[m] + */ +#define TIMER_CONFIGURE(m) do { \ + t[m].i_min = ROLL_TM_IMIN_##m; \ + t[m].i_max = ROLL_TM_IMAX_##m; \ + t[m].k = ROLL_TM_K_##m; \ + t[m].t_active = ROLL_TM_T_ACTIVE_##m; \ + t[m].t_dwell = ROLL_TM_T_DWELL_##m; \ + t[m].t_last_trigger = clock_time(); \ +} while(0) +/*---------------------------------------------------------------------------*/ +/* Sequence Values and Serial Number Arithmetic + * + * Sequence Number Comparisons as per RFC1982 "Serial Number Arithmetic" + * Our 'SERIAL_BITS' value is 15 here + * + * NOTE: There can be pairs of sequence numbers s1 and s2 with an undefined + * ordering. All three macros would evaluate as 0, as in: + * SEQ_VAL_IS_EQUAL(s1, s2) == 0 and + * SEQ_VAL_IS_GT(s1, s2) == 0 and + * SEQ_VAL_IS_LT(s1, s2) == 0 + * + * This is not a bug of this implementation, it's an RFC design choice + */ + +/** + * \brief s1 is said to be equal s2 iif SEQ_VAL_IS_EQ(s1, s2) == 1 + */ +#define SEQ_VAL_IS_EQ(i1, i2) ((i1) == (i2)) + +/** + * \brief s1 is said to be less than s2 iif SEQ_VAL_IS_LT(s1, s2) == 1 + */ +#define SEQ_VAL_IS_LT(i1, i2) \ + ( \ + ((i1) != (i2)) && \ + ((((i1) < (i2)) && ((int16_t)((i2) - (i1)) < 0x4000)) || \ + (((i1) > (i2)) && ((int16_t)((i1) - (i2)) > 0x4000))) \ + ) + +/** + * \brief s1 is said to be greater than s2 iif SEQ_VAL_IS_LT(s1, s2) == 1 + */ +#define SEQ_VAL_IS_GT(i1, i2) \ +( \ + ((i1) != (i2)) && \ + ((((i1) < (i2)) && ((int16_t)((i2) - (i1)) > 0x4000)) || \ + (((i1) > (i2)) && ((int16_t)((i1) - (i2)) < 0x4000))) \ +) + +/** + * \brief Add n to s: (s + n) modulo (2 ^ SERIAL_BITS) => ((s + n) % 0x8000) + */ +#define SEQ_VAL_ADD(s, n) (((s) + (n)) % 0x8000) +/*---------------------------------------------------------------------------*/ +/* Sliding Windows */ +struct sliding_window { + seed_id_t seed_id; + int16_t lower_bound; /* lolipop */ + int16_t upper_bound; /* lolipop */ + int16_t min_listed; /* lolipop */ + uint8_t flags; /* Is used, Trickle param, Is listed */ + uint8_t count; +}; + +#define SLIDING_WINDOW_U_BIT 0x80 /* Is used */ +#define SLIDING_WINDOW_M_BIT 0x40 /* Window trickle parametrization */ +#define SLIDING_WINDOW_L_BIT 0x20 /* Current ICMP message lists us */ +#define SLIDING_WINDOW_B_BIT 0x10 /* Used when updating bounds */ + +/** + * \brief Is Occupied sliding window location w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_IS_USED(w) ((w)->flags & SLIDING_WINDOW_U_BIT) + +/** + * \brief Set 'Is Used' bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_IS_USED_SET(w) ((w)->flags |= SLIDING_WINDOW_U_BIT) + +/** + * \brief Clear 'Is Used' bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_IS_USED_CLR(w) ((w)->flags &= ~SLIDING_WINDOW_U_BIT) +#define window_free(w) SLIDING_WINDOW_IS_USED_CLR(w) + +/** + * \brief Set 'Is Seen' bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_LISTED_SET(w) ((w)->flags |= SLIDING_WINDOW_L_BIT) + +/** + * \brief Clear 'Is Seen' bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_LISTED_CLR(w) ((w)->flags &= ~SLIDING_WINDOW_L_BIT) + +/** + * \brief Is the sliding window at location w listed in current ICMP message? + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_IS_LISTED(w) ((w)->flags & SLIDING_WINDOW_L_BIT) + +/** + * \brief Set M bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_M_SET(w) ((w)->flags |= SLIDING_WINDOW_M_BIT) + +/** + * \brief Clear M bit for window w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_M_CLR(w) ((w)->flags &= ~SLIDING_WINDOW_M_BIT) + +/** + * \brief Retrieve trickle parametrization for sliding window at location w + * w: pointer to a sliding window + */ +#define SLIDING_WINDOW_GET_M(w) \ + ((uint8_t)(((w)->flags & SLIDING_WINDOW_M_BIT) == SLIDING_WINDOW_M_BIT)) +/*---------------------------------------------------------------------------*/ +/* Multicast Packet Buffers */ +struct mcast_packet { +#if ROLL_TM_SHORT_SEEDS + /* Short seeds are stored inside the message */ + seed_id_t seed_id; +#endif + uint32_t active; /* Starts at 0 and increments */ + uint32_t dwell; /* Starts at 0 and increments */ + uint16_t buff_len; + uint16_t seq_val; /* host-byte order */ + struct sliding_window *sw; /* Pointer to the SW this packet belongs to */ + uint8_t flags; /* Is-Used, Must Send, Is Listed */ + uint8_t buff[UIP_BUFSIZE - UIP_LLH_LEN]; +}; + +/* Flag bits */ +#define MCAST_PACKET_U_BIT 0x80 /* Is Used */ +#define MCAST_PACKET_S_BIT 0x20 /* Must Send Next Pass */ +#define MCAST_PACKET_L_BIT 0x10 /* Is listed in ICMP message */ + +/* Fetch a pointer to the Seed ID of a buffered message p */ +#if ROLL_TM_SHORT_SEEDS +#define MCAST_PACKET_GET_SEED(p) ((seed_id_t *)&((p)->seed_id)) +#else +#define MCAST_PACKET_GET_SEED(p) \ + ((seed_id_t *)&((struct uip_ip_hdr *)&(p)->buff[UIP_LLH_LEN])->srcipaddr) +#endif + +/** + * \brief Get the TTL of a buffered packet + * p: pointer to a packet buffer + */ +#define MCAST_PACKET_TTL(p) \ + (((struct uip_ip_hdr *)(p)->buff)->ttl) + +/** + * \brief Set 'Is Used' bit for packet p + * p: pointer to a packet buffer + */ +#define MCAST_PACKET_USED_SET(p) ((p)->flags |= MCAST_PACKET_U_BIT) + +/** + * \brief Clear 'Is Used' bit for packet p + * p: pointer to a packet buffer + */ +#define MCAST_PACKET_USED_CLR(p) ((p)->flags &= ~MCAST_PACKET_U_BIT) + +/** + * \brief Is Occupied buffer location p + */ +#define MCAST_PACKET_IS_USED(p) ((p)->flags & MCAST_PACKET_U_BIT) + +/** + * \brief Must we send this message this pass? + */ +#define MCAST_PACKET_MUST_SEND(p) ((p)->flags & MCAST_PACKET_S_BIT) + +/** + * \brief Set 'Must Send' bit for message p + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_SEND_SET(p) ((p)->flags |= MCAST_PACKET_S_BIT) + +/** + * \brief Clear 'Must Send' bit for message p + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_SEND_CLR(p) ((p)->flags &= ~MCAST_PACKET_S_BIT) + +/** + * \brief Is the message p listed in current ICMP message? + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_IS_LISTED(p) ((p)->flags & MCAST_PACKET_L_BIT) + +/** + * \brief Set 'Is Listed' bit for message p + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_LISTED_SET(p) ((p)->flags |= MCAST_PACKET_L_BIT) + +/** + * \brief Clear 'Is Listed' bit for message p + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_LISTED_CLR(p) ((p)->flags &= ~MCAST_PACKET_L_BIT) + +/** + * \brief Free a multicast packet buffer + * p: pointer to a struct mcast_packet + */ +#define MCAST_PACKET_FREE(p) ((p)->flags = 0) +/*---------------------------------------------------------------------------*/ +/* Sequence Lists in Multicast Trickle ICMP messages */ +struct sequence_list_header { + uint8_t flags; /* S: Seed ID length, M: Trickle parametrization */ + uint8_t seq_len; + seed_id_t seed_id; +}; + +#define SEQUENCE_LIST_S_BIT 0x80 +#define SEQUENCE_LIST_M_BIT 0x40 +#define SEQUENCE_LIST_RES 0x3F + +/** + * \brief Get the Trickle Parametrization for an ICMPv6 sequence list + * l: pointer to a sequence list structure + */ +#define SEQUENCE_LIST_GET_M(l) \ + ((uint8_t)(((l)->flags & SEQUENCE_LIST_M_BIT) == SEQUENCE_LIST_M_BIT)) + +/** + * \brief Get the Seed ID Length for an ICMPv6 sequence list + * l: pointer to a sequence list structure + */ +#define SEQUENCE_LIST_GET_S(l) \ + ((uint8_t)(((l)->flags & SEQUENCE_LIST_S_BIT) == SEQUENCE_LIST_S_BIT)) +/*---------------------------------------------------------------------------*/ +/* Trickle Multicast HBH Option */ +struct hbho_mcast { + uint8_t type; + uint8_t len; +#if ROLL_TM_SHORT_SEEDS + seed_id_t seed_id; +#endif + uint8_t flags; /* M, Seq ID MSB */ + uint8_t seq_id_lsb; +#if !ROLL_TM_SHORT_SEEDS + /* Need to Pad to 8 bytes with PadN */ + uint8_t padn_type; /* 1: PadN */ + uint8_t padn_len; /* 0->2 bytes */ +#endif +}; + +#define HBHO_OPT_TYPE_TRICKLE 0x0C +#define HBHO_LEN_LONG_SEED 2 +#define HBHO_LEN_SHORT_SEED 4 +#define HBHO_TOTAL_LEN 8 +/** + * \brief Get the Trickle Parametrization for a multicast HBHO header + * m: pointer to the HBHO header + */ +#define HBH_GET_M(h) (((h)->flags & 0x80) == 0x80) + +/** + * \brief Set the Trickle Parametrization bit for a multicast HBHO header + * m: pointer to the HBHO header + */ +#define HBH_SET_M(h) ((h)->flags |= 0x80) + +/** + * \brief Retrieve the Sequence Value MSB from a multicast HBHO header + * m: pointer to the HBHO header + */ +#define HBH_GET_SV_MSB(h) ((h)->flags & 0x7F) +/*---------------------------------------------------------------------------*/ +/* Destination for our ICMPv6 datagrams */ +#if ROLL_TM_CONF_DEST_ALL_NODES +#define roll_tm_create_dest(a) uip_create_linklocal_allnodes_mcast(a) +#else +#define roll_tm_create_dest(a) uip_create_linklocal_allrouters_mcast(a) +#endif +/*---------------------------------------------------------------------------*/ +/* Maintain Stats */ +#if UIP_MCAST6_CONF_STATS +struct roll_tm_stats roll_tm_stats; + +#define STATS_ADD(x) roll_tm_stats.x++ +#define STATS_RESET() do { \ + memset(&roll_tm_stats, 0, sizeof(roll_tm_stats)); } while(0) +#else +#define STATS_ADD(x) +#define STATS_RESET() +#endif +/*---------------------------------------------------------------------------*/ +/* Internal Data Structures */ +/*---------------------------------------------------------------------------*/ +static struct trickle_param t[2]; +static struct sliding_window windows[ROLL_TM_WINS]; +static struct mcast_packet buffered_msgs[ROLL_TM_BUFF_NUM]; +/*---------------------------------------------------------------------------*/ +/* Temporary Stores */ +/*---------------------------------------------------------------------------*/ +static struct trickle_param *loctpptr; +static struct sequence_list_header *locslhptr; +static struct sliding_window *locswptr; +static struct sliding_window *iterswptr; +static struct mcast_packet *locmpptr; +static struct hbho_mcast *lochbhmptr; +static uint16_t last_seq; +/*---------------------------------------------------------------------------*/ +/* uIPv6 Pointers */ +/*---------------------------------------------------------------------------*/ +#define UIP_DATA_BUF ((uint8_t *)&uip_buf[uip_l2_l3_hdr_len + UIP_UDPH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) +#define UIP_EXT_BUF_NEXT ((uint8_t *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN + HBHO_TOTAL_LEN]) +#define UIP_EXT_OPT_FIRST ((struct hbho_mcast *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN + 2]) +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_ICMP_PAYLOAD ((unsigned char *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +extern uint16_t uip_slen; +/*---------------------------------------------------------------------------*/ +/* Local function prototypes */ +/*---------------------------------------------------------------------------*/ +static void icmp_output(); +static void window_update_bounds(); +static void reset_trickle_timer(uint8_t); +static void handle_timer(void *); +/*---------------------------------------------------------------------------*/ +/* Return a random number in [I/2, I), for a timer with Imin when the timer's + * current number of doublings is d */ +static clock_time_t +random_interval(clock_time_t i_min, uint8_t d) +{ + clock_time_t min = TRICKLE_TIME(i_min >> 1, d); + + VERBOSE_PRINTF("ROLL TM: Random [%lu, %lu)\n", (unsigned long)min, + (unsigned long)(TRICKLE_TIME(i_min, d))); + + return min + (random_rand() % (TRICKLE_TIME(i_min, d) - 1 - min)); +} +/*---------------------------------------------------------------------------*/ +/* Called at the end of the current interval for timer ptr */ +static void +double_interval(void *ptr) +{ + struct trickle_param *param = (struct trickle_param *)ptr; + int16_t offset; + clock_time_t next; + + /* + * If we got called long past our expiration, store the offset and try to + * compensate this period + */ + offset = (int16_t)(clock_time() - param->t_end); + + /* Calculate next interval */ + if(param->i_current < param->i_max) { + param->i_current++; + } + + param->t_start = param->t_end; + param->t_end = param->t_start + (param->i_min << param->i_current); + + next = random_interval(param->i_min, param->i_current); + if(next > offset) { + next -= offset; + } else { + next = 0; + } + param->t_next = next; + ctimer_set(¶m->ct, param->t_next, handle_timer, (void *)param); + + VERBOSE_PRINTF("ROLL TM: Doubling at %lu (offset %d), Start %lu, End %lu," + " Periodic in %lu\n", clock_time(), offset, + (unsigned long)param->t_start, + (unsigned long)param->t_end, (unsigned long)param->t_next); +} +/*---------------------------------------------------------------------------*/ +/* + * Called at a random point in [I/2,I) of the current interval for ptr + * PARAM is a pointer to the timer that triggered the callback (&t[index]) + */ +static void +handle_timer(void *ptr) +{ + struct trickle_param *param; + clock_time_t diff_last; /* Time diff from last pass */ + clock_time_t diff_start; /* Time diff from interval start */ + uint8_t m; + + param = (struct trickle_param *)ptr; + if(param == &t[0]) { + m = 0; + } else if(param == &t[1]) { + m = 1; + } else { + /* This is an ooops and a serious one too */ + return; + } + + /* Bail out pronto if our uIPv6 stack is not ready to send messages */ + if(uip_ds6_get_link_local(ADDR_PREFERRED) == NULL) { + VERBOSE_PRINTF + ("ROLL TM: Suppressing timer processing. Stack not ready\n"); + reset_trickle_timer(m); + return; + } + + VERBOSE_PRINTF("ROLL TM: M=%u Periodic at %lu, last=%lu\n", + m, (unsigned long)clock_time(), + (unsigned long)param->t_last_trigger); + + /* Temporarily store 'now' in t_next and calculate diffs */ + param->t_next = clock_time(); + diff_last = param->t_next - param->t_last_trigger; + diff_start = param->t_next - param->t_start; + param->t_last_trigger = param->t_next; + + VERBOSE_PRINTF + ("ROLL TM: M=%u Periodic diff from last %lu, from start %lu\n", m, + (unsigned long)diff_last, (unsigned long)diff_start); + + /* Handle all buffered messages */ + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr) + && (SLIDING_WINDOW_GET_M(locmpptr->sw) == m)) { + + /* + * if() + * If the packet was received during the last interval, its reception + * caused an inconsistency (and thus a timer reset). This means that + * the packet was received at about t_start, we increment by diff_start + * + * else() + * If the packet was not received during the last window, it is safe to + * increase its lifetime counters by the time diff from last pass + * + * if active == dwell == 0 but i_current != 0, this is an oops + * (new packet that didn't reset us). We don't handle it + */ + if(locmpptr->active == 0) { + locmpptr->active += diff_start; + locmpptr->dwell += diff_start; + } else { + locmpptr->active += diff_last; + locmpptr->dwell += diff_last; + } + + VERBOSE_PRINTF("ROLL TM: M=%u Packet %u active %lu of %lu\n", + m, locmpptr->seq_val, locmpptr->active, + TRICKLE_ACTIVE(param)); + + if(locmpptr->dwell > TRICKLE_DWELL(param)) { + locmpptr->sw->count--; + PRINTF("ROLL TM: M=%u Free Packet %u (%lu > %lu), Window now at %u\n", + m, locmpptr->seq_val, locmpptr->dwell, + TRICKLE_DWELL(param), locmpptr->sw->count); + if(locmpptr->sw->count == 0) { + PRINTF("ROLL TM: M=%u Free Window ", m); + PRINT_SEED(&locmpptr->sw->seed_id); + PRINTF("\n"); + window_free(locmpptr->sw); + } + MCAST_PACKET_FREE(locmpptr); + } else if(MCAST_PACKET_TTL(locmpptr) > 0) { + /* Handle multicast transmissions */ + if((SUPPRESSION_ENABLED(param) && MCAST_PACKET_MUST_SEND(locmpptr)) || + (SUPPRESSION_DISABLED(param) && + locmpptr->active < TRICKLE_ACTIVE(param))) { + PRINTF("ROLL TM: M=%u Periodic - Sending packet from Seed ", m); + PRINT_SEED(&locmpptr->sw->seed_id); + PRINTF(" seq %u\n", locmpptr->seq_val); + uip_len = locmpptr->buff_len; + memcpy(UIP_IP_BUF, &locmpptr->buff, uip_len); + + STATS_ADD(mcast_fwd); + tcpip_output(NULL); + MCAST_PACKET_SEND_CLR(locmpptr); + watchdog_periodic(); + } + } + } + } + + /* Suppression Enabled - Send an ICMP */ + if(SUPPRESSION_ENABLED(param)) { + if(param->c < param->k) { + icmp_output(); + } + } + + /* Done handling inconsistencies for this timer */ + param->inconsistency = 0; + param->c = 0; + + window_update_bounds(); + + /* Temporarily store 'now' in t_next */ + param->t_next = clock_time(); + if(param->t_next >= param->t_end) { + /* took us too long to process things, double interval asap */ + param->t_next = 0; + } else { + param->t_next = param->t_end - param->t_next; + } + VERBOSE_PRINTF + ("ROLL TM: M=%u Periodic at %lu, Interval End at %lu in %lu\n", m, + (unsigned long)clock_time(), (unsigned long)param->t_end, + (unsigned long)param->t_next); + ctimer_set(¶m->ct, param->t_next, double_interval, (void *)param); + + return; +} +/*---------------------------------------------------------------------------*/ +static void +reset_trickle_timer(uint8_t index) +{ + t[index].t_start = clock_time(); + t[index].t_end = t[index].t_start + (t[index].i_min); + t[index].i_current = 0; + t[index].c = 0; + t[index].t_next = random_interval(t[index].i_min, t[index].i_current); + + VERBOSE_PRINTF + ("ROLL TM: M=%u Reset at %lu, Start %lu, End %lu, New Interval %lu\n", + index, (unsigned long)t[index].t_start, (unsigned long)t[index].t_start, + (unsigned long)t[index].t_end, (unsigned long)t[index].t_next); + + ctimer_set(&t[index].ct, t[index].t_next, handle_timer, (void *)&t[index]); +} +/*---------------------------------------------------------------------------*/ +static struct sliding_window * +window_allocate() +{ + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + if(!SLIDING_WINDOW_IS_USED(iterswptr)) { + iterswptr->count = 0; + iterswptr->lower_bound = -1; + iterswptr->upper_bound = -1; + iterswptr->min_listed = -1; + return iterswptr; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static struct sliding_window * +window_lookup(seed_id_t *s, uint8_t m) +{ + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + VERBOSE_PRINTF("ROLL TM: M=%u (%u) ", SLIDING_WINDOW_GET_M(iterswptr), m); + VERBOSE_PRINT_SEED(&iterswptr->seed_id); + VERBOSE_PRINTF("\n"); + if(seed_id_cmp(s, &iterswptr->seed_id) && + SLIDING_WINDOW_GET_M(iterswptr) == m) { + return iterswptr; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static void +window_update_bounds() +{ + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + iterswptr->lower_bound = -1; + } + + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr)) { + iterswptr = locmpptr->sw; + VERBOSE_PRINTF("ROLL TM: Update Bounds: [%d - %d] vs %u\n", + iterswptr->lower_bound, iterswptr->upper_bound, + locmpptr->seq_val); + if(iterswptr->lower_bound < 0 + || SEQ_VAL_IS_LT(locmpptr->seq_val, iterswptr->lower_bound)) { + iterswptr->lower_bound = locmpptr->seq_val; + } + if(iterswptr->upper_bound < 0 || + SEQ_VAL_IS_GT(locmpptr->seq_val, iterswptr->upper_bound)) { + iterswptr->upper_bound = locmpptr->seq_val; + } + } + } +} +/*---------------------------------------------------------------------------*/ +static struct mcast_packet * +buffer_reclaim() +{ + struct sliding_window *largest = windows; + struct mcast_packet *rv; + + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + if(iterswptr->count > largest->count) { + largest = iterswptr; + } + } + + if(largest->count == 1) { + /* Can't reclaim last entry for a window and this is the largest window */ + return NULL; + } + + PRINTF("ROLL TM: Reclaim from Seed "); + PRINT_SEED(&largest->seed_id); + PRINTF(" M=%u, count was %u\n", + SLIDING_WINDOW_GET_M(largest), largest->count); + /* Find the packet at the lowest bound for the largest window */ + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr) && (locmpptr->sw == largest) && + SEQ_VAL_IS_EQ(locmpptr->seq_val, largest->lower_bound)) { + rv = locmpptr; + PRINTF("ROLL TM: Reclaim seq. val %u\n", locmpptr->seq_val); + MCAST_PACKET_FREE(rv); + largest->count--; + window_update_bounds(); + VERBOSE_PRINTF("ROLL TM: Reclaim - new bounds [%u , %u]\n", + largest->lower_bound, largest->upper_bound); + return rv; + } + } + + /* oops */ + return NULL; +} +/*---------------------------------------------------------------------------*/ +static struct mcast_packet * +buffer_allocate() +{ + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(!MCAST_PACKET_IS_USED(locmpptr)) { + return locmpptr; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static void +icmp_output() +{ + struct sequence_list_header *sl; + uint8_t *buffer; + uint16_t payload_len; + + PRINTF("ROLL TM: ICMPv6 Out\n"); + + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = ROLL_TM_IP_HOP_LIMIT; + + sl = (struct sequence_list_header *)UIP_ICMP_PAYLOAD; + payload_len = 0; + + VERBOSE_PRINTF("ROLL TM: ICMPv6 Out - Hdr @ %p, payload @ %p\n", UIP_ICMP_BUF, sl); + + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + if(SLIDING_WINDOW_IS_USED(iterswptr) && iterswptr->count > 0) { + memset(sl, 0, sizeof(struct sequence_list_header)); +#if ROLL_TM_SHORT_SEEDS + sl->flags = SEQUENCE_LIST_S_BIT; +#endif + if(SLIDING_WINDOW_GET_M(iterswptr)) { + sl->flags |= SEQUENCE_LIST_M_BIT; + } + sl->seq_len = iterswptr->count; + seed_id_cpy(&sl->seed_id, &iterswptr->seed_id); + + PRINTF("ROLL TM: ICMPv6 Out - Seq. F=0x%02x, L=%u, Seed ID=", sl->flags, + sl->seq_len); + PRINT_SEED(&sl->seed_id); + + buffer = (uint8_t *)sl + sizeof(struct sequence_list_header); + + payload_len += sizeof(struct sequence_list_header); + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr)) { + if(locmpptr->sw == iterswptr) { + PRINTF(", %u", locmpptr->seq_val); + *buffer = (uint8_t)(locmpptr->seq_val >> 8); + buffer++; + *buffer = (uint8_t)(locmpptr->seq_val & 0xFF); + buffer++; + } + } + } + PRINTF("\n"); + payload_len += sl->seq_len * 2; + sl = (struct sequence_list_header *)buffer; + } + } + + if(payload_len == 0) { + VERBOSE_PRINTF("ROLL TM: ICMPv6 Out - nothing to send\n"); + return; + } + + roll_tm_create_dest(&UIP_IP_BUF->destipaddr); + uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + + UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8; + UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff; + + UIP_ICMP_BUF->type = ICMP6_ROLL_TM; + UIP_ICMP_BUF->icode = ROLL_TM_ICMP_CODE; + + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len; + + VERBOSE_PRINTF("ROLL TM: ICMPv6 Out - %u bytes\n", payload_len); + + tcpip_ipv6_output(); + STATS_ADD(icmp_out); + return; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Processes an incoming or outgoing multicast message and determines + * whether it should be dropped or accepted + * + * \param in 1: Incoming packet, 0: Outgoing (we are the seed) + * + * \return 0: Drop, 1: Accept + */ +static uint8_t +accept(uint8_t in) +{ + seed_id_t *seed_ptr; + uint8_t m; + uint16_t seq_val; + + PRINTF("ROLL TM: Multicast I/O\n"); + +#if UIP_CONF_IPV6_CHECKS + if(uip_is_addr_mcast_non_routable(&UIP_IP_BUF->destipaddr)) { + PRINTF("ROLL TM: Mcast I/O, bad destination\n"); + UIP_MCAST6_STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } + /* + * Abort transmission if the v6 src is unspecified. This may happen if the + * seed tries to TX while it's still performing DAD or waiting for a prefix + */ + if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) { + PRINTF("ROLL TM: Mcast I/O, bad source\n"); + STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } +#endif + + /* Check the Next Header field: Must be HBHO */ + if(UIP_IP_BUF->proto != UIP_PROTO_HBHO) { + PRINTF("ROLL TM: Mcast I/O, bad proto\n"); + STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } else { + /* Check the Option Type */ + if(UIP_EXT_OPT_FIRST->type != HBHO_OPT_TYPE_TRICKLE) { + PRINTF("ROLL TM: Mcast I/O, bad HBHO type\n"); + STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } + } + lochbhmptr = UIP_EXT_OPT_FIRST; + + PRINTF("ROLL TM: HBHO T=%u, L=%u, M=%u, S=0x%02x%02x\n", + lochbhmptr->type, lochbhmptr->len, HBH_GET_M(lochbhmptr), + HBH_GET_SV_MSB(lochbhmptr), lochbhmptr->seq_id_lsb); + + /* Drop unsupported Seed ID Lengths. S bit: 0->short, 1->long */ +#if ROLL_TM_SHORT_SEEDS + /* Short Seed ID: Len MUST be 4 */ + if(lochbhmptr->len != HBHO_LEN_SHORT_SEED) { + PRINTF("ROLL TM: Mcast I/O, bad length\n"); + STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } +#else + /* Long Seed ID: Len MUST be 2 (Seed ID is elided) */ + if(lochbhmptr->len != HBHO_LEN_LONG_SEED) { + PRINTF("ROLL TM: Mcast I/O, bad length\n"); + STATS_ADD(mcast_bad); + return UIP_MCAST6_DROP; + } +#endif + +#if UIP_MCAST6_CONF_STATS + if(in == ROLL_TM_DGRAM_IN) { + STATS_ADD(mcast_in_all); + } +#endif + + /* Is this for a known window? */ +#if ROLL_TM_SHORT_SEEDS + seed_ptr = &lochbhmptr->seed_id; +#else + seed_ptr = &UIP_IP_BUF->srcipaddr; +#endif + m = HBH_GET_M(lochbhmptr); + + locswptr = window_lookup(seed_ptr, m); + + seq_val = lochbhmptr->seq_id_lsb; + seq_val |= HBH_GET_SV_MSB(lochbhmptr) << 8; + + if(locswptr) { + if(SEQ_VAL_IS_LT(seq_val, locswptr->lower_bound)) { + /* Too old, drop */ + PRINTF("ROLL TM: Too old\n"); + STATS_ADD(mcast_dropped); + return UIP_MCAST6_DROP; + } + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr) && + locmpptr->sw == locswptr && + SLIDING_WINDOW_GET_M(locmpptr->sw) == m && + SEQ_VAL_IS_EQ(seq_val, locmpptr->seq_val)) { + /* Seen before , drop */ + PRINTF("ROLL TM: Seen before\n"); + STATS_ADD(mcast_dropped); + return UIP_MCAST6_DROP; + } + } + } + + PRINTF("ROLL TM: New message\n"); + + /* We have not seen this message before */ + /* Allocate a window if we have to */ + if(!locswptr) { + locswptr = window_allocate(); + PRINTF("ROLL TM: New seed\n"); + } + if(!locswptr) { + /* Couldn't allocate window, drop */ + PRINTF("ROLL TM: Failed to allocate window\n"); + STATS_ADD(mcast_dropped); + return UIP_MCAST6_DROP; + } + + /* Allocate a buffer */ + locmpptr = buffer_allocate(); + if(!locmpptr) { + PRINTF("ROLL TM: Buffer allocation failed, reclaiming\n"); + locmpptr = buffer_reclaim(); + } + + if(!locmpptr) { + /* Failed to allocate / reclaim a buffer. If the window has only just been + * allocated, free it before dropping */ + PRINTF("ROLL TM: Buffer reclaim failed\n"); + if(locswptr->count == 0) { + window_free(locswptr); + STATS_ADD(mcast_dropped); + return UIP_MCAST6_DROP; + } + } +#if UIP_MCAST6_CONF_STATS + if(in == ROLL_TM_DGRAM_IN) { + STATS_ADD(mcast_in_unique); + } +#endif + + /* We have a window and we have a buffer. Accept this message */ + /* Set the seed ID and correct M for this window */ + SLIDING_WINDOW_M_CLR(locswptr); + if(m) { + SLIDING_WINDOW_M_SET(locswptr); + } + SLIDING_WINDOW_IS_USED_SET(locswptr); + seed_id_cpy(&locswptr->seed_id, seed_ptr); + PRINTF("ROLL TM: Window for seed "); + PRINT_SEED(&locswptr->seed_id); + PRINTF(" M=%u, count=%u\n", + SLIDING_WINDOW_GET_M(locswptr), locswptr->count); + + /* If this window was previously empty, set its lower bound to this packet */ + if(locswptr->count == 0) { + locswptr->lower_bound = seq_val; + VERBOSE_PRINTF("ROLL TM: New Lower Bound %u\n", locswptr->lower_bound); + } + + /* If this is a new Seq Num, update the window upper bound */ + if(locswptr->count == 0 || SEQ_VAL_IS_GT(seq_val, locswptr->upper_bound)) { + locswptr->upper_bound = seq_val; + VERBOSE_PRINTF("ROLL TM: New Upper Bound %u\n", locswptr->upper_bound); + } + + locswptr->count++; + + memset(locmpptr, 0, sizeof(struct mcast_packet)); + memcpy(&locmpptr->buff, UIP_IP_BUF, uip_len); + locmpptr->sw = locswptr; + locmpptr->buff_len = uip_len; + locmpptr->seq_val = seq_val; + MCAST_PACKET_USED_SET(locmpptr); + + PRINTF("ROLL TM: Window for seed "); + PRINT_SEED(&locswptr->seed_id); + PRINTF(" M=%u, %u values within [%u , %u]\n", + SLIDING_WINDOW_GET_M(locswptr), locswptr->count, + locswptr->lower_bound, locswptr->upper_bound); + + /* + * If this is an incoming packet, it is inconsistent and we need to decrement + * its TTL before we start forwarding it. + * If on the other hand we are the seed, the caller will trigger a + * transmission so we don't flag inconsistency and we leave the TTL alone + */ + if(in == ROLL_TM_DGRAM_IN) { + MCAST_PACKET_SEND_SET(locmpptr); + MCAST_PACKET_TTL(locmpptr)--; + + t[m].inconsistency = 1; + + PRINTF("ROLL TM: Inconsistency. Reset T%u\n", m); + reset_trickle_timer(m); + } + + /* Deliver if necessary */ + return UIP_MCAST6_ACCEPT; +} +/*---------------------------------------------------------------------------*/ +void +roll_tm_icmp_input() +{ + uint8_t inconsistency; + uint16_t *seq_ptr; + uint16_t *end_ptr; + uint16_t val; + +#if UIP_CONF_IPV6_CHECKS + if(!uip_is_addr_link_local(&UIP_IP_BUF->srcipaddr)) { + PRINTF("ROLL TM: ICMPv6 In, bad source "); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF(" to "); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("\n"); + STATS_ADD(icmp_bad); + return; + } + + if(!uip_is_addr_linklocal_allnodes_mcast(&UIP_IP_BUF->destipaddr) + && !uip_is_addr_linklocal_allrouters_mcast(&UIP_IP_BUF->destipaddr)) { + PRINTF("ROLL TM: ICMPv6 In, bad destination\n"); + STATS_ADD(icmp_bad); + return; + } + + if(UIP_ICMP_BUF->icode != ROLL_TM_ICMP_CODE) { + PRINTF("ROLL TM: ICMPv6 In, bad ICMP code\n"); + STATS_ADD(icmp_bad); + return; + } + + if(UIP_IP_BUF->ttl != ROLL_TM_IP_HOP_LIMIT) { + PRINTF("ROLL TM: ICMPv6 In, bad TTL\n"); + STATS_ADD(icmp_bad); + return; + } +#endif + + PRINTF("ROLL TM: ICMPv6 In from "); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF(" len %u, ext %u\n", uip_len, uip_ext_len); + + STATS_ADD(icmp_in); + + /* Reset Is-Listed bit for all windows */ + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + SLIDING_WINDOW_LISTED_CLR(iterswptr); + } + + /* Reset Is-Listed bit for all cached packets */ + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + MCAST_PACKET_LISTED_CLR(locmpptr); + } + + locslhptr = (struct sequence_list_header *)UIP_ICMP_PAYLOAD; + + VERBOSE_PRINTF("ROLL TM: ICMPv6 In, parse from %p to %p\n", + UIP_ICMP_PAYLOAD, + (uint8_t *)UIP_ICMP_PAYLOAD + uip_len - + uip_l2_l3_icmp_hdr_len); + while(locslhptr < + (struct sequence_list_header *)((uint8_t *)UIP_ICMP_PAYLOAD + + uip_len - uip_l2_l3_icmp_hdr_len)) { + VERBOSE_PRINTF("ROLL TM: ICMPv6 In, seq hdr @ %p\n", locslhptr); + + if((locslhptr->flags & SEQUENCE_LIST_RES) != 0) { + PRINTF("ROLL TM: ICMPv6 In, non-zero reserved bits\n"); + goto drop; + } + + /* Drop unsupported Seed ID Lengths. S bit: 0->short, 1->long */ +#if ROLL_TM_SHORT_SEEDS + if(!SEQUENCE_LIST_GET_S(locslhptr)) { + STATS_ADD(icmp_bad); + goto drop; + } +#else + if(SEQUENCE_LIST_GET_S(locslhptr)) { + STATS_ADD(icmp_bad); + goto drop; + } +#endif + + PRINTF("ROLL TM: ICMPv6 In, Sequence List for Seed ID "); + PRINT_SEED(&locslhptr->seed_id); + PRINTF(" M=%u, S=%u, Len=%u\n", SEQUENCE_LIST_GET_M(locslhptr), + SEQUENCE_LIST_GET_S(locslhptr), locslhptr->seq_len); + + seq_ptr = (uint16_t *)((uint8_t *)locslhptr + + sizeof(struct sequence_list_header)); + end_ptr = (uint16_t *)((uint8_t *)locslhptr + + sizeof(struct sequence_list_header) + + locslhptr->seq_len * 2); + + /* Fetch a pointer to the corresponding trickle timer */ + loctpptr = &t[SEQUENCE_LIST_GET_M(locslhptr)]; + + locswptr = NULL; + + /* Find the sliding window for this Seed ID */ + locswptr = window_lookup(&locslhptr->seed_id, + SEQUENCE_LIST_GET_M(locslhptr)); + + /* If we have a window, iterate sequence values and check consistency */ + if(locswptr) { + SLIDING_WINDOW_LISTED_SET(locswptr); + locswptr->min_listed = -1; + PRINTF("ROLL TM: ICMPv6 In, Window bounds [%u , %u]\n", + locswptr->lower_bound, locswptr->upper_bound); + for(; seq_ptr < end_ptr; seq_ptr++) { + /* Check for "They have new" */ + /* If an advertised seq. val is GT our upper bound */ + val = uip_htons(*seq_ptr); + PRINTF("ROLL TM: ICMPv6 In, Check seq %u @ %p\n", val, seq_ptr); + if(SEQ_VAL_IS_GT(val, locswptr->upper_bound)) { + PRINTF("ROLL TM: Inconsistency - Advertised Seq. ID %u GT upper" + " bound %u\n", val, locswptr->upper_bound); + loctpptr->inconsistency = 1; + } + + /* If an advertised seq. val is within our bounds */ + if((SEQ_VAL_IS_LT(val, locswptr->upper_bound) || + SEQ_VAL_IS_EQ(val, locswptr->upper_bound)) && + (SEQ_VAL_IS_GT(val, locswptr->lower_bound) || + SEQ_VAL_IS_EQ(val, locswptr->lower_bound))) { + + inconsistency = 1; + /* Check if the advertised sequence is in our buffer */ + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr) && locmpptr->sw == locswptr) { + if(SEQ_VAL_IS_EQ(locmpptr->seq_val, val)) { + + inconsistency = 0; + MCAST_PACKET_LISTED_SET(locmpptr); + PRINTF("ROLL TM: ICMPv6 In, %u listed\n", locmpptr->seq_val); + + /* Update lowest seq. num listed for this window + * We need this to check for "we have new" */ + if(locswptr->min_listed == -1 || + SEQ_VAL_IS_LT(val, locswptr->min_listed)) { + locswptr->min_listed = val; + } + break; + } + } + } + if(inconsistency) { + PRINTF("ROLL TM: Inconsistency - "); + PRINTF("Advertised Seq. ID %u within bounds", val); + PRINTF(" [%u, %u] but no matching entry\n", + locswptr->lower_bound, locswptr->upper_bound); + loctpptr->inconsistency = 1; + } + } + } + } else { + /* A new sliding window in an ICMP message is not explicitly stated + * in the draft as inconsistency. Until this is clarified, we consider + * this to be a point where we diverge from the draft for performance + * improvement reasons (or as some would say, 'this is an extension') */ + PRINTF("ROLL TM: Inconsistency - Advertised window unknown to us\n"); + loctpptr->inconsistency = 1; + } + locslhptr = (struct sequence_list_header *)(((uint8_t *)locslhptr) + + sizeof(struct sequence_list_header) + (2 * locslhptr->seq_len)); + } + /* Done parsing the message */ + + /* Check for "We have new */ + PRINTF("ROLL TM: ICMPv6 In, Check our buffer\n"); + for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; + locmpptr >= buffered_msgs; locmpptr--) { + if(MCAST_PACKET_IS_USED(locmpptr)) { + locswptr = locmpptr->sw; + PRINTF("ROLL TM: ICMPv6 In, "); + PRINTF("Check %u, Seed L: %u, This L: %u Min L: %d\n", + locmpptr->seq_val, SLIDING_WINDOW_IS_LISTED(locswptr), + MCAST_PACKET_IS_LISTED(locmpptr), locswptr->min_listed); + if(!SLIDING_WINDOW_IS_LISTED(locswptr)) { + /* If a buffered packet's Seed ID was not listed */ + PRINTF("ROLL TM: Inconsistency - Seed ID "); + PRINT_SEED(&locswptr->seed_id); + PRINTF(" was not listed\n"); + loctpptr->inconsistency = 1; + MCAST_PACKET_SEND_SET(locmpptr); + } else { + /* This packet was not listed but a prior one was */ + if(!MCAST_PACKET_IS_LISTED(locmpptr) && + (locswptr->min_listed >= 0) && + SEQ_VAL_IS_GT(locmpptr->seq_val, locswptr->min_listed)) { + PRINTF("ROLL TM: Inconsistency - "); + PRINTF("Seq. %u was not listed but %u was\n", + locmpptr->seq_val, locswptr->min_listed); + loctpptr->inconsistency = 1; + MCAST_PACKET_SEND_SET(locmpptr); + } + } + } + } + +drop: + + if(t[0].inconsistency) { + reset_trickle_timer(0); + } else { + t[0].c++; + } + if(t[1].inconsistency) { + reset_trickle_timer(1); + } else { + t[1].c++; + } + + return; +} +/*---------------------------------------------------------------------------*/ +static void +out() +{ + + if(uip_len + HBHO_TOTAL_LEN > UIP_BUFSIZE) { + PRINTF("ROLL TM: Multicast Out can not add HBHO. Packet too long\n"); + goto drop; + } + + /* Slide 'right' by HBHO_TOTAL_LEN bytes */ + memmove(UIP_EXT_BUF_NEXT, UIP_EXT_BUF, uip_len - UIP_IPH_LEN); + memset(UIP_EXT_BUF, 0, HBHO_TOTAL_LEN); + + UIP_EXT_BUF->next = UIP_IP_BUF->proto; + UIP_EXT_BUF->len = 0; + + lochbhmptr = UIP_EXT_OPT_FIRST; + lochbhmptr->type = HBHO_OPT_TYPE_TRICKLE; + + /* Set the sequence ID */ + last_seq = SEQ_VAL_ADD(last_seq, 1); + lochbhmptr->flags = last_seq >> 8; + lochbhmptr->seq_id_lsb = last_seq & 0xFF; +#if ROLL_TM_SHORT_SEEDS + seed_id_cpy(&lochbhmptr->seed_id, &uip_lladdr.addr[UIP_LLADDR_LEN - 2]); + lochbhmptr->len = HBHO_LEN_SHORT_SEED; +#else + lochbhmptr->len = HBHO_LEN_LONG_SEED; + /* PadN */ + lochbhmptr->padn_type = UIP_EXT_HDR_OPT_PADN; + lochbhmptr->padn_len = 0; +#endif + + /* Set the M bit for our outgoing messages, if necessary */ +#if ROLL_TM_SET_M_BIT + HBH_SET_M(lochbhmptr); +#endif + + uip_ext_len += HBHO_TOTAL_LEN; + uip_len += HBHO_TOTAL_LEN; + + /* Update the proto and length field in the v6 header */ + UIP_IP_BUF->proto = UIP_PROTO_HBHO; + UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); + + PRINTF("ROLL TM: Multicast Out, HBHO: T=%u, L=%u, M=%u, S=0x%02x%02x\n", + lochbhmptr->type, lochbhmptr->len, HBH_GET_M(lochbhmptr), + HBH_GET_SV_MSB(lochbhmptr), lochbhmptr->seq_id_lsb); + + /* + * We need to remember this message and advertise it in subsequent ICMP + * messages. Otherwise, our neighs will think we are inconsistent and will + * bounce it back to us. + * + * Queue this message but don't set its MUST_SEND flag. We reset the trickle + * timer and we send it immediately. We then set uip_len = 0 to stop the core + * from re-sending it. + */ + if(accept(ROLL_TM_DGRAM_OUT)) { + tcpip_output(NULL); + STATS_ADD(mcast_out); + } + +drop: + uip_slen = 0; + uip_len = 0; + uip_ext_len = 0; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +in() +{ + /* + * We call accept() which will sort out caching and forwarding. Depending + * on accept()'s return value, we then need to signal the core + * whether to deliver this to higher layers + */ + if(accept(ROLL_TM_DGRAM_IN) == UIP_MCAST6_DROP) { + return UIP_MCAST6_DROP; + } + + if(!uip_ds6_is_my_maddr(&UIP_IP_BUF->destipaddr)) { + PRINTF("ROLL TM: Not a group member. No further processing\n"); + return UIP_MCAST6_DROP; + } else { + PRINTF("ROLL TM: Ours. Deliver to upper layers\n"); + STATS_ADD(mcast_in_ours); + return UIP_MCAST6_ACCEPT; + } +} +/*---------------------------------------------------------------------------*/ +static void +init() +{ + PRINTF("ROLL TM: ROLL Multicast - Draft #%u\n", ROLL_TM_VER); + + memset(windows, 0, sizeof(windows)); + memset(buffered_msgs, 0, sizeof(buffered_msgs)); + memset(t, 0, sizeof(t)); + STATS_RESET(); + + for(iterswptr = &windows[ROLL_TM_WINS - 1]; iterswptr >= windows; + iterswptr--) { + iterswptr->lower_bound = -1; + iterswptr->upper_bound = -1; + iterswptr->min_listed = -1; + } + + TIMER_CONFIGURE(0); + reset_trickle_timer(0); + TIMER_CONFIGURE(1); + reset_trickle_timer(1); + return; +} +/*---------------------------------------------------------------------------*/ +const struct uip_mcast6_driver roll_tm_driver = { + "ROLL TM", + init, + out, + in, +}; +/*---------------------------------------------------------------------------*/ + +#endif /* UIP_CONF_IPV6 */ diff --git a/core/net/ipv6/multicast/roll-tm.h b/core/net/ipv6/multicast/roll-tm.h new file mode 100644 index 000000000..0eabe028c --- /dev/null +++ b/core/net/ipv6/multicast/roll-tm.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2011, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for IPv6 multicast according to the algorithm in the + * "MCAST Forwarding Using Trickle" internet draft. + * + * The current version of the draft can always be found in + * http://tools.ietf.org/html/draft-ietf-roll-trickle-mcast + * + * This implementation is based on the draft version stored in + * ROLL_TM_VER. + * + * In draft v2, the document was renamed to + * "Multicast Protocol for Low power and Lossy Networks (MPL)" + * Due to very significant changes between draft versions 1 and 2, + * MPL will be implemented as a separate engine and this file here + * will provide legacy support for Draft v1. + * + * \author + * George Oikonomou - + */ + +#ifndef ROLL_TM_H_ +#define ROLL_TM_H_ + +#include "contiki-conf.h" + +#include +/*---------------------------------------------------------------------------*/ +/* Protocol Constants */ +/*---------------------------------------------------------------------------*/ +#define ROLL_TM_VER 1 /* Supported Draft Version */ +#define ROLL_TM_ICMP_CODE 0 /* ICMPv6 code field */ +#define ROLL_TM_IP_HOP_LIMIT 0xFF /* Hop limit for ICMP messages */ +#define ROLL_TM_INFINITE_REDUNDANCY 0xFF +#define ROLL_TM_DGRAM_OUT 0 +#define ROLL_TM_DGRAM_IN 1 + +/* + * The draft does not currently specify a default number for the trickle + * interval nor a way to derive it. Examples however hint at 100 msec. + * + * In draft terminology, we use an 'aggressive' policy (M=0) and a conservative + * one (M=1). + * + * When experimenting with the two policies on the sky platform, + * an interval of 125ms proves to be way too low: When we have traffic, + * doublings happen after the interval end and periodics fire after point T + * within the interval (and sometimes even after interval end). When traffic + * settles down, the code compensates the offsets. + * + * We consider 125, 250ms etc because they are nice divisors of 1 sec + * (quotient is power of two). For some machines (e.g sky/msp430, + * sensinode/cc243x), this is also a nice number of clock ticks + * + * After experimentation, the values of Imin leading to best performance are: + * ContikiMAC: Imin=64 (500ms) + * Null RDC: imin=16 (125ms) + */ + +/* Configuration for Timer with M=0 (aggressive) */ +#ifdef ROLL_TM_CONF_IMIN_0 +#define ROLL_TM_IMIN_0 ROLL_TM_CONF_IMIN_0 +#else +#define ROLL_TM_IMIN_0 32 /* 250 msec */ +#endif + +#ifdef ROLL_TM_CONF_IMAX_0 +#define ROLL_TM_IMAX_0 ROLL_TM_CONF_IMAX_0 +#else +#define ROLL_TM_IMAX_0 1 /* Imax = 500ms */ +#endif + +#ifdef ROLL_TM_CONF_K_0 +#define ROLL_TM_K_0 ROLL_TM_CONF_K_0 +#else +#define ROLL_TM_K_0 ROLL_TM_INFINITE_REDUNDANCY +#endif + +#ifdef ROLL_TM_CONF_T_ACTIVE_0 +#define ROLL_TM_T_ACTIVE_0 ROLL_TM_CONF_T_ACTIVE_0 +#else +#define ROLL_TM_T_ACTIVE_0 3 +#endif + +#ifdef ROLL_TM_CONF_T_DWELL_0 +#define ROLL_TM_T_DWELL_0 ROLL_TM_CONF_T_DWELL_0 +#else +#define ROLL_TM_T_DWELL_0 11 +#endif + +/* Configuration for Timer with M=1 (conservative) */ +#ifdef ROLL_TM_CONF_IMIN_1 +#define ROLL_TM_IMIN_1 ROLL_TM_CONF_IMIN_1 +#else +#define ROLL_TM_IMIN_1 64 /* 500 msec */ +#endif + +#ifdef ROLL_TM_CONF_IMAX_1 +#define ROLL_TM_IMAX_1 ROLL_TM_CONF_IMAX_1 +#else +#define ROLL_TM_IMAX_1 9 /* Imax = 256 secs */ +#endif + +#ifdef ROLL_TM_CONF_K_1 +#define ROLL_TM_K_1 ROLL_TM_CONF_K_1 +#else +#define ROLL_TM_K_1 1 +#endif + +#ifdef ROLL_TM_CONF_T_ACTIVE_1 +#define ROLL_TM_T_ACTIVE_1 ROLL_TM_CONF_T_ACTIVE_1 +#else +#define ROLL_TM_T_ACTIVE_1 3 +#endif + +#ifdef ROLL_TM_CONF_T_DWELL_1 +#define ROLL_TM_T_DWELL_1 ROLL_TM_CONF_T_DWELL_1 +#else +#define ROLL_TM_T_DWELL_1 12 +#endif +/*---------------------------------------------------------------------------*/ +/* Configuration */ +/*---------------------------------------------------------------------------*/ +/* + * Number of Sliding Windows + * In essence: How many unique sources of simultaneous multicast traffic do we + * want to support for our lowpan + * If a node is seeding two multicast streams, parametrized on different M + * values, then this seed will occupy two different sliding windows + */ +#ifdef ROLL_TM_CONF_WINS +#define ROLL_TM_WINS ROLL_TM_CONF_WINS +#else +#define ROLL_TM_WINS 2 +#endif +/*---------------------------------------------------------------------------*/ +/* + * Maximum Number of Buffered Multicast Messages + * This buffer is shared across all Seed IDs, therefore a new very active Seed + * may eventually occupy all slots. It would make little sense (if any) to + * define support for fewer buffered messages than seeds*2 + */ +#ifdef ROLL_TM_CONF_BUFF_NUM +#define ROLL_TM_BUFF_NUM ROLL_TM_CONF_BUFF_NUM +#else +#define ROLL_TM_BUFF_NUM 6 +#endif +/*---------------------------------------------------------------------------*/ +/* + * Use Short Seed IDs [short: 2, long: 16 (default)] + * It can be argued that we should (and it would be easy to) support both at + * the same time but the draft doesn't list this as a MUST so we opt for + * code/ram savings + */ +#ifdef ROLL_TM_CONF_SHORT_SEEDS +#define ROLL_TM_SHORT_SEEDS ROLL_TM_CONF_SHORT_SEEDS +#else +#define ROLL_TM_SHORT_SEEDS 0 +#endif +/*---------------------------------------------------------------------------*/ +/* + * Destination address for our ICMPv6 advertisements. The draft gives us a + * choice between LL all-nodes or LL all-routers + * + * We use allrouters unless a conf directive chooses otherwise + */ +#ifdef ROLL_TM_CONF_DEST_ALL_NODES +#define ROLL_TM_DEST_ALL_NODES ROLL_TM_CONF_DEST_ALL_NODES +#else +#define ROLL_TM_DEST_ALL_NODES 0 +#endif +/*---------------------------------------------------------------------------*/ +/* + * M param for our outgoing messages + * By default, we set the M bit (conservative). Define this as 0 to clear the + * M bit in our outgoing messages (aggressive) + */ +#ifdef ROLL_TM_CONF_SET_M_BIT +#define ROLL_TM_SET_M_BIT ROLL_TM_CONF_SET_M_BIT +#else +#define ROLL_TM_SET_M_BIT 1 +#endif +/*---------------------------------------------------------------------------*/ +/* Prototypes of additional Trickle Multicast functions */ +/*---------------------------------------------------------------------------*/ +/** + * \brief Called by the uIPv6 engine when it receives a Trickle Multicast + * ICMPv6 datagram + */ +void roll_tm_icmp_input(); +/*---------------------------------------------------------------------------*/ +/* Stats datatype */ +/*---------------------------------------------------------------------------*/ +struct roll_tm_stats { + uint16_t mcast_in_unique; + uint16_t mcast_in_all; /* At layer 3 */ + uint16_t mcast_in_ours; /* Unique and we are a group member */ + uint16_t mcast_fwd; /* Forwarded by us but we are not the seed */ + uint16_t mcast_out; /* We are the seed */ + uint16_t mcast_bad; + uint16_t mcast_dropped; + uint16_t icmp_in; + uint16_t icmp_out; + uint16_t icmp_bad; +}; + +#endif /* ROLL_TM_H_ */