Free (GPLv2) TCP/IP stack developed by TASS Belgium

Fork of PicoTCP by Daniele Lacamera

Committer:
daniele
Date:
Sat Aug 03 08:50:27 2013 +0000
Revision:
51:18637a3d071f
Parent:
6:55b47464d5bc
Branch for CDC-ECM: Work in progress

Who changed what in which revision?

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