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