Commit | Line | Data |
---|---|---|
7c0bf99b | 1 | #ifndef lint |
74d7f201 | 2 | static 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 | |
29 | struct nlist nl[] = { | |
30 | #define N_IFNET 0 | |
31 | { "_ifnet" }, | |
32 | 0, | |
33 | }; | |
34 | ||
35 | struct sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER }; | |
36 | ||
37 | int s; | |
74d7f201 | 38 | int snoroute; /* socket with no routing */ |
77df73ca | 39 | int kmem = -1; |
7c0bf99b | 40 | int supplier; /* process should supply updates */ |
b7fcdb03 | 41 | int install = 1; /* if 1 call kernel */ |
77df73ca SL |
42 | int lookforinterfaces = 1; |
43 | int performnlist = 1; | |
7c0bf99b SL |
44 | int timeval; |
45 | int timer(); | |
46 | int cleanup(); | |
47 | int trace = 0; | |
6d0df65e | 48 | FILE *ftrace; |
7c0bf99b SL |
49 | |
50 | char packet[MAXPACKETSIZE]; | |
51 | ||
3cec0c76 SL |
52 | struct in_addr if_makeaddr(); |
53 | struct ifnet *if_ifwithaddr(), *if_ifwithnet(); | |
7c0bf99b | 54 | extern char *malloc(); |
c5f8709e | 55 | extern int errno, exit(); |
7c0bf99b SL |
56 | |
57 | main(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 | |
87 | again: | |
88 | s = socket(SOCK_DGRAM, 0, &myaddr, 0); | |
89 | if (s < 0) { | |
90 | perror("socket"); | |
91 | sleep(30); | |
92 | goto again; | |
93 | } | |
74d7f201 BJ |
94 | again2: |
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 | */ | |
141 | timer() | |
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); | |
150 | again: | |
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 | 227 | struct 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 | */ |
246 | getinterfaces() | |
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 | */ | |
356 | getothers() | |
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 | */ | |
390 | request() | |
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 | */ | |
404 | broadcast(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 | 419 | sendall() |
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 | |
427 | again: | |
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 | */ | |
450 | supplyall() | |
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 | ||
458 | again: | |
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 |
481 | supply(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; | |
494 | again: | |
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 | */ | |
523 | rip_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 | */ | |
557 | rip_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 | */ | |
664 | tracecmd(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 |
695 | rtinit() |
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 | */ | |
708 | struct rt_entry * | |
709 | rtlookup(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]; | |
723 | again: | |
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 | */ | |
745 | struct rt_entry * | |
746 | rtfind(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 | ||
762 | again: | |
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 | */ | |
788 | rtadd(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 | */ | |
831 | rtchange(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 | */ | |
858 | rtdelete(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 |
871 | log(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 |
930 | struct ifnet * |
931 | if_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 |
956 | struct ifnet * |
957 | if_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 |
979 | struct in_addr |
980 | if_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 | } |