dnsdomainname,hostname: make NOEXEC
[oweals/busybox.git] / networking / route.c
index 111900d338f1f4142fe812194b9486f987441ca8..32789eecc0dfb4d4a7f26499c983620245dac807 100644 (file)
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
 /* route
  *
  * Similar to the standard Unix route, but with only the necessary
@@ -9,13 +10,8 @@
  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  *              (derived from FvK's 'route.c     1.70    01/04/94')
  *
- * This program is free software; you can redistribute it
- * and/or  modify it under  the terms of  the GNU General
- * Public  License as  published  by  the  Free  Software
- * Foundation;  either  version 2 of the License, or  (at
- * your option) any later version.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * $Id: route.c,v 1.24 2004/03/10 07:42:38 mjn3 Exp $
  *
  * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
  * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
  * Rewritten to fix several bugs, add additional error checking, and
  * remove ridiculous amounts of bloat.
  */
+//config:config ROUTE
+//config:      bool "route (8.9 kb)"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:      Route displays or manipulates the kernel's IP routing tables.
+
+//applet:IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ROUTE) += route.o
+
+//usage:#define route_trivial_usage
+//usage:       "[{add|del|delete}]"
+//usage:#define route_full_usage "\n\n"
+//usage:       "Edit kernel routing tables\n"
+//usage:     "\n       -n      Don't resolve names"
+//usage:     "\n       -e      Display other/more information"
+//usage:     "\n       -A inet" IF_FEATURE_IPV6("{6}") "       Select address family"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
 #include <net/route.h>
 #include <net/if.h>
-#include "busybox.h"
+
+#include "libbb.h"
 #include "inet_common.h"
 
+
 #ifndef RTF_UP
 /* Keep this in sync with /usr/src/linux/include/linux/route.h */
 #define RTF_UP          0x0001 /* route usable                 */
 #define RTF_WINDOW      0x0080 /* per route window clamping    */
 #define RTF_IRTT        0x0100 /* Initial round trip time      */
 #define RTF_REJECT      0x0200 /* Reject route                 */
+#define RTF_NONEXTHOP   0x00200000 /* route with no nexthop    */
 #endif
 
-#if defined (SIOCADDRTOLD) || defined (RTF_IRTT)       /* route */
+#if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */
 #define HAVE_NEW_ADDRT 1
 #endif
 
 #endif
 
 /* The RTACTION entries must agree with tbl_verb[] below! */
-#define RTACTION_ADD           1
-#define RTACTION_DEL           2
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
 
 /* For the various tbl_*[] arrays, the 1st byte is the offset to
  * the next entry and the 2nd byte is return value. */
 
-#define NET_FLAG                       1
-#define HOST_FLAG                      2
+#define NET_FLAG  1
+#define HOST_FLAG 2
 
 /* We remap '-' to '#' to avoid problems with getopt. */
-static const char tbl_hash_net_host[] =
+static const char tbl_hash_net_host[] ALIGN1 =
        "\007\001#net\0"
-/*     "\010\002#host\0" */
+/*     "\010\002#host\0" */
        "\007\002#host"                         /* Since last, we can save a byte. */
 ;
 
-#define KW_TAKES_ARG           020
-#define KW_SETS_FLAG           040
+#define KW_TAKES_ARG            020
+#define KW_SETS_FLAG            040
 
-#define KW_IPVx_METRIC         020
-#define KW_IPVx_NETMASK                021
-#define KW_IPVx_GATEWAY                022
-#define KW_IPVx_MSS                    023
-#define KW_IPVx_WINDOW         024
-#define KW_IPVx_IRTT           025
-#define KW_IPVx_DEVICE         026
+#define KW_IPVx_METRIC          020
+#define KW_IPVx_NETMASK         021
+#define KW_IPVx_GATEWAY         022
+#define KW_IPVx_MSS             023
+#define KW_IPVx_WINDOW          024
+#define KW_IPVx_IRTT            025
+#define KW_IPVx_DEVICE          026
 
-#define KW_IPVx_FLAG_ONLY      040
-#define KW_IPVx_REJECT         040
-#define KW_IPVx_MOD                    041
-#define KW_IPVx_DYN                    042
-#define KW_IPVx_REINSTATE      043
+#define KW_IPVx_FLAG_ONLY       040
+#define KW_IPVx_REJECT          040
+#define KW_IPVx_MOD             041
+#define KW_IPVx_DYN             042
+#define KW_IPVx_REINSTATE       043
 
-static const char tbl_ipvx[] 
+static const char tbl_ipvx[] ALIGN1 =
        /* 020 is the "takes an arg" bit */
 #if HAVE_NEW_ADDRT
        "\011\020metric\0"
@@ -128,11 +135,11 @@ static const char tbl_ipvx[] =
 #endif
        "\006\041mod\0"
        "\006\042dyn\0"
-/*     "\014\043reinstate\0" */
+/*     "\014\043reinstate\0" */
        "\013\043reinstate"                     /* Since last, we can save a byte. */
 ;
 
-static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+static const uint16_t flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
 #ifdef RTF_REJECT
        RTF_REJECT,
 #endif
@@ -163,13 +170,14 @@ static int kw_lookup(const char *kwtbl, char ***pargs)
 
 /* Add or delete a route, depending on action. */
 
-static void INET_setroute(int action, char **args)
+static NOINLINE void INET_setroute(int action, char **args)
 {
-       struct rtentry rt;
-       const char *netmask;
-       int skfd, isnet, xflag;
+       /* char buffer instead of bona-fide struct avoids aliasing warning */
+       char rt_buf[sizeof(struct rtentry)];
+       struct rtentry *const rt = (void *)rt_buf;
 
-       assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
+       const char *netmask = NULL;
+       int skfd, isnet, xflag;
 
        /* Grab the -net or -host options.  Remember they were transformed. */
        xflag = kw_lookup(tbl_hash_net_host, &args);
@@ -180,18 +188,37 @@ static void INET_setroute(int action, char **args)
        }
 
        /* Clean out the RTREQ structure. */
-       memset((char *) &rt, 0, sizeof(struct rtentry));
+       memset(rt, 0, sizeof(*rt));
 
        {
                const char *target = *args++;
+               char *prefix;
+
+               /* recognize x.x.x.x/mask format. */
+               prefix = strchr(target, '/');
+               if (prefix) {
+                       int prefix_len;
 
+                       prefix_len = xatoul_range(prefix+1, 0, 32);
+                       mask_in_addr(*rt) = htonl( ~(0xffffffffUL >> prefix_len));
+                       *prefix = '\0';
+#if HAVE_NEW_ADDRT
+                       rt->rt_genmask.sa_family = AF_INET;
+#endif
+               } else {
+                       /* Default netmask. */
+                       netmask = "default";
+               }
                /* Prefer hostname lookup is -host flag (xflag==1) was given. */
-               isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+               isnet = INET_resolve(target, (struct sockaddr_in *) &rt->rt_dst,
                                                         (xflag & HOST_FLAG));
                if (isnet < 0) {
                        bb_error_msg_and_die("resolving %s", target);
                }
-
+               if (prefix) {
+                       /* do not destroy prefix for process args */
+                       *prefix = '/';
+               }
        }
 
        if (xflag) {            /* Reinit isnet if -net or -host was specified. */
@@ -199,22 +226,20 @@ static void INET_setroute(int action, char **args)
        }
 
        /* Fill in the other fields. */
-       rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
-
-       netmask = bb_INET_default;
+       rt->rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
 
        while (*args) {
                int k = kw_lookup(tbl_ipvx, &args);
                const char *args_m1 = args[-1];
-               
+
                if (k & KW_IPVx_FLAG_ONLY) {
-                       rt.rt_flags |= flags_ipvx[k & 3];
+                       rt->rt_flags |= flags_ipvx[k & 3];
                        continue;
                }
 
 #if HAVE_NEW_ADDRT
                if (k == KW_IPVx_METRIC) {
-                       rt.rt_metric = bb_xgetularg10(args_m1) + 1;
+                       rt->rt_metric = xatoul(args_m1) + 1;
                        continue;
                }
 #endif
@@ -222,7 +247,7 @@ static void INET_setroute(int action, char **args)
                if (k == KW_IPVx_NETMASK) {
                        struct sockaddr mask;
 
-                       if (mask_in_addr(rt)) {
+                       if (mask_in_addr(*rt)) {
                                bb_show_usage();
                        }
 
@@ -231,18 +256,18 @@ static void INET_setroute(int action, char **args)
                        if (isnet < 0) {
                                bb_error_msg_and_die("resolving %s", netmask);
                        }
-                       rt.rt_genmask = full_mask(mask);
+                       rt->rt_genmask = full_mask(mask);
                        continue;
                }
 
                if (k == KW_IPVx_GATEWAY) {
-                       if (rt.rt_flags & RTF_GATEWAY) {
+                       if (rt->rt_flags & RTF_GATEWAY) {
                                bb_show_usage();
                        }
 
                        isnet = INET_resolve(args_m1,
-                                                                (struct sockaddr_in *) &rt.rt_gateway, 1);
-                       rt.rt_flags |= RTF_GATEWAY;
+                                               (struct sockaddr_in *) &rt->rt_gateway, 1);
+                       rt->rt_flags |= RTF_GATEWAY;
 
                        if (isnet) {
                                if (isnet < 0) {
@@ -254,24 +279,24 @@ static void INET_setroute(int action, char **args)
                }
 
                if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
-                       rt.rt_flags |= RTF_MSS;
-                       rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768);
+                       rt->rt_flags |= RTF_MSS;
+                       rt->rt_mss = xatoul_range(args_m1, 64, 32768);
                        continue;
                }
 
                if (k == KW_IPVx_WINDOW) {      /* Check valid window bounds. */
-                       rt.rt_flags |= RTF_WINDOW;
-                       rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX);
+                       rt->rt_flags |= RTF_WINDOW;
+                       rt->rt_window = xatoul_range(args_m1, 128, INT_MAX);
                        continue;
                }
 
 #ifdef RTF_IRTT
                if (k == KW_IPVx_IRTT) {
-                       rt.rt_flags |= RTF_IRTT;
-                       rt.rt_irtt = bb_xgetularg10(args_m1);
-                       rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100);     /* FIXME */
+                       rt->rt_flags |= RTF_IRTT;
+                       rt->rt_irtt = xatoul(args_m1);
+                       rt->rt_irtt *= (bb_clk_tck() / 100);    /* FIXME */
 #if 0                                  /* FIXME: do we need to check anything of this? */
-                       if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
+                       if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) {
                                bb_error_msg_and_die("bad irtt");
                        }
 #endif
@@ -280,9 +305,10 @@ static void INET_setroute(int action, char **args)
 #endif
 
                /* Device is special in that it can be the last arg specified
-                * and doesn't requre the dev/device keyword in that case. */
-               if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
-                       rt.rt_dev = (char *) args_m1;
+                * and doesn't require the dev/device keyword in that case. */
+               if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+                       /* Don't use args_m1 here since args may have changed! */
+                       rt->rt_dev = args[-1];
                        continue;
                }
 
@@ -291,59 +317,54 @@ static void INET_setroute(int action, char **args)
        }
 
 #ifdef RTF_REJECT
-       if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
-               rt.rt_dev = "lo";
+       if ((rt->rt_flags & RTF_REJECT) && !rt->rt_dev) {
+               rt->rt_dev = (char*)"lo";
        }
 #endif
 
        /* sanity checks.. */
-       if (mask_in_addr(rt)) {
-               unsigned long mask = mask_in_addr(rt);
+       if (mask_in_addr(*rt)) {
+               uint32_t mask = mask_in_addr(*rt);
 
                mask = ~ntohl(mask);
-               if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
+               if ((rt->rt_flags & RTF_HOST) && mask != 0xffffffff) {
                        bb_error_msg_and_die("netmask %.8x and host route conflict",
                                                                 (unsigned int) mask);
                }
                if (mask & (mask + 1)) {
                        bb_error_msg_and_die("bogus netmask %s", netmask);
                }
-               mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
-               if (mask & ~mask_in_addr(rt)) {
+               mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
+               if (mask & ~(uint32_t)mask_in_addr(*rt)) {
                        bb_error_msg_and_die("netmask and route address conflict");
                }
        }
 
        /* Fill out netmask if still unset */
-       if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
-               mask_in_addr(rt) = 0xffffffff;
+       if ((action == RTACTION_ADD) && (rt->rt_flags & RTF_HOST)) {
+               mask_in_addr(*rt) = 0xffffffff;
        }
 
        /* Create a socket to the INET kernel. */
-       if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-               bb_perror_msg_and_die("socket");
-       }
+       skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
 
-       if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
-               bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
-       }
+       if (action == RTACTION_ADD)
+               xioctl(skfd, SIOCADDRT, rt);
+       else
+               xioctl(skfd, SIOCDELRT, rt);
 
-       /* Don't bother closing, as we're exiting after we return anyway. */
-       /* close(skfd); */
+       if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
 }
 
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
 
-static void INET6_setroute(int action, char **args)
+static NOINLINE void INET6_setroute(int action, char **args)
 {
        struct sockaddr_in6 sa6;
        struct in6_rtmsg rt;
        int prefix_len, skfd;
        const char *devname;
 
-       assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
-
-       {
                /* We know args isn't NULL from the check in route_main. */
                const char *target = *args++;
 
@@ -352,9 +373,10 @@ static void INET6_setroute(int action, char **args)
                        memset(&sa6, 0, sizeof(sa6));
                } else {
                        char *cp;
-                       if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
-                               *cp = 0;
-                               prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);
+                       cp = strchr(target, '/'); /* Yes... const to non is ok. */
+                       if (cp) {
+                               *cp = '\0';
+                               prefix_len = xatoul_range(cp + 1, 0, 128);
                        } else {
                                prefix_len = 128;
                        }
@@ -362,10 +384,9 @@ static void INET6_setroute(int action, char **args)
                                bb_error_msg_and_die("resolving %s", target);
                        }
                }
-       }
 
        /* Clean out the RTREQ structure. */
-       memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+       memset(&rt, 0, sizeof(rt));
 
        memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
 
@@ -386,7 +407,7 @@ static void INET6_setroute(int action, char **args)
                }
 
                if (k == KW_IPVx_METRIC) {
-                       rt.rtmsg_metric = bb_xgetularg10(args_m1);
+                       rt.rtmsg_metric = xatoul(args_m1);
                        continue;
                }
 
@@ -399,15 +420,16 @@ static void INET6_setroute(int action, char **args)
                                bb_error_msg_and_die("resolving %s", args_m1);
                        }
                        memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
-                                  sizeof(struct in6_addr));
+                                       sizeof(struct in6_addr));
                        rt.rtmsg_flags |= RTF_GATEWAY;
                        continue;
                }
 
                /* Device is special in that it can be the last arg specified
-                * and doesn't requre the dev/device keyword in that case. */
+                * and doesn't require the dev/device keyword in that case. */
                if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
-                       devname = args_m1;
+                       /* Don't use args_m1 here since args may have changed! */
+                       devname = args[-1];
                        continue;
                }
 
@@ -416,102 +438,101 @@ static void INET6_setroute(int action, char **args)
        }
 
        /* Create a socket to the INET6 kernel. */
-       if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
-               bb_perror_msg_and_die("socket");
-       }
+       skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
 
        rt.rtmsg_ifindex = 0;
 
        if (devname) {
                struct ifreq ifr;
                memset(&ifr, 0, sizeof(ifr));
-               strcpy(ifr.ifr_name, devname);
-
-               if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
-                       bb_perror_msg_and_die("SIOGIFINDEX");
-               }
+               strncpy_IFNAMSIZ(ifr.ifr_name, devname);
+               xioctl(skfd, SIOCGIFINDEX, &ifr);
                rt.rtmsg_ifindex = ifr.ifr_ifindex;
        }
 
        /* Tell the kernel to accept this route. */
-       if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
-               bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
-       }
+       if (action == RTACTION_ADD)
+               xioctl(skfd, SIOCADDRT, &rt);
+       else
+               xioctl(skfd, SIOCDELRT, &rt);
 
-       /* Don't bother closing, as we're exiting after we return anyway. */
-       /* close(skfd); */
+       if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
 }
 #endif
 
-static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
+static const
+IF_NOT_FEATURE_IPV6(uint16_t)
+IF_FEATURE_IPV6(unsigned)
+flagvals[] = { /* Must agree with flagchars[]. */
+       RTF_UP,
        RTF_GATEWAY,
        RTF_HOST,
        RTF_REINSTATE,
        RTF_DYNAMIC,
        RTF_MODIFIED,
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
        RTF_DEFAULT,
        RTF_ADDRCONF,
-       RTF_CACHE
+       RTF_CACHE,
+       RTF_REJECT,
+       RTF_NONEXTHOP, /* this one doesn't fit into 16 bits */
 #endif
 };
-
-#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
-#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
-
-static const char flagchars[] =                /* Must agree with flagvals[]. */
-       "GHRDM"
-#ifdef CONFIG_FEATURE_IPV6
-       "DAC"
+/* Must agree with flagvals[]. */
+static const char flagchars[] ALIGN1 =
+       "UGHRDM"
+#if ENABLE_FEATURE_IPV6
+       "DAC!n"
 #endif
 ;
+#define IPV4_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE|RTF_REJECT|RTF_NONEXTHOP)
 
-static
-#ifndef CONFIG_FEATURE_IPV6
-__inline
-#endif
-void set_flags(char *flagstr, int flags)
+static void set_flags(char *flagstr, int flags)
 {
        int i;
 
-       *flagstr++ = 'U';
-
-       for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) {
+       for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
                if (flags & flagvals[i]) {
                        ++flagstr;
                }
        }
 }
 
-void displayroutes(int noresolve, int netstatfmt)
+/* also used in netstat */
+void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
 {
-       char devname[64], flags[16], sdest[16], sgw[16];
-       unsigned long int d, g, m;
+       char devname[64], flags[16], *sdest, *sgw;
+       unsigned long d, g, m;
+       int r;
        int flgs, ref, use, metric, mtu, win, ir;
        struct sockaddr_in s_addr;
        struct in_addr mask;
 
-       FILE *fp = bb_xfopen("/proc/net/route", "r");
+       FILE *fp = xfopen_for_read("/proc/net/route");
 
-       bb_printf("Kernel IP routing table\n"
-                         "Destination     Gateway         Genmask"
-                         "         Flags %s Iface\n",
-                         netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
+       printf("Kernel IP routing table\n"
+               "Destination     Gateway         Genmask         Flags %s Iface\n",
+                       netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
 
-       if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
-               goto ERROR;                /* Empty or missing line, or read error. */
+       /* Skip the first line. */
+       r = fscanf(fp, "%*[^\n]\n");
+       if (r < 0) {
+               /* Empty line, read error, or EOF. Yes, if routing table
+                * is completely empty, /proc/net/route has no header.
+                */
+               goto ERROR;
        }
        while (1) {
-               int r;
                r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
-                                  devname, &d, &g, &flgs, &ref, &use, &metric, &m,
-                                  &mtu, &win, &ir);
+                               devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+                               &mtu, &win, &ir);
                if (r != 11) {
+ ERROR:
                        if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
                                break;
                        }
-               ERROR:
-                       bb_error_msg_and_die("fscanf");
+                       bb_perror_msg_and_die(bb_msg_read_error);
                }
 
                if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
@@ -528,28 +549,28 @@ void displayroutes(int noresolve, int netstatfmt)
                memset(&s_addr, 0, sizeof(struct sockaddr_in));
                s_addr.sin_family = AF_INET;
                s_addr.sin_addr.s_addr = d;
-               INET_rresolve(sdest, sizeof(sdest), &s_addr,
-                                         (noresolve | 0x8000), m);     /* Default instead of *. */
-
+               sdest = INET_rresolve(&s_addr, (noresolve | 0x8000), m); /* 'default' instead of '*' */
                s_addr.sin_addr.s_addr = g;
-               INET_rresolve(sgw, sizeof(sgw), &s_addr,
-                                         (noresolve | 0x4000), m);     /* Host instead of net. */
-
+               sgw = INET_rresolve(&s_addr, (noresolve | 0x4000), m); /* Host instead of net */
                mask.s_addr = m;
-               bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+               /* "%15.15s" truncates hostnames, do we really want that? */
+               printf("%-15.15s %-15.15s %-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+               free(sdest);
+               free(sgw);
                if (netstatfmt) {
-                       bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+                       printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
                } else {
-                       bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
+                       printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
                }
        }
+       fclose(fp);
 }
 
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
 
-static void INET6_displayroutes(int noresolve)
+static void INET6_displayroutes(void)
 {
-       char addr6[128], naddr6[128];
+       char addr6[128], *naddr6;
        /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
         * We read the non-delimited strings into the tail of the buffer
         * using fscanf and then modify the buffer by shifting forward
@@ -561,23 +582,23 @@ static void INET6_displayroutes(int noresolve)
        int iflags, metric, refcnt, use, prefix_len, slen;
        struct sockaddr_in6 snaddr6;
 
-       FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
+       FILE *fp = xfopen_for_read("/proc/net/ipv6_route");
 
-       bb_printf("Kernel IPv6 routing table\n%-44s%-40s"
-                         "Flags Metric Ref    Use Iface\n",
-                         "Destination", "Next Hop");
+       printf("Kernel IPv6 routing table\n%-44s%-40s"
+                       "Flags Metric Ref    Use Iface\n",
+                       "Destination", "Next Hop");
 
        while (1) {
                int r;
                r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
-                                  addr6x+14, &prefix_len, &slen, addr6x+40+7,
-                                  &metric, &use, &refcnt, &iflags, iface);
+                               addr6x+14, &prefix_len, &slen, addr6x+40+7,
+                               &metric, &refcnt, &use, &iflags, iface);
                if (r != 9) {
                        if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
                                break;
                        }
              ERROR:
-                       bb_error_msg_and_die("fscanf");
+ ERROR:
+                       bb_perror_msg_and_die(bb_msg_read_error);
                }
 
                /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
@@ -589,7 +610,7 @@ static void INET6_displayroutes(int noresolve)
 
                        do {
                                if (!*p) {
-                                       if (i==40) { /* nul terminator for 1st address? */
+                                       if (i == 40) { /* nul terminator for 1st address? */
                                                addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
                                                ++p;    /* Skip and continue. */
                                                continue;
@@ -597,81 +618,75 @@ static void INET6_displayroutes(int noresolve)
                                        goto ERROR;
                                }
                                addr6x[i++] = *p++;
-                               if (!((i+1)%5)) {
+                               if (!((i+1) % 5)) {
                                        addr6x[i++] = ':';
                                }
                        } while (i < 40+28+7);
                }
 
-               if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
-                       continue;
-               }
-
                set_flags(flags, (iflags & IPV6_MASK));
 
                r = 0;
-               do {
+               while (1) {
                        inet_pton(AF_INET6, addr6x + r,
                                          (struct sockaddr *) &snaddr6.sin6_addr);
                        snaddr6.sin6_family = AF_INET6;
-                       INET6_rresolve(naddr6, sizeof(naddr6),
-                                                  (struct sockaddr_in6 *) &snaddr6,
-#if 0
-                                                  (noresolve | 0x8000) /* Default instead of *. */
-#else
-                                                  0x0fff /* Apparently, upstream never resolves. */
-#endif
-                                                  );
+                       naddr6 = INET6_rresolve((struct sockaddr_in6 *) &snaddr6,
+                                               0x0fff /* Apparently, upstream never resolves. */
+                                               );
 
                        if (!r) {                       /* 1st pass */
                                snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
                                r += 40;
+                               free(naddr6);
                        } else {                        /* 2nd pass */
                                /* Print the info. */
-                               bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
-                                                 addr6, naddr6, flags, metric, refcnt, use, iface);
+                               printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+                                               addr6, naddr6, flags, metric, refcnt, use, iface);
+                               free(naddr6);
                                break;
                        }
-               } while (1);
+               }
        }
+       fclose(fp);
 }
 
 #endif
 
-#define ROUTE_OPT_A                    0x01
-#define ROUTE_OPT_n                    0x02
-#define ROUTE_OPT_e                    0x04
-#define ROUTE_OPT_INET6                0x08 /* Not an actual option. See below. */
+#define ROUTE_OPT_A     0x01
+#define ROUTE_OPT_n     0x02
+#define ROUTE_OPT_e     0x04
+#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
 
 /* 1st byte is offset to next entry offset.  2nd byte is return value. */
-static const char tbl_verb[] =         /* 2nd byte matches RTACTION_* code */
+/* 2nd byte matches RTACTION_* code */
+static const char tbl_verb[] ALIGN1 =
        "\006\001add\0"
        "\006\002del\0"
-/*     "\011\002delete\0" */
-       "\010\002delete"                        /* Since last, we can save a byte. */
+/*     "\011\002delete\0" */
+       "\010\002delete"  /* Since it's last, we can save a byte. */
 ;
 
-int route_main(int argc, char **argv)
+int route_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int route_main(int argc UNUSED_PARAM, char **argv)
 {
-       unsigned long opt;
+       unsigned opt;
        int what;
        char *family;
+       char **p;
 
        /* First, remap '-net' and '-host' to avoid getopt problems. */
-       {
-               char **p = argv;
-
-               while (*++p) {
-                       if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) {
-                               p[0][0] = '#';
-                       }
+       p = argv;
+       while (*++p) {
+               if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
+                       p[0][0] = '#';
                }
-       }       
+       }
 
-       opt = bb_getopt_ulflags(argc, argv, "A:ne", &family);
+       opt = getopt32(argv, "A:ne", &family);
 
-       if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) {
-#ifdef CONFIG_FEATURE_IPV6
+       if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
+#if ENABLE_FEATURE_IPV6
                if (strcmp(family, "inet6") == 0) {
                        opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
                } else
@@ -684,15 +699,14 @@ int route_main(int argc, char **argv)
        /* No more args means display the routing table. */
        if (!*argv) {
                int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
                if (opt & ROUTE_OPT_INET6)
-                       INET6_displayroutes(noresolve);
+                       INET6_displayroutes();
                else
 #endif
-                       displayroutes(noresolve, opt & ROUTE_OPT_e);
+                       bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
 
-               bb_xferror_stdout();
-               bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+               fflush_stdout_and_exit(EXIT_SUCCESS);
        }
 
        /* Check verb.  At the moment, must be add, del, or delete. */
@@ -701,7 +715,7 @@ int route_main(int argc, char **argv)
                bb_show_usage();
        }
 
-#ifdef CONFIG_FEATURE_IPV6
+#if ENABLE_FEATURE_IPV6
        if (opt & ROUTE_OPT_INET6)
                INET6_setroute(what, argv);
        else