1 /* $NetBSD: main.c,v 1.22 2006/05/09 20:18:09 mrg Exp $ */
4 * The mrouted program is covered by the license in the accompanying file
5 * named "LICENSE". Use of the mrouted program represents acceptance of
6 * the terms and conditions listed in that file.
8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9 * Leland Stanford Junior University.
13 * Written by Steve Deering, Stanford University, February 1989.
15 * (An earlier version of DVMRP was implemented by David Waitzman of
16 * BBN STC by extending Berkeley's routed program. Some of Waitzman's
17 * extensions have been incorporated into mrouted, but none of the
18 * original routed code has been adopted.)
30 #include <sys/cdefs.h>
32 __RCSID("@(#) $NetBSD: main.c,v 1.22 2006/05/09 20:18:09 mrg Exp $");
39 extern char *configfilename
;
40 char versionstring
[100];
42 static char dumpfilename
[] = _PATH_MROUTED_DUMP
;
43 static char cachefilename
[] = _PATH_MROUTED_CACHE
;
44 static char genidfilename
[] = _PATH_MROUTED_GENID
;
46 int cache_lifetime
= DEFAULT_CACHE_LIFETIME
;
47 int max_prune_lifetime
= DEFAULT_CACHE_LIFETIME
* 2;
50 u_char pruning
= 1; /* Enable pruning by default */
58 static struct ihandler
{
59 int fd
; /* File descriptor */
60 ihfunc_t func
; /* Function to call with &fd_set */
61 } ihandlers
[NHANDLERS
];
62 static int nhandlers
= 0;
65 * Forward declarations.
67 static void fasttimer(int);
68 static void done(int);
69 static void dump(int);
70 static void fdump(int);
71 static void cdump(int);
72 static void restart(int);
73 static void timer(void);
74 static void cleanup(void);
75 static void resetlogging(void *);
77 /* To shut up gcc -Wstrict-prototypes */
78 int main(int argc
, char *argv
[]);
79 void logit(int severity
, int syserr
, const char *format
, ...)
80 __attribute__((__format__(__printf__
, 3, 4)));
83 register_input_handler(int fd
, ihfunc_t func
)
85 if (nhandlers
>= NHANDLERS
)
88 ihandlers
[nhandlers
].fd
= fd
;
89 ihandlers
[nhandlers
++].func
= func
;
95 main(int argc
, char *argv
[])
102 u_int32_t prev_genid
;
104 fd_set rfds
, readers
;
107 struct timeval timeout
, *tvp
= &timeout
;
108 struct timeval sched
, *svp
= &sched
, now
, *nvp
= &now
;
114 if (geteuid() != 0) {
115 fprintf(stderr
, "must be root\n");
120 while (argc
> 0 && *argv
[0] == '-') {
121 if (strcmp(*argv
, "-d") == 0) {
122 if (argc
> 1 && isdigit((unsigned char)*(argv
+ 1)[0])) {
126 debug
= DEFAULT_DEBUG
;
127 } else if (strcmp(*argv
, "-c") == 0) {
130 configfilename
= *argv
;
133 } else if (strcmp(*argv
, "-p") == 0) {
136 } else if (strcmp(*argv
, "-P") == 0) {
137 if (argc
> 1 && isdigit(*(argv
+ 1)[0])) {
139 dest_port
= atoi(*argv
);
141 dest_port
= DEFAULT_PORT
;
149 usage
: fprintf(stderr
,
150 "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
156 * Detach from the terminal
159 err(1, "can't fork");
163 fprintf(stderr
, "debug level %u\n", debug
);
166 (void)openlog("mrouted", LOG_PID
, LOG_DAEMON
);
167 (void)setlogmask(LOG_UPTO(LOG_NOTICE
));
169 (void)openlog("mrouted", LOG_PID
);
171 snprintf(versionstring
, sizeof(versionstring
),
172 "mrouted version %d.%d", PROTOCOL_VERSION
, MROUTED_VERSION
);
174 logit(LOG_NOTICE
, 0, "%s", versionstring
);
179 gettimeofday(&tv
, 0);
180 dvmrp_genid
= tv
.tv_sec
;
182 fp
= fopen(genidfilename
, "r");
184 fscanf(fp
, "%d", &prev_genid
);
185 if (prev_genid
== dvmrp_genid
)
190 fp
= fopen(genidfilename
, "w");
192 fprintf(fp
, "%d", dvmrp_genid
);
200 k_init_dvmrp(); /* enable DVMRP routing in kernel */
203 vers
= k_get_version();
205 * This function must change whenever the kernel version changes
207 if ((((vers
>> 8) & 0xff) != 3) ||
208 ((vers
& 0xff) != 5))
209 logit(LOG_ERR
, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch",
210 (vers
>> 8) & 0xff, vers
& 0xff,
211 PROTOCOL_VERSION
, MROUTED_VERSION
);
218 gettimeofday(nvp
, 0);
219 if (nvp
->tv_usec
< 500000L){
220 svp
->tv_usec
= nvp
->tv_usec
+ 500000L;
221 svp
->tv_sec
= nvp
->tv_sec
;
223 svp
->tv_usec
= nvp
->tv_usec
- 500000L;
224 svp
->tv_sec
= nvp
->tv_sec
+ 1;
235 * Allow cleanup if unexpected exit. Apparently some architectures
236 * have a kernel bug where closing the socket doesn't do an
237 * ip_mrouter_done(), so we attempt to do it on exit.
242 fprintf(stderr
, "pruning %s\n", pruning
? "on" : "off");
244 (void)signal(SIGALRM
, fasttimer
);
246 (void)signal(SIGHUP
, restart
);
247 (void)signal(SIGTERM
, done
);
248 (void)signal(SIGINT
, done
);
249 (void)signal(SIGUSR1
, fdump
);
250 (void)signal(SIGUSR2
, cdump
);
252 (void)signal(SIGQUIT
, dump
);
255 if (igmp_socket
>= FD_SETSIZE
)
256 logit(LOG_ERR
, 0, "descriptor too big");
257 FD_SET(igmp_socket
, &readers
);
258 nfds
= igmp_socket
+ 1;
259 for (i
= 0; i
< nhandlers
; i
++) {
260 if (ihandlers
[i
].fd
>= FD_SETSIZE
)
261 logit(LOG_ERR
, 0, "descriptor too big");
262 FD_SET(ihandlers
[i
].fd
, &readers
);
263 if (ihandlers
[i
].fd
>= nfds
)
264 nfds
= ihandlers
[i
].fd
+ 1;
268 * Install the vifs in the kernel as late as possible in the
269 * initialization sequence.
273 if (debug
>= 2) dump(0);
275 /* Start up the log rate-limiter */
278 (void)alarm(1); /* schedule first timer interrupt */
285 sigset_t block
, oblock
;
287 bcopy((char *)&readers
, (char *)&rfds
, sizeof(rfds
));
289 gettimeofday(nvp
, 0);
290 if (nvp
->tv_sec
> svp
->tv_sec
291 || (nvp
->tv_sec
== svp
->tv_sec
&& nvp
->tv_usec
> svp
->tv_usec
)){
294 if (nvp
->tv_usec
< 500000L){
295 svp
->tv_usec
= nvp
->tv_usec
+ 500000L;
296 svp
->tv_sec
= nvp
->tv_sec
;
298 svp
->tv_usec
= nvp
->tv_usec
- 500000L;
299 svp
->tv_sec
= nvp
->tv_sec
+ 1;
305 tvp
->tv_usec
= 500000L;
308 snmp_select_info(&nfds
, &rfds
, tvp
, &block
);
310 tvp
= NULL
; /* block without timeout */
311 if ((n
= select(nfds
, &rfds
, NULL
, NULL
, tvp
)) < 0)
313 if ((n
= select(nfds
, &rfds
, NULL
, NULL
, NULL
)) < 0)
316 if (errno
!= EINTR
) /* SIGALRM is expected */
317 logit(LOG_WARNING
, errno
, "select failed");
321 if (FD_ISSET(igmp_socket
, &rfds
)) {
322 recvlen
= recvfrom(igmp_socket
, recv_buf
, RECV_BUF_SIZE
,
325 if (errno
!= EINTR
) logit(LOG_ERR
, errno
, "recvfrom");
329 (void)sigemptyset(&block
);
330 (void)sigaddset(&block
, SIGALRM
);
331 if (sigprocmask(SIG_BLOCK
, &block
, &oblock
) < 0)
332 logit(LOG_ERR
, errno
, "sigprocmask");
334 omask
= sigblock(sigmask(SIGALRM
));
336 accept_igmp(recvlen
);
338 (void)sigprocmask(SIG_SETMASK
, &oblock
, (sigset_t
*)NULL
);
340 (void)sigsetmask(omask
);
344 for (i
= 0; i
< nhandlers
; i
++) {
345 if (FD_ISSET(ihandlers
[i
].fd
, &rfds
)) {
346 (*ihandlers
[i
].func
)(ihandlers
[i
].fd
, &rfds
);
352 snmp_timeout(); /* poll */
359 * routine invoked every second. Its main goal is to cycle through
360 * the routing table and send partial updates to all neighbors at a
361 * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
362 * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to
363 * do all the other time-based processing.
368 static unsigned int tlast
;
369 static unsigned int nsent
;
370 unsigned int t
= tlast
+ 1;
374 * if we're in the last second, send everything that's left.
375 * otherwise send at least the fraction we should have sent by now.
377 if (t
>= ROUTE_REPORT_INTERVAL
) {
378 int nleft
= nroutes
- nsent
;
380 if ((n
= report_next_chunk()) <= 0)
387 unsigned int ncum
= nroutes
* t
/ ROUTE_REPORT_INTERVAL
;
388 while (nsent
< ncum
) {
389 if ((n
= report_next_chunk()) <= 0)
395 if ((t
% TIMER_INTERVAL
) == 0)
398 age_callout_queue();/* Advance the timer for the callout queue
404 * The 'virtual_time' variable is initialized to a value that will cause the
405 * first invocation of timer() to send a probe or route report to all vifs
406 * and send group membership queries to all subnets for which this router is
407 * querier. This first invocation occurs approximately TIMER_INTERVAL seconds
408 * after the router starts up. Note that probes for neighbors and queries
409 * for group memberships are also sent at start-up time, as part of initial-
410 * ization. This repetition after a short interval is desirable for quickly
411 * building up topology and membership information in the presence of possible
414 * 'virtual_time' advances at a rate that is only a crude approximation of
415 * real time, because it does not take into account any time spent processing,
416 * and because the timer intervals are sometimes shrunk by a random amount to
417 * avoid unwanted synchronization with other routers.
420 static u_long virtual_time
= 0;
424 * Timer routine. Performs periodic neighbor probing, route reporting, and
425 * group querying duties, and drives various timers in routing entries and
426 * virtual interface data structures.
431 age_routes(); /* Advance the timers in the route entries */
432 age_vifs(); /* Advance the timers for neighbors */
433 age_table_entry(); /* Advance the timers for the cache entries */
435 if (virtual_time
% GROUP_QUERY_INTERVAL
== 0) {
437 * Time to query the local group memberships on all subnets
438 * for which this router is the elected querier.
443 if (virtual_time
% NEIGHBOR_PROBE_INTERVAL
== 0) {
445 * Time to send a probe on all vifs from which no neighbors have
446 * been heard. Also, check if any inoperative interfaces have now
447 * come up. (If they have, they will also be probed as part of
448 * their initialization.)
450 probe_for_neighbors();
456 delay_change_reports
= FALSE
;
457 if (routes_changed
) {
459 * Some routes have changed since the last timer interrupt, but
460 * have not been reported yet. Report the changed routes to all
463 report_to_all_neighbors(CHANGED_ROUTES
);
471 * Advance virtual time
473 virtual_time
+= TIMER_INTERVAL
;
478 * On termination, let everyone know we're going away.
483 logit(LOG_NOTICE
, 0, "%s exiting", versionstring
);
491 static int in_cleanup
= 0;
499 report_to_all_neighbors(ALL_ROUTES
);
506 * Dump internal data structures to stderr.
517 * Dump internal data structures to a file.
524 fp
= fopen(dumpfilename
, "w");
534 * Dump local cache contents to a file.
541 fp
= fopen(cachefilename
, "w");
557 sigset_t block
, oblock
;
560 logit(LOG_NOTICE
, 0, "%s restart", versionstring
);
563 * reset all the entries
566 (void)sigemptyset(&block
);
567 (void)sigaddset(&block
, SIGALRM
);
568 if (sigprocmask(SIG_BLOCK
, &block
, &oblock
) < 0)
569 logit(LOG_ERR
, errno
, "sigprocmask");
571 omask
= sigblock(sigmask(SIGALRM
));
581 * start processing again
590 k_init_dvmrp(); /* enable DVMRP routing in kernel */
594 (void)sigprocmask(SIG_SETMASK
, &oblock
, (sigset_t
*)NULL
);
596 (void)sigsetmask(omask
);
600 #define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
601 #define LOG_SHUT_UP 600 /* shut up for 10 minutes */
602 static int log_nmsgs
= 0;
605 resetlogging(void *arg
)
610 if (arg
== NULL
&& log_nmsgs
> LOG_MAX_MSGS
) {
611 nxttime
= LOG_SHUT_UP
;
612 narg
= (void *)&log_nmsgs
; /* just need some valid void * */
613 syslog(LOG_WARNING
, "logging too fast, shutting up for %d minutes",
619 timer_setTimer(nxttime
, resetlogging
, narg
);
623 * Log errors and other messages to the system log daemon and to stderr,
624 * according to the severity of the message and the current debug level.
625 * For errors of severity LOG_ERR or worse, terminate the program.
628 logit(int severity
, int syserr
, const char *format
, ...)
631 static char fmt
[211] = "warning - ";
638 va_start(ap
, format
);
639 vsnprintf(&fmt
[10], sizeof(fmt
) - 10, format
, ap
);
641 msg
= (severity
== LOG_WARNING
) ? fmt
: &fmt
[10];
645 case 1: if (severity
> LOG_NOTICE
) break;
646 case 2: if (severity
> LOG_INFO
) break;
648 gettimeofday(&now
,NULL
);
650 thyme
= localtime(&t
);
651 strftime(tbuf
, sizeof(tbuf
), "%X", thyme
);
652 fprintf(stderr
, "%s.%03ld %s", tbuf
, (long)now
.tv_usec
/ 1000,
655 fprintf(stderr
, "\n");
657 fprintf(stderr
, ": %s\n", strerror(syserr
));
660 if (severity
<= LOG_NOTICE
) {
661 if (log_nmsgs
++ < LOG_MAX_MSGS
) {
664 syslog(severity
, "%s: %m", msg
);
666 syslog(severity
, "%s", msg
);
669 if (severity
<= LOG_ERR
) exit(1);
675 md_log(int what
, u_int32_t origin
, u_int32_t mcastgrp
)
677 static FILE *f
= NULL
;
682 if ((f
= fopen("/tmp/mrouted.clog", "w")) == NULL
) {
683 logit(LOG_ERR
, errno
, "open /tmp/mrouted.clog");
687 gettimeofday(&tv
, NULL
);
693 fwrite(buf
, sizeof(u_int32_t
), 4, f
);