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