Annotation of embedaddon/igmpproxy/src/rttable.c, revision 1.1.1.1
1.1 misho 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, version 2
27: **
28: ** mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of
29: ** Leland Stanford Junior University.
30: ** - Original license can be found in the Stanford.txt file.
31: **
32: */
33: /**
34: * rttable.c
35: *
36: * Updates the routingtable according to
37: * recieved request.
38: */
39:
40: #include "igmpproxy.h"
41:
42: /**
43: * Routing table structure definition. Double linked list...
44: */
45: struct RouteTable {
46: struct RouteTable *nextroute; // Pointer to the next group in line.
47: struct RouteTable *prevroute; // Pointer to the previous group in line.
48: uint32_t group; // The group to route
49: uint32_t originAddr; // The origin adress (only set on activated routes)
50: uint32_t vifBits; // Bits representing recieving VIFs.
51:
52: // Keeps the upstream membership state...
53: short upstrState; // Upstream membership state.
54:
55: // These parameters contain aging details.
56: uint32_t ageVifBits; // Bits representing aging VIFs.
57: int ageValue; // Downcounter for death.
58: int ageActivity; // Records any acitivity that notes there are still listeners.
59: };
60:
61:
62: // Keeper for the routing table...
63: static struct RouteTable *routing_table;
64:
65: // Prototypes
66: void logRouteTable(char *header);
67: int internAgeRoute(struct RouteTable* croute);
68: int internUpdateKernelRoute(struct RouteTable *route, int activate);
69:
70: // Socket for sending join or leave requests.
71: int mcGroupSock = 0;
72:
73:
74: /**
75: * Function for retrieving the Multicast Group socket.
76: */
77: int getMcGroupSock() {
78: if( ! mcGroupSock ) {
79: mcGroupSock = openUdpSocket( INADDR_ANY, 0 );;
80: }
81: return mcGroupSock;
82: }
83:
84: /**
85: * Initializes the routing table.
86: */
87: void initRouteTable() {
88: unsigned Ix;
89: struct IfDesc *Dp;
90:
91: // Clear routing table...
92: routing_table = NULL;
93:
94: // Join the all routers group on downstream vifs...
95: for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
96: // If this is a downstream vif, we should join the All routers group...
97: if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) {
98: my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s",
99: inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2));
100:
101: //k_join(allrouters_group, Dp->InAdr.s_addr);
102: joinMcGroup( getMcGroupSock(), Dp, allrouters_group );
103: }
104: }
105: }
106:
107: /**
108: * Internal function to send join or leave requests for
109: * a specified route upstream...
110: */
111: void sendJoinLeaveUpstream(struct RouteTable* route, int join) {
112: struct IfDesc* upstrIf;
113:
114: // Get the upstream VIF...
115: upstrIf = getIfByIx( upStreamVif );
116: if(upstrIf == NULL) {
117: my_log(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF.");
118: }
119:
120: // Send join or leave request...
121: if(join) {
122:
123: // Only join a group if there are listeners downstream...
124: if(route->vifBits > 0) {
125: my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s",
126: inetFmt(route->group, s1),
127: inetFmt(upstrIf->InAdr.s_addr, s2));
128:
129: //k_join(route->group, upstrIf->InAdr.s_addr);
130: joinMcGroup( getMcGroupSock(), upstrIf, route->group );
131:
132: route->upstrState = ROUTESTATE_JOINED;
133: } else {
134: my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.",
135: inetFmt(route->group, s1));
136: }
137:
138: } else {
139: // Only leave if group is not left already...
140: if(route->upstrState != ROUTESTATE_NOTJOINED) {
141: my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s",
142: inetFmt(route->group, s1),
143: inetFmt(upstrIf->InAdr.s_addr, s2));
144:
145: //k_leave(route->group, upstrIf->InAdr.s_addr);
146: leaveMcGroup( getMcGroupSock(), upstrIf, route->group );
147:
148: route->upstrState = ROUTESTATE_NOTJOINED;
149: }
150: }
151: }
152:
153: /**
154: * Clear all routes from routing table, and alerts Leaves upstream.
155: */
156: void clearAllRoutes() {
157: struct RouteTable *croute, *remainroute;
158:
159: // Loop through all routes...
160: for(croute = routing_table; croute; croute = remainroute) {
161:
162: remainroute = croute->nextroute;
163:
164: // Log the cleanup in debugmode...
165: my_log(LOG_DEBUG, 0, "Removing route entry for %s",
166: inetFmt(croute->group, s1));
167:
168: // Uninstall current route
169: if(!internUpdateKernelRoute(croute, 0)) {
170: my_log(LOG_WARNING, 0, "The removal from Kernel failed.");
171: }
172:
173: // Send Leave message upstream.
174: sendJoinLeaveUpstream(croute, 0);
175:
176: // Clear memory, and set pointer to next route...
177: free(croute);
178: }
179: routing_table = NULL;
180:
181: // Send a notice that the routing table is empty...
182: my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty.");
183: }
184:
185: /**
186: * Private access function to find a route from a given
187: * Route Descriptor.
188: */
189: struct RouteTable *findRoute(uint32_t group) {
190: struct RouteTable* croute;
191:
192: for(croute = routing_table; croute; croute = croute->nextroute) {
193: if(croute->group == group) {
194: return croute;
195: }
196: }
197:
198: return NULL;
199: }
200:
201: /**
202: * Adds a specified route to the routingtable.
203: * If the route already exists, the existing route
204: * is updated...
205: */
206: int insertRoute(uint32_t group, int ifx) {
207:
208: struct Config *conf = getCommonConfig();
209: struct RouteTable* croute;
210:
211: // Sanitycheck the group adress...
212: if( ! IN_MULTICAST( ntohl(group) )) {
213: my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.",
214: inetFmt(group, s1));
215: return 0;
216: }
217:
218: // Santiycheck the VIF index...
219: //if(ifx < 0 || ifx >= MAX_MC_VIFS) {
220: if(ifx >= MAX_MC_VIFS) {
221: my_log(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.",ifx,MAX_MC_VIFS);
222: return 0;
223: }
224:
225: // Try to find an existing route for this group...
226: croute = findRoute(group);
227: if(croute==NULL) {
228: struct RouteTable* newroute;
229:
230: my_log(LOG_DEBUG, 0, "No existing route for %s. Create new.",
231: inetFmt(group, s1));
232:
233:
234: // Create and initialize the new route table entry..
235: newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable));
236: // Insert the route desc and clear all pointers...
237: newroute->group = group;
238: newroute->originAddr = 0;
239: newroute->nextroute = NULL;
240: newroute->prevroute = NULL;
241:
242: // The group is not joined initially.
243: newroute->upstrState = ROUTESTATE_NOTJOINED;
244:
245: // The route is not active yet, so the age is unimportant.
246: newroute->ageValue = conf->robustnessValue;
247: newroute->ageActivity = 0;
248:
249: BIT_ZERO(newroute->ageVifBits); // Initially we assume no listeners.
250:
251: // Set the listener flag...
252: BIT_ZERO(newroute->vifBits); // Initially no listeners...
253: if(ifx >= 0) {
254: BIT_SET(newroute->vifBits, ifx);
255: }
256:
257: // Check if there is a table already....
258: if(routing_table == NULL) {
259: // No location set, so insert in on the table top.
260: routing_table = newroute;
261: my_log(LOG_DEBUG, 0, "No routes in table. Insert at beginning.");
262: } else {
263:
264: my_log(LOG_DEBUG, 0, "Found existing routes. Find insert location.");
265:
266: // Check if the route could be inserted at the beginning...
267: if(routing_table->group > group) {
268: my_log(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1));
269:
270: // Insert at beginning...
271: newroute->nextroute = routing_table;
272: newroute->prevroute = NULL;
273: routing_table = newroute;
274:
275: // If the route has a next node, the previous pointer must be updated.
276: if(newroute->nextroute != NULL) {
277: newroute->nextroute->prevroute = newroute;
278: }
279:
280: } else {
281:
282: // Find the location which is closest to the route.
283: for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) {
284: // Find insert position.
285: if(croute->nextroute->group > group) {
286: break;
287: }
288: }
289:
290: my_log(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1));
291:
292: // Insert after current...
293: newroute->nextroute = croute->nextroute;
294: newroute->prevroute = croute;
295: if(croute->nextroute != NULL) {
296: croute->nextroute->prevroute = newroute;
297: }
298: croute->nextroute = newroute;
299: }
300: }
301:
302: // Set the new route as the current...
303: croute = newroute;
304:
305: // Log the cleanup in debugmode...
306: my_log(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d",
307: inetFmt(croute->group, s1),ifx);
308:
309: } else if(ifx >= 0) {
310:
311: // The route exists already, so just update it.
312: BIT_SET(croute->vifBits, ifx);
313:
314: // Register the VIF activity for the aging routine
315: BIT_SET(croute->ageVifBits, ifx);
316:
317: // Log the cleanup in debugmode...
318: my_log(LOG_INFO, 0, "Updated route entry for %s on VIF #%d",
319: inetFmt(croute->group, s1), ifx);
320:
321: // If the route is active, it must be reloaded into the Kernel..
322: if(croute->originAddr != 0) {
323:
324: // Update route in kernel...
325: if(!internUpdateKernelRoute(croute, 1)) {
326: my_log(LOG_WARNING, 0, "The insertion into Kernel failed.");
327: return 0;
328: }
329: }
330: }
331:
332: // Send join message upstream, if the route has no joined flag...
333: if(croute->upstrState != ROUTESTATE_JOINED) {
334: // Send Join request upstream
335: sendJoinLeaveUpstream(croute, 1);
336: }
337:
338: logRouteTable("Insert Route");
339:
340: return 1;
341: }
342:
343: /**
344: * Activates a passive group. If the group is already
345: * activated, it's reinstalled in the kernel. If
346: * the route is activated, no originAddr is needed.
347: */
348: int activateRoute(uint32_t group, uint32_t originAddr) {
349: struct RouteTable* croute;
350: int result = 0;
351:
352: // Find the requested route.
353: croute = findRoute(group);
354: if(croute == NULL) {
355: my_log(LOG_DEBUG, 0,
356: "No table entry for %s [From: %s]. Inserting route.",
357: inetFmt(group, s1),inetFmt(originAddr, s2));
358:
359: // Insert route, but no interfaces have yet requested it downstream.
360: insertRoute(group, -1);
361:
362: // Retrieve the route from table...
363: croute = findRoute(group);
364: }
365:
366: if(croute != NULL) {
367: // If the origin address is set, update the route data.
368: if(originAddr > 0) {
369: if(croute->originAddr > 0 && croute->originAddr!=originAddr) {
370: my_log(LOG_WARNING, 0, "The origin for route %s changed from %s to %s",
371: inetFmt(croute->group, s1),
372: inetFmt(croute->originAddr, s2),
373: inetFmt(originAddr, s3));
374: }
375: croute->originAddr = originAddr;
376: }
377:
378: // Only update kernel table if there are listeners !
379: if(croute->vifBits > 0) {
380: result = internUpdateKernelRoute(croute, 1);
381: }
382: }
383: logRouteTable("Activate Route");
384:
385: return result;
386: }
387:
388:
389: /**
390: * This function loops through all routes, and updates the age
391: * of any active routes.
392: */
393: void ageActiveRoutes() {
394: struct RouteTable *croute, *nroute;
395:
396: my_log(LOG_DEBUG, 0, "Aging routes in table.");
397:
398: // Scan all routes...
399: for( croute = routing_table; croute != NULL; croute = nroute ) {
400:
401: // Keep the next route (since current route may be removed)...
402: nroute = croute->nextroute;
403:
404: // Run the aging round algorithm.
405: if(croute->upstrState != ROUTESTATE_CHECK_LAST_MEMBER) {
406: // Only age routes if Last member probe is not active...
407: internAgeRoute(croute);
408: }
409: }
410: logRouteTable("Age active routes");
411: }
412:
413: /**
414: * Should be called when a leave message is recieved, to
415: * mark a route for the last member probe state.
416: */
417: void setRouteLastMemberMode(uint32_t group) {
418: struct Config *conf = getCommonConfig();
419: struct RouteTable *croute;
420:
421: croute = findRoute(group);
422: if(croute!=NULL) {
423: // Check for fast leave mode...
424: if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) {
425: // Send a leave message right away..
426: sendJoinLeaveUpstream(croute, 0);
427: }
428: // Set the routingstate to Last member check...
429: croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER;
430: // Set the count value for expiring... (-1 since first aging)
431: croute->ageValue = conf->lastMemberQueryCount;
432: }
433: }
434:
435:
436: /**
437: * Ages groups in the last member check state. If the
438: * route is not found, or not in this state, 0 is returned.
439: */
440: int lastMemberGroupAge(uint32_t group) {
441: struct RouteTable *croute;
442:
443: croute = findRoute(group);
444: if(croute!=NULL) {
445: if(croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER) {
446: return !internAgeRoute(croute);
447: } else {
448: return 0;
449: }
450: }
451: return 0;
452: }
453:
454: /**
455: * Remove a specified route. Returns 1 on success,
456: * and 0 if route was not found.
457: */
458: int removeRoute(struct RouteTable* croute) {
459: struct Config *conf = getCommonConfig();
460: int result = 1;
461:
462: // If croute is null, no routes was found.
463: if(croute==NULL) {
464: return 0;
465: }
466:
467: // Log the cleanup in debugmode...
468: my_log(LOG_DEBUG, 0, "Removed route entry for %s from table.",
469: inetFmt(croute->group, s1));
470:
471: //BIT_ZERO(croute->vifBits);
472:
473: // Uninstall current route from kernel
474: if(!internUpdateKernelRoute(croute, 0)) {
475: my_log(LOG_WARNING, 0, "The removal from Kernel failed.");
476: result = 0;
477: }
478:
479: // Send Leave request upstream if group is joined
480: if(croute->upstrState == ROUTESTATE_JOINED ||
481: (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave))
482: {
483: sendJoinLeaveUpstream(croute, 0);
484: }
485:
486: // Update pointers...
487: if(croute->prevroute == NULL) {
488: // Topmost node...
489: if(croute->nextroute != NULL) {
490: croute->nextroute->prevroute = NULL;
491: }
492: routing_table = croute->nextroute;
493:
494: } else {
495: croute->prevroute->nextroute = croute->nextroute;
496: if(croute->nextroute != NULL) {
497: croute->nextroute->prevroute = croute->prevroute;
498: }
499: }
500: // Free the memory, and set the route to NULL...
501: free(croute);
502: croute = NULL;
503:
504: logRouteTable("Remove route");
505:
506: return result;
507: }
508:
509:
510: /**
511: * Ages a specific route
512: */
513: int internAgeRoute(struct RouteTable* croute) {
514: struct Config *conf = getCommonConfig();
515: int result = 0;
516:
517: // Drop age by 1.
518: croute->ageValue--;
519:
520: // Check if there has been any activity...
521: if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) {
522: // There was some activity, check if all registered vifs responded.
523: if(croute->vifBits == croute->ageVifBits) {
524: // Everything is in perfect order, so we just update the route age.
525: croute->ageValue = conf->robustnessValue;
526: //croute->ageActivity = 0;
527: } else {
528: // One or more VIF has not gotten any response.
529: croute->ageActivity++;
530:
531: // Update the actual bits for the route...
532: croute->vifBits = croute->ageVifBits;
533: }
534: }
535: // Check if there have been activity in aging process...
536: else if( croute->ageActivity > 0 ) {
537:
538: // If the bits are different in this round, we must
539: if(croute->vifBits != croute->ageVifBits) {
540: // Or the bits together to insure we don't lose any listeners.
541: croute->vifBits |= croute->ageVifBits;
542:
543: // Register changes in this round as well..
544: croute->ageActivity++;
545: }
546: }
547:
548: // If the aging counter has reached zero, its time for updating...
549: if(croute->ageValue == 0) {
550: // Check for activity in the aging process,
551: if(croute->ageActivity>0) {
552:
553: my_log(LOG_DEBUG, 0, "Updating route after aging : %s",
554: inetFmt(croute->group,s1));
555:
556: // Just update the routing settings in kernel...
557: internUpdateKernelRoute(croute, 1);
558:
559: // We append the activity counter to the age, and continue...
560: croute->ageValue = croute->ageActivity;
561: croute->ageActivity = 0;
562: } else {
563:
564: my_log(LOG_DEBUG, 0, "Removing group %s. Died of old age.",
565: inetFmt(croute->group,s1));
566:
567: // No activity was registered within the timelimit, so remove the route.
568: removeRoute(croute);
569: }
570: // Tell that the route was updated...
571: result = 1;
572: }
573:
574: // The aging vif bits must be reset for each round...
575: BIT_ZERO(croute->ageVifBits);
576:
577: return result;
578: }
579:
580: /**
581: * Updates the Kernel routing table. If activate is 1, the route
582: * is (re-)activated. If activate is false, the route is removed.
583: */
584: int internUpdateKernelRoute(struct RouteTable *route, int activate) {
585: struct MRouteDesc mrDesc;
586: struct IfDesc *Dp;
587: unsigned Ix;
588:
589: if(route->originAddr>0) {
590:
591: // Build route descriptor from table entry...
592: // Set the source address and group address...
593: mrDesc.McAdr.s_addr = route->group;
594: mrDesc.OriginAdr.s_addr = route->originAddr;
595:
596: // clear output interfaces
597: memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) );
598:
599: my_log(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits);
600:
601: // Set the TTL's for the route descriptor...
602: for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
603: if(Dp->state == IF_STATE_UPSTREAM) {
604: mrDesc.InVif = Dp->index;
605: }
606: else if(BIT_TST(route->vifBits, Dp->index)) {
607: my_log(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold);
608: mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
609: }
610: }
611:
612: // Do the actual Kernel route update...
613: if(activate) {
614: // Add route in kernel...
615: addMRoute( &mrDesc );
616:
617: } else {
618: // Delete the route from Kernel...
619: delMRoute( &mrDesc );
620: }
621:
622: } else {
623: my_log(LOG_NOTICE, 0, "Route is not active. No kernel updates done.");
624: }
625:
626: return 1;
627: }
628:
629: /**
630: * Debug function that writes the routing table entries
631: * to the log.
632: */
633: void logRouteTable(char *header) {
634: struct RouteTable* croute = routing_table;
635: unsigned rcount = 0;
636:
637: my_log(LOG_DEBUG, 0, "");
638: my_log(LOG_DEBUG, 0, "Current routing table (%s):", header);
639: my_log(LOG_DEBUG, 0, "-----------------------------------------------------");
640: if(croute==NULL) {
641: my_log(LOG_DEBUG, 0, "No routes in table...");
642: } else {
643: do {
644: /*
645: my_log(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, Prev: 0x%08x, T: 0x%08x, Next: 0x%08x",
646: rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
647: croute->ageValue,(croute->originAddr>0?"A":"I"),
648: croute->prevroute, croute, croute->nextroute);
649: */
650: my_log(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, OutVifs: 0x%08x",
651: rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
652: croute->ageValue,(croute->originAddr>0?"A":"I"),
653: croute->vifBits);
654:
655: croute = croute->nextroute;
656:
657: rcount++;
658: } while ( croute != NULL );
659: }
660:
661: my_log(LOG_DEBUG, 0, "-----------------------------------------------------");
662: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>