BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
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 */