]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv4/igmp.c
Minor fix. Add "static" to IGMP's global variables, and rename group_list to igmp_gro...
[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 static struct igmp_group* igmp_group_list;
91 static struct igmp_stats  igmpstats;
92
93 static struct ip_addr     allsystems;
94 static struct ip_addr     allrouters;
95
96 /**
97  * Initialize this module
98  *
99  * Only network interfaces registered when this function is called
100  * are igmp-enabled.
101  */
102 void
103 igmp_init(void)
104 {
105   struct igmp_group* group;
106   struct netif*      netif;
107
108   LWIP_DEBUGF(IGMP_DEBUG, ("IGMP Initialising\n"));
109
110   IP4_ADDR(&allsystems, 224, 0, 0, 1);
111   IP4_ADDR(&allrouters, 224, 0, 0, 2);
112
113   igmp_group_list = NULL;
114   memset(&igmpstats, 0, sizeof(igmpstats));
115
116   for (netif = netif_list; netif != NULL; netif = netif->next) {
117     group = lookup_group(netif, &allsystems);
118       
119     if (group) {
120       group->group_state = IDLE_MEMBER;
121
122       /* Allow the igmp messages at the MAC level */
123       if (netif->igmp_mac_filter != NULL) {
124         netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
125         netif->igmp_mac_filter(netif, &allrouters, IGMP_ADD_MAC_FILTER);
126       }
127     }
128   }
129 }
130
131 /**
132  * Search for a group in the global igmp_group_list
133  *
134  * @param ifp the network interfacefor which to look
135  * @param addr the group ip address to search
136  * @return a struct igmp_group* if the group has been found,
137  *         NULL if the group wasn't found.
138  */
139 struct igmp_group *
140 lookfor_group(struct netif *ifp, struct ip_addr *addr)
141 {
142   struct igmp_group *group = igmp_group_list;
143
144   while (group) {
145     if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
146       return group;
147     }
148     group = group->next;
149   }
150
151   /* to be clearer, we return NULL here instead of
152    * 'group' (which is also NULL at this point).
153    */
154   return NULL;
155 }
156
157 /**
158  * Search for a specific igmp group and create a new one if not found-
159  *
160  * @param ifp the network interfacefor which to look
161  * @param addr the group ip address to search
162  * @return a struct igmp_group*,
163  *         NULL on memory error.
164  */
165 struct igmp_group *
166 lookup_group(struct netif *ifp, struct ip_addr *addr)
167 {
168   struct igmp_group *group = igmp_group_list;
169   
170   /* Search if the group already exists */
171   group = lookfor_group(ifp, addr);
172   if (group != NULL) {
173     /* Group already exists. */
174     return group;
175   }
176
177   /* Group doesn't exist yet, create a new one. */
178   group = mem_malloc(sizeof(struct igmp_group));
179   if (group) {
180     group->interface          = ifp;
181     ip_addr_set(&(group->group_address), addr);
182     group->timer              = 0; /* Not running */
183     group->group_state        = NON_MEMBER;
184     group->last_reporter_flag = 0;
185     group->next               = igmp_group_list;
186
187     igmp_group_list = group;
188      
189     LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d  allocated a new group with address %x on if %x \n", __LINE__, (int) addr, (int) ifp));
190   } else {
191     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));
192   }
193
194   return group;
195 }
196
197 /**
198  * Called from ip_input() if a new IGMP packet is received.
199  *
200  * @param p received igmp packet, p->payload pointing to the ip header
201  * @param inp network interface on which the packet was received
202  * @param dest destination ip address of the igmp packet
203  */
204 void
205 igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest)
206 {
207   struct ip_hdr *    iphdr;
208   struct igmpmsg*    igmp;
209   struct igmp_group* group;
210   struct igmp_group* groupref;
211
212   iphdr = p->payload;  
213   igmp  = (struct igmpmsg *)(((u8_t *)p->payload)+((u32_t)(IPH_HL(iphdr) * 4)));
214
215   LWIP_DEBUGF(IGMP_DEBUG, ("igmp message to address %l \n", (long)dest->addr));
216
217   if (p->len < IGMP_MINLEN) {
218     /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
219     pbuf_free(p);
220     igmpstats.igmp_length_err++;
221     LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x igmp length error\n", __LINE__));
222     return;
223   }
224
225   /* Now calculate and check the checksum */
226   if (inet_chksum(igmp, IGMP_MINLEN /*p->len*/)) {
227     pbuf_free(p);
228     igmpstats.igmp_checksum_err++;
229     LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp checksum error\n", __LINE__));
230     return;
231   }
232
233   /* Packet is ok so find the group (or create a new one) */
234   group = lookup_group(inp, dest); /* use the incoming IP address! */
235   
236   /* If group can be found or create... */
237   if (!group) {
238     pbuf_free(p);
239     LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp allocation error\n", __LINE__));
240     return;
241   }
242
243   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
244
245   /* The membership query message goes to the all groups address */
246   /* and it control block does not have state */
247   if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && (ip_addr_cmp(dest, &allsystems)) &&
248       (igmp->igmp_group_address.addr == 0)) {
249     /* THIS IS THE GENERAL QUERY */
250     LWIP_DEBUGF(IGMP_DEBUG, ("General IGMP_MEMB_QUERY on ALL SYSTEMS ADDRESS 224.0.0.1\n"));
251
252     if (0 ==igmp->igmp_maxresp) {
253       igmpstats.igmp_v1_rxed++;
254       igmp->igmp_maxresp = 10;
255       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__));
256     }
257
258     igmpstats.igmp_group_query_rxed++;
259     groupref = igmp_group_list;
260     while (groupref) {
261       if ((groupref->interface == inp) &&
262           (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
263         /* Do not send messages on the all systems group address! */
264         if ((groupref->group_state == IDLE_MEMBER) ||
265             ((groupref->group_state == DELAYING_MEMBER) &&
266              (igmp->igmp_maxresp > groupref->timer))) {
267           igmp_start_timer(groupref, (igmp->igmp_maxresp)/2);
268           groupref->group_state = DELAYING_MEMBER;
269         }
270       }
271       groupref = groupref->next;
272     }
273   } else {
274     if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && ip_addr_cmp (dest, &allsystems) &&
275         (group->group_address.addr != 0)) {
276       LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a  query to a specific group using the allsystems address \n", __LINE__));
277
278       /* we first need to re-lookup the group since we used dest last time */
279       group = lookup_group(inp, &igmp->igmp_group_address); /* use the incoming IP address! */
280       igmpstats.igmp_unicast_query++;
281
282       if ((IDLE_MEMBER == group->group_state ) || ((DELAYING_MEMBER == group->group_state) &&
283           (igmp->igmp_maxresp > group->timer))) {
284         igmp_start_timer(group, (igmp->igmp_maxresp)/2);
285         group->group_state = DELAYING_MEMBER;
286       }
287     } else {
288       if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && !(ip_addr_cmp (dest, &allsystems)) &&
289           (group->group_address.addr != 0)) {
290         LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a  query to a specific group with the group address as destination \n", __LINE__));
291
292         igmpstats.igmp_unicast_query++; /* This is the unicast query */
293         if ((IDLE_MEMBER == group->group_state) || ((DELAYING_MEMBER == group->group_state) &&
294             (igmp->igmp_maxresp > group->timer))) {
295           igmp_start_timer(group, (igmp->igmp_maxresp)/2);
296           group->group_state = DELAYING_MEMBER;
297         }
298       } else {
299         if (IGMP_V2_MEMB_REPORT == igmp->igmp_msgtype) {
300           LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got an IGMP_V2_MEMB_REPORT \n", __LINE__));
301
302           igmpstats.report_rxed++;
303           if (DELAYING_MEMBER == group->group_state) {
304             /* This is on a specific group we have already looked up */
305             group->timer = 0; /* stopped */
306             group->group_state = IDLE_MEMBER;
307             group->last_reporter_flag = 0;
308           }
309         } else {
310           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));
311         }
312       }
313     }
314   }
315   pbuf_free(p);
316   return;
317 }
318
319 /**
320  * Join a group on one network interface.
321  *
322  * @param ifp the network interface which should join a new group
323  * @param groupaddr the ip address of the group which to join
324  * @return ERR_OK if group was joined, an err_t otherwise
325  */
326 err_t
327 igmp_joingroup(struct netif *ifp, struct ip_addr *groupaddr)
328 {
329   struct igmp_group *group;
330
331   group = lookup_group(ifp, groupaddr);
332
333   if (group) {
334     /* This should create a new group, check the state to make sure */
335     if (group->group_state != NON_MEMBER) {
336       LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x join to group not in state NON_MEMBER\n", __LINE__));
337       return ERR_OK;
338     }
339
340     /* OK - it was new group */
341     igmpstats.igmp_joins++;
342
343     LWIP_DEBUGF(IGMP_DEBUG, ("igmp join to new group\n"));
344
345     if (ifp->igmp_mac_filter != NULL) {
346       ifp->igmp_mac_filter(ifp, groupaddr, IGMP_ADD_MAC_FILTER);
347     }
348
349     igmp_send(group, IGMP_V2_MEMB_REPORT);
350
351     igmp_start_timer(group, 5);
352
353     /* Need to work out where this timer comes from */
354     group->group_state = DELAYING_MEMBER;
355
356     return ERR_OK;
357   }
358
359   return ERR_MEM;
360 }
361
362 /**
363  * Leave a group on one network interface.
364  *
365  * @param ifp the network interface which should leave a group
366  * @param groupaddr the ip address of the group which to leave
367  * @return ERR_OK if group was left, an err_t otherwise
368  */
369 err_t
370 igmp_leavegroup(struct netif *ifp, struct ip_addr *groupaddr)
371 {
372   struct igmp_group *group;
373
374   group = lookup_group(ifp, groupaddr);
375
376   if (group) {
377     /* Only send a leave if the flag is set according to the state diagram */
378     if (group->last_reporter_flag) {
379       LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x Leaving group\n", __LINE__));
380       igmpstats.igmp_leave_sent++;
381       igmp_send(group, IGMP_LEAVE_GROUP);
382     }
383
384     /* The block is not deleted since the group still exists and we may rejoin */
385     group->last_reporter_flag = 0;
386     group->group_state        = NON_MEMBER;
387     group->timer              = 0;
388
389     if (ifp->igmp_mac_filter != NULL) {
390       ifp->igmp_mac_filter(ifp, groupaddr, IGMP_DEL_MAC_FILTER);
391     }
392
393     return ERR_OK;
394   }
395
396   return ERR_MEM;
397 }
398
399 /**
400  * The igmp timer function (both for NO_SYS=1 and =0)
401  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
402  */
403 void
404 igmp_tmr()
405 {
406   struct igmp_group *group = igmp_group_list;
407
408   while (group) {
409     if (group->timer != 0) {
410       group->timer -= 1;
411       if (group->timer == 0) {
412         igmp_timeout(group);
413       }
414     }
415     group = group->next;
416   }
417 }
418
419 /**
420  * Called if a timeout for one group is reached.
421  * Sends a report for this group.
422  *
423  * @param group an igmp_group for which a timeout is reached
424  */
425 void
426 igmp_timeout(struct igmp_group *group)
427 {
428   /* If the state is DELAYING_MEMBER then we send a report for this group */
429   LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c, got a timeout\n"));
430
431   if (DELAYING_MEMBER == group->group_state) {
432     igmp_send(group, IGMP_V2_MEMB_REPORT);
433   }
434 }
435
436 /**
437  * Start a timer for an igmp group
438  *
439  * @param group the igmp_group for which to start a timer
440  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
441  *        every call to igmp_tmr())
442  */
443 void
444 igmp_start_timer(struct igmp_group *group, u8_t max_time)
445 {
446   /* Important !! this should be random 0 -> max_time
447    * find out how to do this
448    */
449   group->timer = max_time;
450 }
451
452 /**
453  * Stop a timer for an igmp_group
454  *
455  * @param group the igmp_group for which to stop the timer
456  */
457 void
458 igmp_stop_timer(struct igmp_group *group)
459 {
460   group->timer = 0;
461 }
462
463 /**
464  * Sends an IP packet on a network interface. This function constructs the IP header
465  * and calculates the IP header checksum. If the source IP address is NULL,
466  * the IP address of the outgoing network interface is filled in as source address.
467  *
468  * @param p the packet to send (p->payload points to the data, e.g. next
469             protocol header; if dest == IP_HDRINCL, p already includes an IP
470             header and p->payload points to that IP header)
471  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
472  *         IP  address of the netif used to send is used as source address)
473  * @param dest the destination IP address to send the packet to
474  * @param ttl the TTL value to be set in the IP header
475  * @param proto the PROTOCOL to be set in the IP header
476  * @param netif the netif on which to send this packet
477  * @return ERR_OK if the packet was sent OK
478  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
479  *         returns errors returned by netif->output
480  */
481 err_t
482 igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
483                   u8_t ttl, u8_t proto, struct netif *netif)
484 {
485   static struct ip_hdr * iphdr = NULL;
486   static u16_t           ip_id = 0;
487   u16_t *                ra    = NULL;
488
489   /* First write in the "router alert" */
490   if (pbuf_header(p, ROUTER_ALERTLEN)) {
491     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
492     return ERR_BUF;
493   }
494
495   /* This is the "router alert" option */
496   ra    = p->payload;
497   ra[0] = htons (0x9404);
498   ra[1] = 0x0000;
499
500   /* now the normal ip header */
501   if (pbuf_header(p, IP_HLEN)) {
502     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
503     return ERR_BUF;
504   }
505
506   iphdr = p->payload;
507   if (dest != IP_HDRINCL) {
508     iphdr->_ttl_proto = (proto<<8);
509     iphdr->_ttl_proto |= ttl;
510
511     /*  iphdr->dest = dest->addr; */
512     ip_addr_set(&(iphdr->dest), dest);
513 #ifdef HAVE_BITFIELDS
514     iphdr->_v_hl_tos |= ((IP_HLEN+ ROUTER_ALERTLEN)/4)<<16;
515     iphdr->_v_hl_tos |= 4<<24;
516 #else
517     iphdr->_v_hl_tos = (4 << 4) | ((IP_HLEN + ROUTER_ALERTLEN)/ 4 & 0xf);
518 #endif /* HAVE_BITFIELDS */
519
520     iphdr->_v_hl_tos |= 0;
521     iphdr->_len       = htons(p->tot_len);
522     iphdr->_offset    = htons(0);
523     iphdr->_id        = htons(ip_id++);
524
525     if (ip_addr_isany(src)) {
526       ip_addr_set(&(iphdr->src), &(netif->ip_addr));
527     } else {
528       ip_addr_set(&(iphdr->src), src);
529     }
530
531     iphdr->_chksum = 0;
532     iphdr->_chksum = inet_chksum(iphdr, IP_HLEN + ROUTER_ALERTLEN);
533   } else {
534     dest = &(iphdr->dest);
535   }
536
537 #if IP_DEBUG
538   ip_debug_print(p);
539 #endif
540
541   LWIP_DEBUGF(IGMP_DEBUG, ("IGMP sending to netif %x \n", (int) netif));
542
543   return netif->output(netif, p, dest);
544 }
545
546 /**
547  * Send an igmp packet to a specific group.
548  *
549  * @param the group to which to send the packet
550  * @param type the type of igmp packet to send
551  */
552 void
553 igmp_send(struct igmp_group *group, u8_t type)
554 {
555   struct pbuf*    p    = NULL;
556   struct igmpmsg* igmp = NULL;
557   struct ip_addr  src  = {0};
558   struct ip_addr* dest = NULL;
559
560   /* IP header + IGMP header */
561   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
562   
563   if (p) {
564     igmp = p->payload;
565     ip_addr_set(&src, &((group->interface)->ip_addr));
566      
567     if (IGMP_V2_MEMB_REPORT == type) {
568       dest = &(group->group_address);
569       igmpstats.report_sent++;
570       ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
571       group->last_reporter_flag = 1; /* Remember we were the last to report */
572     } else {
573       if (IGMP_LEAVE_GROUP == type) {
574         dest = &allrouters;
575         ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
576       }
577     }
578
579    if ((IGMP_V2_MEMB_REPORT == type) || (IGMP_LEAVE_GROUP == type)) {
580      igmp->igmp_msgtype  = type;
581      igmp->igmp_maxresp  = 0;
582      igmp->igmp_checksum = 0;
583      igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
584
585      igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
586    }
587     
588    pbuf_free (p);
589   } else {
590     LWIP_DEBUGF(IGMP_DEBUG, ("IGMP, not enough memory for igmp_send\n"));
591   }
592 }
593
594 #endif /* LWIP_IGMP */