input.c revision 1.17 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