Only compile the files in this dir if the IP applet is enabled
[oweals/busybox.git] / networking / route.c
index d571fc5a3a770065792777d3662c9b547dd535a1..5c4ce8ca31dc6cf5288716c36398847b0cb349d0 100644 (file)
@@ -1,11 +1,11 @@
 /* route
  *
  * Similar to the standard Unix route, but with only the necessary
- * parts for AF_INET
+ * parts for AF_INET and AF_INET6
  *
  * Bjorn Wesen, Axis Communications AB
  *
- * Author of the original route: 
+ * Author of the original route:
  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  *              (derived from FvK's 'route.c     1.70    01/04/94')
  *
  * Foundation;  either  version 2 of the License, or  (at
  * your option) any later version.
  *
- * $Id: route.c,v 1.10 2001/03/21 07:34:26 andersen Exp $
+ * $Id: route.c,v 1.18 2002/08/22 18:24:43 bug1 Exp $
  *
  * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
  * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
  */
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
-#include <sys/socket.h>
+#include "inet_common.h"
 #include <net/route.h>
-#include <linux/param.h>  // HZ
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/param.h>       /* HZ */
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
 #define E_INTERN        2
 #define E_NOSUPP        1
 
-/* resolve XXX.YYY.ZZZ.QQQ -> binary */
-
-static int
-INET_resolve(char *name, struct sockaddr *sa)
-{
-       struct sockaddr_in *s_in = (struct sockaddr_in *)sa;
-       
-       s_in->sin_family = AF_INET;
-       s_in->sin_port = 0;
-
-       /* Default is special, meaning 0.0.0.0. */
-       if (strcmp(name, "default")==0) {
-               s_in->sin_addr.s_addr = INADDR_ANY;
-               return 1;
-       }
-       /* Look to see if it's a dotted quad. */
-       if (inet_aton(name, &s_in->sin_addr)) {
-               return 0;
-       }
-       /* guess not.. */
-       return -1;
-}
-
-#if defined (SIOCADDRTOLD) || defined (RTF_IRTT)        /* route */
+#if defined (SIOCADDRTOLD) || defined (RTF_IRTT)       /* route */
 #define HAVE_NEW_ADDRT 1
 #endif
-#ifdef RTF_IRTT                 /* route */
+#ifdef RTF_IRTT                        /* route */
 #define HAVE_RTF_IRTT 1
 #endif
-#ifdef RTF_REJECT               /* route */
+#ifdef RTF_REJECT              /* route */
 #define HAVE_RTF_REJECT 1
 #endif
 
@@ -96,61 +74,66 @@ INET_resolve(char *name, struct sockaddr *sa)
 #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
 #endif
 
+
+
 /* add or delete a route depending on action */
 
-static int
-INET_setroute(int action, int options, char **args)
+static int INET_setroute(int action, int options, char **args)
 {
        struct rtentry rt;
-       char target[128], gateway[128] = "NONE", netmask[128] = "default";
+       char target[128], gateway[128] = "NONE";
+       const char *netmask = bb_INET_default;
        int xflag, isnet;
        int skfd;
 
        xflag = 0;
 
-       if (strcmp(*args, "-net")==0) {
+       if (*args == NULL)
+               show_usage();
+       if (strcmp(*args, "-net") == 0) {
                xflag = 1;
                args++;
-       } else if (strcmp(*args, "-host")==0) {
+       } else if (strcmp(*args, "-host") == 0) {
                xflag = 2;
                args++;
        }
        if (*args == NULL)
                show_usage();
-
        safe_strncpy(target, *args++, (sizeof target));
 
        /* Clean out the RTREQ structure. */
        memset((char *) &rt, 0, sizeof(struct rtentry));
 
 
-       if ((isnet = INET_resolve(target, &rt.rt_dst)) < 0) {
+       if ((isnet =
+                INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+                                         xflag != 1)) < 0) {
                error_msg(_("can't resolve %s"), target);
-               return EXIT_FAILURE;   /* XXX change to E_something */
+               return EXIT_FAILURE;    /* XXX change to E_something */
        }
 
        switch (xflag) {
-               case 1:
-                       isnet = 1;
-                       break;
-                       
-               case 2:
-                       isnet = 0;
-                       break;
-                       
-               default:
-                       break;
+       case 1:
+               isnet = 1;
+               break;
+
+       case 2:
+               isnet = 0;
+               break;
+
+       default:
+               break;
        }
-       
+
        /* Fill in the other fields. */
        rt.rt_flags = (RTF_UP | RTF_HOST);
        if (isnet)
                rt.rt_flags &= ~RTF_HOST;
 
        while (*args) {
-               if (strcmp(*args, "metric")==0) {
+               if (strcmp(*args, "metric") == 0) {
                        int metric;
-                       
+
                        args++;
                        if (!*args || !isdigit(**args))
                                show_usage();
@@ -158,20 +141,22 @@ INET_setroute(int action, int options, char **args)
 #if HAVE_NEW_ADDRT
                        rt.rt_metric = metric + 1;
 #else
-                       ENOSUPP("inet_setroute", "NEW_ADDRT (metric)");  /* XXX Fixme */
+                       ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
 #endif
                        args++;
                        continue;
                }
 
-               if (strcmp(*args, "netmask")==0) {
+               if (strcmp(*args, "netmask") == 0) {
                        struct sockaddr mask;
-                       
+
                        args++;
                        if (!*args || mask_in_addr(rt))
                                show_usage();
-                       safe_strncpy(netmask, *args, (sizeof netmask));
-                       if ((isnet = INET_resolve(netmask, &mask)) < 0) {
+                       netmask = *args;
+                       if ((isnet =
+                                INET_resolve(netmask, (struct sockaddr_in *) &mask,
+                                                         0)) < 0) {
                                error_msg(_("can't resolve netmask %s"), netmask);
                                return E_LOOKUP;
                        }
@@ -180,21 +165,21 @@ INET_setroute(int action, int options, char **args)
                        continue;
                }
 
-               if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) {
+               if (strcmp(*args, "gw") == 0 || strcmp(*args, "gateway") == 0) {
                        args++;
                        if (!*args)
                                show_usage();
                        if (rt.rt_flags & RTF_GATEWAY)
                                show_usage();
                        safe_strncpy(gateway, *args, (sizeof gateway));
-                       if ((isnet = INET_resolve(gateway, &rt.rt_gateway)) < 0) {
+                       if ((isnet =
+                                INET_resolve(gateway, (struct sockaddr_in *) &rt.rt_gateway,
+                                                         1)) < 0) {
                                error_msg(_("can't resolve gw %s"), gateway);
                                return E_LOOKUP;
                        }
                        if (isnet) {
-                               error_msg(
-                                       _("%s: cannot use a NETWORK as gateway!"),
-                                       gateway);
+                               error_msg(_("%s: cannot use a NETWORK as gateway!"), gateway);
                                return E_OPTERR;
                        }
                        rt.rt_flags |= RTF_GATEWAY;
@@ -202,7 +187,7 @@ INET_setroute(int action, int options, char **args)
                        continue;
                }
 
-               if (strcmp(*args, "mss")==0) {
+               if (strcmp(*args, "mss") == 0) {
                        args++;
                        rt.rt_flags |= RTF_MSS;
                        if (!*args)
@@ -216,7 +201,7 @@ INET_setroute(int action, int options, char **args)
                        continue;
                }
 
-               if (strcmp(*args, "window")==0) {
+               if (strcmp(*args, "window") == 0) {
                        args++;
                        if (!*args)
                                show_usage();
@@ -230,7 +215,7 @@ INET_setroute(int action, int options, char **args)
                        continue;
                }
 
-               if (strcmp(*args, "irtt")==0) {
+               if (strcmp(*args, "irtt") == 0) {
                        args++;
                        if (!*args)
                                show_usage();
@@ -239,43 +224,43 @@ INET_setroute(int action, int options, char **args)
                        rt.rt_flags |= RTF_IRTT;
                        rt.rt_irtt = atoi(*(args - 1));
                        rt.rt_irtt *= (HZ / 100);       /* FIXME */
-#if 0                          /* FIXME: do we need to check anything of this? */
+#if 0                                  /* FIXME: do we need to check anything of this? */
                        if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
                                error_msg(_("Invalid initial rtt."));
                                return E_OPTERR;
                        }
 #endif
 #else
-                       ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
+                       ENOSUPP("inet_setroute", "RTF_IRTT");   /* XXX Fixme */
 #endif
                        continue;
                }
 
-               if (strcmp(*args, "reject")==0) {
+               if (strcmp(*args, "reject") == 0) {
                        args++;
 #if HAVE_RTF_REJECT
                        rt.rt_flags |= RTF_REJECT;
 #else
-                       ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
+                       ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
 #endif
                        continue;
                }
-               if (strcmp(*args, "mod")==0) {
+               if (strcmp(*args, "mod") == 0) {
                        args++;
                        rt.rt_flags |= RTF_MODIFIED;
                        continue;
                }
-               if (strcmp(*args, "dyn")==0) {
+               if (strcmp(*args, "dyn") == 0) {
                        args++;
                        rt.rt_flags |= RTF_DYNAMIC;
                        continue;
                }
-               if (strcmp(*args, "reinstate")==0) {
+               if (strcmp(*args, "reinstate") == 0) {
                        args++;
                        rt.rt_flags |= RTF_REINSTATE;
                        continue;
                }
-               if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) {
+               if (strcmp(*args, "device") == 0 || strcmp(*args, "dev") == 0) {
                        args++;
                        if (rt.rt_dev || *args == NULL)
                                show_usage();
@@ -300,11 +285,11 @@ INET_setroute(int action, int options, char **args)
        /* sanity checks.. */
        if (mask_in_addr(rt)) {
                unsigned long mask = mask_in_addr(rt);
+
                mask = ~ntohl(mask);
                if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
-                       error_msg(
-                               _("netmask %.8x doesn't make sense with host route"),
-                               (unsigned int)mask);
+                       error_msg(_("netmask %.8x doesn't make sense with host route"),
+                                         (unsigned int) mask);
                        return E_OPTERR;
                }
                if (mask & (mask + 1)) {
@@ -340,89 +325,383 @@ INET_setroute(int action, int options, char **args)
                        return E_SOCK;
                }
        }
-       
+
        /* Close the socket. */
        (void) close(skfd);
        return EXIT_SUCCESS;
 }
 
-static void displayroutes(void)
+#if CONFIG_FEATURE_IPV6
+static int INET6_setroute(int action, int options, char **args)
+{
+       struct in6_rtmsg rt;
+       struct ifreq ifr;
+       struct sockaddr_in6 sa6;
+       char target[128], gateway[128] = "NONE";
+       int metric, prefix_len;
+       char *devname = NULL;
+       char *cp;
+       int skfd;
+
+       if (*args == NULL)
+               show_usage();
+
+       strcpy(target, *args++);
+       if (!strcmp(target, "default")) {
+               prefix_len = 0;
+               memset(&sa6, 0, sizeof(sa6));
+       } else {
+               if ((cp = strchr(target, '/'))) {
+                       prefix_len = atol(cp + 1);
+                       if ((prefix_len < 0) || (prefix_len > 128))
+                               show_usage();
+                       *cp = 0;
+               } else {
+                       prefix_len = 128;
+               }
+               if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+                       error_msg(_("can't resolve %s"), target);
+                       return EXIT_FAILURE;    /* XXX change to E_something */
+               }
+       }
+
+       /* Clean out the RTREQ structure. */
+       memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+
+       memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+       /* Fill in the other fields. */
+       rt.rtmsg_flags = RTF_UP;
+       if (prefix_len == 128)
+               rt.rtmsg_flags |= RTF_HOST;
+       rt.rtmsg_metric = 1;
+       rt.rtmsg_dst_len = prefix_len;
+
+       while (*args) {
+               if (!strcmp(*args, "metric")) {
+
+                       args++;
+                       if (!*args || !isdigit(**args))
+                               show_usage();
+                       metric = atoi(*args);
+                       rt.rtmsg_metric = metric;
+                       args++;
+                       continue;
+               }
+               if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
+                       args++;
+                       if (!*args)
+                               show_usage();
+                       if (rt.rtmsg_flags & RTF_GATEWAY)
+                               show_usage();
+                       strcpy(gateway, *args);
+                       if (INET6_resolve(gateway, (struct sockaddr_in6 *) &sa6) < 0) {
+                               error_msg(_("can't resolve gw %s"), gateway);
+                               return (E_LOOKUP);
+                       }
+                       memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+                                  sizeof(struct in6_addr));
+                       rt.rtmsg_flags |= RTF_GATEWAY;
+                       args++;
+                       continue;
+               }
+               if (!strcmp(*args, "mod")) {
+                       args++;
+                       rt.rtmsg_flags |= RTF_MODIFIED;
+                       continue;
+               }
+               if (!strcmp(*args, "dyn")) {
+                       args++;
+                       rt.rtmsg_flags |= RTF_DYNAMIC;
+                       continue;
+               }
+               if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
+                       args++;
+                       if (!*args)
+                               show_usage();
+               } else if (args[1])
+                       show_usage();
+
+               devname = *args;
+               args++;
+       }
+
+       /* Create a socket to the INET6 kernel. */
+       if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+               perror("socket");
+               return (E_SOCK);
+       }
+       if (devname) {
+               memset(&ifr, 0, sizeof(ifr));
+               strcpy(ifr.ifr_name, devname);
+
+               if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+                       perror("SIOGIFINDEX");
+                       return (E_SOCK);
+               }
+               rt.rtmsg_ifindex = ifr.ifr_ifindex;
+       } else
+               rt.rtmsg_ifindex = 0;
+
+       /* Tell the kernel to accept this route. */
+       if (action == RTACTION_DEL) {
+               if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
+                       perror("SIOCDELRT");
+                       close(skfd);
+                       return (E_SOCK);
+               }
+       } else {
+               if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
+                       perror("SIOCADDRT");
+                       close(skfd);
+                       return (E_SOCK);
+               }
+       }
+
+       /* Close the socket. */
+       (void) close(skfd);
+       return (0);
+}
+#endif
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP          0x0001 /* route usable                 */
+#define RTF_GATEWAY     0x0002 /* destination is a gateway     */
+#define RTF_HOST        0x0004 /* host entry (net otherwise)   */
+#define RTF_REINSTATE   0x0008 /* reinstate route after tmout  */
+#define RTF_DYNAMIC     0x0010 /* created dyn. (by redirect)   */
+#define RTF_MODIFIED    0x0020 /* modified dyn. (by redirect)  */
+#define RTF_MTU         0x0040 /* specific MTU for this route  */
+#ifndef RTF_MSS
+#define RTF_MSS         RTF_MTU        /* Compatibility :-(            */
+#endif
+#define RTF_WINDOW      0x0080 /* per route window clamping    */
+#define RTF_IRTT        0x0100 /* Initial round trip time      */
+#define RTF_REJECT      0x0200 /* Reject route                 */
+#endif
+
+void displayroutes(int noresolve, int netstatfmt)
 {
        char buff[256];
-       int  nl = 0 ;
+       int nl = 0;
        struct in_addr dest;
        struct in_addr gw;
        struct in_addr mask;
-       int flgs, ref, use, metric;
-       char flags[4];
-       unsigned long int d,g,m;
+       int flgs, ref, use, metric, mtu, win, ir;
+       char flags[64];
+       unsigned long int d, g, m;
 
        char sdest[16], sgw[16];
 
-
        FILE *fp = xfopen("/proc/net/route", "r");
 
-       while( fgets(buff, sizeof(buff), fp) != NULL ) {
-               if(nl) {
+       if (noresolve)
+               noresolve = 0x0fff;
+
+       while (fgets(buff, sizeof(buff), fp) != NULL) {
+               if (nl) {
                        int ifl = 0;
-                       while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0')
+                       int numeric;
+                       struct sockaddr_in s_addr;
+
+                       while (buff[ifl] != ' ' && buff[ifl] != '\t' && buff[ifl] != '\0')
                                ifl++;
-                       buff[ifl]=0;    /* interface */
-                       if(sscanf(buff+ifl+1, "%lx%lx%d%d%d%d%lx",
-                          &d, &g, &flgs, &ref, &use, &metric, &m)!=7) {
-                               error_msg_and_die( "Unsuported kernel route format\n");
+                       buff[ifl] = 0;  /* interface */
+                       if (sscanf(buff + ifl + 1, "%lx%lx%X%d%d%d%lx%d%d%d",
+                                          &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win,
+                                          &ir) != 10) {
+                               error_msg_and_die("Unsuported kernel route format\n");
                        }
-                       if(nl==1) {
-                printf("Kernel IP routing table\n"
-"Destination     Gateway         Genmask         Flags Metric Ref    Use Iface\n");
+                       if (nl == 1) {
+                               printf("Kernel IP routing table\n");
+                               printf
+                                       ("Destination     Gateway         Genmask         Flags %s Iface\n",
+                                        netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
                        }
+                       ifl = 0;        /* parse flags */
+                       if (flgs & RTF_UP) {
+                               if (flgs & RTF_REJECT)
+                                       flags[ifl++] = '!';
+                               else
+                                       flags[ifl++] = 'U';
+                               if (flgs & RTF_GATEWAY)
+                                       flags[ifl++] = 'G';
+                               if (flgs & RTF_HOST)
+                                       flags[ifl++] = 'H';
+                               if (flgs & RTF_REINSTATE)
+                                       flags[ifl++] = 'R';
+                               if (flgs & RTF_DYNAMIC)
+                                       flags[ifl++] = 'D';
+                               if (flgs & RTF_MODIFIED)
+                                       flags[ifl++] = 'M';
+                               flags[ifl] = 0;
+                               dest.s_addr = d;
+                               gw.s_addr = g;
+                               mask.s_addr = m;
+                               memset(&s_addr, 0, sizeof(struct sockaddr_in));
+                               s_addr.sin_family = AF_INET;
+                               s_addr.sin_addr = dest;
+                               numeric = noresolve | 0x8000;   /* default instead of * */
+                               INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
+                               numeric = noresolve | 0x4000;   /* host instead of net */
+                               s_addr.sin_addr = gw;
+                               INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
+
+                               printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask),
+                                          flags);
+                               if (netstatfmt)
+                                       printf("%5d %-5d %6d %s\n", mtu, win, ir, buff);
+                               else
+                                       printf("%-6d %-2d %7d %s\n", metric, ref, use, buff);
+                       }
+               }
+               nl++;
+       }
+}
 
-
-                       ifl = 0;        /* parse flags */
-                       if(flgs&1)
-                               flags[ifl++]='U';
-                       if(flgs&2)
-                               flags[ifl++]='G';
-                       if(flgs&4)
-                               flags[ifl++]='H';
-                       flags[ifl]=0;
-                       dest.s_addr = d;
-                       gw.s_addr   = g;
-                       mask.s_addr = m;
-                       strcpy(sdest,  (dest.s_addr==0 ? "default" :
-                                       inet_ntoa(dest)));
-                       strcpy(sgw,    (gw.s_addr==0   ? "*"       :
-                                       inet_ntoa(gw)));
-                       printf("%-16s%-16s%-16s%-6s%-6d %-2d %7d %s\n",
-                               sdest, sgw,
-                               inet_ntoa(mask),
-                               flags, metric, ref, use, buff);
+#if CONFIG_FEATURE_IPV6
+static void INET6_displayroutes(int noresolve)
+{
+       char buff[256];
+       char iface[16], flags[16];
+       char addr6[128], naddr6[128];
+       struct sockaddr_in6 saddr6, snaddr6;
+       int iflags, metric, refcnt, use, prefix_len, slen;
+       int numeric;
+
+       char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
+
+       FILE *fp = xfopen("/proc/net/ipv6_route", "r");
+
+       flags[0] = 'U';
+
+       if (noresolve)
+               noresolve = 0x0fff;
+       numeric = noresolve | 0x8000;   /* default instead of * */
+
+       printf("Kernel IPv6 routing table\n"
+                  "Destination                                 "
+                  "Next Hop                                "
+                  "Flags Metric Ref    Use Iface\n");
+
+       while (fgets(buff, sizeof(buff), fp) != NULL) {
+               int ifl;
+
+               if (sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+                                  "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+                                  "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
+                                  addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+                                  addr6p[4], addr6p[5], addr6p[6], addr6p[7],
+                                  &prefix_len,
+                                  saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
+                                  saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
+                                  &slen,
+                                  naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+                                  naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
+                                  &metric, &use, &refcnt, &iflags, iface) != 31) {
+                       error_msg_and_die("Unsuported kernel route format\n");
                }
-       nl++;
+
+               ifl = 1;                /* parse flags */
+               if (!(iflags & RTF_UP))
+                       continue;
+               if (iflags & RTF_GATEWAY)
+                       flags[ifl++] = 'G';
+               if (iflags & RTF_HOST)
+                       flags[ifl++] = 'H';
+               if (iflags & RTF_DEFAULT)
+                       flags[ifl++] = 'D';
+               if (iflags & RTF_ADDRCONF)
+                       flags[ifl++] = 'A';
+               if (iflags & RTF_CACHE)
+                       flags[ifl++] = 'C';
+               flags[ifl] = 0;
+
+               /* Fetch and resolve the target address. */
+               snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+                                addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+                                addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr);
+               saddr6.sin6_family = AF_INET6;
+
+               INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6,
+                                          numeric);
+               snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len);
+
+               /* Fetch and resolve the nexthop address. */
+               snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+                                naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+                                naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
+               inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr);
+               snaddr6.sin6_family = AF_INET6;
+
+               INET6_rresolve(naddr6, sizeof(naddr6),
+                                          (struct sockaddr_in6 *) &snaddr6, numeric);
+
+               /* Print the info. */
+               printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+                          addr6, naddr6, flags, metric, refcnt, use, iface);
        }
 }
+#endif
 
 int route_main(int argc, char **argv)
 {
+       int opt;
        int what = 0;
 
-       argc--;
-       argv++;
+#if CONFIG_FEATURE_IPV6
+       int af = AF_INET;
+#endif
+
+       if (!argv[1] || (argv[1][0] == '-')) {
+               /* check options */
+               int noresolve = 0;
+               int extended = 0;
+
+               while ((opt = getopt(argc, argv, "A:ne")) > 0) {
+                       switch (opt) {
+                       case 'n':
+                               noresolve = 1;
+                               break;
+                       case 'e':
+                               extended = 1;
+                               break;
+                       case 'A':
+#if CONFIG_FEATURE_IPV6
+                               if (strcmp(optarg, "inet6") == 0)
+                                       af = AF_INET6;
+                               break;
+#endif
+                       default:
+                               show_usage();
+                       }
+               }
 
-       if (*argv == NULL) {
-               displayroutes();
+#if CONFIG_FEATURE_IPV6
+               if (af == AF_INET6)
+                       INET6_displayroutes(*argv != NULL);
+               else
+#endif
+                       displayroutes(noresolve, extended);
                return EXIT_SUCCESS;
        } else {
                /* check verb */
-               if (strcmp(*argv, "add")==0)
+               if (strcmp(argv[1], "add") == 0)
                        what = RTACTION_ADD;
-               else if (strcmp(*argv, "del")==0 || strcmp(*argv, "delete")==0)
+               else if (strcmp(argv[1], "del") == 0
+                                || strcmp(argv[1], "delete") == 0)
                        what = RTACTION_DEL;
-               else if (strcmp(*argv, "flush")==0)
+               else if (strcmp(argv[1], "flush") == 0)
                        what = RTACTION_FLUSH;
                else
                        show_usage();
        }
 
-       return INET_setroute(what, 0, ++argv);
+#if CONFIG_FEATURE_IPV6
+       if (af == AF_INET6)
+               return INET6_setroute(what, 0, argv + 2);
+#endif
+       return INET_setroute(what, 0, argv + 2);
 }