split state and flags; cleanup a bit more
[unix-history] / usr / src / sbin / routed / routed.c
CommitLineData
7c0bf99b 1#ifndef lint
e6b5ed24 2static char sccsid[] = "@(#)routed.c 4.7 %G%";
7c0bf99b
SL
3#endif
4
3cec0c76
SL
5/*
6 * Routing Table Management Daemon
7 */
8#include <sys/types.h>
7c0bf99b
SL
9#include <sys/ioctl.h>
10#include <sys/socket.h>
11#include <net/in.h>
7c0bf99b
SL
12#include <net/if.h>
13#include <errno.h>
14#include <stdio.h>
15#include <nlist.h>
16#include <signal.h>
17#include "rip.h"
18#include "router.h"
19
20#define LOOPBACKNET 0177
21/* casts to keep lint happy */
22#define insque(q,p) _insque((caddr_t)q,(caddr_t)p)
23#define remque(q) _remque((caddr_t)q)
24#define equal(a1, a2) \
25 (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
77df73ca 26#define min(a,b) ((a)>(b)?(b):(a))
7c0bf99b
SL
27
28struct nlist nl[] = {
29#define N_IFNET 0
30 { "_ifnet" },
31 0,
32};
33
34struct sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
35
36int s;
77df73ca 37int kmem = -1;
7c0bf99b
SL
38int supplier; /* process should supply updates */
39int initializing; /* stem off broadcast() calls */
40int install = 0; /* if 1 call kernel */
77df73ca
SL
41int lookforinterfaces = 1;
42int performnlist = 1;
7c0bf99b
SL
43int timeval;
44int timer();
45int cleanup();
46int trace = 0;
47
48char packet[MAXPACKETSIZE];
49
3cec0c76
SL
50struct in_addr if_makeaddr();
51struct ifnet *if_ifwithaddr(), *if_ifwithnet();
7c0bf99b 52extern char *malloc();
c5f8709e 53extern int errno, exit();
7c0bf99b
SL
54
55main(argc, argv)
56 int argc;
57 char *argv[];
58{
59 int cc;
60 struct sockaddr from;
61
62 { int t = open("/dev/tty", 2);
63 if (t >= 0) {
64 ioctl(t, TIOCNOTTY, 0);
65 close(t);
66 }
67 }
68 if (trace) {
77df73ca
SL
69 (void) freopen("/etc/routerlog", "a", stdout);
70 (void) dup2(fileno(stdout), 2);
7c0bf99b 71 setbuf(stdout, NULL);
7c0bf99b 72 }
e6b5ed24 73#ifdef vax || pdp11
7c0bf99b
SL
74 myaddr.sin_port = htons(myaddr.sin_port);
75#endif
76again:
77 s = socket(SOCK_DGRAM, 0, &myaddr, 0);
78 if (s < 0) {
79 perror("socket");
80 sleep(30);
81 goto again;
82 }
83 rtinit();
84 getothers();
77df73ca 85 initializing = 1;
7c0bf99b 86 getinterfaces();
77df73ca 87 initializing = 0;
7c0bf99b
SL
88 request();
89
90 argv++, argc--;
91 while (argc > 0) {
92 if (strcmp(*argv, "-s") == 0)
77df73ca 93 supplier = 1;
7c0bf99b
SL
94 else if (strcmp(*argv, "-q") == 0)
95 supplier = 0;
96 argv++, argc--;
97 }
98 sigset(SIGALRM, timer);
77df73ca 99 timer();
7c0bf99b
SL
100
101 /*
102 * Listen for routing packets
103 */
104 for (;;) {
105 cc = receive(s, &from, packet, sizeof (packet));
106 if (cc <= 0) {
107 if (cc < 0 && errno != EINTR)
108 perror("receive");
109 continue;
110 }
111 sighold(SIGALRM);
112 rip_input(&from, cc);
113 sigrelse(SIGALRM);
114 }
115}
116
117/*
118 * Look in a file for any gateways we should configure
119 * outside the directly connected ones. This is a kludge,
120 * but until we can find out about gateways on the "other side"
121 * of the ARPANET using GGP, it's a must.
122 *
123 * We don't really know the distance to the gateway, so we
124 * assume it's a neighbor.
125 */
126getothers()
127{
128 struct sockaddr_in dst, gate;
129 FILE *fp = fopen("/etc/gateways", "r");
130 struct rt_entry *rt;
131
132 if (fp == NULL)
133 return;
134 bzero((char *)&dst, sizeof (dst));
135 bzero((char *)&gate, sizeof (gate));
136 dst.sin_family = AF_INET;
137 gate.sin_family = AF_INET;
138 while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
139 &gate.sin_addr.s_addr) != EOF) {
140 rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
141 rt = rtlookup((struct sockaddr *)&dst);
142 if (rt)
e6b5ed24 143 rt->rt_state |= RTS_SILENT;
7c0bf99b
SL
144 }
145 fclose(fp);
146}
147
77df73ca
SL
148/*
149 * Timer routine:
150 *
151 * o handle timers on table entries,
152 * o invalidate entries which haven't been updated in a while,
153 * o delete entries which are too old,
154 * o retry ioctl's which weren't successful the first
155 * time due to the kernel entry being busy
156 * o if we're an internetwork router, supply routing updates
157 * periodically
158 */
159timer()
7c0bf99b 160{
3cec0c76 161 register struct rthash *rh;
77df73ca 162 register struct rt_entry *rt;
3cec0c76 163 struct rthash *base = hosthash;
77df73ca 164 int doinghost = 1;
7c0bf99b 165
77df73ca
SL
166 if (trace)
167 printf(">>> time %d >>>\n", timeval);
168again:
169 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
170 rt = rh->rt_forw;
171 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7c0bf99b 172
77df73ca
SL
173 /*
174 * If the host is indicated to be
175 * "silent" (i.e. it's one we got
176 * from the initialization file),
177 * don't time out it's entry.
178 */
e6b5ed24 179 if ((rt->rt_state & RTS_SILENT) == 0)
e929cf48 180 rt->rt_timer += TIMER_RATE;
77df73ca 181 log("", rt);
7c0bf99b 182
77df73ca
SL
183 /*
184 * If the entry should be deleted
185 * attempt to do so and reclaim space.
186 */
187 if (rt->rt_timer >= GARBAGE_TIME ||
e6b5ed24 188 (rt->rt_state & RTS_DELRT)) {
77df73ca 189 rt = rt->rt_back;
e6b5ed24 190 rtdelete(rt->rt_forw);
77df73ca
SL
191 continue;
192 }
7c0bf99b 193
77df73ca
SL
194 /*
195 * If we haven't heard from the router
196 * in a long time indicate the route is
197 * hard to reach.
198 */
199 if (rt->rt_timer >= EXPIRE_TIME)
200 rt->rt_metric = HOPCNT_INFINITY;
e6b5ed24 201 if (rt->rt_state & RTS_CHGRT)
3cec0c76 202 if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) ||
77df73ca 203 --rt->rt_retry == 0)
e6b5ed24 204 rt->rt_state &= ~RTS_CHGRT;
7c0bf99b 205
77df73ca
SL
206 /*
207 * Try to add the route to the kernel tables.
208 * If this fails because the entry already exists
209 * (perhaps because someone manually added it)
210 * change the add to a change. If the operation
211 * fails otherwise (likely because the entry is
212 * in use), retry the operation a few more times.
213 */
e6b5ed24 214 if (rt->rt_state & RTS_ADDRT) {
3cec0c76 215 if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) {
77df73ca 216 if (errno == EEXIST) {
e6b5ed24
SL
217 rt->rt_state &= ~RTS_ADDRT;
218 rt->rt_state |= RTS_CHGRT;
77df73ca
SL
219 rt->rt_retry =
220 (EXPIRE_TIME/TIMER_RATE);
221 continue;
222 }
223 if (--rt->rt_retry)
224 continue;
225 }
e6b5ed24 226 rt->rt_state &= ~RTS_ADDRT;
77df73ca
SL
227 }
228 }
229 }
230 if (doinghost) {
231 doinghost = 0;
232 base = nethash;
233 goto again;
234 }
235 timeval += TIMER_RATE;
236 if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
237 getinterfaces();
238 if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
239 supplyall();
240 if (trace)
241 printf("<<< time %d <<<\n", timeval);
242 alarm(TIMER_RATE);
7c0bf99b
SL
243}
244
3cec0c76 245struct ifnet *ifnet;
7c0bf99b
SL
246/*
247 * Find the network interfaces attached to this machine.
77df73ca 248 * The info is used to:
7c0bf99b
SL
249 *
250 * (1) initialize the routing tables, as done by the kernel.
251 * (2) ignore incoming packets we send.
252 * (3) figure out broadcast capability and addresses.
253 * (4) figure out if we're an internetwork gateway.
254 *
255 * We don't handle anything but Internet addresses.
3cec0c76
SL
256 *
257 * Note: this routine may be called periodically to
258 * scan for new interfaces. In fact, the timer routine
259 * does so based on the flag lookforinterfaces. The
260 * flag performnlist is set whenever something odd occurs
261 * while scanning the kernel; this is likely to occur
262 * if /vmunix isn't up to date (e.g. someone booted /ovmunix).
7c0bf99b
SL
263 */
264getinterfaces()
265{
77df73ca
SL
266 struct ifnet *ifp;
267 struct ifnet ifstruct, *next;
7c0bf99b 268 struct sockaddr_in net;
77df73ca 269 register struct sockaddr *dst;
7c0bf99b
SL
270 int nets;
271
77df73ca
SL
272 if (performnlist) {
273 nlist("/vmunix", nl);
274 if (nl[N_IFNET].n_value == 0) {
275 performnlist++;
276 printf("ifnet: not in namelist\n");
277 return;
278 }
279 performnlist = 0;
7c0bf99b 280 }
7c0bf99b 281 if (kmem < 0) {
77df73ca
SL
282 kmem = open("/dev/kmem", 0);
283 if (kmem < 0) {
284 perror("/dev/kmem");
285 return;
286 }
287 }
288 if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
289 read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
290 performnlist = 1;
291 return;
7c0bf99b 292 }
7c0bf99b
SL
293 bzero((char *)&net, sizeof (net));
294 net.sin_family = AF_INET;
7c0bf99b 295 nets = 0;
77df73ca
SL
296 while (ifp) {
297 if (lseek(kmem, (long)ifp, 0) == -1 ||
298 read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
299 sizeof (ifstruct)) {
7c0bf99b 300 perror("read");
77df73ca 301 performnlist = 1;
7c0bf99b
SL
302 break;
303 }
77df73ca
SL
304 ifp = &ifstruct;
305 if ((ifp->if_flags & IFF_UP) == 0) {
306 lookforinterfaces++;
307 skip:
308 ifp = ifp->if_next;
309 continue;
310 }
311 if (ifp->if_addr.sa_family != AF_INET)
7c0bf99b 312 goto skip;
77df73ca
SL
313 /* ignore software loopback networks. */
314 if (ifp->if_net == LOOPBACKNET)
7c0bf99b 315 goto skip;
77df73ca
SL
316 /* check if we already know about this one */
317 if (if_ifwithaddr(&ifstruct.if_addr)) {
318 nets++;
7c0bf99b
SL
319 goto skip;
320 }
77df73ca
SL
321 ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
322 if (ifp == 0) {
323 printf("routed: out of memory\n");
324 break;
325 }
326 bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
7c0bf99b
SL
327
328 /*
77df73ca
SL
329 * Count the # of directly connected networks
330 * and point to point links which aren't looped
331 * back to ourself. This is used below to
332 * decide if we should be a routing "supplier".
7c0bf99b 333 */
77df73ca
SL
334 if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
335 if_ifwithaddr(&ifp->if_dstaddr) == 0)
336 nets++;
337
338 if (ifp->if_flags & IFF_POINTOPOINT)
339 dst = &ifp->if_dstaddr;
340 else {
341 net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
342 dst = (struct sockaddr *)&net;
343 }
344 next = ifp->if_next;
345 ifp->if_next = ifnet;
346 ifnet = ifp;
347 if (rtlookup(dst) == 0)
348 rtadd(dst, &ifp->if_addr, 0);
349 ifp = next;
7c0bf99b 350 }
7c0bf99b
SL
351 supplier = nets > 1;
352}
353
354/*
355 * Send a request message to all directly
356 * connected hosts and networks.
357 */
358request()
359{
77df73ca 360 register struct rip *msg = (struct rip *)packet;
7c0bf99b 361
77df73ca
SL
362 msg->rip_cmd = RIPCMD_REQUEST;
363 msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
364 msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
365 sendall();
7c0bf99b
SL
366}
367
368/*
369 * Broadcast a new, or modified, routing table entry
370 * to all directly connected hosts and networks.
371 */
372broadcast(entry)
373 struct rt_entry *entry;
77df73ca
SL
374{
375 struct rip *msg = (struct rip *)packet;
376
377 log("broadcast", entry);
378 msg->rip_cmd = RIPCMD_RESPONSE;
379 msg->rip_nets[0].rip_dst = entry->rt_dst;
380 msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
381 sendall();
382}
383
3cec0c76
SL
384/*
385 * Send "packet" to all neighbors.
386 */
77df73ca 387sendall()
7c0bf99b 388{
3cec0c76 389 register struct rthash *rh;
7c0bf99b
SL
390 register struct rt_entry *rt;
391 register struct sockaddr *dst;
3cec0c76 392 struct rthash *base = hosthash;
7c0bf99b 393 int doinghost = 1;
7c0bf99b
SL
394
395again:
396 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
397 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
e6b5ed24 398 if ((rt->rt_state & RTS_SILENT) || rt->rt_metric > 0)
7c0bf99b
SL
399 continue;
400 if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
401 dst = &rt->rt_ifp->if_broadaddr;
402 else
403 dst = &rt->rt_gateway;
404 (*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
405 }
406 if (doinghost) {
7c0bf99b 407 base = nethash;
77df73ca 408 doinghost = 0;
7c0bf99b
SL
409 goto again;
410 }
411}
412
413/*
414 * Supply all directly connected neighbors with the
415 * current state of the routing tables.
416 */
417supplyall()
418{
419 register struct rt_entry *rt;
3cec0c76 420 register struct rthash *rh;
7c0bf99b 421 register struct sockaddr *dst;
3cec0c76 422 struct rthash *base = hosthash;
7c0bf99b
SL
423 int doinghost = 1;
424
425again:
426 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
427 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
e6b5ed24 428 if ((rt->rt_state & RTS_SILENT) || rt->rt_metric > 0)
7c0bf99b
SL
429 continue;
430 if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
431 dst = &rt->rt_ifp->if_broadaddr;
432 else
433 dst = &rt->rt_gateway;
77df73ca 434 log("supply", rt);
7c0bf99b
SL
435 supply(dst);
436 }
437 if (doinghost) {
438 base = nethash;
439 doinghost = 0;
440 goto again;
441 }
442}
443
444/*
445 * Supply routing information to target "sa".
446 */
447supply(sa)
448 struct sockaddr *sa;
449{
450 struct rip *msg = (struct rip *)packet;
451 struct netinfo *n = msg->rip_nets;
3cec0c76 452 register struct rthash *rh;
7c0bf99b 453 register struct rt_entry *rt;
3cec0c76 454 struct rthash *base = hosthash;
77df73ca 455 int doinghost = 1, size;
7c0bf99b
SL
456 int (*output)() = afswitch[sa->sa_family].af_output;
457
458 msg->rip_cmd = RIPCMD_RESPONSE;
459again:
460 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
461 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
462
463 /*
464 * Flush packet out if not enough room for
465 * another routing table entry.
466 */
77df73ca
SL
467 size = (char *)n - packet;
468 if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
469 (*output)(sa, size);
7c0bf99b
SL
470 n = msg->rip_nets;
471 }
472 n->rip_dst = rt->rt_dst;
77df73ca
SL
473 n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
474 n++;
7c0bf99b
SL
475 }
476 if (doinghost) {
477 doinghost = 0;
478 base = nethash;
479 goto again;
480 }
77df73ca
SL
481 if (n != msg->rip_nets)
482 (*output)(sa, (char *)n - packet);
7c0bf99b
SL
483}
484
485/*
486 * Respond to a routing info request.
487 */
488rip_respond(from, size)
489 struct sockaddr *from;
490 int size;
491{
492 register struct rip *msg = (struct rip *)packet;
493 struct netinfo *np = msg->rip_nets;
494 struct rt_entry *rt;
495 int newsize = 0;
496
77df73ca 497 size -= 4 * sizeof (char);
7c0bf99b
SL
498 while (size > 0) {
499 if (size < sizeof (struct netinfo))
500 break;
501 size -= sizeof (struct netinfo);
502 if (np->rip_dst.sa_family == AF_UNSPEC &&
503 np->rip_metric == HOPCNT_INFINITY && size == 0) {
504 supply(from);
505 return;
506 }
507 rt = rtlookup(&np->rip_dst);
77df73ca
SL
508 np->rip_metric = rt == 0 ?
509 HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
7c0bf99b
SL
510 np++, newsize += sizeof (struct netinfo);
511 }
512 if (newsize > 0) {
513 msg->rip_cmd = RIPCMD_RESPONSE;
514 newsize += sizeof (int);
515 (*afswitch[from->sa_family].af_output)(from, newsize);
516 }
517}
518
519/*
520 * Handle an incoming routing packet.
521 */
522rip_input(from, size)
523 struct sockaddr *from;
524 int size;
525{
526 register struct rip *msg = (struct rip *)packet;
527 struct rt_entry *rt;
528 struct netinfo *n;
529
e6b5ed24 530 switch (msg->rip_cmd) {
7c0bf99b 531
e6b5ed24 532 default:
7c0bf99b 533 return;
e6b5ed24
SL
534
535 case RIPCMD_REQUEST:
7c0bf99b
SL
536 rip_respond(from, size);
537 return;
e6b5ed24
SL
538
539 case RIPCMD_RESPONSE:
540 /* verify message came from a priviledged port */
541 if ((*afswitch[from->sa_family].af_portmatch)(from) == 0)
542 return;
543 break;
7c0bf99b
SL
544 }
545
546 /*
547 * Process updates.
548 * Extraneous information like Internet ports
549 * must first be purged from the sender's address for
550 * pattern matching below.
551 */
552 (*afswitch[from->sa_family].af_canon)(from);
553 if (trace)
3cec0c76
SL
554 printf("input from %x\n",
555 ((struct sockaddr_in *)from)->sin_addr);
7c0bf99b
SL
556 /*
557 * If response packet is from ourselves, use it only
558 * to reset timer on entry. Otherwise, we'd believe
559 * it as gospel (since it comes from the router) and
560 * unknowingly update the metric to show the outgoing
561 * cost (higher than our real cost). I guess the protocol
562 * spec doesn't address this because Xerox Ethernets
563 * don't hear their own broadcasts?
564 */
565 if (if_ifwithaddr(from)) {
566 rt = rtlookup(from);
567 if (rt)
568 rt->rt_timer = 0;
569 return;
570 }
77df73ca 571 size -= 4 * sizeof (char);
7c0bf99b
SL
572 n = msg->rip_nets;
573 for (; size > 0; size -= sizeof (struct netinfo), n++) {
574 if (size < sizeof (struct netinfo))
575 break;
576 if (trace)
3cec0c76
SL
577 printf("dst %x hc %d...",
578 ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
7c0bf99b
SL
579 n->rip_metric);
580 rt = rtlookup(&n->rip_dst);
581
582 /*
583 * Unknown entry, add it to the tables only if
584 * its interesting.
585 */
586 if (rt == 0) {
587 if (n->rip_metric < HOPCNT_INFINITY)
588 rtadd(&n->rip_dst, from, n->rip_metric);
589 if (trace)
590 printf("new\n");
591 continue;
592 }
593
594 if (trace)
595 printf("ours: gate %x hc %d timer %d\n",
3cec0c76
SL
596 ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr,
597 rt->rt_metric, rt->rt_timer);
7c0bf99b
SL
598 /*
599 * Update the entry if one of the following is true:
600 *
601 * (1) The update came directly from the gateway.
602 * (2) A shorter path is provided.
603 * (3) The entry hasn't been updated in a while
77df73ca
SL
604 * and a path of equivalent cost is offered
605 * (with the cost finite).
7c0bf99b
SL
606 */
607 if (equal(from, &rt->rt_gateway) ||
608 rt->rt_metric > n->rip_metric ||
609 (rt->rt_timer > (EXPIRE_TIME/2) &&
77df73ca
SL
610 rt->rt_metric == n->rip_metric &&
611 rt->rt_metric < HOPCNT_INFINITY)) {
7c0bf99b
SL
612 rtchange(rt, from, n->rip_metric);
613 rt->rt_timer = 0;
614 }
615 }
616}
617
3cec0c76
SL
618rtinit()
619{
620 register struct rthash *rh;
621
622 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
623 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
624 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
625 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
626}
627
7c0bf99b
SL
628/*
629 * Lookup an entry to the appropriate dstination.
630 */
631struct rt_entry *
632rtlookup(dst)
633 struct sockaddr *dst;
634{
635 register struct rt_entry *rt;
3cec0c76 636 register struct rthash *rh;
7c0bf99b
SL
637 register int hash, (*match)();
638 struct afhash h;
639 int af = dst->sa_family, doinghost = 1;
640
641 if (af >= AF_MAX)
642 return (0);
643 (*afswitch[af].af_hash)(dst, &h);
644 hash = h.afh_hosthash;
645 rh = &hosthash[hash % ROUTEHASHSIZ];
646
647again:
648 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
649 if (rt->rt_hash != hash)
650 continue;
651 if (doinghost) {
652 if (equal(&rt->rt_dst, dst))
653 return (rt);
654 } else {
655 if (rt->rt_dst.sa_family == af &&
656 (*match)(&rt->rt_dst, dst))
657 return (rt);
658 }
659 }
660 if (doinghost) {
661 doinghost = 0;
662 hash = h.afh_nethash;
663 match = afswitch[af].af_netmatch;
664 rh = &nethash[hash % ROUTEHASHSIZ];
665 goto again;
666 }
667 return (0);
668}
669
7c0bf99b
SL
670/*
671 * Add a new entry.
672 */
673rtadd(dst, gate, metric)
674 struct sockaddr *dst, *gate;
675 short metric;
676{
677 struct afhash h;
678 register struct rt_entry *rt;
3cec0c76 679 struct rthash *rh;
7c0bf99b
SL
680 int af = dst->sa_family, flags, hash;
681
682 if (af >= AF_MAX)
683 return;
684 (*afswitch[af].af_hash)(dst, &h);
685 flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
686 if (flags & RTF_HOST) {
687 hash = h.afh_hosthash;
688 rh = &hosthash[hash % ROUTEHASHSIZ];
689 } else {
690 hash = h.afh_nethash;
691 rh = &nethash[hash % ROUTEHASHSIZ];
692 }
693 rt = (struct rt_entry *)malloc(sizeof (*rt));
694 if (rt == 0)
695 return;
696 rt->rt_hash = hash;
697 rt->rt_dst = *dst;
698 rt->rt_gateway = *gate;
699 rt->rt_metric = metric;
700 rt->rt_timer = 0;
701 rt->rt_flags = RTF_UP | flags;
e6b5ed24 702 rt->rt_state = 0;
7c0bf99b
SL
703 rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
704 if (metric == 0)
705 rt->rt_flags |= RTF_DIRECT;
706 insque(rt, rh);
77df73ca 707 log("add", rt);
7c0bf99b
SL
708 if (initializing)
709 return;
710 if (supplier)
711 broadcast(rt);
712 if (install) {
e6b5ed24 713 rt->rt_state |= RTS_ADDRT;
7c0bf99b
SL
714 rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
715 }
716}
717
718/*
719 * Look to see if a change to an existing entry
720 * is warranted; if so, make it.
721 */
722rtchange(rt, gate, metric)
723 struct rt_entry *rt;
724 struct sockaddr *gate;
725 short metric;
726{
727 int change = 0;
728
729 if (!equal(&rt->rt_gateway, gate)) {
730 rt->rt_gateway = *gate;
731 change++;
732 }
733
734 /*
735 * If the hop count has changed, adjust
736 * the flags in the routing table entry accordingly.
737 */
738 if (metric != rt->rt_metric) {
739 if (rt->rt_metric == 0)
740 rt->rt_flags &= ~RTF_DIRECT;
741 rt->rt_metric = metric;
742 if (metric >= HOPCNT_INFINITY)
743 rt->rt_flags &= ~RTF_UP;
744 else
745 rt->rt_flags |= RTF_UP;
746 change++;
747 }
748
749 if (!change)
750 return;
751 if (supplier)
752 broadcast(rt);
77df73ca 753 log("change", rt);
7c0bf99b 754 if (install) {
e6b5ed24 755 rt->rt_state |= RTS_CHGRT;
7c0bf99b
SL
756 rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
757 }
758}
759
760/*
761 * Delete a routing table entry.
762 */
763rtdelete(rt)
764 struct rt_entry *rt;
765{
77df73ca 766 log("delete", rt);
7c0bf99b 767 if (install)
3cec0c76 768 if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) &&
7c0bf99b 769 errno == EBUSY)
e6b5ed24 770 rt->rt_state |= RTS_DELRT;
7c0bf99b
SL
771 remque(rt);
772 free((char *)rt);
773}
774
7c0bf99b
SL
775log(operation, rt)
776 char *operation;
777 struct rt_entry *rt;
778{
779 time_t t = time(0);
780 struct sockaddr_in *dst, *gate;
e6b5ed24 781 static struct bits {
7c0bf99b
SL
782 int t_bits;
783 char *t_name;
e6b5ed24 784 } flagbits[] = {
7c0bf99b
SL
785 { RTF_UP, "UP" },
786 { RTF_DIRECT, "DIRECT" },
787 { RTF_HOST, "HOST" },
e6b5ed24
SL
788 { 0 }
789 }, statebits[] = {
790 { RTS_DELRT, "DELETE" },
791 { RTS_CHGRT, "CHANGE" },
792 { RTS_SILENT, "SILENT" },
7c0bf99b
SL
793 { 0 }
794 };
e6b5ed24 795 register struct bits *p;
7c0bf99b
SL
796 register int first;
797 char *cp;
798
77df73ca
SL
799 if (trace == 0)
800 return;
7c0bf99b
SL
801 printf("%s ", operation);
802 dst = (struct sockaddr_in *)&rt->rt_dst;
803 gate = (struct sockaddr_in *)&rt->rt_gateway;
e6b5ed24 804 printf("dst %x, router %x, metric %d, flags",
7c0bf99b 805 dst->sin_addr, gate->sin_addr, rt->rt_metric);
e6b5ed24
SL
806 cp = " %s";
807 for (first = 1, p = flagbits; p->t_bits > 0; p++) {
7c0bf99b
SL
808 if ((rt->rt_flags & p->t_bits) == 0)
809 continue;
810 printf(cp, p->t_name);
811 if (first) {
812 cp = "|%s";
813 first = 0;
814 }
815 }
e6b5ed24
SL
816 printf(" state");
817 cp = " %s";
818 for (first = 1, p = statebits; p->t_bits > 0; p++) {
819 if ((rt->rt_state & p->t_bits) == 0)
820 continue;
821 printf(cp, p->t_name);
822 if (first) {
823 cp = "|%s";
824 first = 0;
825 }
826 }
7c0bf99b
SL
827 putchar('\n');
828}
77df73ca 829
3cec0c76
SL
830/*
831 * Find the interface with address "addr".
832 */
77df73ca
SL
833struct ifnet *
834if_ifwithaddr(addr)
835 struct sockaddr *addr;
836{
837 register struct ifnet *ifp;
838
839#define same(a1, a2) \
840 (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
841 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
842 if (ifp->if_addr.sa_family != addr->sa_family)
843 continue;
844 if (same(&ifp->if_addr, addr))
845 break;
846 if ((ifp->if_flags & IFF_BROADCAST) &&
847 same(&ifp->if_broadaddr, addr))
848 break;
849 }
850 return (ifp);
851#undef same
852}
853
3cec0c76
SL
854/*
855 * Find the interface with network imbedded in
856 * the sockaddr "addr". Must use per-af routine
857 * look for match.
858 */
77df73ca
SL
859struct ifnet *
860if_ifwithnet(addr)
861 register struct sockaddr *addr;
862{
863 register struct ifnet *ifp;
864 register int af = addr->sa_family;
865 register int (*netmatch)();
866
867 if (af >= AF_MAX)
868 return (0);
869 netmatch = afswitch[af].af_netmatch;
870 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
871 if (af != ifp->if_addr.sa_family)
872 continue;
873 if ((*netmatch)(addr, &ifp->if_addr))
874 break;
875 }
876 return (ifp);
877}
878
3cec0c76
SL
879/*
880 * Formulate an Internet address.
881 */
77df73ca
SL
882struct in_addr
883if_makeaddr(net, host)
884 int net, host;
885{
886 u_long addr;
887
888 if (net < 128)
889 addr = (net << 24) | host;
890 else if (net < 65536)
891 addr = (net << 16) | host;
892 else
893 addr = (net << 8) | host;
894#ifdef vax
895 addr = htonl(addr);
896#endif
897 return (*(struct in_addr *)&addr);
898}