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

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Committer:
tass
Date:
Thu Sep 19 12:38:53 2013 +0000
Revision:
63:97f481e33cb2
Parent:
51:ab4529a384a6
Update from the master branch

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 63:97f481e33cb2 141 static struct pico_queue igmp_in = {};
tass 63:97f481e33cb2 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 63:97f481e33cb2 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 63:97f481e33cb2 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