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