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