/* * 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. */ /** * \addtogroup roll-tm * @{ */ /** * \file * Implementation of the ROLL TM multicast engine * \author * George Oikonomou - */ #include "contiki.h" #include "contiki-lib.h" #include "contiki-net.h" #include "net/ipv6/uip-icmp6.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 /*---------------------------------------------------------------------------*/ /* 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_STATS static struct roll_tm_stats stats; #define ROLL_TM_STATS_ADD(x) stats.x++ #define ROLL_TM_STATS_INIT() do { memset(&stats, 0, sizeof(stats)); } while(0) #else /* UIP_MCAST6_STATS */ #define ROLL_TM_STATS_ADD(x) #define ROLL_TM_STATS_INIT() #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_input(void); static void icmp_output(void); static void window_update_bounds(void); static void reset_trickle_timer(uint8_t); static void handle_timer(void *); /*---------------------------------------------------------------------------*/ /* ROLL TM ICMPv6 handler declaration */ UIP_ICMP6_HANDLER(roll_tm_icmp_handler, ICMP6_ROLL_TM, UIP_ICMP6_HANDLER_CODE_ANY, icmp_input); /*---------------------------------------------------------------------------*/ /* 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(locmpptr->active < TRICKLE_ACTIVE(param) && ((SUPPRESSION_ENABLED(param) && MCAST_PACKET_MUST_SEND(locmpptr)) || SUPPRESSION_DISABLED(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); UIP_MCAST6_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; } seed_id_cpy(&sl->seed_id, &iterswptr->seed_id); PRINTF("ROLL TM: ICMPv6 Out - Seq. F=0x%02x, Seed ID=", sl->flags); PRINT_SEED(&sl->seed_id); buffer = (uint8_t *)sl + sizeof(struct sequence_list_header); for(locmpptr = &buffered_msgs[ROLL_TM_BUFF_NUM - 1]; locmpptr >= buffered_msgs; locmpptr--) { if(MCAST_PACKET_IS_USED(locmpptr) && locmpptr->active < TRICKLE_ACTIVE((&t[SLIDING_WINDOW_GET_M(iterswptr)]))) { if(locmpptr->sw == iterswptr) { sl->seq_len++; PRINTF(", %u", locmpptr->seq_val); *buffer = (uint8_t)(locmpptr->seq_val >> 8); buffer++; *buffer = (uint8_t)(locmpptr->seq_val & 0xFF); buffer++; } } } PRINTF(", Len=%u\n", sl->seq_len); /* Scrap the entire window if it has no content */ if(sl->seq_len > 0) { payload_len += sizeof(struct sequence_list_header) + 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(); ROLL_TM_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"); UIP_MCAST6_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"); UIP_MCAST6_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"); UIP_MCAST6_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"); UIP_MCAST6_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"); UIP_MCAST6_STATS_ADD(mcast_bad); return UIP_MCAST6_DROP; } #endif #if UIP_MCAST6_STATS if(in == ROLL_TM_DGRAM_IN) { UIP_MCAST6_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"); UIP_MCAST6_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"); UIP_MCAST6_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"); UIP_MCAST6_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); UIP_MCAST6_STATS_ADD(mcast_dropped); return UIP_MCAST6_DROP; } } #if UIP_MCAST6_STATS if(in == ROLL_TM_DGRAM_IN) { UIP_MCAST6_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; } /*---------------------------------------------------------------------------*/ /* ROLL TM ICMPv6 Input Handler */ static void 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"); ROLL_TM_STATS_ADD(icmp_bad); goto discard; } 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"); ROLL_TM_STATS_ADD(icmp_bad); goto discard; } if(UIP_ICMP_BUF->icode != ROLL_TM_ICMP_CODE) { PRINTF("ROLL TM: ICMPv6 In, bad ICMP code\n"); ROLL_TM_STATS_ADD(icmp_bad); goto discard; } if(UIP_IP_BUF->ttl != ROLL_TM_IP_HOP_LIMIT) { PRINTF("ROLL TM: ICMPv6 In, bad TTL\n"); ROLL_TM_STATS_ADD(icmp_bad); goto discard; } #endif PRINTF("ROLL TM: ICMPv6 In from "); PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINTF(" len %u, ext %u\n", uip_len, uip_ext_len); ROLL_TM_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)) { ROLL_TM_STATS_ADD(icmp_bad); goto drop; } #else if(SEQUENCE_LIST_GET_S(locslhptr)) { ROLL_TM_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); /* Point to the sliding window's trickle param */ loctpptr = &t[SLIDING_WINDOW_GET_M(locswptr)]; 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++; } discard: uip_len = 0; 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); UIP_MCAST6_STATS_ADD(mcast_out); } drop: uip_slen = 0; uip_clear_buf(); } /*---------------------------------------------------------------------------*/ 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"); UIP_MCAST6_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)); ROLL_TM_STATS_INIT(); UIP_MCAST6_STATS_INIT(&stats); /* Register the ICMPv6 input handler */ uip_icmp6_register_input_handler(&roll_tm_icmp_handler); 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; } /*---------------------------------------------------------------------------*/ /** * \brief The ROLL TM engine driver */ const struct uip_mcast6_driver roll_tm_driver = { "ROLL TM", init, out, in, }; /*---------------------------------------------------------------------------*/ /** @} */