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