1 /*
2 **  igmpproxy - IGMP proxy based multicast router
3 **  Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
4 **
5 **  This program is free software; you can redistribute it and/or modify
6 **  it under the terms of the GNU General Public License as published by
7 **  the Free Software Foundation; either version 2 of the License, or
8 **  (at your option) any later version.
9 **
10 **  This program is distributed in the hope that it will be useful,
11 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 **  GNU General Public License for more details.
14 **
15 **  You should have received a copy of the GNU General Public License
16 **  along with this program; if not, write to the Free Software
17 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 **
19 **----------------------------------------------------------------------------
20 **
21 **  This software is derived work from the following software. The original
22 **  source code has been modified from it's original state by the author
23 **  of igmpproxy.
24 **
25 **  smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26 **  - Licensed under the GNU General Public License, either version 2 or
27 **    any later version.
28 **
29 **  mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of
30 **  Leland Stanford Junior University.
31 **  - Licensed under the 3-clause BSD license, see Stanford.txt file.
32 **
33 */
34 /**
35 *   rttable.c
36 *
37 *   Updates the routingtable according to
38 *     received request.
39 */
40 
41 #include "igmpproxy.h"
42 
43 #define MAX_ORIGINS 4
44 
45 /**
46 *   Routing table structure definition. Double linked list...
47 */
48 struct RouteTable {
49     struct RouteTable   *nextroute;     // Pointer to the next group in line.
50     struct RouteTable   *prevroute;     // Pointer to the previous group in line.
51     uint32_t            group;          // The group to route
52     uint32_t            originAddrs[MAX_ORIGINS]; // The origin adresses (only set on activated routes)
53     uint32_t            vifBits;        // Bits representing recieving VIFs.
54 
55     // Keeps the upstream membership state...
56     short               upstrState;     // Upstream membership state.
57     int                 upstrVif;       // Upstream Vif Index.
58 
59     // These parameters contain aging details.
60     uint32_t            ageVifBits;     // Bits representing aging VIFs.
61     int                 ageValue;       // Downcounter for death.
62     int                 ageActivity;    // Records any acitivity that notes there are still listeners.
63 
64     // Keeps downstream hosts information
65     uint32_t            downstreamHostsHashSeed;
66     uint8_t             downstreamHostsHashTable[];
67 };
68 
69 
70 // Keeper for the routing table...
71 static struct RouteTable   *routing_table;
72 
73 // Prototypes
74 void logRouteTable(const char *header);
75 int internAgeRoute(struct RouteTable *croute);
76 int internUpdateKernelRoute(struct RouteTable *route, int activate);
77 
78 
79 /**
80 *   Functions for downstream hosts hash table
81 */
82 
83 // MurmurHash3 32bit hash function by Austin Appleby, public domain
murmurhash3(uint32_t x)84 static uint32_t murmurhash3(uint32_t x) {
85     x ^= x >> 16;
86     x *= 0x85ebca6b;
87     x ^= x >> 13;
88     x *= 0xc2b2ae35;
89     x ^= x >> 16;
90     return x;
91 }
92 
setDownstreamHost(struct Config * conf,struct RouteTable * croute,uint32_t src)93 static inline void setDownstreamHost(struct Config *conf, struct RouteTable *croute, uint32_t src) {
94     uint32_t hash = murmurhash3(src ^ croute->downstreamHostsHashSeed) % (conf->downstreamHostsHashTableSize*8);
95     BIT_SET(croute->downstreamHostsHashTable[hash/8], hash%8);
96 }
97 
clearDownstreamHost(struct Config * conf,struct RouteTable * croute,uint32_t src)98 static inline void clearDownstreamHost(struct Config *conf, struct RouteTable *croute, uint32_t src) {
99     uint32_t hash = murmurhash3(src ^ croute->downstreamHostsHashSeed) % (conf->downstreamHostsHashTableSize*8);
100     BIT_CLR(croute->downstreamHostsHashTable[hash/8], hash%8);
101 }
102 
zeroDownstreamHosts(struct Config * conf,struct RouteTable * croute)103 static inline void zeroDownstreamHosts(struct Config *conf, struct RouteTable *croute) {
104     croute->downstreamHostsHashSeed = ((uint32_t)rand() << 16) | (uint32_t)rand();
105     memset(croute->downstreamHostsHashTable, 0, conf->downstreamHostsHashTableSize);
106 }
107 
testNoDownstreamHost(struct Config * conf,struct RouteTable * croute)108 static inline int testNoDownstreamHost(struct Config *conf, struct RouteTable *croute) {
109     for (size_t i = 0; i < conf->downstreamHostsHashTableSize; i++) {
110         if (croute->downstreamHostsHashTable[i])
111             return 0;
112     }
113     return 1;
114 }
115 
116 /**
117 *   Initializes the routing table.
118 */
initRouteTable(void)119 void initRouteTable(void) {
120     unsigned Ix;
121     struct IfDesc *Dp;
122 
123     // Clear routing table...
124     routing_table = NULL;
125 
126     // Join the all routers group on downstream vifs...
127     for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
128         // If this is a downstream vif, we should join the All routers group...
129         if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) {
130             my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s",
131                          inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2));
132 
133             k_join(Dp, allrouters_group);
134 
135             my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s",
136                          inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2));
137             k_join(Dp, alligmp3_group);
138         }
139     }
140 }
141 
142 /**
143 *   Internal function to send join or leave requests for
144 *   a specified route upstream...
145 */
sendJoinLeaveUpstream(struct RouteTable * route,int join)146 static void sendJoinLeaveUpstream(struct RouteTable* route, int join) {
147     struct IfDesc*      upstrIf;
148     int i;
149 
150     for(i=0; i<MAX_UPS_VIFS; i++)
151     {
152         if (-1 != upStreamIfIdx[i])
153         {
154             // Get the upstream IF...
155             upstrIf = getIfByIx( upStreamIfIdx[i] );
156             if(upstrIf == NULL) {
157                 my_log(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF.");
158             }
159 
160             // Check if there is a white list for the upstram VIF
161             if (upstrIf->allowedgroups != NULL) {
162               uint32_t           group = route->group;
163                 struct SubnetList* sn;
164 
165                 // Check if this Request is legit to be forwarded to upstream
166                 for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next)
167                     if((group & sn->subnet_mask) == sn->subnet_addr)
168                         // Forward is OK...
169                         break;
170 
171                 if (sn == NULL) {
172                     my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1));
173                     return;
174                 }
175             }
176 
177             // Send join or leave request...
178             if(join) {
179                 // Only join a group if there are listeners downstream...
180                 if(route->vifBits > 0) {
181                     my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s",
182                                  inetFmt(route->group, s1),
183                                  inetFmt(upstrIf->InAdr.s_addr, s2));
184 
185                     k_join(upstrIf, route->group);
186 
187                     route->upstrState = ROUTESTATE_JOINED;
188                 } else {
189                     my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.",
190                         inetFmt(route->group, s1));
191                 }
192             } else {
193                 // Only leave if group is not left already...
194                 if(route->upstrState != ROUTESTATE_NOTJOINED) {
195                     my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s",
196                                  inetFmt(route->group, s1),
197                                  inetFmt(upstrIf->InAdr.s_addr, s2));
198 
199                     k_leave(upstrIf, route->group);
200 
201                     route->upstrState = ROUTESTATE_NOTJOINED;
202                 }
203             }
204         }
205         else
206         {
207             i = MAX_UPS_VIFS;
208         }
209     }
210 }
211 
212 /**
213 *   Clear all routes from routing table, and alerts Leaves upstream.
214 */
clearAllRoutes(void)215 void clearAllRoutes(void) {
216     struct RouteTable   *croute, *remainroute;
217 
218     // Loop through all routes...
219     for(croute = routing_table; croute; croute = remainroute) {
220 
221         remainroute = croute->nextroute;
222 
223         // Log the cleanup in debugmode...
224         my_log(LOG_DEBUG, 0, "Removing route entry for %s",
225                      inetFmt(croute->group, s1));
226 
227         // Uninstall current route
228         if(!internUpdateKernelRoute(croute, 0)) {
229             my_log(LOG_WARNING, 0, "The removal from Kernel failed.");
230         }
231 
232         // Send Leave message upstream.
233         sendJoinLeaveUpstream(croute, 0);
234 
235         // Clear memory, and set pointer to next route...
236         free(croute);
237     }
238     routing_table = NULL;
239 
240     // Send a notice that the routing table is empty...
241     my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty.");
242 }
243 
244 /**
245 *   Private access function to find a route from a given
246 *   Route Descriptor.
247 */
findRoute(uint32_t group)248 static struct RouteTable *findRoute(uint32_t group) {
249     struct RouteTable*  croute;
250 
251     for(croute = routing_table; croute; croute = croute->nextroute) {
252         if(croute->group == group) {
253             return croute;
254         }
255     }
256 
257     return NULL;
258 }
259 
260 /**
261 *   Adds a specified route to the routingtable.
262 *   If the route already exists, the existing route
263 *   is updated...
264 */
insertRoute(uint32_t group,int ifx,uint32_t src)265 int insertRoute(uint32_t group, int ifx, uint32_t src) {
266 
267     struct Config *conf = getCommonConfig();
268     struct RouteTable*  croute;
269 
270     // Sanitycheck the group adress...
271     if( ! IN_MULTICAST( ntohl(group) )) {
272         my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.",
273             inetFmt(group, s1));
274         return 0;
275     }
276 
277     // Santiycheck the VIF index...
278     if(ifx >= MAXVIFS) {
279         my_log(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.", ifx, MAXVIFS-1);
280         return 0;
281     }
282 
283     // Try to find an existing route for this group...
284     croute = findRoute(group);
285     if(croute==NULL) {
286         struct RouteTable*  newroute;
287 
288         my_log(LOG_DEBUG, 0, "No existing route for %s. Create new.",
289                      inetFmt(group, s1));
290 
291 
292         // Create and initialize the new route table entry..
293         newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable) + (conf->fastUpstreamLeave ? conf->downstreamHostsHashTableSize : 0));
294         // Insert the route desc and clear all pointers...
295         newroute->group      = group;
296         memset(newroute->originAddrs, 0, MAX_ORIGINS * sizeof(newroute->originAddrs[0]));
297         newroute->nextroute  = NULL;
298         newroute->prevroute  = NULL;
299         newroute->upstrVif   = -1;
300 
301         if(conf->fastUpstreamLeave) {
302             // Init downstream hosts bit hash table
303             zeroDownstreamHosts(conf, newroute);
304 
305             // Add downstream host
306             setDownstreamHost(conf, newroute, src);
307         }
308 
309         // The group is not joined initially.
310         newroute->upstrState = ROUTESTATE_NOTJOINED;
311 
312         // The route is not active yet, so the age is unimportant.
313         newroute->ageValue    = conf->robustnessValue;
314         newroute->ageActivity = 0;
315 
316         BIT_ZERO(newroute->ageVifBits);     // Initially we assume no listeners.
317 
318         // Set the listener flag...
319         BIT_ZERO(newroute->vifBits);    // Initially no listeners...
320         if(ifx >= 0) {
321             BIT_SET(newroute->vifBits, ifx);
322         }
323 
324         // Check if there is a table already....
325         if(routing_table == NULL) {
326             // No location set, so insert in on the table top.
327             routing_table = newroute;
328             my_log(LOG_DEBUG, 0, "No routes in table. Insert at beginning.");
329         } else {
330 
331             my_log(LOG_DEBUG, 0, "Found existing routes. Find insert location.");
332 
333             // Check if the route could be inserted at the beginning...
334             if(routing_table->group > group) {
335                 my_log(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1));
336 
337                 // Insert at beginning...
338                 newroute->nextroute = routing_table;
339                 newroute->prevroute = NULL;
340                 routing_table = newroute;
341 
342                 // If the route has a next node, the previous pointer must be updated.
343                 if(newroute->nextroute != NULL) {
344                     newroute->nextroute->prevroute = newroute;
345                 }
346 
347             } else {
348 
349                 // Find the location which is closest to the route.
350                 for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) {
351                     // Find insert position.
352                     if(croute->nextroute->group > group) {
353                         break;
354                     }
355                 }
356 
357                 my_log(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1));
358 
359                 // Insert after current...
360                 newroute->nextroute = croute->nextroute;
361                 newroute->prevroute = croute;
362                 if(croute->nextroute != NULL) {
363                     croute->nextroute->prevroute = newroute;
364                 }
365                 croute->nextroute = newroute;
366             }
367         }
368 
369         // Set the new route as the current...
370         croute = newroute;
371 
372         // Log the cleanup in debugmode...
373         my_log(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d",
374             inetFmt(croute->group, s1),ifx);
375 
376     } else if(ifx >= 0) {
377 
378         // The route exists already, so just update it.
379         BIT_SET(croute->vifBits, ifx);
380 
381         // Register the VIF activity for the aging routine
382         BIT_SET(croute->ageVifBits, ifx);
383 
384         // Register dwnstrHosts for host tracking if fastleave is enabled
385         if(conf->fastUpstreamLeave) {
386             setDownstreamHost(conf, croute, src);
387         }
388 
389         // Log the cleanup in debugmode...
390         my_log(LOG_INFO, 0, "Updated route entry for %s on VIF #%d",
391             inetFmt(croute->group, s1), ifx);
392 
393         // Update route in kernel...
394         if(!internUpdateKernelRoute(croute, 1)) {
395             my_log(LOG_WARNING, 0, "The insertion into Kernel failed.");
396             return 0;
397         }
398     }
399 
400     // Send join message upstream, if the route has no joined flag...
401     if(croute->upstrState != ROUTESTATE_JOINED) {
402         // Send Join request upstream
403         sendJoinLeaveUpstream(croute, 1);
404     }
405 
406     logRouteTable("Insert Route");
407 
408     return 1;
409 }
410 
411 /**
412 *   Activates a passive group. If the group is already
413 *   activated, it's reinstalled in the kernel. If
414 *   the route is activated, no originAddr is needed.
415 */
activateRoute(uint32_t group,uint32_t originAddr,int upstrVif)416 int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif) {
417     struct RouteTable*  croute;
418     int result = 0;
419 
420     // Find the requested route.
421     croute = findRoute(group);
422     if(croute == NULL) {
423         my_log(LOG_DEBUG, 0,
424             "No table entry for %s [From: %s]. Inserting route.",
425             inetFmt(group, s1),inetFmt(originAddr, s2));
426 
427         // Insert route, but no interfaces have yet requested it downstream.
428         insertRoute(group, -1, 0);
429 
430         // Retrieve the route from table...
431         croute = findRoute(group);
432     }
433 
434     if(croute != NULL) {
435         // If the origin address is set, update the route data.
436         if(originAddr > 0) {
437             // find this origin, or an unused slot
438             int i;
439             for (i = 0; i < MAX_ORIGINS; i++) {
440                 // unused slots are at the bottom, so we can't miss this origin
441                 if (croute->originAddrs[i] == originAddr || croute->originAddrs[i] == 0) {
442                     break;
443                 }
444             }
445 
446             if (i == MAX_ORIGINS) {
447                 i = MAX_ORIGINS - 1;
448 
449                 my_log(LOG_WARNING, 0, "Too many origins for route %s; replacing %s with %s",
450                     inetFmt(croute->group, s1),
451                     inetFmt(croute->originAddrs[i], s2),
452                     inetFmt(originAddr, s3));
453             }
454 
455             // set origin
456             croute->originAddrs[i] = originAddr;
457 
458             // move it to the top
459             while (i > 0) {
460                 uint32_t t = croute->originAddrs[i - 1];
461                 croute->originAddrs[i - 1] = croute->originAddrs[i];
462                 croute->originAddrs[i] = t;
463                 i--;
464             }
465         }
466         croute->upstrVif = upstrVif;
467 
468         // Only update kernel table if there are listeners !
469         if(croute->vifBits > 0) {
470             result = internUpdateKernelRoute(croute, 1);
471         }
472     }
473     logRouteTable("Activate Route");
474 
475     return result;
476 }
477 
478 
479 /**
480 *   This function loops through all routes, and updates the age
481 *   of any active routes.
482 */
ageActiveRoutes(void)483 void ageActiveRoutes(void) {
484     struct RouteTable   *croute, *nroute;
485 
486     my_log(LOG_DEBUG, 0, "Aging routes in table.");
487 
488     // Scan all routes...
489     for( croute = routing_table; croute != NULL; croute = nroute ) {
490 
491         // Keep the next route (since current route may be removed)...
492         nroute = croute->nextroute;
493 
494         // Run the aging round algorithm.
495         if(croute->upstrState != ROUTESTATE_CHECK_LAST_MEMBER) {
496             // Only age routes if Last member probe is not active...
497             internAgeRoute(croute);
498         }
499     }
500     logRouteTable("Age active routes");
501 }
502 
503 /**
504 *   Counts the number of interfaces a given route is active on
505 */
numberOfInterfaces(struct RouteTable * croute)506 int numberOfInterfaces(struct RouteTable *croute) {
507     int Ix;
508     struct IfDesc *Dp;
509     int result = 0;
510     // Loop through all interfaces
511     for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
512         // If the interface is used by the route, increase counter
513         if(BIT_TST(croute->vifBits, Dp->index)) {
514             result++;
515         }
516     }
517     my_log(LOG_DEBUG, 0, "counted %d interfaces", result);
518     return result;
519 }
520 
521 /**
522 *   Should be called when a leave message is received, to
523 *   mark a route for the last member probe state.
524 */
setRouteLastMemberMode(uint32_t group,uint32_t src)525 void setRouteLastMemberMode(uint32_t group, uint32_t src) {
526     struct Config       *conf = getCommonConfig();
527     struct RouteTable   *croute;
528     int                 routeStateCheck = 1;
529 
530     croute = findRoute(group);
531     if(!croute)
532         return;
533 
534     // Check for fast leave mode...
535     if(conf->fastUpstreamLeave) {
536         if(croute->upstrState == ROUTESTATE_JOINED) {
537             // Remove downstream host from route
538             clearDownstreamHost(conf, croute, src);
539         }
540 
541         // Do route state check if there is no downstream host in hash table
542         // This host does not have to been the last downstream host if hash collision occurred
543         routeStateCheck = testNoDownstreamHost(conf, croute);
544 
545         if(croute->upstrState == ROUTESTATE_JOINED) {
546             // Send a leave message right away but only when the route is not active anymore on any downstream host
547             // It is possible that there are still some interfaces active but no downstream host in hash table due to hash collision
548             if (routeStateCheck && numberOfInterfaces(croute) <= 1) {
549                 my_log(LOG_DEBUG, 0, "quickleave is enabled and this was the last downstream host, leaving group %s now", inetFmt(croute->group, s1));
550                 sendJoinLeaveUpstream(croute, 0);
551             } else {
552                 my_log(LOG_DEBUG, 0, "quickleave is enabled but there are still some downstream hosts left, not leaving group %s", inetFmt(croute->group, s1));
553             }
554         }
555     }
556 
557     // Set the routingstate to last member check if we have no known downstream host left or if fast leave mode is disabled...
558     if(routeStateCheck) {
559         croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER;
560 
561         // Set the count value for expiring... (-1 since first aging)
562         croute->ageValue = conf->lastMemberQueryCount;
563     }
564 }
565 
566 
567 /**
568 *   Ages groups in the last member check state. If the
569 *   route is not found, or not in this state, 0 is returned.
570 */
lastMemberGroupAge(uint32_t group)571 int lastMemberGroupAge(uint32_t group) {
572     struct RouteTable   *croute;
573 
574     croute = findRoute(group);
575     if(croute!=NULL) {
576         if(croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER) {
577             return !internAgeRoute(croute);
578         } else {
579             return 0;
580         }
581     }
582     return 0;
583 }
584 
585 /**
586 *   Remove a specified route. Returns 1 on success,
587 *   and 0 if route was not found.
588 */
removeRoute(struct RouteTable * croute)589 static int removeRoute(struct RouteTable*  croute) {
590     struct Config       *conf = getCommonConfig();
591     int result = 1;
592 
593     // If croute is null, no routes was found.
594     if(croute==NULL) {
595         return 0;
596     }
597 
598     // Log the cleanup in debugmode...
599     my_log(LOG_DEBUG, 0, "Removed route entry for %s from table.",
600                  inetFmt(croute->group, s1));
601 
602     //BIT_ZERO(croute->vifBits);
603 
604     // Uninstall current route from kernel
605     if(!internUpdateKernelRoute(croute, 0)) {
606         my_log(LOG_WARNING, 0, "The removal from Kernel failed.");
607         result = 0;
608     }
609 
610     // Send Leave request upstream if group is joined
611     if(croute->upstrState == ROUTESTATE_JOINED ||
612        (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave))
613     {
614         sendJoinLeaveUpstream(croute, 0);
615     }
616 
617     // Update pointers...
618     if(croute->prevroute == NULL) {
619         // Topmost node...
620         if(croute->nextroute != NULL) {
621             croute->nextroute->prevroute = NULL;
622         }
623         routing_table = croute->nextroute;
624 
625     } else {
626         croute->prevroute->nextroute = croute->nextroute;
627         if(croute->nextroute != NULL) {
628             croute->nextroute->prevroute = croute->prevroute;
629         }
630     }
631     // Free the memory, and set the route to NULL...
632     free(croute);
633     croute = NULL;
634 
635     logRouteTable("Remove route");
636 
637     return result;
638 }
639 
640 
641 /**
642 *   Ages a specific route
643 */
internAgeRoute(struct RouteTable * croute)644 int internAgeRoute(struct RouteTable*  croute) {
645     struct Config *conf = getCommonConfig();
646     int result = 0;
647 
648     // Drop age by 1.
649     croute->ageValue--;
650 
651     // Check if there has been any activity...
652     if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) {
653         // There was some activity, check if all registered vifs responded.
654         if(croute->vifBits == croute->ageVifBits) {
655             // Everything is in perfect order, so we just update the route age.
656             croute->ageValue = conf->robustnessValue;
657             //croute->ageActivity = 0;
658         } else {
659             // One or more VIF has not gotten any response.
660             croute->ageActivity++;
661 
662             // Update the actual bits for the route...
663             croute->vifBits = croute->ageVifBits;
664         }
665     }
666     // Check if there have been activity in aging process...
667     else if( croute->ageActivity > 0 ) {
668 
669         // If the bits are different in this round, we must
670         if(croute->vifBits != croute->ageVifBits) {
671             // Or the bits together to insure we don't lose any listeners.
672             croute->vifBits |= croute->ageVifBits;
673 
674             // Register changes in this round as well..
675             croute->ageActivity++;
676         }
677     }
678 
679     // If the aging counter has reached zero, its time for updating...
680     if(croute->ageValue == 0) {
681         // Check for activity in the aging process,
682         if(croute->ageActivity>0) {
683 
684             my_log(LOG_DEBUG, 0, "Updating route after aging : %s",
685                          inetFmt(croute->group,s1));
686 
687             // Just update the routing settings in kernel...
688             internUpdateKernelRoute(croute, 1);
689 
690             // We append the activity counter to the age, and continue...
691             croute->ageValue = croute->ageActivity;
692             croute->ageActivity = 0;
693         } else {
694 
695             my_log(LOG_DEBUG, 0, "Removing group %s. Died of old age.",
696                          inetFmt(croute->group,s1));
697 
698             // No activity was registered within the timelimit, so remove the route.
699             removeRoute(croute);
700         }
701         // Tell that the route was updated...
702         result = 1;
703     }
704 
705     // The aging vif bits must be reset for each round...
706     BIT_ZERO(croute->ageVifBits);
707 
708     return result;
709 }
710 
711 /**
712 *   Updates the Kernel routing table. If activate is 1, the route
713 *   is (re-)activated. If activate is false, the route is removed.
714 */
internUpdateKernelRoute(struct RouteTable * route,int activate)715 int internUpdateKernelRoute(struct RouteTable *route, int activate) {
716     struct   MRouteDesc mrDesc;
717     struct   IfDesc     *Dp;
718     unsigned            Ix;
719     int i;
720 
721     for (i = 0; i < MAX_ORIGINS; i++) {
722         if (route->originAddrs[i] == 0 || route->upstrVif == -1) {
723             continue;
724         }
725 
726         // Build route descriptor from table entry...
727         // Set the source address and group address...
728         mrDesc.McAdr.s_addr     = route->group;
729         mrDesc.OriginAdr.s_addr = route->originAddrs[i];
730 
731         // clear output interfaces
732         memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) );
733 
734         my_log(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits);
735 
736         mrDesc.InVif = route->upstrVif;
737 
738         // Set the TTL's for the route descriptor...
739         for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
740             if(Dp->state == IF_STATE_UPSTREAM) {
741                 continue;
742             }
743             else if(BIT_TST(route->vifBits, Dp->index)) {
744                 my_log(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold);
745                 mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
746             }
747         }
748 
749         // Do the actual Kernel route update...
750         if(activate) {
751             // Add route in kernel...
752             addMRoute( &mrDesc );
753         } else {
754             // Delete the route from Kernel...
755             delMRoute( &mrDesc );
756         }
757     }
758 
759     return 1;
760 }
761 
762 /**
763 *   Debug function that writes the routing table entries
764 *   to the log.
765 */
logRouteTable(const char * header)766 void logRouteTable(const char *header) {
767         struct Config       *conf = getCommonConfig();
768         struct RouteTable   *croute = routing_table;
769         unsigned            rcount = 0;
770 
771         my_log(LOG_DEBUG, 0, "");
772         my_log(LOG_DEBUG, 0, "Current routing table (%s):", header);
773         my_log(LOG_DEBUG, 0, "-----------------------------------------------------");
774         if(croute==NULL) {
775             my_log(LOG_DEBUG, 0, "No routes in table...");
776         } else {
777             do {
778                 char st = 'I';
779                 char src[MAX_ORIGINS * 30 + 1];
780                 src[0] = '\0';
781                 int i;
782 
783                 for (i = 0; i < MAX_ORIGINS; i++) {
784                     if (croute->originAddrs[i] == 0) {
785                         continue;
786                     }
787                     st = 'A';
788                     sprintf(src + strlen(src), "Src%d: %s, ", i, inetFmt(croute->originAddrs[i], s1));
789                 }
790 
791                 my_log(LOG_DEBUG, 0, "#%d: %sDst: %s, Age:%d, St: %c, OutVifs: 0x%08x, dHosts: %s",
792                     rcount, src, inetFmt(croute->group, s2),
793                     croute->ageValue, st,
794                     croute->vifBits,
795                     !conf->fastUpstreamLeave ? "not tracked" : testNoDownstreamHost(conf, croute) ? "no" : "yes");
796 
797                 croute = croute->nextroute;
798 
799                 rcount++;
800             } while ( croute != NULL );
801         }
802 
803         my_log(LOG_DEBUG, 0, "-----------------------------------------------------");
804 }
805 
806 /**
807 *   Returns true when the given group belongs to the given interface
808 */
interfaceInRoute(int32_t group,int Ix)809 int interfaceInRoute(int32_t group, int Ix) {
810     struct RouteTable*  croute;
811     croute = findRoute(group);
812     if (croute != NULL) {
813         my_log(LOG_DEBUG, 0, "Interface id %d is in group $d", Ix, group);
814         return BIT_TST(croute->vifBits, Ix);
815     } else {
816         return 0;
817     }
818 }
819