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