Commit | Line | Data |
---|---|---|
7c0bf99b | 1 | #ifndef lint |
3bd9bc1e | 2 | static char sccsid[] = "@(#)routed.c 4.25 %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> |
f64bc556 | 18 | #include <netdb.h> |
baa2dcc8 | 19 | #define RIPCMDS |
7c0bf99b SL |
20 | #include "rip.h" |
21 | #include "router.h" | |
22 | ||
23 | #define LOOPBACKNET 0177 | |
24 | /* casts to keep lint happy */ | |
25 | #define insque(q,p) _insque((caddr_t)q,(caddr_t)p) | |
26 | #define remque(q) _remque((caddr_t)q) | |
27 | #define equal(a1, a2) \ | |
28 | (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) | |
77df73ca | 29 | #define min(a,b) ((a)>(b)?(b):(a)) |
7c0bf99b SL |
30 | |
31 | struct nlist nl[] = { | |
32 | #define N_IFNET 0 | |
33 | { "_ifnet" }, | |
34 | 0, | |
35 | }; | |
36 | ||
d0188ba8 SL |
37 | struct sockaddr_in routingaddr = { AF_INET }; |
38 | struct sockaddr_in noroutingaddr = { AF_INET }; | |
7c0bf99b SL |
39 | |
40 | int s; | |
74d7f201 | 41 | int snoroute; /* socket with no routing */ |
77df73ca | 42 | int kmem = -1; |
6db0b3a4 | 43 | int supplier = -1; /* process should supply updates */ |
b7fcdb03 | 44 | int install = 1; /* if 1 call kernel */ |
e0a3d4f9 SL |
45 | int lookforinterfaces = 1; |
46 | int performnlist = 1; | |
71535ddb | 47 | int externalinterfaces = 0; /* # of remote and local interfaces */ |
6db0b3a4 | 48 | int timeval = -TIMER_RATE; |
7c0bf99b SL |
49 | int timer(); |
50 | int cleanup(); | |
6db0b3a4 BJ |
51 | |
52 | #define tprintf if (trace) printf | |
7c0bf99b | 53 | int trace = 0; |
6d0df65e | 54 | FILE *ftrace; |
7c0bf99b | 55 | |
6db0b3a4 BJ |
56 | char packet[MAXPACKETSIZE+1]; |
57 | struct rip *msg = (struct rip *)packet; | |
7c0bf99b | 58 | |
d0188ba8 | 59 | struct in_addr inet_makeaddr(); |
e0a3d4f9 SL |
60 | struct interface *if_ifwithaddr(), *if_ifwithnet(); |
61 | extern char *malloc(), *sys_errlist[]; | |
c5f8709e | 62 | extern int errno, exit(); |
6db0b3a4 BJ |
63 | char **argv0; |
64 | ||
baa2dcc8 | 65 | int sendmsg(), supply(); |
7c0bf99b SL |
66 | |
67 | main(argc, argv) | |
68 | int argc; | |
69 | char *argv[]; | |
70 | { | |
71 | int cc; | |
72 | struct sockaddr from; | |
d0188ba8 | 73 | struct servent *sp; |
7c0bf99b | 74 | |
6db0b3a4 | 75 | argv0 = argv; |
6d0df65e SL |
76 | #ifndef DEBUG |
77 | if (fork()) | |
78 | exit(0); | |
79 | for (cc = 0; cc < 10; cc++) | |
80 | (void) close(cc); | |
81 | (void) open("/", 0); | |
82 | (void) dup2(0, 1); | |
83 | (void) dup2(0, 2); | |
84 | { int t = open("/dev/tty", 2); | |
85 | if (t >= 0) { | |
86 | ioctl(t, TIOCNOTTY, (char *)0); | |
87 | (void) close(t); | |
88 | } | |
7c0bf99b | 89 | } |
6d0df65e | 90 | #endif |
7c0bf99b | 91 | if (trace) { |
6d0df65e SL |
92 | ftrace = fopen("/etc/routerlog", "w"); |
93 | dup2(fileno(ftrace), 1); | |
94 | dup2(fileno(ftrace), 2); | |
7c0bf99b | 95 | } |
71535ddb SL |
96 | |
97 | /* | |
98 | * We use two sockets. One for which outgoing | |
99 | * packets are routed and for which they're not. | |
100 | * The latter allows us to delete routing table | |
101 | * entries in the kernel for network interfaces | |
102 | * attached to our host which we believe are down | |
103 | * while still polling it to see when/if it comes | |
104 | * back up. With the new ipc interface we'll be | |
105 | * able to specify ``don't route'' as an option | |
106 | * to send, but until then we utilize a second port. | |
107 | */ | |
d0188ba8 SL |
108 | sp = getservbyname("router", "udp"); |
109 | if (sp == 0) { | |
110 | fprintf(stderr, "routed: udp/router: unknown service\n"); | |
111 | exit(1); | |
112 | } | |
113 | routingaddr.sin_port = htons(sp->s_port); | |
114 | noroutingaddr.sin_port = htons(sp->s_port + 1); | |
7c0bf99b | 115 | again: |
baa2dcc8 | 116 | s = socket(SOCK_DGRAM, 0, &routingaddr, 0); |
7c0bf99b SL |
117 | if (s < 0) { |
118 | perror("socket"); | |
119 | sleep(30); | |
120 | goto again; | |
121 | } | |
74d7f201 | 122 | again2: |
baa2dcc8 | 123 | snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE); |
74d7f201 BJ |
124 | if (snoroute < 0) { |
125 | perror("socket"); | |
126 | sleep(30); | |
127 | goto again2; | |
128 | } | |
7c0bf99b | 129 | argv++, argc--; |
6db0b3a4 BJ |
130 | while (argc > 0 && **argv == '-') { |
131 | if (!strcmp(*argv, "-s") == 0) { | |
77df73ca | 132 | supplier = 1; |
6db0b3a4 BJ |
133 | argv++, argc--; |
134 | continue; | |
135 | } | |
136 | if (!strcmp(*argv, "-q") == 0) { | |
7c0bf99b | 137 | supplier = 0; |
6db0b3a4 BJ |
138 | argv++, argc--; |
139 | continue; | |
140 | } | |
141 | goto usage; | |
142 | } | |
143 | if (argc > 0) { | |
144 | usage: | |
e0a3d4f9 | 145 | fprintf(stderr, "usage: routed [ -sq ]\n"); |
6db0b3a4 | 146 | exit(1); |
7c0bf99b | 147 | } |
71535ddb SL |
148 | /* |
149 | * Collect an initial view of the world by | |
150 | * snooping in the kernel and the gateway kludge | |
151 | * file. Then, send a request packet on all | |
152 | * directly connected networks to find out what | |
153 | * everyone else thinks. | |
154 | */ | |
6db0b3a4 | 155 | rtinit(); |
71535ddb | 156 | gwkludge(); |
6db0b3a4 BJ |
157 | ifinit(); |
158 | if (supplier < 0) | |
159 | supplier = 0; | |
6db0b3a4 BJ |
160 | msg->rip_cmd = RIPCMD_REQUEST; |
161 | msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; | |
162 | msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; | |
163 | toall(sendmsg); | |
7c0bf99b | 164 | sigset(SIGALRM, timer); |
77df73ca | 165 | timer(); |
7c0bf99b | 166 | |
7c0bf99b SL |
167 | for (;;) { |
168 | cc = receive(s, &from, packet, sizeof (packet)); | |
169 | if (cc <= 0) { | |
170 | if (cc < 0 && errno != EINTR) | |
171 | perror("receive"); | |
172 | continue; | |
173 | } | |
174 | sighold(SIGALRM); | |
175 | rip_input(&from, cc); | |
176 | sigrelse(SIGALRM); | |
177 | } | |
178 | } | |
179 | ||
6db0b3a4 | 180 | rtinit() |
7c0bf99b | 181 | { |
3cec0c76 | 182 | register struct rthash *rh; |
7c0bf99b | 183 | |
6db0b3a4 BJ |
184 | for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) |
185 | rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; | |
186 | for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) | |
187 | rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; | |
7c0bf99b SL |
188 | } |
189 | ||
e0a3d4f9 | 190 | struct interface *ifnet; |
6db0b3a4 | 191 | |
71535ddb SL |
192 | /* |
193 | * Probe the kernel through /dev/kmem to find the network | |
194 | * interfaces which have configured themselves. If the | |
195 | * interface is present but not yet up (for example an | |
196 | * ARPANET IMP), set the lookforinterfaces flag so we'll | |
197 | * come back later and look again. | |
198 | */ | |
6db0b3a4 | 199 | ifinit() |
7c0bf99b | 200 | { |
e0a3d4f9 SL |
201 | struct interface *ifp; |
202 | struct ifnet ifs, *next; | |
e0a3d4f9 SL |
203 | |
204 | if (performnlist) { | |
205 | nlist("/vmunix", nl); | |
206 | if (nl[N_IFNET].n_value == 0) { | |
207 | printf("ifnet: not in namelist\n"); | |
208 | goto bad; | |
209 | } | |
210 | performnlist = 0; | |
7c0bf99b | 211 | } |
7c0bf99b | 212 | if (kmem < 0) { |
e0a3d4f9 SL |
213 | kmem = open("/dev/kmem", 0); |
214 | if (kmem < 0) { | |
215 | perror("/dev/kmem"); | |
216 | goto bad; | |
217 | } | |
77df73ca SL |
218 | } |
219 | if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || | |
6db0b3a4 BJ |
220 | read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) { |
221 | printf("ifnet: error reading kmem\n"); | |
222 | goto bad; | |
7c0bf99b | 223 | } |
e0a3d4f9 | 224 | lookforinterfaces = 0; |
6db0b3a4 | 225 | while (next) { |
6db0b3a4 | 226 | if (lseek(kmem, (long)next, 0) == -1 || |
e0a3d4f9 | 227 | read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) { |
6db0b3a4 BJ |
228 | perror("read"); |
229 | goto bad; | |
77df73ca | 230 | } |
e0a3d4f9 SL |
231 | next = ifs.if_next; |
232 | if ((ifs.if_flags & IFF_UP) == 0) { | |
233 | lookforinterfaces = 1; | |
6db0b3a4 | 234 | continue; |
e0a3d4f9 | 235 | } |
71535ddb | 236 | /* already known to us? */ |
e0a3d4f9 | 237 | if (if_ifwithaddr(&ifs.if_addr)) |
6db0b3a4 | 238 | continue; |
71535ddb | 239 | /* argh, this'll have to change sometime */ |
e0a3d4f9 SL |
240 | if (ifs.if_addr.sa_family != AF_INET) |
241 | continue; | |
71535ddb | 242 | /* no one cares about software loopback interfaces */ |
e0a3d4f9 SL |
243 | if (ifs.if_net == LOOPBACKNET) |
244 | continue; | |
245 | ifp = (struct interface *)malloc(sizeof (struct interface)); | |
246 | if (ifp == 0) { | |
247 | printf("routed: out of memory\n"); | |
248 | break; | |
249 | } | |
250 | /* | |
251 | * Count the # of directly connected networks | |
252 | * and point to point links which aren't looped | |
253 | * back to ourself. This is used below to | |
71535ddb | 254 | * decide if we should be a routing ``supplier''. |
e0a3d4f9 SL |
255 | */ |
256 | if ((ifs.if_flags & IFF_POINTOPOINT) == 0 || | |
257 | if_ifwithaddr(&ifs.if_dstaddr) == 0) | |
258 | externalinterfaces++; | |
259 | ifp->int_addr = ifs.if_addr; | |
260 | ifp->int_flags = ifs.if_flags | IFF_INTERFACE; | |
261 | /* this works because broadaddr overlaps dstaddr */ | |
262 | ifp->int_broadaddr = ifs.if_broadaddr; | |
263 | ifp->int_net = ifs.if_net; | |
264 | ifp->int_metric = 0; | |
265 | ifp->int_next = ifnet; | |
77df73ca | 266 | ifnet = ifp; |
6db0b3a4 BJ |
267 | addrouteforif(ifp); |
268 | } | |
e0a3d4f9 | 269 | if (externalinterfaces > 1 && supplier < 0) |
6db0b3a4 BJ |
270 | supplier = 1; |
271 | return; | |
272 | bad: | |
273 | sleep(60); | |
e0a3d4f9 | 274 | close(kmem), close(s), close(snoroute); |
6db0b3a4 BJ |
275 | execv("/etc/routed", argv0); |
276 | _exit(0177); | |
277 | } | |
74d7f201 | 278 | |
6db0b3a4 | 279 | addrouteforif(ifp) |
e0a3d4f9 | 280 | struct interface *ifp; |
6db0b3a4 BJ |
281 | { |
282 | struct sockaddr_in net; | |
283 | struct sockaddr *dst; | |
e0a3d4f9 | 284 | int state, metric; |
3259727e | 285 | struct rt_entry *rt; |
6db0b3a4 | 286 | |
e0a3d4f9 SL |
287 | if (ifp->int_flags & IFF_POINTOPOINT) |
288 | dst = &ifp->int_dstaddr; | |
6db0b3a4 BJ |
289 | else { |
290 | bzero((char *)&net, sizeof (net)); | |
291 | net.sin_family = AF_INET; | |
d0188ba8 | 292 | net.sin_addr = inet_makeaddr(ifp->int_net, INADDR_ANY); |
6db0b3a4 | 293 | dst = (struct sockaddr *)&net; |
7c0bf99b | 294 | } |
3259727e SL |
295 | rt = rtlookup(dst); |
296 | rtadd(dst, &ifp->int_addr, ifp->int_metric, | |
297 | ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE)); | |
298 | if (rt) | |
299 | rtdelete(rt); | |
74d7f201 BJ |
300 | } |
301 | ||
71535ddb SL |
302 | /* |
303 | * As a concession to the ARPANET we read a list of gateways | |
304 | * from /etc/gateways and add them to our tables. This file | |
305 | * exists at each ARPANET gateway and indicates a set of ``remote'' | |
306 | * gateways (i.e. a gateway which we can't immediately determine | |
307 | * if it's present or not as we can do for those directly connected | |
308 | * at the hardware level). If a gateway is marked ``passive'' | |
309 | * in the file, then we assume it doesn't have a routing process | |
310 | * of our design and simply assume it's always present. Those | |
311 | * not marked passive are treated as if they were directly | |
312 | * connected -- they're added into the interface list so we'll | |
313 | * send them routing updates. | |
314 | */ | |
6db0b3a4 | 315 | gwkludge() |
74d7f201 BJ |
316 | { |
317 | struct sockaddr_in dst, gate; | |
318 | FILE *fp; | |
d0188ba8 | 319 | char *type, *dname, *gname, *qual, buf[BUFSIZ]; |
e0a3d4f9 SL |
320 | struct interface *ifp; |
321 | int metric; | |
74d7f201 BJ |
322 | |
323 | fp = fopen("/etc/gateways", "r"); | |
324 | if (fp == NULL) | |
325 | return; | |
d0188ba8 SL |
326 | qual = buf; |
327 | dname = buf + 64; | |
328 | gname = buf + ((BUFSIZ - 64) / 3); | |
329 | type = buf + (((BUFSIZ - 64) * 2) / 3); | |
74d7f201 BJ |
330 | bzero((char *)&dst, sizeof (dst)); |
331 | bzero((char *)&gate, sizeof (gate)); | |
e0a3d4f9 | 332 | dst.sin_family = gate.sin_family = AF_INET; |
3bd9bc1e | 333 | /* format: {net | host} XX gateway XX metric DD [passive]\n */ |
e0a3d4f9 | 334 | #define readentry(fp) \ |
3bd9bc1e | 335 | fscanf((fp), "%s %s gateway %s metric %d %s\n", \ |
d0188ba8 | 336 | type, dname, gname, &metric, qual) |
6db0b3a4 | 337 | for (;;) { |
f64bc556 | 338 | struct hostent *host; |
d0188ba8 | 339 | struct netent *net; |
f64bc556 | 340 | |
e0a3d4f9 | 341 | if (readentry(fp) == EOF) |
6db0b3a4 | 342 | break; |
d0188ba8 SL |
343 | if (strcmp(type, "net") == 0) { |
344 | net = getnetbyname(dname); | |
345 | if (net == 0 || net->n_addrtype != AF_INET) | |
346 | continue; | |
347 | dst.sin_addr = inet_makeaddr(net->n_net, INADDR_ANY); | |
348 | } else if (strcmp(type, "host") == 0) { | |
349 | host = gethostbyname(dname); | |
350 | if (host == 0) | |
351 | continue; | |
352 | bcopy(host->h_addr, &dst.sin_addr, host->h_length); | |
353 | } else | |
485547bb | 354 | continue; |
f64bc556 SL |
355 | host = gethostbyname(gname); |
356 | if (host == 0) | |
485547bb | 357 | continue; |
f64bc556 | 358 | bcopy(host->h_addr, &gate.sin_addr, host->h_length); |
e0a3d4f9 SL |
359 | ifp = (struct interface *)malloc(sizeof (*ifp)); |
360 | bzero((char *)ifp, sizeof (*ifp)); | |
361 | ifp->int_flags = IFF_REMOTE; | |
362 | /* can't identify broadcast capability */ | |
d0188ba8 SL |
363 | ifp->int_net = inet_netof(dst.sin_addr); |
364 | if (strcmp(type, "host") == 0) { | |
e0a3d4f9 | 365 | ifp->int_flags |= IFF_POINTOPOINT; |
e0a3d4f9 | 366 | ifp->int_dstaddr = *((struct sockaddr *)&dst); |
c7b4f6e8 | 367 | } |
485547bb | 368 | if (strcmp(qual, "passive") == 0) |
e0a3d4f9 | 369 | ifp->int_flags |= IFF_PASSIVE; |
71535ddb SL |
370 | else |
371 | /* assume no duplicate entries */ | |
372 | externalinterfaces++; | |
e0a3d4f9 SL |
373 | ifp->int_addr = *((struct sockaddr *)&gate); |
374 | ifp->int_metric = metric; | |
375 | ifp->int_next = ifnet; | |
376 | ifnet = ifp; | |
377 | addrouteforif(ifp); | |
74d7f201 BJ |
378 | } |
379 | fclose(fp); | |
7c0bf99b SL |
380 | } |
381 | ||
71535ddb SL |
382 | /* |
383 | * Timer routine. Performs routing information supply | |
384 | * duties and manages timers on routing table entries. | |
385 | */ | |
6db0b3a4 | 386 | timer() |
7c0bf99b | 387 | { |
3cec0c76 | 388 | register struct rthash *rh; |
7c0bf99b | 389 | register struct rt_entry *rt; |
3cec0c76 | 390 | struct rthash *base = hosthash; |
71535ddb | 391 | int doinghost = 1, timetobroadcast; |
7c0bf99b | 392 | |
6db0b3a4 | 393 | timeval += TIMER_RATE; |
e0a3d4f9 SL |
394 | if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) |
395 | ifinit(); | |
71535ddb | 396 | timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0; |
6db0b3a4 | 397 | tprintf(">>> time %d >>>\n", timeval); |
7c0bf99b | 398 | again: |
6db0b3a4 BJ |
399 | for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { |
400 | rt = rh->rt_forw; | |
401 | for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { | |
71535ddb SL |
402 | /* |
403 | * We don't advance time on a routing entry for | |
404 | * a passive gateway or that for our only interface. | |
405 | * The latter is excused because we don't act as | |
406 | * a routing information supplier and hence would | |
407 | * time it out. This is fair as if it's down | |
408 | * we're cut off from the world anyway and it's | |
409 | * not likely we'll grow any new hardware in | |
410 | * the mean time. | |
411 | */ | |
412 | if (!(rt->rt_state & RTS_PASSIVE) && | |
413 | (supplier || !(rt->rt_state & RTS_INTERFACE))) | |
6db0b3a4 | 414 | rt->rt_timer += TIMER_RATE; |
6db0b3a4 BJ |
415 | if (rt->rt_timer >= EXPIRE_TIME) |
416 | rt->rt_metric = HOPCNT_INFINITY; | |
e0a3d4f9 SL |
417 | log("", rt); |
418 | if (rt->rt_timer >= GARBAGE_TIME) { | |
6db0b3a4 BJ |
419 | rt = rt->rt_back; |
420 | rtdelete(rt->rt_forw); | |
421 | continue; | |
422 | } | |
e0a3d4f9 SL |
423 | if (rt->rt_state & RTS_CHANGED) { |
424 | rt->rt_state &= ~RTS_CHANGED; | |
425 | /* don't send extraneous packets */ | |
71535ddb | 426 | if (!supplier || timetobroadcast) |
e0a3d4f9 | 427 | continue; |
6db0b3a4 BJ |
428 | log("broadcast", rt); |
429 | msg->rip_cmd = RIPCMD_RESPONSE; | |
430 | msg->rip_nets[0].rip_dst = rt->rt_dst; | |
431 | msg->rip_nets[0].rip_metric = | |
432 | min(rt->rt_metric+1, HOPCNT_INFINITY); | |
baa2dcc8 | 433 | toall(sendmsg); |
6db0b3a4 BJ |
434 | } |
435 | } | |
7c0bf99b SL |
436 | } |
437 | if (doinghost) { | |
77df73ca | 438 | doinghost = 0; |
6db0b3a4 | 439 | base = nethash; |
7c0bf99b SL |
440 | goto again; |
441 | } | |
71535ddb | 442 | if (timetobroadcast) |
6db0b3a4 BJ |
443 | toall(supply); |
444 | tprintf("<<< time %d <<<\n", timeval); | |
445 | alarm(TIMER_RATE); | |
7c0bf99b SL |
446 | } |
447 | ||
6db0b3a4 BJ |
448 | toall(f) |
449 | int (*f)(); | |
7c0bf99b | 450 | { |
e0a3d4f9 | 451 | register struct interface *ifp; |
7c0bf99b | 452 | register struct sockaddr *dst; |
7c0bf99b | 453 | |
e0a3d4f9 SL |
454 | for (ifp = ifnet; ifp; ifp = ifp->int_next) { |
455 | if (ifp->int_flags & IFF_PASSIVE) | |
7c0bf99b | 456 | continue; |
e0a3d4f9 SL |
457 | dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr : |
458 | ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr : | |
459 | &ifp->int_addr; | |
460 | (*f)(dst, ifp->int_flags & IFF_INTERFACE); | |
7c0bf99b SL |
461 | } |
462 | } | |
463 | ||
baa2dcc8 SL |
464 | /*ARGSUSED*/ |
465 | sendmsg(dst, dontroute) | |
6db0b3a4 | 466 | struct sockaddr *dst; |
baa2dcc8 | 467 | int dontroute; |
6db0b3a4 | 468 | { |
6db0b3a4 BJ |
469 | (*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip)); |
470 | } | |
471 | ||
71535ddb SL |
472 | /* |
473 | * Supply dst with the contents of the routing tables. | |
474 | * If this won't fit in one packet, chop it up into several. | |
475 | */ | |
baa2dcc8 SL |
476 | supply(dst, dontroute) |
477 | struct sockaddr *dst; | |
478 | int dontroute; | |
7c0bf99b | 479 | { |
baa2dcc8 | 480 | register struct rt_entry *rt; |
7c0bf99b | 481 | struct netinfo *n = msg->rip_nets; |
3cec0c76 | 482 | register struct rthash *rh; |
3cec0c76 | 483 | struct rthash *base = hosthash; |
77df73ca | 484 | int doinghost = 1, size; |
baa2dcc8 SL |
485 | int (*output)() = afswitch[dst->sa_family].af_output; |
486 | int sto = dontroute ? snoroute : s; | |
7c0bf99b | 487 | |
c7b4f6e8 | 488 | msg->rip_cmd = RIPCMD_RESPONSE; |
7c0bf99b SL |
489 | again: |
490 | for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) | |
491 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { | |
77df73ca SL |
492 | size = (char *)n - packet; |
493 | if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { | |
baa2dcc8 | 494 | (*output)(sto, dst, size); |
7c0bf99b SL |
495 | n = msg->rip_nets; |
496 | } | |
497 | n->rip_dst = rt->rt_dst; | |
77df73ca SL |
498 | n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); |
499 | n++; | |
7c0bf99b SL |
500 | } |
501 | if (doinghost) { | |
502 | doinghost = 0; | |
503 | base = nethash; | |
504 | goto again; | |
505 | } | |
77df73ca | 506 | if (n != msg->rip_nets) |
baa2dcc8 | 507 | (*output)(sto, dst, (char *)n - packet); |
7c0bf99b SL |
508 | } |
509 | ||
510 | /* | |
511 | * Handle an incoming routing packet. | |
512 | */ | |
513 | rip_input(from, size) | |
514 | struct sockaddr *from; | |
515 | int size; | |
516 | { | |
7c0bf99b SL |
517 | struct rt_entry *rt; |
518 | struct netinfo *n; | |
e0a3d4f9 | 519 | struct interface *ifp; |
6db0b3a4 | 520 | time_t t; |
baa2dcc8 SL |
521 | int newsize; |
522 | struct afswitch *afp; | |
7c0bf99b | 523 | |
baa2dcc8 SL |
524 | if (trace) { |
525 | if (msg->rip_cmd < RIPCMD_MAX) | |
526 | printf("%s from %x\n", ripcmds[msg->rip_cmd], | |
527 | ((struct sockaddr_in *)from)->sin_addr); | |
528 | else | |
529 | printf("%x from %x\n", msg->rip_cmd, | |
530 | ((struct sockaddr_in *)from)->sin_addr); | |
531 | } | |
532 | if (from->sa_family >= AF_MAX) | |
7c0bf99b | 533 | return; |
baa2dcc8 SL |
534 | afp = &afswitch[from->sa_family]; |
535 | switch (msg->rip_cmd) { | |
e6b5ed24 SL |
536 | |
537 | case RIPCMD_REQUEST: | |
baa2dcc8 SL |
538 | newsize = 0; |
539 | size -= 4 * sizeof (char); | |
540 | n = msg->rip_nets; | |
541 | while (size > 0) { | |
542 | if (size < sizeof (struct netinfo)) | |
543 | break; | |
544 | size -= sizeof (struct netinfo); | |
545 | ||
546 | /* | |
547 | * A single entry with sa_family == AF_UNSPEC and | |
548 | * metric ``infinity'' means ``all routes''. | |
549 | */ | |
550 | if (n->rip_dst.sa_family == AF_UNSPEC && | |
551 | n->rip_metric == HOPCNT_INFINITY && size == 0) { | |
c7b4f6e8 | 552 | supply(from, 0); |
baa2dcc8 SL |
553 | return; |
554 | } | |
555 | rt = rtlookup(&n->rip_dst); | |
556 | n->rip_metric = rt == 0 ? HOPCNT_INFINITY : | |
557 | min(rt->rt_metric+1, HOPCNT_INFINITY); | |
558 | n++, newsize += sizeof (struct netinfo); | |
559 | } | |
560 | if (newsize > 0) { | |
561 | msg->rip_cmd = RIPCMD_RESPONSE; | |
562 | newsize += sizeof (int); | |
563 | (*afp->af_output)(s, from, newsize); | |
564 | } | |
7c0bf99b | 565 | return; |
e6b5ed24 | 566 | |
6d0df65e | 567 | case RIPCMD_TRACEON: |
baa2dcc8 | 568 | if ((*afp->af_portcheck)(from) == 0) |
6d0df65e | 569 | return; |
6db0b3a4 BJ |
570 | if (trace) |
571 | return; | |
572 | packet[size] = '\0'; | |
573 | ftrace = fopen(msg->rip_tracefile, "a"); | |
574 | if (ftrace == NULL) | |
e6b5ed24 | 575 | return; |
6db0b3a4 BJ |
576 | (void) dup2(fileno(ftrace), 1); |
577 | (void) dup2(fileno(ftrace), 2); | |
578 | trace = 1; | |
579 | t = time(0); | |
580 | printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); | |
7c0bf99b | 581 | return; |
7c0bf99b | 582 | |
6db0b3a4 BJ |
583 | case RIPCMD_TRACEOFF: |
584 | /* verify message came from a priviledged port */ | |
baa2dcc8 | 585 | if ((*afp->af_portcheck)(from) == 0) |
6db0b3a4 | 586 | return; |
6d0df65e SL |
587 | if (!trace) |
588 | return; | |
589 | t = time(0); | |
590 | printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); | |
591 | fflush(stdout), fflush(stderr); | |
592 | if (ftrace) | |
593 | fclose(ftrace); | |
594 | (void) close(1), (void) close(2); | |
595 | trace = 0; | |
596 | return; | |
6d0df65e | 597 | |
6db0b3a4 BJ |
598 | case RIPCMD_RESPONSE: |
599 | /* verify message came from a router */ | |
baa2dcc8 | 600 | if ((*afp->af_portmatch)(from) == 0) |
6db0b3a4 | 601 | return; |
baa2dcc8 | 602 | (*afp->af_canon)(from); |
6db0b3a4 BJ |
603 | /* are we talking to ourselves? */ |
604 | ifp = if_ifwithaddr(from); | |
605 | if (ifp) { | |
e0a3d4f9 SL |
606 | rt = rtfind(from); |
607 | if (rt == 0) | |
6db0b3a4 | 608 | addrouteforif(ifp); |
e0a3d4f9 SL |
609 | else |
610 | rt->rt_timer = 0; | |
6db0b3a4 BJ |
611 | return; |
612 | } | |
613 | size -= 4 * sizeof (char); | |
614 | n = msg->rip_nets; | |
615 | for (; size > 0; size -= sizeof (struct netinfo), n++) { | |
616 | if (size < sizeof (struct netinfo)) | |
617 | break; | |
618 | if (n->rip_metric >= HOPCNT_INFINITY) | |
619 | continue; | |
620 | tprintf("dst %x hc %d...", | |
621 | ((struct sockaddr_in *)&n->rip_dst)->sin_addr, | |
622 | n->rip_metric); | |
623 | rt = rtlookup(&n->rip_dst); | |
624 | if (rt == 0) { | |
625 | rtadd(&n->rip_dst, from, n->rip_metric, 0); | |
6db0b3a4 BJ |
626 | continue; |
627 | } | |
628 | tprintf("ours: gate %x hc %d timer %d\n", | |
629 | ((struct sockaddr_in *)&rt->rt_router)->sin_addr, | |
630 | rt->rt_metric, rt->rt_timer); | |
baa2dcc8 | 631 | |
6db0b3a4 | 632 | /* |
71535ddb | 633 | * Update if from gateway, shorter, or getting |
baa2dcc8 | 634 | * stale and equivalent. |
6db0b3a4 BJ |
635 | */ |
636 | if (equal(from, &rt->rt_router) || | |
637 | n->rip_metric < rt->rt_metric || | |
638 | (rt->rt_timer > (EXPIRE_TIME/2) && | |
639 | rt->rt_metric == n->rip_metric)) { | |
640 | rtchange(rt, from, n->rip_metric); | |
641 | rt->rt_timer = 0; | |
642 | } | |
643 | } | |
644 | return; | |
645 | } | |
e0a3d4f9 | 646 | tprintf("bad packet, cmd=%x\n", msg->rip_cmd); |
3cec0c76 SL |
647 | } |
648 | ||
71535ddb SL |
649 | /* |
650 | * Lookup dst in the tables for an exact match. | |
651 | */ | |
7c0bf99b SL |
652 | struct rt_entry * |
653 | rtlookup(dst) | |
654 | struct sockaddr *dst; | |
655 | { | |
656 | register struct rt_entry *rt; | |
3cec0c76 | 657 | register struct rthash *rh; |
eae14a37 | 658 | register int hash; |
7c0bf99b | 659 | struct afhash h; |
eae14a37 SL |
660 | int doinghost = 1; |
661 | ||
662 | if (dst->sa_family >= AF_MAX) | |
663 | return (0); | |
664 | (*afswitch[dst->sa_family].af_hash)(dst, &h); | |
665 | hash = h.afh_hosthash; | |
666 | rh = &hosthash[hash % ROUTEHASHSIZ]; | |
667 | again: | |
668 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { | |
e0a3d4f9 | 669 | if (rt->rt_hash != hash) |
eae14a37 SL |
670 | continue; |
671 | if (equal(&rt->rt_dst, dst)) | |
672 | return (rt); | |
673 | } | |
674 | if (doinghost) { | |
675 | doinghost = 0; | |
676 | hash = h.afh_nethash; | |
677 | rh = &nethash[hash % ROUTEHASHSIZ]; | |
678 | goto again; | |
679 | } | |
680 | return (0); | |
681 | } | |
682 | ||
71535ddb SL |
683 | /* |
684 | * Find a route to dst as the kernel would. | |
685 | */ | |
eae14a37 | 686 | struct rt_entry * |
e0a3d4f9 | 687 | rtfind(dst) |
eae14a37 SL |
688 | struct sockaddr *dst; |
689 | { | |
690 | register struct rt_entry *rt; | |
691 | register struct rthash *rh; | |
692 | register int hash; | |
693 | struct afhash h; | |
694 | int af = dst->sa_family; | |
695 | int doinghost = 1, (*match)(); | |
7c0bf99b SL |
696 | |
697 | if (af >= AF_MAX) | |
698 | return (0); | |
699 | (*afswitch[af].af_hash)(dst, &h); | |
700 | hash = h.afh_hosthash; | |
701 | rh = &hosthash[hash % ROUTEHASHSIZ]; | |
702 | ||
703 | again: | |
704 | for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { | |
705 | if (rt->rt_hash != hash) | |
706 | continue; | |
707 | if (doinghost) { | |
708 | if (equal(&rt->rt_dst, dst)) | |
709 | return (rt); | |
710 | } else { | |
711 | if (rt->rt_dst.sa_family == af && | |
712 | (*match)(&rt->rt_dst, dst)) | |
713 | return (rt); | |
714 | } | |
715 | } | |
716 | if (doinghost) { | |
717 | doinghost = 0; | |
718 | hash = h.afh_nethash; | |
7c0bf99b | 719 | rh = &nethash[hash % ROUTEHASHSIZ]; |
eae14a37 | 720 | match = afswitch[af].af_netmatch; |
7c0bf99b SL |
721 | goto again; |
722 | } | |
723 | return (0); | |
724 | } | |
725 | ||
baa2dcc8 | 726 | rtadd(dst, gate, metric, state) |
7c0bf99b | 727 | struct sockaddr *dst, *gate; |
baa2dcc8 | 728 | int metric, state; |
7c0bf99b SL |
729 | { |
730 | struct afhash h; | |
731 | register struct rt_entry *rt; | |
3cec0c76 | 732 | struct rthash *rh; |
7c0bf99b SL |
733 | int af = dst->sa_family, flags, hash; |
734 | ||
735 | if (af >= AF_MAX) | |
736 | return; | |
737 | (*afswitch[af].af_hash)(dst, &h); | |
738 | flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; | |
739 | if (flags & RTF_HOST) { | |
740 | hash = h.afh_hosthash; | |
741 | rh = &hosthash[hash % ROUTEHASHSIZ]; | |
742 | } else { | |
743 | hash = h.afh_nethash; | |
744 | rh = &nethash[hash % ROUTEHASHSIZ]; | |
745 | } | |
746 | rt = (struct rt_entry *)malloc(sizeof (*rt)); | |
747 | if (rt == 0) | |
748 | return; | |
749 | rt->rt_hash = hash; | |
750 | rt->rt_dst = *dst; | |
74d7f201 | 751 | rt->rt_router = *gate; |
7c0bf99b SL |
752 | rt->rt_metric = metric; |
753 | rt->rt_timer = 0; | |
baa2dcc8 | 754 | rt->rt_flags = RTF_UP | flags; |
e0a3d4f9 | 755 | rt->rt_state = state | RTS_CHANGED; |
74d7f201 BJ |
756 | rt->rt_ifp = if_ifwithnet(&rt->rt_router); |
757 | if (metric) | |
758 | rt->rt_flags |= RTF_GATEWAY; | |
7c0bf99b | 759 | insque(rt, rh); |
77df73ca | 760 | log("add", rt); |
e0a3d4f9 SL |
761 | if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) |
762 | tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); | |
7c0bf99b SL |
763 | } |
764 | ||
7c0bf99b SL |
765 | rtchange(rt, gate, metric) |
766 | struct rt_entry *rt; | |
767 | struct sockaddr *gate; | |
768 | short metric; | |
769 | { | |
baa2dcc8 | 770 | int doioctl = 0, metricchanged = 0; |
e0a3d4f9 | 771 | struct rtentry oldroute; |
7c0bf99b | 772 | |
e0a3d4f9 | 773 | if (!equal(&rt->rt_router, gate)) |
baa2dcc8 | 774 | doioctl++; |
7c0bf99b | 775 | if (metric != rt->rt_metric) { |
baa2dcc8 | 776 | metricchanged++; |
7c0bf99b | 777 | rt->rt_metric = metric; |
7c0bf99b | 778 | } |
e0a3d4f9 SL |
779 | if (doioctl || metricchanged) { |
780 | log("change", rt); | |
781 | rt->rt_state |= RTS_CHANGED; | |
782 | } | |
baa2dcc8 | 783 | if (doioctl) { |
e0a3d4f9 | 784 | oldroute = rt->rt_rt; |
baa2dcc8 | 785 | rt->rt_router = *gate; |
e0a3d4f9 SL |
786 | if (install) { |
787 | if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) | |
788 | tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); | |
789 | if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) | |
790 | tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); | |
791 | } | |
baa2dcc8 | 792 | } |
7c0bf99b SL |
793 | } |
794 | ||
7c0bf99b SL |
795 | rtdelete(rt) |
796 | struct rt_entry *rt; | |
797 | { | |
77df73ca | 798 | log("delete", rt); |
74d7f201 | 799 | if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) |
e0a3d4f9 | 800 | tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); |
7c0bf99b SL |
801 | remque(rt); |
802 | free((char *)rt); | |
803 | } | |
804 | ||
7c0bf99b SL |
805 | log(operation, rt) |
806 | char *operation; | |
807 | struct rt_entry *rt; | |
808 | { | |
7c0bf99b | 809 | struct sockaddr_in *dst, *gate; |
e6b5ed24 | 810 | static struct bits { |
7c0bf99b SL |
811 | int t_bits; |
812 | char *t_name; | |
e6b5ed24 | 813 | } flagbits[] = { |
7c0bf99b | 814 | { RTF_UP, "UP" }, |
74d7f201 | 815 | { RTF_GATEWAY, "GATEWAY" }, |
7c0bf99b | 816 | { RTF_HOST, "HOST" }, |
e6b5ed24 SL |
817 | { 0 } |
818 | }, statebits[] = { | |
74d7f201 | 819 | { RTS_PASSIVE, "PASSIVE" }, |
e0a3d4f9 SL |
820 | { RTS_REMOTE, "REMOTE" }, |
821 | { RTS_INTERFACE,"INTERFACE" }, | |
822 | { RTS_CHANGED, "CHANGED" }, | |
7c0bf99b SL |
823 | { 0 } |
824 | }; | |
e6b5ed24 | 825 | register struct bits *p; |
7c0bf99b SL |
826 | register int first; |
827 | char *cp; | |
828 | ||
77df73ca SL |
829 | if (trace == 0) |
830 | return; | |
7c0bf99b SL |
831 | printf("%s ", operation); |
832 | dst = (struct sockaddr_in *)&rt->rt_dst; | |
74d7f201 | 833 | gate = (struct sockaddr_in *)&rt->rt_router; |
e0a3d4f9 SL |
834 | printf("dst %x, router %x, metric %d, flags", dst->sin_addr, |
835 | gate->sin_addr, rt->rt_metric); | |
e6b5ed24 SL |
836 | cp = " %s"; |
837 | for (first = 1, p = flagbits; p->t_bits > 0; p++) { | |
7c0bf99b SL |
838 | if ((rt->rt_flags & p->t_bits) == 0) |
839 | continue; | |
840 | printf(cp, p->t_name); | |
841 | if (first) { | |
842 | cp = "|%s"; | |
843 | first = 0; | |
844 | } | |
845 | } | |
e6b5ed24 SL |
846 | printf(" state"); |
847 | cp = " %s"; | |
848 | for (first = 1, p = statebits; p->t_bits > 0; p++) { | |
849 | if ((rt->rt_state & p->t_bits) == 0) | |
850 | continue; | |
851 | printf(cp, p->t_name); | |
852 | if (first) { | |
853 | cp = "|%s"; | |
854 | first = 0; | |
855 | } | |
856 | } | |
7c0bf99b SL |
857 | putchar('\n'); |
858 | } | |
77df73ca | 859 | |
e0a3d4f9 | 860 | struct interface * |
77df73ca SL |
861 | if_ifwithaddr(addr) |
862 | struct sockaddr *addr; | |
863 | { | |
e0a3d4f9 | 864 | register struct interface *ifp; |
77df73ca SL |
865 | |
866 | #define same(a1, a2) \ | |
867 | (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) | |
e0a3d4f9 SL |
868 | for (ifp = ifnet; ifp; ifp = ifp->int_next) { |
869 | if (ifp->int_flags & IFF_REMOTE) | |
870 | continue; | |
871 | if (ifp->int_addr.sa_family != addr->sa_family) | |
77df73ca | 872 | continue; |
e0a3d4f9 | 873 | if (same(&ifp->int_addr, addr)) |
77df73ca | 874 | break; |
e0a3d4f9 SL |
875 | if ((ifp->int_flags & IFF_BROADCAST) && |
876 | same(&ifp->int_broadaddr, addr)) | |
77df73ca SL |
877 | break; |
878 | } | |
879 | return (ifp); | |
880 | #undef same | |
881 | } | |
882 | ||
e0a3d4f9 | 883 | struct interface * |
77df73ca SL |
884 | if_ifwithnet(addr) |
885 | register struct sockaddr *addr; | |
886 | { | |
e0a3d4f9 | 887 | register struct interface *ifp; |
77df73ca SL |
888 | register int af = addr->sa_family; |
889 | register int (*netmatch)(); | |
890 | ||
891 | if (af >= AF_MAX) | |
892 | return (0); | |
893 | netmatch = afswitch[af].af_netmatch; | |
e0a3d4f9 SL |
894 | for (ifp = ifnet; ifp; ifp = ifp->int_next) { |
895 | if (ifp->int_flags & IFF_REMOTE) | |
896 | continue; | |
897 | if (af != ifp->int_addr.sa_family) | |
77df73ca | 898 | continue; |
e0a3d4f9 | 899 | if ((*netmatch)(addr, &ifp->int_addr)) |
77df73ca SL |
900 | break; |
901 | } | |
902 | return (ifp); | |
903 | } |