From: John Crispin Date: Tue, 17 Mar 2015 09:43:07 +0000 (+0000) Subject: igmpproxy: Multiple downlink interfaces fix. X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=29c3611294c6641c079111b229b138c5f7a48f2b;p=librecmc%2Flibrecmc.git igmpproxy: Multiple downlink interfaces fix. from Erik Tews This patch has two effects. First, the quickleave feature/behaviour is disabled for all groups that are used on more than one interface. The idea of quickleave is to leave a group fast and later figure out whether there is still somebody interested in that group. For groups used on more than one interface, it is already known that there is still somebody interested in that group. Second, when a leave is received for a group that is used on more than one interface, igmpproxy sends queries on all interface to discover remeining listeners for that group. Previously these queries were only send on the interface the leave was received on, so that listeners on the other interfaces were not discovered and the group might be left on the upstream router incorrectly. This patch can be improved by sending the queries only on the interface the leave was received on and adapting the algorithm in internAgeRoute(...) in rttable.c in a way that only one interface is actually processed and all other interfaces of the route are silently assumed to be still active. Signed-off-by: Erik Tews SVN-Revision: 44859 --- diff --git a/package/network/services/igmpproxy/patches/250-fix_multiple_downlink_interfaces.patch b/package/network/services/igmpproxy/patches/250-fix_multiple_downlink_interfaces.patch new file mode 100644 index 0000000000..24aefcef26 --- /dev/null +++ b/package/network/services/igmpproxy/patches/250-fix_multiple_downlink_interfaces.patch @@ -0,0 +1,154 @@ +--- 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; ++ } ++} ++