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