2 * Copyright (c) 2002 CITEL Technologies Ltd.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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
35 /************************************************************
36 In the spirit of LW (I hope)
37 -------------------------------------------------------------
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
43 a debug print and statistic have been implemented to
45 -------------------------------------------------------------
46 -------------------------------------------------------------
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
52 a debug print and statistic have been implemented to
54 -------------------------------------------------------------
55 -------------------------------------------------------------
57 The router alert rfc 2113 is implemented in outgoing packets
58 but not checked rigorously incoming
59 -------------------------------------------------------------
61 ------------------------------------------------------------*/
63 /*-----------------------------------------------------------------------------
65 *----------------------------------------------------------------------------*/
67 #include "lwip/debug.h"
71 #include "lwip/inet.h"
72 #include "lwip/netif.h"
73 #include "lwip/icmp.h"
76 #include "lwip/stats.h"
77 #include "lwip/igmp.h"
79 #include "arch/perf.h"
83 /* IGMP support available? */
84 #if defined(LWIP_IGMP) && (LWIP_IGMP > 0)
86 /*-----------------------------------------------------------------------------
88 *----------------------------------------------------------------------------*/
90 static struct igmp_group* igmp_group_list;
91 static struct igmp_stats igmpstats;
93 static struct ip_addr allsystems;
94 static struct ip_addr allrouters;
97 * Initialize this module
99 * Only network interfaces registered when this function is called
105 struct igmp_group* group;
108 LWIP_DEBUGF(IGMP_DEBUG, ("IGMP Initialising\n"));
110 IP4_ADDR(&allsystems, 224, 0, 0, 1);
111 IP4_ADDR(&allrouters, 224, 0, 0, 2);
113 igmp_group_list = NULL;
114 memset(&igmpstats, 0, sizeof(igmpstats));
116 for (netif = netif_list; netif != NULL; netif = netif->next) {
117 group = lookup_group(netif, &allsystems);
120 group->group_state = IDLE_MEMBER;
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);
132 * Search for a group in the global igmp_group_list
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.
140 lookfor_group(struct netif *ifp, struct ip_addr *addr)
142 struct igmp_group *group = igmp_group_list;
145 if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
151 /* to be clearer, we return NULL here instead of
152 * 'group' (which is also NULL at this point).
158 * Search for a specific igmp group and create a new one if not found-
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.
166 lookup_group(struct netif *ifp, struct ip_addr *addr)
168 struct igmp_group *group = igmp_group_list;
170 /* Search if the group already exists */
171 group = lookfor_group(ifp, addr);
173 /* Group already exists. */
177 /* Group doesn't exist yet, create a new one. */
178 group = mem_malloc(sizeof(struct igmp_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;
187 igmp_group_list = group;
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));
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));
198 * Called from ip_input() if a new IGMP packet is received.
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
205 igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest)
207 struct ip_hdr * iphdr;
208 struct igmpmsg* igmp;
209 struct igmp_group* group;
210 struct igmp_group* groupref;
213 igmp = (struct igmpmsg *)(((u8_t *)p->payload)+((u32_t)(IPH_HL(iphdr) * 4)));
215 LWIP_DEBUGF(IGMP_DEBUG, ("igmp message to address %l \n", (long)dest->addr));
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 */
220 igmpstats.igmp_length_err++;
221 LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x igmp length error\n", __LINE__));
225 /* Now calculate and check the checksum */
226 if (inet_chksum(igmp, IGMP_MINLEN /*p->len*/)) {
228 igmpstats.igmp_checksum_err++;
229 LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp checksum error\n", __LINE__));
233 /* Packet is ok so find the group (or create a new one) */
234 group = lookup_group(inp, dest); /* use the incoming IP address! */
236 /* If group can be found or create... */
239 LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp allocation error\n", __LINE__));
243 /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
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"));
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__));
258 igmpstats.igmp_group_query_rxed++;
259 groupref = igmp_group_list;
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;
271 groupref = groupref->next;
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__));
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++;
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;
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__));
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;
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__));
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;
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));
320 * Join a group on one network interface.
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
327 igmp_joingroup(struct netif *ifp, struct ip_addr *groupaddr)
329 struct igmp_group *group;
331 group = lookup_group(ifp, groupaddr);
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__));
340 /* OK - it was new group */
341 igmpstats.igmp_joins++;
343 LWIP_DEBUGF(IGMP_DEBUG, ("igmp join to new group\n"));
345 if (ifp->igmp_mac_filter != NULL) {
346 ifp->igmp_mac_filter(ifp, groupaddr, IGMP_ADD_MAC_FILTER);
349 igmp_send(group, IGMP_V2_MEMB_REPORT);
351 igmp_start_timer(group, 5);
353 /* Need to work out where this timer comes from */
354 group->group_state = DELAYING_MEMBER;
363 * Leave a group on one network interface.
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
370 igmp_leavegroup(struct netif *ifp, struct ip_addr *groupaddr)
372 struct igmp_group *group;
374 group = lookup_group(ifp, groupaddr);
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);
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;
389 if (ifp->igmp_mac_filter != NULL) {
390 ifp->igmp_mac_filter(ifp, groupaddr, IGMP_DEL_MAC_FILTER);
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).
406 struct igmp_group *group = igmp_group_list;
409 if (group->timer != 0) {
411 if (group->timer == 0) {
420 * Called if a timeout for one group is reached.
421 * Sends a report for this group.
423 * @param group an igmp_group for which a timeout is reached
426 igmp_timeout(struct igmp_group *group)
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"));
431 if (DELAYING_MEMBER == group->group_state) {
432 igmp_send(group, IGMP_V2_MEMB_REPORT);
437 * Start a timer for an igmp group
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())
444 igmp_start_timer(struct igmp_group *group, u8_t max_time)
446 /* Important !! this should be random 0 -> max_time
447 * find out how to do this
449 group->timer = max_time;
453 * Stop a timer for an igmp_group
455 * @param group the igmp_group for which to stop the timer
458 igmp_stop_timer(struct igmp_group *group)
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.
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
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)
485 static struct ip_hdr * iphdr = NULL;
486 static u16_t ip_id = 0;
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"));
495 /* This is the "router alert" option */
497 ra[0] = htons (0x9404);
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"));
507 if (dest != IP_HDRINCL) {
508 iphdr->_ttl_proto = (proto<<8);
509 iphdr->_ttl_proto |= ttl;
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;
517 iphdr->_v_hl_tos = (4 << 4) | ((IP_HLEN + ROUTER_ALERTLEN)/ 4 & 0xf);
518 #endif /* HAVE_BITFIELDS */
520 iphdr->_v_hl_tos |= 0;
521 iphdr->_len = htons(p->tot_len);
522 iphdr->_offset = htons(0);
523 iphdr->_id = htons(ip_id++);
525 if (ip_addr_isany(src)) {
526 ip_addr_set(&(iphdr->src), &(netif->ip_addr));
528 ip_addr_set(&(iphdr->src), src);
532 iphdr->_chksum = inet_chksum(iphdr, IP_HLEN + ROUTER_ALERTLEN);
534 dest = &(iphdr->dest);
541 LWIP_DEBUGF(IGMP_DEBUG, ("IGMP sending to netif %x \n", (int) netif));
543 return netif->output(netif, p, dest);
547 * Send an igmp packet to a specific group.
549 * @param the group to which to send the packet
550 * @param type the type of igmp packet to send
553 igmp_send(struct igmp_group *group, u8_t type)
555 struct pbuf* p = NULL;
556 struct igmpmsg* igmp = NULL;
557 struct ip_addr src = {0};
558 struct ip_addr* dest = NULL;
560 /* IP header + IGMP header */
561 p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
565 ip_addr_set(&src, &((group->interface)->ip_addr));
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 */
573 if (IGMP_LEAVE_GROUP == type) {
575 ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
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);
585 igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
590 LWIP_DEBUGF(IGMP_DEBUG, ("IGMP, not enough memory for igmp_send\n"));
594 #endif /* LWIP_IGMP */