]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv4/igmp.c
Remove obsolete include from my port.
[pes-rpp/rpp-lwip.git] / src / core / ipv4 / igmp.c
1 /*
2  * Copyright (c) 2002 CITEL Technologies Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without 
6  * modification, are permitted provided that the following conditions 
7  * are met: 
8  * 1. Redistributions of source code must retain the above copyright 
9  *    notice, this list of conditions and the following disclaimer. 
10  * 2. Redistributions in binary form must reproduce the above copyright 
11  *    notice, this list of conditions and the following disclaimer in the 
12  *    documentation and/or other materials provided with the distribution. 
13  * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 
14  *    may be used to endorse or promote products derived from this software 
15  *    without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
20  * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
27  * SUCH DAMAGE. 
28  *
29  * This file is a contribution to the lwIP TCP/IP stack.
30  * The Swedish Institute of Computer Science and Adam Dunkels
31  * are specifically granted permission to redistribute this
32  * source code.
33 */
34
35 /************************************************************
36 In the spirit of LW (I hope)
37 -------------------------------------------------------------
38 note 1)
39 Although the rfc requires V1 AND V2 capability
40 we will only support v2 since now V1 is 5 years old
41 V1 can be added if required
42
43 a debug print and statistic have been implemented to
44 show this up.
45 -------------------------------------------------------------
46 -------------------------------------------------------------
47 note 2)
48
49 A query for a specific group address (as opposed to ALLHOSTS)
50 has now been implemented as I am unsure if it is required
51
52 a debug print and statistic have been implemented to
53 show this up.
54 -------------------------------------------------------------
55 -------------------------------------------------------------
56 note 3)
57 The router alert rfc 2113 is implemented in outgoing packets
58 but not checked rigorously incoming
59 -------------------------------------------------------------
60 Steve Reynolds
61 ------------------------------------------------------------*/
62
63 /*-----------------------------------------------------------------------------
64  * Includes
65  *----------------------------------------------------------------------------*/
66
67 #include "lwip/debug.h"
68 #include "lwip/def.h"
69 #include "lwip/mem.h"
70 #include "lwip/ip.h"
71 #include "lwip/inet.h"
72 #include "lwip/netif.h"
73 #include "lwip/icmp.h"
74 #include "lwip/udp.h"
75 #include "lwip/tcp.h"
76 #include "lwip/stats.h"
77 #include "lwip/igmp.h"
78
79 #include "arch/perf.h"
80
81 #include "string.h"
82
83 /* IGMP support available? */
84 #if defined(LWIP_IGMP) && (LWIP_IGMP > 0)
85
86 /*-----------------------------------------------------------------------------
87  * Globales
88  *----------------------------------------------------------------------------*/
89
90 struct igmp_group* GroupList;
91 struct igmp_stats  igmpstats;
92
93 struct ip_addr     allsystems;
94 struct ip_addr     allrouters;
95
96 /*-----------------------------------------------------------------------------
97  * igmp_init
98  *----------------------------------------------------------------------------*/
99 void igmp_init(void)
100 { struct igmp_group* group;
101   struct netif*      netif;
102   
103   LWIP_DEBUGF(IGMP_DEBUG, ("IGMP Initialising\n"));
104
105   IP4_ADDR (&allsystems, 224, 0, 0, 1);
106   IP4_ADDR (&allrouters, 224, 0, 0, 2);
107   
108   GroupList = NULL;  
109   memset( &igmpstats, 0, sizeof(igmpstats));
110   
111   for( netif = netif_list; netif != NULL; netif = netif->next)
112    { group = lookup_group (netif, &allsystems);
113       
114      if (group)
115       { group->group_state = IDLE_MEMBER;
116       
117         // Allow the igmp messages at the MAC level
118         if (netif->igmp_mac_filter!=NULL)
119          { netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER);
120            netif->igmp_mac_filter( netif, &allrouters, IGMP_ADD_MAC_FILTER);
121          }
122       }
123    }
124
125   // Start the 10 millisecond tick 
126   // we can optimise this to only run when timers are active later on
127   sys_timeout( IGMP_TICK, igmp_tick, NULL);
128 }
129
130 /*-----------------------------------------------------------------------------
131  * lookfor_group
132  *----------------------------------------------------------------------------*/
133 struct igmp_group * lookfor_group( struct netif *ifp, struct ip_addr *addr)
134 { struct igmp_group *group = GroupList;
135   
136   while (group)
137    { if ((group->interface == ifp) && (ip_addr_cmp (&(group->group_address), addr)))
138       { return group;
139       }
140      group = group->next;
141    }
142    
143   return group;
144 }
145
146 /*-----------------------------------------------------------------------------
147  * lookup_group
148  *----------------------------------------------------------------------------*/
149 struct igmp_group * lookup_group( struct netif *ifp, struct ip_addr *addr)
150 { struct igmp_group *group = GroupList;
151   
152   while (group)
153    { if ((group->interface == ifp) && (ip_addr_cmp (&(group->group_address), addr)))
154       { return group;
155       }
156      group = group->next;
157    }
158    
159   group = mem_malloc (sizeof (struct igmp_group));
160   
161   if (group)
162    { group->interface          = ifp;
163      ip_addr_set (&(group->group_address), addr);
164      group->timer              = 0;        // Not running
165      group->group_state        = NON_MEMBER;
166      group->last_reporter_flag = 0;
167      group->next               = GroupList;
168      
169      GroupList = group;
170      
171      LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d  allocated a new group with address %x on if %x \n", __LINE__, (int) addr, (int) ifp));
172    }
173   else
174    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d  impossible to allocated a new group with address %x on if %x \n", __LINE__, (int) addr, (int) ifp));
175    }
176    
177   return group;
178 }
179
180 /*-----------------------------------------------------------------------------
181  * igmp_input
182  *----------------------------------------------------------------------------*/
183 void igmp_input( struct pbuf *p, struct netif *inp, struct ip_addr *dest)
184 { struct ip_hdr *    iphdr;
185   struct igmpmsg*    igmp;
186   struct igmp_group* group;
187   struct igmp_group* groupref;
188   
189   iphdr = p->payload;  
190   igmp  = (struct igmpmsg *)(((u8_t *)p->payload)+((u32_t)(IPH_HL(iphdr) * 4)));
191
192   LWIP_DEBUGF(IGMP_DEBUG, ("igmp message to address %l \n", (long) dest->addr));
193
194   if (p->len < IGMP_MINLEN)
195    { // Nore that the length CAN be greater than 8 but only 8 are used - All are included in the checksum
196      pbuf_free (p);
197      igmpstats.igmp_length_err++;
198      LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x igmp length error\n", __LINE__));
199      return;
200    }
201
202   // Now calculate and check the checksum
203   if (inet_chksum (igmp, IGMP_MINLEN /*p->len*/))
204    { pbuf_free (p);
205      igmpstats.igmp_checksum_err++;
206      LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp checksum error\n", __LINE__));
207      return;
208    }
209
210   // Packet is ok so find the group (or create a new one)
211   group = lookup_group (inp, dest);    // use the incoming IP address!
212   
213   // If group can be found or create...
214   if (!group)
215    { pbuf_free (p);
216      LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp allocation error\n", __LINE__));
217      return;
218    }
219
220   // NOW ACT ON THE INCOMING MESSAGE TYPE...
221
222   // The membership query message goes to the all groups address
223   // and it control block does not have state
224   if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && (ip_addr_cmp (dest, &allsystems)) && (igmp->igmp_group_address.addr == 0))
225    { // THIS IS THE GENERAL QUERY
226      LWIP_DEBUGF(IGMP_DEBUG, ("General IGMP_MEMB_QUERY on ALL SYSTEMS ADDRESS 224.0.0.1\n"));
227
228      if (0 ==igmp->igmp_maxresp )
229       { igmpstats.igmp_v1_rxed++;
230         igmp->igmp_maxresp = 10;
231         LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n", __LINE__));
232       }
233       
234      igmpstats.igmp_group_query_rxed++;
235      groupref = GroupList;
236      while (groupref)
237       { if ((groupref->interface == inp) && (!(ip_addr_cmp (&(groupref->group_address), &allsystems))))
238          { // Do not send messages on the all systems group address!
239            if ((groupref->group_state == IDLE_MEMBER) || ((groupref->group_state == DELAYING_MEMBER) && (igmp->igmp_maxresp > groupref->timer)))
240             { igmp_start_timer (groupref, (igmp->igmp_maxresp)/2);
241               groupref->group_state = DELAYING_MEMBER;
242             }
243          }
244         groupref = groupref->next;
245       }
246    }
247   else
248   if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && ip_addr_cmp (dest, &allsystems) && (group->group_address.addr != 0))
249    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a  query to a specific group using the allsystems address \n", __LINE__));
250
251      // we first need to re-lookup the group since we used dest last time
252      group = lookup_group (inp, &igmp->igmp_group_address);    // use the incoming IP address!
253      igmpstats.igmp_unicast_query++;
254
255      if ((IDLE_MEMBER == group->group_state ) || ((DELAYING_MEMBER == group->group_state  ) && (igmp->igmp_maxresp > group->timer)))
256       { igmp_start_timer (group, (igmp->igmp_maxresp)/2);
257         group->group_state = DELAYING_MEMBER;
258       }
259    }
260   else
261   if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && !(ip_addr_cmp (dest, &allsystems)) && (group->group_address.addr != 0))
262    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a  query to a specific group with the group address as destination \n", __LINE__));
263    
264      igmpstats.igmp_unicast_query++; /* This is the unicast query */
265      if ((IDLE_MEMBER == group->group_state  ) || ((DELAYING_MEMBER == group->group_state  ) && (igmp->igmp_maxresp > group->timer)))
266       { igmp_start_timer (group, (igmp->igmp_maxresp)/2);
267         group->group_state = DELAYING_MEMBER;
268       }
269    }
270   else
271   if (IGMP_V2_MEMB_REPORT == igmp->igmp_msgtype )
272    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got an IGMP_V2_MEMB_REPORT \n", __LINE__));
273    
274      igmpstats.report_rxed++;
275      if (DELAYING_MEMBER == group->group_state )
276       { // This is on a specific group we have already looked up
277         group->timer = 0;    //stopped
278         group->group_state = IDLE_MEMBER;
279         group->last_reporter_flag = 0;
280       }
281    }
282   else
283    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input, Line %x unexpected msg %x in state %x on group %x  at interface %x\n", __LINE__, (int) igmp->igmp_msgtype, (int) group->group_state, (int) &group, (int) group->interface));
284    }
285    
286   pbuf_free (p);
287   return;
288 }
289
290 /*-----------------------------------------------------------------------------
291  * igmp_joingroup
292  *----------------------------------------------------------------------------*/
293 err_t igmp_joingroup( struct netif *ifp, struct ip_addr *groupaddr)
294 { struct igmp_group *group;
295   
296   group = lookup_group (ifp, groupaddr);
297
298   if (group)
299    { // This should create a new group, check the state to make sure
300      if (group->group_state != NON_MEMBER)
301       { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x join to group not in state NON_MEMBER\n", __LINE__));
302         return ERR_OK;
303       }
304
305      // OK - it was new group
306      igmpstats.igmp_joins++;
307      
308      LWIP_DEBUGF(IGMP_DEBUG, ("igmp join to new group\n"));
309   
310      if (ifp->igmp_mac_filter!=NULL)
311       { ifp->igmp_mac_filter( ifp, groupaddr, IGMP_ADD_MAC_FILTER);
312       }
313      
314      igmp_send( group, IGMP_V2_MEMB_REPORT);
315      
316      igmp_start_timer( group, 5);
317      
318      // Need to work out where this timer comes from
319      group->group_state = DELAYING_MEMBER;
320      
321      return ERR_OK;
322    }
323   
324   return ERR_MEM;
325 }
326
327 /*-----------------------------------------------------------------------------
328  * igmp_leavegroup
329  *----------------------------------------------------------------------------*/
330 err_t igmp_leavegroup( struct netif *ifp, struct ip_addr *groupaddr)
331 { struct igmp_group *group;
332   
333   group = lookup_group (ifp, groupaddr);
334
335   if (group)
336    { // Only send a leave if the flag is set according to the state diagram
337      if (group->last_reporter_flag)
338       { LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x Leaving group\n", __LINE__));
339         igmpstats.igmp_leave_sent++;
340         igmp_send( group, IGMP_LEAVE_GROUP);
341       }
342       
343      // The block is not deleted since the group still exists and we may rejoin
344      group->last_reporter_flag = 0;
345      group->group_state        = NON_MEMBER;
346      group->timer              = 0;
347      
348      if (ifp->igmp_mac_filter!=NULL)
349       { ifp->igmp_mac_filter( ifp, groupaddr, IGMP_DEL_MAC_FILTER);
350       }
351      
352      return ERR_OK;
353    }
354
355   return ERR_MEM;
356 }
357
358 /*-----------------------------------------------------------------------------
359  * igmp_tick
360  *----------------------------------------------------------------------------*/
361 void igmp_tick(void *arg)
362 { struct igmp_group *group = GroupList;
363
364   arg = arg;
365
366   while (group)
367    { if (group->timer != 0)
368       { group->timer -=1;
369         if (group->timer == 0)
370          { igmp_timeout (group);
371          }
372       }
373      group = group->next;
374    }
375
376   // 100 millisecond tick handler
377   // go down the list of all groups here and check for timeouts
378   sys_timeout (IGMP_TICK, igmp_tick, NULL);
379 }
380
381 /*-----------------------------------------------------------------------------
382  * igmp_timeout
383  *----------------------------------------------------------------------------*/
384 void igmp_timeout( struct igmp_group *group)
385 { // If the state is DELAYING_MEMBER then we send a report for this group
386   LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c, got a timeout\n"));
387
388   if (DELAYING_MEMBER == group->group_state)
389    {  igmp_send( group, IGMP_V2_MEMB_REPORT);
390    }
391 }
392
393 /*-----------------------------------------------------------------------------
394  * igmp_start_timer
395  *----------------------------------------------------------------------------*/
396 void igmp_start_timer( struct igmp_group *group, u8_t max_time)
397 { // Important !! this should be random 0 -> max_time
398   // find out how to do this
399   group->timer = max_time;
400 }
401
402 /*-----------------------------------------------------------------------------
403  * igmp_stop_timer
404  *----------------------------------------------------------------------------*/
405 void igmp_stop_timer( struct igmp_group *group)
406 { group->timer = 0;
407 }
408
409 /*-----------------------------------------------------------------------------
410  * igmp_ip_output_if
411  * Sends an IP packet on a network interface. This function constructs the IP header
412  * and calculates the IP header checksum. If the source IP address is NULL,
413  * the IP address of the outgoing network interface is filled in as source address.
414  *----------------------------------------------------------------------------*/
415 err_t igmp_ip_output_if( struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t proto, struct netif *netif)
416 { static struct ip_hdr * iphdr = NULL;
417   static u16_t           ip_id = 0;
418   u16_t *                ra    = NULL;
419
420   // First write in the "router alert"
421   if (pbuf_header (p, ROUTER_ALERTLEN))
422    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
423      return ERR_BUF;
424    }
425
426   // This is the "router alert" option
427   ra    = p->payload;
428   ra[0] = htons (0x9404);
429   ra[1] = 0x0000;
430
431   // now the normal ip header
432   if (pbuf_header (p, IP_HLEN))
433    { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
434      return ERR_BUF;
435    }
436
437   iphdr = p->payload;
438   if (dest != IP_HDRINCL)
439    { iphdr->_ttl_proto = (proto<<8);
440      iphdr->_ttl_proto |= ttl;
441
442      /*  iphdr->dest = dest->addr; */
443      ip_addr_set (&(iphdr->dest), dest);
444 #ifdef HAVE_BITFIELDS
445      iphdr->_v_hl_tos |= ((IP_HLEN+ ROUTER_ALERTLEN)/4)<<16;
446      iphdr->_v_hl_tos |= 4<<24;
447 #else
448      iphdr->_v_hl_tos = (4 << 4) | ((IP_HLEN + ROUTER_ALERTLEN)/ 4 & 0xf);
449 #endif /* HAVE_BITFIELDS */
450
451      iphdr->_v_hl_tos |= 0;
452      iphdr->_len       = htons (p->tot_len);
453      iphdr->_offset    = htons (0);
454      iphdr->_id        = htons (ip_id++);
455
456      if (ip_addr_isany (src))
457       { ip_addr_set (&(iphdr->src), &(netif->ip_addr));
458       }
459      else
460       { ip_addr_set (&(iphdr->src), src);
461       }
462
463      iphdr->_chksum = 0;
464      iphdr->_chksum = inet_chksum (iphdr, IP_HLEN + ROUTER_ALERTLEN);
465    }
466   else
467    { dest = &(iphdr->dest);
468    }
469
470 #if IP_DEBUG
471   ip_debug_print (p);
472 #endif
473
474   LWIP_DEBUGF(IGMP_DEBUG, ("IGMP sending to netif %x \n", (int) netif));
475
476   return netif->output (netif, p, dest);
477 }
478
479 /*-----------------------------------------------------------------------------
480  * igmp_send
481  *----------------------------------------------------------------------------*/
482 void igmp_send( struct igmp_group *group, u8_t type)
483 { struct pbuf*    p    = NULL;
484   struct igmpmsg* igmp = NULL;
485   struct ip_addr  src  = {0};
486   struct ip_addr* dest = NULL;
487
488   /* IP header + IGMP header */
489   p = pbuf_alloc( PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
490   
491   if (p)
492    { igmp = p->payload;
493      ip_addr_set (&src, &((group->interface)->ip_addr));
494      
495      if (IGMP_V2_MEMB_REPORT == type)
496       { dest = &(group->group_address);
497         igmpstats.report_sent++;
498         ip_addr_set (&(igmp->igmp_group_address), &(group->group_address));
499         group->last_reporter_flag = 1; // Remember we were the last to report
500       }
501      else
502      if (IGMP_LEAVE_GROUP == type)
503       { dest = &allrouters;
504         ip_addr_set (&(igmp->igmp_group_address), &(group->group_address));
505       }
506
507      if ((IGMP_V2_MEMB_REPORT == type) || (IGMP_LEAVE_GROUP == type))
508       { igmp->igmp_msgtype  = type;
509         igmp->igmp_maxresp  = 0;
510         igmp->igmp_checksum = 0;
511         igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
512         
513         igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
514       }
515       
516      pbuf_free (p);
517    }
518   else
519    { LWIP_DEBUGF(IGMP_DEBUG, ("IGMP, not enough memory for igmp_send\n"));
520    }
521 }
522
523 #endif /* LWIP_IGMP */