/* ** igmpproxy - IGMP proxy based multicast router ** Copyright (C) 2005 Johnny Egeland ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** **---------------------------------------------------------------------------- ** ** This software is derived work from the following software. The original ** source code has been modified from it's original state by the author ** of igmpproxy. ** ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill ** - Licensed under the GNU General Public License, version 2 ** ** mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of ** Leland Stanford Junior University. ** - Original license can be found in the Stanford.txt file. ** */ /** * igmpproxy.c - The main file for the IGMP proxy application. * * February 2005 - Johnny Egeland */ #include "igmpproxy.h" static const char Usage[] = "Usage: igmpproxy [-h] [-d] [-v [-v]] \n" "\n" " -h Display this help screen\n" " -d Run in debug mode. Output all messages on stderr\n" " -v Be verbose. Give twice to see even debug messages.\n" "\n" PACKAGE_STRING "\n" ; // Local function Prototypes static void signalHandler(int); int igmpProxyInit(); void igmpProxyCleanUp(); void igmpProxyRun(); // Global vars... static int sighandled = 0; #define GOT_SIGINT 0x01 #define GOT_SIGHUP 0x02 #define GOT_SIGUSR1 0x04 #define GOT_SIGUSR2 0x08 // The upstream VIF index int upStreamVif; /** * Program main method. Is invoked when the program is started * on commandline. The number of commandline arguments, and a * pointer to the arguments are recieved on the line... */ int main( int ArgCn, char *ArgVc[] ) { // Parse the commandline options and setup basic settings.. for (int c; (c = getopt(ArgCn, ArgVc, "vdh")) != -1;) { switch (c) { case 'd': Log2Stderr = true; break; case 'v': if (LogLevel == LOG_INFO) LogLevel = LOG_DEBUG; else LogLevel = LOG_INFO; break; case 'h': fputs(Usage, stderr); exit(0); break; default: exit(1); break; } } if (optind != ArgCn - 1) { fputs("You must specify the configuration file.\n", stderr); exit(1); } char *configFilePath = ArgVc[optind]; // Chech that we are root if (geteuid() != 0) { fprintf(stderr, "igmpproxy: must be root\n"); exit(1); } openlog("igmpproxy", LOG_PID, LOG_USER); // Write debug notice with file path... my_log(LOG_DEBUG, 0, "Searching for config file at '%s'" , configFilePath); do { // Loads the config file... if( ! loadConfig( configFilePath ) ) { my_log(LOG_ERR, 0, "Unable to load config file..."); break; } // Initializes the deamon. if ( !igmpProxyInit() ) { my_log(LOG_ERR, 0, "Unable to initialize IGMPproxy."); break; } // Go to the main loop. igmpProxyRun(); // Clean up igmpProxyCleanUp(); } while ( false ); // Inform that we are exiting. my_log(LOG_INFO, 0, "Shutdown complete...."); exit(0); } /** * Handles the initial startup of the daemon. */ int igmpProxyInit() { struct sigaction sa; int Err; sa.sa_handler = signalHandler; sa.sa_flags = 0; /* Interrupt system calls */ sigemptyset(&sa.sa_mask); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); // Loads configuration for Physical interfaces... buildIfVc(); // Configures IF states and settings configureVifs(); switch ( Err = enableMRouter() ) { case 0: break; case EADDRINUSE: my_log( LOG_ERR, EADDRINUSE, "MC-Router API already in use" ); break; 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; upStreamVif = -1; for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { if(Dp->state == IF_STATE_UPSTREAM) { if(upStreamVif == -1) { upStreamVif = Ix; } else { my_log(LOG_ERR, 0, "Vif #%d was already upstream. Cannot set VIF #%d as upstream as well.", upStreamVif, Ix); } } addVIF( Dp ); vifcount++; } } // If there is only one VIF, or no defined upstream VIF, we send an error. if(vifcount < 2 || upStreamVif < 0) { my_log(LOG_ERR, 0, "There must be at least 2 Vif's where one is upstream."); } } // Initialize IGMP initIgmp(); // Initialize Routing table initRouteTable(); // Initialize timer callout_init(); return 1; } /** * Clean up all on exit... */ void igmpProxyCleanUp() { my_log( LOG_DEBUG, 0, "clean handler called" ); free_all_callouts(); // No more timeouts. clearAllRoutes(); // Remove all routes. disableMRouter(); // Disable the multirout API } /** * Main daemon loop. */ void igmpProxyRun() { // Get the config. //struct Config *config = getCommonConfig(); // Set some needed values. register int recvlen; int MaxFD, Rt, secs; fd_set ReadFDS; socklen_t dummy = 0; struct timeval curtime, lasttime, difftime, tv; // The timeout is a pointer in order to set it to NULL if nessecary. struct timeval *timeout = &tv; // Initialize timer vars difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; // First thing we send a membership query in downstream VIF's... sendGeneralMembershipQuery(); // Loop until the end... for (;;) { // Process signaling... if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; my_log(LOG_NOTICE, 0, "Got a interupt signal. Exiting."); break; } } // Prepare timeout... secs = timer_nextTimer(); if(secs == -1) { timeout = NULL; } else { timeout->tv_usec = 0; timeout->tv_sec = secs; } // Prepare for select. MaxFD = MRouterFD; FD_ZERO( &ReadFDS ); FD_SET( MRouterFD, &ReadFDS ); // wait for input Rt = select( MaxFD +1, &ReadFDS, NULL, NULL, timeout ); // log and ignore failures if( Rt < 0 ) { my_log( LOG_WARNING, errno, "select() failure" ); continue; } else if( Rt > 0 ) { // Read IGMP request, and handle it... if( FD_ISSET( MRouterFD, &ReadFDS ) ) { recvlen = recvfrom(MRouterFD, recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy); if (recvlen < 0) { if (errno != EINTR) my_log(LOG_ERR, errno, "recvfrom"); continue; } 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_usec = lasttime.tv_usec; Rt = -1; /* don't do this next time through the loop */ } else { gettimeofday(&curtime, NULL); } difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; while (difftime.tv_usec > 1000000) { difftime.tv_sec++; difftime.tv_usec -= 1000000; } if (difftime.tv_usec < 0) { difftime.tv_sec--; difftime.tv_usec += 1000000; } lasttime = curtime; if (secs == 0 || difftime.tv_sec > 0) age_callout_queue(difftime.tv_sec); secs = -1; } while (difftime.tv_sec > 0); } } /* * Signal handler. Take note of the fact that the signal arrived * so that the main loop can take care of it. */ static void signalHandler(int sig) { switch (sig) { case SIGINT: case SIGTERM: sighandled |= GOT_SIGINT; break; /* XXX: Not in use. case SIGHUP: sighandled |= GOT_SIGHUP; break; case SIGUSR1: sighandled |= GOT_SIGUSR1; break; case SIGUSR2: sighandled |= GOT_SIGUSR2; break; */ } }