]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv4/igmp.c
Replace API programmation error checking for IGMP join/leave (ismulticast(addr))...
[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 ifp 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, an err_t otherwise
336  */
337 err_t
338 igmp_joingroup(struct netif *ifp, struct ip_addr *groupaddr)
339 {
340   struct igmp_group *group;
341
342   /* make sure it is multicast address */
343   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
344
345   /* find group or create a new one if not found */
346   group = igmp_lookup_group(ifp, groupaddr);
347
348   if (group != NULL) {
349     /* This should create a new group, check the state to make sure */
350     if (group->group_state != NON_MEMBER) {
351       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state NON_MEMBER\n"));
352       return ERR_OK;
353     }
354
355     /* OK - it was new group */
356     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
357     ip_addr_debug_print(IGMP_DEBUG, groupaddr);
358     LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
359
360     if (ifp->igmp_mac_filter != NULL) {
361       ifp->igmp_mac_filter(ifp, groupaddr, IGMP_ADD_MAC_FILTER);
362     }
363
364     IGMP_STATS_INC(igmp.join_sent);
365     igmp_send(group, IGMP_V2_MEMB_REPORT);
366
367     igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
368
369     /* Need to work out where this timer comes from */
370     group->group_state = DELAYING_MEMBER;
371
372     return ERR_OK;
373   }
374
375   return ERR_MEM;
376 }
377
378 /**
379  * Leave a group on one network interface.
380  *
381  * @param ifp the network interface which should leave a group
382  * @param groupaddr the ip address of the group which to leave
383  * @return ERR_OK if group was left, an err_t otherwise
384  */
385 err_t
386 igmp_leavegroup(struct netif *ifp, struct ip_addr *groupaddr)
387 {
388   struct igmp_group *group;
389
390   /* make sure it is multicast address */
391   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
392
393   /* find group */
394   group = igmp_lookfor_group(ifp, groupaddr);
395
396   if (group != NULL) {
397     /* Only send a leave if the flag is set according to the state diagram */
398     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
399     ip_addr_debug_print(IGMP_DEBUG, groupaddr);
400     LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
401
402     if (group->last_reporter_flag) {
403       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
404       IGMP_STATS_INC(igmp.leave_sent);
405       igmp_send(group, IGMP_LEAVE_GROUP);
406     }
407
408     /* The block is not deleted since the group still exists and we may rejoin */
409     group->last_reporter_flag = 0;
410     group->group_state        = NON_MEMBER;
411     group->timer              = 0;
412
413     if (ifp->igmp_mac_filter != NULL) {
414       ifp->igmp_mac_filter(ifp, groupaddr, IGMP_DEL_MAC_FILTER);
415     }
416
417     return ERR_OK;
418   }
419
420   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
421
422   return ERR_VAL;
423 }
424
425 /**
426  * The igmp timer function (both for NO_SYS=1 and =0)
427  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
428  */
429 void
430 igmp_tmr(void)
431 {
432   struct igmp_group *group = igmp_group_list;
433
434   while (group != NULL) {
435     if (group->timer != 0) {
436       group->timer -= 1;
437       if (group->timer == 0) {
438         igmp_timeout(group);
439       }
440     }
441     group = group->next;
442   }
443 }
444
445 /**
446  * Called if a timeout for one group is reached.
447  * Sends a report for this group.
448  *
449  * @param group an igmp_group for which a timeout is reached
450  */
451 void
452 igmp_timeout(struct igmp_group *group)
453 {
454   /* If the state is DELAYING_MEMBER then we send a report for this group */
455   if (group->group_state == DELAYING_MEMBER) {
456     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
457     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
458     LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) group->interface));
459
460     igmp_send(group, IGMP_V2_MEMB_REPORT);
461   }
462 }
463
464 /**
465  * Start a timer for an igmp group
466  *
467  * @param group the igmp_group for which to start a timer
468  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
469  *        every call to igmp_tmr())
470  */
471 void
472 igmp_start_timer(struct igmp_group *group, u8_t max_time)
473 {
474   /**
475    * @todo Important !! this should be random 0 -> max_time. Find out how to do this
476    */
477   group->timer = max_time;
478 }
479
480 /**
481  * Stop a timer for an igmp_group
482  *
483  * @param group the igmp_group for which to stop the timer
484  */
485 void
486 igmp_stop_timer(struct igmp_group *group)
487 {
488   group->timer = 0;
489 }
490
491 /**
492  * Delaying membership report for a group if necessary
493  *
494  * @param group the igmp_group for which "delaying" membership report
495  * @param maxresp query delay
496  */
497 void
498 igmp_delaying_member( struct igmp_group *group, u8_t maxresp)
499 {
500   if ((group->group_state == IDLE_MEMBER) || ((group->group_state == DELAYING_MEMBER) && (maxresp > group->timer))) {
501     igmp_start_timer(group, (maxresp)/2);
502     group->group_state = DELAYING_MEMBER;
503   }
504 }
505
506
507 /**
508  * Sends an IP packet on a network interface. This function constructs the IP header
509  * and calculates the IP header checksum. If the source IP address is NULL,
510  * the IP address of the outgoing network interface is filled in as source address.
511  *
512  * @param p the packet to send (p->payload points to the data, e.g. next
513             protocol header; if dest == IP_HDRINCL, p already includes an IP
514             header and p->payload points to that IP header)
515  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
516  *         IP  address of the netif used to send is used as source address)
517  * @param dest the destination IP address to send the packet to
518  * @param ttl the TTL value to be set in the IP header
519  * @param proto the PROTOCOL to be set in the IP header
520  * @param netif the netif on which to send this packet
521  * @return ERR_OK if the packet was sent OK
522  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
523  *         returns errors returned by netif->output
524  */
525 err_t
526 igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
527                   u8_t ttl, u8_t proto, struct netif *netif)
528 {
529   static u16_t    ip_id = 0;
530   struct ip_hdr * iphdr = NULL;
531   u16_t *         ra    = NULL;
532
533   /* First write in the "router alert" */
534   if (pbuf_header(p, ROUTER_ALERTLEN)) {
535     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
536     return ERR_BUF;
537   }
538
539   /* This is the "router alert" option */
540   ra    = p->payload;
541   ra[0] = htons (0x9404);
542   ra[1] = 0x0000;
543
544   /* now the normal ip header */
545   if (pbuf_header(p, IP_HLEN)) {
546     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
547     return ERR_BUF;
548   }
549
550   iphdr = p->payload;
551   if (dest != IP_HDRINCL) {
552     iphdr->_ttl_proto = (proto<<8);
553     iphdr->_ttl_proto |= ttl;
554
555     /*  iphdr->dest = dest->addr; */
556     ip_addr_set(&(iphdr->dest), dest);
557 #ifdef HAVE_BITFIELDS
558     iphdr->_v_hl_tos |= ((IP_HLEN+ ROUTER_ALERTLEN)/4)<<16;
559     iphdr->_v_hl_tos |= 4<<24;
560 #else
561     iphdr->_v_hl_tos = (4 << 4) | ((IP_HLEN + ROUTER_ALERTLEN)/ 4 & 0xf);
562 #endif /* HAVE_BITFIELDS */
563
564     iphdr->_v_hl_tos |= 0;
565     iphdr->_len       = htons(p->tot_len);
566     iphdr->_offset    = htons(0);
567     iphdr->_id        = htons(ip_id++);
568
569     if (ip_addr_isany(src)) {
570       ip_addr_set(&(iphdr->src), &(netif->ip_addr));
571     } else {
572       ip_addr_set(&(iphdr->src), src);
573     }
574
575     iphdr->_chksum = 0;
576     iphdr->_chksum = inet_chksum(iphdr, IP_HLEN + ROUTER_ALERTLEN);
577   } else {
578     dest = &(iphdr->dest);
579   }
580
581 #if IP_DEBUG
582   ip_debug_print(p);
583 #endif
584
585   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: sending to if %x\n", (int) netif));
586
587   return netif->output(netif, p, dest);
588 }
589
590 /**
591  * Send an igmp packet to a specific group.
592  *
593  * @param group the group to which to send the packet
594  * @param type the type of igmp packet to send
595  */
596 void
597 igmp_send(struct igmp_group *group, u8_t type)
598 {
599   struct pbuf*     p    = NULL;
600   struct igmp_msg* igmp = NULL;
601   struct ip_addr   src  = {0};
602   struct ip_addr*  dest = NULL;
603
604   /* IP header + IGMP header */
605   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
606   
607   if (p) {
608     igmp = p->payload;
609     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
610                (p->len >= sizeof(struct igmp_msg)));
611     ip_addr_set(&src, &((group->interface)->ip_addr));
612      
613     if (type == IGMP_V2_MEMB_REPORT) {
614       dest = &(group->group_address);
615       IGMP_STATS_INC(igmp.report_sent);
616       ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
617       group->last_reporter_flag = 1; /* Remember we were the last to report */
618     } else {
619       if (type == IGMP_LEAVE_GROUP) {
620         dest = &allrouters;
621         ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
622       }
623     }
624
625     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
626       igmp->igmp_msgtype  = type;
627       igmp->igmp_maxresp  = 0;
628       igmp->igmp_checksum = 0;
629       igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
630
631       igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
632     }
633
634     pbuf_free (p);
635   } else {
636     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
637   }
638 }
639
640 #endif /* LWIP_IGMP */