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