| 1 | #ifndef lint |
| 2 | static char sccsid[] = "@(#)routed.c 4.14 82/06/09"; |
| 3 | #endif |
| 4 | |
| 5 | /* |
| 6 | * Routing Table Management Daemon |
| 7 | */ |
| 8 | #include <sys/types.h> |
| 9 | #include <sys/ioctl.h> |
| 10 | #include <sys/socket.h> |
| 11 | #include <net/in.h> |
| 12 | #include <net/if.h> |
| 13 | #include <errno.h> |
| 14 | #include <stdio.h> |
| 15 | #include <nlist.h> |
| 16 | #include <signal.h> |
| 17 | #include <time.h> |
| 18 | #include "rip.h" |
| 19 | #include "router.h" |
| 20 | |
| 21 | #define LOOPBACKNET 0177 |
| 22 | /* casts to keep lint happy */ |
| 23 | #define insque(q,p) _insque((caddr_t)q,(caddr_t)p) |
| 24 | #define remque(q) _remque((caddr_t)q) |
| 25 | #define equal(a1, a2) \ |
| 26 | (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) |
| 27 | #define min(a,b) ((a)>(b)?(b):(a)) |
| 28 | |
| 29 | struct nlist nl[] = { |
| 30 | #define N_IFNET 0 |
| 31 | { "_ifnet" }, |
| 32 | 0, |
| 33 | }; |
| 34 | |
| 35 | struct sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER }; |
| 36 | |
| 37 | int s; |
| 38 | int snoroute; /* socket with no routing */ |
| 39 | int kmem = -1; |
| 40 | int supplier = -1; /* process should supply updates */ |
| 41 | int install = 1; /* if 1 call kernel */ |
| 42 | int timeval = -TIMER_RATE; |
| 43 | int timer(); |
| 44 | int cleanup(); |
| 45 | |
| 46 | #define tprintf if (trace) printf |
| 47 | int trace = 0; |
| 48 | FILE *ftrace; |
| 49 | |
| 50 | char packet[MAXPACKETSIZE+1]; |
| 51 | struct rip *msg = (struct rip *)packet; |
| 52 | |
| 53 | struct in_addr if_makeaddr(); |
| 54 | struct ifnet *if_ifwithaddr(), *if_ifwithnet(); |
| 55 | extern char *malloc(); |
| 56 | extern int errno, exit(); |
| 57 | char **argv0; |
| 58 | |
| 59 | int sndmsg(), supply(); |
| 60 | |
| 61 | main(argc, argv) |
| 62 | int argc; |
| 63 | char *argv[]; |
| 64 | { |
| 65 | int cc; |
| 66 | struct sockaddr from; |
| 67 | |
| 68 | argv0 = argv; |
| 69 | #ifndef DEBUG |
| 70 | if (fork()) |
| 71 | exit(0); |
| 72 | for (cc = 0; cc < 10; cc++) |
| 73 | (void) close(cc); |
| 74 | (void) open("/", 0); |
| 75 | (void) dup2(0, 1); |
| 76 | (void) dup2(0, 2); |
| 77 | { int t = open("/dev/tty", 2); |
| 78 | if (t >= 0) { |
| 79 | ioctl(t, TIOCNOTTY, (char *)0); |
| 80 | (void) close(t); |
| 81 | } |
| 82 | } |
| 83 | #endif |
| 84 | if (trace) { |
| 85 | ftrace = fopen("/etc/routerlog", "w"); |
| 86 | dup2(fileno(ftrace), 1); |
| 87 | dup2(fileno(ftrace), 2); |
| 88 | } |
| 89 | #ifdef vax || pdp11 |
| 90 | myaddr.sin_port = htons(myaddr.sin_port); |
| 91 | #endif |
| 92 | again: |
| 93 | s = socket(SOCK_DGRAM, 0, &myaddr, 0); |
| 94 | if (s < 0) { |
| 95 | perror("socket"); |
| 96 | sleep(30); |
| 97 | goto again; |
| 98 | } |
| 99 | again2: |
| 100 | snoroute = socket(SOCK_DGRAM, 0, 0, SO_DONTROUTE); |
| 101 | if (snoroute < 0) { |
| 102 | perror("socket"); |
| 103 | sleep(30); |
| 104 | goto again2; |
| 105 | } |
| 106 | argv++, argc--; |
| 107 | while (argc > 0 && **argv == '-') { |
| 108 | if (!strcmp(*argv, "-s") == 0) { |
| 109 | supplier = 1; |
| 110 | argv++, argc--; |
| 111 | continue; |
| 112 | } |
| 113 | if (!strcmp(*argv, "-q") == 0) { |
| 114 | supplier = 0; |
| 115 | argv++, argc--; |
| 116 | continue; |
| 117 | } |
| 118 | goto usage; |
| 119 | } |
| 120 | if (argc > 0) { |
| 121 | usage: |
| 122 | fprintf(stderr, "usage: routed [ -s ]\n"); |
| 123 | exit(1); |
| 124 | } |
| 125 | rtinit(); |
| 126 | ifinit(); |
| 127 | if (supplier < 0) |
| 128 | supplier = 0; |
| 129 | gwkludge(); |
| 130 | msg->rip_cmd = RIPCMD_REQUEST; |
| 131 | msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; |
| 132 | msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; |
| 133 | toall(sendmsg); |
| 134 | sigset(SIGALRM, timer); |
| 135 | timer(); |
| 136 | |
| 137 | for (;;) { |
| 138 | cc = receive(s, &from, packet, sizeof (packet)); |
| 139 | if (cc <= 0) { |
| 140 | if (cc < 0 && errno != EINTR) |
| 141 | perror("receive"); |
| 142 | continue; |
| 143 | } |
| 144 | sighold(SIGALRM); |
| 145 | rip_input(&from, cc); |
| 146 | sigrelse(SIGALRM); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | rtinit() |
| 151 | { |
| 152 | register struct rthash *rh; |
| 153 | |
| 154 | for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) |
| 155 | rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; |
| 156 | for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) |
| 157 | rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; |
| 158 | } |
| 159 | |
| 160 | struct ifnet *ifnet; |
| 161 | |
| 162 | ifinit() |
| 163 | { |
| 164 | struct ifnet *ifp, *next; |
| 165 | register struct sockaddr *dst; |
| 166 | int uniquemultihostinterfaces = 0; |
| 167 | |
| 168 | nlist("/vmunix", nl); |
| 169 | if (nl[N_IFNET].n_value == 0) { |
| 170 | printf("ifnet: not in namelist\n"); |
| 171 | goto bad; |
| 172 | } |
| 173 | kmem = open("/dev/kmem", 0); |
| 174 | if (kmem < 0) { |
| 175 | perror("/dev/kmem"); |
| 176 | goto bad; |
| 177 | } |
| 178 | if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || |
| 179 | read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) { |
| 180 | printf("ifnet: error reading kmem\n"); |
| 181 | goto bad; |
| 182 | } |
| 183 | while (next) { |
| 184 | ifp = (struct ifnet *)malloc(sizeof (struct ifnet)); |
| 185 | if (ifp == 0) { |
| 186 | printf("routed: out of memory\n"); |
| 187 | break; |
| 188 | } |
| 189 | if (lseek(kmem, (long)next, 0) == -1 || |
| 190 | read(kmem, (char *)ifp, sizeof (*ifp)) != sizeof (*ifp)) { |
| 191 | perror("read"); |
| 192 | goto bad; |
| 193 | } |
| 194 | next = ifp->if_next; |
| 195 | if (ifp->if_addr.sa_family != AF_INET) |
| 196 | continue; |
| 197 | if (ifp->if_net == LOOPBACKNET) |
| 198 | continue; |
| 199 | if ((ifp->if_flags & IFF_POINTOPOINT) == 0 || |
| 200 | if_ifwithaddr(&ifp->if_dstaddr) == 0) |
| 201 | uniquemultihostinterfaces++; |
| 202 | ifp->if_next = ifnet; |
| 203 | ifnet = ifp; |
| 204 | addrouteforif(ifp); |
| 205 | } |
| 206 | if (uniquemultihostinterfaces > 1 && supplier < 0) |
| 207 | supplier = 1; |
| 208 | return; |
| 209 | bad: |
| 210 | sleep(60); |
| 211 | execv("/etc/routed", argv0); |
| 212 | _exit(0177); |
| 213 | } |
| 214 | |
| 215 | addrouteforif(ifp) |
| 216 | struct ifnet *ifp; |
| 217 | { |
| 218 | struct sockaddr_in net; |
| 219 | struct sockaddr *dst; |
| 220 | |
| 221 | if (ifp->if_flags & IFF_POINTOPOINT) |
| 222 | dst = &ifp->if_dstaddr; |
| 223 | else { |
| 224 | bzero((char *)&net, sizeof (net)); |
| 225 | net.sin_family = AF_INET; |
| 226 | net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); |
| 227 | dst = (struct sockaddr *)&net; |
| 228 | } |
| 229 | rtadd(dst, &ifp->if_addr, 0, RTS_INTERFACE); |
| 230 | } |
| 231 | |
| 232 | gwkludge() |
| 233 | { |
| 234 | struct sockaddr_in dst, gate; |
| 235 | FILE *fp; |
| 236 | struct rt_entry *rt; |
| 237 | char flags[BUFSIZ]; |
| 238 | |
| 239 | fp = fopen("/etc/gateways", "r"); |
| 240 | if (fp == NULL) |
| 241 | return; |
| 242 | bzero((char *)&dst, sizeof (dst)); |
| 243 | bzero((char *)&gate, sizeof (gate)); |
| 244 | dst.sin_family = AF_INET; |
| 245 | gate.sin_family = AF_INET; |
| 246 | for (;;) { |
| 247 | if (fscanf(fp, "dst %x gateway %x\n", &dst.sin_addr.s_addr, |
| 248 | &gate.sin_addr.s_addr, flags) == EOF) |
| 249 | break; |
| 250 | rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1, |
| 251 | RTS_GLOBAL|(!strcmp(flags, "passive") ? RTS_PASSIVE : 0)); |
| 252 | } |
| 253 | fclose(fp); |
| 254 | } |
| 255 | |
| 256 | timer() |
| 257 | { |
| 258 | register struct rthash *rh; |
| 259 | register struct rt_entry *rt; |
| 260 | struct rthash *base = hosthash; |
| 261 | int doinghost = 1, state; |
| 262 | |
| 263 | timeval += TIMER_RATE; |
| 264 | tprintf(">>> time %d >>>\n", timeval); |
| 265 | again: |
| 266 | for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { |
| 267 | rt = rh->rt_forw; |
| 268 | for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { |
| 269 | if (!(rt->rt_state & RTS_GLOBAL)) |
| 270 | rt->rt_timer += TIMER_RATE; |
| 271 | log("", rt); |
| 272 | if (rt->rt_timer >= EXPIRE_TIME) |
| 273 | rt->rt_metric = HOPCNT_INFINITY; |
| 274 | if ((rt->rt_state & RTS_DELRT) || |
| 275 | rt->rt_timer >= GARBAGE_TIME) { |
| 276 | if (rt->rt_state&(RTS_INTERFACE|RTS_GLOBAL)) { |
| 277 | if (rt->rt_timer > 9999) |
| 278 | rt->rt_timer = 9999; |
| 279 | continue; |
| 280 | } |
| 281 | rt = rt->rt_back; |
| 282 | rtdelete(rt->rt_forw); |
| 283 | continue; |
| 284 | } |
| 285 | state = rt->rt_state; |
| 286 | if (rt->rt_state & RTS_ADDRT) { |
| 287 | if (ioctl(s, SIOCADDRT,(char *)&rt->rt_rt) < 0) |
| 288 | perror("SIOCADDRT"); |
| 289 | rt->rt_state &= ~RTS_ADDRT; |
| 290 | } |
| 291 | if (rt->rt_state & RTS_CHGRT) { |
| 292 | struct rtentry oldroute; |
| 293 | |
| 294 | oldroute = rt->rt_rt; |
| 295 | rt->rt_router = rt->rt_newrouter; |
| 296 | if (ioctl(s, SIOCADDRT,(char *)&rt->rt_rt) < 0) |
| 297 | perror("SIOCADDRT"); |
| 298 | if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) |
| 299 | perror("SIOCDELRT"); |
| 300 | rt->rt_state &= ~RTS_CHGRT; |
| 301 | } |
| 302 | if (supplier && (state & (RTS_CHGRT|RTS_ADDRT))) { |
| 303 | log("broadcast", rt); |
| 304 | msg->rip_cmd = RIPCMD_RESPONSE; |
| 305 | msg->rip_nets[0].rip_dst = rt->rt_dst; |
| 306 | msg->rip_nets[0].rip_metric = |
| 307 | min(rt->rt_metric+1, HOPCNT_INFINITY); |
| 308 | sendmsgtoall(); |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | if (doinghost) { |
| 313 | doinghost = 0; |
| 314 | base = nethash; |
| 315 | goto again; |
| 316 | } |
| 317 | if (supplier && (timeval % SUPPLY_INTERVAL) == 0) |
| 318 | toall(supply); |
| 319 | tprintf("<<< time %d <<<\n", timeval); |
| 320 | alarm(TIMER_RATE); |
| 321 | } |
| 322 | |
| 323 | toall(f) |
| 324 | int (*f)(); |
| 325 | { |
| 326 | register struct rthash *rh; |
| 327 | register struct rt_entry *rt; |
| 328 | register struct sockaddr *dst; |
| 329 | struct rthash *base = hosthash; |
| 330 | int doinghost = 1; |
| 331 | |
| 332 | again: |
| 333 | for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) |
| 334 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { |
| 335 | if ((rt->rt_state&RTS_PASSIVE) || rt->rt_metric > 0) |
| 336 | continue; |
| 337 | if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) |
| 338 | dst = &rt->rt_ifp->if_broadaddr; |
| 339 | else |
| 340 | dst = &rt->rt_router; |
| 341 | (*f)(rt, dst); |
| 342 | } |
| 343 | if (doinghost) { |
| 344 | base = nethash; |
| 345 | doinghost = 0; |
| 346 | goto again; |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | sendmsg(rt, dst) |
| 351 | register struct rt_entry *rt; |
| 352 | struct sockaddr *dst; |
| 353 | { |
| 354 | |
| 355 | (*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip)); |
| 356 | } |
| 357 | |
| 358 | supply(rt, sa) |
| 359 | register struct rt_entry *rt; |
| 360 | struct sockaddr *sa; |
| 361 | { |
| 362 | struct netinfo *n = msg->rip_nets; |
| 363 | register struct rthash *rh; |
| 364 | struct rthash *base = hosthash; |
| 365 | int doinghost = 1, size; |
| 366 | int (*output)() = afswitch[sa->sa_family].af_output; |
| 367 | int sto = (rt->rt_state&RTS_INTERFACE) ? snoroute : s; |
| 368 | |
| 369 | log("supply", rt); |
| 370 | msg->rip_cmd = RIPCMD_RESPONSE; |
| 371 | again: |
| 372 | for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) |
| 373 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { |
| 374 | size = (char *)n - packet; |
| 375 | if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { |
| 376 | (*output)(sto, sa, size); |
| 377 | n = msg->rip_nets; |
| 378 | } |
| 379 | n->rip_dst = rt->rt_dst; |
| 380 | n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); |
| 381 | n++; |
| 382 | } |
| 383 | if (doinghost) { |
| 384 | doinghost = 0; |
| 385 | base = nethash; |
| 386 | goto again; |
| 387 | } |
| 388 | if (n != msg->rip_nets) |
| 389 | (*output)(sto, sa, (char *)n - packet); |
| 390 | } |
| 391 | |
| 392 | /* |
| 393 | * Respond to a routing info request. |
| 394 | */ |
| 395 | rip_respond(from, size) |
| 396 | struct sockaddr *from; |
| 397 | int size; |
| 398 | { |
| 399 | struct netinfo *np = msg->rip_nets; |
| 400 | struct rt_entry *rt; |
| 401 | int newsize = 0; |
| 402 | |
| 403 | size -= 4 * sizeof (char); |
| 404 | while (size > 0) { |
| 405 | if (size < sizeof (struct netinfo)) |
| 406 | break; |
| 407 | size -= sizeof (struct netinfo); |
| 408 | if (np->rip_dst.sa_family == AF_UNSPEC && |
| 409 | np->rip_metric == HOPCNT_INFINITY && size == 0) { |
| 410 | supply(s, from); |
| 411 | return; |
| 412 | } |
| 413 | rt = rtlookup(&np->rip_dst); |
| 414 | np->rip_metric = rt == 0 ? |
| 415 | HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY); |
| 416 | np++, newsize += sizeof (struct netinfo); |
| 417 | } |
| 418 | if (newsize > 0) { |
| 419 | msg->rip_cmd = RIPCMD_RESPONSE; |
| 420 | newsize += sizeof (int); |
| 421 | (*afswitch[from->sa_family].af_output)(s, from, newsize); |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | /* |
| 426 | * Handle an incoming routing packet. |
| 427 | */ |
| 428 | rip_input(from, size) |
| 429 | struct sockaddr *from; |
| 430 | int size; |
| 431 | { |
| 432 | struct rt_entry *rt; |
| 433 | struct netinfo *n; |
| 434 | struct ifnet *ifp; |
| 435 | time_t t; |
| 436 | |
| 437 | switch (msg->rip_cmd) { |
| 438 | |
| 439 | default: |
| 440 | return; |
| 441 | |
| 442 | case RIPCMD_REQUEST: |
| 443 | rip_respond(from, size); |
| 444 | return; |
| 445 | |
| 446 | case RIPCMD_TRACEON: |
| 447 | if ((*afswitch[from->sa_family].af_portcheck)(from) == 0) |
| 448 | return; |
| 449 | if (trace) |
| 450 | return; |
| 451 | packet[size] = '\0'; |
| 452 | ftrace = fopen(msg->rip_tracefile, "a"); |
| 453 | if (ftrace == NULL) |
| 454 | return; |
| 455 | (void) dup2(fileno(ftrace), 1); |
| 456 | (void) dup2(fileno(ftrace), 2); |
| 457 | trace = 1; |
| 458 | t = time(0); |
| 459 | printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); |
| 460 | return; |
| 461 | |
| 462 | case RIPCMD_TRACEOFF: |
| 463 | /* verify message came from a priviledged port */ |
| 464 | if ((*afswitch[from->sa_family].af_portcheck)(from) == 0) |
| 465 | return; |
| 466 | if (!trace) |
| 467 | return; |
| 468 | t = time(0); |
| 469 | printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); |
| 470 | fflush(stdout), fflush(stderr); |
| 471 | if (ftrace) |
| 472 | fclose(ftrace); |
| 473 | (void) close(1), (void) close(2); |
| 474 | trace = 0; |
| 475 | return; |
| 476 | |
| 477 | case RIPCMD_RESPONSE: |
| 478 | /* verify message came from a router */ |
| 479 | if ((*afswitch[from->sa_family].af_portmatch)(from) == 0) |
| 480 | return; |
| 481 | (*afswitch[from->sa_family].af_canon)(from); |
| 482 | tprintf("input from %x\n", |
| 483 | ((struct sockaddr_in *)from)->sin_addr); |
| 484 | /* are we talking to ourselves? */ |
| 485 | ifp = if_ifwithaddr(from); |
| 486 | if (ifp) { |
| 487 | rt = rtfind(from); |
| 488 | if (rt) |
| 489 | rt->rt_timer = 0; |
| 490 | else |
| 491 | addrouteforif(ifp); |
| 492 | return; |
| 493 | } |
| 494 | size -= 4 * sizeof (char); |
| 495 | n = msg->rip_nets; |
| 496 | for (; size > 0; size -= sizeof (struct netinfo), n++) { |
| 497 | if (size < sizeof (struct netinfo)) |
| 498 | break; |
| 499 | if (n->rip_metric >= HOPCNT_INFINITY) |
| 500 | continue; |
| 501 | tprintf("dst %x hc %d...", |
| 502 | ((struct sockaddr_in *)&n->rip_dst)->sin_addr, |
| 503 | n->rip_metric); |
| 504 | rt = rtlookup(&n->rip_dst); |
| 505 | if (rt == 0) { |
| 506 | rtadd(&n->rip_dst, from, n->rip_metric, 0); |
| 507 | tprintf("new\n"); |
| 508 | continue; |
| 509 | } |
| 510 | tprintf("ours: gate %x hc %d timer %d\n", |
| 511 | ((struct sockaddr_in *)&rt->rt_router)->sin_addr, |
| 512 | rt->rt_metric, rt->rt_timer); |
| 513 | /* |
| 514 | * update if from gateway, shorter, or getting stale |
| 515 | * and equivalent. |
| 516 | */ |
| 517 | if (equal(from, &rt->rt_router) || |
| 518 | n->rip_metric < rt->rt_metric || |
| 519 | (rt->rt_timer > (EXPIRE_TIME/2) && |
| 520 | rt->rt_metric == n->rip_metric)) { |
| 521 | rtchange(rt, from, n->rip_metric); |
| 522 | rt->rt_timer = 0; |
| 523 | } |
| 524 | } |
| 525 | return; |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | struct rt_entry * |
| 530 | rtlookup(dst) |
| 531 | struct sockaddr *dst; |
| 532 | { |
| 533 | register struct rt_entry *rt; |
| 534 | register struct rthash *rh; |
| 535 | register int hash; |
| 536 | struct afhash h; |
| 537 | int doinghost = 1; |
| 538 | |
| 539 | if (dst->sa_family >= AF_MAX) |
| 540 | return (0); |
| 541 | (*afswitch[dst->sa_family].af_hash)(dst, &h); |
| 542 | hash = h.afh_hosthash; |
| 543 | rh = &hosthash[hash % ROUTEHASHSIZ]; |
| 544 | again: |
| 545 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { |
| 546 | if (rt->rt_hash != hash) |
| 547 | continue; |
| 548 | if (equal(&rt->rt_dst, dst)) |
| 549 | return (rt); |
| 550 | } |
| 551 | if (doinghost) { |
| 552 | doinghost = 0; |
| 553 | hash = h.afh_nethash; |
| 554 | rh = &nethash[hash % ROUTEHASHSIZ]; |
| 555 | goto again; |
| 556 | } |
| 557 | return (0); |
| 558 | } |
| 559 | |
| 560 | struct rt_entry * |
| 561 | rtfind(dst) |
| 562 | struct sockaddr *dst; |
| 563 | { |
| 564 | register struct rt_entry *rt; |
| 565 | register struct rthash *rh; |
| 566 | register int hash; |
| 567 | struct afhash h; |
| 568 | int af = dst->sa_family; |
| 569 | int doinghost = 1, (*match)(); |
| 570 | |
| 571 | if (af >= AF_MAX) |
| 572 | return (0); |
| 573 | (*afswitch[af].af_hash)(dst, &h); |
| 574 | hash = h.afh_hosthash; |
| 575 | rh = &hosthash[hash % ROUTEHASHSIZ]; |
| 576 | |
| 577 | again: |
| 578 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { |
| 579 | if (rt->rt_hash != hash) |
| 580 | continue; |
| 581 | if (doinghost) { |
| 582 | if (equal(&rt->rt_dst, dst)) |
| 583 | return (rt); |
| 584 | } else { |
| 585 | if (rt->rt_dst.sa_family == af && |
| 586 | (*match)(&rt->rt_dst, dst)) |
| 587 | return (rt); |
| 588 | } |
| 589 | } |
| 590 | if (doinghost) { |
| 591 | doinghost = 0; |
| 592 | hash = h.afh_nethash; |
| 593 | rh = &nethash[hash % ROUTEHASHSIZ]; |
| 594 | match = afswitch[af].af_netmatch; |
| 595 | goto again; |
| 596 | } |
| 597 | return (0); |
| 598 | } |
| 599 | |
| 600 | rtadd(dst, gate, metric, iflags) |
| 601 | struct sockaddr *dst, *gate; |
| 602 | int metric, iflags; |
| 603 | { |
| 604 | struct afhash h; |
| 605 | register struct rt_entry *rt; |
| 606 | struct rthash *rh; |
| 607 | int af = dst->sa_family, flags, hash; |
| 608 | |
| 609 | if (af >= AF_MAX) |
| 610 | return; |
| 611 | (*afswitch[af].af_hash)(dst, &h); |
| 612 | flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; |
| 613 | if (flags & RTF_HOST) { |
| 614 | hash = h.afh_hosthash; |
| 615 | rh = &hosthash[hash % ROUTEHASHSIZ]; |
| 616 | } else { |
| 617 | hash = h.afh_nethash; |
| 618 | rh = &nethash[hash % ROUTEHASHSIZ]; |
| 619 | } |
| 620 | rt = (struct rt_entry *)malloc(sizeof (*rt)); |
| 621 | if (rt == 0) |
| 622 | return; |
| 623 | rt->rt_hash = hash; |
| 624 | rt->rt_dst = *dst; |
| 625 | rt->rt_router = *gate; |
| 626 | rt->rt_metric = metric; |
| 627 | rt->rt_timer = 0; |
| 628 | rt->rt_flags = RTF_UP | flags | iflags; |
| 629 | rt->rt_state = 0; |
| 630 | rt->rt_ifp = if_ifwithnet(&rt->rt_router); |
| 631 | if (metric) |
| 632 | rt->rt_flags |= RTF_GATEWAY; |
| 633 | insque(rt, rh); |
| 634 | log("add", rt); |
| 635 | if (install) |
| 636 | rt->rt_state |= RTS_ADDRT; |
| 637 | } |
| 638 | |
| 639 | rtchange(rt, gate, metric) |
| 640 | struct rt_entry *rt; |
| 641 | struct sockaddr *gate; |
| 642 | short metric; |
| 643 | { |
| 644 | int change = 0; |
| 645 | |
| 646 | if (!equal(&rt->rt_router, gate)) { |
| 647 | rt->rt_newrouter = *gate; |
| 648 | change++; |
| 649 | } |
| 650 | if (metric != rt->rt_metric) { |
| 651 | if (metric == 0) |
| 652 | rt->rt_flags |= RTF_GATEWAY; |
| 653 | rt->rt_metric = metric; |
| 654 | change++; |
| 655 | } |
| 656 | if (!change) |
| 657 | return; |
| 658 | log("change", rt); |
| 659 | if (install) |
| 660 | rt->rt_state |= RTS_CHGRT; |
| 661 | } |
| 662 | |
| 663 | rtdelete(rt) |
| 664 | struct rt_entry *rt; |
| 665 | { |
| 666 | |
| 667 | log("delete", rt); |
| 668 | if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) |
| 669 | perror("SIOCDELRT"); |
| 670 | /* don't delete interface entries so we can poll them later */ |
| 671 | if (rt->rt_state & RTS_INTERFACE) |
| 672 | return; |
| 673 | remque(rt); |
| 674 | free((char *)rt); |
| 675 | } |
| 676 | |
| 677 | log(operation, rt) |
| 678 | char *operation; |
| 679 | struct rt_entry *rt; |
| 680 | { |
| 681 | time_t t = time(0); |
| 682 | struct sockaddr_in *dst, *gate; |
| 683 | static struct bits { |
| 684 | int t_bits; |
| 685 | char *t_name; |
| 686 | } flagbits[] = { |
| 687 | { RTF_UP, "UP" }, |
| 688 | { RTF_GATEWAY, "GATEWAY" }, |
| 689 | { RTF_HOST, "HOST" }, |
| 690 | { 0 } |
| 691 | }, statebits[] = { |
| 692 | { RTS_DELRT, "DELETE" }, |
| 693 | { RTS_CHGRT, "CHANGE" }, |
| 694 | { RTS_PASSIVE, "PASSIVE" }, |
| 695 | { RTS_INTERFACE,"INTERFACE" }, |
| 696 | { RTS_GLOBAL, "GLOBAL" }, |
| 697 | { 0 } |
| 698 | }; |
| 699 | register struct bits *p; |
| 700 | register int first; |
| 701 | char *cp; |
| 702 | |
| 703 | if (trace == 0) |
| 704 | return; |
| 705 | printf("%s ", operation); |
| 706 | dst = (struct sockaddr_in *)&rt->rt_dst; |
| 707 | gate = (struct sockaddr_in *)&rt->rt_router; |
| 708 | printf("dst %x, router %x, metric %d, flags", |
| 709 | dst->sin_addr, gate->sin_addr, rt->rt_metric); |
| 710 | cp = " %s"; |
| 711 | for (first = 1, p = flagbits; p->t_bits > 0; p++) { |
| 712 | if ((rt->rt_flags & p->t_bits) == 0) |
| 713 | continue; |
| 714 | printf(cp, p->t_name); |
| 715 | if (first) { |
| 716 | cp = "|%s"; |
| 717 | first = 0; |
| 718 | } |
| 719 | } |
| 720 | printf(" state"); |
| 721 | cp = " %s"; |
| 722 | for (first = 1, p = statebits; p->t_bits > 0; p++) { |
| 723 | if ((rt->rt_state & p->t_bits) == 0) |
| 724 | continue; |
| 725 | printf(cp, p->t_name); |
| 726 | if (first) { |
| 727 | cp = "|%s"; |
| 728 | first = 0; |
| 729 | } |
| 730 | } |
| 731 | putchar('\n'); |
| 732 | } |
| 733 | |
| 734 | struct ifnet * |
| 735 | if_ifwithaddr(addr) |
| 736 | struct sockaddr *addr; |
| 737 | { |
| 738 | register struct ifnet *ifp; |
| 739 | |
| 740 | #define same(a1, a2) \ |
| 741 | (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) |
| 742 | for (ifp = ifnet; ifp; ifp = ifp->if_next) { |
| 743 | if (ifp->if_addr.sa_family != addr->sa_family) |
| 744 | continue; |
| 745 | if (same(&ifp->if_addr, addr)) |
| 746 | break; |
| 747 | if ((ifp->if_flags & IFF_BROADCAST) && |
| 748 | same(&ifp->if_broadaddr, addr)) |
| 749 | break; |
| 750 | } |
| 751 | return (ifp); |
| 752 | #undef same |
| 753 | } |
| 754 | |
| 755 | struct ifnet * |
| 756 | if_ifwithnet(addr) |
| 757 | register struct sockaddr *addr; |
| 758 | { |
| 759 | register struct ifnet *ifp; |
| 760 | register int af = addr->sa_family; |
| 761 | register int (*netmatch)(); |
| 762 | |
| 763 | if (af >= AF_MAX) |
| 764 | return (0); |
| 765 | netmatch = afswitch[af].af_netmatch; |
| 766 | for (ifp = ifnet; ifp; ifp = ifp->if_next) { |
| 767 | if (af != ifp->if_addr.sa_family) |
| 768 | continue; |
| 769 | if ((*netmatch)(addr, &ifp->if_addr)) |
| 770 | break; |
| 771 | } |
| 772 | return (ifp); |
| 773 | } |
| 774 | |
| 775 | struct in_addr |
| 776 | if_makeaddr(net, host) |
| 777 | int net, host; |
| 778 | { |
| 779 | u_long addr; |
| 780 | |
| 781 | if (net < 128) |
| 782 | addr = (net << 24) | host; |
| 783 | else if (net < 65536) |
| 784 | addr = (net << 16) | host; |
| 785 | else |
| 786 | addr = (net << 8) | host; |
| 787 | #ifdef vax |
| 788 | addr = htonl(addr); |
| 789 | #endif |
| 790 | return (*(struct in_addr *)&addr); |
| 791 | } |