$OpenBSD: patch-rttable_c,v 1.1.1.1 2008/02/08 19:30:52 sthen Exp $
--- rttable.c.orig	Sat Aug 20 13:46:20 2005
+++ rttable.c	Mon Nov 19 12:56:50 2007
@@ -38,15 +38,22 @@
 */
 
 #include "defs.h"
+#include <sys/queue.h>
     
 /**
 *   Routing table structure definition. Double linked list...
 */
+struct Origin {
+    TAILQ_ENTRY(Origin) next;
+    uint32		originAddr;
+    int			flood;
+    uint32		pktcnt;
+};
+
 struct RouteTable {
     struct RouteTable   *nextroute;     // Pointer to the next group in line.
     struct RouteTable   *prevroute;     // Pointer to the previous group in line.
     uint32              group;          // The group to route
-    uint32              originAddr;     // The origin adress (only set on activated routes)
     uint32              vifBits;        // Bits representing recieving VIFs.
 
     // Keeps the upstream membership state...
@@ -56,6 +63,7 @@ struct RouteTable {
     uint32              ageVifBits;     // Bits representing aging VIFs.
     int                 ageValue;       // Downcounter for death.          
     int                 ageActivity;    // Records any acitivity that notes there are still listeners.
+    TAILQ_HEAD(originhead, Origin) originList; // The origin adresses (non-empty on activated routes)
 };
 
                  
@@ -65,19 +73,17 @@ static struct RouteTable   *routing_table;
 // Prototypes
 void logRouteTable(char *header);
 int  internAgeRoute(struct RouteTable*  croute);
+int  internUpdateKernelRoute(struct RouteTable *route, int activate, struct Origin *o);
 
-// Socket for sending join or leave requests.
-int mcGroupSock = 0;
 
-
 /**
 *   Function for retrieving the Multicast Group socket.
 */
 int getMcGroupSock() {
-    if( ! mcGroupSock ) {
-        mcGroupSock = openUdpSocket( INADDR_ANY, 0 );;
+    if (MRouterFD < 0) {
+        log(LOG_ERR, errno, "no MRouterFD.");
     }
-    return mcGroupSock;
+    return MRouterFD;
 }
  
 /**
@@ -91,7 +97,7 @@ void initRouteTable() {
     routing_table = NULL;
 
     // Join the all routers group on downstream vifs...
-    for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
+    for ( Ix = 0; (Dp = getIfByIx( Ix )); Ix++ ) {
         // If this is a downstream vif, we should join the All routers group...
         if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) {
             IF_DEBUG log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s",
@@ -160,6 +166,7 @@ void sendJoinLeaveUpstream(struct RouteTable* route, i
 */
 void clearAllRoutes() {
     struct RouteTable   *croute, *remainroute;
+    struct Origin *o;
 
     // Loop through all routes...
     for(croute = routing_table; croute; croute = remainroute) {
@@ -171,7 +178,7 @@ void clearAllRoutes() {
                      inetFmt(croute->group, s1));
 
         // Uninstall current route
-        if(!internUpdateKernelRoute(croute, 0)) {
+        if(!internUpdateKernelRoute(croute, 0, NULL)) {
             log(LOG_WARNING, 0, "The removal from Kernel failed.");
         }
 
@@ -179,6 +186,10 @@ void clearAllRoutes() {
         sendJoinLeaveUpstream(croute, 0);
 
         // Clear memory, and set pointer to next route...
+        while ((o = TAILQ_FIRST(&croute->originList))) {
+            TAILQ_REMOVE(&croute->originList, o, next);
+            free(o);
+        }
         free(croute);
     }
     routing_table = NULL;
@@ -212,7 +223,6 @@ int insertRoute(uint32 group, int ifx) {
     
     struct Config *conf = getCommonConfig();
     struct RouteTable*  croute;
-    int result = 1;
 
     // Sanitycheck the group adress...
     if( ! IN_MULTICAST( ntohl(group) )) {
@@ -241,7 +251,8 @@ int insertRoute(uint32 group, int ifx) {
         newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable));
         // Insert the route desc and clear all pointers...
         newroute->group      = group;
-        newroute->originAddr = 0;
+        TAILQ_INIT(&newroute->originList);
+
         newroute->nextroute  = NULL;
         newroute->prevroute  = NULL;
 
@@ -325,10 +336,10 @@ int insertRoute(uint32 group, int ifx) {
             inetFmt(croute->group, s1), ifx);
 
         // If the route is active, it must be reloaded into the Kernel..
-        if(croute->originAddr != 0) {
+        if(!TAILQ_EMPTY(&croute->originList)) {
 
             // Update route in kernel...
-            if(!internUpdateKernelRoute(croute, 1)) {
+            if(!internUpdateKernelRoute(croute, 1, NULL)) {
                 log(LOG_WARNING, 0, "The insertion into Kernel failed.");
                 return 0;
             }
@@ -351,7 +362,7 @@ int insertRoute(uint32 group, int ifx) {
 *   activated, it's reinstalled in the kernel. If
 *   the route is activated, no originAddr is needed.
 */
-int activateRoute(uint32 group, uint32 originAddr) {
+int activateRoute(uint32 group, uint32 originAddr, int downIf) {
     struct RouteTable*  croute;
     int result = 0;
 
@@ -369,21 +380,42 @@ int activateRoute(uint32 group, uint32 originAddr) {
     }
 
     if(croute != NULL) {
+	struct Origin *o = NULL;
+	int found = 0;
+
         // If the origin address is set, update the route data.
-        if(originAddr > 0) {
-            if(croute->originAddr > 0 && croute->originAddr!=originAddr) {
-                log(LOG_WARNING, 0, "The origin for route %s changed from %s to %s",
-                    inetFmt(croute->group, s1),
-                    inetFmt(croute->originAddr, s2),
-                    inetFmt(originAddr, s3));
-            }
-            croute->originAddr = originAddr;
-        }
+	if(originAddr > 0) {
 
-        // Only update kernel table if there are listeners !
-        if(croute->vifBits > 0) {
-            result = internUpdateKernelRoute(croute, 1);
-        }
+	    TAILQ_FOREACH(o, &croute->originList, next) {
+		log(LOG_INFO, 0, "Origin for route %s have %s, new %s",
+		    inetFmt(croute->group, s1),
+		    inetFmt(o->originAddr, s2),
+		    inetFmt(originAddr, s3));
+		if (o->originAddr==originAddr) {
+		    found++;
+		    break;
+		}
+	    }
+	    if (!found) {
+		log(LOG_NOTICE, 0, "New origin for route %s is %s, flood %d",
+		    inetFmt(croute->group, s1),
+		    inetFmt(originAddr, s3), downIf);
+		o = malloc(sizeof(*o));
+		o->originAddr = originAddr;
+		o->flood = downIf;
+		o->pktcnt = 0;
+		TAILQ_INSERT_TAIL(&croute->originList, o, next);
+	    } else {
+		log(LOG_INFO, 0, "Have origin for route %s at %s, pktcnt %d",
+		    inetFmt(croute->group, s1),
+		    inetFmt(o->originAddr, s3),
+		    o->pktcnt);
+	    }
+	}
+
+        // Only update kernel table if there are listeners, but flood upstream!
+        if(croute->vifBits > 0 || downIf >= 0)
+            result = internUpdateKernelRoute(croute, 1, o);
     }
     IF_DEBUG logRouteTable("Activate Route");
 
@@ -443,7 +475,6 @@ void setRouteLastMemberMode(uint32 group) {
 *   route is not found, or not in this state, 0 is returned.
 */
 int lastMemberGroupAge(uint32 group) {
-    struct Config       *conf = getCommonConfig();
     struct RouteTable   *croute;
 
     croute = findRoute(group);
@@ -463,6 +494,7 @@ int lastMemberGroupAge(uint32 group) {
 */
 int removeRoute(struct RouteTable*  croute) {
     struct Config       *conf = getCommonConfig();
+    struct Origin *o;
     int result = 1;
     
     // If croute is null, no routes was found.
@@ -477,7 +509,7 @@ int removeRoute(struct RouteTable*  croute) {
     //BIT_ZERO(croute->vifBits);
 
     // Uninstall current route from kernel
-    if(!internUpdateKernelRoute(croute, 0)) {
+    if(!internUpdateKernelRoute(croute, 0, NULL)) {
         log(LOG_WARNING, 0, "The removal from Kernel failed.");
         result = 0;
     }
@@ -503,7 +535,12 @@ int removeRoute(struct RouteTable*  croute) {
             croute->nextroute->prevroute = croute->prevroute;
         }
     }
+
     // Free the memory, and set the route to NULL...
+    while ((o = TAILQ_FIRST(&croute->originList))) {
+	TAILQ_REMOVE(&croute->originList, o, next);
+	free(o);
+    }
     free(croute);
     croute = NULL;
 
@@ -551,6 +588,36 @@ int internAgeRoute(struct RouteTable*  croute) {
         }
     }
 
+    {
+	struct Origin *o, *nxt;
+	struct sioc_sg_req sg_req;
+
+	sg_req.grp.s_addr = croute->group;
+	for (o = TAILQ_FIRST(&croute->originList); o; o = nxt) {
+	    nxt = TAILQ_NEXT(o, next);
+	    sg_req.src.s_addr = o->originAddr;
+	    if (ioctl(MRouterFD, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+		log(LOG_WARNING, errno, "%s (%s %s)",
+		    "age_table_entry: SIOCGETSGCNT failing for",
+		    inetFmt(o->originAddr, s1),
+		    inetFmt(croute->group, s2));
+		/* Make sure it gets deleted below */
+		sg_req.pktcnt = o->pktcnt;
+	    }
+	    log(LOG_DEBUG, 0, "Aging Origin %s Dst %s PktCnt %d -> %d",
+		inetFmt(o->originAddr, s1), inetFmt(croute->group, s2),
+		o->pktcnt, sg_req.pktcnt);
+	    if (sg_req.pktcnt == o->pktcnt) {
+		/* no traffic, remove from kernel cache */
+		internUpdateKernelRoute(croute, 0, o);
+		TAILQ_REMOVE(&croute->originList, o, next);
+		free(o);
+	    } else {
+		o->pktcnt = sg_req.pktcnt;
+	    }
+	}
+    }
+
     // If the aging counter has reached zero, its time for updating...
     if(croute->ageValue == 0) {
         // Check for activity in the aging process,
@@ -560,7 +627,7 @@ int internAgeRoute(struct RouteTable*  croute) {
                          inetFmt(croute->group,s1));
             
             // Just update the routing settings in kernel...
-            internUpdateKernelRoute(croute, 1);
+            internUpdateKernelRoute(croute, 1, NULL);
     
             // We append the activity counter to the age, and continue...
             croute->ageValue = croute->ageActivity;
@@ -586,34 +653,58 @@ int internAgeRoute(struct RouteTable*  croute) {
 /**
 *   Updates the Kernel routing table. If activate is 1, the route
 *   is (re-)activated. If activate is false, the route is removed.
+*   if 'origin' is given, only the route with 'origin' will be
+*   updated, otherwise all MFC routes for the group will updated.
 */
-int internUpdateKernelRoute(struct RouteTable *route, int activate) {
+int internUpdateKernelRoute(struct RouteTable *route, int activate, struct Origin *origin) {
     struct   MRouteDesc     mrDesc;
     struct   IfDesc         *Dp;
     unsigned                Ix;
+    struct Origin *o;
     
-    if(route->originAddr>0) {
+    if (TAILQ_EMPTY(&route->originList)) {
+        log(LOG_NOTICE, 0, "Route is not active. No kernel updates done.");
+        return 1;
+    }
+    TAILQ_FOREACH(o, &route->originList, next) {
+	if (origin && origin != o)
+		continue;
 
         // Build route descriptor from table entry...
         // Set the source address and group address...
         mrDesc.McAdr.s_addr     = route->group;
-        mrDesc.OriginAdr.s_addr = route->originAddr;
+        mrDesc.OriginAdr.s_addr = o->originAddr;
     
         // clear output interfaces 
         memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) );
     
-        IF_DEBUG log(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits);
+        IF_DEBUG log(LOG_DEBUG, 0, "Origin %s Vif bits : 0x%08x", inetFmt(o->originAddr, s1), route->vifBits);
 
         // Set the TTL's for the route descriptor...
-        for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
-            if(Dp->state == IF_STATE_UPSTREAM) {
-                //IF_DEBUG log(LOG_DEBUG, 0, "Identified VIF #%d as upstream.", Dp->index);
-                mrDesc.InVif = Dp->index;
-            }
-            else if(BIT_TST(route->vifBits, Dp->index)) {
-                IF_DEBUG log(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold);
-                mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
-            }
+        for ( Ix = 0; (Dp = getIfByIx( Ix )); Ix++ ) {
+	    if (o->flood >= 0) {
+		if(Ix == o->flood) {
+		    IF_DEBUG log(LOG_DEBUG, 0, "Identified Input VIF #%d as DOWNSTREAM.", Dp->index);
+		    mrDesc.InVif = Dp->index;
+		}
+		else if(Dp->state == IF_STATE_UPSTREAM) {
+		    IF_DEBUG log(LOG_DEBUG, 0, "Setting TTL for UPSTREAM Vif %d to %d", Dp->index, Dp->threshold);
+		    mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
+		}
+		else if(BIT_TST(route->vifBits, Dp->index)) {
+		    IF_DEBUG log(LOG_DEBUG, 0, "Setting TTL for DOWNSTREAM Vif %d to %d", Dp->index, Dp->threshold);
+		    mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
+		}
+	    } else {
+		if(Dp->state == IF_STATE_UPSTREAM) {
+		    IF_DEBUG log(LOG_DEBUG, 0, "Identified VIF #%d as upstream.", Dp->index);
+		    mrDesc.InVif = Dp->index;
+		}
+		else if(BIT_TST(route->vifBits, Dp->index)) {
+		    IF_DEBUG log(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold);
+		    mrDesc.TtlVc[ Dp->index ] = Dp->threshold;
+		}
+	    }
         }
     
         // Do the actual Kernel route update...
@@ -625,9 +716,6 @@ int internUpdateKernelRoute(struct RouteTable *route, 
             // Delete the route from Kernel...
             delMRoute( &mrDesc );
         }
-
-    } else {
-        log(LOG_NOTICE, 0, "Route is not active. No kernel updates done.");
     }
 
     return 1;
@@ -647,16 +735,17 @@ void logRouteTable(char *header) {
             log(LOG_DEBUG, 0, "No routes in table...");
         } else {
             do {
-                /*
-                log(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, Prev: 0x%08x, T: 0x%08x, Next: 0x%08x",
-                    rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
-                    croute->ageValue,(croute->originAddr>0?"A":"I"),
-                    croute->prevroute, croute, croute->nextroute);
-                */
-                log(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, OutVifs: 0x%08x",
-                    rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2),
-                    croute->ageValue,(croute->originAddr>0?"A":"I"),
-                    croute->vifBits);
+		log(LOG_DEBUG, 0, "#%d: Dst: %s, Age:%d, St: %s, OutVifs: 0x%08x",
+		    rcount, inetFmt(croute->group, s2),
+		    croute->ageValue,(TAILQ_EMPTY(&croute->originList)?"I":"A"),
+		    croute->vifBits);
+		{
+		    struct Origin *o;
+		    TAILQ_FOREACH(o, &croute->originList, next) {
+			log(LOG_DEBUG, 0, "#%d: Origin: %s floodIf %d pktcnt %d",
+			    rcount, inetFmt(o->originAddr, s1), o->flood, o->pktcnt);
+		    }
+		}
                   
                 croute = croute->nextroute; 
         
