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