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 "doc/mrouted-LINCESE" file.
31**
32*/
33/**
34*   rttable.c
35*
36*   Updates the routingtable according to
37*     recieved request.
38*/
39
40#include "defs.h"
41
42
43
44
45// Keeper for the routing table...
46#ifdef USE_ATH_HEADER
47static struct RouteTable   *routing_table;
48#else
49struct RouteTable   *routing_table;
50#endif
51
52// Prototypes
53void logRouteTable(char *header);
54int  internAgeRoute(struct RouteTable*  croute);
55
56// Socket for sending join or leave requests.
57int mcGroupSock = 0;
58
59#if (0)
60void v2_join_group(int port, uint32 mgroup)
61{
62	char line[64];
63	if (port > 0)
64	{
65		sprintf(line,"ebtables -I port%d -p ipv4 --ip-dst %u.%u.%u.%u -j ACCEPT", port, NIPQUAD(mgroup));
66		system(line);
67	}
68}
69
70void v2_leave_group(int port, uint32 mgroup)
71{
72	char line[64];
73	if (port > 0)
74	{
75		sprintf(line,"ebtables -D port%d -p ipv4 --ip-dst %u.%u.%u.%u -j ACCEPT", port, NIPQUAD(mgroup));
76		system(line);
77	}
78}
79
80void v2_port_update (struct RouteTable *croute, int oldport, int newport)
81{
82	uint32 port[PORT_MAX_NUM+1];
83	memcpy(port, croute->port, sizeof(uint32)*(PORT_MAX_NUM+1));
84	port[newport]++;
85	if (port[oldport]) port[oldport]--;
86	if (port[oldport] == 0)
87		v2_leave_group(oldport, croute->group);
88	if (port[newport] == 1)
89		v2_join_group(newport, croute->group);
90	memcpy( croute->port, port, sizeof(uint32)*(PORT_MAX_NUM+1));
91}
92#endif
93
94#ifdef USE_ATH_HEADER
95int internUpdateKernelRoute(struct RouteTable *route, int activate);
96static inline void
97update_port(struct RouteTable *croute, uint32 oldport, uint32 newport)
98{
99	uint32 i, ifbits = 0, port[PORT_MAX_NUM+1];
100	memcpy(port, croute->port, sizeof(uint32)*(PORT_MAX_NUM+1));
101	port[newport]++;
102	if (port[oldport]) port[oldport]--;
103
104	if ((port[newport] == 1) || (port[oldport] == 0))
105	{
106		DPRINTF("%s :: group [%x] port snoop status changed, to update the fdb table....\n", __FUNCTION__, croute->group);
107		for (i = 1; i < PORT_MAX_NUM+1; i++)
108		{
109			if (port[i])
110				ifbits |= (1<<i);
111		}
112		internUpdateFdb(croute->group, ifbits, 1);
113	}
114	memcpy( croute->port, port, sizeof(uint32)*(PORT_MAX_NUM+1));
115}
116#endif
117/**
118*   Function for retrieving the Multicast Group socket.
119*/
120int getMcGroupSock() {
121    if( ! mcGroupSock ) {
122        mcGroupSock = openUdpSocket( INADDR_ANY, 0 );;
123    }
124    return mcGroupSock;
125}
126
127/**
128*   Initializes the routing table.
129*/
130void initRouteTable() {
131    unsigned Ix;
132    struct IfDesc *Dp;
133
134    // Clear routing table...
135    routing_table = NULL;
136
137    // Join the all routers group on downstream vifs...
138    for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
139        // If this is a downstream vif, we should join the All routers group...
140        if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) {
141            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s",
142                         inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2));
143
144            joinMcGroup( getMcGroupSock(), Dp, allrouters_group );
145
146#if (SUPPORT_IGMPV3)
147            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s",
148                         inetFmt(all_group_igmpv3,s1),inetFmt(Dp->InAdr.s_addr,s2));
149
150            joinMcGroup( getMcGroupSock(), Dp, all_group_igmpv3 );
151#endif
152        }
153    }
154}
155
156/**
157*   Internal function to send join or leave requests for
158*   a specified route upstream...
159*/
160void sendJoinLeaveUpstream(struct RouteTable* route, int join) {
161    struct IfDesc*      upstrIf;
162
163    // Get the upstream VIF...
164    upstrIf = getIfByIx( upStreamVif );
165    if(upstrIf == NULL) {
166        igmp_syslog(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF.");
167    }
168
169    IF_DEBUG {
170        igmp_syslog(LOG_DEBUG, 0, "Upstream IF addr  : %s", inetFmt(upstrIf->InAdr.s_addr,s1));
171        igmp_syslog(LOG_DEBUG, 0, "Upstream IF state : %d", upstrIf->state);
172        igmp_syslog(LOG_DEBUG, 0, "Upstream IF index : %d", upstrIf->index);
173    }
174
175    // Send join or leave request...
176    if(join) {
177
178        // Only join a group if there are listeners downstream...
179        if(route->vifBits > 0) {
180            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s",
181                         inetFmt(route->group, s1),
182                         inetFmt(upstrIf->InAdr.s_addr, s2));
183
184            //k_join(route->group, upstrIf->InAdr.s_addr);
185            joinMcGroup( getMcGroupSock(), upstrIf, route->group );
186
187            route->upstrState = ROUTESTATE_JOINED;
188        } else IF_DEBUG {
189            igmp_syslog(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.",
190                inetFmt(route->group, s1));
191        }
192
193    } else {
194        // Only leave if group is not left already...
195        if(route->upstrState != ROUTESTATE_NOTJOINED) {
196            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s",
197                         inetFmt(route->group, s1),
198                         inetFmt(upstrIf->InAdr.s_addr, s2));
199
200			leaveMcGroup( getMcGroupSock(), upstrIf, route->group );
201
202            route->upstrState = ROUTESTATE_NOTJOINED;
203        }
204    }
205}
206
207/**
208*   Clear all routes from routing table, and alerts Leaves upstream.
209*/
210void clearAllRoutes() {
211    struct RouteTable   *croute, *remainroute;
212
213    // Loop through all routes...
214    for(croute = routing_table; croute; croute = remainroute) {
215
216        remainroute = croute->nextroute;
217
218        // Log the cleanup in debugmode...
219        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removing route entry for %s",
220                     inetFmt(croute->group, s1));
221
222        // Uninstall current route
223        if(!internUpdateKernelRoute(croute, 0)) {
224            igmp_syslog(LOG_WARNING, 0, "The removal from Kernel failed.");
225        }
226
227        // Send Leave message upstream.
228        sendJoinLeaveUpstream(croute, 0);
229
230        // Clear memory, and set pointer to next route...
231        free(croute);
232    }
233    routing_table = NULL;
234
235    // Send a notice that the routing table is empty...
236    igmp_syslog(LOG_NOTICE, 0, "All routes removed. Routing table is empty.");
237}
238
239/**
240*   Private access function to find a route from a given
241*   Route Descriptor.
242*/
243struct RouteTable *findRoute(uint32 group) {
244    struct RouteTable*  croute;
245
246    for(croute = routing_table; croute; croute = croute->nextroute) {
247        if(croute->group == group) {
248            return croute;
249        }
250    }
251
252    return NULL;
253}
254
255#if (SUPPURT_MCAST_TO_UNICAST)
256static inline int
257leave_group(unsigned long src, unsigned long group)
258{
259	char line[64];
260	sprintf(line, "echo \"d %d.%d.%d.%d %d.%d.%d.%d\" > /proc/mcast",
261		NIPQUAD(src), NIPQUAD(group));
262//	DPRINTF("%s\n",line);
263	system(line);
264	return 0;
265}
266#endif
267
268#if (IGMP_IMMEDIATE_LEAVE)
269void update_listener(struct RouteTable*  croute)
270{
271	struct Listener *listener;
272	struct Listener *nlistener = NULL;
273	uint32 now = GetSysUpTime();
274	listener = croute->listeners;
275
276	while (listener)
277	{
278		nlistener = listener->nextlistener;
279		if (now - listener->timeout >= IGMP_MBR_TIMEOUT)
280			setRouteLastMemberMode(croute->group, listener->srcAddr);
281		listener = nlistener;
282	}
283}
284
285void check_mbr_timeout(void)
286{
287	struct RouteTable*  croute = routing_table;
288	struct RouteTable*  nroute = NULL;
289
290	while(croute)
291	{
292		nroute = croute->nextroute;
293		update_listener(croute);
294		croute = nroute;
295	}
296
297	timer_setTimer( INTERVAL_QUERY_RESPONSE, check_mbr_timeout, NULL);
298}
299
300struct Listener *findListener(struct RouteTable*  croute, uint32 src)
301{
302	struct Listener*  listener;
303
304    for(listener = croute->listeners; listener; listener = listener->nextlistener) {
305        if(listener->srcAddr == src) {
306            listener->timeout = GetSysUpTime();
307            return listener;
308        }
309    }
310
311    return NULL;
312}
313#ifdef USE_ATH_HEADER
314struct Listener* insertListener(uint32 port, struct RouteTable*  croute, uint32 src) {
315#else
316void insertListener(struct RouteTable*  croute, uint32 src) {
317#endif
318	struct Listener*  newlistener;
319
320	newlistener = (struct Listener*)malloc(sizeof(struct Listener));
321	newlistener->nextlistener = NULL;
322	newlistener->srcAddr = src;
323	newlistener->timeout = GetSysUpTime();
324#ifdef USE_ATH_HEADER
325	newlistener->port = port;
326#if !(DNI_MULTI_LAN_SUPPORT)
327	update_port(croute, 0, port);
328#endif
329#endif
330	if(croute->listeners == NULL)
331	{
332		croute->listeners = newlistener;
333	}
334	else
335	{
336	    struct Listener*  listener;
337
338	    listener = croute->listeners;
339
340		for(listener = croute->listeners; listener; listener = listener->nextlistener)
341		{
342			if(listener->nextlistener == NULL)
343			{
344				listener->nextlistener = newlistener;
345				break;
346			}
347		}
348	}
349#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3)
350	newlistener->mode = st_is_ex;
351	newlistener->version = IGMP_V2_MEMBERSHIP_REPORT;
352	memcpy(&newlistener->source, &ZEROSET, sizeof(ZEROSET));
353#endif
354	return newlistener;
355}
356
357int removeListener(struct RouteTable*  croute, uint32 src)
358{
359    struct Listener*  listener;
360	struct Listener*  prevlistener;
361    int result = 0;
362
363	if(croute->listeners == NULL)
364		return 0;
365
366
367	prevlistener = NULL;
368	listener = croute->listeners;
369    while(listener != NULL)
370    {
371		if(listener->srcAddr == src)
372            break;
373
374      	prevlistener = listener;
375      	listener = listener->nextlistener;
376    }
377// modified by Kane
378// to avoid some abnormal conditions
379// if client A joined the group G and client B leaved the group G without joining,
380// the igmpproxy will be crashed
381    if (listener == NULL)
382    {
383        DPRINTF("%s :: listener is null ... group [%x] has entry [%d]\n", __FUNCTION__,
384			croute->group, (croute->listeners)?1:0);
385        if (croute->listeners) // there are some clients joined the group
386            return 1;
387        else
388            return 0; // there is no client joined the group
389    }
390#ifdef USE_ATH_HEADER
391#if !(DNI_MULTI_LAN_SUPPORT)
392	update_port(croute, listener->port, 0);
393#endif
394#endif
395    // Free the memory
396    if((prevlistener == NULL) && (listener->nextlistener == NULL))
397    {
398    	 croute->listeners = NULL;
399    }
400    else if((prevlistener == NULL) && (listener->nextlistener != NULL))
401    {
402    	 croute->listeners = listener->nextlistener;
403        result = 1;
404    }
405    else
406    {
407         prevlistener->nextlistener = listener->nextlistener;
408         result = 1;
409    }
410
411	free(listener);
412	return result;
413}
414
415#endif
416
417/**
418*   Adds a specified route to the routingtable.
419*   If the route already exists, the existing route
420*   is updated...
421*/
422#if (IGMP_IMMEDIATE_LEAVE)
423#ifdef USE_ATH_HEADER
424int insertRoute(uint32 port, uint32 group, int ifx, uint32 src) {
425#else
426int insertRoute(uint32 group, int ifx, uint32 src) {
427#endif
428#else
429int insertRoute(uint32 group, int ifx) {
430#endif
431
432    struct Config *conf = getCommonConfig();
433    struct RouteTable*  croute;
434    //int result = 1;
435#ifdef USE_ATH_HEADER
436	int i;
437#endif
438    // Sanitycheck the group adress...
439    if( ! IN_MULTICAST( ntohl(group) )) {
440        igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.",
441            inetFmt(group, s1));
442        return 0;
443    }
444
445    // Santiycheck the VIF index...
446    //if(ifx < 0 || ifx >= MAX_MC_VIFS) {
447    if(ifx >= MAX_MC_VIFS) {
448        igmp_syslog(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.",ifx,MAX_MC_VIFS);
449        return 0;
450    }
451
452    // Try to find an existing route for this group...
453    croute = findRoute(group);
454    if(croute==NULL) {
455        struct RouteTable*  newroute;
456
457        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No existing route for %s. Create new.",
458                     inetFmt(group, s1));
459
460
461        // Create and initialize the new route table entry..
462        newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable));
463        // Insert the route desc and clear all pointers...
464        newroute->group      = group;
465        newroute->originAddr = 0;
466        newroute->nextroute  = NULL;
467        newroute->prevroute  = NULL;
468
469        // The group is not joined initially.
470        newroute->upstrState = ROUTESTATE_NOTJOINED;
471
472        // The route is not active yet, so the age is unimportant.
473        newroute->ageValue    = conf->robustnessValue;
474        newroute->ageActivity = 0;
475#ifdef USE_ATH_HEADER
476	for (i = 0; i <= PORT_MAX_NUM; i++)
477		newroute->port[i] = 0;
478	newroute->port[port]++;
479#endif
480#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3)
481	memset(&newroute->source, &ZEROSET, sizeof(ZEROSET));
482	for (i = 0; i <= PORT_MAX_NUM; i++)
483	{
484		memcpy(&newroute->port_source[i], &ZEROSET, sizeof(ZEROSET));
485		newroute->port_source[i].num = PORT_INIT_STATE;
486		newroute->port_mode[i] = st_is_in;
487	}
488	newroute->mode = 0;
489#endif
490#if (IGMP_IMMEDIATE_LEAVE)
491		newroute->listeners = NULL;
492		if(src != 0)
493		{
494			struct Listener*  newlistener;
495
496	        	newlistener = (struct Listener*)malloc(sizeof(struct Listener));
497	        	newlistener->nextlistener = NULL;
498	        	newlistener->srcAddr = src;
499			newroute->listeners = newlistener;
500#ifdef USE_ATH_HEADER
501			newlistener->port = port;
502#endif
503#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3)
504			newlistener->mode = st_is_ex;
505			newlistener->version = IGMP_V2_MEMBERSHIP_REPORT;
506			memcpy(&newlistener->source, &ZEROSET, sizeof(ZEROSET));
507#endif
508		}
509#endif
510
511        BIT_ZERO(newroute->ageVifBits);     // Initially we assume no listeners.
512
513        // Set the listener flag...
514        BIT_ZERO(newroute->vifBits);    // Initially no listeners...
515        if(ifx >= 0) {
516            BIT_SET(newroute->vifBits, ifx);
517        }
518
519        // Check if there is a table already....
520        if(routing_table == NULL) {
521            // No location set, so insert in on the table top.
522            routing_table = newroute;
523            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No routes in table. Insert at beginning.");
524        } else {
525
526            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Found existing routes. Find insert location.");
527
528            // Check if the route could be inserted at the beginning...
529            if(routing_table->group > group) {
530                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1));
531
532                // Insert at beginning...
533                newroute->nextroute = routing_table;
534                newroute->prevroute = NULL;
535                routing_table = newroute;
536
537                // If the route has a next node, the previous pointer must be updated.
538                if(newroute->nextroute != NULL) {
539                    newroute->nextroute->prevroute = newroute;
540                }
541
542            } else {
543
544                // Find the location which is closest to the route.
545                for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) {
546                    // Find insert position.
547                    if(croute->nextroute->group > group) {
548                        break;
549                    }
550                }
551
552                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1));
553
554                // Insert after current...
555                newroute->nextroute = croute->nextroute;
556                newroute->prevroute = croute;
557                if(croute->nextroute != NULL) {
558                    croute->nextroute->prevroute = newroute;
559                }
560                croute->nextroute = newroute;
561            }
562        }
563
564        // Set the new route as the current...
565        croute = newroute;
566
567        // Log the cleanup in debugmode...
568        igmp_syslog(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d",
569            inetFmt(croute->group, s1),ifx);
570#ifdef USE_ATH_HEADER
571	if(commonConfig.mode&0x01){
572        	IF_DEBUG atlog(LOG_DEBUG, 0,
573                     "Should insert group %s port %d to fdb table.",
574                     inetFmt(group, s1),port);
575#if !(DNI_MULTI_LAN_SUPPORT)
576        	if (port > 0)
577			insertFdb(group, port);
578#endif
579	}
580#endif
581    } else if(ifx >= 0) {
582
583        // The route exists already, so just update it.
584        BIT_SET(croute->vifBits, ifx);
585
586        // Register the VIF activity for the aging routine
587        BIT_SET(croute->ageVifBits, ifx);
588
589        // Log the cleanup in debugmode...
590        igmp_syslog(LOG_INFO, 0, "Updated route entry for %s on VIF #%d",
591            inetFmt(croute->group, s1), ifx);
592
593#if (IGMP_IMMEDIATE_LEAVE)
594		if(src != 0)
595		{
596#ifdef USE_ATH_HEADER
597			struct Listener *mbr;
598			if((mbr = findListener(croute, src)) == NULL)
599			{
600				insertListener(port, croute, src);
601			}
602			else
603			{
604#if !(DNI_MULTI_LAN_SUPPORT)
605				// if we found the client switch the port, try to update the snooping status.
606				if(mbr->port != port)
607				{
608					update_port(croute, mbr->port, port);
609					mbr->port = port;
610				}
611#endif
612			}
613#else
614			if(findListener(croute, src) == NULL)
615			{
616				insertListener(croute, src);
617			}
618
619#endif
620			update_listener(croute);
621		}
622#endif
623
624        // If the route is active, it must be reloaded into the Kernel..
625        if(croute->originAddr != 0) {
626            // Update route in kernel...
627            if(!internUpdateKernelRoute(croute, 1)) {
628                igmp_syslog(LOG_WARNING, 0, "The insertion into Kernel failed.");
629                return 0;
630            }
631        }
632    }
633
634    // Send join message upstream, if the route has no joined flag...
635    if(croute->upstrState != ROUTESTATE_JOINED) {
636	// Send Join request upstream
637	sendJoinLeaveUpstream(croute, 1);
638    }
639	//send join report if there are listeners downstream in case some unfriendly video server provider periodically need IGMP report for giving video stream.
640    else if ( !strcmp(config_get("enable_bt_igmp"), "1") && croute->originAddr != 0 && croute->vifBits > 0)
641    {
642	struct IfDesc*      upstrIf;
643	upstrIf = getIfByIx( upStreamVif );
644	sendIgmp(upstrIf->InAdr.s_addr, all_group_igmpv3, IGMP_V3_MEMBERSHIP_REPORT, 0, group, sizeof(struct igmpv3_grec));
645	//sendIgmp(upstrIf->InAdr.s_addr, allrouters_group, IGMP_V2_MEMBERSHIP_REPORT, 0, group, 0); // send IGMPV2 join report
646    }
647
648    IF_DEBUG logRouteTable("Insert Route");
649
650    return 1;
651}
652
653/**
654*   Activates a passive group. If the group is already
655*   activated, it's reinstalled in the kernel. If
656*   the route is activated, no originAddr is needed.
657*/
658int activateRoute(uint32 group, uint32 originAddr) {
659    struct RouteTable*  croute;
660    int result = 0;
661
662    // Find the requested route.
663    croute = findRoute(group);
664    if(croute == NULL) {
665        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No table entry for %s [From: %s]. Inserting route.",
666            inetFmt(group, s1),inetFmt(originAddr, s2));
667
668        // Insert route, but no interfaces have yet requested it downstream.
669#if (IGMP_IMMEDIATE_LEAVE)
670#ifdef USE_ATH_HEADER
671        insertRoute(0, group, -1, 0);
672#else
673        insertRoute(group, -1, 0);
674#endif
675#else
676        insertRoute(group, -1);
677#endif
678        // Retrieve the route from table...
679        croute = findRoute(group);
680    }
681
682    if(croute != NULL) {
683        // If the origin address is set, update the route data.
684        if(originAddr > 0) {
685            if(croute->originAddr > 0 && croute->originAddr!=originAddr) {
686                igmp_syslog(LOG_WARNING, 0, "The origin for route %s changed from %s to %s",
687                    inetFmt(croute->group, s1),
688                    inetFmt(croute->originAddr, s2),
689                    inetFmt(originAddr, s3));
690            }
691            croute->originAddr = originAddr;
692        }
693
694        // Only update kernel table if there are listeners !
695        if(croute->vifBits > 0) {
696            result = internUpdateKernelRoute(croute, 1);
697        }
698    }
699    IF_DEBUG logRouteTable("Activate Route");
700
701    return result;
702}
703
704
705/**
706*   This function loops through all routes, and updates the age
707*   of any active routes.
708*/
709void ageActiveRoutes() {
710    struct RouteTable   *croute, *nroute;
711
712    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Aging routes in table.");
713
714    // Scan all routes...
715    for( croute = routing_table; croute != NULL; croute = nroute ) {
716
717        // Keep the next route (since current route may be removed)...
718        nroute = croute->nextroute;
719        // Run the aging round algorithm.
720        if(croute->upstrState != ROUTESTATE_CHECK_LAST_MEMBER) {
721            // Only age routes if Last member probe is not active...
722            internAgeRoute(croute);
723        }
724    }
725    IF_DEBUG logRouteTable("Age active routes");
726}
727
728/**
729*   Should be called when a leave message is recieved, to
730*   mark a route for the last member probe state.
731*/
732#if (IGMP_IMMEDIATE_LEAVE)
733void setRouteLastMemberMode(uint32 group, uint32 src) {
734#else
735void setRouteLastMemberMode(uint32 group) {
736#endif
737    struct Config       *conf = getCommonConfig();
738    struct RouteTable   *croute;
739
740    croute = findRoute(group);
741    if(croute!=NULL) {
742        // Check for fast leave mode...
743        if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) {
744#if (IGMP_IMMEDIATE_LEAVE)
745            if( removeListener(croute, src) == 0 )
746            {
747	            BIT_ZERO(croute->ageVifBits);
748
749	            // No activity was registered within the timelimit, so remove the route.
750	            removeRoute(croute);
751		}
752		else
753		{
754#if (DNI_MULTI_LAN_SUPPORT)
755			update_all_ports(croute);
756#endif
757			if(croute->listeners == NULL)
758			{
759				// Send a leave message right away..
760				sendJoinLeaveUpstream(croute, 0);
761			}
762		}
763#else
764            // Send a leave message right away..
765            sendJoinLeaveUpstream(croute, 0);
766#endif
767#if (SUPPURT_MCAST_TO_UNICAST)
768		leave_group(src, group);
769#endif
770        }
771        // Set the routingstate to Last member check...
772#if (!IGMP_IMMEDIATE_LEAVE)
773        croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER;
774        // Set the count value for expiring... (-1 since first aging)
775        croute->ageValue = conf->lastMemberQueryCount;
776#endif
777
778    }
779}
780
781
782/**
783*   Ages groups in the last member check state. If the
784*   route is not found, or not in this state, 0 is returned.
785*/
786int lastMemberGroupAge(uint32 group) {
787    struct Config       *conf = NULL;
788    struct RouteTable   *croute;
789
790    conf = getCommonConfig();
791
792    croute = findRoute(group);
793    if(croute!=NULL) {
794        if(croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER) {
795            return !internAgeRoute(croute);
796        } else {
797            return 0;
798        }
799    }
800    return 0;
801}
802
803/**
804*   Remove a specified route. Returns 1 on success,
805*   and 0 if route was not found.
806*/
807int removeRoute(struct RouteTable*  croute) {
808    struct Config       *conf = getCommonConfig();
809    int result = 1;
810
811    // If croute is null, no routes was found.
812    if(croute==NULL) {
813        return 0;
814    }
815#if (IGMP_IMMEDIATE_LEAVE)
816    if (croute->listeners)
817    {
818         struct Listener  *listener, *nextlistener;
819         DPRINTF("%s :: to remove entry [%x]\n", __FUNCTION__, croute->group);
820	  listener = croute->listeners;
821	  nextlistener = listener->nextlistener;
822	  while (nextlistener)
823	  {
824#if (SUPPURT_MCAST_TO_UNICAST)
825		leave_group(listener->srcAddr, croute->group);
826#endif
827	  	free(listener);
828		listener = nextlistener;
829		nextlistener = listener->nextlistener;
830	  }
831#if (SUPPURT_MCAST_TO_UNICAST)
832	leave_group(listener->srcAddr, croute->group);
833#endif
834	  free(listener);
835	  croute->listeners = NULL;
836    }
837#if (DNI_MULTI_LAN_SUPPORT)
838	update_all_ports(croute);
839#endif
840#endif
841    // Log the cleanup in debugmode...
842    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removed route entry for %s from table.",
843                 inetFmt(croute->group, s1));
844
845    //BIT_ZERO(croute->vifBits);
846
847    // Uninstall current route from kernel
848    if(!internUpdateKernelRoute(croute, 0)) {
849        igmp_syslog(LOG_WARNING, 0, "The removal from Kernel failed.");
850        result = 0;
851    }
852
853    // Send Leave request upstream if group is joined
854    if(croute->upstrState == ROUTESTATE_JOINED ||
855       (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave))
856    {
857        sendJoinLeaveUpstream(croute, 0);
858    }
859
860    // Update pointers...
861    if(croute->prevroute == NULL) {
862        // Topmost node...
863        if(croute->nextroute != NULL) {
864            croute->nextroute->prevroute = NULL;
865        }
866        routing_table = croute->nextroute;
867
868    } else {
869        croute->prevroute->nextroute = croute->nextroute;
870        if(croute->nextroute != NULL) {
871            croute->nextroute->prevroute = croute->prevroute;
872        }
873    }
874    // Free the memory, and set the route to NULL...
875    free(croute);
876    croute = NULL;
877
878    IF_DEBUG logRouteTable("Remove route");
879
880    return result;
881}
882
883
884/**
885*   Ages a specific route
886*/
887int internAgeRoute(struct RouteTable*  croute) {
888    struct Config *conf = getCommonConfig();
889    int result = 0;
890
891    // Drop age by 1.
892    croute->ageValue--;
893    // Check if there has been any activity...
894    if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) {
895        // There was some activity, check if all registered vifs responded.
896        if(croute->vifBits == croute->ageVifBits) {
897            // Everything is in perfect order, so we just update the route age.
898            croute->ageValue = conf->robustnessValue;
899            //croute->ageActivity = 0;
900        } else {
901            // One or more VIF has not gotten any response.
902            croute->ageActivity++;
903            // Update the actual bits for the route...
904            croute->vifBits = croute->ageVifBits;
905        }
906    }
907    // Check if there have been activity in aging process...
908    else if( croute->ageActivity > 0 ) {
909        // If the bits are different in this round, we must
910        if(croute->vifBits != croute->ageVifBits) {
911            // Or the bits together to insure we don't lose any listeners.
912            croute->vifBits |= croute->ageVifBits;
913
914            // Register changes in this round as well..
915            croute->ageActivity++;
916        }
917    }
918
919    // If the aging counter has reached zero, its time for updating...
920    if(croute->ageValue == 0) {
921        // Check for activity in the aging process,
922        if(croute->ageActivity>0) {
923
924            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Updating route after aging : %s",
925                         inetFmt(croute->group,s1));
926
927            // Just update the routing settings in kernel...
928            internUpdateKernelRoute(croute, 1);
929
930            // We append the activity counter to the age, and continue...
931            croute->ageValue = croute->ageActivity;
932            croute->ageActivity = 0;
933        } else {
934
935            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removing group %s. Died of old age.",
936                         inetFmt(croute->group,s1));
937
938            // No activity was registered within the timelimit, so remove the route.
939            removeRoute(croute);
940        }
941        // Tell that the route was updated...
942        result = 1;
943    }
944    // The aging vif bits must be reset for each round...
945    BIT_ZERO(croute->ageVifBits);
946
947    return result;
948}
949
950/**
951*   Updates the Kernel routing table. If activate is 1, the route
952*   is (re-)activated. If activate is false, the route is removed.
953*/
954int internUpdateKernelRoute(struct RouteTable *route, int activate) {
955    struct   MRouteDesc     mrDesc;
956    struct   IfDesc         *Dp;
957    unsigned                Ix;
958
959    if(route->originAddr>0) {
960
961        // Build route descriptor from table entry...
962        // Set the source address and group address...
963        mrDesc.McAdr.s_addr     = route->group;
964        mrDesc.OriginAdr.s_addr = route->originAddr;
965        // clear output interfaces
966        memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) );
967
968        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits);
969
970        // Set the TTL's for the route descriptor...
971        for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
972            if(Dp->state == IF_STATE_UPSTREAM) {
973                //IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Identified VIF #%d as upstream.", Dp->index);
974                mrDesc.InVif = Dp->index;
975            }
976            else if(BIT_TST(route->vifBits, Dp->index)) {
977                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold);
978                mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
979            }
980        }
981
982        // Do the actual Kernel route update...
983        if(activate) {
984            // Add route in kernel...
985            addMRoute( &mrDesc );
986        } else {
987            // Delete the route from Kernel...
988#ifndef USE_ATH_HEADER
989            mrDesc.OriginAdr.s_addr = 0;
990#endif
991            delMRoute( &mrDesc );
992        }
993
994    } else {
995        igmp_syslog(LOG_NOTICE, 0, "Route is not active. No kernel updates done.");
996    }
997
998    return 1;
999}
1000
1001/**
1002*   Debug function that writes the routing table entries
1003*   to the igmp_syslog.
1004*/
1005void logRouteTable(char *header) {
1006    IF_DEBUG  {
1007        struct RouteTable*  croute = routing_table;
1008        unsigned            rcount = 0;
1009
1010        igmp_syslog(LOG_DEBUG, 0, "\nCurrent routing table (%s);\n-----------------------------------------------------\n", header);
1011        if(croute==NULL) {
1012            igmp_syslog(LOG_DEBUG, 0, "No routes in table...");
1013        } else {
1014            do {
1015                /*
1016                igmp_syslog(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, Prev: 0x%08x, T: 0x%08x, Next: 0x%08x",
1017                    rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
1018                    croute->ageValue,(croute->originAddr>0?"A":"I"),
1019                    croute->prevroute, croute, croute->nextroute);
1020                */
1021                igmp_syslog(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, OutVifs: 0x%08x",
1022                    rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
1023                    croute->ageValue,(croute->originAddr>0?"A":"I"),
1024                    croute->vifBits);
1025
1026                croute = croute->nextroute;
1027
1028                rcount++;
1029            } while ( croute != NULL );
1030        }
1031
1032        igmp_syslog(LOG_DEBUG, 0, "\n-----------------------------------------------------\n");
1033    }
1034}
1035