From 1b73a1292897d4027b5de65eafdbd2b0dd7d6727 Mon Sep 17 00:00:00 2001 From: Tim Balmer Date: Thu, 27 Feb 2020 20:47:28 +0100 Subject: [PATCH 1/2] Implemented host tracking with fastleave enabled, leave will now only be sent upstream when the leave comes from the last downstream host in the multicast group --- src/igmpproxy.h | 4 +-- src/request.c | 6 ++-- src/rttable.c | 91 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/src/igmpproxy.h b/src/igmpproxy.h index ad1063be..d4f58bb3 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -260,10 +260,10 @@ int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ); */ void initRouteTable(void); void clearAllRoutes(void); -int insertRoute(uint32_t group, int ifx); +int insertRoute(uint32_t group, int ifx, uint32_t src); int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif); void ageActiveRoutes(void); -void setRouteLastMemberMode(uint32_t group); +void setRouteLastMemberMode(uint32_t group, uint32_t src); int lastMemberGroupAge(uint32_t group); int interfaceInRoute(int32_t group, int Ix); int getMcGroupSock(void); diff --git a/src/request.c b/src/request.c index 13d92ff7..06d14c44 100644 --- a/src/request.c +++ b/src/request.c @@ -86,7 +86,7 @@ void acceptGroupReport(uint32_t src, uint32_t group) { // If we don't have a whitelist we insertRoute and done if(sourceVif->allowedgroups == NULL) { - insertRoute(group, sourceVif->index); + insertRoute(group, sourceVif->index, src); return; } // Check if this Request is legit on this interface @@ -95,7 +95,7 @@ void acceptGroupReport(uint32_t src, uint32_t group) { if((group & sn->subnet_mask) == sn->subnet_addr) { // The membership report was OK... Insert it into the route table.. - insertRoute(group, sourceVif->index); + insertRoute(group, sourceVif->index, src); return; } my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); @@ -138,7 +138,7 @@ void acceptLeaveMessage(uint32_t src, uint32_t group) { gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc)); // Tell the route table that we are checking for remaining members... - setRouteLastMemberMode(group); + setRouteLastMemberMode(group, src); // Call the group spesific membership querier... gvDesc->group = group; diff --git a/src/rttable.c b/src/rttable.c index bee109c9..01f3b05f 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -41,6 +41,7 @@ #include "igmpproxy.h" #define MAX_ORIGINS 4 +#define MAX_DOWNSTREAM_HOSTS 20 /** * Routing table structure definition. Double linked list... @@ -60,6 +61,9 @@ struct RouteTable { uint32_t ageVifBits; // Bits representing aging VIFs. int ageValue; // Downcounter for death. int ageActivity; // Records any acitivity that notes there are still listeners. + + // Keeps the number of downstream hosts + uint32_t dwnstrHosts[MAX_DOWNSTREAM_HOSTS]; // The downstream hosts the route is active for }; @@ -216,6 +220,44 @@ void clearAllRoutes(void) { my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); } +/** +* Function which return the number of active dowsnstream hosts +*/ +int countDownstreamHosts(struct RouteTable *croute) { + int result = 0; + + for (int i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + if(croute->dwnstrHosts[i] != 0) { + result++; + } + } + return result; +} + +/** +* Function to find first downstream host slot +*/ +size_t findFreeDownstreamSlot(struct RouteTable *croute) { + for (size_t i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + if(croute->dwnstrHosts[i] == 0) { + return i; + } + } + return (size_t)0; +} + +/** +* Function to find downstream slot for host +*/ +size_t findDownstreamSlot(struct RouteTable *croute, uint32_t dstrHost) { + for (size_t i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + if(croute->dwnstrHosts[i] == dstrHost) { + return i; + } + } + return (size_t)-1; +} + /** * Private access function to find a route from a given * Route Descriptor. @@ -237,7 +279,7 @@ static struct RouteTable *findRoute(uint32_t group) { * If the route already exists, the existing route * is updated... */ -int insertRoute(uint32_t group, int ifx) { +int insertRoute(uint32_t group, int ifx, uint32_t src) { struct Config *conf = getCommonConfig(); struct RouteTable* croute; @@ -274,6 +316,11 @@ int insertRoute(uint32_t group, int ifx) { newroute->prevroute = NULL; newroute->upstrVif = -1; + // Downstream host can always be set at first slot for host tracking in fastleave mode + if(conf->fastUpstreamLeave) { + newroute->dwnstrHosts[0] = src; + } + // The group is not joined initially. newroute->upstrState = ROUTESTATE_NOTJOINED; @@ -349,6 +396,14 @@ int insertRoute(uint32_t group, int ifx) { // Register the VIF activity for the aging routine BIT_SET(croute->ageVifBits, ifx); + // Register dwnstrHosts for host tracking if fastleave is enabled + if(conf->fastUpstreamLeave) { + if(findDownstreamSlot(croute, src) == (size_t)-1){ + size_t hostIdx = findFreeDownstreamSlot(croute); + croute->dwnstrHosts[hostIdx] = src; + } + } + // Log the cleanup in debugmode... my_log(LOG_INFO, 0, "Updated route entry for %s on VIF #%d", inetFmt(croute->group, s1), ifx); @@ -388,7 +443,7 @@ int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif) { inetFmt(group, s1),inetFmt(originAddr, s2)); // Insert route, but no interfaces have yet requested it downstream. - insertRoute(group, -1); + insertRoute(group, -1, 0); // Retrieve the route from table... croute = findRoute(group); @@ -485,7 +540,7 @@ int numberOfInterfaces(struct RouteTable *croute) { * Should be called when a leave message is received, to * mark a route for the last member probe state. */ -void setRouteLastMemberMode(uint32_t group) { +void setRouteLastMemberMode(uint32_t group, uint32_t src) { struct Config *conf = getCommonConfig(); struct RouteTable *croute; @@ -493,18 +548,30 @@ void setRouteLastMemberMode(uint32_t group) { if(croute!=NULL) { // Check for fast leave mode... if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) { - // 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); + + // Remove downstreamhost from route + size_t hostIdx = findDownstreamSlot(croute, src); + if(hostIdx != (size_t)-1){ + croute->dwnstrHosts[hostIdx] = 0; + } + + // Send a leave message right away but only when the route is not active anymore on any downstream host + if (countDownstreamHosts(croute) < 1) { + my_log(LOG_DEBUG, 0, "Fastleave is enabled and this was the last downstream host, leaving group %s now", inetFmt(croute->group, s1)); sendJoinLeaveUpstream(croute, 0); + } else { + my_log(LOG_DEBUG, 0, "Fast leave enabled but there are still %d downstream hosts left, not leaving group %s", countDownstreamHosts(croute), inetFmt(croute->group, s1)); } + } - // Set the routingstate to Last member check... - croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; + // Set the routingstate to last member check if we have no known downstream host left or if fast leave mode is disabled... + if(countDownstreamHosts(croute) < 1 || !conf->fastUpstreamLeave) { + croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; - // Set the count value for expiring... (-1 since first aging) - croute->ageValue = conf->lastMemberQueryCount; + // Set the count value for expiring... (-1 since first aging) + croute->ageValue = conf->lastMemberQueryCount; + } } } @@ -732,10 +799,10 @@ void logRouteTable(const char *header) { sprintf(src + strlen(src), "Src%d: %s, ", i, inetFmt(croute->originAddrs[i], s1)); } - my_log(LOG_DEBUG, 0, "#%d: %sDst: %s, Age:%d, St: %c, OutVifs: 0x%08x", + my_log(LOG_DEBUG, 0, "#%d: %sDst: %s, Age:%d, St: %c, OutVifs: 0x%08x, dHosts: %d", rcount, src, inetFmt(croute->group, s2), croute->ageValue, st, - croute->vifBits); + croute->vifBits, countDownstreamHosts(croute)); croute = croute->nextroute; From a5f5f405b33f0b3d42d5fccd65860e78ee409b22 Mon Sep 17 00:00:00 2001 From: Tim Balmer Date: Sat, 29 Feb 2020 01:55:21 +0100 Subject: [PATCH 2/2] Make size of dowstream hosts array dynamic, this fixes potential issues with more than 20 downstream hosts --- src/rttable.c | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/rttable.c b/src/rttable.c index 01f3b05f..c0ae8cb0 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -41,7 +41,6 @@ #include "igmpproxy.h" #define MAX_ORIGINS 4 -#define MAX_DOWNSTREAM_HOSTS 20 /** * Routing table structure definition. Double linked list... @@ -62,8 +61,10 @@ struct RouteTable { int ageValue; // Downcounter for death. int ageActivity; // Records any acitivity that notes there are still listeners. - // Keeps the number of downstream hosts - uint32_t dwnstrHosts[MAX_DOWNSTREAM_HOSTS]; // The downstream hosts the route is active for + // Keeps downstream hosts information + uint32_t *dwnstrHosts; // The downstream hosts the route is active for + int dwnstrHostsCapacity; // Current capacity of the downstream hosts array + int dwnstrHostsSize; // Current size of the downstream hosts array }; @@ -226,7 +227,7 @@ void clearAllRoutes(void) { int countDownstreamHosts(struct RouteTable *croute) { int result = 0; - for (int i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + for (int i = 0; i < croute->dwnstrHostsCapacity; i++) { if(croute->dwnstrHosts[i] != 0) { result++; } @@ -238,7 +239,7 @@ int countDownstreamHosts(struct RouteTable *croute) { * Function to find first downstream host slot */ size_t findFreeDownstreamSlot(struct RouteTable *croute) { - for (size_t i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + for (size_t i = 0; i < (size_t)croute->dwnstrHostsCapacity; i++) { if(croute->dwnstrHosts[i] == 0) { return i; } @@ -250,7 +251,7 @@ size_t findFreeDownstreamSlot(struct RouteTable *croute) { * Function to find downstream slot for host */ size_t findDownstreamSlot(struct RouteTable *croute, uint32_t dstrHost) { - for (size_t i = 0; i < MAX_DOWNSTREAM_HOSTS; i++) { + for (size_t i = 0; i < (size_t)croute->dwnstrHostsCapacity; i++) { if(croute->dwnstrHosts[i] == dstrHost) { return i; } @@ -258,6 +259,28 @@ size_t findDownstreamSlot(struct RouteTable *croute, uint32_t dstrHost) { return (size_t)-1; } +/** +* Function to add downstream hosts to route +*/ +void addDownStreamHost(struct RouteTable *croute, uint32_t dstrHost){ + croute->dwnstrHostsSize = croute->dwnstrHostsSize + 1; + + if(croute->dwnstrHostsSize > croute->dwnstrHostsCapacity){ + int newcapacity = croute->dwnstrHostsCapacity * 2; + + uint32_t* newptr = (uint32_t*)realloc(croute->dwnstrHosts, newcapacity * sizeof(uint32_t)); + if(newptr == NULL) { + croute->dwnstrHostsSize = croute->dwnstrHostsSize - 1; + my_log(LOG_WARNING, 0, "Failed to realloc dwnstrHosts, quickleave last host tracking may not work properly"); + } else { + croute->dwnstrHosts = newptr; + croute->dwnstrHostsCapacity = newcapacity; + } + } + size_t idx = findFreeDownstreamSlot(croute); + croute->dwnstrHosts[idx] = dstrHost; +} + /** * Private access function to find a route from a given * Route Descriptor. @@ -316,9 +339,14 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { newroute->prevroute = NULL; newroute->upstrVif = -1; - // Downstream host can always be set at first slot for host tracking in fastleave mode + // Init downstream hosts dynamic array + newroute->dwnstrHostsSize = 0; + newroute->dwnstrHostsCapacity = 2; + newroute->dwnstrHosts = malloc(2 * sizeof(uint32_t)); + + // Add downstream hosts if(conf->fastUpstreamLeave) { - newroute->dwnstrHosts[0] = src; + addDownStreamHost(newroute, src); } // The group is not joined initially. @@ -399,8 +427,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { // Register dwnstrHosts for host tracking if fastleave is enabled if(conf->fastUpstreamLeave) { if(findDownstreamSlot(croute, src) == (size_t)-1){ - size_t hostIdx = findFreeDownstreamSlot(croute); - croute->dwnstrHosts[hostIdx] = src; + addDownStreamHost(croute, src); } } @@ -553,6 +580,7 @@ void setRouteLastMemberMode(uint32_t group, uint32_t src) { size_t hostIdx = findDownstreamSlot(croute, src); if(hostIdx != (size_t)-1){ croute->dwnstrHosts[hostIdx] = 0; + croute->dwnstrHostsSize = croute->dwnstrHostsSize - 1; } // Send a leave message right away but only when the route is not active anymore on any downstream host