diff --git a/doc/igmpproxy.conf.5.in b/doc/igmpproxy.conf.5.in index a07055ce..280b67f6 100644 --- a/doc/igmpproxy.conf.5.in +++ b/doc/igmpproxy.conf.5.in @@ -55,6 +55,23 @@ interface, this function should not be used. Disabling this function increases the risk of bandwidth saturation. .RE +.B loglevel +.RS +Sets the loglevel for the daemon. Must be between 0-7 and corrsponds to unix syslog levels. +.RE + +.B rescanvif +.RS +Enables periodic rescanning of interfaces in system. The value specifies the interval +in seconds, with a minimum of 10 seconds. 0 is disabled. Currently only changes of downstream +interfaces are supported. +.RE + +.B rescanconf +.RS +Enables periodic rescanning of confiuration file. The value specifies the interval +in seconds, with a minimum of 10 seconds. 0 is disabled. This implies and overrules rescanvif. +.RE .B phyint .I interface diff --git a/src/callout.c b/src/callout.c index c49da6cf..c4ab4292 100644 --- a/src/callout.c +++ b/src/callout.c @@ -37,26 +37,19 @@ /* the code below implements a callout queue */ static int id = 0; -static struct timeOutQueue *queue = 0; /* pointer to the beginning of timeout queue */ +static struct timeOutQueue *queue = NULL; /* pointer to the beginning of timeout queue */ struct timeOutQueue { struct timeOutQueue *next; // Next event in queue int id; timer_f func; // function to call void *data; // Data for function - int time; // Time offset for next event + long time; // Time for event }; // Method for dumping the Queue to the log. static void debugQueue(void); -/** -* Initializes the callout queue -*/ -void callout_init(void) { - queue = NULL; -} - /** * Clears all scheduled timeouts... */ @@ -70,58 +63,22 @@ void free_all_callouts(void) { } } - /** - * elapsed_time seconds have passed; perform all the events that should - * happen. + * Execute all expired timers, using .5s grace. */ -void age_callout_queue(int elapsed_time) { - struct timeOutQueue *ptr; - struct timeOutQueue *_queue = NULL; - struct timeOutQueue *last = NULL; - int i = 0; - - for (ptr = queue; ptr; ptr = ptr->next) { - if (ptr->time > elapsed_time) { - ptr->time -= elapsed_time; - break; - } else { - elapsed_time -= ptr->time; - if (_queue == NULL) - _queue = ptr; - last = ptr; - } - } - - queue = ptr; - if (last) { - last->next = NULL; - } - - /* process existing events */ - for (ptr = _queue; ptr; ptr = _queue, i++) { - _queue = _queue->next; +void age_callout_queue(struct timespec curtime) { + struct timeOutQueue *ptr = queue; + int i = 1; + + if (curtime.tv_sec == 0) clock_gettime (CLOCK_MONOTONIC, &curtime); + while (ptr && ((ptr->time <= curtime.tv_sec) || ( curtime.tv_nsec >= 500000000 && ptr->time <= curtime.tv_sec-1))) { my_log(LOG_DEBUG, 0, "About to call timeout %d (#%d)", ptr->id, i); - if (ptr->func) - ptr->func(ptr->data); - free(ptr); - } -} - -/** - * Return in how many seconds age_callout_queue() would like to be called. - * Return -1 if there are no events pending. - */ -int timer_nextTimer(void) { - if (queue) { - if (queue->time < 0) { - my_log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d", - queue->time); - return 0; - } - return queue->time; + struct timeOutQueue *tmp = ptr; + if (ptr->func) ptr->func(ptr->data); + queue = ptr = ptr->next; + free(tmp); + i++; } - return -1; } /** @@ -131,61 +88,41 @@ int timer_nextTimer(void) { * @param data - Pointer to the function data to supply... */ int timer_setTimer(int delay, timer_f action, void *data) { - struct timeOutQueue *ptr, *node, *prev; - int i = 0; + struct timeOutQueue *ptr = queue, *node; + struct timespec curtime; + int i = 1; - /* create a node */ + // create a node. node = (struct timeOutQueue *)malloc(sizeof(struct timeOutQueue)); if (node == 0) { my_log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); return -1; } + clock_gettime(CLOCK_MONOTONIC, &curtime); node->func = action; node->data = data; - node->time = delay; - node->next = 0; + node->time = curtime.tv_sec + delay; + node->next = NULL; node->id = ++id; - prev = ptr = queue; - - /* insert node in the queue */ - - /* if the queue is empty, insert the node and return */ - if (!queue) { - queue = node; - } + // if the queue is empty, insert the node and return. + if (!queue) queue = node; else { - /* chase the pointer looking for the right place */ - while (ptr) { - if (delay < ptr->time) { - // We found the correct node - node->next = ptr; - if (ptr == queue) { - queue = node; - } - else { - prev->next = node; - } - ptr->time -= node->time; - my_log(LOG_DEBUG, 0, - "Created timeout %d (#%d) - delay %d secs", - node->id, i, node->time); - debugQueue(); - return node->id; - } else { - // Continur to check nodes. - delay -= ptr->time; node->time = delay; - prev = ptr; - ptr = ptr->next; - } - i++; + // chase the queue looking for the right place. + for ( i++; ptr->next && node->time >= ptr->next->time; ptr = ptr->next ) i++; + if ( ptr == queue && node->time < ptr->time ) { + // Start of queue, insert. + queue = node; node->next = ptr; + } else if ( ptr->next ) { + // Middle of queue, insert + node->next = ptr->next; ptr->next = node; + } else { + // End of queue, append. + ptr->next = node; } - prev->next = node; } - my_log(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs", - node->id, i, node->time); debugQueue(); - + my_log(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs", node->id, i, delay); return node->id; } @@ -193,77 +130,26 @@ int timer_setTimer(int delay, timer_f action, void *data) { * returns the time until the timer is scheduled */ int timer_leftTimer(int timer_id) { - struct timeOutQueue *ptr; - int left = 0; - - if (!timer_id) - return -1; - - for (ptr = queue; ptr; ptr = ptr->next) { - left += ptr->time; - if (ptr->id == timer_id) { - return left; - } + struct timeOutQueue *ptr = queue; + struct timespec curtime; + + if (!timer_id || !queue) return -1; + while (ptr && ptr->id != timer_id) ptr = ptr->next; + if ( ptr ){ + clock_gettime (CLOCK_MONOTONIC, &curtime); + return (ptr->time - curtime.tv_sec); } return -1; } -/** -* clears the associated timer. Returns 1 if succeeded. -*/ -int timer_clearTimer(int timer_id) { - struct timeOutQueue *ptr, *prev; - int i = 0; - - if (!timer_id) - return 0; - - prev = ptr = queue; - - /* - * find the right node, delete it. the subsequent node's time - * gets bumped up - */ - - debugQueue(); - while (ptr) { - if (ptr->id == timer_id) { - /* got the right node */ - - /* unlink it from the queue */ - if (ptr == queue) - queue = queue->next; - else - prev->next = ptr->next; - - /* increment next node if any */ - if (ptr->next != 0) - (ptr->next)->time += ptr->time; - - if (ptr->data) - free(ptr->data); - my_log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i); - free(ptr); - debugQueue(); - return 1; - } - prev = ptr; - ptr = ptr->next; - i++; - } - // If we get here, the timer was not deleted. - my_log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i); - debugQueue(); - return 0; -} - /** * debugging utility */ static void debugQueue(void) { - struct timeOutQueue *ptr; + struct timeOutQueue *ptr; int i = 1; for (ptr = queue; ptr; ptr = ptr->next) { - my_log(LOG_DEBUG, 0, "(Id:%d, Time:%d) ", ptr->id, ptr->time); + my_log(LOG_DEBUG, 0, "(%d - Id:%d, Time:%d) ", i, ptr->id, ptr->time); + i++; } } diff --git a/src/config.c b/src/config.c index 030da1ac..b478cc46 100644 --- a/src/config.c +++ b/src/config.c @@ -90,7 +90,8 @@ static void initCommonConfig(void) { // aimwang: default value commonConfig.defaultInterfaceState = IF_STATE_DISABLED; - commonConfig.rescanVif = 0; + commonConfig.rescanVif = 0; + commonConfig.rescanConf = 0; } /** @@ -100,6 +101,37 @@ struct Config *getCommonConfig(void) { return &commonConfig; } +// Reloads the configuration file and removes interfaces which were removed from config. +void reloadConfig() { + struct vifconfig *OldConfPtr, *TmpConfPtr; + + // Load the new configuration keep reference to the old. + OldConfPtr = vifconf; + if ( ! loadConfig(configFilePath) ) my_log(LOG_ERR, 0, "reloadConfig: Unable to load config file."); + + // Rebuild the interfaces config. + rebuildIfVc(); + + my_log(LOG_DEBUG, 0, "reloadConfig: Config Reloaded. OldConfPtr %x, NewConfPtr, %x", OldConfPtr, vifconf); + // Free all the old mallocd subnetlists and vifconf list. + while ( OldConfPtr ) { + TmpConfPtr=OldConfPtr->next; // Increment before free or pointers may be invalid. + struct SubnetList *TmpNetPtr; + for ( ; OldConfPtr->allowednets; ) { + TmpNetPtr = OldConfPtr->allowednets->next; + free (OldConfPtr->allowednets); + OldConfPtr->allowednets = TmpNetPtr; + } + for ( ; OldConfPtr->allowedgroups; ) { + TmpNetPtr = OldConfPtr->allowedgroups->next; + free (OldConfPtr->allowedgroups); + OldConfPtr->allowedgroups = TmpNetPtr; + } + free (OldConfPtr); + OldConfPtr = TmpConfPtr; + } +} + /** * Loads the configuration from file, and stores the config in * respective holders... @@ -189,9 +221,43 @@ int loadConfig(char *configFile) { continue; } else if(strcmp("rescanvif", token)==0) { - // Got a defaultdown token... - my_log(LOG_DEBUG, 0, "Config: Need detect new interface."); - commonConfig.rescanVif = 1; + // Got a rescanvif token... + token = nextConfigToken(); + int intToken = atoi(token); + if (intToken != 0 ) { + if (intToken<10) intToken=10; + my_log(LOG_DEBUG, 0, "Config: Need detect new interface every %ds.", intToken); + } + commonConfig.rescanVif = intToken; + + // Read next token... + token = nextConfigToken(); + continue; + } + else if(strcmp("rescanconf", token)==0) { + // Got a rescanconf token... + token = nextConfigToken(); + int intToken = atoi(token); + if (intToken != 0 ) { + if (intToken<10) intToken=10; + my_log(LOG_DEBUG, 0, "Config: Need detect config change every %ds.", intToken); + } + commonConfig.rescanConf = intToken; + + // Read next token... + token = nextConfigToken(); + continue; + } + else if(strcmp("loglevel", token)==0) { + // Got a loglevel token... + token = nextConfigToken(); + int intToken = atoi(token); + if (intToken < 0 || intToken > 7) { + my_log(LOG_ERR, 0, "Config: Loglevel must be 0 - 7"); + } else { + LogLevel = intToken; + my_log(LOG_DEBUG, 0, "Config: LogLevel %d", LogLevel); + } // Read next token... token = nextConfigToken(); @@ -260,6 +326,97 @@ void configureVifs(void) { } } +/* create VIFs for all IP, non-loop interfaces. + When argument is not NULL rebuild the interface table. +*/ +void createVifs(struct IfDescP *RebuildP) { + struct IfDesc *Dp, *oDp = NULL; + int vifcount = 0, upsvifcount = 0, Ix = 0; + bool join = 0; + + // init array to "not set" + for ( Ix = 0; Ix < MAX_UPS_VIFS; Ix++) upStreamIfIdx[Ix] = -1; + + if ( RebuildP != NULL ) { + // When rebuild, check if interfaces have dissapeared and call delVIF if necessary. + for ( oDp=RebuildP->S; oDpE; oDp++ ) { + for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) if ( ! strcmp (oDp->Name, Dp->Name) ) break; + if ( Dp == NULL ) { + my_log(LOG_DEBUG, 0, "Interface %s disappeared from system", oDp->Name ); + if ( oDp->index != -1 ) delVIF(oDp); + } + } + } + + for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { + if ( RebuildP == NULL ) { + // Only add vif for valid interfaces on start-up. + if ( (Dp->Flags & IFF_LOOPBACK) || (Dp->state != IF_STATE_DOWNSTREAM && Dp->state != IF_STATE_UPSTREAM) ) continue; + } else { + /* Need rebuild, check if interface is new or already exists (check table below). + old: disabled new: disabled -> do nothing + old: disabled new: downstream -> addVIF(new), joinmcroutergroups + old: disabled new: upstream -> addVIF(new) + old: downstream new: disabled -> delVIF(old) + state table old: downstream new: downstream -> addvif(new,old) + old: downstream new: upstream -> delvif(old), addvif(new) + old: upstream new: disabled -> clear routes oldvif, delVIF(old) + old: upstream new: downstream -> clear routes oldvif, delvif(old)),addvif(new), joinmcroutergroup + old: upstream new: upstream -> addvif(new,old) + */ + for ( oDp=RebuildP->S; oDpE; oDp++ ) if ( ! strcmp (oDp->Name, Dp->Name) ) break; + if ( oDp < RebuildP->E ) { + switch (oDp->state) { + case IF_STATE_DISABLED: + switch (Dp->state) { + case IF_STATE_DISABLED: { continue; } + case IF_STATE_DOWNSTREAM: { oDp=NULL; join=1; break; } + case IF_STATE_UPSTREAM: { oDp=NULL; break; } + } + break; + case IF_STATE_DOWNSTREAM: + switch (Dp->state) { + case IF_STATE_DISABLED: { delVIF(oDp); continue; } + case IF_STATE_DOWNSTREAM: { break; } + case IF_STATE_UPSTREAM: { delVIF(oDp); oDp=NULL; break; } + } + break; + case IF_STATE_UPSTREAM: + switch (Dp->state) { + case IF_STATE_DISABLED: { clearRoutes(oDp); delVIF(oDp); continue; } + case IF_STATE_DOWNSTREAM: { clearRoutes(oDp); delVIF(oDp); oDp=NULL; join=1; break; } + case IF_STATE_UPSTREAM: { break; } + } + break; + } + if (Dp->Flags & IFF_LOOPBACK) continue; + } else { + // New Interface. Only add valid up/downstream vif. + if ( (Dp->Flags & IFF_LOOPBACK) || (Dp->state != IF_STATE_DOWNSTREAM && Dp->state != IF_STATE_UPSTREAM) ) continue; + oDp=NULL; + } + } + if(Dp->state == IF_STATE_UPSTREAM) { + if (upsvifcount < MAX_UPS_VIFS -1) + { + my_log(LOG_DEBUG, 0, "Found upstream IF #%d, will assign as upstream Vif %d", + upsvifcount, Ix); + upStreamIfIdx[upsvifcount++] = Ix; + } else { + my_log(LOG_ERR, 0, "Cannot set VIF #%d as upstream as well. Max upstream Vif count is %d", + Ix, MAX_UPS_VIFS); + } + } + addVIF( Dp, oDp ); + if ( join ) joinMcRoutersGroup(Dp); + vifcount++; + } + + // All vifs created, check if there is an upstream and at least one downstream. + if ( upsvifcount == 0 || vifcount == upsvifcount ) { + my_log(LOG_ERR, 0, "There must be at least 1 Vif as upstream and 1 as dowstream."); + } +} /** * Internal function to parse phyint config diff --git a/src/ifvc.c b/src/ifvc.c index 509012f0..fd587432 100644 --- a/src/ifvc.c +++ b/src/ifvc.c @@ -41,7 +41,7 @@ static inline uint32_t s_addr_from_sockaddr(const struct sockaddr *addr) { return addr_in.sin_addr.s_addr; } -struct IfDesc IfDescVc[ MAX_IF ], *IfDescEp = IfDescVc; +struct IfDescP IfDescP = { NULL, NULL, 0 }; /* aimwang: add for detect interface and rebuild IfVc record */ /*************************************************** @@ -50,280 +50,121 @@ struct IfDesc IfDescVc[ MAX_IF ], *IfDescEp = IfDescVc; * So I can check if the file exist then run me and delete the file. ***************************************************/ void rebuildIfVc () { - struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; - struct ifreq *IfEp; - struct ifconf IoCtlReq; - struct IfDesc *Dp; - struct ifreq *IfPt, *IfNext; - uint32_t addr, subnet, mask; - int Sock; + // Build new IfDesc Table. Keep Copy of Old. + struct IfDescP OldIfDescP=IfDescP, TmpIfDescP=IfDescP; + buildIfVc(); + + // Call configureVifs to link the new IfDesc table. + configureVifs(); + + // Call createvifs with pointer to old IfDesc table for relinking vifs and removing or adding interfaces if required. + my_log (LOG_DEBUG,0,"RebuildIfVc: creating vifs, Old IfDescP: %x, New: %x", OldIfDescP.S, IfDescP.S); + createVifs(&OldIfDescP); + + // Free the old IfDesc Table. + if ( OldIfDescP.S != NULL ) { + for (struct IfDesc *Dp = TmpIfDescP.S; Dp < TmpIfDescP.E; Dp++) free(Dp->allowednets); + free(OldIfDescP.S); + } +} +/* +** Builds up a vector with the interface of the machine. Calls to the other functions of +** the module will fail if they are called before the vector is build. +** +*/ +void buildIfVc() { // Get the config. struct Config *config = getCommonConfig(); - if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) - my_log( LOG_ERR, errno, "RAW socket open" ); + unsigned int NrInt=0; + struct ifaddrs *IfAddrsP, *TmpIfAddrsP; - // aimwang: set all downstream IF as lost, for check IF exist or gone. - for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { - if (Dp->state == IF_STATE_DOWNSTREAM) { - Dp->state = IF_STATE_LOST; - } + if ( (getifaddrs (&IfAddrsP)) == -1 ) { + my_log ( LOG_ERR, errno, "buildIfVc: getifaddr() failed, cannot enumerate interfaces" ); + exit (1); } - IoCtlReq.ifc_buf = (void *)IfVc; - IoCtlReq.ifc_len = sizeof( IfVc ); + // Check nr of interfaces in system. + for ( TmpIfAddrsP=IfAddrsP; TmpIfAddrsP; NrInt++) TmpIfAddrsP = TmpIfAddrsP->ifa_next; + IfDescP.nrint=NrInt; + my_log (LOG_DEBUG, 0 , "buildIfVc: Found %u interface(s) on system", NrInt); - if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) - my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); + // Allocate memory for IfDesc Table. + struct IfDesc *IfDescA =(struct IfDesc*)calloc(IfDescP.nrint,sizeof(struct IfDesc)); + if(IfDescA == NULL) my_log(LOG_ERR, 0, "Out of memory !"); + IfDescP.S=IfDescA; + IfDescP.E=IfDescA; - IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); - - for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { - struct ifreq IfReq; + // loop over interfaces and copy interface info to IfDescP + for (TmpIfAddrsP=IfAddrsP; TmpIfAddrsP; TmpIfAddrsP=TmpIfAddrsP->ifa_next) { + // Temp keepers of interface params... + uint32_t addr, subnet, mask; char FmtBu[ 32 ]; - IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - IfPt->ifr_addr.sa_len -#else - sizeof(struct sockaddr_in) -#endif - ); - if (IfNext < IfPt + 1) - IfNext = IfPt + 1; - - for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { - if (0 == strcmp(Dp->Name, IfPt->ifr_name)) { - break; - } - } + // don't create IfDesc for non-IP interfaces. + if ( TmpIfAddrsP->ifa_addr->sa_family != AF_INET ) continue; - if (Dp == IfDescEp) { - strncpy( Dp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); - } + // Copy the interface name. + int sz = strlen(TmpIfAddrsP->ifa_name) < sizeof(IfDescP.E->Name) ? strlen(TmpIfAddrsP->ifa_name) : sizeof(IfDescP.E->Name); + memcpy( IfDescP.E->Name, TmpIfAddrsP->ifa_name, sz ); IfDescP.E->Name[sz]='\0'; - if ( IfPt->ifr_addr.sa_family != AF_INET ) { - if (Dp == IfDescEp) { - IfDescEp++; - } - Dp->InAdr.s_addr = 0; /* mark as non-IP interface */ - continue; - } + // Set the index to -1 by default. + IfDescP.E->index = (unsigned int)-1; // Get the interface adress... - Dp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); - addr = Dp->InAdr.s_addr; + IfDescP.E->InAdr.s_addr = s_addr_from_sockaddr(TmpIfAddrsP->ifa_addr); + addr = IfDescP.E->InAdr.s_addr; - memcpy( IfReq.ifr_name, Dp->Name, sizeof( IfReq.ifr_name ) ); // Get the subnet mask... - if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) - my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); - mask = s_addr_from_sockaddr(&IfReq.ifr_addr); // Do not use ifr_netmask as it is not available on freebsd + mask = s_addr_from_sockaddr(TmpIfAddrsP->ifa_netmask); subnet = addr & mask; - if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) - my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); - Dp->Flags = IfReq.ifr_flags; - - if (0x10d1 == Dp->Flags) - { - if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) - my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); - addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); + /* get if flags + ** + ** typical flags: + ** lo 0x0049 -> Running, Loopback, Up + ** ethx 0x1043 -> Multicast, Running, Broadcast, Up + ** ipppx 0x0091 -> NoArp, PointToPoint, Up + ** grex 0x00C1 -> NoArp, Running, Up + ** ipipx 0x00C1 -> NoArp, Running, Up + */ + IfDescP.E->Flags = TmpIfAddrsP->ifa_flags; + + // aimwang: when pppx get dstaddr for use + if (0x10d1 == IfDescP.E->Flags) { + addr = s_addr_from_sockaddr(TmpIfAddrsP->ifa_dstaddr); subnet = addr & mask; } - if (Dp == IfDescEp) { - // Insert the verified subnet as an allowed net... - Dp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); - if(IfDescEp->allowednets == NULL) { - my_log(LOG_ERR, 0, "Out of memory !"); - } - Dp->allowednets->next = NULL; - Dp->state = IF_STATE_DOWNSTREAM; - Dp->robustness = DEFAULT_ROBUSTNESS; - Dp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ - Dp->ratelimit = DEFAULT_RATELIMIT; - } + // Insert the verified subnet as an allowed net... + IfDescP.E->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); + if(IfDescP.E->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); - // Set the network address for the IF.. - Dp->allowednets->subnet_mask = mask; - Dp->allowednets->subnet_addr = subnet; + // Create the network address for the IF.. + IfDescP.E->allowednets->next = NULL; + IfDescP.E->allowednets->subnet_mask = mask; + IfDescP.E->allowednets->subnet_addr = subnet; - // Set the state for the IF... - if (Dp->state == IF_STATE_LOST) { - Dp->state = IF_STATE_DOWNSTREAM; - } - - // when IF become enabeld from downstream, addVIF to enable its VIF - if (Dp->state == IF_STATE_HIDDEN) { - my_log(LOG_NOTICE, 0, "%s [Hidden -> Downstream]", Dp->Name); - Dp->state = IF_STATE_DOWNSTREAM; - addVIF(Dp); - joinMcGroup(getMcGroupSock(), Dp, allrouters_group); - } - - // addVIF when found new IF - if (Dp == IfDescEp) { - my_log(LOG_NOTICE, 0, "%s [New]", Dp->Name); - Dp->state = config->defaultInterfaceState; - addVIF(Dp); - joinMcGroup(getMcGroupSock(), Dp, allrouters_group); - IfDescEp++; - } + // Set the default params for the IF... + IfDescP.E->state = config->defaultInterfaceState; + IfDescP.E->robustness = DEFAULT_ROBUSTNESS; + IfDescP.E->threshold = DEFAULT_THRESHOLD; /* ttl limit */ + IfDescP.E->ratelimit = DEFAULT_RATELIMIT; // Debug log the result... - my_log( LOG_DEBUG, 0, "rebuildIfVc: Interface %s Addr: %s, Flags: 0x%04x, Network: %s", - Dp->Name, - fmtInAdr( FmtBu, Dp->InAdr ), - Dp->Flags, - inetFmts(subnet, mask, s1)); - } - - // aimwang: search not longer exist IF, set as hidden and call delVIF - for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { - if (IF_STATE_LOST == Dp->state) { - my_log(LOG_NOTICE, 0, "%s [Downstream -> Hidden]", Dp->Name); - Dp->state = IF_STATE_HIDDEN; - leaveMcGroup( getMcGroupSock(), Dp, allrouters_group ); - delVIF(Dp); - } - } - - close( Sock ); -} - -/* -** Builds up a vector with the interface of the machine. Calls to the other functions of -** the module will fail if they are called before the vector is build. -** -*/ -void buildIfVc(void) { - struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; - struct ifreq *IfEp; - struct Config *config = getCommonConfig(); - - int Sock; - - if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) - my_log( LOG_ERR, errno, "RAW socket open" ); + my_log( LOG_DEBUG, 0, "buildIfVc: Interface %s Addr: %s, Flags: 0x%04x, Network: %s", + IfDescP.E->Name, + fmtInAdr( FmtBu, IfDescP.E->InAdr ), + IfDescP.E->Flags, + inetFmts(subnet,mask, s1)); - /* get If vector - */ - { - struct ifconf IoCtlReq; - - IoCtlReq.ifc_buf = (void *)IfVc; - IoCtlReq.ifc_len = sizeof( IfVc ); - - if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) - my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); - - IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); + IfDescP.E++; } - - /* loop over interfaces and copy interface info to IfDescVc - */ - { - struct ifreq *IfPt, *IfNext; - - // Temp keepers of interface params... - uint32_t addr, subnet, mask; - - for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { - struct ifreq IfReq; - char FmtBu[ 32 ]; - - IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - IfPt->ifr_addr.sa_len -#else - sizeof(struct sockaddr_in) -#endif - ); - if (IfNext < IfPt + 1) - IfNext = IfPt + 1; - - strncpy( IfDescEp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); - - // Currently don't set any allowed nets... - //IfDescEp->allowednets = NULL; - - // Set the index to -1 by default. - IfDescEp->index = (unsigned int)-1; - - /* don't retrieve more info for non-IP interfaces - */ - if ( IfPt->ifr_addr.sa_family != AF_INET ) { - IfDescEp->InAdr.s_addr = 0; /* mark as non-IP interface */ - IfDescEp++; - continue; - } - - // Get the interface adress... - IfDescEp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); - addr = IfDescEp->InAdr.s_addr; - - memcpy( IfReq.ifr_name, IfDescEp->Name, sizeof( IfReq.ifr_name ) ); - - // Get the subnet mask... - if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) - my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); - mask = s_addr_from_sockaddr(&IfReq.ifr_addr); // Do not use ifr_netmask as it is not available on freebsd - subnet = addr & mask; - - /* get if flags - ** - ** typical flags: - ** lo 0x0049 -> Running, Loopback, Up - ** ethx 0x1043 -> Multicast, Running, Broadcast, Up - ** ipppx 0x0091 -> NoArp, PointToPoint, Up - ** grex 0x00C1 -> NoArp, Running, Up - ** ipipx 0x00C1 -> NoArp, Running, Up - */ - if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) - my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); - - IfDescEp->Flags = IfReq.ifr_flags; - - // aimwang: when pppx get dstaddr for use - if (0x10d1 == IfDescEp->Flags) - { - if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) - my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); - addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); - subnet = addr & mask; - } - - // Insert the verified subnet as an allowed net... - IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); - if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); - - // Create the network address for the IF.. - IfDescEp->allowednets->next = NULL; - IfDescEp->allowednets->subnet_mask = mask; - IfDescEp->allowednets->subnet_addr = subnet; - - // Set the default params for the IF... - IfDescEp->state = config->defaultInterfaceState; - IfDescEp->robustness = DEFAULT_ROBUSTNESS; - IfDescEp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ - IfDescEp->ratelimit = DEFAULT_RATELIMIT; - - // Debug log the result... - my_log( LOG_DEBUG, 0, "buildIfVc: Interface %s Addr: %s, Flags: 0x%04x, Network: %s", - IfDescEp->Name, - fmtInAdr( FmtBu, IfDescEp->InAdr ), - IfDescEp->Flags, - inetFmts(subnet,mask, s1)); - - IfDescEp++; - } - } - - close( Sock ); + + // Free the getifadds struct. + free (IfAddrsP); } /* @@ -336,7 +177,7 @@ void buildIfVc(void) { struct IfDesc *getIfByName( const char *IfName ) { struct IfDesc *Dp; - for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) + for ( Dp = IfDescP.S; Dp < IfDescP.E; Dp++ ) if ( ! strcmp( IfName, Dp->Name ) ) return Dp; @@ -351,8 +192,8 @@ struct IfDesc *getIfByName( const char *IfName ) { ** */ struct IfDesc *getIfByIx( unsigned Ix ) { - struct IfDesc *Dp = &IfDescVc[ Ix ]; - return Dp < IfDescEp ? Dp : NULL; + struct IfDesc *Dp = IfDescP.S+Ix; + return Dp < IfDescP.E ? Dp : NULL; } /** @@ -367,7 +208,7 @@ struct IfDesc *getIfByAddress( uint32_t ipaddr ) { struct IfDesc *res = NULL; uint32_t last_subnet_mask = 0; - for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { + for ( Dp = IfDescP.S; Dp < IfDescP.E; Dp++ ) { // Loop through all registered allowed nets of the VIF... for(currsubnet = Dp->allowednets; currsubnet != NULL; currsubnet = currsubnet->next) { // Check if the ip falls in under the subnet.... @@ -386,10 +227,10 @@ struct IfDesc *getIfByAddress( uint32_t ipaddr ) { * the supplied IP adress. The IP must match a interfaces * subnet, or any configured allowed subnet on a interface. */ -struct IfDesc *getIfByVifIndex( unsigned vifindex ) { +struct IfDesc *getIfByVifIndex( signed vifindex ) { struct IfDesc *Dp; if(vifindex>0) { - for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { + for ( Dp = IfDescP.S; Dp < IfDescP.E; Dp++ ) { if(Dp->index == vifindex) { return Dp; } diff --git a/src/igmp.c b/src/igmp.c index 38914377..041bd7a5 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -166,7 +166,7 @@ void acceptIgmp(int recvlen) { } } else { // Activate the route. - int vifindex = checkVIF->index; + unsigned vifindex = checkVIF->index; my_log(LOG_DEBUG, 0, "Route activate request from %s to %s on VIF[%d]", inetFmt(src,s1), inetFmt(dst,s2), vifindex); activateRoute(dst, src, vifindex); @@ -329,7 +329,7 @@ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, in IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) - my_log(LOG_ERR, errno, "Sender VIF was down."); + my_log(LOG_NOTICE, errno, "Sender VIF was down."); else my_log(LOG_INFO, errno, "sendto to %s on %s", diff --git a/src/igmpproxy.c b/src/igmpproxy.c index c72a567f..9f825514 100644 --- a/src/igmpproxy.c +++ b/src/igmpproxy.c @@ -38,8 +38,10 @@ */ /* getopt() and clock_getime() */ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L +#ifndef __FreeBSD__ + #ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200112L + #endif #endif #include "igmpproxy.h" @@ -113,7 +115,7 @@ int main( int ArgCn, char *ArgVc[] ) { fputs("You must specify the configuration file.\n", stderr); exit(1); } - char *configFilePath = ArgVc[optind]; + configFilePath = ArgVc[optind]; // Chech that we are root if (geteuid() != 0) { @@ -180,6 +182,7 @@ int igmpProxyInit(void) { sigemptyset(&sa.sa_mask); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); // Loads configuration for Physical interfaces... buildIfVc(); @@ -193,52 +196,14 @@ int igmpProxyInit(void) { default: my_log( LOG_ERR, Err, "MRT_INIT failed" ); } - /* create VIFs for all IP, non-loop interfaces - */ - { - unsigned Ix; - struct IfDesc *Dp; - int vifcount = 0, upsvifcount = 0; - - // init array to "not set" - for ( Ix = 0; Ix < MAX_UPS_VIFS; Ix++) - { - upStreamIfIdx[Ix] = -1; - } - - for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { - - if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { - if(Dp->state == IF_STATE_UPSTREAM) { - if (upsvifcount < MAX_UPS_VIFS -1) - { - my_log(LOG_DEBUG, 0, "Found upstrem IF #%d, will assing as upstream Vif %d", - upsvifcount, Ix); - upStreamIfIdx[upsvifcount++] = Ix; - } else { - my_log(LOG_ERR, 0, "Cannot set VIF #%d as upstream as well. Mac upstream Vif count is %d", - Ix, MAX_UPS_VIFS); - } - } - - if (Dp->state != IF_STATE_DISABLED) { - addVIF( Dp ); - vifcount++; - } - } - } - - if(0 == upsvifcount) { - my_log(LOG_ERR, 0, "There must be at least 1 Vif as upstream."); - } - } - + createVifs(NULL); + // Initialize IGMP initIgmp(); // Initialize Routing table initRouteTable(); // Initialize timer - callout_init(); + free_all_callouts(); return 1; } @@ -250,7 +215,7 @@ void igmpProxyCleanUp(void) { my_log( LOG_DEBUG, 0, "clean handler called" ); free_all_callouts(); // No more timeouts. - clearAllRoutes(); // Remove all routes. + clearRoutes(NULL); // Remove all routes. disableMRouter(); // Disable the multirout API } @@ -262,21 +227,19 @@ void igmpProxyRun(void) { struct Config *config = getCommonConfig(); // Set some needed values. register int recvlen; - int MaxFD, Rt, secs; + int MaxFD, Rt, secs, rescanvif_timer = -1, rescanconf_timer = -1; fd_set ReadFDS; socklen_t dummy = 0; - struct timespec curtime, lasttime, difftime, tv; - // The timeout is a pointer in order to set it to NULL if nessecary. - struct timespec *timeout = &tv; + struct timespec curtime, lasttime, difftime, *timeout = &difftime; + + // First thing we send a membership query in downstream VIF's... + sendGeneralMembershipQuery(); // Initialize timer vars difftime.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &curtime); lasttime = curtime; - // First thing we send a membership query in downstream VIF's... - sendGeneralMembershipQuery(); - // Loop until the end... for (;;) { @@ -287,19 +250,39 @@ void igmpProxyRun(void) { my_log(LOG_NOTICE, 0, "Got a interrupt signal. Exiting."); break; } + if (sighandled & GOT_SIGHUP) { + sighandled &= ~GOT_SIGHUP; + + // Write debug notice with file path... + my_log(LOG_DEBUG, 0, "SIGHUP: Reloading config file at '%s'" , configFilePath); + + reloadConfig(); + } } - /* aimwang: call rebuildIfVc */ - if (config->rescanVif) - rebuildIfVc(); + // Set rescanvif or rescanconf timer. + if (!config->rescanConf && config->rescanVif > 0 && timer_leftTimer(rescanvif_timer) == -1) { + rescanvif_timer=timer_setTimer(config->rescanVif, (timer_f)rebuildIfVc, NULL); + } + if (config->rescanConf > 0 && timer_leftTimer(rescanconf_timer) == -1) { + rescanconf_timer=timer_setTimer(config->rescanConf, (timer_f)reloadConfig, NULL); + } - // Prepare timeout... - secs = timer_nextTimer(); - if(secs == -1) { - timeout = NULL; + // Timeout = 1s - difference between current and last time age_callout queue with .01s grace. + // This will make sure age_callout_queue is run once every s (timer resolution) +- 0.01s. + // If aging queues takes > .01s on very slow systems or when queue is very large, + // this will become less accurate by about the time it takes to age the queue + time to process a request. + clock_gettime(CLOCK_MONOTONIC, &curtime); + difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; + if (curtime.tv_nsec >= lasttime.tv_nsec ) { + timeout->tv_nsec = 999999999 - (curtime.tv_nsec - lasttime.tv_nsec); } else { - timeout->tv_nsec = 0; - timeout->tv_sec = (secs > 3) ? 3 : secs; // aimwang: set max timeout + timeout->tv_nsec = 999999999 - (1000000000 - lasttime.tv_nsec + curtime.tv_nsec); difftime.tv_sec--; + } + if ( difftime.tv_sec > 0 || timeout->tv_nsec < 10000000 ) { + timeout->tv_nsec = 999999999; timeout->tv_sec = 0; + lasttime = curtime; + age_callout_queue(curtime); } // Prepare for select. @@ -331,39 +314,7 @@ void igmpProxyRun(void) { acceptIgmp(recvlen); } } - - // At this point, we can handle timeouts... - do { - /* - * If the select timed out, then there's no other - * activity to account for and we don't need to - * call gettimeofday. - */ - if (Rt == 0) { - curtime.tv_sec = lasttime.tv_sec + secs; - curtime.tv_nsec = lasttime.tv_nsec; - Rt = -1; /* don't do this next time through the loop */ - } else { - clock_gettime(CLOCK_MONOTONIC, &curtime); - } - difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; - difftime.tv_nsec += curtime.tv_nsec - lasttime.tv_nsec; - while (difftime.tv_nsec > 1000000000) { - difftime.tv_sec++; - difftime.tv_nsec -= 1000000000; - } - if (difftime.tv_nsec < 0) { - difftime.tv_sec--; - difftime.tv_nsec += 1000000000; - } - lasttime = curtime; - if (secs == 0 || difftime.tv_sec > 0) - age_callout_queue(difftime.tv_sec); - secs = -1; - } while (difftime.tv_sec > 0); - } - } /* @@ -376,11 +327,10 @@ static void signalHandler(int sig) { case SIGTERM: sighandled |= GOT_SIGINT; break; + case SIGHUP: + sighandled |= GOT_SIGHUP; + break; /* XXX: Not in use. - case SIGHUP: - sighandled |= GOT_SIGHUP; - break; - case SIGUSR1: sighandled |= GOT_SIGUSR1; break; diff --git a/src/igmpproxy.h b/src/igmpproxy.h index 5e25b23a..0cdb4517 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -35,8 +35,10 @@ * igmpproxy.h - Header file for common includes. */ -#include "config.h" -#include "os.h" +#ifndef __FreeBSD__ + #include "config.h" + #include "os.h" +#endif #include #include @@ -59,6 +61,12 @@ #include #include #include +#include + +#ifdef __FreeBSD__ + #include "config.h" + #include "os.h" +#endif /* * Limit on length of route data @@ -113,14 +121,11 @@ void my_log( int Serverity, int Errno, const char *FmtSt, ... ); /* ifvc.c */ -#define MAX_IF 40 // max. number of interfaces recognized // Interface states #define IF_STATE_DISABLED 0 // Interface should be ignored. #define IF_STATE_UPSTREAM 1 // Interface is the upstream interface #define IF_STATE_DOWNSTREAM 2 // Interface is a downstream interface -#define IF_STATE_LOST 3 // aimwang: Temp from downstream to hidden -#define IF_STATE_HIDDEN 4 // aimwang: Interface is hidden // Multicast default values... #define DEFAULT_ROBUSTNESS 2 @@ -155,7 +160,13 @@ struct IfDesc { unsigned int robustness; unsigned char threshold; /* ttl limit */ unsigned int ratelimit; - unsigned int index; + signed int index; +}; + +struct IfDescP { + struct IfDesc *S; + struct IfDesc *E; + unsigned int nrint; }; // Keeps common configuration settings @@ -175,7 +186,9 @@ struct Config { unsigned int downstreamHostsHashTableSize; //~ aimwang added // Set if nneed to detect new interface. - unsigned short rescanVif; + unsigned short rescanVif; + // Set if nneed to detect config change. + unsigned short rescanConf; // Set if not detect new interface for down stream. unsigned short defaultInterfaceState; // 0: disable, 2: downstream //~ aimwang added done @@ -191,7 +204,7 @@ void buildIfVc( void ); struct IfDesc *getIfByName( const char *IfName ); struct IfDesc *getIfByIx( unsigned Ix ); struct IfDesc *getIfByAddress( uint32_t Ix ); -struct IfDesc *getIfByVifIndex( unsigned vifindex ); +struct IfDesc *getIfByVifIndex( signed vifindex ); int isAdressValidForIf(struct IfDesc* intrface, uint32_t ipaddr); /* mroute-api.c @@ -208,7 +221,7 @@ extern int MRouterFD; int enableMRouter( void ); void disableMRouter( void ); -void addVIF( struct IfDesc *Dp ); +void addVIF( struct IfDesc *Dp, struct IfDesc *oDp ); void delVIF( struct IfDesc *Dp ); int addMRoute( struct MRouteDesc * Dp ); int delMRoute( struct MRouteDesc * Dp ); @@ -216,8 +229,11 @@ int getVifIx( struct IfDesc *IfDp ); /* config.c */ +char *configFilePath; +void reloadConfig(); int loadConfig(char *configFile); void configureVifs(void); +void createVifs(struct IfDescP *rebuildP); struct Config *getCommonConfig(void); /* igmp.c @@ -261,9 +277,10 @@ int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ); /* rttable.c */ void initRouteTable(void); -void clearAllRoutes(void); +void joinMcRoutersGroup(struct IfDesc *Dp); +void clearRoutes(struct IfDesc *IfDp); int insertRoute(uint32_t group, int ifx, uint32_t src); -int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif); +int activateRoute(uint32_t group, uint32_t originAddr, unsigned upstrVif); void ageActiveRoutes(void); void setRouteLastMemberMode(uint32_t group, uint32_t src); int lastMemberGroupAge(uint32_t group); @@ -276,16 +293,14 @@ void acceptGroupReport(uint32_t src, uint32_t group); void acceptLeaveMessage(uint32_t src, uint32_t group); void sendGeneralMembershipQuery(void); -/* callout.c +/* callout.c */ typedef void (*timer_f)(void *); void callout_init(void); void free_all_callouts(void); -void age_callout_queue(int); -int timer_nextTimer(void); +void age_callout_queue(struct timespec curtime); int timer_setTimer(int, timer_f, void *); -int timer_clearTimer(int); int timer_leftTimer(int); /* confread.c diff --git a/src/mroute-api.c b/src/mroute-api.c index fae4ab02..54910af9 100644 --- a/src/mroute-api.c +++ b/src/mroute-api.c @@ -53,9 +53,11 @@ char *send_buf; /* output packet buffer */ // my internal virtual interfaces descriptor vector -static struct VifDesc { +struct VifDesc { struct IfDesc *IfDp; -} VifDescVc[ MAXVIFS ]; + struct VifDesc *next; +}; +struct VifDesc *VifDescVc = NULL; /* ** Initialises the mrouted API and locks it by this exclusively. @@ -99,6 +101,7 @@ void disableMRouter(void) void delVIF( struct IfDesc *IfDp ) { struct vifctl VifCtl; + struct VifDesc *VifDp, *PrevVifDp = NULL; if ((unsigned int)-1 == IfDp->index) return; @@ -111,44 +114,104 @@ void delVIF( struct IfDesc *IfDp ) if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_DEL_VIF, (char *)&VifCtl, sizeof( VifCtl ) ) ) my_log( LOG_WARNING, errno, "MRT_DEL_VIF" ); + + // Remove Vif from list. + for ( VifDp = VifDescVc; VifDp; VifDp=VifDp->next) { + if ( VifDp->IfDp == IfDp ) { + if ( PrevVifDp == NULL ) { + if (VifDp->next == NULL ) { + VifDescVc = NULL; + } else { + VifDescVc = VifDp->next; + } + free (VifDp); + break; + } else { + PrevVifDp->next=VifDp->next; + free (VifDp); + } + } + PrevVifDp = VifDp; + } } /* ** Adds the interface '*IfDp' as virtual interface to the mrouted API ** */ -void addVIF( struct IfDesc *IfDp ) +void addVIF( struct IfDesc *IfDp, struct IfDesc *oDp ) { struct vifctl VifCtl; - struct VifDesc *VifDp; - - /* search free (aimwang: or exist) VifDesc - */ - for ( VifDp = VifDescVc; VifDp < VCEP( VifDescVc ); VifDp++ ) { - if ( ! VifDp->IfDp || VifDp->IfDp == IfDp) - break; + struct VifDesc *VifDp, *NewVifDp; + int nrVif = 0, Ix = 0; + + // Search IfDescVc for available vif Ix and relink vifs during rebuild. + for ( VifDp = VifDescVc; VifDp; VifDp=VifDp->next ) { + if ( oDp != NULL && VifDp->IfDp == oDp ) { + // Relink vifindex during rebuild or SIGHUP + VifDp->IfDp = IfDp; VifDp->IfDp->index = oDp->index; + my_log (LOG_DEBUG,0,"addVIF: relinking %s as vif Ix %d",VifDp->IfDp->Name, VifDp->IfDp->index ); + return; + } + if ( VifDp->next != NULL ) { + // Middle of list if next Ix is free, set. + if ( VifDp->IfDp->index == nrVif && VifDp->IfDp->index < VifDp->next->IfDp->index - 1 ) Ix = VifDp->IfDp->index + 1; + } else if ( VifDp->IfDp->index == nrVif ) { + // End of list, if in order set Ix to next. Otherwise Ix is already set above (or 0 if available). + Ix = nrVif + 1; + } + nrVif++; } - /* no more space - */ - if ( VifDp >= VCEP( VifDescVc ) ) + // no more space + if ( nrVif >= MAXVIFS ) my_log( LOG_ERR, ENOMEM, "addVIF, out of VIF space" ); - VifDp->IfDp = IfDp; + // Allocate memory for new VifDesc. + NewVifDp=(struct VifDesc*)malloc(sizeof(struct VifDesc)); + if ( ! NewVifDp ) my_log(LOG_ERR, 0, "Out of memory."); + + // Insert vif into the list at the correct spot. + if ( VifDescVc == NULL ) { + // List is empty, new list. + VifDescVc = NewVifDp; + NewVifDp->next = NULL; + } else { + if ( Ix == 0 ) { + // Insert at begin of list. + NewVifDp->next = VifDescVc; + VifDescVc = NewVifDp; + } else { + // Find spot for Ix and insert. + for ( VifDp = VifDescVc; VifDp; VifDp=VifDp->next ) { + if ( VifDp->next == NULL ) { + // Append to end of list. + NewVifDp->next = NULL; + VifDp->next = NewVifDp; + break; + } + if ( VifDp->next->IfDp->index > Ix ) { + // Found correct spot, insert. + NewVifDp->next = VifDp->next; + VifDp->next = NewVifDp; + break; + } + } + } + } - VifCtl.vifc_vifi = VifDp - VifDescVc; + // Set the index flags etc... + NewVifDp->IfDp = IfDp; + VifCtl.vifc_vifi = IfDp->index = Ix; VifCtl.vifc_flags = 0; /* no tunnel, no source routing, register ? */ - VifCtl.vifc_threshold = VifDp->IfDp->threshold; // Packet TTL must be at least 1 to pass them - VifCtl.vifc_rate_limit = VifDp->IfDp->ratelimit; // Ratelimit + VifCtl.vifc_threshold = IfDp->threshold; // Packet TTL must be at least 1 to pass them + VifCtl.vifc_rate_limit = IfDp->ratelimit; // Ratelimit - VifCtl.vifc_lcl_addr.s_addr = VifDp->IfDp->InAdr.s_addr; + VifCtl.vifc_lcl_addr.s_addr = IfDp->InAdr.s_addr; VifCtl.vifc_rmt_addr.s_addr = INADDR_ANY; - // Set the index... - VifDp->IfDp->index = VifCtl.vifc_vifi; - my_log( LOG_NOTICE, 0, "adding VIF, Ix %d Fl 0x%x IP 0x%08x %s, Threshold: %d, Ratelimit: %d", - VifCtl.vifc_vifi, VifCtl.vifc_flags, VifCtl.vifc_lcl_addr.s_addr, VifDp->IfDp->Name, + VifCtl.vifc_vifi, VifCtl.vifc_flags, VifCtl.vifc_lcl_addr.s_addr, IfDp->Name, VifCtl.vifc_threshold, VifCtl.vifc_rate_limit); struct SubnetList *currSubnet; @@ -161,7 +224,6 @@ void addVIF( struct IfDesc *IfDp ) if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_ADD_VIF, (char *)&VifCtl, sizeof( VifCtl ) ) ) my_log( LOG_ERR, errno, "MRT_ADD_VIF" ); - } /* diff --git a/src/rttable.c b/src/rttable.c index 3d33c77e..574f4154 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -54,7 +54,7 @@ struct RouteTable { // Keeps the upstream membership state... short upstrState; // Upstream membership state. - int upstrVif; // Upstream Vif Index. + unsigned upstrVif; // Upstream Vif Index. // These parameters contain aging details. uint32_t ageVifBits; // Bits representing aging VIFs. @@ -139,18 +139,20 @@ void initRouteTable(void) { // Join the all routers group on downstream vifs... 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) { - my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", - inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); + if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) joinMcRoutersGroup(Dp); + } +} - //k_join(allrouters_group, Dp->InAdr.s_addr); - joinMcGroup( getMcGroupSock(), Dp, allrouters_group ); +void joinMcRoutersGroup(struct IfDesc *Dp) { + my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", + inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); - my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s", - inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); - joinMcGroup( getMcGroupSock(), Dp, alligmp3_group ); - } - } + //k_join(allrouters_group, Dp->InAdr.s_addr); + joinMcGroup( getMcGroupSock(), Dp, allrouters_group ); + + my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s", + inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); + joinMcGroup( getMcGroupSock(), Dp, alligmp3_group ); } /** @@ -227,15 +229,18 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { /** * Clear all routes from routing table, and alerts Leaves upstream. +* If argument is pointer to interface clear routes for corresponding if. */ -void clearAllRoutes(void) { - struct RouteTable *croute, *remainroute; +void clearRoutes(struct IfDesc *IfDp) { + struct RouteTable *croute, *remainroute, *prevroute = NULL; // Loop through all routes... for(croute = routing_table; croute; croute = remainroute) { remainroute = croute->nextroute; + if ( IfDp && croute->upstrVif != IfDp->index ) continue; + // Log the cleanup in debugmode... my_log(LOG_DEBUG, 0, "Removing route entry for %s", inetFmt(croute->group, s1)); @@ -246,15 +251,18 @@ void clearAllRoutes(void) { } // Send Leave message upstream. - sendJoinLeaveUpstream(croute, 0); + if ( IfDp ) leaveMcGroup( getMcGroupSock(), IfDp, croute->group ); + else if ( croute->upstrVif != -1 ) leaveMcGroup( getMcGroupSock(), getIfByIx(croute->upstrVif), croute->group ); // Clear memory, and set pointer to next route... free(croute); } - routing_table = NULL; + if ( ! IfDp ) { + routing_table = NULL; - // Send a notice that the routing table is empty... - my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); + // Send a notice that the routing table is empty... + my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); + } } /** @@ -430,7 +438,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { * activated, it's reinstalled in the kernel. If * the route is activated, no originAddr is needed. */ -int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif) { +int activateRoute(uint32_t group, uint32_t originAddr, unsigned upstrVif) { struct RouteTable* croute; int result = 0; @@ -603,7 +611,7 @@ int lastMemberGroupAge(uint32_t group) { * Remove a specified route. Returns 1 on success, * and 0 if route was not found. */ -static int removeRoute(struct RouteTable* croute) { +int removeRoute(struct RouteTable* croute) { struct Config *conf = getCommonConfig(); int result = 1;