source: UsbWattMeter/trunk/lwip-1.4.1/src/core/ipv4/igmp.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 8 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 26.5 KB
Line 
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/*-------------------------------------------------------------
42Note 1)
43Although the rfc requires V1 AND V2 capability
44we will only support v2 since now V1 is very old (August 1989)
45V1 can be added if required
46
47a debug print and statistic have been implemented to
48show this up.
49-------------------------------------------------------------
50-------------------------------------------------------------
51Note 2)
52A query for a specific group address (as opposed to ALLHOSTS)
53has now been implemented as I am unsure if it is required
54
55a debug print and statistic have been implemented to
56show this up.
57-------------------------------------------------------------
58-------------------------------------------------------------
59Note 3)
60The router alert rfc 2113 is implemented in outgoing packets
61but not checked rigorously incoming
62-------------------------------------------------------------
63Steve 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
125PACK_STRUCT_BEGIN
126struct 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;
132PACK_STRUCT_END
133#ifdef PACK_STRUCT_USE_INCLUDES
134# include "arch/epstruct.h"
135#endif
136
137
138static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
139static err_t igmp_remove_group(struct igmp_group *group);
140static void igmp_timeout( struct igmp_group *group);
141static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
142static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
143static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
144static void igmp_send(struct igmp_group *group, u8_t type);
145
146
147static struct igmp_group* igmp_group_list;
148static ip_addr_t allsystems;
149static ip_addr_t allrouters;
150
151
152/**
153 * Initialize the IGMP module
154 */
155void
156igmp_init(void)
157{
158 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
159
160 IP4_ADDR(&allsystems, 224, 0, 0, 1);
161 IP4_ADDR(&allrouters, 224, 0, 0, 2);
162}
163
164#ifdef LWIP_DEBUG
165/**
166 * Dump global IGMP groups list
167 */
168void
169igmp_dump_group_list()
170{
171 struct igmp_group *group = igmp_group_list;
172
173 while (group != NULL) {
174 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
175 ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
176 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
177 group = group->next;
178 }
179 LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
180}
181#else
182#define igmp_dump_group_list()
183#endif /* LWIP_DEBUG */
184
185/**
186 * Start IGMP processing on interface
187 *
188 * @param netif network interface on which start IGMP processing
189 */
190err_t
191igmp_start(struct netif *netif)
192{
193 struct igmp_group* group;
194
195 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
196
197 group = igmp_lookup_group(netif, &allsystems);
198
199 if (group != NULL) {
200 group->group_state = IGMP_GROUP_IDLE_MEMBER;
201 group->use++;
202
203 /* Allow the igmp messages at the MAC level */
204 if (netif->igmp_mac_filter != NULL) {
205 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
206 ip_addr_debug_print(IGMP_DEBUG, &allsystems);
207 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
208 netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
209 }
210
211 return ERR_OK;
212 }
213
214 return ERR_MEM;
215}
216
217/**
218 * Stop IGMP processing on interface
219 *
220 * @param netif network interface on which stop IGMP processing
221 */
222err_t
223igmp_stop(struct netif *netif)
224{
225 struct igmp_group *group = igmp_group_list;
226 struct igmp_group *prev = NULL;
227 struct igmp_group *next;
228
229 /* look for groups joined on this interface further down the list */
230 while (group != NULL) {
231 next = group->next;
232 /* is it a group joined on this interface? */
233 if (group->netif == netif) {
234 /* is it the first group of the list? */
235 if (group == igmp_group_list) {
236 igmp_group_list = next;
237 }
238 /* is there a "previous" group defined? */
239 if (prev != NULL) {
240 prev->next = next;
241 }
242 /* disable the group at the MAC level */
243 if (netif->igmp_mac_filter != NULL) {
244 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
245 ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
246 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
247 netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
248 }
249 /* free group */
250 memp_free(MEMP_IGMP_GROUP, group);
251 } else {
252 /* change the "previous" */
253 prev = group;
254 }
255 /* move to "next" */
256 group = next;
257 }
258 return ERR_OK;
259}
260
261/**
262 * Report IGMP memberships for this interface
263 *
264 * @param netif network interface on which report IGMP memberships
265 */
266void
267igmp_report_groups(struct netif *netif)
268{
269 struct igmp_group *group = igmp_group_list;
270
271 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
272
273 while (group != NULL) {
274 if (group->netif == netif) {
275 igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
276 }
277 group = group->next;
278 }
279}
280
281/**
282 * Search for a group in the global igmp_group_list
283 *
284 * @param ifp the network interface for which to look
285 * @param addr the group ip address to search for
286 * @return a struct igmp_group* if the group has been found,
287 * NULL if the group wasn't found.
288 */
289struct igmp_group *
290igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
291{
292 struct igmp_group *group = igmp_group_list;
293
294 while (group != NULL) {
295 if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
296 return group;
297 }
298 group = group->next;
299 }
300
301 /* to be clearer, we return NULL here instead of
302 * 'group' (which is also NULL at this point).
303 */
304 return NULL;
305}
306
307/**
308 * Search for a specific igmp group and create a new one if not found-
309 *
310 * @param ifp the network interface for which to look
311 * @param addr the group ip address to search
312 * @return a struct igmp_group*,
313 * NULL on memory error.
314 */
315struct igmp_group *
316igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
317{
318 struct igmp_group *group = igmp_group_list;
319
320 /* Search if the group already exists */
321 group = igmp_lookfor_group(ifp, addr);
322 if (group != NULL) {
323 /* Group already exists. */
324 return group;
325 }
326
327 /* Group doesn't exist yet, create a new one */
328 group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
329 if (group != NULL) {
330 group->netif = ifp;
331 ip_addr_set(&(group->group_address), addr);
332 group->timer = 0; /* Not running */
333 group->group_state = IGMP_GROUP_NON_MEMBER;
334 group->last_reporter_flag = 0;
335 group->use = 0;
336 group->next = igmp_group_list;
337
338 igmp_group_list = group;
339 }
340
341 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
342 ip_addr_debug_print(IGMP_DEBUG, addr);
343 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
344
345 return group;
346}
347
348/**
349 * Remove a group in the global igmp_group_list
350 *
351 * @param group the group to remove from the global igmp_group_list
352 * @return ERR_OK if group was removed from the list, an err_t otherwise
353 */
354static err_t
355igmp_remove_group(struct igmp_group *group)
356{
357 err_t err = ERR_OK;
358
359 /* Is it the first group? */
360 if (igmp_group_list == group) {
361 igmp_group_list = group->next;
362 } else {
363 /* look for group further down the list */
364 struct igmp_group *tmpGroup;
365 for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
366 if (tmpGroup->next == group) {
367 tmpGroup->next = group->next;
368 break;
369 }
370 }
371 /* Group not found in the global igmp_group_list */
372 if (tmpGroup == NULL)
373 err = ERR_ARG;
374 }
375 /* free group */
376 memp_free(MEMP_IGMP_GROUP, group);
377
378 return err;
379}
380
381/**
382 * Called from ip_input() if a new IGMP packet is received.
383 *
384 * @param p received igmp packet, p->payload pointing to the ip header
385 * @param inp network interface on which the packet was received
386 * @param dest destination ip address of the igmp packet
387 */
388void
389igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
390{
391 struct ip_hdr * iphdr;
392 struct igmp_msg* igmp;
393 struct igmp_group* group;
394 struct igmp_group* groupref;
395
396 IGMP_STATS_INC(igmp.recv);
397
398 /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
399 iphdr = (struct ip_hdr *)p->payload;
400 if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
401 pbuf_free(p);
402 IGMP_STATS_INC(igmp.lenerr);
403 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
404 return;
405 }
406
407 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
408 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
409 LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
410 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
411 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
412
413 /* Now calculate and check the checksum */
414 igmp = (struct igmp_msg *)p->payload;
415 if (inet_chksum(igmp, p->len)) {
416 pbuf_free(p);
417 IGMP_STATS_INC(igmp.chkerr);
418 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
419 return;
420 }
421
422 /* Packet is ok so find an existing group */
423 group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
424
425 /* If group can be found or create... */
426 if (!group) {
427 pbuf_free(p);
428 IGMP_STATS_INC(igmp.drop);
429 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
430 return;
431 }
432
433 /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
434 switch (igmp->igmp_msgtype) {
435 case IGMP_MEMB_QUERY: {
436 /* IGMP_MEMB_QUERY to the "all systems" address ? */
437 if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
438 /* THIS IS THE GENERAL QUERY */
439 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)));
440
441 if (igmp->igmp_maxresp == 0) {
442 IGMP_STATS_INC(igmp.rx_v1);
443 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
444 igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
445 } else {
446 IGMP_STATS_INC(igmp.rx_general);
447 }
448
449 groupref = igmp_group_list;
450 while (groupref) {
451 /* Do not send messages on the all systems group address! */
452 if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
453 igmp_delaying_member(groupref, igmp->igmp_maxresp);
454 }
455 groupref = groupref->next;
456 }
457 } else {
458 /* IGMP_MEMB_QUERY to a specific group ? */
459 if (!ip_addr_isany(&igmp->igmp_group_address)) {
460 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
461 ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
462 if (ip_addr_cmp(dest, &allsystems)) {
463 ip_addr_t groupaddr;
464 LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
465 /* we first need to re-look for the group since we used dest last time */
466 ip_addr_copy(groupaddr, igmp->igmp_group_address);
467 group = igmp_lookfor_group(inp, &groupaddr);
468 } else {
469 LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
470 }
471
472 if (group != NULL) {
473 IGMP_STATS_INC(igmp.rx_group);
474 igmp_delaying_member(group, igmp->igmp_maxresp);
475 } else {
476 IGMP_STATS_INC(igmp.drop);
477 }
478 } else {
479 IGMP_STATS_INC(igmp.proterr);
480 }
481 }
482 break;
483 }
484 case IGMP_V2_MEMB_REPORT: {
485 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
486 IGMP_STATS_INC(igmp.rx_report);
487 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
488 /* This is on a specific group we have already looked up */
489 group->timer = 0; /* stopped */
490 group->group_state = IGMP_GROUP_IDLE_MEMBER;
491 group->last_reporter_flag = 0;
492 }
493 break;
494 }
495 default: {
496 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
497 igmp->igmp_msgtype, group->group_state, &group, group->netif));
498 IGMP_STATS_INC(igmp.proterr);
499 break;
500 }
501 }
502
503 pbuf_free(p);
504 return;
505}
506
507/**
508 * Join a group on one network interface.
509 *
510 * @param ifaddr ip address of the network interface which should join a new group
511 * @param groupaddr the ip address of the group which to join
512 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
513 */
514err_t
515igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
516{
517 err_t err = ERR_VAL; /* no matching interface */
518 struct igmp_group *group;
519 struct netif *netif;
520
521 /* make sure it is multicast address */
522 LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
523 LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
524
525 /* loop through netif's */
526 netif = netif_list;
527 while (netif != NULL) {
528 /* Should we join this interface ? */
529 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
530 /* find group or create a new one if not found */
531 group = igmp_lookup_group(netif, groupaddr);
532
533 if (group != NULL) {
534 /* This should create a new group, check the state to make sure */
535 if (group->group_state != IGMP_GROUP_NON_MEMBER) {
536 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
537 } else {
538 /* OK - it was new group */
539 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
540 ip_addr_debug_print(IGMP_DEBUG, groupaddr);
541 LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
542
543 /* If first use of the group, allow the group at the MAC level */
544 if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
545 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
546 ip_addr_debug_print(IGMP_DEBUG, groupaddr);
547 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
548 netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
549 }
550
551 IGMP_STATS_INC(igmp.tx_join);
552 igmp_send(group, IGMP_V2_MEMB_REPORT);
553
554 igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
555
556 /* Need to work out where this timer comes from */
557 group->group_state = IGMP_GROUP_DELAYING_MEMBER;
558 }
559 /* Increment group use */
560 group->use++;
561 /* Join on this interface */
562 err = ERR_OK;
563 } else {
564 /* Return an error even if some network interfaces are joined */
565 /** @todo undo any other netif already joined */
566 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
567 return ERR_MEM;
568 }
569 }
570 /* proceed to next network interface */
571 netif = netif->next;
572 }
573
574 return err;
575}
576
577/**
578 * Leave a group on one network interface.
579 *
580 * @param ifaddr ip address of the network interface which should leave a group
581 * @param groupaddr the ip address of the group which to leave
582 * @return ERR_OK if group was left on the netif(s), an err_t otherwise
583 */
584err_t
585igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
586{
587 err_t err = ERR_VAL; /* no matching interface */
588 struct igmp_group *group;
589 struct netif *netif;
590
591 /* make sure it is multicast address */
592 LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
593 LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
594
595 /* loop through netif's */
596 netif = netif_list;
597 while (netif != NULL) {
598 /* Should we leave this interface ? */
599 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
600 /* find group */
601 group = igmp_lookfor_group(netif, groupaddr);
602
603 if (group != NULL) {
604 /* Only send a leave if the flag is set according to the state diagram */
605 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
606 ip_addr_debug_print(IGMP_DEBUG, groupaddr);
607 LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
608
609 /* If there is no other use of the group */
610 if (group->use <= 1) {
611 /* If we are the last reporter for this group */
612 if (group->last_reporter_flag) {
613 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
614 IGMP_STATS_INC(igmp.tx_leave);
615 igmp_send(group, IGMP_LEAVE_GROUP);
616 }
617
618 /* Disable the group at the MAC level */
619 if (netif->igmp_mac_filter != NULL) {
620 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
621 ip_addr_debug_print(IGMP_DEBUG, groupaddr);
622 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
623 netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
624 }
625
626 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
627 ip_addr_debug_print(IGMP_DEBUG, groupaddr);
628 LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
629
630 /* Free the group */
631 igmp_remove_group(group);
632 } else {
633 /* Decrement group use */
634 group->use--;
635 }
636 /* Leave on this interface */
637 err = ERR_OK;
638 } else {
639 /* It's not a fatal error on "leavegroup" */
640 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
641 }
642 }
643 /* proceed to next network interface */
644 netif = netif->next;
645 }
646
647 return err;
648}
649
650/**
651 * The igmp timer function (both for NO_SYS=1 and =0)
652 * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
653 */
654void
655igmp_tmr(void)
656{
657 struct igmp_group *group = igmp_group_list;
658
659 while (group != NULL) {
660 if (group->timer > 0) {
661 group->timer--;
662 if (group->timer == 0) {
663 igmp_timeout(group);
664 }
665 }
666 group = group->next;
667 }
668}
669
670/**
671 * Called if a timeout for one group is reached.
672 * Sends a report for this group.
673 *
674 * @param group an igmp_group for which a timeout is reached
675 */
676static void
677igmp_timeout(struct igmp_group *group)
678{
679 /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
680 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
681 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
682 ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
683 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
684
685 IGMP_STATS_INC(igmp.tx_report);
686 igmp_send(group, IGMP_V2_MEMB_REPORT);
687 }
688}
689
690/**
691 * Start a timer for an igmp group
692 *
693 * @param group the igmp_group for which to start a timer
694 * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
695 * every call to igmp_tmr())
696 */
697static void
698igmp_start_timer(struct igmp_group *group, u8_t max_time)
699{
700 /* ensure the input value is > 0 */
701 if (max_time <= 1) {
702 max_time = 2;
703 }
704 /* ensure the random value is > 0 */
705 group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
706}
707
708/**
709 * Delaying membership report for a group if necessary
710 *
711 * @param group the igmp_group for which "delaying" membership report
712 * @param maxresp query delay
713 */
714static void
715igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
716{
717 if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
718 ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
719 ((group->timer == 0) || (maxresp < group->timer)))) {
720 igmp_start_timer(group, maxresp);
721 group->group_state = IGMP_GROUP_DELAYING_MEMBER;
722 }
723}
724
725
726/**
727 * Sends an IP packet on a network interface. This function constructs the IP header
728 * and calculates the IP header checksum. If the source IP address is NULL,
729 * the IP address of the outgoing network interface is filled in as source address.
730 *
731 * @param p the packet to send (p->payload points to the data, e.g. next
732 protocol header; if dest == IP_HDRINCL, p already includes an IP
733 header and p->payload points to that IP header)
734 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
735 * IP address of the netif used to send is used as source address)
736 * @param dest the destination IP address to send the packet to
737 * @param ttl the TTL value to be set in the IP header
738 * @param proto the PROTOCOL to be set in the IP header
739 * @param netif the netif on which to send this packet
740 * @return ERR_OK if the packet was sent OK
741 * ERR_BUF if p doesn't have enough space for IP/LINK headers
742 * returns errors returned by netif->output
743 */
744static err_t
745igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
746{
747 /* This is the "router alert" option */
748 u16_t ra[2];
749 ra[0] = PP_HTONS(ROUTER_ALERT);
750 ra[1] = 0x0000; /* Router shall examine packet */
751 IGMP_STATS_INC(igmp.xmit);
752 return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
753}
754
755/**
756 * Send an igmp packet to a specific group.
757 *
758 * @param group the group to which to send the packet
759 * @param type the type of igmp packet to send
760 */
761static void
762igmp_send(struct igmp_group *group, u8_t type)
763{
764 struct pbuf* p = NULL;
765 struct igmp_msg* igmp = NULL;
766 ip_addr_t src = *IP_ADDR_ANY;
767 ip_addr_t* dest = NULL;
768
769 /* IP header + "router alert" option + IGMP header */
770 p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
771
772 if (p) {
773 igmp = (struct igmp_msg *)p->payload;
774 LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
775 (p->len >= sizeof(struct igmp_msg)));
776 ip_addr_copy(src, group->netif->ip_addr);
777
778 if (type == IGMP_V2_MEMB_REPORT) {
779 dest = &(group->group_address);
780 ip_addr_copy(igmp->igmp_group_address, group->group_address);
781 group->last_reporter_flag = 1; /* Remember we were the last to report */
782 } else {
783 if (type == IGMP_LEAVE_GROUP) {
784 dest = &allrouters;
785 ip_addr_copy(igmp->igmp_group_address, group->group_address);
786 }
787 }
788
789 if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
790 igmp->igmp_msgtype = type;
791 igmp->igmp_maxresp = 0;
792 igmp->igmp_checksum = 0;
793 igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
794
795 igmp_ip_output_if(p, &src, dest, group->netif);
796 }
797
798 pbuf_free(p);
799 } else {
800 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
801 IGMP_STATS_INC(igmp.memerr);
802 }
803}
804
805#endif /* LWIP_IGMP */
Note: See TracBrowser for help on using the repository browser.