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