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:
Mon Sep 02 08:02:21 2013 +0000
Revision:
51:ab4529a384a6
Parent:
6:55b47464d5bc
Child:
63:97f481e33cb2
Updated from masterbranch

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