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