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:
Fri Sep 27 12:43:36 2013 +0000
Revision:
73:dfb737147f6e
Parent:
70:cd218dd180e5
Child:
122:5b1e9de8bf7f
Import from master branch.

Who changed what in which revision?

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