main.c revision 1.2 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