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