1 1.43 andvar /* $NetBSD: main.c,v 1.43 2022/04/07 19:33:37 andvar Exp $ */ 2 1.7 cgd 3 1.1 cgd /* 4 1.5 mycroft * Copyright (c) 1983, 1988, 1993 5 1.5 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.1 cgd * 3. All advertising materials mentioning features or use of this software 16 1.20 christos * must display the following acknowledgment: 17 1.1 cgd * This product includes software developed by the University of 18 1.1 cgd * California, Berkeley and its contributors. 19 1.1 cgd * 4. Neither the name of the University nor the names of its contributors 20 1.1 cgd * may be used to endorse or promote products derived from this software 21 1.1 cgd * without specific prior written permission. 22 1.1 cgd * 23 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 1.1 cgd * SUCH DAMAGE. 34 1.1 cgd */ 35 1.1 cgd 36 1.1 cgd #include "defs.h" 37 1.14 thorpej #include "pathnames.h" 38 1.41 christos #if defined(__NetBSD__) 39 1.41 christos #include <util.h> 40 1.41 christos #endif 41 1.14 thorpej #include <signal.h> 42 1.14 thorpej #include <fcntl.h> 43 1.1 cgd #include <sys/file.h> 44 1.1 cgd 45 1.40 lukem __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\ 46 1.40 lukem The Regents of the University of California. All rights reserved."); 47 1.28 christos #ifdef __NetBSD__ 48 1.43 andvar __RCSID("$NetBSD: main.c,v 1.43 2022/04/07 19:33:37 andvar Exp $"); 49 1.28 christos #elif defined(__FreeBSD__) 50 1.28 christos __RCSID("$FreeBSD$"); 51 1.28 christos #else 52 1.31 christos __RCSID("Revision: 2.27 "); 53 1.31 christos #ident "Revision: 2.27 " 54 1.28 christos #endif 55 1.28 christos 56 1.14 thorpej pid_t mypid; 57 1.14 thorpej 58 1.14 thorpej naddr myaddr; /* system address */ 59 1.14 thorpej char myname[MAXHOSTNAMELEN+1]; 60 1.1 cgd 61 1.18 thorpej int verbose; 62 1.18 thorpej 63 1.14 thorpej int supplier; /* supply or broadcast updates */ 64 1.14 thorpej int supplier_set; 65 1.14 thorpej int ipforwarding = 1; /* kernel forwarding on */ 66 1.14 thorpej 67 1.14 thorpej int default_gateway; /* 1=advertise default */ 68 1.14 thorpej int background = 1; 69 1.14 thorpej int ridhosts; /* 1=reduce host routes */ 70 1.14 thorpej int mhome; /* 1=want multi-homed host route */ 71 1.20 christos int advertise_mhome; /* 1=must continue advertising it */ 72 1.14 thorpej int auth_ok = 1; /* 1=ignore auth if we do not care */ 73 1.14 thorpej 74 1.14 thorpej struct timeval epoch; /* when started */ 75 1.14 thorpej struct timeval clk, prev_clk; 76 1.24 christos static int usec_fudge; 77 1.14 thorpej struct timeval now; /* current idea of time */ 78 1.14 thorpej time_t now_stale; 79 1.15 christos time_t now_expire; 80 1.14 thorpej time_t now_garbage; 81 1.14 thorpej 82 1.14 thorpej struct timeval next_bcast; /* next general broadcast */ 83 1.28 christos struct timeval no_flash = { /* inhibit flash update */ 84 1.28 christos EPOCH+SUPPLY_INTERVAL, 0 85 1.28 christos }; 86 1.14 thorpej 87 1.18 thorpej struct timeval flush_kern_timer; 88 1.18 thorpej 89 1.33 itojun fd_set *fdbitsp; 90 1.14 thorpej int sock_max; 91 1.14 thorpej int rip_sock = -1; /* RIP socket */ 92 1.14 thorpej struct interface *rip_sock_mcast; /* current multicast interface */ 93 1.14 thorpej int rt_sock; /* routing socket */ 94 1.14 thorpej int rt_sock_seqno; 95 1.1 cgd 96 1.1 cgd 97 1.14 thorpej static int get_rip_sock(naddr, int); 98 1.14 thorpej static void timevalsub(struct timeval *, struct timeval *, struct timeval *); 99 1.1 cgd 100 1.6 cgd int 101 1.14 thorpej main(int argc, 102 1.14 thorpej char *argv[]) 103 1.14 thorpej { 104 1.14 thorpej int n, mib[4], off; 105 1.14 thorpej size_t len; 106 1.14 thorpej char *p, *q; 107 1.21 christos const char *cp; 108 1.14 thorpej struct timeval wtime, t2; 109 1.14 thorpej time_t dt; 110 1.35 enami fd_set *ibitsp = NULL; 111 1.16 christos naddr p_net, p_mask; 112 1.14 thorpej struct interface *ifp; 113 1.14 thorpej struct parm parm; 114 1.14 thorpej char *tracename = 0; 115 1.14 thorpej 116 1.14 thorpej 117 1.16 christos /* Some shells are badly broken and send SIGHUP to backgrounded 118 1.16 christos * processes. 119 1.16 christos */ 120 1.16 christos signal(SIGHUP, SIG_IGN); 121 1.16 christos 122 1.26 lukem openlog("routed", LOG_PID, LOG_DAEMON); 123 1.14 thorpej ftrace = stdout; 124 1.14 thorpej 125 1.14 thorpej gettimeofday(&clk, 0); 126 1.14 thorpej prev_clk = clk; 127 1.14 thorpej epoch = clk; 128 1.14 thorpej epoch.tv_sec -= EPOCH; 129 1.14 thorpej now.tv_sec = EPOCH; 130 1.14 thorpej now_stale = EPOCH - STALE_TIME; 131 1.15 christos now_expire = EPOCH - EXPIRE_TIME; 132 1.14 thorpej now_garbage = EPOCH - GARBAGE_TIME; 133 1.14 thorpej wtime.tv_sec = 0; 134 1.14 thorpej 135 1.24 christos (void)gethostname(myname, sizeof(myname) - 1); 136 1.14 thorpej (void)gethost(myname, &myaddr); 137 1.14 thorpej 138 1.37 wiz while ((n = getopt(argc, argv, "sqdghmAtvT:F:P:")) != -1) { 139 1.14 thorpej switch (n) { 140 1.14 thorpej case 's': 141 1.14 thorpej supplier = 1; 142 1.14 thorpej supplier_set = 1; 143 1.14 thorpej break; 144 1.14 thorpej 145 1.14 thorpej case 'q': 146 1.14 thorpej supplier = 0; 147 1.14 thorpej supplier_set = 1; 148 1.14 thorpej break; 149 1.14 thorpej 150 1.14 thorpej case 'd': 151 1.14 thorpej background = 0; 152 1.14 thorpej break; 153 1.14 thorpej 154 1.14 thorpej case 'g': 155 1.17 lukem memset(&parm, 0, sizeof(parm)); 156 1.14 thorpej parm.parm_d_metric = 1; 157 1.21 christos cp = check_parms(&parm); 158 1.21 christos if (cp != 0) 159 1.21 christos msglog("bad -g: %s", cp); 160 1.14 thorpej else 161 1.14 thorpej default_gateway = 1; 162 1.14 thorpej break; 163 1.14 thorpej 164 1.14 thorpej case 'h': /* suppress extra host routes */ 165 1.14 thorpej ridhosts = 1; 166 1.14 thorpej break; 167 1.14 thorpej 168 1.14 thorpej case 'm': /* advertise host route */ 169 1.14 thorpej mhome = 1; /* on multi-homed hosts */ 170 1.14 thorpej break; 171 1.14 thorpej 172 1.14 thorpej case 'A': 173 1.14 thorpej /* Ignore authentication if we do not care. 174 1.14 thorpej * Crazy as it is, that is what RFC 1723 requires. 175 1.14 thorpej */ 176 1.14 thorpej auth_ok = 0; 177 1.14 thorpej break; 178 1.14 thorpej 179 1.14 thorpej case 't': 180 1.14 thorpej new_tracelevel++; 181 1.14 thorpej break; 182 1.14 thorpej 183 1.14 thorpej case 'T': 184 1.14 thorpej tracename = optarg; 185 1.14 thorpej break; 186 1.14 thorpej 187 1.14 thorpej case 'F': /* minimal routes for SLIP */ 188 1.16 christos n = FAKE_METRIC; 189 1.14 thorpej p = strchr(optarg,','); 190 1.14 thorpej if (p && *p != '\0') { 191 1.14 thorpej n = (int)strtoul(p+1, &q, 0); 192 1.14 thorpej if (*q == '\0' 193 1.14 thorpej && n <= HOPCNT_INFINITY-1 194 1.14 thorpej && n >= 1) 195 1.14 thorpej *p = '\0'; 196 1.14 thorpej } 197 1.16 christos if (!getnet(optarg, &p_net, &p_mask)) { 198 1.14 thorpej msglog("bad network; \"-F %s\"", 199 1.14 thorpej optarg); 200 1.14 thorpej break; 201 1.14 thorpej } 202 1.17 lukem memset(&parm, 0, sizeof(parm)); 203 1.16 christos parm.parm_net = p_net; 204 1.14 thorpej parm.parm_mask = p_mask; 205 1.14 thorpej parm.parm_d_metric = n; 206 1.21 christos cp = check_parms(&parm); 207 1.21 christos if (cp != 0) 208 1.21 christos msglog("bad -F: %s", cp); 209 1.14 thorpej break; 210 1.14 thorpej 211 1.14 thorpej case 'P': 212 1.18 thorpej /* handle arbitrary parameters. 213 1.14 thorpej */ 214 1.18 thorpej q = strdup(optarg); 215 1.21 christos cp = parse_parms(q, 0); 216 1.21 christos if (cp != 0) 217 1.21 christos msglog("%s in \"-P %s\"", cp, optarg); 218 1.18 thorpej free(q); 219 1.18 thorpej break; 220 1.18 thorpej 221 1.18 thorpej case 'v': 222 1.18 thorpej /* display version */ 223 1.18 thorpej verbose++; 224 1.41 christos msglog("version 2.32"); 225 1.14 thorpej break; 226 1.14 thorpej 227 1.14 thorpej default: 228 1.14 thorpej goto usage; 229 1.14 thorpej } 230 1.14 thorpej } 231 1.14 thorpej argc -= optind; 232 1.14 thorpej argv += optind; 233 1.14 thorpej 234 1.14 thorpej if (tracename == 0 && argc >= 1) { 235 1.14 thorpej tracename = *argv++; 236 1.14 thorpej argc--; 237 1.14 thorpej } 238 1.16 christos if (tracename != 0 && tracename[0] == '\0') 239 1.16 christos goto usage; 240 1.14 thorpej if (argc != 0) { 241 1.14 thorpej usage: 242 1.38 wiz logbad(0, "usage: routed [-sqdghmAtv] [-T tracefile]" 243 1.39 wiz " [-F net[/mask[,metric]]] [-P parms]"); 244 1.14 thorpej } 245 1.18 thorpej if (geteuid() != 0) { 246 1.18 thorpej if (verbose) 247 1.18 thorpej exit(0); 248 1.14 thorpej logbad(0, "requires UID 0"); 249 1.18 thorpej } 250 1.14 thorpej 251 1.14 thorpej mib[0] = CTL_NET; 252 1.14 thorpej mib[1] = PF_INET; 253 1.14 thorpej mib[2] = IPPROTO_IP; 254 1.14 thorpej mib[3] = IPCTL_FORWARDING; 255 1.14 thorpej len = sizeof(ipforwarding); 256 1.14 thorpej if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) 257 1.14 thorpej LOGERR("sysctl(IPCTL_FORWARDING)"); 258 1.14 thorpej 259 1.14 thorpej if (!ipforwarding) { 260 1.14 thorpej if (supplier) 261 1.14 thorpej msglog("-s incompatible with ipforwarding=0"); 262 1.14 thorpej if (default_gateway) { 263 1.14 thorpej msglog("-g incompatible with ipforwarding=0"); 264 1.14 thorpej default_gateway = 0; 265 1.14 thorpej } 266 1.14 thorpej supplier = 0; 267 1.14 thorpej supplier_set = 1; 268 1.14 thorpej } 269 1.14 thorpej if (default_gateway) { 270 1.14 thorpej if (supplier_set && !supplier) { 271 1.14 thorpej msglog("-g and -q incompatible"); 272 1.14 thorpej } else { 273 1.14 thorpej supplier = 1; 274 1.14 thorpej supplier_set = 1; 275 1.14 thorpej } 276 1.14 thorpej } 277 1.14 thorpej 278 1.14 thorpej 279 1.15 christos signal(SIGALRM, sigalrm); 280 1.15 christos if (!background) 281 1.15 christos signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ 282 1.15 christos signal(SIGTERM, sigterm); 283 1.15 christos signal(SIGINT, sigterm); 284 1.15 christos signal(SIGUSR1, sigtrace_on); 285 1.15 christos signal(SIGUSR2, sigtrace_off); 286 1.15 christos 287 1.14 thorpej /* get into the background */ 288 1.18 thorpej if (background && daemon(0, 1) < 0) 289 1.16 christos BADERR(0,"daemon()"); 290 1.14 thorpej 291 1.23 thorpej #if defined(__NetBSD__) 292 1.23 thorpej pidfile(NULL); 293 1.23 thorpej #endif 294 1.14 thorpej mypid = getpid(); 295 1.14 thorpej 296 1.14 thorpej /* prepare socket connected to the kernel. 297 1.14 thorpej */ 298 1.21 christos rt_sock = socket(AF_ROUTE, SOCK_RAW, 0); 299 1.14 thorpej if (rt_sock < 0) 300 1.14 thorpej BADERR(1,"rt_sock = socket()"); 301 1.14 thorpej if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1) 302 1.14 thorpej logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno)); 303 1.14 thorpej off = 0; 304 1.14 thorpej if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, 305 1.14 thorpej &off,sizeof(off)) < 0) 306 1.14 thorpej LOGERR("setsockopt(SO_USELOOPBACK,0)"); 307 1.14 thorpej 308 1.14 thorpej fix_select(); 309 1.14 thorpej 310 1.14 thorpej 311 1.14 thorpej if (tracename != 0) { 312 1.32 itojun strlcpy(inittracename, tracename, sizeof(inittracename)); 313 1.16 christos set_tracefile(inittracename, "%s", -1); 314 1.16 christos } else { 315 1.16 christos tracelevel_msg("%s", -1); /* turn on tracing to stdio */ 316 1.14 thorpej } 317 1.16 christos 318 1.16 christos bufinit(); 319 1.14 thorpej 320 1.14 thorpej /* initialize radix tree */ 321 1.14 thorpej rtinit(); 322 1.14 thorpej 323 1.14 thorpej /* Pick a random part of the second for our output to minimize 324 1.14 thorpej * collisions. 325 1.14 thorpej * 326 1.14 thorpej * Start broadcasting after hearing from other routers, and 327 1.14 thorpej * at a random time so a bunch of systems do not get synchronized 328 1.14 thorpej * after a power failure. 329 1.14 thorpej */ 330 1.14 thorpej intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); 331 1.14 thorpej age_timer.tv_usec = next_bcast.tv_usec; 332 1.14 thorpej age_timer.tv_sec = EPOCH+MIN_WAITTIME; 333 1.14 thorpej rdisc_timer = next_bcast; 334 1.14 thorpej ifinit_timer.tv_usec = next_bcast.tv_usec; 335 1.14 thorpej 336 1.14 thorpej /* Collect an initial view of the world by checking the interface 337 1.14 thorpej * configuration and the kludge file. 338 1.14 thorpej */ 339 1.14 thorpej gwkludge(); 340 1.14 thorpej ifinit(); 341 1.14 thorpej 342 1.14 thorpej /* Ask for routes */ 343 1.14 thorpej rip_query(); 344 1.16 christos rdisc_sol(); 345 1.14 thorpej 346 1.18 thorpej /* Now turn off stdio if not tracing */ 347 1.18 thorpej if (new_tracelevel == 0) 348 1.18 thorpej trace_close(background); 349 1.18 thorpej 350 1.14 thorpej /* Loop forever, listening and broadcasting. 351 1.14 thorpej */ 352 1.14 thorpej for (;;) { 353 1.14 thorpej prev_clk = clk; 354 1.14 thorpej gettimeofday(&clk, 0); 355 1.24 christos if (prev_clk.tv_sec == clk.tv_sec 356 1.24 christos && prev_clk.tv_usec == clk.tv_usec+usec_fudge) { 357 1.24 christos /* Much of `routed` depends on time always advancing. 358 1.24 christos * On systems that do not guarantee that gettimeofday() 359 1.24 christos * produces unique timestamps even if called within 360 1.24 christos * a single tick, use trickery like that in classic 361 1.24 christos * BSD kernels. 362 1.14 thorpej */ 363 1.24 christos clk.tv_usec += ++usec_fudge; 364 1.24 christos 365 1.24 christos } else { 366 1.24 christos usec_fudge = 0; 367 1.24 christos 368 1.24 christos timevalsub(&t2, &clk, &prev_clk); 369 1.24 christos if (t2.tv_sec < 0 370 1.24 christos || t2.tv_sec > wtime.tv_sec + 5) { 371 1.24 christos /* Deal with time changes before other 372 1.24 christos * housekeeping to keep everything straight. 373 1.24 christos */ 374 1.24 christos dt = t2.tv_sec; 375 1.24 christos if (dt > 0) 376 1.24 christos dt -= wtime.tv_sec; 377 1.24 christos trace_act("time changed by %d sec", (int)dt); 378 1.24 christos epoch.tv_sec += dt; 379 1.24 christos } 380 1.14 thorpej } 381 1.14 thorpej timevalsub(&now, &clk, &epoch); 382 1.14 thorpej now_stale = now.tv_sec - STALE_TIME; 383 1.15 christos now_expire = now.tv_sec - EXPIRE_TIME; 384 1.14 thorpej now_garbage = now.tv_sec - GARBAGE_TIME; 385 1.14 thorpej 386 1.16 christos /* deal with signals that should affect tracing */ 387 1.14 thorpej set_tracelevel(); 388 1.14 thorpej 389 1.14 thorpej if (stopint != 0) { 390 1.16 christos rip_bcast(0); 391 1.16 christos rdisc_adv(); 392 1.16 christos trace_off("exiting with signal %d", stopint); 393 1.14 thorpej exit(stopint | 128); 394 1.14 thorpej } 395 1.14 thorpej 396 1.14 thorpej /* look for new or dead interfaces */ 397 1.14 thorpej timevalsub(&wtime, &ifinit_timer, &now); 398 1.14 thorpej if (wtime.tv_sec <= 0) { 399 1.14 thorpej wtime.tv_sec = 0; 400 1.14 thorpej ifinit(); 401 1.14 thorpej rip_query(); 402 1.1 cgd continue; 403 1.1 cgd } 404 1.14 thorpej 405 1.43 andvar /* Check the kernel table occasionally for mysteriously 406 1.18 thorpej * evaporated routes 407 1.18 thorpej */ 408 1.18 thorpej timevalsub(&t2, &flush_kern_timer, &now); 409 1.18 thorpej if (t2.tv_sec <= 0) { 410 1.18 thorpej flush_kern(); 411 1.18 thorpej flush_kern_timer.tv_sec = (now.tv_sec 412 1.18 thorpej + CHECK_QUIET_INTERVAL); 413 1.18 thorpej continue; 414 1.18 thorpej } 415 1.18 thorpej if (timercmp(&t2, &wtime, <)) 416 1.18 thorpej wtime = t2; 417 1.18 thorpej 418 1.14 thorpej /* If it is time, then broadcast our routes. 419 1.14 thorpej */ 420 1.14 thorpej if (supplier || advertise_mhome) { 421 1.14 thorpej timevalsub(&t2, &next_bcast, &now); 422 1.14 thorpej if (t2.tv_sec <= 0) { 423 1.14 thorpej /* Synchronize the aging and broadcast 424 1.14 thorpej * timers to minimize awakenings 425 1.14 thorpej */ 426 1.14 thorpej age(0); 427 1.14 thorpej 428 1.14 thorpej rip_bcast(0); 429 1.14 thorpej 430 1.14 thorpej /* It is desirable to send routing updates 431 1.14 thorpej * regularly. So schedule the next update 432 1.14 thorpej * 30 seconds after the previous one was 433 1.20 christos * scheduled, instead of 30 seconds after 434 1.14 thorpej * the previous update was finished. 435 1.14 thorpej * Even if we just started after discovering 436 1.14 thorpej * a 2nd interface or were otherwise delayed, 437 1.14 thorpej * pick a 30-second aniversary of the 438 1.14 thorpej * original broadcast time. 439 1.14 thorpej */ 440 1.14 thorpej n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL; 441 1.14 thorpej next_bcast.tv_sec += n*SUPPLY_INTERVAL; 442 1.14 thorpej 443 1.14 thorpej continue; 444 1.14 thorpej } 445 1.14 thorpej 446 1.14 thorpej if (timercmp(&t2, &wtime, <)) 447 1.14 thorpej wtime = t2; 448 1.14 thorpej } 449 1.14 thorpej 450 1.14 thorpej /* If we need a flash update, either do it now or 451 1.14 thorpej * set the delay to end when it is time. 452 1.14 thorpej * 453 1.14 thorpej * If we are within MIN_WAITTIME seconds of a full update, 454 1.14 thorpej * do not bother. 455 1.14 thorpej */ 456 1.14 thorpej if (need_flash 457 1.14 thorpej && supplier 458 1.14 thorpej && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { 459 1.14 thorpej /* accurate to the millisecond */ 460 1.14 thorpej if (!timercmp(&no_flash, &now, >)) 461 1.14 thorpej rip_bcast(1); 462 1.14 thorpej timevalsub(&t2, &no_flash, &now); 463 1.14 thorpej if (timercmp(&t2, &wtime, <)) 464 1.14 thorpej wtime = t2; 465 1.14 thorpej } 466 1.14 thorpej 467 1.14 thorpej /* trigger the main aging timer. 468 1.14 thorpej */ 469 1.14 thorpej timevalsub(&t2, &age_timer, &now); 470 1.14 thorpej if (t2.tv_sec <= 0) { 471 1.14 thorpej age(0); 472 1.1 cgd continue; 473 1.1 cgd } 474 1.14 thorpej if (timercmp(&t2, &wtime, <)) 475 1.14 thorpej wtime = t2; 476 1.14 thorpej 477 1.14 thorpej /* update the kernel routing table 478 1.14 thorpej */ 479 1.14 thorpej timevalsub(&t2, &need_kern, &now); 480 1.14 thorpej if (t2.tv_sec <= 0) { 481 1.14 thorpej age(0); 482 1.1 cgd continue; 483 1.1 cgd } 484 1.14 thorpej if (timercmp(&t2, &wtime, <)) 485 1.14 thorpej wtime = t2; 486 1.14 thorpej 487 1.14 thorpej /* take care of router discovery, 488 1.18 thorpej * but do it in the correct the millisecond 489 1.14 thorpej */ 490 1.14 thorpej if (!timercmp(&rdisc_timer, &now, >)) { 491 1.14 thorpej rdisc_age(0); 492 1.1 cgd continue; 493 1.1 cgd } 494 1.14 thorpej timevalsub(&t2, &rdisc_timer, &now); 495 1.14 thorpej if (timercmp(&t2, &wtime, <)) 496 1.14 thorpej wtime = t2; 497 1.14 thorpej 498 1.14 thorpej 499 1.14 thorpej /* wait for input or a timer to expire. 500 1.14 thorpej */ 501 1.14 thorpej trace_flush(); 502 1.33 itojun if (ibitsp) 503 1.33 itojun free(ibitsp); 504 1.33 itojun ibitsp = (fd_set *)calloc(howmany(sock_max, NFDBITS), 505 1.33 itojun sizeof(fd_mask)); 506 1.33 itojun if (ibitsp == NULL) 507 1.33 itojun BADERR(1, "calloc"); 508 1.33 itojun memcpy(ibitsp, fdbitsp, howmany(sock_max, NFDBITS) * 509 1.33 itojun sizeof(fd_mask)); 510 1.33 itojun n = select(sock_max, ibitsp, 0, 0, &wtime); 511 1.14 thorpej if (n <= 0) { 512 1.14 thorpej if (n < 0 && errno != EINTR && errno != EAGAIN) 513 1.14 thorpej BADERR(1,"select"); 514 1.1 cgd continue; 515 1.1 cgd } 516 1.14 thorpej 517 1.33 itojun if (FD_ISSET(rt_sock, ibitsp)) { 518 1.14 thorpej read_rt(); 519 1.14 thorpej n--; 520 1.14 thorpej } 521 1.33 itojun if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, ibitsp)) { 522 1.14 thorpej read_d(); 523 1.14 thorpej n--; 524 1.14 thorpej } 525 1.33 itojun if (rip_sock >= 0 && FD_ISSET(rip_sock, ibitsp)) { 526 1.14 thorpej read_rip(rip_sock, 0); 527 1.14 thorpej n--; 528 1.14 thorpej } 529 1.14 thorpej 530 1.14 thorpej for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) { 531 1.14 thorpej if (ifp->int_rip_sock >= 0 532 1.33 itojun && FD_ISSET(ifp->int_rip_sock, ibitsp)) { 533 1.14 thorpej read_rip(ifp->int_rip_sock, ifp); 534 1.14 thorpej n--; 535 1.14 thorpej } 536 1.14 thorpej } 537 1.1 cgd } 538 1.14 thorpej } 539 1.1 cgd 540 1.14 thorpej 541 1.14 thorpej /* ARGSUSED */ 542 1.14 thorpej void 543 1.21 christos sigalrm(int s UNUSED) 544 1.14 thorpej { 545 1.14 thorpej /* Historically, SIGALRM would cause the daemon to check for 546 1.14 thorpej * new and broken interfaces. 547 1.1 cgd */ 548 1.14 thorpej ifinit_timer.tv_sec = now.tv_sec; 549 1.16 christos trace_act("SIGALRM"); 550 1.14 thorpej } 551 1.14 thorpej 552 1.14 thorpej 553 1.14 thorpej /* watch for fatal signals */ 554 1.14 thorpej void 555 1.14 thorpej sigterm(int sig) 556 1.14 thorpej { 557 1.14 thorpej stopint = sig; 558 1.14 thorpej (void)signal(sig, SIG_DFL); /* catch it only once */ 559 1.14 thorpej } 560 1.14 thorpej 561 1.14 thorpej 562 1.14 thorpej void 563 1.14 thorpej fix_select(void) 564 1.14 thorpej { 565 1.14 thorpej struct interface *ifp; 566 1.14 thorpej 567 1.14 thorpej sock_max = 0; 568 1.14 thorpej 569 1.14 thorpej if (sock_max <= rt_sock) 570 1.33 itojun sock_max = rt_sock + 1; 571 1.33 itojun if (rip_sock >= 0) 572 1.14 thorpej if (sock_max <= rip_sock) 573 1.33 itojun sock_max = rip_sock + 1; 574 1.14 thorpej for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { 575 1.33 itojun if (ifp->int_rip_sock >= 0) 576 1.14 thorpej if (sock_max <= ifp->int_rip_sock) 577 1.33 itojun sock_max = ifp->int_rip_sock + 1; 578 1.14 thorpej } 579 1.33 itojun if (rdisc_sock >= 0) 580 1.14 thorpej if (sock_max <= rdisc_sock) 581 1.33 itojun sock_max = rdisc_sock + 1; 582 1.33 itojun 583 1.33 itojun if (fdbitsp) 584 1.33 itojun free(fdbitsp); 585 1.33 itojun fdbitsp = (fd_set *)calloc(howmany(sock_max, NFDBITS), 586 1.33 itojun sizeof(fd_mask)); 587 1.33 itojun if (fdbitsp == NULL) 588 1.33 itojun BADERR(1, "calloc"); 589 1.33 itojun 590 1.33 itojun FD_SET(rt_sock, fdbitsp); 591 1.33 itojun if (rip_sock >= 0) 592 1.33 itojun FD_SET(rip_sock, fdbitsp); 593 1.33 itojun for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { 594 1.33 itojun if (ifp->int_rip_sock >= 0) 595 1.33 itojun FD_SET(ifp->int_rip_sock, fdbitsp); 596 1.14 thorpej } 597 1.33 itojun if (rdisc_sock >= 0) 598 1.33 itojun FD_SET(rdisc_sock, fdbitsp); 599 1.14 thorpej } 600 1.14 thorpej 601 1.14 thorpej 602 1.14 thorpej void 603 1.14 thorpej fix_sock(int sock, 604 1.21 christos const char *name) 605 1.14 thorpej { 606 1.14 thorpej int on; 607 1.14 thorpej #define MIN_SOCKBUF (4*1024) 608 1.14 thorpej static int rbuf; 609 1.1 cgd 610 1.14 thorpej if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) 611 1.14 thorpej logbad(1, "fcntl(%s) O_NONBLOCK: %s", 612 1.14 thorpej name, strerror(errno)); 613 1.14 thorpej on = 1; 614 1.16 christos if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, &on,sizeof(on)) < 0) 615 1.14 thorpej msglog("setsockopt(%s,SO_BROADCAST): %s", 616 1.14 thorpej name, strerror(errno)); 617 1.16 christos #ifdef USE_PASSIFNAME 618 1.16 christos on = 1; 619 1.16 christos if (setsockopt(sock, SOL_SOCKET, SO_PASSIFNAME, &on,sizeof(on)) < 0) 620 1.16 christos msglog("setsockopt(%s,SO_PASSIFNAME): %s", 621 1.16 christos name, strerror(errno)); 622 1.16 christos #endif 623 1.16 christos 624 1.14 thorpej if (rbuf >= MIN_SOCKBUF) { 625 1.14 thorpej if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, 626 1.14 thorpej &rbuf, sizeof(rbuf)) < 0) 627 1.14 thorpej msglog("setsockopt(%s,SO_RCVBUF=%d): %s", 628 1.14 thorpej name, rbuf, strerror(errno)); 629 1.14 thorpej } else { 630 1.14 thorpej for (rbuf = 60*1024; ; rbuf -= 4096) { 631 1.14 thorpej if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, 632 1.14 thorpej &rbuf, sizeof(rbuf)) == 0) { 633 1.16 christos trace_act("RCVBUF=%d", rbuf); 634 1.14 thorpej break; 635 1.1 cgd } 636 1.14 thorpej if (rbuf < MIN_SOCKBUF) { 637 1.14 thorpej msglog("setsockopt(%s,SO_RCVBUF = %d): %s", 638 1.14 thorpej name, rbuf, strerror(errno)); 639 1.14 thorpej break; 640 1.1 cgd } 641 1.14 thorpej } 642 1.14 thorpej } 643 1.14 thorpej } 644 1.14 thorpej 645 1.14 thorpej 646 1.14 thorpej /* get a rip socket 647 1.14 thorpej */ 648 1.14 thorpej static int /* <0 or file descriptor */ 649 1.14 thorpej get_rip_sock(naddr addr, 650 1.14 thorpej int serious) /* 1=failure to bind is serious */ 651 1.14 thorpej { 652 1.30 lukem struct sockaddr_in rsin; 653 1.14 thorpej unsigned char ttl; 654 1.14 thorpej int s; 655 1.14 thorpej 656 1.14 thorpej 657 1.14 thorpej if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 658 1.14 thorpej BADERR(1,"rip_sock = socket()"); 659 1.14 thorpej 660 1.30 lukem memset(&rsin, 0, sizeof(rsin)); 661 1.14 thorpej #ifdef _HAVE_SIN_LEN 662 1.30 lukem rsin.sin_len = sizeof(rsin); 663 1.14 thorpej #endif 664 1.30 lukem rsin.sin_family = AF_INET; 665 1.30 lukem rsin.sin_port = htons(RIP_PORT); 666 1.30 lukem rsin.sin_addr.s_addr = addr; 667 1.30 lukem if (bind(s, (struct sockaddr *)&rsin, sizeof(rsin)) < 0) { 668 1.14 thorpej if (serious) 669 1.14 thorpej BADERR(errno != EADDRINUSE, "bind(rip_sock)"); 670 1.14 thorpej return -1; 671 1.14 thorpej } 672 1.14 thorpej fix_sock(s,"rip_sock"); 673 1.14 thorpej 674 1.14 thorpej ttl = 1; 675 1.14 thorpej if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, 676 1.14 thorpej &ttl, sizeof(ttl)) < 0) 677 1.14 thorpej DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)"); 678 1.14 thorpej 679 1.14 thorpej return s; 680 1.14 thorpej } 681 1.14 thorpej 682 1.14 thorpej 683 1.14 thorpej /* turn off main RIP socket */ 684 1.14 thorpej void 685 1.14 thorpej rip_off(void) 686 1.14 thorpej { 687 1.14 thorpej struct interface *ifp; 688 1.17 lukem naddr addr; 689 1.14 thorpej 690 1.14 thorpej 691 1.14 thorpej if (rip_sock >= 0 && !mhome) { 692 1.16 christos trace_act("turn off RIP"); 693 1.14 thorpej 694 1.14 thorpej (void)close(rip_sock); 695 1.14 thorpej rip_sock = -1; 696 1.14 thorpej 697 1.14 thorpej /* get non-broadcast sockets to listen to queries. 698 1.14 thorpej */ 699 1.14 thorpej for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { 700 1.16 christos if (ifp->int_state & IS_REMOTE) 701 1.16 christos continue; 702 1.16 christos if (ifp->int_rip_sock < 0) { 703 1.14 thorpej addr = ((ifp->int_if_flags & IFF_POINTOPOINT) 704 1.14 thorpej ? ifp->int_dstaddr 705 1.14 thorpej : ifp->int_addr); 706 1.14 thorpej ifp->int_rip_sock = get_rip_sock(addr, 0); 707 1.1 cgd } 708 1.1 cgd } 709 1.14 thorpej 710 1.14 thorpej fix_select(); 711 1.14 thorpej 712 1.14 thorpej age(0); 713 1.14 thorpej } 714 1.14 thorpej } 715 1.14 thorpej 716 1.14 thorpej 717 1.14 thorpej /* turn on RIP multicast input via an interface 718 1.14 thorpej */ 719 1.14 thorpej static void 720 1.14 thorpej rip_mcast_on(struct interface *ifp) 721 1.14 thorpej { 722 1.14 thorpej struct ip_mreq m; 723 1.14 thorpej 724 1.14 thorpej if (!IS_RIP_IN_OFF(ifp->int_state) 725 1.14 thorpej && (ifp->int_if_flags & IFF_MULTICAST) 726 1.14 thorpej #ifdef MCAST_PPP_BUG 727 1.14 thorpej && !(ifp->int_if_flags & IFF_POINTOPOINT) 728 1.1 cgd #endif 729 1.14 thorpej && !(ifp->int_state & IS_ALIAS)) { 730 1.14 thorpej m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); 731 1.27 itojun #ifdef MCAST_IFINDEX 732 1.27 itojun m.imr_interface.s_addr = htonl(ifp->int_index); 733 1.27 itojun #else 734 1.14 thorpej m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) 735 1.14 thorpej ? ifp->int_dstaddr 736 1.14 thorpej : ifp->int_addr); 737 1.27 itojun #endif 738 1.14 thorpej if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, 739 1.14 thorpej &m, sizeof(m)) < 0) 740 1.14 thorpej LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)"); 741 1.1 cgd } 742 1.1 cgd } 743 1.1 cgd 744 1.14 thorpej 745 1.14 thorpej /* Prepare socket used for RIP. 746 1.14 thorpej */ 747 1.6 cgd void 748 1.14 thorpej rip_on(struct interface *ifp) 749 1.1 cgd { 750 1.14 thorpej /* If the main RIP socket is already alive, only start receiving 751 1.14 thorpej * multicasts for this interface. 752 1.14 thorpej */ 753 1.14 thorpej if (rip_sock >= 0) { 754 1.14 thorpej if (ifp != 0) 755 1.14 thorpej rip_mcast_on(ifp); 756 1.14 thorpej return; 757 1.14 thorpej } 758 1.14 thorpej 759 1.16 christos /* If the main RIP socket is off and it makes sense to turn it on, 760 1.16 christos * then turn it on for all of the interfaces. 761 1.18 thorpej * It makes sense if either router discovery is off, or if 762 1.18 thorpej * router discover is on and at most one interface is doing RIP. 763 1.14 thorpej */ 764 1.18 thorpej if (rip_interfaces > 0 && (!rdisc_ok || rip_interfaces > 1)) { 765 1.16 christos trace_act("turn on RIP"); 766 1.14 thorpej 767 1.14 thorpej /* Close all of the query sockets so that we can open 768 1.14 thorpej * the main socket. SO_REUSEPORT is not a solution, 769 1.14 thorpej * since that would let two daemons bind to the broadcast 770 1.14 thorpej * socket. 771 1.14 thorpej */ 772 1.14 thorpej for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { 773 1.14 thorpej if (ifp->int_rip_sock >= 0) { 774 1.14 thorpej (void)close(ifp->int_rip_sock); 775 1.14 thorpej ifp->int_rip_sock = -1; 776 1.14 thorpej } 777 1.14 thorpej } 778 1.14 thorpej 779 1.14 thorpej rip_sock = get_rip_sock(INADDR_ANY, 1); 780 1.14 thorpej rip_sock_mcast = 0; 781 1.14 thorpej 782 1.14 thorpej /* Do not advertise anything until we have heard something 783 1.14 thorpej */ 784 1.14 thorpej if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) 785 1.14 thorpej next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; 786 1.1 cgd 787 1.14 thorpej for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { 788 1.16 christos ifp->int_query_time = NEVER; 789 1.14 thorpej rip_mcast_on(ifp); 790 1.1 cgd } 791 1.14 thorpej ifinit_timer.tv_sec = now.tv_sec; 792 1.14 thorpej 793 1.14 thorpej } else if (ifp != 0 794 1.16 christos && !(ifp->int_state & IS_REMOTE) 795 1.16 christos && ifp->int_rip_sock < 0) { 796 1.14 thorpej /* RIP is off, so ensure there are sockets on which 797 1.14 thorpej * to listen for queries. 798 1.14 thorpej */ 799 1.14 thorpej ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); 800 1.16 christos } 801 1.14 thorpej 802 1.16 christos fix_select(); 803 1.15 christos } 804 1.15 christos 805 1.15 christos 806 1.15 christos /* die if malloc(3) fails 807 1.15 christos */ 808 1.15 christos void * 809 1.15 christos rtmalloc(size_t size, 810 1.21 christos const char *msg) 811 1.15 christos { 812 1.15 christos void *p = malloc(size); 813 1.15 christos if (p == 0) 814 1.24 christos logbad(1,"malloc(%lu) failed in %s", (u_long)size, msg); 815 1.15 christos return p; 816 1.1 cgd } 817 1.1 cgd 818 1.14 thorpej 819 1.14 thorpej /* get a random instant in an interval 820 1.14 thorpej */ 821 1.14 thorpej void 822 1.14 thorpej intvl_random(struct timeval *tp, /* put value here */ 823 1.14 thorpej u_long lo, /* value is after this second */ 824 1.14 thorpej u_long hi) /* and before this */ 825 1.14 thorpej { 826 1.14 thorpej tp->tv_sec = (time_t)(hi == lo 827 1.14 thorpej ? lo 828 1.34 itojun : (lo + arc4random() % ((hi - lo)))); 829 1.34 itojun tp->tv_usec = arc4random() % 1000000; 830 1.14 thorpej } 831 1.14 thorpej 832 1.14 thorpej 833 1.14 thorpej void 834 1.14 thorpej timevaladd(struct timeval *t1, 835 1.14 thorpej struct timeval *t2) 836 1.14 thorpej { 837 1.14 thorpej 838 1.14 thorpej t1->tv_sec += t2->tv_sec; 839 1.20 christos if ((t1->tv_usec += t2->tv_usec) >= 1000000) { 840 1.14 thorpej t1->tv_sec++; 841 1.14 thorpej t1->tv_usec -= 1000000; 842 1.1 cgd } 843 1.14 thorpej } 844 1.14 thorpej 845 1.14 thorpej 846 1.14 thorpej /* t1 = t2 - t3 847 1.14 thorpej */ 848 1.14 thorpej static void 849 1.14 thorpej timevalsub(struct timeval *t1, 850 1.14 thorpej struct timeval *t2, 851 1.14 thorpej struct timeval *t3) 852 1.14 thorpej { 853 1.14 thorpej t1->tv_sec = t2->tv_sec - t3->tv_sec; 854 1.14 thorpej if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) { 855 1.14 thorpej t1->tv_sec--; 856 1.14 thorpej t1->tv_usec += 1000000; 857 1.1 cgd } 858 1.14 thorpej } 859 1.14 thorpej 860 1.14 thorpej 861 1.16 christos /* put a message into the system log 862 1.16 christos */ 863 1.14 thorpej void 864 1.21 christos msglog(const char *p, ...) 865 1.14 thorpej { 866 1.14 thorpej va_list args; 867 1.14 thorpej 868 1.14 thorpej trace_flush(); 869 1.14 thorpej 870 1.14 thorpej va_start(args, p); 871 1.14 thorpej vsyslog(LOG_ERR, p, args); 872 1.29 wiz va_end(args); 873 1.14 thorpej 874 1.14 thorpej if (ftrace != 0) { 875 1.14 thorpej if (ftrace == stdout) 876 1.14 thorpej (void)fputs("routed: ", ftrace); 877 1.29 wiz va_start(args, p); 878 1.16 christos (void)vfprintf(ftrace, p, args); 879 1.29 wiz va_end(args); 880 1.16 christos (void)fputc('\n', ftrace); 881 1.16 christos } 882 1.16 christos } 883 1.16 christos 884 1.16 christos 885 1.16 christos /* Put a message about a bad system into the system log if 886 1.16 christos * we have not complained about it recently. 887 1.16 christos * 888 1.16 christos * It is desirable to complain about all bad systems, but not too often. 889 1.16 christos * In the worst case, it is not practical to keep track of all bad systems. 890 1.16 christos * For example, there can be many systems with the wrong password. 891 1.16 christos */ 892 1.16 christos void 893 1.21 christos msglim(struct msg_limit *lim, naddr addr, const char *p, ...) 894 1.16 christos { 895 1.16 christos va_list args; 896 1.16 christos int i; 897 1.16 christos struct msg_sub *ms1, *ms; 898 1.21 christos const char *p1; 899 1.16 christos 900 1.16 christos /* look for the oldest slot in the table 901 1.16 christos * or the slot for the bad router. 902 1.16 christos */ 903 1.16 christos ms = ms1 = lim->subs; 904 1.16 christos for (i = MSG_SUBJECT_N; ; i--, ms1++) { 905 1.16 christos if (i == 0) { 906 1.16 christos /* Reuse a slot at most once every 10 minutes. 907 1.16 christos */ 908 1.16 christos if (lim->reuse > now.tv_sec) { 909 1.16 christos ms = 0; 910 1.16 christos } else { 911 1.16 christos ms = ms1; 912 1.16 christos lim->reuse = now.tv_sec + 10*60; 913 1.16 christos } 914 1.16 christos break; 915 1.16 christos } 916 1.16 christos if (ms->addr == addr) { 917 1.16 christos /* Repeat a complaint about a given system at 918 1.16 christos * most once an hour. 919 1.16 christos */ 920 1.16 christos if (ms->until > now.tv_sec) 921 1.16 christos ms = 0; 922 1.16 christos break; 923 1.16 christos } 924 1.16 christos if (ms->until < ms1->until) 925 1.16 christos ms = ms1; 926 1.16 christos } 927 1.16 christos if (ms != 0) { 928 1.16 christos ms->addr = addr; 929 1.16 christos ms->until = now.tv_sec + 60*60; /* 60 minutes */ 930 1.16 christos 931 1.16 christos trace_flush(); 932 1.16 christos for (p1 = p; *p1 == ' '; p1++) 933 1.16 christos continue; 934 1.29 wiz va_start(args, p); 935 1.29 wiz vsyslog(LOG_ERR, p1, args); 936 1.29 wiz va_end(args); 937 1.16 christos } 938 1.16 christos 939 1.16 christos /* always display the message if tracing */ 940 1.16 christos if (ftrace != 0) { 941 1.29 wiz va_start(args, p); 942 1.14 thorpej (void)vfprintf(ftrace, p, args); 943 1.14 thorpej (void)fputc('\n', ftrace); 944 1.29 wiz va_end(args); 945 1.1 cgd } 946 1.14 thorpej } 947 1.14 thorpej 948 1.14 thorpej 949 1.14 thorpej void 950 1.21 christos logbad(int dump, const char *p, ...) 951 1.14 thorpej { 952 1.14 thorpej va_list args; 953 1.14 thorpej 954 1.14 thorpej trace_flush(); 955 1.14 thorpej 956 1.14 thorpej va_start(args, p); 957 1.14 thorpej vsyslog(LOG_ERR, p, args); 958 1.29 wiz va_end(args); 959 1.14 thorpej 960 1.14 thorpej (void)fputs("routed: ", stderr); 961 1.29 wiz va_start(args, p); 962 1.14 thorpej (void)vfprintf(stderr, p, args); 963 1.29 wiz va_end(args); 964 1.14 thorpej (void)fputs("; giving up\n",stderr); 965 1.14 thorpej (void)fflush(stderr); 966 1.14 thorpej 967 1.14 thorpej if (dump) 968 1.14 thorpej abort(); 969 1.14 thorpej exit(1); 970 1.1 cgd } 971