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