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