CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2

Dependents:   USBEthernet_TEST

Fork of USB_Ethernet by Daniele Lacamera

Committer:
daniele
Date:
Sat Aug 03 13:16:14 2013 +0000
Revision:
2:540f6e142d59
Moved to single package

Who changed what in which revision?

UserRevisionLine numberNew contents of line
daniele 2:540f6e142d59 1 /*********************************************************************
daniele 2:540f6e142d59 2 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
daniele 2:540f6e142d59 3 See LICENSE and COPYING for usage.
daniele 2:540f6e142d59 4
daniele 2:540f6e142d59 5 RFC 1112, 2236, 3376, 3569, 3678, 4607
daniele 2:540f6e142d59 6
daniele 2:540f6e142d59 7 Authors: Kristof Roelants (IGMPv3), Simon Maes, Brecht Van Cauwenberghe
daniele 2:540f6e142d59 8 *********************************************************************/
daniele 2:540f6e142d59 9
daniele 2:540f6e142d59 10 #include "pico_stack.h"
daniele 2:540f6e142d59 11 #include "pico_ipv4.h"
daniele 2:540f6e142d59 12 #include "pico_igmp.h"
daniele 2:540f6e142d59 13 #include "pico_config.h"
daniele 2:540f6e142d59 14 #include "pico_eth.h"
daniele 2:540f6e142d59 15 #include "pico_addressing.h"
daniele 2:540f6e142d59 16 #include "pico_frame.h"
daniele 2:540f6e142d59 17 #include "pico_tree.h"
daniele 2:540f6e142d59 18 #include "pico_device.h"
daniele 2:540f6e142d59 19 #include "pico_socket.h"
daniele 2:540f6e142d59 20
daniele 2:540f6e142d59 21 #define igmp_dbg(...) do{}while(0)
daniele 2:540f6e142d59 22 //#define igmp_dbg dbg
daniele 2:540f6e142d59 23
daniele 2:540f6e142d59 24 /* membership states */
daniele 2:540f6e142d59 25 #define IGMP_STATE_NON_MEMBER (0x0)
daniele 2:540f6e142d59 26 #define IGMP_STATE_DELAYING_MEMBER (0x1)
daniele 2:540f6e142d59 27 #define IGMP_STATE_IDLE_MEMBER (0x2)
daniele 2:540f6e142d59 28
daniele 2:540f6e142d59 29 /* events */
daniele 2:540f6e142d59 30 #define IGMP_EVENT_DELETE_GROUP (0x0)
daniele 2:540f6e142d59 31 #define IGMP_EVENT_CREATE_GROUP (0x1)
daniele 2:540f6e142d59 32 #define IGMP_EVENT_UPDATE_GROUP (0x2)
daniele 2:540f6e142d59 33 #define IGMP_EVENT_QUERY_RECV (0x3)
daniele 2:540f6e142d59 34 #define IGMP_EVENT_REPORT_RECV (0x4)
daniele 2:540f6e142d59 35 #define IGMP_EVENT_TIMER_EXPIRED (0x5)
daniele 2:540f6e142d59 36
daniele 2:540f6e142d59 37 /* message types */
daniele 2:540f6e142d59 38 #define IGMP_TYPE_MEM_QUERY (0x11)
daniele 2:540f6e142d59 39 #define IGMP_TYPE_MEM_REPORT_V1 (0x12)
daniele 2:540f6e142d59 40 #define IGMP_TYPE_MEM_REPORT_V2 (0x16)
daniele 2:540f6e142d59 41 #define IGMP_TYPE_LEAVE_GROUP (0x17)
daniele 2:540f6e142d59 42 #define IGMP_TYPE_MEM_REPORT_V3 (0x22)
daniele 2:540f6e142d59 43
daniele 2:540f6e142d59 44 /* group record types */
daniele 2:540f6e142d59 45 #define IGMP_MODE_IS_INCLUDE (1)
daniele 2:540f6e142d59 46 #define IGMP_MODE_IS_EXCLUDE (2)
daniele 2:540f6e142d59 47 #define IGMP_CHANGE_TO_INCLUDE_MODE (3)
daniele 2:540f6e142d59 48 #define IGMP_CHANGE_TO_EXCLUDE_MODE (4)
daniele 2:540f6e142d59 49 #define IGMP_ALLOW_NEW_SOURCES (5)
daniele 2:540f6e142d59 50 #define IGMP_BLOCK_OLD_SOURCES (6)
daniele 2:540f6e142d59 51
daniele 2:540f6e142d59 52 /* host flag */
daniele 2:540f6e142d59 53 #define IGMP_HOST_LAST (0x1)
daniele 2:540f6e142d59 54 #define IGMP_HOST_NOT_LAST (0x0)
daniele 2:540f6e142d59 55
daniele 2:540f6e142d59 56 /* list of timers, counters and their default values */
daniele 2:540f6e142d59 57 #define IGMP_ROBUSTNESS (2)
daniele 2:540f6e142d59 58 #define IGMP_QUERY_INTERVAL (125) /* secs */
daniele 2:540f6e142d59 59 #define IGMP_QUERY_RESPONSE_INTERVAL (10) /* secs */
daniele 2:540f6e142d59 60 #define IGMP_STARTUP_QUERY_INTERVAL (IGMPV3_QUERY_INTERVAL / 4)
daniele 2:540f6e142d59 61 #define IGMP_STARTUP_QUERY_COUNT (IGMPV3_ROBUSTNESS)
daniele 2:540f6e142d59 62 #define IGMP_LAST_MEMBER_QUERY_INTERVAL (1) /* secs */
daniele 2:540f6e142d59 63 #define IGMP_LAST_MEMBER_QUERY_COUNT (IGMPV3_ROBUSTNESS)
daniele 2:540f6e142d59 64 #define IGMP_UNSOLICITED_REPORT_INTERVAL (1) /* secs */
daniele 2:540f6e142d59 65 #define IGMP_DEFAULT_MAX_RESPONSE_TIME (100)
daniele 2:540f6e142d59 66
daniele 2:540f6e142d59 67 /* custom timers types */
daniele 2:540f6e142d59 68 #define IGMP_TIMER_GROUP_REPORT (1)
daniele 2:540f6e142d59 69 #define IGMP_TIMER_V1_QUERIER (2)
daniele 2:540f6e142d59 70 #define IGMP_TIMER_V2_QUERIER (3)
daniele 2:540f6e142d59 71
daniele 2:540f6e142d59 72 /* IGMP groups */
daniele 2:540f6e142d59 73 #define IGMP_ALL_HOST_GROUP long_be(0xE0000001) /* 224.0.0.1 */
daniele 2:540f6e142d59 74 #define IGMP_ALL_ROUTER_GROUP long_be(0xE0000002) /* 224.0.0.2 */
daniele 2:540f6e142d59 75 #define IGMPV3_ALL_ROUTER_GROUP long_be(0xE0000016) /* 224.0.0.22 */
daniele 2:540f6e142d59 76
daniele 2:540f6e142d59 77 /* misc */
daniele 2:540f6e142d59 78 #define IGMP_TIMER_STOPPED (1)
daniele 2:540f6e142d59 79 #define IP_OPTION_ROUTER_ALERT_LEN (4)
daniele 2:540f6e142d59 80 #define IGMP_MAX_GROUPS (32) /* max 255 */
daniele 2:540f6e142d59 81
daniele 2:540f6e142d59 82 struct __attribute__((packed)) igmp_message {
daniele 2:540f6e142d59 83 uint8_t type;
daniele 2:540f6e142d59 84 uint8_t max_resp_time;
daniele 2:540f6e142d59 85 uint16_t crc;
daniele 2:540f6e142d59 86 uint32_t mcast_group;
daniele 2:540f6e142d59 87 };
daniele 2:540f6e142d59 88
daniele 2:540f6e142d59 89 struct __attribute__((packed)) igmpv3_query {
daniele 2:540f6e142d59 90 uint8_t type;
daniele 2:540f6e142d59 91 uint8_t max_resp_time;
daniele 2:540f6e142d59 92 uint16_t crc;
daniele 2:540f6e142d59 93 uint32_t mcast_group;
daniele 2:540f6e142d59 94 uint8_t rsq;
daniele 2:540f6e142d59 95 uint8_t qqic;
daniele 2:540f6e142d59 96 uint16_t sources;
daniele 2:540f6e142d59 97 uint32_t source_addr[0];
daniele 2:540f6e142d59 98 };
daniele 2:540f6e142d59 99
daniele 2:540f6e142d59 100 struct __attribute__((packed)) igmpv3_group_record {
daniele 2:540f6e142d59 101 uint8_t type;
daniele 2:540f6e142d59 102 uint8_t aux;
daniele 2:540f6e142d59 103 uint16_t sources;
daniele 2:540f6e142d59 104 uint32_t mcast_group;
daniele 2:540f6e142d59 105 uint32_t source_addr[0];
daniele 2:540f6e142d59 106 };
daniele 2:540f6e142d59 107
daniele 2:540f6e142d59 108 struct __attribute__((packed)) igmpv3_report {
daniele 2:540f6e142d59 109 uint8_t type;
daniele 2:540f6e142d59 110 uint8_t res0;
daniele 2:540f6e142d59 111 uint16_t crc;
daniele 2:540f6e142d59 112 uint16_t res1;
daniele 2:540f6e142d59 113 uint16_t groups;
daniele 2:540f6e142d59 114 struct igmpv3_group_record record[0];
daniele 2:540f6e142d59 115 };
daniele 2:540f6e142d59 116
daniele 2:540f6e142d59 117 struct igmp_parameters {
daniele 2:540f6e142d59 118 uint8_t event;
daniele 2:540f6e142d59 119 uint8_t state;
daniele 2:540f6e142d59 120 uint8_t last_host;
daniele 2:540f6e142d59 121 uint8_t filter_mode;
daniele 2:540f6e142d59 122 uint8_t max_resp_time;
daniele 2:540f6e142d59 123 struct pico_ip4 mcast_link;
daniele 2:540f6e142d59 124 struct pico_ip4 mcast_group;
daniele 2:540f6e142d59 125 struct pico_tree *MCASTFilter;
daniele 2:540f6e142d59 126 struct pico_frame *f;
daniele 2:540f6e142d59 127 };
daniele 2:540f6e142d59 128
daniele 2:540f6e142d59 129 struct igmp_timer {
daniele 2:540f6e142d59 130 uint8_t type;
daniele 2:540f6e142d59 131 uint8_t stopped;
daniele 2:540f6e142d59 132 unsigned long start;
daniele 2:540f6e142d59 133 unsigned long delay;
daniele 2:540f6e142d59 134 struct pico_ip4 mcast_link;
daniele 2:540f6e142d59 135 struct pico_ip4 mcast_group;
daniele 2:540f6e142d59 136 struct pico_frame *f;
daniele 2:540f6e142d59 137 void (*callback)(struct igmp_timer *t);
daniele 2:540f6e142d59 138 };
daniele 2:540f6e142d59 139
daniele 2:540f6e142d59 140 /* queues */
daniele 2:540f6e142d59 141 static struct pico_queue igmp_in = {};
daniele 2:540f6e142d59 142 static struct pico_queue igmp_out = {};
daniele 2:540f6e142d59 143
daniele 2:540f6e142d59 144 /* finite state machine caller */
daniele 2:540f6e142d59 145 static int pico_igmp_process_event(struct igmp_parameters *p);
daniele 2:540f6e142d59 146
daniele 2:540f6e142d59 147 /* state callback prototype */
daniele 2:540f6e142d59 148 typedef int (*callback)(struct igmp_parameters *);
daniele 2:540f6e142d59 149
daniele 2:540f6e142d59 150 /* redblack trees */
daniele 2:540f6e142d59 151 static int igmp_timer_cmp(void *ka, void *kb)
daniele 2:540f6e142d59 152 {
daniele 2:540f6e142d59 153 struct igmp_timer *a = ka, *b =kb;
daniele 2:540f6e142d59 154 if (a->type < b->type)
daniele 2:540f6e142d59 155 return -1;
daniele 2:540f6e142d59 156 if (a->type > b->type)
daniele 2:540f6e142d59 157 return 1;
daniele 2:540f6e142d59 158 if (a->mcast_group.addr < b->mcast_group.addr)
daniele 2:540f6e142d59 159 return -1;
daniele 2:540f6e142d59 160 if (a->mcast_group.addr > b->mcast_group.addr)
daniele 2:540f6e142d59 161 return 1;
daniele 2:540f6e142d59 162 if (a->mcast_link.addr < b->mcast_link.addr)
daniele 2:540f6e142d59 163 return -1;
daniele 2:540f6e142d59 164 if (a->mcast_link.addr > b->mcast_link.addr)
daniele 2:540f6e142d59 165 return 1;
daniele 2:540f6e142d59 166 return 0;
daniele 2:540f6e142d59 167 }
daniele 2:540f6e142d59 168 PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
daniele 2:540f6e142d59 169
daniele 2:540f6e142d59 170 static int igmp_parameters_cmp(void *ka, void *kb)
daniele 2:540f6e142d59 171 {
daniele 2:540f6e142d59 172 struct igmp_parameters *a = ka, *b = kb;
daniele 2:540f6e142d59 173 if (a->mcast_group.addr < b->mcast_group.addr)
daniele 2:540f6e142d59 174 return -1;
daniele 2:540f6e142d59 175 if (a->mcast_group.addr > b->mcast_group.addr)
daniele 2:540f6e142d59 176 return 1;
daniele 2:540f6e142d59 177 if (a->mcast_link.addr < b->mcast_link.addr)
daniele 2:540f6e142d59 178 return -1;
daniele 2:540f6e142d59 179 if (a->mcast_link.addr > b->mcast_link.addr)
daniele 2:540f6e142d59 180 return 1;
daniele 2:540f6e142d59 181 return 0;
daniele 2:540f6e142d59 182 }
daniele 2:540f6e142d59 183 PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
daniele 2:540f6e142d59 184
daniele 2:540f6e142d59 185 static int igmp_sources_cmp(void *ka, void *kb)
daniele 2:540f6e142d59 186 {
daniele 2:540f6e142d59 187 struct pico_ip4 *a = ka, *b = kb;
daniele 2:540f6e142d59 188 if (a->addr < b->addr)
daniele 2:540f6e142d59 189 return -1;
daniele 2:540f6e142d59 190 if (a->addr > b->addr)
daniele 2:540f6e142d59 191 return 1;
daniele 2:540f6e142d59 192 return 0;
daniele 2:540f6e142d59 193 }
daniele 2:540f6e142d59 194 PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
daniele 2:540f6e142d59 195 PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
daniele 2:540f6e142d59 196
daniele 2:540f6e142d59 197 static struct igmp_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
daniele 2:540f6e142d59 198 {
daniele 2:540f6e142d59 199 struct igmp_parameters test = {0};
daniele 2:540f6e142d59 200 test.mcast_link.addr = mcast_link->addr;
daniele 2:540f6e142d59 201 test.mcast_group.addr = mcast_group->addr;
daniele 2:540f6e142d59 202 return pico_tree_findKey(&IGMPParameters, &test);
daniele 2:540f6e142d59 203 }
daniele 2:540f6e142d59 204
daniele 2:540f6e142d59 205 static int pico_igmp_delete_parameter(struct igmp_parameters *p)
daniele 2:540f6e142d59 206 {
daniele 2:540f6e142d59 207 if (pico_tree_delete(&IGMPParameters, p))
daniele 2:540f6e142d59 208 pico_free(p);
daniele 2:540f6e142d59 209 else
daniele 2:540f6e142d59 210 return -1;
daniele 2:540f6e142d59 211
daniele 2:540f6e142d59 212 return 0;
daniele 2:540f6e142d59 213 }
daniele 2:540f6e142d59 214
daniele 2:540f6e142d59 215 static void pico_igmp_timer_expired(unsigned long now, void *arg)
daniele 2:540f6e142d59 216 {
daniele 2:540f6e142d59 217 struct igmp_timer *t = NULL, *timer = NULL, test = {0};
daniele 2:540f6e142d59 218
daniele 2:540f6e142d59 219 t = (struct igmp_timer *)arg;
daniele 2:540f6e142d59 220 test.type = t->type;
daniele 2:540f6e142d59 221 test.mcast_link = t->mcast_link;
daniele 2:540f6e142d59 222 test.mcast_group = t->mcast_group;
daniele 2:540f6e142d59 223 igmp_dbg("IGMP: timer expired for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
daniele 2:540f6e142d59 224 timer = pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 225 if (!timer) {
daniele 2:540f6e142d59 226 return;
daniele 2:540f6e142d59 227 }
daniele 2:540f6e142d59 228 if (timer->stopped == IGMP_TIMER_STOPPED) {
daniele 2:540f6e142d59 229 pico_free(t);
daniele 2:540f6e142d59 230 return;
daniele 2:540f6e142d59 231 }
daniele 2:540f6e142d59 232 if (timer->start + timer->delay < PICO_TIME_MS()) {
daniele 2:540f6e142d59 233 pico_tree_delete(&IGMPTimers, timer);
daniele 2:540f6e142d59 234 if (timer->callback)
daniele 2:540f6e142d59 235 timer->callback(timer);
daniele 2:540f6e142d59 236 pico_free(timer);
daniele 2:540f6e142d59 237 } else {
daniele 2:540f6e142d59 238 igmp_dbg("IGMP: restart timer for %08X, delay %lu, new delay %lu\n", t->mcast_group.addr, t->delay, (timer->start + timer->delay) - PICO_TIME_MS());
daniele 2:540f6e142d59 239 pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer);
daniele 2:540f6e142d59 240 }
daniele 2:540f6e142d59 241 return;
daniele 2:540f6e142d59 242 }
daniele 2:540f6e142d59 243
daniele 2:540f6e142d59 244 static int pico_igmp_timer_reset(struct igmp_timer *t)
daniele 2:540f6e142d59 245 {
daniele 2:540f6e142d59 246 struct igmp_timer *timer = NULL, test = {0};
daniele 2:540f6e142d59 247
daniele 2:540f6e142d59 248 igmp_dbg("IGMP: reset timer for %08X, delay %lu\n", t->mcast_group.addr, t->delay);
daniele 2:540f6e142d59 249 test.type = t->type;
daniele 2:540f6e142d59 250 test.mcast_link = t->mcast_link;
daniele 2:540f6e142d59 251 test.mcast_group = t->mcast_group;
daniele 2:540f6e142d59 252 timer = pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 253 if (!timer)
daniele 2:540f6e142d59 254 return -1;
daniele 2:540f6e142d59 255
daniele 2:540f6e142d59 256 *timer = *t;
daniele 2:540f6e142d59 257 timer->start = PICO_TIME_MS();
daniele 2:540f6e142d59 258 return 0;
daniele 2:540f6e142d59 259 }
daniele 2:540f6e142d59 260
daniele 2:540f6e142d59 261 static int pico_igmp_timer_start(struct igmp_timer *t)
daniele 2:540f6e142d59 262 {
daniele 2:540f6e142d59 263 struct igmp_timer *timer = NULL, test = {0};
daniele 2:540f6e142d59 264
daniele 2:540f6e142d59 265 igmp_dbg("IGMP: start timer for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
daniele 2:540f6e142d59 266 test.type = t->type;
daniele 2:540f6e142d59 267 test.mcast_link = t->mcast_link;
daniele 2:540f6e142d59 268 test.mcast_group = t->mcast_group;
daniele 2:540f6e142d59 269 timer = pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 270 if (timer)
daniele 2:540f6e142d59 271 return pico_igmp_timer_reset(t);
daniele 2:540f6e142d59 272
daniele 2:540f6e142d59 273 timer = pico_zalloc(sizeof(struct igmp_timer));
daniele 2:540f6e142d59 274 if (!timer) {
daniele 2:540f6e142d59 275 pico_err = PICO_ERR_ENOMEM;
daniele 2:540f6e142d59 276 return -1;
daniele 2:540f6e142d59 277 }
daniele 2:540f6e142d59 278 *timer = *t;
daniele 2:540f6e142d59 279 timer->start = PICO_TIME_MS();
daniele 2:540f6e142d59 280
daniele 2:540f6e142d59 281 pico_tree_insert(&IGMPTimers, timer);
daniele 2:540f6e142d59 282 pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer);
daniele 2:540f6e142d59 283 return 0;
daniele 2:540f6e142d59 284 }
daniele 2:540f6e142d59 285
daniele 2:540f6e142d59 286 static int pico_igmp_timer_stop(struct igmp_timer *t)
daniele 2:540f6e142d59 287 {
daniele 2:540f6e142d59 288 struct igmp_timer *timer = NULL, test = {0};
daniele 2:540f6e142d59 289
daniele 2:540f6e142d59 290 test.type = t->type;
daniele 2:540f6e142d59 291 test.mcast_link = t->mcast_link;
daniele 2:540f6e142d59 292 test.mcast_group = t->mcast_group;
daniele 2:540f6e142d59 293 timer = pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 294 if (!timer)
daniele 2:540f6e142d59 295 return 0;
daniele 2:540f6e142d59 296
daniele 2:540f6e142d59 297 igmp_dbg("IGMP: stop timer for %08X, delay %lu\n", timer->mcast_group.addr, timer->delay);
daniele 2:540f6e142d59 298 timer->stopped = IGMP_TIMER_STOPPED;
daniele 2:540f6e142d59 299 return 0;
daniele 2:540f6e142d59 300 }
daniele 2:540f6e142d59 301
daniele 2:540f6e142d59 302 static int pico_igmp_timer_is_running(struct igmp_timer *t)
daniele 2:540f6e142d59 303 {
daniele 2:540f6e142d59 304 struct igmp_timer *timer = NULL, test = {0};
daniele 2:540f6e142d59 305
daniele 2:540f6e142d59 306 test.type = t->type;
daniele 2:540f6e142d59 307 test.mcast_link = t->mcast_link;
daniele 2:540f6e142d59 308 test.mcast_group = t->mcast_group;
daniele 2:540f6e142d59 309 timer = pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 310 if (timer)
daniele 2:540f6e142d59 311 return 1;
daniele 2:540f6e142d59 312 return 0;
daniele 2:540f6e142d59 313 }
daniele 2:540f6e142d59 314
daniele 2:540f6e142d59 315 static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
daniele 2:540f6e142d59 316 {
daniele 2:540f6e142d59 317 struct igmp_timer test = {0};
daniele 2:540f6e142d59 318
daniele 2:540f6e142d59 319 test.type = type;
daniele 2:540f6e142d59 320 test.mcast_link = *mcast_link;
daniele 2:540f6e142d59 321 test.mcast_group = *mcast_group;
daniele 2:540f6e142d59 322 return pico_tree_findKey(&IGMPTimers, &test);
daniele 2:540f6e142d59 323 }
daniele 2:540f6e142d59 324
daniele 2:540f6e142d59 325 static void pico_igmp_report_expired(struct igmp_timer *t)
daniele 2:540f6e142d59 326 {
daniele 2:540f6e142d59 327 struct igmp_parameters *p = NULL;
daniele 2:540f6e142d59 328
daniele 2:540f6e142d59 329 p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group);
daniele 2:540f6e142d59 330 if (!p)
daniele 2:540f6e142d59 331 return;
daniele 2:540f6e142d59 332
daniele 2:540f6e142d59 333 p->event = IGMP_EVENT_TIMER_EXPIRED;
daniele 2:540f6e142d59 334 pico_igmp_process_event(p);
daniele 2:540f6e142d59 335 }
daniele 2:540f6e142d59 336
daniele 2:540f6e142d59 337 static void pico_igmp_v2querier_expired(struct igmp_timer *t)
daniele 2:540f6e142d59 338 {
daniele 2:540f6e142d59 339 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 340 struct pico_tree_node *index = NULL, *_tmp = NULL;
daniele 2:540f6e142d59 341
daniele 2:540f6e142d59 342 link = pico_ipv4_link_by_dev(t->f->dev);
daniele 2:540f6e142d59 343 if (!link)
daniele 2:540f6e142d59 344 return;
daniele 2:540f6e142d59 345
daniele 2:540f6e142d59 346 /* When changing compatibility mode, cancel all pending response
daniele 2:540f6e142d59 347 * and retransmission timers.
daniele 2:540f6e142d59 348 */
daniele 2:540f6e142d59 349 pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
daniele 2:540f6e142d59 350 {
daniele 2:540f6e142d59 351 ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
daniele 2:540f6e142d59 352 pico_tree_delete(&IGMPTimers, index->keyValue);
daniele 2:540f6e142d59 353 }
daniele 2:540f6e142d59 354 igmp_dbg("IGMP: switch to compatibility mode IGMPv3\n");
daniele 2:540f6e142d59 355 link->mcast_compatibility = PICO_IGMPV3;
daniele 2:540f6e142d59 356 return;
daniele 2:540f6e142d59 357 }
daniele 2:540f6e142d59 358
daniele 2:540f6e142d59 359 static int pico_igmp_is_checksum_valid(struct pico_frame *f)
daniele 2:540f6e142d59 360 {
daniele 2:540f6e142d59 361 struct pico_ipv4_hdr *hdr = NULL;
daniele 2:540f6e142d59 362 uint8_t ihl = 24, datalen = 0;
daniele 2:540f6e142d59 363
daniele 2:540f6e142d59 364 hdr = (struct pico_ipv4_hdr *)f->net_hdr;
daniele 2:540f6e142d59 365 ihl = (hdr->vhl & 0x0F) * 4; /* IHL is in 32bit words */
daniele 2:540f6e142d59 366 datalen = short_be(hdr->len) - ihl;
daniele 2:540f6e142d59 367
daniele 2:540f6e142d59 368 if (short_be(pico_checksum(f->transport_hdr, datalen)) == 0)
daniele 2:540f6e142d59 369 return 1;
daniele 2:540f6e142d59 370 igmp_dbg("IGMP: invalid checksum\n");
daniele 2:540f6e142d59 371 return 0;
daniele 2:540f6e142d59 372 }
daniele 2:540f6e142d59 373
daniele 2:540f6e142d59 374 /* RFC 3376 $7.1 */
daniele 2:540f6e142d59 375 static int pico_igmp_compatibility_mode(struct pico_frame *f)
daniele 2:540f6e142d59 376 {
daniele 2:540f6e142d59 377 struct pico_ipv4_hdr *hdr = NULL;
daniele 2:540f6e142d59 378 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 379 struct pico_tree_node *index = NULL, *_tmp = NULL;
daniele 2:540f6e142d59 380 struct igmp_timer t = {0};
daniele 2:540f6e142d59 381 uint8_t ihl = 24, datalen = 0;
daniele 2:540f6e142d59 382
daniele 2:540f6e142d59 383 link = pico_ipv4_link_by_dev(f->dev);
daniele 2:540f6e142d59 384 if (!link)
daniele 2:540f6e142d59 385 return -1;
daniele 2:540f6e142d59 386
daniele 2:540f6e142d59 387 hdr = (struct pico_ipv4_hdr *) f->net_hdr;
daniele 2:540f6e142d59 388 ihl = (hdr->vhl & 0x0F) * 4; /* IHL is in 32bit words */
daniele 2:540f6e142d59 389 datalen = short_be(hdr->len) - ihl;
daniele 2:540f6e142d59 390 igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen);
daniele 2:540f6e142d59 391
daniele 2:540f6e142d59 392 if (datalen > 12) {
daniele 2:540f6e142d59 393 /* IGMPv3 query */
daniele 2:540f6e142d59 394 t.type = IGMP_TIMER_V2_QUERIER;
daniele 2:540f6e142d59 395 if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */
daniele 2:540f6e142d59 396 return -1;
daniele 2:540f6e142d59 397 } else {
daniele 2:540f6e142d59 398 link->mcast_compatibility = PICO_IGMPV3;
daniele 2:540f6e142d59 399 return 0;
daniele 2:540f6e142d59 400 }
daniele 2:540f6e142d59 401 } else if (datalen == 8) {
daniele 2:540f6e142d59 402 struct igmp_message *query = (struct igmp_message *)f->transport_hdr;
daniele 2:540f6e142d59 403 if (query->max_resp_time != 0) {
daniele 2:540f6e142d59 404 /* IGMPv2 query */
daniele 2:540f6e142d59 405 /* When changing compatibility mode, cancel all pending response
daniele 2:540f6e142d59 406 * and retransmission timers.
daniele 2:540f6e142d59 407 */
daniele 2:540f6e142d59 408 pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
daniele 2:540f6e142d59 409 {
daniele 2:540f6e142d59 410 ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
daniele 2:540f6e142d59 411 pico_tree_delete(&IGMPTimers, index->keyValue);
daniele 2:540f6e142d59 412 }
daniele 2:540f6e142d59 413 igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n");
daniele 2:540f6e142d59 414 link->mcast_compatibility = PICO_IGMPV2;
daniele 2:540f6e142d59 415 t.type = IGMP_TIMER_V2_QUERIER;
daniele 2:540f6e142d59 416 t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000;
daniele 2:540f6e142d59 417 t.f = f;
daniele 2:540f6e142d59 418 t.callback = pico_igmp_v2querier_expired;
daniele 2:540f6e142d59 419 /* only one of this type of timer may exist! */
daniele 2:540f6e142d59 420 pico_igmp_timer_start(&t);
daniele 2:540f6e142d59 421 } else {
daniele 2:540f6e142d59 422 /* IGMPv1 query, not supported */
daniele 2:540f6e142d59 423 return -1;
daniele 2:540f6e142d59 424 }
daniele 2:540f6e142d59 425 } else {
daniele 2:540f6e142d59 426 /* invalid query, silently ignored */
daniele 2:540f6e142d59 427 return -1;
daniele 2:540f6e142d59 428 }
daniele 2:540f6e142d59 429 return 0;
daniele 2:540f6e142d59 430 }
daniele 2:540f6e142d59 431
daniele 2:540f6e142d59 432 static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
daniele 2:540f6e142d59 433 {
daniele 2:540f6e142d59 434 struct igmp_message *message = NULL;
daniele 2:540f6e142d59 435 struct igmp_parameters *p = NULL;
daniele 2:540f6e142d59 436 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 437 struct pico_ip4 mcast_group = {0};
daniele 2:540f6e142d59 438
daniele 2:540f6e142d59 439 link = pico_ipv4_link_by_dev(f->dev);
daniele 2:540f6e142d59 440 if (!link)
daniele 2:540f6e142d59 441 return NULL;
daniele 2:540f6e142d59 442
daniele 2:540f6e142d59 443 /* IGMPv2 and IGMPv3 have a similar structure for the first 8 bytes */
daniele 2:540f6e142d59 444 message = (struct igmp_message *)f->transport_hdr;
daniele 2:540f6e142d59 445 mcast_group.addr = message->mcast_group;
daniele 2:540f6e142d59 446 p = pico_igmp_find_parameter(&link->address, &mcast_group);
daniele 2:540f6e142d59 447 if (!p && mcast_group.addr == 0) { /* general query */
daniele 2:540f6e142d59 448 p = pico_zalloc(sizeof(struct igmp_parameters));
daniele 2:540f6e142d59 449 if (!p)
daniele 2:540f6e142d59 450 return NULL;
daniele 2:540f6e142d59 451 p->state = IGMP_STATE_NON_MEMBER;
daniele 2:540f6e142d59 452 p->mcast_link.addr = link->address.addr;
daniele 2:540f6e142d59 453 p->mcast_group.addr = mcast_group.addr;
daniele 2:540f6e142d59 454 pico_tree_insert(&IGMPParameters, p);
daniele 2:540f6e142d59 455 } else if (!p) {
daniele 2:540f6e142d59 456 return NULL;
daniele 2:540f6e142d59 457 }
daniele 2:540f6e142d59 458
daniele 2:540f6e142d59 459 switch (message->type) {
daniele 2:540f6e142d59 460 case IGMP_TYPE_MEM_QUERY:
daniele 2:540f6e142d59 461 p->event = IGMP_EVENT_QUERY_RECV;
daniele 2:540f6e142d59 462 break;
daniele 2:540f6e142d59 463 case IGMP_TYPE_MEM_REPORT_V1:
daniele 2:540f6e142d59 464 p->event = IGMP_EVENT_REPORT_RECV;
daniele 2:540f6e142d59 465 break;
daniele 2:540f6e142d59 466 case IGMP_TYPE_MEM_REPORT_V2:
daniele 2:540f6e142d59 467 p->event = IGMP_EVENT_REPORT_RECV;
daniele 2:540f6e142d59 468 break;
daniele 2:540f6e142d59 469 case IGMP_TYPE_MEM_REPORT_V3:
daniele 2:540f6e142d59 470 p->event = IGMP_EVENT_REPORT_RECV;
daniele 2:540f6e142d59 471 break;
daniele 2:540f6e142d59 472 default:
daniele 2:540f6e142d59 473 return NULL;
daniele 2:540f6e142d59 474 }
daniele 2:540f6e142d59 475 p->max_resp_time = message->max_resp_time; /* if IGMPv3 report this will be 0 (res0 field) */
daniele 2:540f6e142d59 476 p->f = f;
daniele 2:540f6e142d59 477
daniele 2:540f6e142d59 478 return p;
daniele 2:540f6e142d59 479 }
daniele 2:540f6e142d59 480
daniele 2:540f6e142d59 481 static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
daniele 2:540f6e142d59 482 {
daniele 2:540f6e142d59 483 struct igmp_parameters *p = NULL;
daniele 2:540f6e142d59 484
daniele 2:540f6e142d59 485 if (!pico_igmp_is_checksum_valid(f))
daniele 2:540f6e142d59 486 goto out;
daniele 2:540f6e142d59 487 if (pico_igmp_compatibility_mode(f) < 0)
daniele 2:540f6e142d59 488 goto out;
daniele 2:540f6e142d59 489 p = pico_igmp_analyse_packet(f);
daniele 2:540f6e142d59 490 if (!p)
daniele 2:540f6e142d59 491 goto out;
daniele 2:540f6e142d59 492
daniele 2:540f6e142d59 493 return pico_igmp_process_event(p);
daniele 2:540f6e142d59 494
daniele 2:540f6e142d59 495 out:
daniele 2:540f6e142d59 496 pico_frame_discard(f);
daniele 2:540f6e142d59 497 return 0;
daniele 2:540f6e142d59 498 }
daniele 2:540f6e142d59 499
daniele 2:540f6e142d59 500 static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) {
daniele 2:540f6e142d59 501 /* packets are directly transferred to the IP layer by calling pico_ipv4_frame_push */
daniele 2:540f6e142d59 502 return 0;
daniele 2:540f6e142d59 503 }
daniele 2:540f6e142d59 504
daniele 2:540f6e142d59 505 /* Interface: protocol definition */
daniele 2:540f6e142d59 506 struct pico_protocol pico_proto_igmp = {
daniele 2:540f6e142d59 507 .name = "igmp",
daniele 2:540f6e142d59 508 .proto_number = PICO_PROTO_IGMP,
daniele 2:540f6e142d59 509 .layer = PICO_LAYER_TRANSPORT,
daniele 2:540f6e142d59 510 .process_in = pico_igmp_process_in,
daniele 2:540f6e142d59 511 .process_out = pico_igmp_process_out,
daniele 2:540f6e142d59 512 .q_in = &igmp_in,
daniele 2:540f6e142d59 513 .q_out = &igmp_out,
daniele 2:540f6e142d59 514 };
daniele 2:540f6e142d59 515
daniele 2:540f6e142d59 516 int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *MCASTFilter, uint8_t state)
daniele 2:540f6e142d59 517 {
daniele 2:540f6e142d59 518 struct igmp_parameters *p = NULL;
daniele 2:540f6e142d59 519
daniele 2:540f6e142d59 520 if (mcast_group->addr == IGMP_ALL_HOST_GROUP)
daniele 2:540f6e142d59 521 return 0;
daniele 2:540f6e142d59 522
daniele 2:540f6e142d59 523 p = pico_igmp_find_parameter(mcast_link, mcast_group);
daniele 2:540f6e142d59 524 if (!p && state == PICO_IGMP_STATE_CREATE) {
daniele 2:540f6e142d59 525 p = pico_zalloc(sizeof(struct igmp_parameters));
daniele 2:540f6e142d59 526 if (!p) {
daniele 2:540f6e142d59 527 pico_err = PICO_ERR_ENOMEM;
daniele 2:540f6e142d59 528 return -1;
daniele 2:540f6e142d59 529 }
daniele 2:540f6e142d59 530 p->state = IGMP_STATE_NON_MEMBER;
daniele 2:540f6e142d59 531 p->mcast_link = *mcast_link;
daniele 2:540f6e142d59 532 p->mcast_group = *mcast_group;
daniele 2:540f6e142d59 533 pico_tree_insert(&IGMPParameters, p);
daniele 2:540f6e142d59 534 } else if (!p) {
daniele 2:540f6e142d59 535 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 536 return -1;
daniele 2:540f6e142d59 537 }
daniele 2:540f6e142d59 538
daniele 2:540f6e142d59 539 switch (state) {
daniele 2:540f6e142d59 540 case PICO_IGMP_STATE_CREATE:
daniele 2:540f6e142d59 541 p->event = IGMP_EVENT_CREATE_GROUP;
daniele 2:540f6e142d59 542 break;
daniele 2:540f6e142d59 543
daniele 2:540f6e142d59 544 case PICO_IGMP_STATE_UPDATE:
daniele 2:540f6e142d59 545 p->event = IGMP_EVENT_UPDATE_GROUP;
daniele 2:540f6e142d59 546 break;
daniele 2:540f6e142d59 547
daniele 2:540f6e142d59 548 case PICO_IGMP_STATE_DELETE:
daniele 2:540f6e142d59 549 p->event = IGMP_EVENT_DELETE_GROUP;
daniele 2:540f6e142d59 550 break;
daniele 2:540f6e142d59 551
daniele 2:540f6e142d59 552 default:
daniele 2:540f6e142d59 553 return -1;
daniele 2:540f6e142d59 554 }
daniele 2:540f6e142d59 555 p->filter_mode = filter_mode;
daniele 2:540f6e142d59 556 p->MCASTFilter = MCASTFilter;
daniele 2:540f6e142d59 557
daniele 2:540f6e142d59 558 return pico_igmp_process_event(p);
daniele 2:540f6e142d59 559 }
daniele 2:540f6e142d59 560
daniele 2:540f6e142d59 561 static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f)
daniele 2:540f6e142d59 562 {
daniele 2:540f6e142d59 563 struct pico_ip4 dst = {0};
daniele 2:540f6e142d59 564 struct pico_ip4 mcast_group = {0};
daniele 2:540f6e142d59 565 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 566
daniele 2:540f6e142d59 567 link = pico_ipv4_link_get(&p->mcast_link);
daniele 2:540f6e142d59 568 if (!link)
daniele 2:540f6e142d59 569 return -1;
daniele 2:540f6e142d59 570
daniele 2:540f6e142d59 571 mcast_group.addr = p->mcast_group.addr;
daniele 2:540f6e142d59 572 switch (link->mcast_compatibility) {
daniele 2:540f6e142d59 573 case PICO_IGMPV2:
daniele 2:540f6e142d59 574 if (p->event == IGMP_EVENT_DELETE_GROUP)
daniele 2:540f6e142d59 575 dst.addr = IGMP_ALL_ROUTER_GROUP;
daniele 2:540f6e142d59 576 else
daniele 2:540f6e142d59 577 dst.addr = mcast_group.addr;
daniele 2:540f6e142d59 578 break;
daniele 2:540f6e142d59 579
daniele 2:540f6e142d59 580 case PICO_IGMPV3:
daniele 2:540f6e142d59 581 dst.addr = IGMPV3_ALL_ROUTER_GROUP;
daniele 2:540f6e142d59 582 break;
daniele 2:540f6e142d59 583
daniele 2:540f6e142d59 584 default:
daniele 2:540f6e142d59 585 pico_err = PICO_ERR_EPROTONOSUPPORT;
daniele 2:540f6e142d59 586 return -1;
daniele 2:540f6e142d59 587 }
daniele 2:540f6e142d59 588
daniele 2:540f6e142d59 589 igmp_dbg("IGMP: send membership report on group %08X to %08X\n", mcast_group.addr, dst.addr);
daniele 2:540f6e142d59 590 pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP);
daniele 2:540f6e142d59 591 return 0;
daniele 2:540f6e142d59 592 }
daniele 2:540f6e142d59 593
daniele 2:540f6e142d59 594 static int pico_igmp_generate_report(struct igmp_parameters *p)
daniele 2:540f6e142d59 595 {
daniele 2:540f6e142d59 596 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 597 int i = 0;
daniele 2:540f6e142d59 598
daniele 2:540f6e142d59 599 link = pico_ipv4_link_get(&p->mcast_link);
daniele 2:540f6e142d59 600 if (!link) {
daniele 2:540f6e142d59 601 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 602 return -1;
daniele 2:540f6e142d59 603 }
daniele 2:540f6e142d59 604
daniele 2:540f6e142d59 605 switch (link->mcast_compatibility) {
daniele 2:540f6e142d59 606 case PICO_IGMPV1:
daniele 2:540f6e142d59 607 pico_err = PICO_ERR_EPROTONOSUPPORT;
daniele 2:540f6e142d59 608 return -1;
daniele 2:540f6e142d59 609
daniele 2:540f6e142d59 610 case PICO_IGMPV2:
daniele 2:540f6e142d59 611 {
daniele 2:540f6e142d59 612 struct igmp_message *report = NULL;
daniele 2:540f6e142d59 613 uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
daniele 2:540f6e142d59 614 if (p->event == IGMP_EVENT_DELETE_GROUP)
daniele 2:540f6e142d59 615 report_type = IGMP_TYPE_LEAVE_GROUP;
daniele 2:540f6e142d59 616
daniele 2:540f6e142d59 617 p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
daniele 2:540f6e142d59 618 p->f->net_len += IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 619 p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 620 p->f->transport_len -= IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 621 p->f->dev = pico_ipv4_link_find(&p->mcast_link);
daniele 2:540f6e142d59 622 /* p->f->len is correctly set by alloc */
daniele 2:540f6e142d59 623
daniele 2:540f6e142d59 624 report = (struct igmp_message *)p->f->transport_hdr;
daniele 2:540f6e142d59 625 report->type = report_type;
daniele 2:540f6e142d59 626 report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
daniele 2:540f6e142d59 627 report->mcast_group = p->mcast_group.addr;
daniele 2:540f6e142d59 628
daniele 2:540f6e142d59 629 report->crc = 0;
daniele 2:540f6e142d59 630 report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
daniele 2:540f6e142d59 631 break;
daniele 2:540f6e142d59 632 }
daniele 2:540f6e142d59 633 case PICO_IGMPV3:
daniele 2:540f6e142d59 634 {
daniele 2:540f6e142d59 635 struct igmpv3_report *report = NULL;
daniele 2:540f6e142d59 636 struct igmpv3_group_record *record = NULL;
daniele 2:540f6e142d59 637 struct pico_mcast_group *g = NULL, test = {0};
daniele 2:540f6e142d59 638 struct pico_tree_node *index = NULL, *_tmp = NULL;
daniele 2:540f6e142d59 639 struct pico_tree *IGMPFilter = NULL;
daniele 2:540f6e142d59 640 struct pico_ip4 *source = NULL;
daniele 2:540f6e142d59 641 uint8_t record_type = 0;
daniele 2:540f6e142d59 642 uint8_t sources = 0;
daniele 2:540f6e142d59 643 int len = 0;
daniele 2:540f6e142d59 644
daniele 2:540f6e142d59 645 test.mcast_addr = p->mcast_group;
daniele 2:540f6e142d59 646 g = pico_tree_findKey(link->MCASTGroups, &test);
daniele 2:540f6e142d59 647 if (!g) {
daniele 2:540f6e142d59 648 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 649 return -1;
daniele 2:540f6e142d59 650 }
daniele 2:540f6e142d59 651
daniele 2:540f6e142d59 652 if (p->event == IGMP_EVENT_DELETE_GROUP) { /* "non-existent" state of filter mode INCLUDE and empty source list */
daniele 2:540f6e142d59 653 p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
daniele 2:540f6e142d59 654 p->MCASTFilter = NULL;
daniele 2:540f6e142d59 655 }
daniele 2:540f6e142d59 656
daniele 2:540f6e142d59 657 /* cleanup filters */
daniele 2:540f6e142d59 658 pico_tree_foreach_safe(index, &IGMPAllow, _tmp)
daniele 2:540f6e142d59 659 {
daniele 2:540f6e142d59 660 pico_tree_delete(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 661 }
daniele 2:540f6e142d59 662 pico_tree_foreach_safe(index, &IGMPBlock, _tmp)
daniele 2:540f6e142d59 663 {
daniele 2:540f6e142d59 664 pico_tree_delete(&IGMPBlock, index->keyValue);
daniele 2:540f6e142d59 665 }
daniele 2:540f6e142d59 666
daniele 2:540f6e142d59 667 switch (g->filter_mode) {
daniele 2:540f6e142d59 668
daniele 2:540f6e142d59 669 case PICO_IP_MULTICAST_INCLUDE:
daniele 2:540f6e142d59 670 switch (p->filter_mode) {
daniele 2:540f6e142d59 671 case PICO_IP_MULTICAST_INCLUDE:
daniele 2:540f6e142d59 672 if (p->event == IGMP_EVENT_DELETE_GROUP) { /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
daniele 2:540f6e142d59 673 /* TO_IN (B) */
daniele 2:540f6e142d59 674 record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
daniele 2:540f6e142d59 675 IGMPFilter = &IGMPAllow;
daniele 2:540f6e142d59 676 if (p->MCASTFilter) {
daniele 2:540f6e142d59 677 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 678 {
daniele 2:540f6e142d59 679 pico_tree_insert(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 680 sources++;
daniele 2:540f6e142d59 681 }
daniele 2:540f6e142d59 682 } /* else { IGMPAllow stays empty } */
daniele 2:540f6e142d59 683 break;
daniele 2:540f6e142d59 684 }
daniele 2:540f6e142d59 685
daniele 2:540f6e142d59 686 /* ALLOW (B-A) */
daniele 2:540f6e142d59 687 /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
daniele 2:540f6e142d59 688 if (p->event == IGMP_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
daniele 2:540f6e142d59 689 record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
daniele 2:540f6e142d59 690 else
daniele 2:540f6e142d59 691 record_type = IGMP_ALLOW_NEW_SOURCES;
daniele 2:540f6e142d59 692 IGMPFilter = &IGMPAllow;
daniele 2:540f6e142d59 693 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 694 {
daniele 2:540f6e142d59 695 pico_tree_insert(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 696 sources++;
daniele 2:540f6e142d59 697 }
daniele 2:540f6e142d59 698 pico_tree_foreach(index, &g->MCASTSources) /* A */
daniele 2:540f6e142d59 699 {
daniele 2:540f6e142d59 700 source = pico_tree_findKey(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 701 if (source) {
daniele 2:540f6e142d59 702 pico_tree_delete(&IGMPAllow, source);
daniele 2:540f6e142d59 703 sources--;
daniele 2:540f6e142d59 704 }
daniele 2:540f6e142d59 705 }
daniele 2:540f6e142d59 706 if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
daniele 2:540f6e142d59 707 break;
daniele 2:540f6e142d59 708
daniele 2:540f6e142d59 709 /* BLOCK (A-B) */
daniele 2:540f6e142d59 710 record_type = IGMP_BLOCK_OLD_SOURCES;
daniele 2:540f6e142d59 711 IGMPFilter = &IGMPBlock;
daniele 2:540f6e142d59 712 pico_tree_foreach(index, &g->MCASTSources) /* A */
daniele 2:540f6e142d59 713 {
daniele 2:540f6e142d59 714 pico_tree_insert(&IGMPBlock, index->keyValue);
daniele 2:540f6e142d59 715 sources++;
daniele 2:540f6e142d59 716 }
daniele 2:540f6e142d59 717 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 718 {
daniele 2:540f6e142d59 719 source = pico_tree_findKey(&IGMPBlock, index->keyValue);
daniele 2:540f6e142d59 720 if (source) {
daniele 2:540f6e142d59 721 pico_tree_delete(&IGMPBlock, source);
daniele 2:540f6e142d59 722 sources--;
daniele 2:540f6e142d59 723 }
daniele 2:540f6e142d59 724 }
daniele 2:540f6e142d59 725 if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
daniele 2:540f6e142d59 726 break;
daniele 2:540f6e142d59 727
daniele 2:540f6e142d59 728 /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report (RFC 3376 $5.1) */
daniele 2:540f6e142d59 729 p->f = NULL;
daniele 2:540f6e142d59 730 return 0;
daniele 2:540f6e142d59 731
daniele 2:540f6e142d59 732 case PICO_IP_MULTICAST_EXCLUDE:
daniele 2:540f6e142d59 733 /* TO_EX (B) */
daniele 2:540f6e142d59 734 record_type = IGMP_CHANGE_TO_EXCLUDE_MODE;
daniele 2:540f6e142d59 735 IGMPFilter = &IGMPBlock;
daniele 2:540f6e142d59 736 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 737 {
daniele 2:540f6e142d59 738 pico_tree_insert(&IGMPBlock, index->keyValue);
daniele 2:540f6e142d59 739 sources++;
daniele 2:540f6e142d59 740 }
daniele 2:540f6e142d59 741 break;
daniele 2:540f6e142d59 742
daniele 2:540f6e142d59 743 default:
daniele 2:540f6e142d59 744 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 745 return -1;
daniele 2:540f6e142d59 746 }
daniele 2:540f6e142d59 747 break;
daniele 2:540f6e142d59 748
daniele 2:540f6e142d59 749 case PICO_IP_MULTICAST_EXCLUDE:
daniele 2:540f6e142d59 750 switch (p->filter_mode) {
daniele 2:540f6e142d59 751 case PICO_IP_MULTICAST_INCLUDE:
daniele 2:540f6e142d59 752 /* TO_IN (B) */
daniele 2:540f6e142d59 753 record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
daniele 2:540f6e142d59 754 IGMPFilter = &IGMPAllow;
daniele 2:540f6e142d59 755 if (p->MCASTFilter) {
daniele 2:540f6e142d59 756 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 757 {
daniele 2:540f6e142d59 758 pico_tree_insert(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 759 sources++;
daniele 2:540f6e142d59 760 }
daniele 2:540f6e142d59 761 } /* else { IGMPAllow stays empty } */
daniele 2:540f6e142d59 762 break;
daniele 2:540f6e142d59 763
daniele 2:540f6e142d59 764 case PICO_IP_MULTICAST_EXCLUDE:
daniele 2:540f6e142d59 765 /* BLOCK (B-A) */
daniele 2:540f6e142d59 766 record_type = IGMP_BLOCK_OLD_SOURCES;
daniele 2:540f6e142d59 767 IGMPFilter = &IGMPBlock;
daniele 2:540f6e142d59 768 pico_tree_foreach(index, p->MCASTFilter)
daniele 2:540f6e142d59 769 {
daniele 2:540f6e142d59 770 pico_tree_insert(&IGMPBlock, index->keyValue);
daniele 2:540f6e142d59 771 sources++;
daniele 2:540f6e142d59 772 }
daniele 2:540f6e142d59 773 pico_tree_foreach(index, &g->MCASTSources) /* A */
daniele 2:540f6e142d59 774 {
daniele 2:540f6e142d59 775 source = pico_tree_findKey(&IGMPBlock, index->keyValue); /* B */
daniele 2:540f6e142d59 776 if (source) {
daniele 2:540f6e142d59 777 pico_tree_delete(&IGMPBlock, source);
daniele 2:540f6e142d59 778 sources--;
daniele 2:540f6e142d59 779 }
daniele 2:540f6e142d59 780 }
daniele 2:540f6e142d59 781 if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
daniele 2:540f6e142d59 782 break;
daniele 2:540f6e142d59 783
daniele 2:540f6e142d59 784 /* ALLOW (A-B) */
daniele 2:540f6e142d59 785 record_type = IGMP_ALLOW_NEW_SOURCES;
daniele 2:540f6e142d59 786 IGMPFilter = &IGMPAllow;
daniele 2:540f6e142d59 787 pico_tree_foreach(index, &g->MCASTSources)
daniele 2:540f6e142d59 788 {
daniele 2:540f6e142d59 789 pico_tree_insert(&IGMPAllow, index->keyValue);
daniele 2:540f6e142d59 790 sources++;
daniele 2:540f6e142d59 791 }
daniele 2:540f6e142d59 792 pico_tree_foreach(index, p->MCASTFilter) /* B */
daniele 2:540f6e142d59 793 {
daniele 2:540f6e142d59 794 source = pico_tree_findKey(&IGMPAllow, index->keyValue); /* A */
daniele 2:540f6e142d59 795 if (source) {
daniele 2:540f6e142d59 796 pico_tree_delete(&IGMPAllow, source);
daniele 2:540f6e142d59 797 sources--;
daniele 2:540f6e142d59 798 }
daniele 2:540f6e142d59 799 }
daniele 2:540f6e142d59 800 if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
daniele 2:540f6e142d59 801 break;
daniele 2:540f6e142d59 802
daniele 2:540f6e142d59 803 /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report (RFC 3376 $5.1) */
daniele 2:540f6e142d59 804 p->f = NULL;
daniele 2:540f6e142d59 805 return 0;
daniele 2:540f6e142d59 806
daniele 2:540f6e142d59 807 default:
daniele 2:540f6e142d59 808 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 809 return -1;
daniele 2:540f6e142d59 810 }
daniele 2:540f6e142d59 811 break;
daniele 2:540f6e142d59 812
daniele 2:540f6e142d59 813 default:
daniele 2:540f6e142d59 814 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 815 return -1;
daniele 2:540f6e142d59 816 }
daniele 2:540f6e142d59 817
daniele 2:540f6e142d59 818 len = sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (sources * sizeof(struct pico_ip4));
daniele 2:540f6e142d59 819 p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + len);
daniele 2:540f6e142d59 820 p->f->net_len += IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 821 p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 822 p->f->transport_len -= IP_OPTION_ROUTER_ALERT_LEN;
daniele 2:540f6e142d59 823 p->f->dev = pico_ipv4_link_find(&p->mcast_link);
daniele 2:540f6e142d59 824 /* p->f->len is correctly set by alloc */
daniele 2:540f6e142d59 825
daniele 2:540f6e142d59 826 report = (struct igmpv3_report *)p->f->transport_hdr;
daniele 2:540f6e142d59 827 report->type = IGMP_TYPE_MEM_REPORT_V3;
daniele 2:540f6e142d59 828 report->res0 = 0;
daniele 2:540f6e142d59 829 report->crc = 0;
daniele 2:540f6e142d59 830 report->res1 = 0;
daniele 2:540f6e142d59 831 report->groups = short_be(1);
daniele 2:540f6e142d59 832
daniele 2:540f6e142d59 833 record = &report->record[0];
daniele 2:540f6e142d59 834 record->type = record_type;
daniele 2:540f6e142d59 835 record->aux = 0;
daniele 2:540f6e142d59 836 record->sources = short_be(sources);
daniele 2:540f6e142d59 837 record->mcast_group = p->mcast_group.addr;
daniele 2:540f6e142d59 838 if (!pico_tree_empty(IGMPFilter)) {
daniele 2:540f6e142d59 839 i = 0;
daniele 2:540f6e142d59 840 pico_tree_foreach(index, IGMPFilter)
daniele 2:540f6e142d59 841 {
daniele 2:540f6e142d59 842 record->source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
daniele 2:540f6e142d59 843 i++;
daniele 2:540f6e142d59 844 }
daniele 2:540f6e142d59 845 }
daniele 2:540f6e142d59 846 report->crc = short_be(pico_checksum(report, len));
daniele 2:540f6e142d59 847 break;
daniele 2:540f6e142d59 848 }
daniele 2:540f6e142d59 849
daniele 2:540f6e142d59 850 default:
daniele 2:540f6e142d59 851 pico_err = PICO_ERR_EINVAL;
daniele 2:540f6e142d59 852 return -1;
daniele 2:540f6e142d59 853 }
daniele 2:540f6e142d59 854 return 0;
daniele 2:540f6e142d59 855 }
daniele 2:540f6e142d59 856
daniele 2:540f6e142d59 857 /* stop timer, send leave if flag set */
daniele 2:540f6e142d59 858 static int stslifs(struct igmp_parameters *p)
daniele 2:540f6e142d59 859 {
daniele 2:540f6e142d59 860 struct igmp_timer t = {0};
daniele 2:540f6e142d59 861
daniele 2:540f6e142d59 862 igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n");
daniele 2:540f6e142d59 863
daniele 2:540f6e142d59 864 t.type = IGMP_TIMER_GROUP_REPORT;
daniele 2:540f6e142d59 865 t.mcast_link = p->mcast_link;
daniele 2:540f6e142d59 866 t.mcast_group = p->mcast_group;
daniele 2:540f6e142d59 867 if (pico_igmp_timer_stop(&t) < 0)
daniele 2:540f6e142d59 868 return -1;
daniele 2:540f6e142d59 869
daniele 2:540f6e142d59 870 /* always send leave, even if not last host */
daniele 2:540f6e142d59 871 if (pico_igmp_send_report(p, p->f) < 0)
daniele 2:540f6e142d59 872 return -1;
daniele 2:540f6e142d59 873
daniele 2:540f6e142d59 874 pico_igmp_delete_parameter(p);
daniele 2:540f6e142d59 875 igmp_dbg("IGMP: new state = non-member\n");
daniele 2:540f6e142d59 876 return 0;
daniele 2:540f6e142d59 877 }
daniele 2:540f6e142d59 878
daniele 2:540f6e142d59 879 /* send report, set flag, start timer */
daniele 2:540f6e142d59 880 static int srsfst(struct igmp_parameters *p)
daniele 2:540f6e142d59 881 {
daniele 2:540f6e142d59 882 struct igmp_timer t = {0};
daniele 2:540f6e142d59 883 struct pico_frame *copy_frame = NULL;
daniele 2:540f6e142d59 884
daniele 2:540f6e142d59 885 igmp_dbg("IGMP: event = join group | action = send report, set flag, start timer\n");
daniele 2:540f6e142d59 886
daniele 2:540f6e142d59 887 p->last_host = IGMP_HOST_LAST;
daniele 2:540f6e142d59 888
daniele 2:540f6e142d59 889 if (pico_igmp_generate_report(p) < 0)
daniele 2:540f6e142d59 890 return -1;
daniele 2:540f6e142d59 891 if (!p->f)
daniele 2:540f6e142d59 892 return 0;
daniele 2:540f6e142d59 893 copy_frame = pico_frame_copy(p->f);
daniele 2:540f6e142d59 894 if (!copy_frame) {
daniele 2:540f6e142d59 895 pico_err = PICO_ERR_ENOMEM;
daniele 2:540f6e142d59 896 return -1;
daniele 2:540f6e142d59 897 }
daniele 2:540f6e142d59 898 if (pico_igmp_send_report(p, copy_frame) < 0)
daniele 2:540f6e142d59 899 return -1;
daniele 2:540f6e142d59 900
daniele 2:540f6e142d59 901 t.type = IGMP_TIMER_GROUP_REPORT;
daniele 2:540f6e142d59 902 t.mcast_link = p->mcast_link;
daniele 2:540f6e142d59 903 t.mcast_group = p->mcast_group;
daniele 2:540f6e142d59 904 t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
daniele 2:540f6e142d59 905 t.f = p->f;
daniele 2:540f6e142d59 906 t.callback = pico_igmp_report_expired;
daniele 2:540f6e142d59 907 pico_igmp_timer_start(&t);
daniele 2:540f6e142d59 908
daniele 2:540f6e142d59 909 p->state = IGMP_STATE_DELAYING_MEMBER;
daniele 2:540f6e142d59 910 igmp_dbg("IGMP: new state = delaying member\n");
daniele 2:540f6e142d59 911 return 0;
daniele 2:540f6e142d59 912 }
daniele 2:540f6e142d59 913
daniele 2:540f6e142d59 914 /* merge report, send report, reset timer (IGMPv3 only) */
daniele 2:540f6e142d59 915 static int mrsrrt(struct igmp_parameters *p)
daniele 2:540f6e142d59 916 {
daniele 2:540f6e142d59 917 struct igmp_timer *t = NULL;
daniele 2:540f6e142d59 918 struct pico_frame *copy_frame = NULL;
daniele 2:540f6e142d59 919 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 920
daniele 2:540f6e142d59 921 igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n");
daniele 2:540f6e142d59 922
daniele 2:540f6e142d59 923 link = pico_ipv4_link_get(&p->mcast_link);
daniele 2:540f6e142d59 924 if (!link)
daniele 2:540f6e142d59 925 return -1;
daniele 2:540f6e142d59 926
daniele 2:540f6e142d59 927 if (link->mcast_compatibility != PICO_IGMPV3) {
daniele 2:540f6e142d59 928 igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
daniele 2:540f6e142d59 929 return -1;
daniele 2:540f6e142d59 930 }
daniele 2:540f6e142d59 931
daniele 2:540f6e142d59 932 /* XXX: merge with pending report rfc 3376 $5.1 */
daniele 2:540f6e142d59 933
daniele 2:540f6e142d59 934 copy_frame = pico_frame_copy(p->f);
daniele 2:540f6e142d59 935 if (!copy_frame)
daniele 2:540f6e142d59 936 return -1;
daniele 2:540f6e142d59 937 if (pico_igmp_send_report(p, copy_frame) < 0)
daniele 2:540f6e142d59 938 return -1;
daniele 2:540f6e142d59 939
daniele 2:540f6e142d59 940 t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
daniele 2:540f6e142d59 941 if (!t)
daniele 2:540f6e142d59 942 return -1;
daniele 2:540f6e142d59 943 t->delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
daniele 2:540f6e142d59 944 pico_igmp_timer_reset(t);
daniele 2:540f6e142d59 945
daniele 2:540f6e142d59 946 p->state = IGMP_STATE_DELAYING_MEMBER;
daniele 2:540f6e142d59 947 igmp_dbg("IGMP: new state = delaying member\n");
daniele 2:540f6e142d59 948 return 0;
daniele 2:540f6e142d59 949 }
daniele 2:540f6e142d59 950
daniele 2:540f6e142d59 951 /* send report, start timer (IGMPv3 only) */
daniele 2:540f6e142d59 952 static int srst(struct igmp_parameters *p)
daniele 2:540f6e142d59 953 {
daniele 2:540f6e142d59 954 struct igmp_timer t = {0};
daniele 2:540f6e142d59 955 struct pico_frame *copy_frame = NULL;
daniele 2:540f6e142d59 956 struct pico_ipv4_link *link = NULL;
daniele 2:540f6e142d59 957
daniele 2:540f6e142d59 958 igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n");
daniele 2:540f6e142d59 959
daniele 2:540f6e142d59 960 link = pico_ipv4_link_get(&p->mcast_link);
daniele 2:540f6e142d59 961 if (!link)
daniele 2:540f6e142d59 962 return -1;
daniele 2:540f6e142d59 963
daniele 2:540f6e142d59 964 if (link->mcast_compatibility != PICO_IGMPV3) {
daniele 2:540f6e142d59 965 igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
daniele 2:540f6e142d59 966 return -1;
daniele 2:540f6e142d59 967 }
daniele 2:540f6e142d59 968
daniele 2:540f6e142d59 969 if (pico_igmp_generate_report(p) < 0)
daniele 2:540f6e142d59 970 return -1;
daniele 2:540f6e142d59 971 if (!p->f)
daniele 2:540f6e142d59 972 return 0;
daniele 2:540f6e142d59 973 copy_frame = pico_frame_copy(p->f);
daniele 2:540f6e142d59 974 if (!copy_frame)
daniele 2:540f6e142d59 975 return -1;
daniele 2:540f6e142d59 976 if (pico_igmp_send_report(p, copy_frame) < 0)
daniele 2:540f6e142d59 977 return -1;
daniele 2:540f6e142d59 978
daniele 2:540f6e142d59 979 t.type = IGMP_TIMER_GROUP_REPORT;
daniele 2:540f6e142d59 980 t.mcast_link = p->mcast_link;
daniele 2:540f6e142d59 981 t.mcast_group = p->mcast_group;
daniele 2:540f6e142d59 982 t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
daniele 2:540f6e142d59 983 t.f = p->f;
daniele 2:540f6e142d59 984 t.callback = pico_igmp_report_expired;
daniele 2:540f6e142d59 985 pico_igmp_timer_start(&t);
daniele 2:540f6e142d59 986
daniele 2:540f6e142d59 987 p->state = IGMP_STATE_DELAYING_MEMBER;
daniele 2:540f6e142d59 988 igmp_dbg("IGMP: new state = delaying member\n");
daniele 2:540f6e142d59 989 return 0;
daniele 2:540f6e142d59 990 }
daniele 2:540f6e142d59 991
daniele 2:540f6e142d59 992 /* send leave if flag set */
daniele 2:540f6e142d59 993 static int slifs(struct igmp_parameters *p)
daniele 2:540f6e142d59 994 {
daniele 2:540f6e142d59 995 igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n");
daniele 2:540f6e142d59 996
daniele 2:540f6e142d59 997 /* always send leave, even if not last host */
daniele 2:540f6e142d59 998 if (pico_igmp_send_report(p, p->f) < 0)
daniele 2:540f6e142d59 999 return -1;
daniele 2:540f6e142d59 1000
daniele 2:540f6e142d59 1001 pico_igmp_delete_parameter(p);
daniele 2:540f6e142d59 1002 igmp_dbg("IGMP: new state = non-member\n");
daniele 2:540f6e142d59 1003 return 0;
daniele 2:540f6e142d59 1004 }
daniele 2:540f6e142d59 1005
daniele 2:540f6e142d59 1006 /* start timer */
daniele 2:540f6e142d59 1007 static int st(struct igmp_parameters *p)
daniele 2:540f6e142d59 1008 {
daniele 2:540f6e142d59 1009 struct igmp_timer t = {0};
daniele 2:540f6e142d59 1010
daniele 2:540f6e142d59 1011 igmp_dbg("IGMP: event = query received | action = start timer\n");
daniele 2:540f6e142d59 1012
daniele 2:540f6e142d59 1013 if (pico_igmp_generate_report(p) < 0)
daniele 2:540f6e142d59 1014 return -1;
daniele 2:540f6e142d59 1015 if (!p->f)
daniele 2:540f6e142d59 1016 return -1;
daniele 2:540f6e142d59 1017
daniele 2:540f6e142d59 1018 t.type = IGMP_TIMER_GROUP_REPORT;
daniele 2:540f6e142d59 1019 t.mcast_link = p->mcast_link;
daniele 2:540f6e142d59 1020 t.mcast_group = p->mcast_group;
daniele 2:540f6e142d59 1021 t.delay = (pico_rand() % (p->max_resp_time * 100));
daniele 2:540f6e142d59 1022 t.f = p->f;
daniele 2:540f6e142d59 1023 t.callback = pico_igmp_report_expired;
daniele 2:540f6e142d59 1024 pico_igmp_timer_start(&t);
daniele 2:540f6e142d59 1025
daniele 2:540f6e142d59 1026 p->state = IGMP_STATE_DELAYING_MEMBER;
daniele 2:540f6e142d59 1027 igmp_dbg("IGMP: new state = delaying member\n");
daniele 2:540f6e142d59 1028 return 0;
daniele 2:540f6e142d59 1029 }
daniele 2:540f6e142d59 1030
daniele 2:540f6e142d59 1031 /* stop timer, clear flag */
daniele 2:540f6e142d59 1032 static int stcl(struct igmp_parameters *p)
daniele 2:540f6e142d59 1033 {
daniele 2:540f6e142d59 1034 struct igmp_timer t = {0};
daniele 2:540f6e142d59 1035
daniele 2:540f6e142d59 1036 igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n");
daniele 2:540f6e142d59 1037
daniele 2:540f6e142d59 1038 t.type = IGMP_TIMER_GROUP_REPORT;
daniele 2:540f6e142d59 1039 t.mcast_link = p->mcast_link;
daniele 2:540f6e142d59 1040 t.mcast_group = p->mcast_group;
daniele 2:540f6e142d59 1041 if (pico_igmp_timer_stop(&t) < 0)
daniele 2:540f6e142d59 1042 return -1;
daniele 2:540f6e142d59 1043
daniele 2:540f6e142d59 1044 p->last_host = IGMP_HOST_NOT_LAST;
daniele 2:540f6e142d59 1045 p->state = IGMP_STATE_IDLE_MEMBER;
daniele 2:540f6e142d59 1046 igmp_dbg("IGMP: new state = idle member\n");
daniele 2:540f6e142d59 1047 return 0;
daniele 2:540f6e142d59 1048 }
daniele 2:540f6e142d59 1049
daniele 2:540f6e142d59 1050 /* send report, set flag */
daniele 2:540f6e142d59 1051 static int srsf(struct igmp_parameters *p)
daniele 2:540f6e142d59 1052 {
daniele 2:540f6e142d59 1053 igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n");
daniele 2:540f6e142d59 1054
daniele 2:540f6e142d59 1055 if (pico_igmp_send_report(p, p->f) < 0)
daniele 2:540f6e142d59 1056 return -1;
daniele 2:540f6e142d59 1057
daniele 2:540f6e142d59 1058 p->state = IGMP_STATE_IDLE_MEMBER;
daniele 2:540f6e142d59 1059 igmp_dbg("IGMP: new state = idle member\n");
daniele 2:540f6e142d59 1060 return 0;
daniele 2:540f6e142d59 1061 }
daniele 2:540f6e142d59 1062
daniele 2:540f6e142d59 1063 /* reset timer if max response time < current timer */
daniele 2:540f6e142d59 1064 static int rtimrtct(struct igmp_parameters *p)
daniele 2:540f6e142d59 1065 {
daniele 2:540f6e142d59 1066 struct igmp_timer *t = NULL;
daniele 2:540f6e142d59 1067 unsigned long time_to_run = 0;
daniele 2:540f6e142d59 1068
daniele 2:540f6e142d59 1069 igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n");
daniele 2:540f6e142d59 1070
daniele 2:540f6e142d59 1071 t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
daniele 2:540f6e142d59 1072 if (!t)
daniele 2:540f6e142d59 1073 return -1;
daniele 2:540f6e142d59 1074
daniele 2:540f6e142d59 1075 time_to_run = t->start + t->delay - PICO_TIME_MS();
daniele 2:540f6e142d59 1076 if ((p->max_resp_time * 100) < time_to_run) { /* max_resp_time in units of 1/10 seconds */
daniele 2:540f6e142d59 1077 t->delay = pico_rand() % (p->max_resp_time * 100);
daniele 2:540f6e142d59 1078 pico_igmp_timer_reset(t);
daniele 2:540f6e142d59 1079 }
daniele 2:540f6e142d59 1080
daniele 2:540f6e142d59 1081 p->state = IGMP_STATE_DELAYING_MEMBER;
daniele 2:540f6e142d59 1082 igmp_dbg("IGMP: new state = delaying member\n");
daniele 2:540f6e142d59 1083 return 0;
daniele 2:540f6e142d59 1084 }
daniele 2:540f6e142d59 1085
daniele 2:540f6e142d59 1086 static int discard(struct igmp_parameters *p){
daniele 2:540f6e142d59 1087 igmp_dbg("IGMP: ignore and discard frame\n");
daniele 2:540f6e142d59 1088 pico_frame_discard(p->f);
daniele 2:540f6e142d59 1089 return 0;
daniele 2:540f6e142d59 1090 }
daniele 2:540f6e142d59 1091
daniele 2:540f6e142d59 1092 /* finite state machine table */
daniele 2:540f6e142d59 1093 const callback host_membership_diagram_table[3][6] =
daniele 2:540f6e142d59 1094 { /* event |Delete Group |Create Group |Update Group |Query Received |Report Received |Timer Expired */
daniele 2:540f6e142d59 1095 /* state Non-Member */ { discard, srsfst, srsfst, discard, discard, discard },
daniele 2:540f6e142d59 1096 /* state Delaying Member */ { stslifs, mrsrrt, mrsrrt, rtimrtct, stcl, srsf },
daniele 2:540f6e142d59 1097 /* state Idle Member */ { slifs, srst, srst, st, discard, discard }
daniele 2:540f6e142d59 1098 };
daniele 2:540f6e142d59 1099
daniele 2:540f6e142d59 1100 static int pico_igmp_process_event(struct igmp_parameters *p)
daniele 2:540f6e142d59 1101 {
daniele 2:540f6e142d59 1102 struct pico_tree_node *index = NULL;
daniele 2:540f6e142d59 1103 struct igmp_parameters *_p = NULL;
daniele 2:540f6e142d59 1104
daniele 2:540f6e142d59 1105 igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.addr);
daniele 2:540f6e142d59 1106 if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.addr == 0) { /* general query */
daniele 2:540f6e142d59 1107 pico_tree_foreach(index, &IGMPParameters) {
daniele 2:540f6e142d59 1108 _p = index->keyValue;
daniele 2:540f6e142d59 1109 _p->max_resp_time = p->max_resp_time;
daniele 2:540f6e142d59 1110 _p->event = IGMP_EVENT_QUERY_RECV;
daniele 2:540f6e142d59 1111 igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.addr, _p->state);
daniele 2:540f6e142d59 1112 host_membership_diagram_table[_p->state][_p->event](_p);
daniele 2:540f6e142d59 1113 }
daniele 2:540f6e142d59 1114 } else {
daniele 2:540f6e142d59 1115 igmp_dbg("IGMP: state = %u (0: non-member - 1: delaying member - 2: idle member)\n", p->state);
daniele 2:540f6e142d59 1116 host_membership_diagram_table[p->state][p->event](p);
daniele 2:540f6e142d59 1117 }
daniele 2:540f6e142d59 1118 return 0;
daniele 2:540f6e142d59 1119 }
daniele 2:540f6e142d59 1120