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