1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* Copyright (c) 1990 Mentat Inc. */ 10 11 /* 12 * 13 * Copyright (c) 1983, 1989, 1991, 1993 14 * The Regents of the University of California. All rights reserved. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by the University of 27 * California, Berkeley and its contributors. 28 * 4. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 * @(#)route.c 8.6 (Berkeley) 4/28/95 45 * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93 46 */ 47 48 #pragma ident "%Z%%M% %I% %E% SMI" 49 50 #include <sys/param.h> 51 #include <sys/file.h> 52 #include <sys/socket.h> 53 #include <sys/ioctl.h> 54 #include <sys/stat.h> 55 #include <sys/stream.h> 56 #include <sys/sysmacros.h> 57 #include <sys/tihdr.h> 58 #include <sys/types.h> 59 #include <sys/ccompile.h> 60 61 #include <net/if.h> 62 #include <net/route.h> 63 #include <net/if_dl.h> 64 #include <netinet/in.h> 65 #include <arpa/inet.h> 66 #include <netdb.h> 67 #include <inet/mib2.h> 68 #include <inet/ip.h> 69 70 #include <limits.h> 71 #include <locale.h> 72 73 #include <errno.h> 74 #include <unistd.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <string.h> 78 #include <stropts.h> 79 #include <fcntl.h> 80 #include <stdarg.h> 81 #include <assert.h> 82 83 static struct keytab { 84 char *kt_cp; 85 int kt_i; 86 } keywords[] = { 87 #define K_ADD 1 88 {"add", K_ADD}, 89 #define K_BLACKHOLE 2 90 {"blackhole", K_BLACKHOLE}, 91 #define K_CHANGE 3 92 {"change", K_CHANGE}, 93 #define K_CLONING 4 94 {"cloning", K_CLONING}, 95 #define K_DELETE 5 96 {"delete", K_DELETE}, 97 #define K_DST 6 98 {"dst", K_DST}, 99 #define K_EXPIRE 7 100 {"expire", K_EXPIRE}, 101 #define K_FLUSH 8 102 {"flush", K_FLUSH}, 103 #define K_GATEWAY 9 104 {"gateway", K_GATEWAY}, 105 #define K_GET 11 106 {"get", K_GET}, 107 #define K_HOPCOUNT 12 108 {"hopcount", K_HOPCOUNT}, 109 #define K_HOST 13 110 {"host", K_HOST}, 111 #define K_IFA 14 112 {"ifa", K_IFA}, 113 #define K_IFACE 15 114 {"iface", K_IFACE}, 115 #define K_IFP 16 116 {"ifp", K_IFP}, 117 #define K_INET 17 118 {"inet", K_INET}, 119 #define K_INET6 18 120 {"inet6", K_INET6}, 121 #define K_INTERFACE 19 122 {"interface", K_INTERFACE}, 123 #define K_LINK 20 124 {"link", K_LINK}, 125 #define K_LOCK 21 126 {"lock", K_LOCK}, 127 #define K_LOCKREST 22 128 {"lockrest", K_LOCKREST}, 129 #define K_MASK 23 130 {"mask", K_MASK}, 131 #define K_MONITOR 24 132 {"monitor", K_MONITOR}, 133 #define K_MTU 25 134 {"mtu", K_MTU}, 135 #define K_NET 26 136 {"net", K_NET}, 137 #define K_NETMASK 27 138 {"netmask", K_NETMASK}, 139 #define K_NOSTATIC 28 140 {"nostatic", K_NOSTATIC}, 141 #define K_PRIVATE 29 142 {"private", K_PRIVATE}, 143 #define K_PROTO1 30 144 {"proto1", K_PROTO1}, 145 #define K_PROTO2 31 146 {"proto2", K_PROTO2}, 147 #define K_RECVPIPE 32 148 {"recvpipe", K_RECVPIPE}, 149 #define K_REJECT 33 150 {"reject", K_REJECT}, 151 #define K_RTT 34 152 {"rtt", K_RTT}, 153 #define K_RTTVAR 35 154 {"rttvar", K_RTTVAR}, 155 #define K_SA 36 156 {"sa", K_SA}, 157 #define K_SENDPIPE 37 158 {"sendpipe", K_SENDPIPE}, 159 #define K_SSTHRESH 38 160 {"ssthresh", K_SSTHRESH}, 161 #define K_STATIC 39 162 {"static", K_STATIC}, 163 #define K_XRESOLVE 40 164 {"xresolve", K_XRESOLVE}, 165 #define K_MULTIRT 41 166 {"multirt", K_MULTIRT}, 167 #define K_SETSRC 42 168 {"setsrc", K_SETSRC}, 169 #define K_SHOW 43 170 {"show", K_SHOW}, 171 {0, 0} 172 }; 173 174 /* 175 * Size of buffers used to hold command lines from the saved route file as 176 * well as error strings. 177 */ 178 #define BUF_SIZE 2048 179 180 typedef union sockunion { 181 struct sockaddr sa; 182 struct sockaddr_in sin; 183 struct sockaddr_dl sdl; 184 struct sockaddr_in6 sin6; 185 } su_t; 186 187 /* 188 * This structure represents the digested information from parsing arguments 189 * to route add, change, delete, and get. 190 * 191 */ 192 typedef struct rtcmd_irep { 193 int ri_cmd; 194 int ri_flags; 195 int ri_af; 196 ulong_t ri_inits; 197 struct rt_metrics ri_metrics; 198 int ri_addrs; 199 su_t ri_dst; 200 char *ri_dest_str; 201 su_t ri_src; 202 su_t ri_gate; 203 struct hostent *ri_gate_hp; 204 char *ri_gate_str; 205 su_t ri_mask; 206 su_t ri_ifa; 207 su_t ri_ifp; 208 char *ri_ifp_str; 209 } rtcmd_irep_t; 210 211 typedef struct mib_item_s { 212 struct mib_item_s *next_item; 213 long group; 214 long mib_id; 215 long length; 216 intmax_t *valp; 217 } mib_item_t; 218 219 typedef enum { 220 ADDR_TYPE_ANY, 221 ADDR_TYPE_HOST, 222 ADDR_TYPE_NET 223 } addr_type_t; 224 225 typedef enum { 226 SEARCH_MODE_NULL, 227 SEARCH_MODE_PRINT, 228 SEARCH_MODE_DEL 229 } search_mode_t; 230 231 static boolean_t args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, 232 char *cmd_string); 233 static void bprintf(FILE *fp, int b, char *s); 234 static boolean_t compare_rtcmd(rtcmd_irep_t *srch_rt, 235 rtcmd_irep_t *file_rt); 236 static void delRouteEntry(mib2_ipRouteEntry_t *rp, 237 mib2_ipv6RouteEntry_t *rp6, int seqno); 238 static void del_rtcmd_irep(rtcmd_irep_t *rcip); 239 static void flushroutes(int argc, char *argv[]); 240 static boolean_t getaddr(rtcmd_irep_t *rcip, int which, char *s, 241 addr_type_t atype); 242 static boolean_t in6_getaddr(char *s, struct sockaddr_in6 *sin6, 243 int *plenp, struct hostent **hpp); 244 static boolean_t in_getaddr(char *s, struct sockaddr_in *sin, 245 int *plenp, int which, struct hostent **hpp, addr_type_t atype, 246 rtcmd_irep_t *rcip); 247 static int in_getprefixlen(char *addr, int max_plen); 248 static boolean_t in_prefixlentomask(int prefixlen, int maxlen, 249 uchar_t *mask); 250 static void inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, 251 struct sockaddr_in *sin); 252 static in_addr_t inet_makesubnetmask(in_addr_t addr, in_addr_t mask); 253 static int keyword(const char *cp); 254 static void link_addr(const char *addr, struct sockaddr_dl *sdl); 255 static char *link_ntoa(const struct sockaddr_dl *sdl); 256 static mib_item_t *mibget(int sd); 257 static char *netname(struct sockaddr *sa); 258 static int newroute(char **argv); 259 static rtcmd_irep_t *new_rtcmd_irep(void); 260 static void pmsg_addrs(char *cp, int addrs); 261 static void pmsg_common(struct rt_msghdr *rtm); 262 static void print_getmsg(rtcmd_irep_t *req_rt, 263 struct rt_msghdr *rtm, int msglen); 264 static void print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, 265 boolean_t gw_good, boolean_t to_saved); 266 static void print_rtmsg(struct rt_msghdr *rtm, int msglen); 267 static void quit(char *s, int err) __NORETURN; 268 static char *routename(struct sockaddr *sa); 269 static void rtmonitor(int argc, char *argv[]); 270 static int rtmsg(rtcmd_irep_t *rcip); 271 static int salen(struct sockaddr *sa); 272 static void save_route(int argc, char **argv, int do_flush); 273 static void save_string(char **dst, char *src); 274 static int search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, 275 search_mode_t mode); 276 static void set_metric(rtcmd_irep_t *rcip, char *value, int key, 277 boolean_t lock); 278 static int show_saved_routes(int argc); 279 static void sockaddr(char *addr, struct sockaddr *sa); 280 static void sodump(su_t *su, char *which); 281 static void syntax_arg_missing(char *keyword); 282 static void syntax_bad_keyword(char *keyword); 283 static void syntax_error(char *err, ...); 284 static void usage(char *cp); 285 static void write_to_rtfile(FILE *fp, int argc, char **argv); 286 287 static pid_t pid; 288 static int s; 289 static boolean_t nflag; 290 static int af = AF_INET; 291 static boolean_t qflag, tflag; 292 static boolean_t verbose; 293 static boolean_t debugonly; 294 static boolean_t fflag; 295 static boolean_t update_table; 296 static boolean_t perm_flag; 297 static boolean_t early_v6_keyword; 298 static char perm_file_sfx[] = "/etc/inet/static_routes"; 299 static char *perm_file; 300 static char temp_file_sfx[] = "/etc/inet/static_routes.tmp"; 301 static char *temp_file; 302 static struct in6_addr in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 303 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 304 /* 305 * WARNING: 306 * This next variable indicates whether certain functions exit when an error 307 * is detected in the user input. Currently, exit_on_error is only set false 308 * in search_rtfile(), when argument are being parsed. Only those functions 309 * used by search_rtfile() to parse its arguments are designed to work in 310 * both modes. Take particular care in setting this false to ensure that any 311 * functions you call that might act on this flag properly return errors when 312 * exit_on_error is false. 313 */ 314 static int exit_on_error = B_TRUE; 315 316 static struct { 317 struct rt_msghdr m_rtm; 318 char m_space[512]; 319 } m_rtmsg; 320 321 /* 322 * Sizes of data structures extracted from the base mib. 323 * This allows the size of the tables entries to grow while preserving 324 * binary compatibility. 325 */ 326 static int ipRouteEntrySize; 327 static int ipv6RouteEntrySize; 328 329 #define ROUNDUP_LONG(a) \ 330 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long)) 331 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n))) 332 #define C(x) ((x) & 0xff) 333 334 /* 335 * return values from in_getprefixlen() 336 */ 337 #define BAD_ADDR -1 /* prefix is invalid */ 338 #define NO_PREFIX -2 /* no prefix was found */ 339 340 341 void 342 usage(char *cp) 343 { 344 if (cp != NULL) { 345 (void) fprintf(stderr, gettext("route: botched keyword: %s\n"), 346 cp); 347 } 348 (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] " 349 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n")); 350 exit(1); 351 /* NOTREACHED */ 352 } 353 354 /*PRINTFLIKE1*/ 355 void 356 syntax_error(char *err, ...) 357 { 358 va_list args; 359 360 if (exit_on_error) { 361 va_start(args, err); 362 (void) vfprintf(stderr, err, args); 363 va_end(args); 364 exit(1); 365 } 366 /* NOTREACHED */ 367 } 368 369 void 370 syntax_bad_keyword(char *keyword) 371 { 372 syntax_error(gettext("route: botched keyword: %s\n"), keyword); 373 } 374 375 void 376 syntax_arg_missing(char *keyword) 377 { 378 syntax_error(gettext("route: argument required following keyword %s\n"), 379 keyword); 380 } 381 382 void 383 quit(char *s, int sverrno) 384 { 385 (void) fprintf(stderr, "route: "); 386 if (s != NULL) 387 (void) fprintf(stderr, "%s: ", s); 388 (void) fprintf(stderr, "%s\n", strerror(sverrno)); 389 exit(sverrno); 390 /* NOTREACHED */ 391 } 392 393 int 394 main(int argc, char **argv) 395 { 396 extern int optind; 397 extern char *optarg; 398 int ch; 399 int rval; 400 size_t size; 401 const char *root_dir = NULL; 402 403 (void) setlocale(LC_ALL, ""); 404 405 #if !defined(TEXT_DOMAIN) 406 #define TEXT_DOMAIN "SYS_TEST" 407 #endif 408 (void) textdomain(TEXT_DOMAIN); 409 410 if (argc < 2) 411 usage((char *)NULL); 412 413 while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) { 414 switch (ch) { 415 case 'n': 416 nflag = B_TRUE; 417 break; 418 case 'q': 419 qflag = B_TRUE; 420 break; 421 case 'v': 422 verbose = B_TRUE; 423 break; 424 case 't': 425 tflag = B_TRUE; 426 break; 427 case 'd': 428 debugonly = B_TRUE; 429 break; 430 case 'f': 431 fflag = B_TRUE; 432 break; 433 case 'p': 434 perm_flag = B_TRUE; 435 break; 436 case 'R': 437 root_dir = optarg; 438 break; 439 case '?': 440 default: 441 usage((char *)NULL); 442 /* NOTREACHED */ 443 } 444 } 445 argc -= optind; 446 argv += optind; 447 448 pid = getpid(); 449 if (tflag) 450 s = open("/dev/null", O_WRONLY); 451 else 452 s = socket(PF_ROUTE, SOCK_RAW, 0); 453 if (s < 0) 454 quit("socket", errno); 455 456 /* 457 * Handle the -p and -R flags. The -R flag only applies 458 * when the -p flag is set. 459 */ 460 if (root_dir == NULL) { 461 perm_file = perm_file_sfx; 462 temp_file = temp_file_sfx; 463 } else { 464 size = strlen(root_dir) + sizeof (perm_file_sfx); 465 perm_file = malloc(size); 466 if (perm_file == NULL) 467 quit("malloc", errno); 468 (void) snprintf(perm_file, size, "%s%s", root_dir, 469 perm_file_sfx); 470 size = strlen(root_dir) + sizeof (temp_file_sfx); 471 temp_file = malloc(size); 472 if (temp_file == NULL) 473 quit("malloc", errno); 474 (void) snprintf(temp_file, size, "%s%s", root_dir, 475 temp_file_sfx); 476 } 477 /* 478 * Whether or not to act on the routing table. The only time the 479 * routing table is not modified is when both -p and -R are present. 480 */ 481 update_table = (!perm_flag || root_dir == NULL); 482 if (tflag) 483 perm_flag = 0; 484 485 if (fflag) { 486 /* 487 * Accept an address family keyword after the -f. Since the 488 * default address family is AF_INET, reassign af only for the 489 * other valid address families. 490 */ 491 if (*argv != NULL) { 492 switch (keyword(*argv)) { 493 case K_INET6: 494 af = AF_INET6; 495 early_v6_keyword = B_TRUE; 496 /* fallthrough */ 497 case K_INET: 498 /* Skip over the address family parameter. */ 499 argc--; 500 argv++; 501 break; 502 } 503 } 504 flushroutes(0, NULL); 505 } 506 507 if (*argv != NULL) { 508 switch (keyword(*argv)) { 509 case K_GET: 510 case K_CHANGE: 511 case K_ADD: 512 case K_DELETE: 513 rval = 0; 514 if (update_table) { 515 rval = newroute(argv); 516 } 517 if (perm_flag && (rval == 0 || rval == EEXIST || 518 rval == ESRCH)) { 519 save_route(argc, argv, B_FALSE); 520 return (0); 521 } 522 return (rval); 523 case K_SHOW: 524 if (perm_flag) { 525 return (show_saved_routes(argc)); 526 } else { 527 syntax_error(gettext( 528 "route: show command requires -p\n")); 529 } 530 /* NOTREACHED */ 531 case K_MONITOR: 532 rtmonitor(argc, argv); 533 /* NOTREACHED */ 534 535 case K_FLUSH: 536 flushroutes(argc, argv); 537 return (0); 538 } 539 } 540 if (!fflag) 541 usage(*argv); 542 return (0); 543 } 544 545 /* 546 * Purge all entries in the routing tables not 547 * associated with network interfaces. 548 */ 549 void 550 flushroutes(int argc, char *argv[]) 551 { 552 int seqno; 553 int sd; /* mib stream */ 554 mib_item_t *item; 555 mib2_ipRouteEntry_t *rp; 556 mib2_ipv6RouteEntry_t *rp6; 557 int oerrno; 558 int off = 0; 559 int on = 1; 560 561 if (argc > 1) { 562 argv++; 563 if (argc == 2 && **argv == '-') { 564 /* 565 * The address family (preceded by a dash) may be used 566 * to flush the routes of that particular family. 567 */ 568 switch (keyword(*argv + 1)) { 569 case K_INET: 570 af = AF_INET; 571 break; 572 case K_LINK: 573 af = AF_LINK; 574 break; 575 case K_INET6: 576 af = AF_INET6; 577 break; 578 default: 579 usage(*argv); 580 /* NOTREACHED */ 581 } 582 } else { 583 usage(*argv); 584 } 585 } 586 if (perm_flag) { 587 /* This flushes the persistent route file */ 588 save_route(0, NULL, B_TRUE); 589 } 590 if (!update_table) { 591 return; 592 } 593 594 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off, 595 sizeof (off)) < 0) 596 quit("setsockopt", errno); 597 598 sd = open("/dev/ip", O_RDWR); 599 oerrno = errno; 600 if (sd < 0) { 601 switch (errno) { 602 case EACCES: 603 (void) fprintf(stderr, 604 gettext("route: flush: insufficient privileges\n")); 605 exit(oerrno); 606 /* NOTREACHED */ 607 default: 608 quit(gettext("can't open mib stream"), oerrno); 609 /* NOTREACHED */ 610 } 611 } 612 if ((item = mibget(sd)) == NULL) 613 quit("mibget", errno); 614 if (verbose) { 615 (void) printf("Examining routing table from " 616 "T_SVR4_OPTMGMT_REQ\n"); 617 } 618 seqno = 0; /* ??? */ 619 switch (af) { 620 case AF_INET: 621 /* Extract ipRouteEntrySize */ 622 for (; item != NULL; item = item->next_item) { 623 if (item->mib_id != 0) 624 continue; 625 if (item->group == MIB2_IP) { 626 ipRouteEntrySize = 627 ((mib2_ip_t *)item->valp)->ipRouteEntrySize; 628 assert(IS_P2ALIGNED(ipRouteEntrySize, 629 sizeof (mib2_ipRouteEntry_t *))); 630 break; 631 } 632 } 633 if (ipRouteEntrySize == 0) { 634 (void) fprintf(stderr, 635 gettext("ipRouteEntrySize can't be determined.\n")); 636 exit(1); 637 } 638 for (; item != NULL; item = item->next_item) { 639 /* 640 * skip all the other trash that comes up the mib stream 641 */ 642 if (item->group != MIB2_IP || 643 item->mib_id != MIB2_IP_ROUTE) 644 continue; 645 for (rp = (mib2_ipRouteEntry_t *)item->valp; 646 (char *)rp < (char *)item->valp + item->length; 647 /* LINTED */ 648 rp = (mib2_ipRouteEntry_t *) 649 ((char *)rp + ipRouteEntrySize)) { 650 delRouteEntry(rp, NULL, seqno); 651 seqno++; 652 } 653 break; 654 } 655 break; 656 case AF_INET6: 657 /* Extract ipv6RouteEntrySize */ 658 for (; item != NULL; item = item->next_item) { 659 if (item->mib_id != 0) 660 continue; 661 if (item->group == MIB2_IP6) { 662 ipv6RouteEntrySize = 663 ((mib2_ipv6IfStatsEntry_t *)item->valp)-> 664 ipv6RouteEntrySize; 665 assert(IS_P2ALIGNED(ipv6RouteEntrySize, 666 sizeof (mib2_ipv6RouteEntry_t *))); 667 break; 668 } 669 } 670 if (ipv6RouteEntrySize == 0) { 671 (void) fprintf(stderr, gettext( 672 "ipv6RouteEntrySize cannot be determined.\n")); 673 exit(1); 674 } 675 for (; item != NULL; item = item->next_item) { 676 /* 677 * skip all the other trash that comes up the mib stream 678 */ 679 if (item->group != MIB2_IP6 || 680 item->mib_id != MIB2_IP6_ROUTE) 681 continue; 682 for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp; 683 (char *)rp6 < (char *)item->valp + item->length; 684 /* LINTED */ 685 rp6 = (mib2_ipv6RouteEntry_t *) 686 ((char *)rp6 + ipv6RouteEntrySize)) { 687 delRouteEntry(NULL, rp6, seqno); 688 seqno++; 689 } 690 break; 691 } 692 break; 693 } 694 695 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on, 696 sizeof (on)) < 0) 697 quit("setsockopt", errno); 698 } 699 700 /* 701 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or 702 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in 703 * order to facilitate the flushing of RTF_GATEWAY routes. 704 */ 705 static void 706 delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno) 707 { 708 char *cp; 709 int ire_type; 710 int rlen; 711 struct rt_msghdr *rtm; 712 struct sockaddr_in sin; 713 struct sockaddr_in6 sin6; 714 int slen; 715 716 if (rp != NULL) 717 ire_type = rp->ipRouteInfo.re_ire_type; 718 else 719 ire_type = rp6->ipv6RouteInfo.re_ire_type; 720 if (ire_type != IRE_DEFAULT && 721 ire_type != IRE_PREFIX && 722 ire_type != IRE_HOST && 723 ire_type != IRE_HOST_REDIRECT) 724 return; 725 726 rtm = &m_rtmsg.m_rtm; 727 (void) memset(rtm, 0, sizeof (m_rtmsg)); 728 rtm->rtm_type = RTM_DELETE; 729 rtm->rtm_seq = seqno; 730 rtm->rtm_flags |= RTF_GATEWAY; 731 rtm->rtm_version = RTM_VERSION; 732 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 733 cp = m_rtmsg.m_space; 734 if (rp != NULL) { 735 slen = sizeof (struct sockaddr_in); 736 if (rp->ipRouteMask == IP_HOST_MASK) 737 rtm->rtm_flags |= RTF_HOST; 738 (void) memset(&sin, 0, slen); 739 sin.sin_family = AF_INET; 740 sin.sin_addr.s_addr = rp->ipRouteDest; 741 (void) memmove(cp, &sin, slen); 742 cp += slen; 743 sin.sin_addr.s_addr = rp->ipRouteNextHop; 744 (void) memmove(cp, &sin, slen); 745 cp += slen; 746 sin.sin_addr.s_addr = rp->ipRouteMask; 747 (void) memmove(cp, &sin, slen); 748 cp += slen; 749 } else { 750 slen = sizeof (struct sockaddr_in6); 751 if (rp6->ipv6RoutePfxLength == IPV6_ABITS) 752 rtm->rtm_flags |= RTF_HOST; 753 (void) memset(&sin6, 0, slen); 754 sin6.sin6_family = AF_INET6; 755 sin6.sin6_addr = rp6->ipv6RouteDest; 756 (void) memmove(cp, &sin6, slen); 757 cp += slen; 758 sin6.sin6_addr = rp6->ipv6RouteNextHop; 759 (void) memmove(cp, &sin6, slen); 760 cp += slen; 761 (void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr)); 762 (void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS, 763 (uchar_t *)&sin6.sin6_addr.s6_addr); 764 (void) memmove(cp, &sin6, slen); 765 cp += slen; 766 } 767 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 768 if (debugonly) { 769 /* 770 * In debugonly mode, the routing socket message to delete the 771 * current entry is not actually sent. However if verbose is 772 * also set, the routing socket message that would have been 773 * is printed. 774 */ 775 if (verbose) 776 print_rtmsg(rtm, rtm->rtm_msglen); 777 return; 778 } 779 780 rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen); 781 if (rlen < (int)rtm->rtm_msglen) { 782 if (rlen < 0) { 783 (void) fprintf(stderr, 784 gettext("route: write to routing socket: %s\n"), 785 strerror(errno)); 786 } else { 787 (void) fprintf(stderr, gettext("route: write to " 788 "routing socket got only %d for rlen\n"), rlen); 789 } 790 return; 791 } 792 if (qflag) { 793 /* 794 * In quiet mode, nothing is printed at all (unless the write() 795 * itself failed. 796 */ 797 return; 798 } 799 if (verbose) { 800 print_rtmsg(rtm, rlen); 801 } else { 802 struct sockaddr *sa = (struct sockaddr *)(rtm + 1); 803 804 (void) printf("%-20.20s ", 805 rtm->rtm_flags & RTF_HOST ? routename(sa) : 806 netname(sa)); 807 /* LINTED */ 808 sa = (struct sockaddr *)(salen(sa) + (char *)sa); 809 (void) printf("%-20.20s ", routename(sa)); 810 (void) printf("done\n"); 811 } 812 } 813 814 /* 815 * Return the name of the host whose address is given. 816 */ 817 char * 818 routename(struct sockaddr *sa) 819 { 820 char *cp; 821 static char line[MAXHOSTNAMELEN + 1]; 822 struct hostent *hp = NULL; 823 static char domain[MAXHOSTNAMELEN + 1]; 824 static boolean_t first = B_TRUE; 825 struct in_addr in; 826 struct in6_addr in6; 827 int error_num; 828 ushort_t *s; 829 ushort_t *slim; 830 831 if (first) { 832 first = B_FALSE; 833 if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 834 (cp = strchr(domain, '.'))) 835 (void) strcpy(domain, cp + 1); 836 else 837 domain[0] = 0; 838 } 839 840 if (salen(sa) == 0) { 841 (void) strcpy(line, "default"); 842 return (line); 843 } 844 switch (sa->sa_family) { 845 846 case AF_INET: 847 /* LINTED */ 848 in = ((struct sockaddr_in *)sa)->sin_addr; 849 850 cp = NULL; 851 if (in.s_addr == INADDR_ANY) 852 cp = "default"; 853 if (cp == NULL && !nflag) { 854 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), 855 AF_INET); 856 if (hp != NULL) { 857 if (((cp = strchr(hp->h_name, '.')) != NULL) && 858 (strcmp(cp + 1, domain) == 0)) 859 *cp = 0; 860 cp = hp->h_name; 861 } 862 } 863 if (cp != NULL) { 864 (void) strncpy(line, cp, MAXHOSTNAMELEN); 865 line[MAXHOSTNAMELEN] = '\0'; 866 } else { 867 in.s_addr = ntohl(in.s_addr); 868 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), 869 C(in.s_addr >> 16), C(in.s_addr >> 8), 870 C(in.s_addr)); 871 } 872 break; 873 874 case AF_LINK: 875 return (link_ntoa((struct sockaddr_dl *)sa)); 876 877 case AF_INET6: 878 /* LINTED */ 879 in6 = ((struct sockaddr_in6 *)sa)->sin6_addr; 880 881 cp = NULL; 882 if (IN6_IS_ADDR_UNSPECIFIED(&in6)) 883 cp = "default"; 884 if (cp == NULL && !nflag) { 885 hp = getipnodebyaddr((char *)&in6, 886 sizeof (struct in6_addr), AF_INET6, &error_num); 887 if (hp != NULL) { 888 if (((cp = strchr(hp->h_name, '.')) != NULL) && 889 (strcmp(cp + 1, domain) == 0)) 890 *cp = 0; 891 cp = hp->h_name; 892 } 893 } 894 if (cp != NULL) { 895 (void) strncpy(line, cp, MAXHOSTNAMELEN); 896 line[MAXHOSTNAMELEN] = '\0'; 897 } else { 898 (void) inet_ntop(AF_INET6, (void *)&in6, line, 899 INET6_ADDRSTRLEN); 900 } 901 if (hp != NULL) 902 freehostent(hp); 903 904 break; 905 906 default: 907 s = (ushort_t *)sa; 908 909 slim = s + ((salen(sa) + 1) >> 1); 910 cp = line + sprintf(line, "(%d)", sa->sa_family); 911 912 while (++s < slim) /* start with sa->sa_data */ 913 cp += sprintf(cp, " %x", *s); 914 break; 915 } 916 return (line); 917 } 918 919 /* 920 * Return the name of the network whose address is given. 921 * The address is assumed to be that of a net or subnet, not a host. 922 */ 923 static char * 924 netname(struct sockaddr *sa) 925 { 926 char *cp = NULL; 927 static char line[MAXHOSTNAMELEN + 1]; 928 struct netent *np; 929 in_addr_t net, mask; 930 int subnetshift; 931 struct in_addr in; 932 ushort_t *s; 933 ushort_t *slim; 934 935 switch (sa->sa_family) { 936 937 case AF_INET: 938 /* LINTED */ 939 in = ((struct sockaddr_in *)sa)->sin_addr; 940 941 in.s_addr = ntohl(in.s_addr); 942 if (in.s_addr == INADDR_ANY) { 943 cp = "default"; 944 } else if (!nflag) { 945 if (IN_CLASSA(in.s_addr)) { 946 mask = IN_CLASSA_NET; 947 subnetshift = 8; 948 } else if (IN_CLASSB(in.s_addr)) { 949 mask = IN_CLASSB_NET; 950 subnetshift = 8; 951 } else { 952 mask = IN_CLASSC_NET; 953 subnetshift = 4; 954 } 955 /* 956 * If there are more bits than the standard mask 957 * would suggest, subnets must be in use. 958 * Guess at the subnet mask, assuming reasonable 959 * width subnet fields. 960 */ 961 while (in.s_addr &~ mask) 962 mask = (long)mask >> subnetshift; 963 net = in.s_addr & mask; 964 while ((mask & 1) == 0) 965 mask >>= 1, net >>= 1; 966 np = getnetbyaddr(net, AF_INET); 967 if (np != NULL) 968 cp = np->n_name; 969 } 970 if (cp != NULL) { 971 (void) strncpy(line, cp, MAXHOSTNAMELEN); 972 line[MAXHOSTNAMELEN] = '\0'; 973 } else if ((in.s_addr & 0xffffff) == 0) { 974 (void) sprintf(line, "%u", C(in.s_addr >> 24)); 975 } else if ((in.s_addr & 0xffff) == 0) { 976 (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), 977 C(in.s_addr >> 16)); 978 } else if ((in.s_addr & 0xff) == 0) { 979 (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), 980 C(in.s_addr >> 16), C(in.s_addr >> 8)); 981 } else { 982 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), 983 C(in.s_addr >> 16), C(in.s_addr >> 8), 984 C(in.s_addr)); 985 } 986 break; 987 988 case AF_LINK: 989 return (link_ntoa((struct sockaddr_dl *)sa)); 990 991 case AF_INET6: 992 return (routename(sa)); 993 994 default: 995 /* LINTED */ 996 s = (ushort_t *)sa->sa_data; 997 998 slim = s + ((salen(sa) + 1) >> 1); 999 cp = line + sprintf(line, "af %d:", sa->sa_family); 1000 1001 while (s < slim) 1002 cp += sprintf(cp, " %x", *s++); 1003 break; 1004 } 1005 return (line); 1006 } 1007 1008 /* 1009 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and 1010 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL 1011 * or point to dynamically allocated memory. 1012 */ 1013 rtcmd_irep_t * 1014 new_rtcmd_irep(void) 1015 { 1016 rtcmd_irep_t *rcip; 1017 1018 rcip = calloc(1, sizeof (rtcmd_irep_t)); 1019 if (rcip == NULL) { 1020 quit("calloc", errno); 1021 } 1022 rcip->ri_af = af; 1023 rcip->ri_flags = RTF_STATIC; 1024 return (rcip); 1025 } 1026 1027 void 1028 del_rtcmd_irep(rtcmd_irep_t *rcip) 1029 { 1030 free(rcip->ri_dest_str); 1031 free(rcip->ri_gate_str); 1032 free(rcip->ri_ifp_str); 1033 if (rcip->ri_gate_hp != NULL) { 1034 freehostent(rcip->ri_gate_hp); 1035 } 1036 free(rcip); 1037 } 1038 1039 void 1040 save_string(char **dst, char *src) 1041 { 1042 free(*dst); 1043 *dst = strdup(src); 1044 if (*dst == NULL) { 1045 quit("malloc", errno); 1046 } 1047 } 1048 1049 /* 1050 * Print the short form summary of a route command. 1051 * Eg. "add net default: gateway 10.0.0.1" 1052 * The final newline is not added, allowing the caller to append additional 1053 * information. 1054 */ 1055 void 1056 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good, 1057 boolean_t to_saved) 1058 { 1059 char *cmd; 1060 char obuf[INET6_ADDRSTRLEN]; 1061 1062 switch (rcip->ri_cmd) { 1063 case RTM_ADD: 1064 cmd = "add"; 1065 break; 1066 case RTM_CHANGE: 1067 cmd = "change"; 1068 break; 1069 case RTM_DELETE: 1070 cmd = "delete"; 1071 break; 1072 case RTM_GET: 1073 cmd = "get"; 1074 break; 1075 default: 1076 assert(0); 1077 } 1078 1079 (void) fprintf(to, "%s%s %s %s", cmd, 1080 (to_saved) ? " persistent" : "", 1081 (rcip->ri_flags & RTF_HOST) ? "host" : "net", 1082 (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str); 1083 1084 if (rcip->ri_gate_str != NULL) { 1085 switch (rcip->ri_af) { 1086 case AF_INET: 1087 if (nflag) { 1088 (void) fprintf(to, ": gateway %s", 1089 inet_ntoa(rcip->ri_gate.sin.sin_addr)); 1090 } else if (gw_good && 1091 rcip->ri_gate_hp != NULL && 1092 rcip->ri_gate_hp->h_addr_list[1] != NULL) { 1093 /* 1094 * Print the actual address used in the case 1095 * where there was more than one address 1096 * available for the name, and one was used 1097 * successfully. 1098 */ 1099 (void) fprintf(to, ": gateway %s (%s)", 1100 rcip->ri_gate_str, 1101 inet_ntoa(rcip->ri_gate.sin.sin_addr)); 1102 } else { 1103 (void) fprintf(to, ": gateway %s", 1104 rcip->ri_gate_str); 1105 } 1106 break; 1107 case AF_INET6: 1108 if (inet_ntop(AF_INET6, 1109 &rcip->ri_gate.sin6.sin6_addr, obuf, 1110 INET6_ADDRSTRLEN) != NULL) { 1111 if (nflag) { 1112 (void) fprintf(to, ": gateway %s", 1113 obuf); 1114 break; 1115 } 1116 if (gw_good && 1117 rcip->ri_gate_hp->h_addr_list[1] != NULL) { 1118 (void) fprintf(to, ": gateway %s (%s)", 1119 rcip->ri_gate_str, obuf); 1120 break; 1121 } 1122 } 1123 /* FALLTHROUGH */ 1124 default: 1125 (void) fprintf(to, ": gateway %s", 1126 rcip->ri_gate_str); 1127 break; 1128 } 1129 } 1130 } 1131 1132 void 1133 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock) 1134 { 1135 int flag = 0; 1136 uint_t noval, *valp = &noval; 1137 1138 switch (key) { 1139 #define caseof(x, y, z) \ 1140 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break 1141 1142 caseof(K_MTU, RTV_MTU, rmx_mtu); 1143 caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); 1144 caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); 1145 caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); 1146 caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); 1147 caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); 1148 caseof(K_RTT, RTV_RTT, rmx_rtt); 1149 caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); 1150 #undef caseof 1151 } 1152 rcip->ri_inits |= flag; 1153 if (lock) 1154 rcip->ri_metrics.rmx_locks |= flag; 1155 *valp = atoi(value); 1156 } 1157 1158 /* 1159 * Parse the options give in argv[], filling in rcip with the results. 1160 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is 1161 * tokenized to produce the command line. Cmd_string is tokenized using 1162 * strtok, which will overwrite whitespace in the string with nulls. 1163 * 1164 * Returns B_TRUE on success and B_FALSE on failure. 1165 */ 1166 boolean_t 1167 args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string) 1168 { 1169 const char *ws = "\f\n\r\t\v "; 1170 char *tok = cmd_string; 1171 char *keyword_str; 1172 addr_type_t atype = ADDR_TYPE_ANY; 1173 boolean_t iflag = B_FALSE; 1174 boolean_t locknext = B_FALSE; 1175 boolean_t lockrest = B_FALSE; 1176 boolean_t dash_keyword; 1177 int key; 1178 char *err; 1179 1180 if (cmd_string == NULL) { 1181 tok = argv[0]; 1182 } else { 1183 tok = strtok(cmd_string, ws); 1184 } 1185 1186 /* 1187 * The command keywords are already fully checked by main() or 1188 * search_rtfile(). 1189 */ 1190 switch (*tok) { 1191 case 'a': 1192 rcip->ri_cmd = RTM_ADD; 1193 break; 1194 case 'c': 1195 rcip->ri_cmd = RTM_CHANGE; 1196 break; 1197 case 'd': 1198 rcip->ri_cmd = RTM_DELETE; 1199 break; 1200 case 'g': 1201 rcip->ri_cmd = RTM_GET; 1202 break; 1203 default: 1204 /* NOTREACHED */ 1205 quit(gettext("Internal Error"), EINVAL); 1206 /* NOTREACHED */ 1207 } 1208 1209 #define NEXTTOKEN \ 1210 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL) 1211 1212 while (NEXTTOKEN) { 1213 keyword_str = tok; 1214 if (*tok == '-') { 1215 dash_keyword = B_TRUE; 1216 key = keyword(tok + 1); 1217 } else { 1218 dash_keyword = B_FALSE; 1219 key = keyword(tok); 1220 if (key != K_HOST && key != K_NET) { 1221 /* All others must be preceded by '-' */ 1222 key = 0; 1223 } 1224 } 1225 switch (key) { 1226 case K_HOST: 1227 if (atype == ADDR_TYPE_NET) { 1228 syntax_error(gettext("route: -host and -net " 1229 "are mutually exclusive\n")); 1230 return (B_FALSE); 1231 } 1232 atype = ADDR_TYPE_HOST; 1233 break; 1234 case K_NET: 1235 if (atype == ADDR_TYPE_HOST) { 1236 syntax_error(gettext("route: -host and -net " 1237 "are mutually exclusive\n")); 1238 return (B_FALSE); 1239 } 1240 atype = ADDR_TYPE_NET; 1241 break; 1242 case K_LINK: 1243 rcip->ri_af = AF_LINK; 1244 break; 1245 case K_INET: 1246 rcip->ri_af = AF_INET; 1247 break; 1248 case K_SA: 1249 rcip->ri_af = PF_ROUTE; 1250 break; 1251 case K_INET6: 1252 rcip->ri_af = AF_INET6; 1253 break; 1254 case K_IFACE: 1255 case K_INTERFACE: 1256 iflag = B_TRUE; 1257 /* fallthrough */ 1258 case K_NOSTATIC: 1259 rcip->ri_flags &= ~RTF_STATIC; 1260 break; 1261 case K_LOCK: 1262 locknext = B_TRUE; 1263 break; 1264 case K_LOCKREST: 1265 lockrest = B_TRUE; 1266 break; 1267 case K_REJECT: 1268 rcip->ri_flags |= RTF_REJECT; 1269 break; 1270 case K_BLACKHOLE: 1271 rcip->ri_flags |= RTF_BLACKHOLE; 1272 break; 1273 case K_PROTO1: 1274 rcip->ri_flags |= RTF_PROTO1; 1275 break; 1276 case K_PROTO2: 1277 rcip->ri_flags |= RTF_PROTO2; 1278 break; 1279 case K_CLONING: 1280 rcip->ri_flags |= RTF_CLONING; 1281 break; 1282 case K_XRESOLVE: 1283 rcip->ri_flags |= RTF_XRESOLVE; 1284 break; 1285 case K_STATIC: 1286 rcip->ri_flags |= RTF_STATIC; 1287 break; 1288 case K_IFA: 1289 if (!NEXTTOKEN) { 1290 syntax_arg_missing(keyword_str); 1291 return (B_FALSE); 1292 } 1293 if (!getaddr(rcip, RTA_IFA, tok, atype)) { 1294 return (B_FALSE); 1295 } 1296 break; 1297 case K_IFP: 1298 if (!NEXTTOKEN) { 1299 syntax_arg_missing(keyword_str); 1300 return (B_FALSE); 1301 } 1302 if (!getaddr(rcip, RTA_IFP, tok, atype)) { 1303 return (B_FALSE); 1304 } 1305 break; 1306 case K_GATEWAY: 1307 if (!NEXTTOKEN) { 1308 syntax_arg_missing(keyword_str); 1309 return (B_FALSE); 1310 } 1311 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) { 1312 return (B_FALSE); 1313 } 1314 break; 1315 case K_DST: 1316 if (!NEXTTOKEN) { 1317 syntax_arg_missing(keyword_str); 1318 return (B_FALSE); 1319 } 1320 if (!getaddr(rcip, RTA_DST, tok, atype)) { 1321 return (B_FALSE); 1322 } 1323 break; 1324 case K_NETMASK: 1325 if (!NEXTTOKEN) { 1326 syntax_arg_missing(keyword_str); 1327 return (B_FALSE); 1328 } 1329 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) { 1330 return (B_FALSE); 1331 } 1332 atype = ADDR_TYPE_NET; 1333 break; 1334 case K_MTU: 1335 case K_HOPCOUNT: 1336 case K_EXPIRE: 1337 case K_RECVPIPE: 1338 case K_SENDPIPE: 1339 case K_SSTHRESH: 1340 case K_RTT: 1341 case K_RTTVAR: 1342 if (!NEXTTOKEN) { 1343 syntax_arg_missing(keyword_str); 1344 return (B_FALSE); 1345 } 1346 set_metric(rcip, tok, key, locknext || lockrest); 1347 locknext = B_FALSE; 1348 break; 1349 case K_PRIVATE: 1350 rcip->ri_flags |= RTF_PRIVATE; 1351 break; 1352 case K_MULTIRT: 1353 rcip->ri_flags |= RTF_MULTIRT; 1354 break; 1355 case K_SETSRC: 1356 if (!NEXTTOKEN) { 1357 syntax_arg_missing(keyword_str); 1358 return (B_FALSE); 1359 } 1360 if (!getaddr(rcip, RTA_SRC, tok, atype)) { 1361 return (B_FALSE); 1362 } 1363 rcip->ri_flags |= RTF_SETSRC; 1364 break; 1365 default: 1366 if (dash_keyword) { 1367 syntax_bad_keyword(tok + 1); 1368 return (B_FALSE); 1369 } 1370 if ((rcip->ri_addrs & RTA_DST) == 0) { 1371 if (!getaddr(rcip, RTA_DST, tok, atype)) { 1372 return (B_FALSE); 1373 } 1374 } else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) { 1375 /* 1376 * For the gateway parameter, retrieve the 1377 * pointer to the struct hostent so that all 1378 * possible addresses can be tried until one 1379 * is successful. 1380 */ 1381 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) { 1382 return (B_FALSE); 1383 } 1384 } else { 1385 ulong_t metric; 1386 /* 1387 * Assume that a regular number is a metric. 1388 * Needed for compatibility with old route 1389 * command syntax. 1390 */ 1391 errno = 0; 1392 metric = strtoul(tok, &err, 10); 1393 if (errno == 0 && *err == '\0' && 1394 metric < 0x80000000ul) { 1395 iflag = (metric == 0); 1396 if (verbose) { 1397 (void) printf("old usage of " 1398 "trailing number, assuming " 1399 "route %s\n", iflag ? 1400 "to if" : "via gateway"); 1401 } 1402 continue; 1403 } 1404 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) { 1405 return (B_FALSE); 1406 } 1407 } 1408 } 1409 } 1410 #undef NEXTTOKEN 1411 1412 if ((rcip->ri_addrs & RTA_DST) == 0) { 1413 syntax_error(gettext("route: destination required\n")); 1414 return (B_FALSE); 1415 } else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) && 1416 (rcip->ri_addrs & RTA_GATEWAY) == 0) { 1417 syntax_error(gettext( 1418 "route: gateway required for add or delete command\n")); 1419 return (B_FALSE); 1420 } 1421 1422 if (!iflag) { 1423 rcip->ri_flags |= RTF_GATEWAY; 1424 } 1425 1426 if (atype != ADDR_TYPE_NET) { 1427 if (rcip->ri_addrs & RTA_NETMASK) { 1428 /* 1429 * We know the netmask, so we can set the host flag 1430 * based on whether the netmask is the host netmask. 1431 */ 1432 if (rcip->ri_af == AF_INET && 1433 rcip->ri_mask.sin.sin_addr.s_addr == 1434 IP_HOST_MASK) { 1435 rcip->ri_flags |= RTF_HOST; 1436 } 1437 if (rcip->ri_af == AF_INET6 && 1438 memcmp(&rcip->ri_mask.sin6.sin6_addr, 1439 &in6_host_mask, 1440 sizeof (struct in6_addr)) == 0) { 1441 rcip->ri_flags |= RTF_HOST; 1442 } 1443 } else { 1444 /* 1445 * If no prefix mask has been saved at this point, it 1446 * only makes sense to treat the destination address 1447 * as a host address. 1448 */ 1449 rcip->ri_flags |= RTF_HOST; 1450 } 1451 } 1452 return (B_TRUE); 1453 } 1454 1455 /* 1456 * This command always seeks to the end of the file prior to writing. 1457 */ 1458 void 1459 write_to_rtfile(FILE *fp, int argc, char **argv) 1460 { 1461 char file_line[BUF_SIZE]; 1462 int len; 1463 int i; 1464 1465 len = 0; 1466 if (early_v6_keyword) { 1467 /* 1468 * This flag is set when "inet6" was seen as an 1469 * argument to the -f flag. Normally, when writing 1470 * routes to the persistent route file, everything on 1471 * the command line after "add" is saved verbatim. 1472 * In this case, the arguments after "add" may not be 1473 * sufficient, as the ipv6 keyword came before "add", 1474 * yet must be present in the persistent route file. 1475 */ 1476 len += snprintf(file_line, BUF_SIZE, "-inet6 "); 1477 } 1478 for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) { 1479 len += snprintf(&file_line[len], BUF_SIZE - len, "%s ", 1480 argv[i]); 1481 } 1482 if (len >= BUF_SIZE) 1483 quit(gettext("Internal Error"), EINVAL); 1484 file_line[len - 1] = '\n'; 1485 if (fseek(fp, 0, SEEK_END) != 0 || 1486 fputs(file_line, fp) == EOF) { 1487 quit(gettext("failed to write to route file"), 1488 errno); 1489 } 1490 } 1491 1492 boolean_t 1493 compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt) 1494 { 1495 if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 || 1496 memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) { 1497 return (B_FALSE); 1498 } 1499 return (srch_rt->ri_gate_str == NULL || 1500 strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0); 1501 } 1502 1503 /* 1504 * Search the route file for routes matching the supplied route. There are 3 1505 * modes of operation: 1506 * SEARCH_MODE_RET - no side effects. 1507 * SEARCH_MODE_PRINT - prints each matching line. 1508 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp. 1509 * 1510 * In all cases, the number of matches is returned. If rt is NULL, all routes 1511 * matching the global af value are considered matching. 1512 */ 1513 int 1514 search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode) 1515 { 1516 char *tmp_buf; 1517 int match_cnt; 1518 boolean_t match; 1519 char file_line[BUF_SIZE + 4] = "add "; 1520 rtcmd_irep_t *thisrt; 1521 1522 match_cnt = 0; 1523 1524 /* 1525 * Leave space at the beginning of file_line for "add ". 1526 */ 1527 while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) { 1528 1529 if (file_line[4] == '#' || file_line[4] == '\n') { 1530 /* Handle comments and blank lines */ 1531 if (mode == SEARCH_MODE_DEL && 1532 fputs(file_line + 4, temp_fp) == EOF) { 1533 quit(gettext( 1534 "route: failed to write to temp file"), 1535 errno); 1536 } 1537 continue; 1538 } 1539 thisrt = new_rtcmd_irep(); 1540 /* 1541 * thisrt->ri_af defaults to whatever address family happens 1542 * to be set in the global af, but routes in the persistent 1543 * route file must be treated as AF_INET by default. 1544 */ 1545 thisrt->ri_af = AF_INET; 1546 1547 exit_on_error = B_FALSE; 1548 tmp_buf = strdup(file_line); 1549 /* args_to_rtcmd() will mangle the string passed. */ 1550 if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) { 1551 /* There was an error in args_to_rtcmd() or helpers */ 1552 del_rtcmd_irep(thisrt); 1553 free(tmp_buf); 1554 continue; 1555 } 1556 exit_on_error = B_TRUE; 1557 free(tmp_buf); 1558 1559 if (thisrt->ri_gate_str == NULL) { 1560 del_rtcmd_irep(thisrt); 1561 continue; 1562 } 1563 match = (rt == NULL) ? (thisrt->ri_af == af) : 1564 compare_rtcmd(rt, thisrt); 1565 1566 if (match) match_cnt++; 1567 if (match && mode == SEARCH_MODE_PRINT) { 1568 (void) printf("persistent: route %s", file_line); 1569 } 1570 if (match && mode == SEARCH_MODE_DEL) { 1571 thisrt->ri_cmd = RTM_DELETE; 1572 print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE); 1573 (void) printf("\n"); 1574 } 1575 del_rtcmd_irep(thisrt); 1576 1577 if (!match && mode == SEARCH_MODE_DEL && 1578 fputs(file_line + 4, temp_fp) == EOF) { 1579 quit(gettext("failed to write to temp file"), 1580 errno); 1581 } 1582 } 1583 return (match_cnt); 1584 } 1585 1586 /* 1587 * Perform the route operation given in argv on the persistent route file. 1588 * If do_flush is set, the persistent route file is flushed of all routes 1589 * matching the global family, and the arguments are ignored. 1590 */ 1591 void 1592 save_route(int argc, char **argv, int do_flush) 1593 { 1594 rtcmd_irep_t *rt; 1595 int perm_fd; 1596 FILE *perm_fp; 1597 FILE *temp_fp; 1598 mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 1599 struct flock lock; 1600 struct stat st; 1601 const char commentstr[] = 1602 "# File generated by route(1M) - do not edit.\n"; 1603 1604 perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode); 1605 if (perm_fd == -1 || fstat(perm_fd, &st) == -1) 1606 quit("failed to open route file", errno); 1607 1608 lock.l_type = F_WRLCK; 1609 lock.l_whence = SEEK_SET; 1610 lock.l_start = 0; 1611 lock.l_len = 0; 1612 if (fcntl(perm_fd, F_SETLK, &lock) != 0) { 1613 quit(gettext("failed to lock route file"), errno); 1614 /* NOTREACHED */ 1615 } 1616 if (st.st_size == 0 && 1617 write(perm_fd, commentstr, sizeof (commentstr) - 1) != 1618 sizeof (commentstr) - 1) 1619 quit(gettext("failed to open route file"), errno); 1620 1621 if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) { 1622 quit(gettext("failed to open route file"), errno); 1623 /* NOTREACHED */ 1624 } 1625 1626 if (!do_flush) { 1627 rt = new_rtcmd_irep(); 1628 (void) args_to_rtcmd(rt, argv, NULL); 1629 } 1630 if (do_flush || rt->ri_cmd == RTM_DELETE) { 1631 if ((temp_fp = fopen(temp_file, "w")) == NULL) { 1632 quit(gettext("failed to open temp file"), errno); 1633 /* NOTREACHED */ 1634 } 1635 } 1636 if (do_flush) { 1637 (void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL); 1638 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) { 1639 quit(gettext("failed to update route file"), errno); 1640 /* NOTREACHED */ 1641 } 1642 (void) fclose(perm_fp); 1643 return; 1644 } 1645 1646 switch (rt->ri_cmd) { 1647 case RTM_ADD: 1648 if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) { 1649 /* Route is already in the file */ 1650 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE); 1651 (void) fprintf(stderr, ": entry exists\n"); 1652 exit(1); 1653 } 1654 write_to_rtfile(perm_fp, argc - 1, argv + 1); 1655 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE); 1656 (void) printf("\n"); 1657 break; 1658 1659 case RTM_CHANGE: 1660 syntax_error( 1661 gettext("route: change command not supported with -p\n")); 1662 /* NOTREACHED */ 1663 1664 case RTM_DELETE: 1665 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) { 1666 /* Route not found */ 1667 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE); 1668 (void) fprintf(stderr, gettext(": not in file\n")); 1669 exit(1); 1670 } 1671 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) { 1672 quit(gettext("failed to update route file"), errno); 1673 /* NOTREACHED */ 1674 } 1675 break; 1676 1677 case RTM_GET: 1678 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <= 1679 0) { 1680 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE); 1681 (void) printf(gettext(": not in file\n")); 1682 } 1683 break; 1684 1685 default: 1686 quit(gettext("Internal Error"), EINVAL); 1687 /* NOTREACHED */ 1688 } 1689 1690 /* 1691 * Closing the file unlocks it. 1692 */ 1693 (void) fclose(perm_fp); 1694 } 1695 1696 int 1697 show_saved_routes(int argc) 1698 { 1699 int perm_fd; 1700 FILE *perm_fp; 1701 struct flock lock; 1702 int count = 0; 1703 1704 if (argc != 1) { 1705 syntax_error(gettext("route: invalid arguments for show\n")); 1706 } 1707 1708 perm_fd = open(perm_file, O_RDONLY, 0); 1709 1710 if (perm_fd == -1) { 1711 if (errno == ENOENT) { 1712 (void) printf("No persistent routes are defined\n"); 1713 return (0); 1714 } else { 1715 quit(gettext("failed to open route file"), errno); 1716 } 1717 } 1718 lock.l_type = F_RDLCK; 1719 lock.l_whence = SEEK_SET; 1720 lock.l_start = 0; 1721 lock.l_len = 0; 1722 if (fcntl(perm_fd, F_SETLK, &lock) != 0) { 1723 quit(gettext("failed to lock route file"), 1724 errno); 1725 /* NOTREACHED */ 1726 } 1727 if ((perm_fp = fdopen(perm_fd, "r")) == NULL) { 1728 quit(gettext("failed to open route file"), errno); 1729 /* NOTREACHED */ 1730 } 1731 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT); 1732 (void) fseek(perm_fp, 0, SEEK_SET); 1733 af = AF_INET6; 1734 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT); 1735 1736 if (count == 0) 1737 (void) printf("No persistent routes are defined\n"); 1738 1739 (void) fclose(perm_fp); 1740 return (0); 1741 } 1742 1743 int 1744 newroute(char **argv) 1745 { 1746 rtcmd_irep_t *newrt; 1747 int ret, attempts, oerrno; 1748 char *err; 1749 char obuf[INET6_ADDRSTRLEN]; 1750 #define hp (newrt->ri_gate_hp) 1751 1752 newrt = new_rtcmd_irep(); 1753 (void) args_to_rtcmd(newrt, argv, NULL); 1754 1755 if (newrt->ri_cmd != RTM_GET && !tflag) { 1756 /* Don't want to read back our messages */ 1757 (void) shutdown(s, 0); 1758 } 1759 if (newrt->ri_addrs & RTA_IFP) { 1760 newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str); 1761 if (newrt->ri_ifp.sdl.sdl_index == 0) { 1762 if (errno != ENXIO) { 1763 quit("if_nametoindex", errno); 1764 } else { 1765 (void) fprintf(stderr, 1766 gettext("route: %s: no such interface\n"), 1767 newrt->ri_ifp_str); 1768 exit(1); 1769 } 1770 } 1771 newrt->ri_ifp.sdl.sdl_family = AF_LINK; 1772 } 1773 for (attempts = 1; ; attempts++) { 1774 errno = 0; 1775 if ((ret = rtmsg(newrt)) == 0) 1776 break; 1777 if (errno != ENETUNREACH && errno != ESRCH) 1778 break; 1779 if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL && 1780 hp->h_addr_list[attempts] != NULL) { 1781 switch (af) { 1782 case AF_INET: 1783 (void) memmove(&newrt->ri_gate.sin.sin_addr, 1784 hp->h_addr_list[attempts], hp->h_length); 1785 continue; 1786 case AF_INET6: 1787 (void) memmove(&newrt->ri_gate.sin6.sin6_addr, 1788 hp->h_addr_list[attempts], hp->h_length); 1789 continue; 1790 } 1791 } 1792 break; 1793 } 1794 oerrno = errno; 1795 1796 if (newrt->ri_cmd != RTM_GET) { 1797 print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE); 1798 if (ret == 0) 1799 (void) printf("\n"); 1800 } else if (ret != 0) { 1801 /* 1802 * Note: there is nothing additional to print for get 1803 * if ret == 0. 1804 */ 1805 if (nflag) { 1806 switch (newrt->ri_af) { 1807 case AF_INET: 1808 (void) printf(" %s", 1809 inet_ntoa(newrt->ri_dst.sin.sin_addr)); 1810 break; 1811 case AF_INET6: 1812 if (inet_ntop(AF_INET6, 1813 (void *)&newrt->ri_dst.sin6.sin6_addr, 1814 obuf, INET6_ADDRSTRLEN) != NULL) { 1815 (void) printf(" %s", obuf); 1816 break; 1817 } 1818 /* FALLTHROUGH */ 1819 default: 1820 (void) printf("%s", newrt->ri_dest_str); 1821 break; 1822 } 1823 } else { 1824 (void) printf("%s", newrt->ri_dest_str); 1825 } 1826 } 1827 1828 if (ret != 0) { 1829 switch (oerrno) { 1830 case ESRCH: 1831 err = "not in table"; 1832 break; 1833 case EBUSY: 1834 err = "entry in use"; 1835 break; 1836 case ENOBUFS: 1837 err = "routing table overflow"; 1838 break; 1839 case EEXIST: 1840 err = "entry exists"; 1841 break; 1842 case EPERM: 1843 err = "insufficient privileges"; 1844 break; 1845 default: 1846 err = strerror(oerrno); 1847 break; 1848 } 1849 (void) printf(": %s\n", err); 1850 } 1851 1852 del_rtcmd_irep(newrt); 1853 1854 return (oerrno); 1855 #undef hp 1856 } 1857 1858 1859 /* 1860 * Convert a network number to the corresponding IP address. 1861 * If the RTA_NETMASK hasn't been specified yet set it based 1862 * on the class of address. 1863 */ 1864 static void 1865 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin) 1866 { 1867 in_addr_t addr, mask; 1868 1869 if (net == 0) { 1870 mask = addr = 0; 1871 } else if (net < 128) { 1872 addr = net << IN_CLASSA_NSHIFT; 1873 mask = IN_CLASSA_NET; 1874 } else if (net < 65536) { 1875 addr = net << IN_CLASSB_NSHIFT; 1876 mask = IN_CLASSB_NET; 1877 } else if (net < 16777216L) { 1878 addr = net << IN_CLASSC_NSHIFT; 1879 mask = IN_CLASSC_NET; 1880 } else { 1881 addr = net; 1882 if ((addr & IN_CLASSA_HOST) == 0) 1883 mask = IN_CLASSA_NET; 1884 else if ((addr & IN_CLASSB_HOST) == 0) 1885 mask = IN_CLASSB_NET; 1886 else if ((addr & IN_CLASSC_HOST) == 0) 1887 mask = IN_CLASSC_NET; 1888 else { 1889 if (IN_CLASSA(addr)) 1890 mask = IN_CLASSA_NET; 1891 else if (IN_CLASSB(addr)) 1892 mask = IN_CLASSB_NET; 1893 else if (IN_CLASSC(addr)) 1894 mask = IN_CLASSC_NET; 1895 else 1896 mask = IP_HOST_MASK; 1897 mask = inet_makesubnetmask(addr, mask); 1898 } 1899 } 1900 sin->sin_addr.s_addr = htonl(addr); 1901 1902 if (!(rcip->ri_addrs & RTA_NETMASK)) { 1903 rcip->ri_addrs |= RTA_NETMASK; 1904 sin = &rcip->ri_mask.sin; 1905 sin->sin_addr.s_addr = htonl(mask); 1906 sin->sin_family = AF_INET; 1907 } 1908 } 1909 1910 static in_addr_t 1911 inet_makesubnetmask(in_addr_t addr, in_addr_t mask) 1912 { 1913 int n; 1914 struct ifconf ifc; 1915 struct ifreq ifreq; 1916 struct ifreq *ifr; 1917 struct sockaddr_in *sin; 1918 char *buf; 1919 int numifs; 1920 size_t bufsize; 1921 int iosoc; 1922 in_addr_t if_addr, if_mask; 1923 in_addr_t if_subnetmask = 0; 1924 short if_flags; 1925 1926 if (mask == 0) 1927 return (0); 1928 if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1929 quit("socket", errno); 1930 if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0) 1931 quit("ioctl", errno); 1932 bufsize = numifs * sizeof (struct ifreq); 1933 buf = malloc(bufsize); 1934 if (buf == NULL) 1935 quit("malloc", errno); 1936 (void) memset(&ifc, 0, sizeof (ifc)); 1937 ifc.ifc_len = bufsize; 1938 ifc.ifc_buf = buf; 1939 if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0) 1940 quit("ioctl (get interface configuration)", errno); 1941 /* Let's check to see if this is maybe a local subnet route. */ 1942 ifr = ifc.ifc_req; 1943 for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) { 1944 ifreq = *ifr; 1945 /* LINTED */ 1946 sin = (struct sockaddr_in *)&ifr->ifr_addr; 1947 if_addr = ntohl(sin->sin_addr.s_addr); 1948 1949 if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0) 1950 quit("ioctl (get interface flags)", errno); 1951 if ((ifreq.ifr_flags & IFF_UP) == 0) 1952 continue; 1953 if_flags = ifreq.ifr_flags; 1954 1955 if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0) 1956 quit("ioctl (get netmask)", errno); 1957 /* LINTED */ 1958 sin = (struct sockaddr_in *)&ifreq.ifr_addr; 1959 if_mask = ntohl(sin->sin_addr.s_addr); 1960 if ((if_addr & mask) == (addr & mask)) { 1961 /* 1962 * Don't trust pt-pt interfaces if there are 1963 * other interfaces. 1964 */ 1965 if (if_flags & IFF_POINTOPOINT) { 1966 if_subnetmask = if_mask; 1967 continue; 1968 } 1969 /* 1970 * Fine. Just assume the same net mask as the 1971 * directly attached subnet interface is using. 1972 */ 1973 return (if_mask); 1974 } 1975 } 1976 if (if_subnetmask != 0) 1977 return (if_subnetmask); 1978 return (mask); 1979 } 1980 1981 /* 1982 * Interpret an argument as a network address of some kind. 1983 * 1984 * If the address family is one looked up in getaddr() using one of the 1985 * getipnodebyX() functions (currently only AF_INET6), then callers should 1986 * freehostent() the returned "struct hostent" pointer if one was passed in. 1987 * 1988 * If exit_on_error is true, this function will cause route to exit on error by 1989 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE 1990 * on failure. 1991 */ 1992 static boolean_t 1993 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype) 1994 { 1995 su_t *su; 1996 struct hostent **hpp; 1997 struct hostent *hp; 1998 int masklen; 1999 2000 if (which == RTA_GATEWAY) { 2001 hpp = &(rcip->ri_gate_hp); 2002 } else { 2003 hpp = &hp; 2004 } 2005 *hpp = NULL; 2006 2007 rcip->ri_addrs |= which; 2008 switch (which) { 2009 case RTA_DST: 2010 save_string(&rcip->ri_dest_str, s); 2011 su = &rcip->ri_dst; 2012 su->sa.sa_family = rcip->ri_af; 2013 break; 2014 case RTA_GATEWAY: 2015 save_string(&rcip->ri_gate_str, s); 2016 su = &rcip->ri_gate; 2017 su->sa.sa_family = rcip->ri_af; 2018 break; 2019 case RTA_NETMASK: 2020 su = &rcip->ri_mask; 2021 su->sa.sa_family = rcip->ri_af; 2022 break; 2023 case RTA_IFP: 2024 save_string(&rcip->ri_ifp_str, s); 2025 return (B_TRUE); 2026 /* 2027 * RTA_SRC has overloaded meaning. It can represent the 2028 * src address of incoming or outgoing packets. 2029 */ 2030 case RTA_IFA: 2031 su = &rcip->ri_ifa; 2032 su->sa.sa_family = rcip->ri_af; 2033 break; 2034 case RTA_SRC: 2035 su = &rcip->ri_src; 2036 su->sa.sa_family = rcip->ri_af; 2037 break; 2038 default: 2039 /* NOTREACHED */ 2040 quit(gettext("Internal Error"), EINVAL); 2041 /* NOTREACHED */ 2042 } 2043 if (strcmp(s, "default") == 0) { 2044 if (which == RTA_DST) { 2045 return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET)); 2046 } 2047 if (which == RTA_SRC) { 2048 return (B_TRUE); 2049 } 2050 return (B_TRUE); 2051 } 2052 switch (rcip->ri_af) { 2053 case AF_LINK: 2054 link_addr(s, &su->sdl); 2055 return (B_TRUE); 2056 case PF_ROUTE: 2057 sockaddr(s, &su->sa); 2058 return (B_TRUE); 2059 case AF_INET6: 2060 switch (which) { 2061 case RTA_DST: 2062 if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) { 2063 return (B_FALSE); 2064 } 2065 if (masklen != NO_PREFIX) { 2066 (void) memset(&rcip->ri_mask.sin6.sin6_addr, 0, 2067 sizeof (rcip->ri_mask.sin6.sin6_addr)); 2068 if (!in_prefixlentomask(masklen, IPV6_ABITS, 2069 (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) { 2070 syntax_error(gettext( 2071 "route: bad prefix length: %d\n"), 2072 masklen); 2073 return (B_FALSE); 2074 } 2075 rcip->ri_mask.sin6.sin6_family = rcip->ri_af; 2076 rcip->ri_addrs |= RTA_NETMASK; 2077 } 2078 return (B_TRUE); 2079 case RTA_GATEWAY: 2080 case RTA_IFA: 2081 case RTA_SRC: 2082 return (in6_getaddr(s, &su->sin6, NULL, hpp)); 2083 case RTA_NETMASK: 2084 syntax_error( 2085 gettext("route: -netmask not supported for IPv6: " 2086 "use <prefix>/<prefix-length> instead\n")); 2087 return (B_FALSE); 2088 default: 2089 quit(gettext("Internal Error"), EINVAL); 2090 /* NOTREACHED */ 2091 } 2092 case AF_INET: 2093 switch (which) { 2094 case RTA_DST: 2095 if (!in_getaddr(s, &su->sin, &masklen, which, hpp, 2096 atype, rcip)) { 2097 return (B_FALSE); 2098 } 2099 if (masklen != NO_PREFIX) { 2100 (void) memset(&rcip->ri_mask.sin.sin_addr, 0, 2101 sizeof (rcip->ri_mask.sin.sin_addr)); 2102 if (!in_prefixlentomask(masklen, IP_ABITS, 2103 (uchar_t *)&rcip->ri_mask.sin.sin_addr)) { 2104 syntax_error(gettext( 2105 "route: bad prefix length: %d\n"), 2106 masklen); 2107 return (B_FALSE); 2108 } 2109 rcip->ri_mask.sin.sin_family = rcip->ri_af; 2110 rcip->ri_addrs |= RTA_NETMASK; 2111 } 2112 return (B_TRUE); 2113 case RTA_GATEWAY: 2114 case RTA_IFA: 2115 case RTA_NETMASK: 2116 case RTA_SRC: 2117 return (in_getaddr(s, &su->sin, NULL, which, hpp, atype, 2118 rcip)); 2119 default: 2120 quit(gettext("Internal Error"), EINVAL); 2121 /* NOTREACHED */ 2122 } 2123 default: 2124 quit(gettext("Internal Error"), EINVAL); 2125 /* NOTREACHED */ 2126 } 2127 return (B_TRUE); 2128 } 2129 2130 /* 2131 * Interpret an argument as an IPv4 network address of some kind, 2132 * returning B_TRUE on success or B_FALSE on failure. 2133 * This function will cause an exit() on failure if exit_on_failure is set. 2134 * 2135 * Note that this tries host interpretation before network interpretation, 2136 * except when -net has been given and the destination address is being parsed. 2137 * 2138 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and 2139 * pass out <n> in *plenp. 2140 * If <n> doesn't parse return BAD_ADDR as *plenp. 2141 * If no /<n> is present return NO_PREFIX as *plenp. 2142 */ 2143 static boolean_t 2144 in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which, 2145 struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip) 2146 { 2147 struct hostent *hp; 2148 struct netent *np; 2149 in_addr_t val; 2150 char str[BUFSIZ]; 2151 2152 (void) strlcpy(str, s, sizeof (str)); 2153 2154 /* 2155 * If plenp is non-NULL, /<n> syntax for netmask is allowed. 2156 */ 2157 if (plenp != NULL) { 2158 char *cp; 2159 2160 *plenp = in_getprefixlen(str, IP_ABITS); 2161 if (*plenp == BAD_ADDR) 2162 return (B_FALSE); 2163 cp = strchr(str, '/'); 2164 if (cp != NULL) 2165 *cp = '\0'; 2166 } else if (strchr(str, '/') != NULL) { 2167 syntax_error(gettext("route: %s: unexpected '/'\n"), str); 2168 return (B_FALSE); 2169 } 2170 2171 (void) memset(sin, 0, sizeof (*sin)); 2172 sin->sin_family = AF_INET; 2173 2174 /* 2175 * Handle 255.255.255.255 as a special case first. 2176 */ 2177 if (strcmp(str, "255.255.255.255") == 0) { 2178 sin->sin_addr.s_addr = INADDR_BROADCAST; 2179 return (B_TRUE); 2180 } 2181 2182 val = inet_addr(str); 2183 if (val != (in_addr_t)-1) { 2184 /* Numeric address */ 2185 sin->sin_addr.s_addr = val; 2186 if (which == RTA_DST) { 2187 if (atype == ADDR_TYPE_NET || 2188 (atype == ADDR_TYPE_ANY && 2189 inet_lnaof(sin->sin_addr) == INADDR_ANY)) { 2190 /* This looks like a network address. */ 2191 inet_makenetandmask(rcip, ntohl(val), 2192 sin); 2193 } 2194 } 2195 return (B_TRUE); 2196 } 2197 /* Host or net name */ 2198 if (which != RTA_DST || atype != ADDR_TYPE_NET) { 2199 /* A host name is allowed. */ 2200 if ((hp = gethostbyname(str)) != NULL) { 2201 *hpp = hp; 2202 (void) memmove(&sin->sin_addr, hp->h_addr, 2203 hp->h_length); 2204 return (B_TRUE); 2205 } 2206 } 2207 if (atype != ADDR_TYPE_HOST) { 2208 /* A network name is allowed */ 2209 if ((np = getnetbyname(str)) != NULL && 2210 (val = np->n_net) != 0) { 2211 if (which == RTA_DST) { 2212 inet_makenetandmask(rcip, val, sin); 2213 } 2214 return (B_TRUE); 2215 } 2216 } 2217 syntax_error(gettext("%s: bad value\n"), s); 2218 return (B_FALSE); 2219 } 2220 2221 /* 2222 * Interpret an argument as an IPv6 network address of some kind, 2223 * returning B_TRUE on success or B_FALSE on failure. 2224 * This function will cause an exit() on failure if exit_on_failure is set. 2225 * 2226 * If the last argument is non-NULL allow a <addr>/<n> syntax and 2227 * pass out <n> in *plenp. 2228 * If <n> doesn't parse return BAD_ADDR as *plenp. 2229 * If no /<n> is present return NO_PREFIX as *plenp. 2230 */ 2231 static boolean_t 2232 in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp, 2233 struct hostent **hpp) 2234 { 2235 struct hostent *hp; 2236 char str[BUFSIZ]; 2237 int error_num; 2238 2239 (void) strlcpy(str, s, sizeof (str)); 2240 2241 /* 2242 * If plenp is non-NULL, /<n> syntax for netmask is allowed. 2243 */ 2244 if (plenp != NULL) { 2245 char *cp; 2246 2247 *plenp = in_getprefixlen(str, IPV6_ABITS); 2248 if (*plenp == BAD_ADDR) 2249 return (B_FALSE); 2250 cp = strchr(str, '/'); 2251 if (cp != NULL) 2252 *cp = '\0'; 2253 } else if (strchr(str, '/') != NULL) { 2254 syntax_error(gettext("route: %s: unexpected '/'\n"), str); 2255 return (B_FALSE); 2256 } 2257 2258 (void) memset(sin6, 0, sizeof (struct sockaddr_in6)); 2259 sin6->sin6_family = AF_INET6; 2260 2261 hp = getipnodebyname(str, AF_INET6, 0, &error_num); 2262 if (hp != NULL) { 2263 *hpp = hp; 2264 (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length); 2265 return (B_TRUE); 2266 } 2267 if (error_num == TRY_AGAIN) { 2268 /* 2269 * This isn't a problem if we aren't going to use the address 2270 * right away. 2271 */ 2272 if (!exit_on_error) { 2273 return (B_TRUE); 2274 } 2275 syntax_error(gettext("route: %s: bad address (try " 2276 "again later)\n"), s); 2277 return (B_FALSE); 2278 } 2279 syntax_error(gettext("route: %s: bad address\n"), s); 2280 return (B_FALSE); 2281 } 2282 2283 /* 2284 * Parse <addr>/<n> syntax and return the integer n. 2285 * If <addr> is missing or <n> is not a valid integer, this function calls 2286 * syntax_error() and returns BAD_ADDR. 2287 * if n is not between 0 and max_plen inclusive, this functions calls 2288 * syntax_error() and returns BAD_ADDR. 2289 * If /<n> is not present, this function returns NO_PREFIX. 2290 * The string addr is not modified. 2291 */ 2292 int 2293 in_getprefixlen(char *addr, int max_plen) 2294 { 2295 int prefixlen; 2296 char *str, *end; 2297 2298 str = strchr(addr, '/'); 2299 if (str == addr) { 2300 syntax_error(gettext("route: %s: unexpected '/'\n"), addr); 2301 return (BAD_ADDR); 2302 } 2303 if (str == NULL) 2304 return (NO_PREFIX); 2305 str++; 2306 2307 errno = 0; 2308 prefixlen = strtoul(str, &end, 10); 2309 if (errno != 0 || str == end) { 2310 syntax_error(gettext("route: bad prefix length %s\n"), str); 2311 return (BAD_ADDR); 2312 } 2313 if (prefixlen > max_plen) { 2314 syntax_error(gettext("route: prefix length %s out of range\n"), 2315 str); 2316 return (BAD_ADDR); 2317 } 2318 return (prefixlen); 2319 } 2320 2321 /* 2322 * Convert a prefix length to a mask. 2323 * Returns B_TRUE if ok. B_FALSE otherwise. 2324 * Assumes the mask array is zeroed by the caller. 2325 */ 2326 boolean_t 2327 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask) 2328 { 2329 if (prefixlen < 0 || prefixlen > maxlen) 2330 return (B_FALSE); 2331 2332 while (prefixlen > 0) { 2333 if (prefixlen >= 8) { 2334 *mask++ = 0xFF; 2335 prefixlen -= 8; 2336 continue; 2337 } 2338 *mask |= 1 << (8 - prefixlen); 2339 prefixlen--; 2340 } 2341 return (B_TRUE); 2342 } 2343 2344 void 2345 rtmonitor(int argc, char *argv[]) 2346 { 2347 int n; 2348 intmax_t msg[2048 / sizeof (intmax_t)]; 2349 2350 if (tflag) 2351 exit(0); 2352 verbose = B_TRUE; 2353 if (argc > 1) { 2354 argv++; 2355 if (argc == 2 && **argv == '-') { 2356 switch (keyword(*argv + 1)) { 2357 case K_INET: 2358 af = AF_INET; 2359 break; 2360 case K_LINK: 2361 af = AF_LINK; 2362 break; 2363 case K_INET6: 2364 af = AF_INET6; 2365 break; 2366 default: 2367 usage(*argv); 2368 /* NOTREACHED */ 2369 } 2370 } else { 2371 usage(*argv); 2372 } 2373 (void) close(s); 2374 s = socket(PF_ROUTE, SOCK_RAW, af); 2375 if (s < 0) 2376 quit("socket", errno); 2377 } 2378 for (;;) { 2379 n = read(s, msg, sizeof (msg)); 2380 if (n <= 0) 2381 quit("read", errno); 2382 (void) printf("got message of size %d\n", n); 2383 print_rtmsg((struct rt_msghdr *)msg, n); 2384 } 2385 } 2386 2387 int 2388 rtmsg(rtcmd_irep_t *newrt) 2389 { 2390 static int seq; 2391 int rlen; 2392 char *cp = m_rtmsg.m_space; 2393 int l; 2394 2395 errno = 0; 2396 (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg)); 2397 2398 if (newrt->ri_cmd == RTM_GET) { 2399 newrt->ri_ifp.sa.sa_family = AF_LINK; 2400 newrt->ri_addrs |= RTA_IFP; 2401 } 2402 2403 #define rtm m_rtmsg.m_rtm 2404 rtm.rtm_type = newrt->ri_cmd; 2405 rtm.rtm_flags = newrt->ri_flags; 2406 rtm.rtm_version = RTM_VERSION; 2407 rtm.rtm_seq = ++seq; 2408 rtm.rtm_addrs = newrt->ri_addrs; 2409 rtm.rtm_rmx = newrt->ri_metrics; 2410 rtm.rtm_inits = newrt->ri_inits; 2411 2412 #define NEXTADDR(w, u) \ 2413 if (newrt->ri_addrs & (w)) { \ 2414 l = ROUNDUP_LONG(salen(&u.sa)); \ 2415 (void) memmove(cp, &(u), l); \ 2416 cp += l; \ 2417 if (verbose) \ 2418 sodump(&(u), #u); \ 2419 } 2420 NEXTADDR(RTA_DST, newrt->ri_dst); 2421 NEXTADDR(RTA_GATEWAY, newrt->ri_gate); 2422 NEXTADDR(RTA_NETMASK, newrt->ri_mask); 2423 NEXTADDR(RTA_IFP, newrt->ri_ifp); 2424 NEXTADDR(RTA_IFA, newrt->ri_ifa); 2425 /* 2426 * RTA_SRC has overloaded meaning. It can represent the 2427 * src address of incoming or outgoing packets. 2428 */ 2429 NEXTADDR(RTA_SRC, newrt->ri_src); 2430 #undef NEXTADDR 2431 rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; 2432 if (verbose) 2433 print_rtmsg(&rtm, l); 2434 if (debugonly) 2435 return (0); 2436 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 2437 switch (errno) { 2438 case ESRCH: 2439 case EBUSY: 2440 case ENOBUFS: 2441 case EEXIST: 2442 case ENETUNREACH: 2443 case EHOSTUNREACH: 2444 case EPERM: 2445 break; 2446 default: 2447 perror(gettext("writing to routing socket")); 2448 break; 2449 } 2450 return (-1); 2451 } else if (rlen < (int)rtm.rtm_msglen) { 2452 (void) fprintf(stderr, 2453 gettext("route: write to routing socket got only %d for " 2454 "len\n"), rlen); 2455 return (-1); 2456 } 2457 if (newrt->ri_cmd == RTM_GET) { 2458 do { 2459 l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg)); 2460 } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); 2461 if (l < 0) { 2462 (void) fprintf(stderr, 2463 gettext("route: read from routing socket: %s\n"), 2464 strerror(errno)); 2465 } else { 2466 print_getmsg(newrt, &rtm, l); 2467 } 2468 } 2469 #undef rtm 2470 return (0); 2471 } 2472 2473 static char *msgtypes[] = { 2474 "", 2475 "RTM_ADD: Add Route", 2476 "RTM_DELETE: Delete Route", 2477 "RTM_CHANGE: Change Metrics or flags", 2478 "RTM_GET: Report Metrics", 2479 "RTM_LOSING: Kernel Suspects Partitioning", 2480 "RTM_REDIRECT: Told to use different route", 2481 "RTM_MISS: Lookup failed on this address", 2482 "RTM_LOCK: fix specified metrics", 2483 "RTM_OLDADD: caused by SIOCADDRT", 2484 "RTM_OLDDEL: caused by SIOCDELRT", 2485 "RTM_RESOLVE: Route created by cloning", 2486 "RTM_NEWADDR: address being added to iface", 2487 "RTM_DELADDR: address being removed from iface", 2488 "RTM_IFINFO: iface status change", 2489 0, 2490 }; 2491 2492 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0])) 2493 2494 static char metricnames[] = 2495 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" 2496 "\1mtu"; 2497 static char routeflags[] = 2498 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" 2499 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" 2500 "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC"; 2501 static char ifnetflags[] = 2502 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP" 2503 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST" 2504 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE" 2505 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF" 2506 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6" 2507 "\033MIP\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE" 2508 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY"; 2509 static char addrnames[] = 2510 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC"; 2511 2512 void 2513 print_rtmsg(struct rt_msghdr *rtm, int msglen) 2514 { 2515 struct if_msghdr *ifm; 2516 struct ifa_msghdr *ifam; 2517 2518 if (!verbose) 2519 return; 2520 if (rtm->rtm_version != RTM_VERSION) { 2521 (void) printf("routing message version %d not understood\n", 2522 rtm->rtm_version); 2523 return; 2524 } 2525 if (rtm->rtm_msglen > (ushort_t)msglen) { 2526 (void) printf("message length mismatch, in packet %d, " 2527 "returned %d\n", 2528 rtm->rtm_msglen, msglen); 2529 } 2530 /* 2531 * Since rtm->rtm_type is unsigned, we'll just check the case of zero 2532 * and the upper-bound of (NMSGTYPES - 1). 2533 */ 2534 if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) { 2535 (void) printf("routing message type %d not understood\n", 2536 rtm->rtm_type); 2537 return; 2538 } 2539 (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); 2540 switch (rtm->rtm_type) { 2541 case RTM_IFINFO: 2542 ifm = (struct if_msghdr *)rtm; 2543 (void) printf("if# %d, flags:", ifm->ifm_index); 2544 bprintf(stdout, ifm->ifm_flags, ifnetflags); 2545 pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 2546 break; 2547 case RTM_NEWADDR: 2548 case RTM_DELADDR: 2549 ifam = (struct ifa_msghdr *)rtm; 2550 (void) printf("metric %d, flags:", ifam->ifam_metric); 2551 bprintf(stdout, ifam->ifam_flags, routeflags); 2552 pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); 2553 break; 2554 default: 2555 (void) printf("pid: %ld, seq %d, errno %d, flags:", 2556 rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); 2557 bprintf(stdout, rtm->rtm_flags, routeflags); 2558 pmsg_common(rtm); 2559 } 2560 } 2561 2562 void 2563 print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen) 2564 { 2565 struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL; 2566 struct sockaddr_dl *ifp = NULL; 2567 struct sockaddr *sa; 2568 char *cp; 2569 int i; 2570 2571 (void) printf(" route to: %s\n", routename(&req_rt->ri_dst.sa)); 2572 if (rtm->rtm_version != RTM_VERSION) { 2573 (void) fprintf(stderr, 2574 gettext("routing message version %d not understood\n"), 2575 rtm->rtm_version); 2576 return; 2577 } 2578 if (rtm->rtm_msglen > (ushort_t)msglen) { 2579 (void) fprintf(stderr, 2580 gettext("message length mismatch, in packet %d, " 2581 "returned %d\n"), rtm->rtm_msglen, msglen); 2582 } 2583 if (rtm->rtm_errno) { 2584 (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", 2585 strerror(rtm->rtm_errno), rtm->rtm_errno); 2586 return; 2587 } 2588 cp = ((char *)(rtm + 1)); 2589 if (rtm->rtm_addrs != 0) { 2590 for (i = 1; i != 0; i <<= 1) { 2591 if (i & rtm->rtm_addrs) { 2592 /* LINTED */ 2593 sa = (struct sockaddr *)cp; 2594 switch (i) { 2595 case RTA_DST: 2596 dst = sa; 2597 break; 2598 case RTA_GATEWAY: 2599 gate = sa; 2600 break; 2601 case RTA_NETMASK: 2602 mask = sa; 2603 break; 2604 case RTA_IFP: 2605 if (sa->sa_family == AF_LINK && 2606 ((struct sockaddr_dl *)sa)-> 2607 sdl_nlen != 0) 2608 ifp = (struct sockaddr_dl *)sa; 2609 break; 2610 case RTA_SRC: 2611 src = sa; 2612 break; 2613 } 2614 ADVANCE(cp, sa); 2615 } 2616 } 2617 } 2618 if (dst != NULL && mask != NULL) 2619 mask->sa_family = dst->sa_family; /* XXX */ 2620 if (dst != NULL) 2621 (void) printf("destination: %s\n", routename(dst)); 2622 if (mask != NULL) { 2623 boolean_t savenflag = nflag; 2624 2625 nflag = B_TRUE; 2626 (void) printf(" mask: %s\n", routename(mask)); 2627 nflag = savenflag; 2628 } 2629 if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY) 2630 (void) printf(" gateway: %s\n", routename(gate)); 2631 if (src != NULL && rtm->rtm_flags & RTF_SETSRC) 2632 (void) printf(" setsrc: %s\n", routename(src)); 2633 if (ifp != NULL) { 2634 if (verbose) { 2635 int i; 2636 2637 (void) printf(" interface: %.*s index %d address ", 2638 ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index); 2639 for (i = ifp->sdl_nlen; 2640 i < ifp->sdl_nlen + ifp->sdl_alen; 2641 i++) { 2642 (void) printf("%02x ", 2643 ifp->sdl_data[i] & 0xFF); 2644 } 2645 (void) printf("\n"); 2646 } else { 2647 (void) printf(" interface: %.*s\n", 2648 ifp->sdl_nlen, ifp->sdl_data); 2649 } 2650 } 2651 (void) printf(" flags: "); 2652 bprintf(stdout, rtm->rtm_flags, routeflags); 2653 2654 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ') 2655 #define msec(u) (((u) + 500) / 1000) /* usec to msec */ 2656 2657 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms " 2658 "rttvar,ms hopcount mtu expire"); 2659 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); 2660 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); 2661 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); 2662 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); 2663 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); 2664 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); 2665 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); 2666 if (rtm->rtm_rmx.rmx_expire) 2667 rtm->rtm_rmx.rmx_expire -= time(0); 2668 (void) printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); 2669 #undef lock 2670 #undef msec 2671 #define RTA_IGN \ 2672 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC) 2673 if (verbose) { 2674 pmsg_common(rtm); 2675 } else if (rtm->rtm_addrs &~ RTA_IGN) { 2676 (void) printf("sockaddrs: "); 2677 bprintf(stdout, rtm->rtm_addrs, addrnames); 2678 (void) putchar('\n'); 2679 } 2680 #undef RTA_IGN 2681 } 2682 2683 void 2684 pmsg_common(struct rt_msghdr *rtm) 2685 { 2686 (void) printf("\nlocks: "); 2687 bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames); 2688 (void) printf(" inits: "); 2689 bprintf(stdout, (int)rtm->rtm_inits, metricnames); 2690 pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); 2691 } 2692 2693 void 2694 pmsg_addrs(char *cp, int addrs) 2695 { 2696 struct sockaddr *sa; 2697 int i; 2698 2699 if (addrs == 0) 2700 return; 2701 (void) printf("\nsockaddrs: "); 2702 bprintf(stdout, addrs, addrnames); 2703 (void) putchar('\n'); 2704 for (i = 1; i != 0; i <<= 1) { 2705 if (i & addrs) { 2706 /* LINTED */ 2707 sa = (struct sockaddr *)cp; 2708 (void) printf(" %s", routename(sa)); 2709 ADVANCE(cp, sa); 2710 } 2711 } 2712 (void) putchar('\n'); 2713 (void) fflush(stdout); 2714 } 2715 2716 void 2717 bprintf(FILE *fp, int b, char *s) 2718 { 2719 int i; 2720 boolean_t gotsome = B_FALSE; 2721 2722 if (b == 0) 2723 return; 2724 while ((i = *s++) != 0) { 2725 if (b & (1 << (i - 1))) { 2726 if (!gotsome) 2727 i = '<'; 2728 else 2729 i = ','; 2730 (void) putc(i, fp); 2731 gotsome = B_TRUE; 2732 for (; (i = *s) > ' '; s++) 2733 (void) putc(i, fp); 2734 } else { 2735 while (*s > ' ') 2736 s++; 2737 } 2738 } 2739 if (gotsome) 2740 (void) putc('>', fp); 2741 } 2742 2743 int 2744 keyword(const char *cp) 2745 { 2746 struct keytab *kt = keywords; 2747 2748 while (kt->kt_cp && strcmp(kt->kt_cp, cp)) 2749 kt++; 2750 return (kt->kt_i); 2751 } 2752 2753 void 2754 sodump(su_t *su, char *which) 2755 { 2756 static char obuf[INET6_ADDRSTRLEN]; 2757 2758 switch (su->sa.sa_family) { 2759 case AF_LINK: 2760 (void) printf("%s: link %s; ", 2761 which, link_ntoa(&su->sdl)); 2762 break; 2763 case AF_INET: 2764 (void) printf("%s: inet %s; ", 2765 which, inet_ntoa(su->sin.sin_addr)); 2766 break; 2767 case AF_INET6: 2768 if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf, 2769 INET6_ADDRSTRLEN) != NULL) { 2770 (void) printf("%s: inet6 %s; ", which, obuf); 2771 break; 2772 } 2773 /* FALLTHROUGH */ 2774 default: 2775 quit(gettext("Internal Error"), EINVAL); 2776 /* NOTREACHED */ 2777 } 2778 (void) fflush(stdout); 2779 } 2780 2781 /* States */ 2782 #define VIRGIN 0 2783 #define GOTONE 1 2784 #define GOTTWO 2 2785 #define RESET 3 2786 /* Inputs */ 2787 #define DIGIT (4*0) 2788 #define END (4*1) 2789 #define DELIM (4*2) 2790 #define LETTER (4*3) 2791 2792 void 2793 sockaddr(char *addr, struct sockaddr *sa) 2794 { 2795 char *cp = (char *)sa; 2796 int size = salen(sa); 2797 char *cplim = cp + size; 2798 int byte = 0, state = VIRGIN, new; 2799 2800 (void) memset(cp, 0, size); 2801 cp++; 2802 do { 2803 if ((*addr >= '0') && (*addr <= '9')) { 2804 new = *addr - '0'; 2805 } else if ((*addr >= 'a') && (*addr <= 'f')) { 2806 new = *addr - 'a' + 10; 2807 } else if ((*addr >= 'A') && (*addr <= 'F')) { 2808 new = *addr - 'A' + 10; 2809 } else if (*addr == 0) { 2810 state |= END; 2811 } else { 2812 state |= DELIM; 2813 } 2814 addr++; 2815 switch (state /* | INPUT */) { 2816 case GOTTWO | DIGIT: 2817 *cp++ = byte; 2818 /* FALLTHROUGH */ 2819 case VIRGIN | DIGIT: 2820 state = GOTONE; byte = new; continue; 2821 case GOTONE | DIGIT: 2822 state = GOTTWO; byte = new + (byte << 4); continue; 2823 default: /* | DELIM */ 2824 state = VIRGIN; *cp++ = byte; byte = 0; continue; 2825 case GOTONE | END: 2826 case GOTTWO | END: 2827 *cp++ = byte; 2828 /* FALLTHROUGH */ 2829 case VIRGIN | END: 2830 break; 2831 } 2832 break; 2833 } while (cp < cplim); 2834 } 2835 2836 int 2837 salen(struct sockaddr *sa) 2838 { 2839 switch (sa->sa_family) { 2840 case AF_INET: 2841 return (sizeof (struct sockaddr_in)); 2842 case AF_LINK: 2843 return (sizeof (struct sockaddr_dl)); 2844 case AF_INET6: 2845 return (sizeof (struct sockaddr_in6)); 2846 default: 2847 return (sizeof (struct sockaddr)); 2848 } 2849 } 2850 2851 void 2852 link_addr(const char *addr, struct sockaddr_dl *sdl) 2853 { 2854 char *cp = sdl->sdl_data; 2855 char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl; 2856 int byte = 0, state = VIRGIN, new; 2857 2858 (void) memset(sdl, 0, sizeof (struct sockaddr_dl)); 2859 sdl->sdl_family = AF_LINK; 2860 do { 2861 state &= ~LETTER; 2862 if ((*addr >= '0') && (*addr <= '9')) { 2863 new = *addr - '0'; 2864 } else if ((*addr >= 'a') && (*addr <= 'f')) { 2865 new = *addr - 'a' + 10; 2866 } else if ((*addr >= 'A') && (*addr <= 'F')) { 2867 new = *addr - 'A' + 10; 2868 } else if (*addr == 0) { 2869 state |= END; 2870 } else if (state == VIRGIN && 2871 (((*addr >= 'A') && (*addr <= 'Z')) || 2872 ((*addr >= 'a') && (*addr <= 'z')))) { 2873 state |= LETTER; 2874 } else { 2875 state |= DELIM; 2876 } 2877 addr++; 2878 switch (state /* | INPUT */) { 2879 case VIRGIN | DIGIT: 2880 case VIRGIN | LETTER: 2881 *cp++ = addr[-1]; 2882 continue; 2883 case VIRGIN | DELIM: 2884 state = RESET; 2885 sdl->sdl_nlen = cp - sdl->sdl_data; 2886 continue; 2887 case GOTTWO | DIGIT: 2888 *cp++ = byte; 2889 /* FALLTHROUGH */ 2890 case RESET | DIGIT: 2891 state = GOTONE; 2892 byte = new; 2893 continue; 2894 case GOTONE | DIGIT: 2895 state = GOTTWO; 2896 byte = new + (byte << 4); 2897 continue; 2898 default: /* | DELIM */ 2899 state = RESET; 2900 *cp++ = byte; 2901 byte = 0; 2902 continue; 2903 case GOTONE | END: 2904 case GOTTWO | END: 2905 *cp++ = byte; 2906 /* FALLTHROUGH */ 2907 case RESET | END: 2908 break; 2909 } 2910 break; 2911 } while (cp < cplim); 2912 sdl->sdl_alen = cp - LLADDR(sdl); 2913 } 2914 2915 static char hexlist[] = "0123456789abcdef"; 2916 2917 char * 2918 link_ntoa(const struct sockaddr_dl *sdl) 2919 { 2920 static char obuf[64]; 2921 char *out = obuf; 2922 int i; 2923 uchar_t *in = (uchar_t *)LLADDR(sdl); 2924 uchar_t *inlim = in + sdl->sdl_alen; 2925 boolean_t firsttime = B_TRUE; 2926 2927 if (sdl->sdl_nlen) { 2928 (void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen); 2929 out += sdl->sdl_nlen; 2930 if (sdl->sdl_alen) 2931 *out++ = ':'; 2932 } 2933 while (in < inlim) { 2934 if (firsttime) 2935 firsttime = B_FALSE; 2936 else 2937 *out++ = '.'; 2938 i = *in++; 2939 if (i > 0xf) { 2940 out[1] = hexlist[i & 0xf]; 2941 i >>= 4; 2942 out[0] = hexlist[i]; 2943 out += 2; 2944 } else { 2945 *out++ = hexlist[i]; 2946 } 2947 } 2948 *out = 0; 2949 return (obuf); 2950 } 2951 2952 static mib_item_t * 2953 mibget(int sd) 2954 { 2955 intmax_t buf[512 / sizeof (intmax_t)]; 2956 int flags; 2957 int i, j, getcode; 2958 struct strbuf ctlbuf, databuf; 2959 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; 2960 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; 2961 struct T_error_ack *tea = (struct T_error_ack *)buf; 2962 struct opthdr *req; 2963 mib_item_t *first_item = NULL; 2964 mib_item_t *last_item = NULL; 2965 mib_item_t *temp; 2966 2967 tor->PRIM_type = T_SVR4_OPTMGMT_REQ; 2968 tor->OPT_offset = sizeof (struct T_optmgmt_req); 2969 tor->OPT_length = sizeof (struct opthdr); 2970 tor->MGMT_flags = T_CURRENT; 2971 req = (struct opthdr *)&tor[1]; 2972 req->level = MIB2_IP; /* any MIB2_xxx value ok here */ 2973 req->name = 0; 2974 req->len = 0; 2975 2976 ctlbuf.buf = (char *)buf; 2977 ctlbuf.len = tor->OPT_length + tor->OPT_offset; 2978 flags = 0; 2979 if (putmsg(sd, &ctlbuf, NULL, flags) < 0) { 2980 perror("mibget: putmsg (ctl)"); 2981 return (NULL); 2982 } 2983 /* 2984 * each reply consists of a ctl part for one fixed structure 2985 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, 2986 * containing an opthdr structure. level/name identify the entry, 2987 * len is the size of the data part of the message. 2988 */ 2989 req = (struct opthdr *)&toa[1]; 2990 ctlbuf.maxlen = sizeof (buf); 2991 for (j = 1; ; j++) { 2992 flags = 0; 2993 getcode = getmsg(sd, &ctlbuf, NULL, &flags); 2994 if (getcode < 0) { 2995 perror("mibget: getmsg (ctl)"); 2996 if (verbose) { 2997 (void) fprintf(stderr, 2998 "# level name len\n"); 2999 i = 0; 3000 for (last_item = first_item; last_item != NULL; 3001 last_item = last_item->next_item) { 3002 (void) printf("%d %4ld %5ld %ld\n", 3003 ++i, last_item->group, 3004 last_item->mib_id, 3005 last_item->length); 3006 } 3007 } 3008 break; 3009 } 3010 if (getcode == 0 && 3011 ctlbuf.len >= sizeof (struct T_optmgmt_ack) && 3012 toa->PRIM_type == T_OPTMGMT_ACK && 3013 toa->MGMT_flags == T_SUCCESS && 3014 req->len == 0) { 3015 if (verbose) { 3016 (void) printf("mibget getmsg() %d returned EOD " 3017 "(level %lu, name %lu)\n", j, req->level, 3018 req->name); 3019 } 3020 return (first_item); /* this is EOD msg */ 3021 } 3022 3023 if (ctlbuf.len >= sizeof (struct T_error_ack) && 3024 tea->PRIM_type == T_ERROR_ACK) { 3025 (void) fprintf(stderr, gettext("mibget %d gives " 3026 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = " 3027 "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error); 3028 errno = (tea->TLI_error == TSYSERR) 3029 ? tea->UNIX_error : EPROTO; 3030 break; 3031 } 3032 3033 if (getcode != MOREDATA || 3034 ctlbuf.len < sizeof (struct T_optmgmt_ack) || 3035 toa->PRIM_type != T_OPTMGMT_ACK || 3036 toa->MGMT_flags != T_SUCCESS) { 3037 (void) printf("mibget getmsg(ctl) %d returned %d, " 3038 "ctlbuf.len = %d, PRIM_type = %ld\n", 3039 j, getcode, ctlbuf.len, toa->PRIM_type); 3040 if (toa->PRIM_type == T_OPTMGMT_ACK) { 3041 (void) printf("T_OPTMGMT_ACK: " 3042 "MGMT_flags = 0x%lx, req->len = %ld\n", 3043 toa->MGMT_flags, req->len); 3044 } 3045 errno = ENOMSG; 3046 break; 3047 } 3048 3049 temp = malloc(sizeof (mib_item_t)); 3050 if (temp == NULL) { 3051 perror("mibget: malloc"); 3052 break; 3053 } 3054 if (last_item != NULL) 3055 last_item->next_item = temp; 3056 else 3057 first_item = temp; 3058 last_item = temp; 3059 last_item->next_item = NULL; 3060 last_item->group = req->level; 3061 last_item->mib_id = req->name; 3062 last_item->length = req->len; 3063 last_item->valp = malloc(req->len); 3064 if (verbose) { 3065 (void) printf("msg %d: group = %4ld mib_id = %5ld " 3066 "length = %ld\n", 3067 j, last_item->group, last_item->mib_id, 3068 last_item->length); 3069 } 3070 3071 databuf.maxlen = last_item->length; 3072 databuf.buf = (char *)last_item->valp; 3073 databuf.len = 0; 3074 flags = 0; 3075 getcode = getmsg(sd, NULL, &databuf, &flags); 3076 if (getcode < 0) { 3077 perror("mibget: getmsg (data)"); 3078 break; 3079 } else if (getcode != 0) { 3080 (void) printf("mibget getmsg(data) returned %d, " 3081 "databuf.maxlen = %d, databuf.len = %d\n", 3082 getcode, databuf.maxlen, databuf.len); 3083 break; 3084 } 3085 } 3086 3087 /* 3088 * On error, free all the allocated mib_item_t objects. 3089 */ 3090 while (first_item != NULL) { 3091 last_item = first_item; 3092 first_item = first_item->next_item; 3093 free(last_item); 3094 } 3095 return (NULL); 3096 } 3097