busybox/networking/route.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * route
   4 *
   5 * Similar to the standard Unix route, but with only the necessary
   6 * parts for AF_INET and AF_INET6
   7 *
   8 * Bjorn Wesen, Axis Communications AB
   9 *
  10 * Author of the original route:
  11 *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  12 *              (derived from FvK's 'route.c     1.70    01/04/94')
  13 *
  14 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  15 *
  16 *
  17 * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
  18 * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
  19 *
  20 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
  21 */
  22/* 2004/03/09  Manuel Novoa III <mjn3@codepoet.org>
  23 *
  24 * Rewritten to fix several bugs, add additional error checking, and
  25 * remove ridiculous amounts of bloat.
  26 */
  27//config:config ROUTE
  28//config:       bool "route (8.7 kb)"
  29//config:       default y
  30//config:       help
  31//config:       Route displays or manipulates the kernel's IP routing tables.
  32
  33//applet:IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP))
  34
  35//kbuild:lib-$(CONFIG_ROUTE) += route.o
  36
  37#include <net/route.h>
  38#include <net/if.h>
  39
  40#include "libbb.h"
  41#include "inet_common.h"
  42
  43
  44#ifndef RTF_UP
  45/* Keep this in sync with /usr/src/linux/include/linux/route.h */
  46#define RTF_UP          0x0001  /* route usable                 */
  47#define RTF_GATEWAY     0x0002  /* destination is a gateway     */
  48#define RTF_HOST        0x0004  /* host entry (net otherwise)   */
  49#define RTF_REINSTATE   0x0008  /* reinstate route after tmout  */
  50#define RTF_DYNAMIC     0x0010  /* created dyn. (by redirect)   */
  51#define RTF_MODIFIED    0x0020  /* modified dyn. (by redirect)  */
  52#define RTF_MTU         0x0040  /* specific MTU for this route  */
  53#ifndef RTF_MSS
  54#define RTF_MSS         RTF_MTU /* Compatibility :-(            */
  55#endif
  56#define RTF_WINDOW      0x0080  /* per route window clamping    */
  57#define RTF_IRTT        0x0100  /* Initial round trip time      */
  58#define RTF_REJECT      0x0200  /* Reject route                 */
  59#define RTF_NONEXTHOP   0x00200000 /* route with no nexthop     */
  60#endif
  61
  62#if defined(SIOCADDRTOLD) || defined(RTF_IRTT)  /* route */
  63#define HAVE_NEW_ADDRT 1
  64#endif
  65
  66#if HAVE_NEW_ADDRT
  67#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
  68#define full_mask(x) (x)
  69#else
  70#define mask_in_addr(x) ((x).rt_genmask)
  71#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
  72#endif
  73
  74/* The RTACTION entries must agree with tbl_verb[] below! */
  75#define RTACTION_ADD 1
  76#define RTACTION_DEL 2
  77
  78/* For the various tbl_*[] arrays, the 1st byte is return value. */
  79
  80#define NET_FLAG  1
  81#define HOST_FLAG 2
  82
  83/* We remap '-' to '#' to avoid problems with getopt. */
  84static const char tbl_hash_net_host[] ALIGN1 =
  85        "\001#net\0"
  86        "\002#host\0"
  87;
  88
  89#define KW_TAKES_ARG            020
  90#define KW_SETS_FLAG            040
  91
  92#define KW_IPVx_METRIC          020
  93#define KW_IPVx_NETMASK         021
  94#define KW_IPVx_GATEWAY         022
  95#define KW_IPVx_MSS             023
  96#define KW_IPVx_WINDOW          024
  97#define KW_IPVx_IRTT            025
  98#define KW_IPVx_DEVICE          026
  99
 100#define KW_IPVx_FLAG_ONLY       040
 101#define KW_IPVx_REJECT          040
 102#define KW_IPVx_MOD             041
 103#define KW_IPVx_DYN             042
 104#define KW_IPVx_REINSTATE       043
 105
 106static const char tbl_ipvx[] ALIGN1 =
 107        /* 020 is the "takes an arg" bit */
 108#if HAVE_NEW_ADDRT
 109        "\020metric\0"
 110#endif
 111        "\021netmask\0"
 112        "\022gw\0"
 113        "\022gateway\0"
 114        "\023mss\0"
 115        "\024window\0"
 116#ifdef RTF_IRTT
 117        "\025irtt\0"
 118#endif
 119        "\026dev\0"
 120        "\026device\0"
 121        /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
 122#ifdef RTF_REJECT
 123        "\040reject\0"
 124#endif
 125        "\041mod\0"
 126        "\042dyn\0"
 127        "\043reinstate\0"
 128;
 129
 130static const uint16_t flags_ipvx[] ALIGN2 = { /* MUST match tbl_ipvx[] values above. */
 131#ifdef RTF_REJECT
 132        RTF_REJECT,
 133#endif
 134        RTF_MODIFIED,
 135        RTF_DYNAMIC,
 136        RTF_REINSTATE
 137};
 138
 139static int kw_lookup(const char *kwtbl, char ***pargs)
 140{
 141        if (**pargs) {
 142                do {
 143                        if (strcmp(kwtbl + 1, **pargs) == 0) { /* Found a match. */
 144                                *pargs += 1;
 145                                if (kwtbl[0] & KW_TAKES_ARG) {
 146                                        if (!**pargs) { /* No more args! */
 147                                                bb_show_usage();
 148                                        }
 149                                        *pargs += 1; /* Calling routine will use args[-1]. */
 150                                }
 151                                return kwtbl[0];
 152                        }
 153                        kwtbl += strlen(kwtbl) + 1;
 154                } while (*kwtbl);
 155        }
 156        return 0;
 157}
 158
 159/* Add or delete a route, depending on action. */
 160
 161static NOINLINE void INET_setroute(int action, char **args)
 162{
 163        /* char buffer instead of bona-fide struct avoids aliasing warning */
 164        char rt_buf[sizeof(struct rtentry)];
 165        struct rtentry *const rt = (void *)rt_buf;
 166
 167        const char *netmask = NULL;
 168        int skfd, isnet, xflag;
 169
 170        /* Grab the -net or -host options.  Remember they were transformed. */
 171        xflag = kw_lookup(tbl_hash_net_host, &args);
 172
 173        /* If we did grab -net or -host, make sure we still have an arg left. */
 174        if (*args == NULL) {
 175                bb_show_usage();
 176        }
 177
 178        /* Clean out the RTREQ structure. */
 179        memset(rt, 0, sizeof(*rt));
 180
 181        {
 182                const char *target = *args++;
 183                char *prefix;
 184
 185                /* recognize x.x.x.x/mask format. */
 186                prefix = strchr(target, '/');
 187                if (prefix) {
 188                        int prefix_len;
 189
 190                        prefix_len = xatoul_range(prefix+1, 0, 32);
 191                        mask_in_addr(*rt) = htonl( ~(0xffffffffUL >> prefix_len));
 192                        *prefix = '\0';
 193#if HAVE_NEW_ADDRT
 194                        rt->rt_genmask.sa_family = AF_INET;
 195#endif
 196                } else {
 197                        /* Default netmask. */
 198                        netmask = "default";
 199                }
 200                /* Prefer hostname lookup if -host flag (xflag==1) was given. */
 201                isnet = INET_resolve(target, (struct sockaddr_in *) &rt->rt_dst,
 202                                                         (xflag & HOST_FLAG));
 203                if (isnet < 0) {
 204                        bb_error_msg_and_die("resolving %s", target);
 205                }
 206                if (prefix) {
 207                        /* do not destroy prefix for process args */
 208                        *prefix = '/';
 209                }
 210        }
 211
 212        if (xflag) {            /* Reinit isnet if -net or -host was specified. */
 213                isnet = (xflag & NET_FLAG);
 214        }
 215
 216        /* Fill in the other fields. */
 217        rt->rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
 218
 219        while (*args) {
 220                int k = kw_lookup(tbl_ipvx, &args);
 221                const char *args_m1 = args[-1];
 222
 223                if (k & KW_IPVx_FLAG_ONLY) {
 224                        rt->rt_flags |= flags_ipvx[k & 3];
 225                        continue;
 226                }
 227
 228#if HAVE_NEW_ADDRT
 229                if (k == KW_IPVx_METRIC) {
 230                        rt->rt_metric = xatoul(args_m1) + 1;
 231                        continue;
 232                }
 233#endif
 234
 235                if (k == KW_IPVx_NETMASK) {
 236                        struct sockaddr mask;
 237
 238                        if (mask_in_addr(*rt)) {
 239                                bb_show_usage();
 240                        }
 241
 242                        netmask = args_m1;
 243                        isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
 244                        if (isnet < 0) {
 245                                bb_error_msg_and_die("resolving %s", netmask);
 246                        }
 247                        rt->rt_genmask = full_mask(mask);
 248                        continue;
 249                }
 250
 251                if (k == KW_IPVx_GATEWAY) {
 252                        if (rt->rt_flags & RTF_GATEWAY) {
 253                                bb_show_usage();
 254                        }
 255
 256                        isnet = INET_resolve(args_m1,
 257                                                (struct sockaddr_in *) &rt->rt_gateway, 1);
 258                        rt->rt_flags |= RTF_GATEWAY;
 259
 260                        if (isnet) {
 261                                if (isnet < 0) {
 262                                        bb_error_msg_and_die("resolving %s", args_m1);
 263                                }
 264                                bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
 265                        }
 266                        continue;
 267                }
 268
 269                if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
 270                        rt->rt_flags |= RTF_MSS;
 271                        rt->rt_mss = xatoul_range(args_m1, 64, 32768);
 272                        continue;
 273                }
 274
 275                if (k == KW_IPVx_WINDOW) {      /* Check valid window bounds. */
 276                        rt->rt_flags |= RTF_WINDOW;
 277                        rt->rt_window = xatoul_range(args_m1, 128, INT_MAX);
 278                        continue;
 279                }
 280
 281#ifdef RTF_IRTT
 282                if (k == KW_IPVx_IRTT) {
 283                        rt->rt_flags |= RTF_IRTT;
 284                        rt->rt_irtt = xatoul(args_m1);
 285                        rt->rt_irtt *= (bb_clk_tck() / 100);    /* FIXME */
 286#if 0                                   /* FIXME: do we need to check anything of this? */
 287                        if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) {
 288                                bb_error_msg_and_die("bad irtt");
 289                        }
 290#endif
 291                        continue;
 292                }
 293#endif
 294
 295                /* Device is special in that it can be the last arg specified
 296                 * and doesn't require the dev/device keyword in that case. */
 297                if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
 298                        /* Don't use args_m1 here since args may have changed! */
 299                        rt->rt_dev = args[-1];
 300                        continue;
 301                }
 302
 303                /* Nothing matched. */
 304                bb_show_usage();
 305        }
 306
 307#ifdef RTF_REJECT
 308        if ((rt->rt_flags & RTF_REJECT) && !rt->rt_dev) {
 309                rt->rt_dev = (char*)"lo";
 310        }
 311#endif
 312
 313        /* sanity checks.. */
 314        if (mask_in_addr(*rt)) {
 315                uint32_t mask = mask_in_addr(*rt);
 316
 317                mask = ~ntohl(mask);
 318                if ((rt->rt_flags & RTF_HOST) && mask != 0xffffffff) {
 319                        bb_error_msg_and_die("netmask %.8x and host route conflict",
 320                                                                 (unsigned int) mask);
 321                }
 322                if (mask & (mask + 1)) {
 323                        bb_error_msg_and_die("bogus netmask %s", netmask);
 324                }
 325                mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
 326                if (mask & ~(uint32_t)mask_in_addr(*rt)) {
 327                        bb_simple_error_msg_and_die("netmask and route address conflict");
 328                }
 329        }
 330
 331        /* Fill out netmask if still unset */
 332        if ((action == RTACTION_ADD) && (rt->rt_flags & RTF_HOST)) {
 333                mask_in_addr(*rt) = 0xffffffff;
 334        }
 335
 336        /* Create a socket to the INET kernel. */
 337        skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
 338
 339        if (action == RTACTION_ADD)
 340                xioctl(skfd, SIOCADDRT, rt);
 341        else
 342                xioctl(skfd, SIOCDELRT, rt);
 343
 344        if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
 345}
 346
 347#if ENABLE_FEATURE_IPV6
 348
 349static NOINLINE void INET6_setroute(int action, char **args)
 350{
 351        struct sockaddr_in6 sa6;
 352        struct in6_rtmsg rt;
 353        int prefix_len, skfd;
 354        const char *devname;
 355
 356                /* We know args isn't NULL from the check in route_main. */
 357                const char *target = *args++;
 358
 359                if (strcmp(target, "default") == 0) {
 360                        prefix_len = 0;
 361                        memset(&sa6, 0, sizeof(sa6));
 362                } else {
 363                        char *cp;
 364                        cp = strchr(target, '/'); /* Yes... const to non is ok. */
 365                        if (cp) {
 366                                *cp = '\0';
 367                                prefix_len = xatoul_range(cp + 1, 0, 128);
 368                        } else {
 369                                prefix_len = 128;
 370                        }
 371                        if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
 372                                bb_error_msg_and_die("resolving %s", target);
 373                        }
 374                }
 375
 376        /* Clean out the RTREQ structure. */
 377        memset(&rt, 0, sizeof(rt));
 378
 379        memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
 380
 381        /* Fill in the other fields. */
 382        rt.rtmsg_dst_len = prefix_len;
 383        rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
 384        rt.rtmsg_metric = 1;
 385
 386        devname = NULL;
 387
 388        while (*args) {
 389                int k = kw_lookup(tbl_ipvx, &args);
 390                const char *args_m1 = args[-1];
 391
 392                if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
 393                        rt.rtmsg_flags |= flags_ipvx[k & 3];
 394                        continue;
 395                }
 396
 397                if (k == KW_IPVx_METRIC) {
 398                        rt.rtmsg_metric = xatoul(args_m1);
 399                        continue;
 400                }
 401
 402                if (k == KW_IPVx_GATEWAY) {
 403                        if (rt.rtmsg_flags & RTF_GATEWAY) {
 404                                bb_show_usage();
 405                        }
 406
 407                        if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
 408                                bb_error_msg_and_die("resolving %s", args_m1);
 409                        }
 410                        memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
 411                                        sizeof(struct in6_addr));
 412                        rt.rtmsg_flags |= RTF_GATEWAY;
 413                        continue;
 414                }
 415
 416                /* Device is special in that it can be the last arg specified
 417                 * and doesn't require the dev/device keyword in that case. */
 418                if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
 419                        /* Don't use args_m1 here since args may have changed! */
 420                        devname = args[-1];
 421                        continue;
 422                }
 423
 424                /* Nothing matched. */
 425                bb_show_usage();
 426        }
 427
 428        /* Create a socket to the INET6 kernel. */
 429        skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
 430
 431        rt.rtmsg_ifindex = 0;
 432
 433        if (devname) {
 434                struct ifreq ifr;
 435                /*memset(&ifr, 0, sizeof(ifr)); - SIOCGIFINDEX does not need to clear all */
 436                strncpy_IFNAMSIZ(ifr.ifr_name, devname);
 437                xioctl(skfd, SIOCGIFINDEX, &ifr);
 438                rt.rtmsg_ifindex = ifr.ifr_ifindex;
 439        }
 440
 441        /* Tell the kernel to accept this route. */
 442        if (action == RTACTION_ADD)
 443                xioctl(skfd, SIOCADDRT, &rt);
 444        else
 445                xioctl(skfd, SIOCDELRT, &rt);
 446
 447        if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
 448}
 449#endif
 450
 451static const
 452IF_NOT_FEATURE_IPV6(uint16_t flagvals[] ALIGN2 = )
 453IF_FEATURE_IPV6(uint32_t flagvals[] ALIGN4 = )
 454{ /* Must agree with flagchars[]. */
 455        RTF_UP,
 456        RTF_GATEWAY,
 457        RTF_HOST,
 458        RTF_REINSTATE,
 459        RTF_DYNAMIC,
 460        RTF_MODIFIED,
 461#if ENABLE_FEATURE_IPV6
 462        RTF_DEFAULT,
 463        RTF_ADDRCONF,
 464        RTF_CACHE,
 465        RTF_REJECT,
 466        RTF_NONEXTHOP, /* this one doesn't fit into 16 bits */
 467#endif
 468};
 469/* Must agree with flagvals[]. */
 470static const char flagchars[] ALIGN1 =
 471        "UGHRDM"
 472#if ENABLE_FEATURE_IPV6
 473        "DAC!n"
 474#endif
 475;
 476#define IPV4_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
 477#define IPV6_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE|RTF_REJECT|RTF_NONEXTHOP)
 478
 479static void set_flags(char *flagstr, int flags)
 480{
 481        int i;
 482
 483        for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
 484                if (flags & flagvals[i]) {
 485                        ++flagstr;
 486                }
 487        }
 488}
 489
 490/* also used in netstat */
 491void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
 492{
 493        char devname[64], flags[16], *sdest, *sgw;
 494        unsigned long d, g, m;
 495        int r;
 496        int flgs, ref, use, metric, mtu, win, ir;
 497        struct sockaddr_in s_addr;
 498        struct in_addr mask;
 499
 500        FILE *fp = xfopen_for_read("/proc/net/route");
 501
 502        printf("Kernel IP routing table\n"
 503                "Destination     Gateway         Genmask         Flags %s Iface\n",
 504                        netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
 505
 506        /* Skip the first line. */
 507        r = fscanf(fp, "%*[^\n]\n");
 508        if (r < 0) {
 509                /* Empty line, read error, or EOF. Yes, if routing table
 510                 * is completely empty, /proc/net/route has no header.
 511                 */
 512                goto ERROR;
 513        }
 514        while (1) {
 515                r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
 516                                devname, &d, &g, &flgs, &ref, &use, &metric, &m,
 517                                &mtu, &win, &ir);
 518                if (r != 11) {
 519 ERROR:
 520                        if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
 521                                break;
 522                        }
 523                        bb_simple_perror_msg_and_die(bb_msg_read_error);
 524                }
 525
 526                if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
 527                        continue;
 528                }
 529
 530                set_flags(flags, (flgs & IPV4_MASK));
 531#ifdef RTF_REJECT
 532                if (flgs & RTF_REJECT) {
 533                        flags[0] = '!';
 534                }
 535#endif
 536                memset(&s_addr, 0, sizeof(struct sockaddr_in));
 537                s_addr.sin_family = AF_INET;
 538                s_addr.sin_addr.s_addr = d;
 539                sdest = INET_rresolve(&s_addr, (noresolve | 0x8000), m); /* 'default' instead of '*' */
 540                s_addr.sin_addr.s_addr = g;
 541                sgw = INET_rresolve(&s_addr, (noresolve | 0x4000), m); /* Host instead of net */
 542                mask.s_addr = m;
 543                /* "%15.15s" truncates hostnames, do we really want that? */
 544                printf("%-15.15s %-15.15s %-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
 545                free(sdest);
 546                free(sgw);
 547                if (netstatfmt) {
 548                        printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
 549                } else {
 550                        printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
 551                }
 552        }
 553        fclose(fp);
 554}
 555
 556#if ENABLE_FEATURE_IPV6
 557
 558static void INET6_displayroutes(void)
 559{
 560        char addr6[128], *naddr6;
 561        /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
 562         * We read the non-delimited strings into the tail of the buffer
 563         * using fscanf and then modify the buffer by shifting forward
 564         * while inserting ':'s and the nul terminator for the first string.
 565         * Hence the strings are at addr6x and addr6x+40.  This generates
 566         * _much_ less code than the previous (upstream) approach. */
 567        char addr6x[80];
 568        char iface[16], flags[16];
 569        int iflags, metric, refcnt, use, prefix_len, slen;
 570        struct sockaddr_in6 snaddr6;
 571
 572        FILE *fp = xfopen_for_read("/proc/net/ipv6_route");
 573
 574        printf("Kernel IPv6 routing table\n%-44s%-40s"
 575                        "Flags Metric Ref    Use Iface\n",
 576                        "Destination", "Next Hop");
 577
 578        while (1) {
 579                int r;
 580                r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
 581                                addr6x+14, &prefix_len, &slen, addr6x+40+7,
 582                                &metric, &refcnt, &use, &iflags, iface);
 583                if (r != 9) {
 584                        if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
 585                                break;
 586                        }
 587 ERROR:
 588                        bb_simple_perror_msg_and_die(bb_msg_read_error);
 589                }
 590
 591                /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
 592                 * For now, always do this to validate the proc route format, even
 593                 * if the interface is down. */
 594                {
 595                        int i = 0;
 596                        char *p = addr6x+14;
 597
 598                        do {
 599                                if (!*p) {
 600                                        if (i == 40) { /* nul terminator for 1st address? */
 601                                                addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
 602                                                ++p;    /* Skip and continue. */
 603                                                continue;
 604                                        }
 605                                        goto ERROR;
 606                                }
 607                                addr6x[i++] = *p++;
 608                                if (!((i+1) % 5)) {
 609                                        addr6x[i++] = ':';
 610                                }
 611                        } while (i < 40+28+7);
 612                }
 613
 614                set_flags(flags, (iflags & IPV6_MASK));
 615
 616                r = 0;
 617                while (1) {
 618                        memset(&snaddr6, 0, sizeof(snaddr6));
 619                        inet_pton(AF_INET6, addr6x + r,
 620                                          (struct sockaddr *) &snaddr6.sin6_addr);
 621                        snaddr6.sin6_family = AF_INET6;
 622                        naddr6 = INET6_rresolve((struct sockaddr_in6 *) &snaddr6,
 623                                                0x0fff /* Apparently, upstream never resolves. */
 624                                                );
 625                        if (!r) {                       /* 1st pass */
 626                                snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
 627                                r += 40;
 628                                free(naddr6);
 629                        } else {                        /* 2nd pass */
 630                                /* Print the info. */
 631                                printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
 632                                                addr6, naddr6, flags, metric, refcnt, use, iface);
 633                                free(naddr6);
 634                                break;
 635                        }
 636                }
 637        }
 638        fclose(fp);
 639}
 640
 641#endif
 642
 643//usage:#define route_trivial_usage
 644///////:       "[-ne]"IF_FEATURE_IPV6(" [-A inet[6]]")" [{add|del|delete} [-net|-host] TARGET [netmask MASK] [gw GATEWAY] [metric N] [mss BYTES] [window BYTES] [irtt MSEC] [reject] [mod] [dyn] [reinstate] [[dev] IFACE]]"
 645///////too wordy
 646//usage:       "[-ne]"IF_FEATURE_IPV6(" [-A inet[6]]")" [{add|del} [-net|-host] TARGET [netmask MASK]\n"
 647//usage:       "        [gw GATEWAY] [metric N] [mss BYTES] [window BYTES] [reject] [IFACE]]"
 648//usage:#define route_full_usage "\n\n"
 649//usage:       "Show or edit kernel routing tables\n"
 650//usage:     "\n        -n      Don't resolve names"
 651//usage:     "\n        -e      Display other/more information"
 652//usage:     "\n        -A inet" IF_FEATURE_IPV6("[6]") "       Select address family"
 653
 654#define ROUTE_OPT_A     0x01
 655#define ROUTE_OPT_n     0x02
 656#define ROUTE_OPT_e     0x04
 657#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
 658
 659/* 1st byte is return value, matches RTACTION_* code */
 660static const char tbl_verb[] ALIGN1 =
 661        "\001add\0"
 662        "\002del\0"
 663        "\002delete\0"
 664;
 665
 666int route_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 667int route_main(int argc UNUSED_PARAM, char **argv)
 668{
 669        unsigned opt;
 670        int what;
 671        char *family;
 672        char **p;
 673
 674        /* First, remap '-net' and '-host' to avoid getopt problems. */
 675        p = argv;
 676        while (*++p) {
 677                if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
 678                        p[0][0] = '#';
 679                }
 680        }
 681
 682        opt = getopt32(argv, "A:ne", &family);
 683
 684        if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
 685#if ENABLE_FEATURE_IPV6
 686                if (strcmp(family, "inet6") == 0) {
 687                        opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
 688                } else
 689#endif
 690                bb_show_usage();
 691        }
 692
 693        argv += optind;
 694
 695        /* No more args means display the routing table. */
 696        if (!*argv) {
 697                int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
 698#if ENABLE_FEATURE_IPV6
 699                if (opt & ROUTE_OPT_INET6)
 700                        INET6_displayroutes();
 701                else
 702#endif
 703                        bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
 704
 705                fflush_stdout_and_exit(EXIT_SUCCESS);
 706        }
 707
 708        /* Check verb.  At the moment, must be add, del, or delete. */
 709        what = kw_lookup(tbl_verb, &argv);
 710        if (!what || !*argv) {          /* Unknown verb or no more args. */
 711                bb_show_usage();
 712        }
 713
 714#if ENABLE_FEATURE_IPV6
 715        if (opt & ROUTE_OPT_INET6)
 716                INET6_setroute(what, argv);
 717        else
 718#endif
 719                INET_setroute(what, argv);
 720
 721        return EXIT_SUCCESS;
 722}
 723