--- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -251,6 +251,7 @@ int activateRoute(uint32_t group, uint32 void ageActiveRoutes(); void setRouteLastMemberMode(uint32_t group); int lastMemberGroupAge(uint32_t group); +int interfaceInRoute(int32_t group, int Ix); /* request.c */ --- a/src/request.c +++ b/src/request.c @@ -41,10 +41,10 @@ // Prototypes... void sendGroupSpecificMemberQuery(void *argument); - + typedef struct { uint32_t group; - uint32_t vifAddr; + // uint32_t vifAddr; short started; } GroupVifDesc; @@ -142,7 +142,7 @@ void acceptLeaveMessage(uint32_t src, ui // Call the group spesific membership querier... gvDesc->group = group; - gvDesc->vifAddr = sourceVif->InAdr.s_addr; + // gvDesc->vifAddr = sourceVif->InAdr.s_addr; gvDesc->started = 0; sendGroupSpecificMemberQuery(gvDesc); @@ -159,6 +159,9 @@ void acceptLeaveMessage(uint32_t src, ui */ void sendGroupSpecificMemberQuery(void *argument) { struct Config *conf = getCommonConfig(); + struct IfDesc *Dp; + struct RouteTable *croute; + int Ix; // Cast argument to correct type... GroupVifDesc *gvDesc = (GroupVifDesc*) argument; @@ -166,22 +169,38 @@ void sendGroupSpecificMemberQuery(void * if(gvDesc->started) { // If aging returns false, we don't do any further action... if(!lastMemberGroupAge(gvDesc->group)) { + // FIXME: Should we free gvDesc here? return; } } else { gvDesc->started = 1; } - // Send a group specific membership query... - sendIgmp(gvDesc->vifAddr, gvDesc->group, - IGMP_MEMBERSHIP_QUERY, - conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, - gvDesc->group, 0); - - my_log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", - inetFmt(gvDesc->vifAddr,s1), inetFmt(gvDesc->group,s2), - conf->lastMemberQueryInterval); - + /** + * FIXME: This loops through all interfaces the group is active on an sends queries. + * It might be better to send only a query on the interface the leave was accepted on and remove only that interface from the route. + */ + + // Loop through all downstream interfaces + for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { + if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { + if(Dp->state == IF_STATE_DOWNSTREAM) { + // Is that interface used in the group? + if (interfaceInRoute(gvDesc->group ,Dp->index)) { + + // Send a group specific membership query... + sendIgmp(Dp->InAdr.s_addr, gvDesc->group, + IGMP_MEMBERSHIP_QUERY, + conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, + gvDesc->group, 0); + + my_log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", + inetFmt(Dp->InAdr.s_addr,s1), inetFmt(gvDesc->group,s2), + conf->lastMemberQueryInterval); + } + } + } + } // Set timeout for next round... timer_setTimer(conf->lastMemberQueryInterval, sendGroupSpecificMemberQuery, gvDesc); --- a/src/rttable.c +++ b/src/rttable.c @@ -428,6 +428,25 @@ void ageActiveRoutes() { } /** +* Counts the number of interfaces a given route is active on +*/ +int numberOfInterfaces(struct RouteTable *croute) { + int Ix; + struct IfDesc *Dp; + int result = 0; + // Loop through all interfaces + for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { + // If the interface is used by the route, increase counter + if(BIT_TST(croute->vifBits, Dp->index)) { + result++; + } + } + my_log(LOG_DEBUG, 0, "counted %d interfaces", result); + return result; +} + + +/** * Should be called when a leave message is recieved, to * mark a route for the last member probe state. */ @@ -439,8 +458,11 @@ void setRouteLastMemberMode(uint32_t gro if(croute!=NULL) { // Check for fast leave mode... if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) { - // Send a leave message right away.. - sendJoinLeaveUpstream(croute, 0); + // Send a leave message right away only when the route has been active on only one interface + if (numberOfInterfaces(croute) <= 1) { + my_log(LOG_DEBUG, 0, "Leaving group %d now", group); + sendJoinLeaveUpstream(croute, 0); + } } // Set the routingstate to Last member check... croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; @@ -677,3 +699,18 @@ void logRouteTable(char *header) { my_log(LOG_DEBUG, 0, "-----------------------------------------------------"); } + +/** +* Returns true when the given group belongs to the given interface +*/ +int interfaceInRoute(int32_t group, int Ix) { + struct RouteTable* croute; + croute = findRoute(group); + if (croute != NULL) { + my_log(LOG_DEBUG, 0, "Interface id %d is in group $d", Ix, group); + return BIT_TST(croute->vifBits, Ix); + } else { + return 0; + } +} +