xref: /netbsd-src/usr.sbin/mrouted/main.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*
2  * The mrouted program is covered by the license in the accompanying file
3  * named "LICENSE".  Use of the mrouted program represents acceptance of
4  * the terms and conditions listed in that file.
5  *
6  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7  * Leland Stanford Junior University.
8  *
9  *
10  * From: Id: main.c,v 1.5 1993/06/24 05:11:16 deering Exp $
11  *      $Id: main.c,v 1.2 1994/05/08 15:08:55 brezak Exp $
12  */
13 
14 /*
15  * Written by Steve Deering, Stanford University, February 1989.
16  *
17  * (An earlier version of DVMRP was implemented by David Waitzman of
18  *  BBN STC by extending Berkeley's routed program.  Some of Waitzman's
19  *  extensions have been incorporated into mrouted, but none of the
20  *  original routed code has been adopted.)
21  */
22 
23 #ifndef lint
24 static char rcsid[] = "$Id: main.c,v 1.2 1994/05/08 15:08:55 brezak Exp $";
25 #endif
26 
27 #include "defs.h"
28 
29 extern char *configfilename;
30 
31 static char pidfilename[]  = _PATH_MROUTED_PID;
32 static char dumpfilename[] = _PATH_MROUTED_DUMP;
33 
34 int debug = 0;
35 
36 
37 /*
38  * Forward declarations.
39  */
40 static void fasttimer();
41 static void timer();
42 static void hup();
43 static void dump();
44 static void fdump();
45 
46 
47 main(argc, argv)
48     int argc;
49     char *argv[];
50 {
51     register int recvlen;
52     register int omask;
53     int dummy;
54     FILE *fp;
55     extern uid_t geteuid();
56 
57     setlinebuf(stderr);
58 
59     if (geteuid() != 0) {
60 	fprintf(stderr, "mrouted: must be root\n");
61 	exit(1);
62     }
63 
64     argv++, argc--;
65     while (argc > 0 && *argv[0] == '-') {
66 	if (strcmp(*argv, "-d") == 0) {
67 	    if (argc > 1 && isdigit(*(argv + 1)[0])) {
68 		argv++, argc--;
69 		debug = atoi(*argv);
70 	    } else
71 		debug = DEFAULT_DEBUG;
72 	} else if (strcmp(*argv, "-c") == 0) {
73 	    if (argc > 1) {
74 		argv++, argc--;
75 		configfilename = *argv;
76 	    } else
77 		goto usage;
78 	} else
79 	    goto usage;
80 	argv++, argc--;
81     }
82 
83     if (argc > 0) {
84 usage:	fprintf(stderr, "usage: mrouted [-c configfile] [-d [debug_level]]\n");
85 	exit(1);
86     }
87 
88     if (debug == 0) {
89 	/*
90 	 * Detach from the terminal
91 	 */
92 	int t;
93 
94 	if (fork()) exit(0);
95 	(void)close(0);
96 	(void)close(1);
97 	(void)close(2);
98 	(void)open("/", 0);
99 	(void)dup2(0, 1);
100 	(void)dup2(0, 2);
101 	t = open("/dev/tty", 2);
102 	if (t >= 0) {
103 	    (void)ioctl(t, TIOCNOTTY, (char *)0);
104 	    (void)close(t);
105 	}
106     }
107     else fprintf(stderr, "debug level %u\n", debug);
108 
109 #ifdef LOG_DAEMON
110     (void)openlog("mrouted", LOG_PID, LOG_DAEMON);
111     (void)setlogmask(LOG_UPTO(LOG_NOTICE));
112 #else
113     (void)openlog("mrouted", LOG_PID);
114 #endif
115     log(LOG_NOTICE, 0, "mrouted version %d.%d",
116 			PROTOCOL_VERSION, MROUTED_VERSION);
117 
118     fp = fopen(pidfilename, "w");
119     if (fp != NULL) {
120 	fprintf(fp, "%d\n", getpid());
121 	(void) fclose(fp);
122     }
123 
124     init_igmp();
125     k_init_dvmrp();		/* enable DVMRP routing in kernel */
126     init_routes();
127     init_vifs();
128 
129     if (debug >= 2) dump();
130 
131     (void)signal(SIGALRM, fasttimer);
132     (void)signal(SIGHUP,  hup);
133     (void)signal(SIGTERM, hup);
134     (void)signal(SIGINT,  hup);
135     (void)signal(SIGUSR1, fdump);
136     if (debug != 0)
137 	(void)signal(SIGQUIT, dump);
138 
139     (void)alarm(1);	 /* schedule first timer interrupt */
140 
141     /*
142      * Main receive loop.
143      */
144     dummy = 0;
145     for(;;) {
146 	recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
147 			   0, NULL, &dummy);
148 	if (recvlen < 0) {
149 	    if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
150 	    continue;
151 	}
152 	omask = sigblock(sigmask(SIGALRM));
153 	accept_igmp(recvlen);
154 	(void)sigsetmask(omask);
155     }
156 }
157 
158 
159 /*
160  * routine invoked every second.  It's main goal is to cycle through
161  * the routing table and send partial updates to all neighbors at a
162  * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
163  * seconds.  Also, every TIMER_INTERVAL seconds it calls timer() to
164  * do all the other time-based processing.
165  */
166 static void fasttimer()
167 {
168     static unsigned int tlast;
169     static unsigned int nsent;
170     register unsigned int t = tlast + 1;
171     register int n;
172 
173     /*
174      * if we're in the last second, send everything that's left.
175      * otherwise send at least the fraction we should have sent by now.
176      */
177     if (t >= ROUTE_REPORT_INTERVAL) {
178 	register int nleft = nroutes - nsent;
179 	while (nleft > 0) {
180 	    if ((n = report_next_chunk()) <= 0)
181 		break;
182 	    nleft -= n;
183 	}
184 	tlast = 0;
185 	nsent = 0;
186     } else {
187 	register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
188 	while (nsent < ncum) {
189 	    if ((n = report_next_chunk()) <= 0)
190 		break;
191 	    nsent += n;
192 	}
193 	tlast = t;
194     }
195     if ((t % TIMER_INTERVAL) == 0)
196 	timer();
197 
198     alarm(1);
199 }
200 
201 /*
202  * The 'virtual_time' variable is initialized to a value that will cause the
203  * first invocation of timer() to send a probe or route report to all vifs
204  * and send group membership queries to all subnets for which this router is
205  * querier.  This first invocation occurs approximately TIMER_INTERVAL seconds
206  * after the router starts up.   Note that probes for neighbors and queries
207  * for group memberships are also sent at start-up time, as part of initial-
208  * ization.  This repetition after a short interval is desirable for quickly
209  * building up topology and membership information in the presence of possible
210  * packet loss.
211  *
212  * 'virtual_time' advances at a rate that is only a crude approximation of
213  * real time, because it does not take into account any time spent processing,
214  * and because the timer intervals are sometimes shrunk by a random amount to
215  * avoid unwanted synchronization with other routers.
216  */
217 
218 static u_long virtual_time = 0;
219 
220 
221 /*
222  * Timer routine.  Performs periodic neighbor probing, route reporting, and
223  * group querying duties, and drives various timers in routing entries and
224  * virtual interface data structures.
225  */
226 static void timer()
227 {
228     age_routes();	/* Advance the timers in the route entries     */
229     age_vifs();		/* Advance the timers for neighbors and groups */
230 
231     if (virtual_time % GROUP_QUERY_INTERVAL == 0) {
232 	/*
233 	 * Time to query the local group memberships on all subnets
234 	 * for which this router is the elected querier.
235 	 */
236 	query_groups();
237     }
238 
239     if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
240 	/*
241 	 * Time to send a probe on all vifs from which no neighbors have
242 	 * been heard.  Also, check if any inoperative interfaces have now
243 	 * come up.  (If they have, they will also be probed as part of
244 	 * their initialization.)
245 	 */
246 	probe_for_neighbors();
247 
248 	if (vifs_down)
249 	    check_vif_state();
250     }
251 
252     delay_change_reports = FALSE;
253     if (routes_changed) {
254 	/*
255 	 * Some routes have changed since the last timer interrupt, but
256 	 * have not been reported yet.  Report the changed routes to all
257 	 * neighbors.
258 	 */
259 	report_to_all_neighbors(CHANGED_ROUTES);
260     }
261 
262     /*
263      * Advance virtual time
264      */
265     virtual_time += TIMER_INTERVAL;
266 }
267 
268 
269 /*
270  * On hangup signal, let everyone know we're going away.
271  */
272 static void hup()
273 {
274     log(LOG_INFO, 0, "hup");
275     expire_all_routes();
276     report_to_all_neighbors(ALL_ROUTES);
277     exit(1);
278 }
279 
280 
281 /*
282  * Dump internal data structures to stderr.
283  */
284 static void dump()
285 {
286     dump_vifs(stderr);
287     dump_routes(stderr);
288 }
289 
290 
291 /*
292  * Dump internal data structures to a file.
293  */
294 static void fdump()
295 {
296     FILE *fp;
297 
298     fp = fopen(dumpfilename, "w");
299     if (fp != NULL) {
300 	dump_vifs(fp);
301 	dump_routes(fp);
302 	(void) fclose(fp);
303     }
304 }
305 
306 
307 /*
308  * Log errors and other messages to the system log daemon and to stderr,
309  * according to the severity of the message and the current debug level.
310  * For errors of severity LOG_ERR or worse, terminate the program.
311  */
312 void log(severity, syserr, format, a, b, c, d, e)
313     int severity, syserr;
314     char *format;
315     int a, b, c, d, e;
316 {
317     char fmt[100];
318 
319     switch (debug) {
320 	case 0: break;
321 	case 1: if (severity > LOG_NOTICE) break;
322 	case 2: if (severity > LOG_INFO  ) break;
323 	default:
324 	    fmt[0] = '\0';
325 	    if (severity == LOG_WARNING) strcat(fmt, "warning - ");
326 	    strncat(fmt, format, 80);
327 	    fprintf(stderr, fmt, a, b, c, d, e);
328 	    if (syserr == 0)
329 		fprintf(stderr, "\n");
330 	    else if(syserr < sys_nerr)
331 		fprintf(stderr, ": %s\n", sys_errlist[syserr]);
332 	    else
333 		fprintf(stderr, ": errno %d\n", syserr);
334     }
335 
336     if (severity <= LOG_NOTICE) {
337 	fmt[0] = '\0';
338 	if (severity == LOG_WARNING) strcat(fmt, "warning - ");
339 	strncat(fmt, format, 80);
340 	if (syserr != 0) {
341 		strcat(fmt, ": %m");
342 		errno = syserr;
343 	}
344 	syslog(severity, fmt, a, b, c, d, e);
345 
346 	if (severity <= LOG_ERR) exit(-1);
347     }
348 }
349