Index: igmp.c =================================================================== RCS file: /sources/lwip/lwip/src/core/ipv4/igmp.c,v retrieving revision 1.9 diff -u -r1.9 igmp.c --- igmp.c 3 Jul 2007 20:28:36 -0000 1.9 +++ igmp.c 17 Jul 2007 06:24:41 -0000 @@ -1,3 +1,9 @@ +/** + * @file + * + * IGMP - Internet Group Management Protocol + */ + /* * Copyright (c) 2002 CITEL Technologies Ltd. * All rights reserved. @@ -60,6 +66,63 @@ Steve Reynolds ------------------------------------------------------------*/ +/************************************************************ +Additional changes +------------------------------------------------------------- +- NOTES: This only implements "host" function + Interface should be up before joining groups + +- Removed igmp_init() from tcpip_thread() function and move it to tcpip_init(). + [It should work either way but it seemed a better fit.] + +- Reworked igmp.c to allow for different group list per interface. + [This required adding a igmp_group_list to the netif structure (netif.h).] + +- Reworked igmp.c so debug reporting was formatted like other lwip files. + [I hope don't offended anyone.] + +- Reworked igmp.c so all function names all begin with "igmp". + +- netif_add() now initializes igmp_mac_filter to NULL + [as well as the new field igmp_group_list.] + +- igmp_joingroup() now checks to make sure you only add multicast addresses + to the list. + +- Added group_state member DEAD_MEMBER to separate groups that are being + initialized to those that are stale. [This allow correct building of hash + key as we may not deleting records] + +- Removed igmp_mac_filter() to allrouters group. + [I don't think this needed in V2] + +- In igmp_input, changed igmp_lookup_group() to igmp_lookfor_group. + [If it's not for us, we don't want it!] + +- igmp_leavegroup() can now delete memory used by the group. + Use LWIP_IGMP_FREE_GROUPS to enable this feature + +TODO: It looks like the delay from igmp_maxresp is just cut in two and used + for delay where it should be a random number between 0 and igmp_maxresp. + See igmp_start_timer + + +------------------------------------------------------------- +Bill Florac 7-17-2007 +------------------------------------------------------------*/ + +/* + Relevant RFC's + RFC 988 (obsolete) V0 + RFC 1112 (updated..STD-5) V1 + RFC 2236 (obsoleted) V2 <- this code is based on this RFC + RFC 3376 (updated) V3 + RFC 4604 V3+ + + RFC 2113 + */ + + /*----------------------------------------------------------------------------- * Includes *----------------------------------------------------------------------------*/ @@ -87,7 +150,7 @@ * Globales *----------------------------------------------------------------------------*/ -static struct igmp_group* igmp_group_list; +// TODO: Should we have stats per netif? static struct igmp_stats igmpstats; static struct ip_addr allsystems; @@ -98,6 +161,10 @@ * * Only network interfaces registered when this function is called * are igmp-enabled. + * + * This will enable igmp on all interface. In the current implementation it + * is not possible to have igmp on one interface but not the other. However + * you can join different groups on a per interface level */ void igmp_init(void) @@ -105,16 +172,16 @@ struct igmp_group* group; struct netif* netif; - LWIP_DEBUGF(IGMP_DEBUG, ("IGMP Initialising\n")); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); IP4_ADDR(&allsystems, 224, 0, 0, 1); IP4_ADDR(&allrouters, 224, 0, 0, 2); - igmp_group_list = NULL; + /* Clear stats*/ memset(&igmpstats, 0, sizeof(igmpstats)); for (netif = netif_list; netif != NULL; netif = netif->next) { - group = lookup_group(netif, &allsystems); + group = igmp_lookup_group(netif, &allsystems); if (group) { group->group_state = IDLE_MEMBER; @@ -122,27 +189,26 @@ /* Allow the igmp messages at the MAC level */ if (netif->igmp_mac_filter != NULL) { netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); - netif->igmp_mac_filter(netif, &allrouters, IGMP_ADD_MAC_FILTER); } } } } /** - * Search for a group in the global igmp_group_list + * Search for a group in the netif igmp_group_list * - * @param ifp the network interfacefor which to look - * @param addr the group ip address to search - * @return a struct igmp_group* if the group has been found, + * @param ifp The network interfacefor which to look + * @param addr The group ip address to search for + * @return A struct igmp_group* if the group has been found, * NULL if the group wasn't found. */ struct igmp_group * -lookfor_group(struct netif *ifp, struct ip_addr *addr) +igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr) { - struct igmp_group *group = igmp_group_list; + struct igmp_group *group = ifp->igmp_group_list; while (group) { - if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + if (ip_addr_cmp(&(group->group_address), addr)) { return group; } group = group->next; @@ -163,12 +229,12 @@ * NULL on memory error. */ struct igmp_group * -lookup_group(struct netif *ifp, struct ip_addr *addr) +igmp_lookup_group(struct netif *ifp, struct ip_addr *addr) { - struct igmp_group *group = igmp_group_list; + struct igmp_group *group = ifp->igmp_group_list; /* Search if the group already exists */ - group = lookfor_group(ifp, addr); + group = igmp_lookfor_group(ifp, addr); if (group != NULL) { /* Group already exists. */ return group; @@ -182,13 +248,13 @@ group->timer = 0; /* Not running */ group->group_state = NON_MEMBER; group->last_reporter_flag = 0; - group->next = igmp_group_list; + group->next = ifp->igmp_group_list; - igmp_group_list = group; + ifp->igmp_group_list = group; - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d allocated a new group with address %x on if %x \n", __LINE__, (int) addr, (int) ifp)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: allocated a new group with address %x on if %x \n", (int) addr, (int) ifp)); } else { - 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)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: impossible to allocated a new group with address %x on if %x \n", (int) addr, (int) ifp)); } return group; @@ -212,13 +278,13 @@ iphdr = p->payload; igmp = (struct igmpmsg *)(((u8_t *)p->payload)+((u32_t)(IPH_HL(iphdr) * 4))); - LWIP_DEBUGF(IGMP_DEBUG, ("igmp message to address %l \n", (long)dest->addr)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message to address %l \n", (long)dest->addr)); if (p->len < IGMP_MINLEN) { /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ pbuf_free(p); igmpstats.igmp_length_err++; - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x igmp length error\n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); return; } @@ -226,17 +292,17 @@ if (inet_chksum(igmp, IGMP_MINLEN /*p->len*/)) { pbuf_free(p); igmpstats.igmp_checksum_err++; - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp checksum error\n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); return; } /* Packet is ok so find the group (or create a new one) */ - group = lookup_group(inp, dest); /* use the incoming IP address! */ + group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */ /* If group can be found or create... */ if (!group) { pbuf_free(p); - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %d igmp allocation error\n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); return; } @@ -247,19 +313,18 @@ if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && (ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) { /* THIS IS THE GENERAL QUERY */ - LWIP_DEBUGF(IGMP_DEBUG, ("General IGMP_MEMB_QUERY on ALL SYSTEMS ADDRESS 224.0.0.1\n")); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on ALL SYSTEMS ADDRESS 224.0.0.1\n")); if (0 ==igmp->igmp_maxresp) { igmpstats.igmp_v1_rxed++; igmp->igmp_maxresp = 10; - 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__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); } igmpstats.igmp_group_query_rxed++; - groupref = igmp_group_list; + groupref = inp->igmp_group_list; while (groupref) { - if ((groupref->interface == inp) && - (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + if (!(ip_addr_cmp(&(groupref->group_address), &allsystems))) { /* Do not send messages on the all systems group address! */ if ((groupref->group_state == IDLE_MEMBER) || ((groupref->group_state == DELAYING_MEMBER) && @@ -273,21 +338,23 @@ } else { if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && ip_addr_cmp (dest, &allsystems) && (group->group_address.addr != 0)) { - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a query to a specific group using the allsystems address \n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got a query to a specific group using the allsystems address \n")); /* we first need to re-lookup the group since we used dest last time */ - group = lookup_group(inp, &igmp->igmp_group_address); /* use the incoming IP address! */ - igmpstats.igmp_unicast_query++; - - if ((IDLE_MEMBER == group->group_state ) || ((DELAYING_MEMBER == group->group_state) && - (igmp->igmp_maxresp > group->timer))) { - igmp_start_timer(group, (igmp->igmp_maxresp)/2); - group->group_state = DELAYING_MEMBER; + group = igmp_lookfor_group(inp, &igmp->igmp_group_address); /* use the incoming IP address! */ + if (group) { + igmpstats.igmp_unicast_query++; + + if ((IDLE_MEMBER == group->group_state ) || ((DELAYING_MEMBER == group->group_state) && + (igmp->igmp_maxresp > group->timer))) { + igmp_start_timer(group, (igmp->igmp_maxresp)/2); + group->group_state = DELAYING_MEMBER; + } } } else { if ((IGMP_MEMB_QUERY == igmp->igmp_msgtype) && !(ip_addr_cmp (dest, &allsystems)) && (group->group_address.addr != 0)) { - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got a query to a specific group with the group address as destination \n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got a query to a specific group with the group address as destination \n")); igmpstats.igmp_unicast_query++; /* This is the unicast query */ if ((IDLE_MEMBER == group->group_state) || ((DELAYING_MEMBER == group->group_state) && @@ -297,7 +364,7 @@ } } else { if (IGMP_V2_MEMB_REPORT == igmp->igmp_msgtype) { - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c,Line %x got an IGMP_V2_MEMB_REPORT \n", __LINE__)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an IGMP_V2_MEMB_REPORT \n")); igmpstats.report_rxed++; if (DELAYING_MEMBER == group->group_state) { @@ -307,7 +374,8 @@ group->last_reporter_flag = 0; } } else { - 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)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %x in state %x on group %x at interface %x\n", + (int) igmp->igmp_msgtype, (int) group->group_state, (int) &group, (int) group->interface)); } } } @@ -328,19 +396,29 @@ { struct igmp_group *group; - group = lookup_group(ifp, groupaddr); + /* make sure it is multicast address */ + if (!ip_addr_ismulticast(groupaddr)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: attempt to join non-multicast address\n")); + return ERR_VAL; + } + + /* find group or create a new one if not found */ + group = igmp_lookup_group(ifp, groupaddr); if (group) { /* This should create a new group, check the state to make sure */ - if (group->group_state != NON_MEMBER) { - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x join to group not in state NON_MEMBER\n", __LINE__)); + if ((group->group_state =! NON_MEMBER) && (group->group_state =! DEAD_MEMBER)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state NON_MEMBER\n")); return ERR_OK; } /* OK - it was new group */ + group->group_state = NON_MEMBER; igmpstats.igmp_joins++; - LWIP_DEBUGF(IGMP_DEBUG, ("igmp join to new group\n")); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); if (ifp->igmp_mac_filter != NULL) { ifp->igmp_mac_filter(ifp, groupaddr, IGMP_ADD_MAC_FILTER); @@ -369,22 +447,60 @@ err_t igmp_leavegroup(struct netif *ifp, struct ip_addr *groupaddr) { - struct igmp_group *group; + struct igmp_group *group = ifp->igmp_group_list; + struct igmp_group *last_group = NULL; + + SYS_ARCH_DECL_PROTECT(old_level); - group = lookup_group(ifp, groupaddr); + /* make sure it is multicast address */ + if (!ip_addr_ismulticast(groupaddr)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: attempt to leave non-multicast address\n")); + return ERR_VAL; + } + /* find group */ + //group = igmp_lookfor_group(ifp, groupaddr); + while (group) { + if (ip_addr_cmp(&(group->group_address), groupaddr)) { + break; + } + if (group->next) { + last_group = group; + } + group = group->next; + } if (group) { /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); if (group->last_reporter_flag) { - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c Line %x Leaving group\n", __LINE__)); + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group")); igmpstats.igmp_leave_sent++; igmp_send(group, IGMP_LEAVE_GROUP); } /* The block is not deleted since the group still exists and we may rejoin */ - group->last_reporter_flag = 0; - group->group_state = NON_MEMBER; - group->timer = 0; + #if LWIP_IGMP_FREE_GROUPS + // make sure no one uses this + SYS_ARCH_PROTECT(old_level); + // first one in chain? + if (!last_group) { + // point netif to it (may be NULL) + ifp->igmp_group_list = group->next; + } else { + // the point last_group around this one (may be NULL) + last_group->next = group->next; + } + // Now we can free it + mem_free((void *)group); + SYS_ARCH_UNPROTECT(old_level); + #else + group->last_reporter_flag = 0; + group->group_state = DEAD_MEMBER; // flag as not in use + group->timer = 0; + #endif if (ifp->igmp_mac_filter != NULL) { ifp->igmp_mac_filter(ifp, groupaddr, IGMP_DEL_MAC_FILTER); @@ -393,26 +509,33 @@ return ERR_OK; } - return ERR_MEM; + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group")); + + return ERR_VAL; } /** * The igmp timer function (both for NO_SYS=1 and =0) * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). */ +// TODO: should timer be different for each netif? void -igmp_tmr() +igmp_tmr(void) { - struct igmp_group *group = igmp_group_list; + struct netif* netif; + struct igmp_group* group; - while (group) { - if (group->timer != 0) { - group->timer -= 1; - if (group->timer == 0) { - igmp_timeout(group); + for (netif = netif_list; netif != NULL; netif = netif->next) { + group = netif->igmp_group_list; + while (group) { + if (group->timer != 0) { + group->timer -= 1; + if (group->timer == 0) { + igmp_timeout(group); + } } + group = group->next; } - group = group->next; } } @@ -426,7 +549,7 @@ igmp_timeout(struct igmp_group *group) { /* If the state is DELAYING_MEMBER then we send a report for this group */ - LWIP_DEBUGF(IGMP_DEBUG, ("igmp.c, got a timeout\n")); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: got a timeout\n")); if (DELAYING_MEMBER == group->group_state) { igmp_send(group, IGMP_V2_MEMB_REPORT); @@ -538,7 +661,7 @@ ip_debug_print(p); #endif - LWIP_DEBUGF(IGMP_DEBUG, ("IGMP sending to netif %x \n", (int) netif)); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: sending to netif %x \n", (int) netif)); return netif->output(netif, p, dest); } @@ -562,7 +685,7 @@ if (p) { igmp = p->payload; - LWIP_ASSERT("check that first pbuf can hold struct igmpmsg", + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmpmsg", (p->len >= sizeof(struct igmpmsg))); ip_addr_set(&src, &((group->interface)->ip_addr)); @@ -589,7 +712,7 @@ pbuf_free (p); } else { - LWIP_DEBUGF(IGMP_DEBUG, ("IGMP, not enough memory for igmp_send\n")); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); } }