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