1/* 2** igmpproxy - IGMP proxy based multicast router 3** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org> 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU General Public License as published by 7** the Free Software Foundation; either version 2 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU General Public License for more details. 14** 15** You should have received a copy of the GNU General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18** 19**---------------------------------------------------------------------------- 20** 21** This software is derived work from the following software. The original 22** source code has been modified from it's original state by the author 23** of igmpproxy. 24** 25** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de> 26** - Licensed under the GNU General Public License, version 2 27** 28** mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of 29** Leland Stanford Junior University. 30** - Original license can be found in the Stanford.txt file. 31** 32*/ 33/** 34* igmpproxy.c - The main file for the IGMP proxy application. 35* 36* February 2005 - Johnny Egeland 37*/ 38 39#include "igmpproxy.h" 40#include <sys/sysinfo.h> 41 42static const char Usage[] = 43"Usage: igmpproxy [-h] [-d] [-v [-v]] <configfile>\n" 44"\n" 45" -h Display this help screen\n" 46" -d Run in debug mode. Output all messages on stderr\n" 47" -v Be verbose. Give twice to see even debug messages.\n" 48"\n" 49PACKAGE_STRING "\n" 50; 51 52// Local function Prototypes 53static void signalHandler(int); 54int igmpProxyInit(); 55void igmpProxyCleanUp(); 56void igmpProxyRun(); 57 58// Global vars... 59static int sighandled = 0; 60#define GOT_SIGINT 0x01 61#define GOT_SIGHUP 0x02 62#define GOT_SIGUSR1 0x04 63#define GOT_SIGUSR2 0x08 64 65// The upstream VIF index 66int upStreamVif; 67 68/** 69* Program main method. Is invoked when the program is started 70* on commandline. The number of commandline arguments, and a 71* pointer to the arguments are recieved on the line... 72*/ 73int main( int ArgCn, char *ArgVc[] ) { 74 75 int c; 76 77 // Parse the commandline options and setup basic settings.. 78 while ((c = getopt(ArgCn, ArgVc, "vdh")) != -1) { 79 switch (c) { 80 case 'd': 81 Log2Stderr = true; 82 break; 83 case 'v': 84 LogLevel++; 85 break; 86 case 'h': 87 fputs(Usage, stderr); 88 exit(0); 89 break; 90 default: 91 exit(1); 92 break; 93 } 94 } 95 96 if (optind != ArgCn - 1) { 97 fputs("You must specify the configuration file.\n", stderr); 98 exit(1); 99 } 100 char *configFilePath = ArgVc[optind]; 101 102 // Chech that we are root 103 if (geteuid() != 0) { 104 fprintf(stderr, "igmpproxy: must be root\n"); 105 exit(1); 106 } 107 108 openlog("igmpproxy", LOG_PID, LOG_USER); 109 110 // Write debug notice with file path... 111 my_log(LOG_DEBUG, 0, "Searching for config file at '%s'" , configFilePath); 112 113 do { 114 115 // Loads the config file... 116 if( ! loadConfig( configFilePath ) ) { 117 my_log(LOG_ERR, 0, "Unable to load config file..."); 118 break; 119 } 120 121 // Initializes the deamon. 122 if ( !igmpProxyInit() ) { 123 my_log(LOG_ERR, 0, "Unable to initialize IGMPproxy."); 124 break; 125 } 126 127 if ( !Log2Stderr ) { 128 129 // Only daemon goes past this line... 130 if (fork()) exit(0); 131 132 // Detach daemon from terminal 133 if ( close( 0 ) < 0 || close( 1 ) < 0 || close( 2 ) < 0 134 || open( "/dev/null", 0 ) != 0 || dup2( 0, 1 ) < 0 || dup2( 0, 2 ) < 0 135 || setpgrp() < 0 136 ) { 137 my_log( LOG_ERR, errno, "failed to detach daemon" ); 138 } 139 } 140 141 // Go to the main loop. 142 igmpProxyRun(); 143 144 // Clean up 145 igmpProxyCleanUp(); 146 147 } while ( false ); 148 149 // Inform that we are exiting. 150 my_log(LOG_INFO, 0, "Shutdown complete...."); 151 152 exit(0); 153} 154 155 156 157/** 158* Handles the initial startup of the daemon. 159*/ 160int igmpProxyInit() { 161 struct sigaction sa; 162 int Err; 163 164 165 sa.sa_handler = signalHandler; 166 sa.sa_flags = 0; /* Interrupt system calls */ 167 sigemptyset(&sa.sa_mask); 168 sigaction(SIGTERM, &sa, NULL); 169 sigaction(SIGINT, &sa, NULL); 170 171 // Loads configuration for Physical interfaces... 172 buildIfVc(); 173 174 // Configures IF states and settings 175 configureVifs(); 176 177 switch ( Err = enableMRouter() ) { 178 case 0: break; 179 case EADDRINUSE: my_log( LOG_ERR, EADDRINUSE, "MC-Router API already in use" ); break; 180 default: my_log( LOG_ERR, Err, "MRT_INIT failed" ); 181 } 182 183 /* create VIFs for all IP, non-loop interfaces 184 */ 185 { 186 unsigned Ix; 187 struct IfDesc *Dp; 188 int vifcount = 0; 189 upStreamVif = -1; 190 191 for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 192 193 if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state != IF_STATE_DISABLED ) { 194 if(Dp->state == IF_STATE_UPSTREAM) { 195 if(upStreamVif == -1) { 196 upStreamVif = Ix; 197 } else { 198 my_log(LOG_ERR, 0, "Vif #%d was already upstream. Cannot set VIF #%d as upstream as well.", 199 upStreamVif, Ix); 200 } 201 } 202 203 addVIF( Dp ); 204 vifcount++; 205 } 206 } 207 208 // If there is only one VIF, or no defined upstream VIF, we send an error. 209 if(vifcount < 2 || upStreamVif < 0) { 210 my_log(LOG_ERR, 0, "There must be at least 2 Vif's where one is upstream."); 211 } 212 } 213 214 // Initialize IGMP 215 initIgmp(); 216 // Initialize Routing table 217 initRouteTable(); 218 // Initialize timer 219 callout_init(); 220 221 222 return 1; 223} 224 225/** 226* Clean up all on exit... 227*/ 228void igmpProxyCleanUp() { 229 230 my_log( LOG_DEBUG, 0, "clean handler called" ); 231 232 free_all_callouts(); // No more timeouts. 233 clearAllRoutes(); // Remove all routes. 234 disableMRouter(); // Disable the multirout API 235 236} 237 238static void getuptime(struct timeval *tv) 239{ 240 struct sysinfo si; 241 242 sysinfo(&si); 243 244 tv->tv_sec = si.uptime; 245 tv->tv_usec = 0; 246} 247 248/** 249* Main daemon loop. 250*/ 251void igmpProxyRun() { 252 // Get the config. 253 //struct Config *config = getCommonConfig(); 254 // Set some needed values. 255 register int recvlen; 256 int MaxFD, Rt, secs; 257 fd_set ReadFDS; 258 socklen_t dummy = 0; 259 struct timeval curtime, lasttime, difftime, tv; 260 // The timeout is a pointer in order to set it to NULL if nessecary. 261 struct timeval *timeout = &tv; 262 263 // Initialize timer vars 264 difftime.tv_usec = 0; 265 getuptime(&curtime); 266 lasttime = curtime; 267 268 // First thing we send a membership query in downstream VIF's... 269 sendGeneralMembershipQuery(); 270 271 // Loop until the end... 272 for (;;) { 273 274 // Process signaling... 275 if (sighandled) { 276 if (sighandled & GOT_SIGINT) { 277 sighandled &= ~GOT_SIGINT; 278 my_log(LOG_NOTICE, 0, "Got a interupt signal. Exiting."); 279 break; 280 } 281 } 282 283 // Prepare timeout... 284 secs = timer_nextTimer(); 285 if(secs == -1) { 286 timeout = NULL; 287 } else { 288 timeout->tv_usec = 0; 289 timeout->tv_sec = secs; 290 } 291 292 // Prepare for select. 293 MaxFD = MRouterFD; 294 295 FD_ZERO( &ReadFDS ); 296 FD_SET( MRouterFD, &ReadFDS ); 297 298 // wait for input 299 Rt = select( MaxFD +1, &ReadFDS, NULL, NULL, timeout ); 300 301 // log and ignore failures 302 if( Rt < 0 ) { 303 my_log( LOG_WARNING, errno, "select() failure" ); 304 continue; 305 } 306 else if( Rt > 0 ) { 307 308 // Read IGMP request, and handle it... 309 if( FD_ISSET( MRouterFD, &ReadFDS ) ) { 310 311 recvlen = recvfrom(MRouterFD, recv_buf, RECV_BUF_SIZE, 312 0, NULL, &dummy); 313 if (recvlen < 0) { 314 if (errno != EINTR) my_log(LOG_ERR, errno, "recvfrom"); 315 continue; 316 } 317 318 319 acceptIgmp(recvlen); 320 } 321 } 322 323 // At this point, we can handle timeouts... 324 do { 325 /* 326 * If the select timed out, then there's no other 327 * activity to account for and we don't need to 328 * call getuptime. 329 */ 330 if (Rt == 0) { 331 curtime.tv_sec = lasttime.tv_sec + secs; 332 curtime.tv_usec = lasttime.tv_usec; 333 Rt = -1; /* don't do this next time through the loop */ 334 } else { 335 getuptime(&curtime); 336 } 337 difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; 338 difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; 339 while (difftime.tv_usec > 1000000) { 340 difftime.tv_sec++; 341 difftime.tv_usec -= 1000000; 342 } 343 if (difftime.tv_usec < 0) { 344 difftime.tv_sec--; 345 difftime.tv_usec += 1000000; 346 } 347 lasttime = curtime; 348 if (secs == 0 || difftime.tv_sec > 0) 349 age_callout_queue(difftime.tv_sec); 350 secs = -1; 351 } while (difftime.tv_sec > 0); 352 353 } 354 355} 356 357/* 358 * Signal handler. Take note of the fact that the signal arrived 359 * so that the main loop can take care of it. 360 */ 361static void signalHandler(int sig) { 362 switch (sig) { 363 case SIGINT: 364 case SIGTERM: 365 sighandled |= GOT_SIGINT; 366 break; 367 /* XXX: Not in use. 368 case SIGHUP: 369 sighandled |= GOT_SIGHUP; 370 break; 371 372 case SIGUSR1: 373 sighandled |= GOT_SIGUSR1; 374 break; 375 376 case SIGUSR2: 377 sighandled |= GOT_SIGUSR2; 378 break; 379 */ 380 } 381} 382