xref: /netbsd-src/sbin/routed/input.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
1 /*	$NetBSD: input.c,v 1.17 1996/08/10 01:29:17 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #if !defined(lint) && !defined(sgi)
37 #if 0
38 static char sccsid[] = "@(#)input.c	8.1 (Berkeley) 6/5/93";
39 #else
40 static char rcsid[] = "$NetBSD: input.c,v 1.17 1996/08/10 01:29:17 thorpej Exp $";
41 #endif
42 #endif /* not lint */
43 
44 #include "defs.h"
45 
46 static void input(struct sockaddr_in *, struct interface*, struct rip *, int);
47 static void input_route(struct interface *, naddr,
48 			naddr, naddr, naddr, struct netinfo *);
49 
50 
51 /* process RIP input
52  */
53 void
54 read_rip(int sock,
55 	 struct interface *ifp)
56 {
57 	struct sockaddr_in from;
58 	int fromlen, cc;
59 	union pkt_buf inbuf;
60 
61 
62 	for (;;) {
63 		fromlen = sizeof(from);
64 		cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0,
65 			      (struct sockaddr*)&from, &fromlen);
66 		if (cc <= 0) {
67 			if (cc < 0 && errno != EWOULDBLOCK)
68 				LOGERR("recvfrom(rip)");
69 			break;
70 		}
71 		if (fromlen != sizeof(struct sockaddr_in))
72 			logbad(1,"impossible recvfrom(rip) fromlen=%d",
73 			       fromlen);
74 
75 		input(&from,
76 		      (ifp != 0) ? ifp : iflookup(from.sin_addr.s_addr),
77 		      &inbuf.rip, cc);
78 	}
79 }
80 
81 
82 /* Process a RIP packet
83  */
84 static void
85 input(struct sockaddr_in *from,		/* received from this IP address */
86       struct interface *ifp,
87       struct rip *rip,
88       int size)
89 {
90 #	define FROM_NADDR from->sin_addr.s_addr
91 	static naddr use_auth, bad_len, bad_mask;
92 	static naddr unk_router, bad_router, bad_nhop;
93 
94 	struct rt_entry *rt;
95 	struct netinfo *n, *lim;
96 	struct interface *ifp1;
97 	naddr gate, mask, v1_mask, dst, ddst_h;
98 	int i;
99 
100 
101 	if (ifp != 0)
102 		ifp->int_state |= IS_ACTIVE;
103 
104 	trace_rip("Recv", "from", from, ifp, rip, size);
105 
106 	if (rip->rip_vers == 0) {
107 		if (from->sin_addr.s_addr != bad_router)
108 			msglog("RIP version 0, cmd %d, packet received"
109 			       " from %s",
110 			       rip->rip_cmd, naddr_ntoa(FROM_NADDR));
111 		bad_router = from->sin_addr.s_addr;
112 		return;
113 	}
114 	if (size > MAXPACKETSIZE) {
115 		if (from->sin_addr.s_addr != bad_router)
116 			msglog("packet at least %d bytes too long received"
117 			       " from %s",
118 			       size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR));
119 		bad_router = from->sin_addr.s_addr;
120 		return;
121 	}
122 
123 	n = rip->rip_nets;
124 	lim = (struct netinfo *)((char*)rip + size);
125 
126 	/* Notice authentication.
127 	 * As required by section 4.2 in RFC 1723, discard authenticated
128 	 * RIPv2 messages, but only if configured for that silliness.
129 	 *
130 	 * RIPv2 authentication is lame, since snooping on the wire makes
131 	 * its simple passwords evident.  Also, why authenticate queries?
132 	 * Why should a RIPv2 implementation with authentication disabled
133 	 * not be able to listen to RIPv2 packets with authenication, while
134 	 * RIPv1 systems will listen?  Crazy!
135 	 */
136 	if (!auth_ok
137 	    && rip->rip_vers >= RIPv2
138 	    && n < lim && n->n_family == RIP_AF_AUTH) {
139 		if (from->sin_addr.s_addr != use_auth)
140 			msglog("RIPv2 message with authentication"
141 			       " from %s discarded",
142 			       naddr_ntoa(FROM_NADDR));
143 		use_auth = from->sin_addr.s_addr;
144 		trace_pkt("discard authenticated RIPv2 message\n");
145 		return;
146 	}
147 
148 	switch (rip->rip_cmd) {
149 	case RIPCMD_REQUEST:
150 		/* did the request come from a router?
151 		 */
152 		if (from->sin_port == htons(RIP_PORT)) {
153 			/* yes, ignore it if RIP is off so that it does not
154 			 * depend on us.
155 			 */
156 			if (rip_sock < 0) {
157 				trace_pkt("ignore request while RIP off\n");
158 				return;
159 			}
160 
161 			/* Ignore the request if we talking to ourself
162 			 * (and not a remote gateway).
163 			 */
164 			if (ifwithaddr(FROM_NADDR, 0, 0) != 0) {
165 				trace_pkt("discard our own RIP request\n");
166 				return;
167 			}
168 		}
169 
170 		/* According to RFC 1723, we should ignore unathenticated
171 		 * queries.  That is too silly to bother with.  Sheesh!
172 		 * Are forwarding tables supposed to be secret?  When
173 		 * a bad guy can infer them with test traffic?
174 		 * Maybe on firewalls you'd care, but not enough to
175 		 * give up the diagnostic facilities of remote probing.
176 		 */
177 
178 		if (n >= lim
179 		    || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
180 			if (from->sin_addr.s_addr != bad_len)
181 				msglog("request of bad length (%d) from %s",
182 				       size, naddr_ntoa(FROM_NADDR));
183 			bad_len = from->sin_addr.s_addr;
184 		}
185 		for (; n < lim; n++) {
186 			n->n_metric = ntohl(n->n_metric);
187 
188 			/* A single entry with family RIP_AF_UNSPEC and
189 			 * metric HOPCNT_INFINITY means "all routes".
190 			 * We respond to routers only if we are acting
191 			 * as a supplier, or to anyone other than a router
192 			 * (i.e. a query).
193 			 *
194 			 * Answer a query from a stray program with all
195 			 * we know. Filter the answer to a query from a
196 			 * router in the about same way broadcasts are
197 			 * filtered.
198 			 *
199 			 * Only answer a router if we are a supplier
200 			 * to keep an unwary host that is just starting
201 			 * from picking us an a router.
202 			 */
203 			if (n->n_family == RIP_AF_UNSPEC
204 			    && n->n_metric == HOPCNT_INFINITY
205 			    && n == rip->rip_nets
206 			    && n+1 == lim) {
207 				if (from->sin_port != htons(RIP_PORT)) {
208 					/* query */
209 					supply(from, ifp,
210 					       OUT_QUERY, 0, rip->rip_vers);
211 				} else if (supplier) {
212 					supply(from, ifp,
213 					       OUT_UNICAST, 0, rip->rip_vers);
214 				}
215 				return;
216 			}
217 
218 			if (n->n_family != RIP_AF_INET) {
219 				if (from->sin_addr.s_addr != bad_router)
220 					msglog("request from %s"
221 					       " for unsupported (af %d) %s",
222 					       naddr_ntoa(FROM_NADDR),
223 					       ntohs(n->n_family),
224 					       naddr_ntoa(n->n_dst));
225 				bad_router = from->sin_addr.s_addr;
226 				return;
227 			}
228 
229 			dst = n->n_dst;
230 			if (!check_dst(dst)) {
231 				if (from->sin_addr.s_addr != bad_router)
232 					msglog("bad queried destination"
233 					       " %s from %s",
234 					       naddr_ntoa(dst),
235 					       naddr_ntoa(FROM_NADDR));
236 				bad_router = from->sin_addr.s_addr;
237 				return;
238 			}
239 
240 			if (rip->rip_vers == RIPv1
241 			    || 0 == (mask = ntohl(n->n_mask))
242 			    || 0 != (ntohl(dst) & ~mask))
243 				mask = ripv1_mask_host(dst,ifp);
244 
245 			rt = rtget(dst, mask);
246 			if (!rt && dst != RIP_DEFAULT)
247 				rt = rtfind(n->n_dst);
248 
249 			n->n_tag = 0;
250 			n->n_nhop = 0;
251 			if (rip->rip_vers == RIPv1) {
252 				n->n_mask = 0;
253 			} else {
254 				n->n_mask = mask;
255 			}
256 			if (rt == 0) {
257 				n->n_metric = HOPCNT_INFINITY;
258 			} else {
259 				n->n_metric = rt->rt_metric+1;
260 				n->n_metric += (ifp!=0) ? ifp->int_metric : 1;
261 				if (n->n_metric > HOPCNT_INFINITY)
262 					n->n_metric = HOPCNT_INFINITY;
263 				if (rip->rip_vers != RIPv1) {
264 					n->n_tag = rt->rt_tag;
265 					if (ifp != 0
266 					    && on_net(rt->rt_gate,
267 						      ifp->int_net,
268 						      ifp->int_mask)
269 					    && rt->rt_gate != ifp->int_addr)
270 						n->n_nhop = rt->rt_gate;
271 				}
272 			}
273 			HTONL(n->n_metric);
274 		}
275 		/* Answer about specific routes.
276 		 * Only answer a router if we are a supplier
277 		 * to keep an unwary host that is just starting
278 		 * from picking us an a router.
279 		 */
280 		rip->rip_cmd = RIPCMD_RESPONSE;
281 		rip->rip_res1 = 0;
282 		if (rip->rip_vers != RIPv1)
283 			rip->rip_vers = RIPv2;
284 		if (from->sin_port != htons(RIP_PORT)) {
285 			/* query */
286 			(void)output(OUT_QUERY, from, ifp, rip, size);
287 		} else if (supplier) {
288 			(void)output(OUT_UNICAST, from, ifp, rip, size);
289 		}
290 		return;
291 
292 	case RIPCMD_TRACEON:
293 	case RIPCMD_TRACEOFF:
294 		/* verify message came from a privileged port */
295 		if (ntohs(from->sin_port) > IPPORT_RESERVED) {
296 			msglog("trace command from untrusted port on %s",
297 			       naddr_ntoa(FROM_NADDR));
298 			return;
299 		}
300 		if (ifp == 0) {
301 			msglog("trace command from unknown router %s",
302 			       naddr_ntoa(FROM_NADDR));
303 			return;
304 		}
305 		if (rip->rip_cmd == RIPCMD_TRACEON) {
306 			rip->rip_tracefile[size-4] = '\0';
307 			trace_on(rip->rip_tracefile, 0);
308 		} else {
309 			trace_off("tracing turned off by %s\n",
310 				  naddr_ntoa(FROM_NADDR));
311 		}
312 		return;
313 
314 	case RIPCMD_RESPONSE:
315 		if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
316 			if (from->sin_addr.s_addr != bad_len)
317 				msglog("response of bad length (%d) from %s",
318 				       size, naddr_ntoa(FROM_NADDR));
319 			bad_len = from->sin_addr.s_addr;
320 		}
321 
322 		/* verify message came from a router */
323 		if (from->sin_port != ntohs(RIP_PORT)) {
324 			trace_pkt("discard RIP response from unknown port\n");
325 			return;
326 		}
327 
328 		if (rip_sock < 0) {
329 			trace_pkt("discard response while RIP off\n");
330 			return;
331 		}
332 
333 		/* Are we talking to ourself or a remote gateway?
334 		 */
335 		ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
336 		if (ifp1) {
337 			if (ifp1->int_state & IS_REMOTE) {
338 				if (ifp1->int_state & IS_PASSIVE) {
339 					msglog("bogus input from %s on"
340 					       " supposedly passive %s",
341 					       naddr_ntoa(FROM_NADDR),
342 					       ifp1->int_name);
343 				} else {
344 					ifp1->int_act_time = now.tv_sec;
345 					if (if_ok(ifp1, "remote "))
346 						addrouteforif(ifp1);
347 				}
348 			} else {
349 				trace_pkt("discard our own RIP response\n");
350 			}
351 			return;
352 		}
353 
354 		/* Check the router from which message originated. We accept
355 		 * routing packets from routers directly connected via
356 		 * broadcast or point-to-point networks, and from
357 		 * those listed in /etc/gateways.
358 		 */
359 		if (!ifp) {
360 			if (from->sin_addr.s_addr != unk_router)
361 				msglog("packet from unknown router %s"
362 				       " or via unidentified interface",
363 				       naddr_ntoa(FROM_NADDR));
364 			unk_router = from->sin_addr.s_addr;
365 			return;
366 		}
367 		if (ifp->int_state & IS_PASSIVE) {
368 			trace_act("packet from %s via passive interface %s\n",
369 				  naddr_ntoa(FROM_NADDR),
370 				  ifp->int_name);
371 			return;
372 		}
373 
374 		/* Check required version
375 		 */
376 		if (((ifp->int_state & IS_NO_RIPV1_IN)
377 		     && rip->rip_vers == RIPv1)
378 		    || ((ifp->int_state & IS_NO_RIPV2_IN)
379 			&& rip->rip_vers != RIPv1)) {
380 			trace_pkt("discard RIPv%d response\n",
381 				  rip->rip_vers);
382 			return;
383 		}
384 
385 		/* Ignore routes via dead interface.
386 		 */
387 		if (ifp->int_state & IS_BROKE) {
388 			trace_pkt("discard response via broken interface %s\n",
389 				  ifp->int_name);
390 			return;
391 		}
392 
393 		/* Authenticate the packet.
394 		 */
395 		if (ifp->int_passwd[0] != '\0') {
396 			if ((n < lim) &&
397 			    (((struct netauth*)n)->a_type == RIP_AUTH_PW) &&
398 			    (bcmp(((struct netauth*)n)->au.au_pw,
399 			    ifp->int_passwd, sizeof(ifp->int_passwd)) == 0))
400 				goto auth_ok;
401 
402 			/*
403 			 * Authentication failed.
404 			 */
405 			if (from->sin_addr.s_addr != use_auth)
406 				msglog("missing authentication from %s",
407 				    naddr_ntoa(FROM_NADDR));
408 			use_auth = from->sin_addr.s_addr;
409 			return;
410 		}
411 
412  auth_ok:
413 
414 		for (; n < lim; n++) {
415 			if (n->n_family == RIP_AF_AUTH)
416 				continue;
417 
418 			NTOHL(n->n_metric);
419 			dst = n->n_dst;
420 			if (n->n_family != RIP_AF_INET
421 			    && (n->n_family != RIP_AF_UNSPEC
422 				|| dst != RIP_DEFAULT)) {
423 				if (from->sin_addr.s_addr != bad_router)
424 					msglog("route from %s to unsupported"
425 					       " address family %d,"
426 					       " destination %s",
427 					       naddr_ntoa(FROM_NADDR),
428 					       n->n_family,
429 					       naddr_ntoa(dst));
430 				bad_router = from->sin_addr.s_addr;
431 				continue;
432 			}
433 			if (!check_dst(dst)) {
434 				if (from->sin_addr.s_addr != bad_router)
435 					msglog("bad destination %s from %s",
436 					       naddr_ntoa(dst),
437 					       naddr_ntoa(FROM_NADDR));
438 				bad_router = from->sin_addr.s_addr;
439 				return;
440 			}
441 			if (n->n_metric == 0
442 			    || n->n_metric > HOPCNT_INFINITY) {
443 				if (from->sin_addr.s_addr != bad_router)
444 					msglog("bad metric %d from %s"
445 					       " for destination %s",
446 					       n->n_metric,
447 					       naddr_ntoa(FROM_NADDR),
448 					       naddr_ntoa(dst));
449 				bad_router = from->sin_addr.s_addr;
450 				return;
451 			}
452 
453 			/* Notice the next-hop.
454 			 */
455 			gate = from->sin_addr.s_addr;
456 			if (n->n_nhop != 0
457 			    && rip->rip_vers == RIPv2) {
458 				/* Ignore the route if it points to us */
459 				if (0 != ifwithaddr(n->n_nhop, 1, 0))
460 					continue;
461 
462 				/* Use it only if it is valid. */
463 				if (on_net(n->n_nhop,
464 					   ifp->int_net, ifp->int_mask)
465 				    && check_dst(n->n_nhop)) {
466 					gate = n->n_nhop;
467 				} else {
468 					if (bad_nhop != from->sin_addr.s_addr)
469 					    msglog("router %s to %s has"
470 						   " bad next hop %s",
471 						   naddr_ntoa(FROM_NADDR),
472 						   naddr_ntoa(dst),
473 						   naddr_ntoa(n->n_nhop));
474 					bad_nhop = from->sin_addr.s_addr;
475 				}
476 			}
477 
478 			if (rip->rip_vers == RIPv1
479 			    || 0 == (mask = ntohl(n->n_mask))) {
480 				mask = ripv1_mask_host(dst,ifp);
481 			} else if ((ntohl(dst) & ~mask) != 0) {
482 				if (bad_mask != from->sin_addr.s_addr) {
483 					msglog("router %s sent bad netmask"
484 					       " %#x with %s",
485 					       naddr_ntoa(FROM_NADDR),
486 					       mask,
487 					       naddr_ntoa(dst));
488 					bad_mask = from->sin_addr.s_addr;
489 				}
490 				continue;
491 			}
492 			if (rip->rip_vers == RIPv1)
493 				n->n_tag = 0;
494 
495 			/* Adjust metric according to incoming interface..
496 			 */
497 			n->n_metric += ifp->int_metric;
498 			if (n->n_metric > HOPCNT_INFINITY)
499 				n->n_metric = HOPCNT_INFINITY;
500 
501 			/* Recognize and ignore a default route we faked
502 			 * which is being sent back to us by a machine with
503 			 * broken split-horizon.
504 			 * Be a little more paranoid than that, and reject
505 			 * default routes with the same metric we advertised.
506 			 */
507 			if (ifp->int_d_metric != 0
508 			    && dst == RIP_DEFAULT
509 			    && n->n_metric >= ifp->int_d_metric)
510 				continue;
511 
512 			/* We can receive aggregated RIPv2 routes that must
513 			 * be broken down before they are transmitted by
514 			 * RIPv1 via an interface on a subnet.
515 			 * We might also receive the same routes aggregated
516 			 * via other RIPv2 interfaces.
517 			 * This could cause duplicate routes to be sent on
518 			 * the RIPv1 interfaces.  "Longest matching variable
519 			 * length netmasks" lets RIPv2 listeners understand,
520 			 * but breaking down the aggregated routes for RIPv1
521 			 * listeners can produce duplicate routes.
522 			 *
523 			 * Breaking down aggregated routes here bloats
524 			 * the daemon table, but does not hurt the kernel
525 			 * table, since routes are always aggregated for
526 			 * the kernel.
527 			 *
528 			 * Notice that this does not break down network
529 			 * routes corresponding to subnets.  This is part
530 			 * of the defense against RS_NET_SYN.
531 			 */
532 			if (have_ripv1_out
533 			    && (v1_mask = ripv1_mask_net(dst,0)) > mask
534 			    && (((rt = rtget(dst,mask)) == 0
535 				 || !(rt->rt_state & RS_NET_SYN)))) {
536 				ddst_h = v1_mask & -v1_mask;
537 				i = (v1_mask & ~mask)/ddst_h;
538 				if (i >= 1024) {
539 					/* Punt if we would have to generate
540 					 * an unreasonable number of routes.
541 					 */
542 #ifdef DEBUG
543 					msglog("accept %s from %s as-is"
544 					       " instead of as %d routes",
545 					       addrname(dst,mask,0),
546 					       naddr_ntoa(FROM_NADDR), i);
547 #endif
548 					i = 0;
549 				} else {
550 					mask = v1_mask;
551 				}
552 			} else {
553 				i = 0;
554 			}
555 
556 			for (;;) {
557 				input_route(ifp, FROM_NADDR,
558 					    dst, mask, gate, n);
559 				if (i-- == 0)
560 					break;
561 				dst = htonl(ntohl(dst) + ddst_h);
562 			}
563 		}
564 		break;
565 	}
566 }
567 
568 
569 /* Process a single input route.
570  */
571 static void
572 input_route(struct interface *ifp,
573 	    naddr from,
574 	    naddr dst,
575 	    naddr mask,
576 	    naddr gate,
577 	    struct netinfo *n)
578 {
579 	int i;
580 	struct rt_entry *rt;
581 	struct rt_spare *rts, *rts0;
582 	struct interface *ifp1;
583 	time_t new_time;
584 
585 
586 	/* See if the other guy is telling us to send our packets to him.
587 	 * Sometimes network routes arrive over a point-to-point link for
588 	 * the network containing the address(es) of the link.
589 	 *
590 	 * If our interface is broken, switch to using the other guy.
591 	 */
592 	ifp1 = ifwithaddr(dst, 1, 1);
593 	if (ifp1 != 0
594 	    && !(ifp1->int_state & IS_BROKE))
595 		return;
596 
597 	/* Look for the route in our table.
598 	 */
599 	rt = rtget(dst, mask);
600 
601 	/* Consider adding the route if we do not already have it.
602 	 */
603 	if (rt == 0) {
604 		/* Ignore unknown routes being poisoned.
605 		 */
606 		if (n->n_metric == HOPCNT_INFINITY)
607 			return;
608 
609 		rtadd(dst, mask, gate, from, n->n_metric, n->n_tag, 0, ifp);
610 		return;
611 	}
612 
613 	/* We already know about the route.  Consider this update.
614 	 *
615 	 * If (rt->rt_state & RS_NET_SYN), then this route
616 	 * is the same as a network route we have inferred
617 	 * for subnets we know, in order to tell RIPv1 routers
618 	 * about the subnets.
619 	 *
620 	 * It is impossible to tell if the route is coming
621 	 * from a distant RIPv2 router with the standard
622 	 * netmask because that router knows about the entire
623 	 * network, or if it is a round-about echo of a
624 	 * synthetic, RIPv1 network route of our own.
625 	 * The worst is that both kinds of routes might be
626 	 * received, and the bad one might have the smaller
627 	 * metric.  Partly solve this problem by faking the
628 	 * RIPv1 route with a metric that reflects the most
629 	 * distant part of the subnet.  Also never
630 	 * aggregate into such a route.  Also keep it
631 	 * around as long as the interface exists.
632 	 */
633 
634 	rts0 = rt->rt_spares;
635 	for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) {
636 		if (rts->rts_router == from)
637 			break;
638 		/* Note the worst slot to reuse,
639 		 * other than the current slot.
640 		 */
641 		if (rts0 == rt->rt_spares
642 		    || BETTER_LINK(rt, rts0, rts))
643 			rts0 = rts;
644 	}
645 	if (i != 0) {
646 		/* Found the router
647 		 */
648 		int old_metric = rts->rts_metric;
649 
650 		/* Keep poisoned routes around only long
651 		 * enough to pass the poison on.
652 		 */
653 		if (old_metric < HOPCNT_INFINITY)
654 			new_time = now.tv_sec;
655 
656 		/* If this is an update for the router we currently prefer,
657 		 * then note it.
658 		 */
659 		if (i == NUM_SPARES) {
660 			rtchange(rt,rt->rt_state, gate,rt->rt_router,
661 				 n->n_metric, n->n_tag, ifp, new_time, 0);
662 			/* If the route got worse, check for something better.
663 			 */
664 			if (n->n_metric > old_metric)
665 				rtswitch(rt, 0);
666 			return;
667 		}
668 
669 		/* This is an update for a spare route.
670 		 * Finished if the route is unchanged.
671 		 */
672 		if (rts->rts_gate == gate
673 		    && old_metric == n->n_metric
674 		    && rts->rts_tag == n->n_tag) {
675 			rts->rts_time = new_time;
676 			return;
677 		}
678 
679 	} else {
680 		/* The update is for a route we know about,
681 		 * but not from a familiar router.
682 		 */
683 		rts = rts0;
684 
685 		/* Save the route as a spare only if it has
686 		 * a better metric than our worst spare.
687 		 * This also ignores poisoned routes (those
688 		 * received with metric HOPCNT_INFINITY).
689 		 */
690 		if (n->n_metric >= rts->rts_metric)
691 			return;
692 
693 		new_time = now.tv_sec;
694 	}
695 
696 	trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time);
697 
698 	rts->rts_gate = gate;
699 	rts->rts_router = from;
700 	rts->rts_metric = n->n_metric;
701 	rts->rts_tag = n->n_tag;
702 	rts->rts_time = new_time;
703 	rts->rts_ifp = ifp;
704 
705 	/* try to switch to a better route */
706 	rtswitch(rt, rts);
707 }
708