BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * IGMP - Internet Group Management Protocol |
||
4 | * |
||
5 | * @defgroup igmp IGMP |
||
6 | * @ingroup ip4 |
||
7 | * To be called from TCPIP thread |
||
8 | */ |
||
9 | |||
10 | /* |
||
11 | * Copyright (c) 2002 CITEL Technologies Ltd. |
||
12 | * All rights reserved. |
||
13 | * |
||
14 | * Redistribution and use in source and binary forms, with or without |
||
15 | * modification, are permitted provided that the following conditions |
||
16 | * are met: |
||
17 | * 1. Redistributions of source code must retain the above copyright |
||
18 | * notice, this list of conditions and the following disclaimer. |
||
19 | * 2. Redistributions in binary form must reproduce the above copyright |
||
20 | * notice, this list of conditions and the following disclaimer in the |
||
21 | * documentation and/or other materials provided with the distribution. |
||
22 | * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors |
||
23 | * may be used to endorse or promote products derived from this software |
||
24 | * without specific prior written permission. |
||
25 | * |
||
26 | * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' |
||
27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
29 | * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE |
||
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
36 | * SUCH DAMAGE. |
||
37 | * |
||
38 | * This file is a contribution to the lwIP TCP/IP stack. |
||
39 | * The Swedish Institute of Computer Science and Adam Dunkels |
||
40 | * are specifically granted permission to redistribute this |
||
41 | * source code. |
||
42 | */ |
||
43 | |||
44 | /*------------------------------------------------------------- |
||
45 | Note 1) |
||
46 | Although the rfc requires V1 AND V2 capability |
||
47 | we will only support v2 since now V1 is very old (August 1989) |
||
48 | V1 can be added if required |
||
49 | |||
50 | a debug print and statistic have been implemented to |
||
51 | show this up. |
||
52 | ------------------------------------------------------------- |
||
53 | ------------------------------------------------------------- |
||
54 | Note 2) |
||
55 | A query for a specific group address (as opposed to ALLHOSTS) |
||
56 | has now been implemented as I am unsure if it is required |
||
57 | |||
58 | a debug print and statistic have been implemented to |
||
59 | show this up. |
||
60 | ------------------------------------------------------------- |
||
61 | ------------------------------------------------------------- |
||
62 | Note 3) |
||
63 | The router alert rfc 2113 is implemented in outgoing packets |
||
64 | but not checked rigorously incoming |
||
65 | ------------------------------------------------------------- |
||
66 | Steve Reynolds |
||
67 | ------------------------------------------------------------*/ |
||
68 | |||
69 | /*----------------------------------------------------------------------------- |
||
70 | * RFC 988 - Host extensions for IP multicasting - V0 |
||
71 | * RFC 1054 - Host extensions for IP multicasting - |
||
72 | * RFC 1112 - Host extensions for IP multicasting - V1 |
||
73 | * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) |
||
74 | * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 |
||
75 | * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ |
||
76 | * RFC 2113 - IP Router Alert Option - |
||
77 | *----------------------------------------------------------------------------*/ |
||
78 | |||
79 | /*----------------------------------------------------------------------------- |
||
80 | * Includes |
||
81 | *----------------------------------------------------------------------------*/ |
||
82 | |||
83 | #include "lwip/opt.h" |
||
84 | |||
85 | #if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ |
||
86 | |||
87 | #include "lwip/igmp.h" |
||
88 | #include "lwip/debug.h" |
||
89 | #include "lwip/def.h" |
||
90 | #include "lwip/mem.h" |
||
91 | #include "lwip/ip.h" |
||
92 | #include "lwip/inet_chksum.h" |
||
93 | #include "lwip/netif.h" |
||
94 | #include "lwip/stats.h" |
||
95 | #include "lwip/prot/igmp.h" |
||
96 | |||
97 | #include "string.h" |
||
98 | |||
99 | static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr); |
||
100 | static err_t igmp_remove_group(struct netif *netif, struct igmp_group *group); |
||
101 | static void igmp_timeout(struct netif *netif, struct igmp_group *group); |
||
102 | static void igmp_start_timer(struct igmp_group *group, u8_t max_time); |
||
103 | static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); |
||
104 | static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif); |
||
105 | static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type); |
||
106 | |||
107 | static ip4_addr_t allsystems; |
||
108 | static ip4_addr_t allrouters; |
||
109 | |||
110 | /** |
||
111 | * Initialize the IGMP module |
||
112 | */ |
||
113 | void |
||
114 | igmp_init(void) |
||
115 | { |
||
116 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); |
||
117 | |||
118 | IP4_ADDR(&allsystems, 224, 0, 0, 1); |
||
119 | IP4_ADDR(&allrouters, 224, 0, 0, 2); |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Start IGMP processing on interface |
||
124 | * |
||
125 | * @param netif network interface on which start IGMP processing |
||
126 | */ |
||
127 | err_t |
||
128 | igmp_start(struct netif *netif) |
||
129 | { |
||
130 | struct igmp_group *group; |
||
131 | |||
132 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif)); |
||
133 | |||
134 | group = igmp_lookup_group(netif, &allsystems); |
||
135 | |||
136 | if (group != NULL) { |
||
137 | group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||
138 | group->use++; |
||
139 | |||
140 | /* Allow the igmp messages at the MAC level */ |
||
141 | if (netif->igmp_mac_filter != NULL) { |
||
142 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); |
||
143 | ip4_addr_debug_print_val(IGMP_DEBUG, allsystems); |
||
144 | LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||
145 | netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER); |
||
146 | } |
||
147 | |||
148 | return ERR_OK; |
||
149 | } |
||
150 | |||
151 | return ERR_MEM; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Stop IGMP processing on interface |
||
156 | * |
||
157 | * @param netif network interface on which stop IGMP processing |
||
158 | */ |
||
159 | err_t |
||
160 | igmp_stop(struct netif *netif) |
||
161 | { |
||
162 | struct igmp_group *group = netif_igmp_data(netif); |
||
163 | |||
164 | netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL); |
||
165 | |||
166 | while (group != NULL) { |
||
167 | struct igmp_group *next = group->next; /* avoid use-after-free below */ |
||
168 | |||
169 | /* disable the group at the MAC level */ |
||
170 | if (netif->igmp_mac_filter != NULL) { |
||
171 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); |
||
172 | ip4_addr_debug_print(IGMP_DEBUG, &group->group_address); |
||
173 | LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||
174 | netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); |
||
175 | } |
||
176 | |||
177 | /* free group */ |
||
178 | memp_free(MEMP_IGMP_GROUP, group); |
||
179 | |||
180 | /* move to "next" */ |
||
181 | group = next; |
||
182 | } |
||
183 | return ERR_OK; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Report IGMP memberships for this interface |
||
188 | * |
||
189 | * @param netif network interface on which report IGMP memberships |
||
190 | */ |
||
191 | void |
||
192 | igmp_report_groups(struct netif *netif) |
||
193 | { |
||
194 | struct igmp_group *group = netif_igmp_data(netif); |
||
195 | |||
196 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif)); |
||
197 | |||
198 | /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||
199 | if (group != NULL) { |
||
200 | group = group->next; |
||
201 | } |
||
202 | |||
203 | while (group != NULL) { |
||
204 | igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); |
||
205 | group = group->next; |
||
206 | } |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Search for a group in the netif's igmp group list |
||
211 | * |
||
212 | * @param ifp the network interface for which to look |
||
213 | * @param addr the group ip address to search for |
||
214 | * @return a struct igmp_group* if the group has been found, |
||
215 | * NULL if the group wasn't found. |
||
216 | */ |
||
217 | struct igmp_group * |
||
218 | igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr) |
||
219 | { |
||
220 | struct igmp_group *group = netif_igmp_data(ifp); |
||
221 | |||
222 | while (group != NULL) { |
||
223 | if (ip4_addr_cmp(&(group->group_address), addr)) { |
||
224 | return group; |
||
225 | } |
||
226 | group = group->next; |
||
227 | } |
||
228 | |||
229 | /* to be clearer, we return NULL here instead of |
||
230 | * 'group' (which is also NULL at this point). |
||
231 | */ |
||
232 | return NULL; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Search for a specific igmp group and create a new one if not found- |
||
237 | * |
||
238 | * @param ifp the network interface for which to look |
||
239 | * @param addr the group ip address to search |
||
240 | * @return a struct igmp_group*, |
||
241 | * NULL on memory error. |
||
242 | */ |
||
243 | static struct igmp_group * |
||
244 | igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) |
||
245 | { |
||
246 | struct igmp_group *group; |
||
247 | struct igmp_group *list_head = netif_igmp_data(ifp); |
||
248 | |||
249 | /* Search if the group already exists */ |
||
250 | group = igmp_lookfor_group(ifp, addr); |
||
251 | if (group != NULL) { |
||
252 | /* Group already exists. */ |
||
253 | return group; |
||
254 | } |
||
255 | |||
256 | /* Group doesn't exist yet, create a new one */ |
||
257 | group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); |
||
258 | if (group != NULL) { |
||
259 | ip4_addr_set(&(group->group_address), addr); |
||
260 | group->timer = 0; /* Not running */ |
||
261 | group->group_state = IGMP_GROUP_NON_MEMBER; |
||
262 | group->last_reporter_flag = 0; |
||
263 | group->use = 0; |
||
264 | |||
265 | /* Ensure allsystems group is always first in list */ |
||
266 | if (list_head == NULL) { |
||
267 | /* this is the first entry in linked list */ |
||
268 | LWIP_ASSERT("igmp_lookup_group: first group must be allsystems", |
||
269 | (ip4_addr_cmp(addr, &allsystems) != 0)); |
||
270 | group->next = NULL; |
||
271 | netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group); |
||
272 | } else { |
||
273 | /* append _after_ first entry */ |
||
274 | LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems", |
||
275 | (ip4_addr_cmp(addr, &allsystems) == 0)); |
||
276 | group->next = list_head->next; |
||
277 | list_head->next = group; |
||
278 | } |
||
279 | } |
||
280 | |||
281 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to "))); |
||
282 | ip4_addr_debug_print(IGMP_DEBUG, addr); |
||
283 | LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp)); |
||
284 | |||
285 | return group; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Remove a group from netif's igmp group list, but don't free it yet |
||
290 | * |
||
291 | * @param group the group to remove from the netif's igmp group list |
||
292 | * @return ERR_OK if group was removed from the list, an err_t otherwise |
||
293 | */ |
||
294 | static err_t |
||
295 | igmp_remove_group(struct netif *netif, struct igmp_group *group) |
||
296 | { |
||
297 | err_t err = ERR_OK; |
||
298 | struct igmp_group *tmp_group; |
||
299 | |||
300 | /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||
301 | for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) { |
||
302 | if (tmp_group->next == group) { |
||
303 | tmp_group->next = group->next; |
||
304 | break; |
||
305 | } |
||
306 | } |
||
307 | /* Group not found in netif's igmp group list */ |
||
308 | if (tmp_group == NULL) { |
||
309 | err = ERR_ARG; |
||
310 | } |
||
311 | |||
312 | return err; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Called from ip_input() if a new IGMP packet is received. |
||
317 | * |
||
318 | * @param p received igmp packet, p->payload pointing to the igmp header |
||
319 | * @param inp network interface on which the packet was received |
||
320 | * @param dest destination ip address of the igmp packet |
||
321 | */ |
||
322 | void |
||
323 | igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest) |
||
324 | { |
||
325 | struct igmp_msg *igmp; |
||
326 | struct igmp_group *group; |
||
327 | struct igmp_group *groupref; |
||
328 | |||
329 | IGMP_STATS_INC(igmp.recv); |
||
330 | |||
331 | /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ |
||
332 | if (p->len < IGMP_MINLEN) { |
||
333 | pbuf_free(p); |
||
334 | IGMP_STATS_INC(igmp.lenerr); |
||
335 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); |
||
336 | return; |
||
337 | } |
||
338 | |||
339 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); |
||
340 | ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src)); |
||
341 | LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); |
||
342 | ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest)); |
||
343 | LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)inp)); |
||
344 | |||
345 | /* Now calculate and check the checksum */ |
||
346 | igmp = (struct igmp_msg *)p->payload; |
||
347 | if (inet_chksum(igmp, p->len)) { |
||
348 | pbuf_free(p); |
||
349 | IGMP_STATS_INC(igmp.chkerr); |
||
350 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); |
||
351 | return; |
||
352 | } |
||
353 | |||
354 | /* Packet is ok so find an existing group */ |
||
355 | group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ |
||
356 | |||
357 | /* If group can be found or create... */ |
||
358 | if (!group) { |
||
359 | pbuf_free(p); |
||
360 | IGMP_STATS_INC(igmp.drop); |
||
361 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); |
||
362 | return; |
||
363 | } |
||
364 | |||
365 | /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ |
||
366 | switch (igmp->igmp_msgtype) { |
||
367 | case IGMP_MEMB_QUERY: |
||
368 | /* IGMP_MEMB_QUERY to the "all systems" address ? */ |
||
369 | if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) { |
||
370 | /* THIS IS THE GENERAL QUERY */ |
||
371 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); |
||
372 | |||
373 | if (igmp->igmp_maxresp == 0) { |
||
374 | IGMP_STATS_INC(igmp.rx_v1); |
||
375 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); |
||
376 | igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; |
||
377 | } else { |
||
378 | IGMP_STATS_INC(igmp.rx_general); |
||
379 | } |
||
380 | |||
381 | groupref = netif_igmp_data(inp); |
||
382 | |||
383 | /* Do not send messages on the all systems group address! */ |
||
384 | /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ |
||
385 | if (groupref != NULL) { |
||
386 | groupref = groupref->next; |
||
387 | } |
||
388 | |||
389 | while (groupref) { |
||
390 | igmp_delaying_member(groupref, igmp->igmp_maxresp); |
||
391 | groupref = groupref->next; |
||
392 | } |
||
393 | } else { |
||
394 | /* IGMP_MEMB_QUERY to a specific group ? */ |
||
395 | if (!ip4_addr_isany(&igmp->igmp_group_address)) { |
||
396 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); |
||
397 | ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); |
||
398 | if (ip4_addr_cmp(dest, &allsystems)) { |
||
399 | ip4_addr_t groupaddr; |
||
400 | LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); |
||
401 | /* we first need to re-look for the group since we used dest last time */ |
||
402 | ip4_addr_copy(groupaddr, igmp->igmp_group_address); |
||
403 | group = igmp_lookfor_group(inp, &groupaddr); |
||
404 | } else { |
||
405 | LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); |
||
406 | } |
||
407 | |||
408 | if (group != NULL) { |
||
409 | IGMP_STATS_INC(igmp.rx_group); |
||
410 | igmp_delaying_member(group, igmp->igmp_maxresp); |
||
411 | } else { |
||
412 | IGMP_STATS_INC(igmp.drop); |
||
413 | } |
||
414 | } else { |
||
415 | IGMP_STATS_INC(igmp.proterr); |
||
416 | } |
||
417 | } |
||
418 | break; |
||
419 | case IGMP_V2_MEMB_REPORT: |
||
420 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); |
||
421 | IGMP_STATS_INC(igmp.rx_report); |
||
422 | if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { |
||
423 | /* This is on a specific group we have already looked up */ |
||
424 | group->timer = 0; /* stopped */ |
||
425 | group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||
426 | group->last_reporter_flag = 0; |
||
427 | } |
||
428 | break; |
||
429 | default: |
||
430 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", |
||
431 | igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp)); |
||
432 | IGMP_STATS_INC(igmp.proterr); |
||
433 | break; |
||
434 | } |
||
435 | |||
436 | pbuf_free(p); |
||
437 | return; |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * @ingroup igmp |
||
442 | * Join a group on one network interface. |
||
443 | * |
||
444 | * @param ifaddr ip address of the network interface which should join a new group |
||
445 | * @param groupaddr the ip address of the group which to join |
||
446 | * @return ERR_OK if group was joined on the netif(s), an err_t otherwise |
||
447 | */ |
||
448 | err_t |
||
449 | igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) |
||
450 | { |
||
451 | err_t err = ERR_VAL; /* no matching interface */ |
||
452 | struct netif *netif; |
||
453 | |||
454 | /* make sure it is multicast address */ |
||
455 | LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); |
||
456 | LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); |
||
457 | |||
458 | /* loop through netif's */ |
||
459 | NETIF_FOREACH(netif) { |
||
460 | /* Should we join this interface ? */ |
||
461 | if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { |
||
462 | err = igmp_joingroup_netif(netif, groupaddr); |
||
463 | if (err != ERR_OK) { |
||
464 | /* Return an error even if some network interfaces are joined */ |
||
465 | /** @todo undo any other netif already joined */ |
||
466 | return err; |
||
467 | } |
||
468 | } |
||
469 | } |
||
470 | |||
471 | return err; |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * @ingroup igmp |
||
476 | * Join a group on one network interface. |
||
477 | * |
||
478 | * @param netif the network interface which should join a new group |
||
479 | * @param groupaddr the ip address of the group which to join |
||
480 | * @return ERR_OK if group was joined on the netif, an err_t otherwise |
||
481 | */ |
||
482 | err_t |
||
483 | igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) |
||
484 | { |
||
485 | struct igmp_group *group; |
||
486 | |||
487 | /* make sure it is multicast address */ |
||
488 | LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); |
||
489 | LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); |
||
490 | |||
491 | /* make sure it is an igmp-enabled netif */ |
||
492 | LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); |
||
493 | |||
494 | /* find group or create a new one if not found */ |
||
495 | group = igmp_lookup_group(netif, groupaddr); |
||
496 | |||
497 | if (group != NULL) { |
||
498 | /* This should create a new group, check the state to make sure */ |
||
499 | if (group->group_state != IGMP_GROUP_NON_MEMBER) { |
||
500 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n")); |
||
501 | } else { |
||
502 | /* OK - it was new group */ |
||
503 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: ")); |
||
504 | ip4_addr_debug_print(IGMP_DEBUG, groupaddr); |
||
505 | LWIP_DEBUGF(IGMP_DEBUG, ("\n")); |
||
506 | |||
507 | /* If first use of the group, allow the group at the MAC level */ |
||
508 | if ((group->use == 0) && (netif->igmp_mac_filter != NULL)) { |
||
509 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD ")); |
||
510 | ip4_addr_debug_print(IGMP_DEBUG, groupaddr); |
||
511 | LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||
512 | netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER); |
||
513 | } |
||
514 | |||
515 | IGMP_STATS_INC(igmp.tx_join); |
||
516 | igmp_send(netif, group, IGMP_V2_MEMB_REPORT); |
||
517 | |||
518 | igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); |
||
519 | |||
520 | /* Need to work out where this timer comes from */ |
||
521 | group->group_state = IGMP_GROUP_DELAYING_MEMBER; |
||
522 | } |
||
523 | /* Increment group use */ |
||
524 | group->use++; |
||
525 | /* Join on this interface */ |
||
526 | return ERR_OK; |
||
527 | } else { |
||
528 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n")); |
||
529 | return ERR_MEM; |
||
530 | } |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * @ingroup igmp |
||
535 | * Leave a group on one network interface. |
||
536 | * |
||
537 | * @param ifaddr ip address of the network interface which should leave a group |
||
538 | * @param groupaddr the ip address of the group which to leave |
||
539 | * @return ERR_OK if group was left on the netif(s), an err_t otherwise |
||
540 | */ |
||
541 | err_t |
||
542 | igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) |
||
543 | { |
||
544 | err_t err = ERR_VAL; /* no matching interface */ |
||
545 | struct netif *netif; |
||
546 | |||
547 | /* make sure it is multicast address */ |
||
548 | LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); |
||
549 | LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); |
||
550 | |||
551 | /* loop through netif's */ |
||
552 | NETIF_FOREACH(netif) { |
||
553 | /* Should we leave this interface ? */ |
||
554 | if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { |
||
555 | err_t res = igmp_leavegroup_netif(netif, groupaddr); |
||
556 | if (err != ERR_OK) { |
||
557 | /* Store this result if we have not yet gotten a success */ |
||
558 | err = res; |
||
559 | } |
||
560 | } |
||
561 | } |
||
562 | |||
563 | return err; |
||
564 | } |
||
565 | |||
566 | /** |
||
567 | * @ingroup igmp |
||
568 | * Leave a group on one network interface. |
||
569 | * |
||
570 | * @param netif the network interface which should leave a group |
||
571 | * @param groupaddr the ip address of the group which to leave |
||
572 | * @return ERR_OK if group was left on the netif, an err_t otherwise |
||
573 | */ |
||
574 | err_t |
||
575 | igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) |
||
576 | { |
||
577 | struct igmp_group *group; |
||
578 | |||
579 | /* make sure it is multicast address */ |
||
580 | LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); |
||
581 | LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); |
||
582 | |||
583 | /* make sure it is an igmp-enabled netif */ |
||
584 | LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); |
||
585 | |||
586 | /* find group */ |
||
587 | group = igmp_lookfor_group(netif, groupaddr); |
||
588 | |||
589 | if (group != NULL) { |
||
590 | /* Only send a leave if the flag is set according to the state diagram */ |
||
591 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: ")); |
||
592 | ip4_addr_debug_print(IGMP_DEBUG, groupaddr); |
||
593 | LWIP_DEBUGF(IGMP_DEBUG, ("\n")); |
||
594 | |||
595 | /* If there is no other use of the group */ |
||
596 | if (group->use <= 1) { |
||
597 | /* Remove the group from the list */ |
||
598 | igmp_remove_group(netif, group); |
||
599 | |||
600 | /* If we are the last reporter for this group */ |
||
601 | if (group->last_reporter_flag) { |
||
602 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n")); |
||
603 | IGMP_STATS_INC(igmp.tx_leave); |
||
604 | igmp_send(netif, group, IGMP_LEAVE_GROUP); |
||
605 | } |
||
606 | |||
607 | /* Disable the group at the MAC level */ |
||
608 | if (netif->igmp_mac_filter != NULL) { |
||
609 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL ")); |
||
610 | ip4_addr_debug_print(IGMP_DEBUG, groupaddr); |
||
611 | LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); |
||
612 | netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); |
||
613 | } |
||
614 | |||
615 | /* Free group struct */ |
||
616 | memp_free(MEMP_IGMP_GROUP, group); |
||
617 | } else { |
||
618 | /* Decrement group use */ |
||
619 | group->use--; |
||
620 | } |
||
621 | return ERR_OK; |
||
622 | } else { |
||
623 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n")); |
||
624 | return ERR_VAL; |
||
625 | } |
||
626 | } |
||
627 | |||
628 | /** |
||
629 | * The igmp timer function (both for NO_SYS=1 and =0) |
||
630 | * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). |
||
631 | */ |
||
632 | void |
||
633 | igmp_tmr(void) |
||
634 | { |
||
635 | struct netif *netif; |
||
636 | |||
637 | NETIF_FOREACH(netif) { |
||
638 | struct igmp_group *group = netif_igmp_data(netif); |
||
639 | |||
640 | while (group != NULL) { |
||
641 | if (group->timer > 0) { |
||
642 | group->timer--; |
||
643 | if (group->timer == 0) { |
||
644 | igmp_timeout(netif, group); |
||
645 | } |
||
646 | } |
||
647 | group = group->next; |
||
648 | } |
||
649 | } |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Called if a timeout for one group is reached. |
||
654 | * Sends a report for this group. |
||
655 | * |
||
656 | * @param group an igmp_group for which a timeout is reached |
||
657 | */ |
||
658 | static void |
||
659 | igmp_timeout(struct netif *netif, struct igmp_group *group) |
||
660 | { |
||
661 | /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group |
||
662 | (unless it is the allsystems group) */ |
||
663 | if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && |
||
664 | (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) { |
||
665 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); |
||
666 | ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address)); |
||
667 | LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif)); |
||
668 | |||
669 | group->group_state = IGMP_GROUP_IDLE_MEMBER; |
||
670 | |||
671 | IGMP_STATS_INC(igmp.tx_report); |
||
672 | igmp_send(netif, group, IGMP_V2_MEMB_REPORT); |
||
673 | } |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * Start a timer for an igmp group |
||
678 | * |
||
679 | * @param group the igmp_group for which to start a timer |
||
680 | * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with |
||
681 | * every call to igmp_tmr()) |
||
682 | */ |
||
683 | static void |
||
684 | igmp_start_timer(struct igmp_group *group, u8_t max_time) |
||
685 | { |
||
686 | #ifdef LWIP_RAND |
||
687 | group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1); |
||
688 | #else /* LWIP_RAND */ |
||
689 | /* ATTENTION: use this only if absolutely necessary! */ |
||
690 | group->timer = max_time / 2; |
||
691 | #endif /* LWIP_RAND */ |
||
692 | |||
693 | if (group->timer == 0) { |
||
694 | group->timer = 1; |
||
695 | } |
||
696 | } |
||
697 | |||
698 | /** |
||
699 | * Delaying membership report for a group if necessary |
||
700 | * |
||
701 | * @param group the igmp_group for which "delaying" membership report |
||
702 | * @param maxresp query delay |
||
703 | */ |
||
704 | static void |
||
705 | igmp_delaying_member(struct igmp_group *group, u8_t maxresp) |
||
706 | { |
||
707 | if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || |
||
708 | ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && |
||
709 | ((group->timer == 0) || (maxresp < group->timer)))) { |
||
710 | igmp_start_timer(group, maxresp); |
||
711 | group->group_state = IGMP_GROUP_DELAYING_MEMBER; |
||
712 | } |
||
713 | } |
||
714 | |||
715 | |||
716 | /** |
||
717 | * Sends an IP packet on a network interface. This function constructs the IP header |
||
718 | * and calculates the IP header checksum. If the source IP address is NULL, |
||
719 | * the IP address of the outgoing network interface is filled in as source address. |
||
720 | * |
||
721 | * @param p the packet to send (p->payload points to the data, e.g. next |
||
722 | protocol header; if dest == LWIP_IP_HDRINCL, p already includes an |
||
723 | IP header and p->payload points to that IP header) |
||
724 | * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the |
||
725 | * IP address of the netif used to send is used as source address) |
||
726 | * @param dest the destination IP address to send the packet to |
||
727 | * @param netif the netif on which to send this packet |
||
728 | * @return ERR_OK if the packet was sent OK |
||
729 | * ERR_BUF if p doesn't have enough space for IP/LINK headers |
||
730 | * returns errors returned by netif->output |
||
731 | */ |
||
732 | static err_t |
||
733 | igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif) |
||
734 | { |
||
735 | /* This is the "router alert" option */ |
||
736 | u16_t ra[2]; |
||
737 | ra[0] = PP_HTONS(ROUTER_ALERT); |
||
738 | ra[1] = 0x0000; /* Router shall examine packet */ |
||
739 | IGMP_STATS_INC(igmp.xmit); |
||
740 | return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Send an igmp packet to a specific group. |
||
745 | * |
||
746 | * @param group the group to which to send the packet |
||
747 | * @param type the type of igmp packet to send |
||
748 | */ |
||
749 | static void |
||
750 | igmp_send(struct netif *netif, struct igmp_group *group, u8_t type) |
||
751 | { |
||
752 | struct pbuf *p = NULL; |
||
753 | struct igmp_msg *igmp = NULL; |
||
754 | ip4_addr_t src = *IP4_ADDR_ANY4; |
||
755 | ip4_addr_t *dest = NULL; |
||
756 | |||
757 | /* IP header + "router alert" option + IGMP header */ |
||
758 | p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); |
||
759 | |||
760 | if (p) { |
||
761 | igmp = (struct igmp_msg *)p->payload; |
||
762 | LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", |
||
763 | (p->len >= sizeof(struct igmp_msg))); |
||
764 | ip4_addr_copy(src, *netif_ip4_addr(netif)); |
||
765 | |||
766 | if (type == IGMP_V2_MEMB_REPORT) { |
||
767 | dest = &(group->group_address); |
||
768 | ip4_addr_copy(igmp->igmp_group_address, group->group_address); |
||
769 | group->last_reporter_flag = 1; /* Remember we were the last to report */ |
||
770 | } else { |
||
771 | if (type == IGMP_LEAVE_GROUP) { |
||
772 | dest = &allrouters; |
||
773 | ip4_addr_copy(igmp->igmp_group_address, group->group_address); |
||
774 | } |
||
775 | } |
||
776 | |||
777 | if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { |
||
778 | igmp->igmp_msgtype = type; |
||
779 | igmp->igmp_maxresp = 0; |
||
780 | igmp->igmp_checksum = 0; |
||
781 | igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); |
||
782 | |||
783 | igmp_ip_output_if(p, &src, dest, netif); |
||
784 | } |
||
785 | |||
786 | pbuf_free(p); |
||
787 | } else { |
||
788 | LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); |
||
789 | IGMP_STATS_INC(igmp.memerr); |
||
790 | } |
||
791 | } |
||
792 | |||
793 | #endif /* LWIP_IPV4 && LWIP_IGMP */ |