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