1453 lines
47 KiB
C
1453 lines
47 KiB
C
/*
|
|
* 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 - <oikonomou@users.sourceforge.net>
|
|
*/
|
|
|
|
#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 <string.h>
|
|
|
|
#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_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);
|
|
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");
|
|
ROLL_TM_STATS_ADD(icmp_bad);
|
|
return;
|
|
}
|
|
|
|
if(UIP_ICMP_BUF->icode != ROLL_TM_ICMP_CODE) {
|
|
PRINTF("ROLL TM: ICMPv6 In, bad ICMP code\n");
|
|
ROLL_TM_STATS_ADD(icmp_bad);
|
|
return;
|
|
}
|
|
|
|
if(UIP_IP_BUF->ttl != ROLL_TM_IP_HOP_LIMIT) {
|
|
PRINTF("ROLL TM: ICMPv6 In, bad TTL\n");
|
|
ROLL_TM_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);
|
|
|
|
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++;
|
|
}
|
|
|
|
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_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");
|
|
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;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
const struct uip_mcast6_driver roll_tm_driver = {
|
|
"ROLL TM",
|
|
init,
|
|
out,
|
|
in,
|
|
};
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#endif /* UIP_CONF_IPV6 */
|