This patch from Bart Visscher <magick@linux-fan.com> adds
[oweals/busybox.git] / networking / route.c
1 /* route
2  *
3  * Similar to the standard Unix route, but with only the necessary
4  * parts for AF_INET and AF_INET6
5  *
6  * Bjorn Wesen, Axis Communications AB
7  *
8  * Author of the original route:
9  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
10  *              (derived from FvK's 'route.c     1.70    01/04/94')
11  *
12  * This program is free software; you can redistribute it
13  * and/or  modify it under  the terms of  the GNU General
14  * Public  License as  published  by  the  Free  Software
15  * Foundation;  either  version 2 of the License, or  (at
16  * your option) any later version.
17  *
18  * $Id: route.c,v 1.17 2002/07/03 11:46:34 andersen Exp $
19  *
20  * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
21  * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
22  *
23  * IPV6 support added by Bart Visscher <magick@linux-fan.com>
24  */
25
26 #include <sys/types.h>
27 #include <sys/ioctl.h>
28 #include "inet_common.h"
29 #include <net/route.h>
30 #include <net/if.h>
31 #include <linux/param.h>  // HZ
32 #include <stdio.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <getopt.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include "busybox.h"
41
42 #define _(x) x
43
44 #define RTACTION_ADD   1
45 #define RTACTION_DEL   2
46 #define RTACTION_HELP  3
47 #define RTACTION_FLUSH 4
48 #define RTACTION_SHOW  5
49
50 #define E_NOTFOUND      8
51 #define E_SOCK          7
52 #define E_LOOKUP        6
53 #define E_VERSION       5
54 #define E_USAGE         4
55 #define E_OPTERR        3
56 #define E_INTERN        2
57 #define E_NOSUPP        1
58
59 #if defined (SIOCADDRTOLD) || defined (RTF_IRTT)        /* route */
60 #define HAVE_NEW_ADDRT 1
61 #endif
62 #ifdef RTF_IRTT                 /* route */
63 #define HAVE_RTF_IRTT 1
64 #endif
65 #ifdef RTF_REJECT               /* route */
66 #define HAVE_RTF_REJECT 1
67 #endif
68
69 #if HAVE_NEW_ADDRT
70 #define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
71 #define full_mask(x) (x)
72 #else
73 #define mask_in_addr(x) ((x).rt_genmask)
74 #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
75 #endif
76
77
78
79 /* add or delete a route depending on action */
80
81 static int
82 INET_setroute(int action, int options, char **args)
83 {
84         struct rtentry rt;
85         char target[128], gateway[128] = "NONE";
86         const char *netmask = bb_INET_default;
87         int xflag, isnet;
88         int skfd;
89
90         xflag = 0;
91
92         if (*args == NULL)
93             show_usage();
94         if (strcmp(*args, "-net")==0) {
95                 xflag = 1;
96                 args++;
97         } else if (strcmp(*args, "-host")==0) {
98                 xflag = 2;
99                 args++;
100         }
101         if (*args == NULL)
102             show_usage();
103         safe_strncpy(target, *args++, (sizeof target));
104
105         /* Clean out the RTREQ structure. */
106         memset((char *) &rt, 0, sizeof(struct rtentry));
107
108
109         if ((isnet = INET_resolve(target, (struct sockaddr_in *)&rt.rt_dst, xflag!=1)) < 0) {
110                 error_msg(_("can't resolve %s"), target);
111                 return EXIT_FAILURE;   /* XXX change to E_something */
112         }
113
114         switch (xflag) {
115                 case 1:
116                         isnet = 1;
117                         break;
118
119                 case 2:
120                         isnet = 0;
121                         break;
122
123                 default:
124                         break;
125         }
126
127         /* Fill in the other fields. */
128         rt.rt_flags = (RTF_UP | RTF_HOST);
129         if (isnet)
130                 rt.rt_flags &= ~RTF_HOST;
131
132         while (*args) {
133                 if (strcmp(*args, "metric")==0) {
134                         int metric;
135
136                         args++;
137                         if (!*args || !isdigit(**args))
138                                 show_usage();
139                         metric = atoi(*args);
140 #if HAVE_NEW_ADDRT
141                         rt.rt_metric = metric + 1;
142 #else
143                         ENOSUPP("inet_setroute", "NEW_ADDRT (metric)");  /* XXX Fixme */
144 #endif
145                         args++;
146                         continue;
147                 }
148
149                 if (strcmp(*args, "netmask")==0) {
150                         struct sockaddr mask;
151
152                         args++;
153                         if (!*args || mask_in_addr(rt))
154                                 show_usage();
155                         netmask = *args;
156                         if ((isnet = INET_resolve(netmask, (struct sockaddr_in *)&mask, 0)) < 0) {
157                                 error_msg(_("can't resolve netmask %s"), netmask);
158                                 return E_LOOKUP;
159                         }
160                         rt.rt_genmask = full_mask(mask);
161                         args++;
162                         continue;
163                 }
164
165                 if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) {
166                         args++;
167                         if (!*args)
168                                 show_usage();
169                         if (rt.rt_flags & RTF_GATEWAY)
170                                 show_usage();
171                         safe_strncpy(gateway, *args, (sizeof gateway));
172                         if ((isnet = INET_resolve(gateway, (struct sockaddr_in *)&rt.rt_gateway, 1)) < 0) {
173                                 error_msg(_("can't resolve gw %s"), gateway);
174                                 return E_LOOKUP;
175                         }
176                         if (isnet) {
177                                 error_msg(
178                                         _("%s: cannot use a NETWORK as gateway!"),
179                                         gateway);
180                                 return E_OPTERR;
181                         }
182                         rt.rt_flags |= RTF_GATEWAY;
183                         args++;
184                         continue;
185                 }
186
187                 if (strcmp(*args, "mss")==0) {
188                         args++;
189                         rt.rt_flags |= RTF_MSS;
190                         if (!*args)
191                                 show_usage();
192                         rt.rt_mss = atoi(*args);
193                         args++;
194                         if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
195                                 error_msg(_("Invalid MSS."));
196                                 return E_OPTERR;
197                         }
198                         continue;
199                 }
200
201                 if (strcmp(*args, "window")==0) {
202                         args++;
203                         if (!*args)
204                                 show_usage();
205                         rt.rt_flags |= RTF_WINDOW;
206                         rt.rt_window = atoi(*args);
207                         args++;
208                         if (rt.rt_window < 128) {
209                                 error_msg(_("Invalid window."));
210                                 return E_OPTERR;
211                         }
212                         continue;
213                 }
214
215                 if (strcmp(*args, "irtt")==0) {
216                         args++;
217                         if (!*args)
218                                 show_usage();
219                         args++;
220 #if HAVE_RTF_IRTT
221                         rt.rt_flags |= RTF_IRTT;
222                         rt.rt_irtt = atoi(*(args - 1));
223                         rt.rt_irtt *= (HZ / 100);       /* FIXME */
224 #if 0                           /* FIXME: do we need to check anything of this? */
225                         if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
226                                 error_msg(_("Invalid initial rtt."));
227                                 return E_OPTERR;
228                         }
229 #endif
230 #else
231                         ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
232 #endif
233                         continue;
234                 }
235
236                 if (strcmp(*args, "reject")==0) {
237                         args++;
238 #if HAVE_RTF_REJECT
239                         rt.rt_flags |= RTF_REJECT;
240 #else
241                         ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
242 #endif
243                         continue;
244                 }
245                 if (strcmp(*args, "mod")==0) {
246                         args++;
247                         rt.rt_flags |= RTF_MODIFIED;
248                         continue;
249                 }
250                 if (strcmp(*args, "dyn")==0) {
251                         args++;
252                         rt.rt_flags |= RTF_DYNAMIC;
253                         continue;
254                 }
255                 if (strcmp(*args, "reinstate")==0) {
256                         args++;
257                         rt.rt_flags |= RTF_REINSTATE;
258                         continue;
259                 }
260                 if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) {
261                         args++;
262                         if (rt.rt_dev || *args == NULL)
263                                 show_usage();
264                         rt.rt_dev = *args++;
265                         continue;
266                 }
267                 /* nothing matches */
268                 if (!rt.rt_dev) {
269                         rt.rt_dev = *args++;
270                         if (*args)
271                                 show_usage();   /* must be last to catch typos */
272                 } else {
273                         show_usage();
274                 }
275         }
276
277 #if HAVE_RTF_REJECT
278         if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
279                 rt.rt_dev = "lo";
280 #endif
281
282         /* sanity checks.. */
283         if (mask_in_addr(rt)) {
284                 unsigned long mask = mask_in_addr(rt);
285                 mask = ~ntohl(mask);
286                 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
287                         error_msg(
288                                 _("netmask %.8x doesn't make sense with host route"),
289                                 (unsigned int)mask);
290                         return E_OPTERR;
291                 }
292                 if (mask & (mask + 1)) {
293                         error_msg(_("bogus netmask %s"), netmask);
294                         return E_OPTERR;
295                 }
296                 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
297                 if (mask & ~mask_in_addr(rt)) {
298                         error_msg(_("netmask doesn't match route address"));
299                         return E_OPTERR;
300                 }
301         }
302         /* Fill out netmask if still unset */
303         if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
304                 mask_in_addr(rt) = 0xffffffff;
305
306         /* Create a socket to the INET kernel. */
307         if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
308                 perror("socket");
309                 return E_SOCK;
310         }
311         /* Tell the kernel to accept this route. */
312         if (action == RTACTION_DEL) {
313                 if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
314                         perror("SIOCDELRT");
315                         close(skfd);
316                         return E_SOCK;
317                 }
318         } else {
319                 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
320                         perror("SIOCADDRT");
321                         close(skfd);
322                         return E_SOCK;
323                 }
324         }
325
326         /* Close the socket. */
327         (void) close(skfd);
328         return EXIT_SUCCESS;
329 }
330
331 #if CONFIG_FEATURE_IPV6
332 static int INET6_setroute(int action, int options, char **args)
333 {
334         struct in6_rtmsg rt;
335         struct ifreq ifr;
336         struct sockaddr_in6 sa6;
337         char target[128], gateway[128] = "NONE";
338         int metric, prefix_len;
339         char *devname = NULL;
340         char *cp;
341         int skfd;
342
343         if (*args == NULL)
344                 show_usage();
345
346         strcpy(target, *args++);
347         if (!strcmp(target, "default")) {
348                 prefix_len = 0;
349                 memset(&sa6, 0, sizeof(sa6));
350         } else {
351                 if ((cp = strchr(target, '/'))) {
352                         prefix_len = atol(cp + 1);
353                         if ((prefix_len < 0) || (prefix_len > 128))
354                                 show_usage();
355                         *cp = 0;
356                 } else {
357                         prefix_len = 128;
358                 }
359                 if (INET6_resolve(target, (struct sockaddr_in6 *)&sa6) < 0) {
360                         error_msg(_("can't resolve %s"), target);
361                         return EXIT_FAILURE;   /* XXX change to E_something */
362                 }
363         }
364
365         /* Clean out the RTREQ structure. */
366         memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
367
368         memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
369
370         /* Fill in the other fields. */
371         rt.rtmsg_flags = RTF_UP;
372         if (prefix_len == 128)
373                 rt.rtmsg_flags |= RTF_HOST;
374         rt.rtmsg_metric = 1;
375         rt.rtmsg_dst_len = prefix_len;
376
377         while (*args) {
378                 if (!strcmp(*args, "metric")) {
379
380                         args++;
381                         if (!*args || !isdigit(**args))
382                                 show_usage();
383                         metric = atoi(*args);
384                         rt.rtmsg_metric = metric;
385                         args++;
386                         continue;
387                 }
388                 if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
389                         args++;
390                         if (!*args)
391                                 show_usage();
392                         if (rt.rtmsg_flags & RTF_GATEWAY)
393                                 show_usage();
394                         strcpy(gateway, *args);
395                         if (INET6_resolve(gateway, (struct sockaddr_in6 *)&sa6) < 0) {
396                                 error_msg(_("can't resolve gw %s"), gateway);
397                                 return (E_LOOKUP);
398                         }
399                         memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
400                                sizeof(struct in6_addr));
401                         rt.rtmsg_flags |= RTF_GATEWAY;
402                         args++;
403                         continue;
404                 }
405                 if (!strcmp(*args, "mod")) {
406                         args++;
407                         rt.rtmsg_flags |= RTF_MODIFIED;
408                         continue;
409                 }
410                 if (!strcmp(*args, "dyn")) {
411                         args++;
412                         rt.rtmsg_flags |= RTF_DYNAMIC;
413                         continue;
414                 }
415                 if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
416                         args++;
417                         if (!*args)
418                                 show_usage();
419                 } else if (args[1])
420                         show_usage();
421
422                 devname = *args;
423                 args++;
424         }
425
426         /* Create a socket to the INET6 kernel. */
427         if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
428                 perror("socket");
429                 return (E_SOCK);
430         }
431         if (devname) {
432                 memset(&ifr, 0, sizeof(ifr));
433                 strcpy(ifr.ifr_name, devname);
434
435                 if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
436                         perror("SIOGIFINDEX");
437                         return (E_SOCK);
438                 }
439                 rt.rtmsg_ifindex = ifr.ifr_ifindex;
440         } else
441                 rt.rtmsg_ifindex = 0;
442
443         /* Tell the kernel to accept this route. */
444         if (action == RTACTION_DEL) {
445                 if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
446                         perror("SIOCDELRT");
447                         close(skfd);
448                         return (E_SOCK);
449                 }
450         } else {
451                 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
452                         perror("SIOCADDRT");
453                         close(skfd);
454                         return (E_SOCK);
455                 }
456         }
457
458         /* Close the socket. */
459         (void) close(skfd);
460         return (0);
461 }
462 #endif
463
464 #ifndef RTF_UP
465 /* Keep this in sync with /usr/src/linux/include/linux/route.h */
466 #define RTF_UP          0x0001          /* route usable                 */
467 #define RTF_GATEWAY     0x0002          /* destination is a gateway     */
468 #define RTF_HOST        0x0004          /* host entry (net otherwise)   */
469 #define RTF_REINSTATE   0x0008          /* reinstate route after tmout  */
470 #define RTF_DYNAMIC     0x0010          /* created dyn. (by redirect)   */
471 #define RTF_MODIFIED    0x0020          /* modified dyn. (by redirect)  */
472 #define RTF_MTU         0x0040          /* specific MTU for this route  */
473 #ifndef RTF_MSS
474 #define RTF_MSS         RTF_MTU         /* Compatibility :-(            */
475 #endif
476 #define RTF_WINDOW      0x0080          /* per route window clamping    */
477 #define RTF_IRTT        0x0100          /* Initial round trip time      */
478 #define RTF_REJECT      0x0200          /* Reject route                 */
479 #endif
480
481 void displayroutes(int noresolve, int netstatfmt)
482 {
483         char buff[256];
484         int  nl = 0 ;
485         struct in_addr dest;
486         struct in_addr gw;
487         struct in_addr mask;
488         int flgs, ref, use, metric, mtu, win, ir;
489         char flags[64];
490         unsigned long int d,g,m;
491
492         char sdest[16], sgw[16];
493
494         FILE *fp = xfopen("/proc/net/route", "r");
495
496         if(noresolve)
497                 noresolve = 0x0fff;
498
499         while( fgets(buff, sizeof(buff), fp) != NULL ) {
500                 if(nl) {
501                         int ifl = 0;
502                         int numeric;
503                         struct sockaddr_in s_addr;
504
505                         while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0')
506                                 ifl++;
507                         buff[ifl]=0;    /* interface */
508                         if(sscanf(buff+ifl+1, "%lx%lx%X%d%d%d%lx%d%d%d",
509                            &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win, &ir )!=10) {
510                                 error_msg_and_die( "Unsuported kernel route format\n");
511                         }
512                         if(nl==1) {
513                                 printf("Kernel IP routing table\n");
514                                 printf("Destination     Gateway         Genmask         Flags %s Iface\n",
515                                        netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
516                         }
517                         ifl = 0;        /* parse flags */
518                         if(flgs&RTF_UP) {
519                                 if(flgs&RTF_REJECT)
520                                         flags[ifl++]='!';
521                                 else
522                                         flags[ifl++]='U';
523                                 if(flgs&RTF_GATEWAY)
524                                         flags[ifl++]='G';
525                                 if(flgs&RTF_HOST)
526                                         flags[ifl++]='H';
527                                 if(flgs&RTF_REINSTATE)
528                                         flags[ifl++]='R';
529                                 if(flgs&RTF_DYNAMIC)
530                                         flags[ifl++]='D';
531                                 if(flgs&RTF_MODIFIED)
532                                         flags[ifl++]='M';
533                                 flags[ifl]=0;
534                                 dest.s_addr = d;
535                                 gw.s_addr   = g;
536                                 mask.s_addr = m;
537                                 memset(&s_addr, 0, sizeof(struct sockaddr_in));
538                                 s_addr.sin_family = AF_INET;
539                                 s_addr.sin_addr = dest;
540                                 numeric = noresolve | 0x8000; /* default instead of * */
541                                 INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
542                                 numeric = noresolve | 0x4000; /* host instead of net */
543                                 s_addr.sin_addr = gw;
544                                 INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
545
546                                 printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
547                                 if ( netstatfmt )
548                                         printf("%5d %-5d %6d %s\n", mtu, win, ir, buff);
549                                 else
550                                         printf("%-6d %-2d %7d %s\n", metric, ref, use, buff);
551                         }
552                 }
553                 nl++;
554         }
555 }
556
557 #if CONFIG_FEATURE_IPV6
558 static void INET6_displayroutes(int noresolve)
559 {
560         char buff[256];
561         char iface[16], flags[16];
562         char addr6[128], naddr6[128];
563         struct sockaddr_in6 saddr6, snaddr6;
564         int iflags, metric, refcnt, use, prefix_len, slen;
565         int numeric;
566
567         char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
568
569         FILE *fp = xfopen("/proc/net/ipv6_route", "r");
570         flags[0]='U';
571
572         if(noresolve)
573                 noresolve = 0x0fff;
574         numeric = noresolve | 0x8000; /* default instead of * */
575
576         printf("Kernel IPv6 routing table\n"
577                "Destination                                 "
578                "Next Hop                                "
579                "Flags Metric Ref    Use Iface\n");
580
581         while( fgets(buff, sizeof(buff), fp) != NULL ) {
582                 int ifl;
583
584                 if(sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
585                           "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
586                           "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
587                           addr6p[0], addr6p[1], addr6p[2], addr6p[3],
588                           addr6p[4], addr6p[5], addr6p[6], addr6p[7],
589                           &prefix_len,
590                           saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
591                           saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
592                           &slen,
593                           naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
594                           naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
595                           &metric, &use, &refcnt, &iflags, iface)!=31) {
596                         error_msg_and_die( "Unsuported kernel route format\n");
597                 }
598
599                 ifl = 1;        /* parse flags */
600                 if (!(iflags & RTF_UP))
601                         continue;
602                 if (iflags & RTF_GATEWAY)
603                         flags[ifl++]='G';
604                 if (iflags & RTF_HOST)
605                         flags[ifl++]='H';
606                 if (iflags & RTF_DEFAULT)
607                         flags[ifl++]='D';
608                 if (iflags & RTF_ADDRCONF)
609                         flags[ifl++]='A';
610                 if (iflags & RTF_CACHE)
611                         flags[ifl++]='C';
612                 flags[ifl]=0;
613
614                 /* Fetch and resolve the target address. */
615                 snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
616                          addr6p[0], addr6p[1], addr6p[2], addr6p[3],
617                          addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
618                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr);
619                 saddr6.sin6_family=AF_INET6;
620
621                 INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6, numeric);
622                 snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len);
623
624                 /* Fetch and resolve the nexthop address. */
625                 snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
626                          naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
627                          naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
628                 inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr);
629                 snaddr6.sin6_family=AF_INET6;
630
631                 INET6_rresolve(naddr6, sizeof(naddr6), (struct sockaddr_in6 *) &snaddr6, numeric);
632
633                 /* Print the info. */
634                 printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
635                        addr6, naddr6, flags, metric, refcnt, use, iface);
636         }
637 }
638 #endif
639
640 int route_main(int argc, char **argv)
641 {
642         int opt;
643         int what = 0;
644 #if CONFIG_FEATURE_IPV6
645         int af=AF_INET;
646 #endif
647
648         if ( !argv [1] || ( argv [1][0] == '-' )) {
649                 /* check options */
650                 int noresolve = 0;
651                 int extended = 0;
652         
653                 while ((opt = getopt(argc, argv, "A:ne")) > 0) {
654                         switch (opt) {
655                                 case 'n':
656                                         noresolve = 1;
657                                         break;
658                                 case 'e':
659                                         extended = 1;
660                                         break;
661                                 case 'A':
662 #if CONFIG_FEATURE_IPV6
663                                         if (strcmp(optarg, "inet6")==0)
664                                                 af=AF_INET6;
665                                         break;
666 #endif
667                                 default:
668                                         show_usage ( );
669                         }
670                 }
671         
672 #if CONFIG_FEATURE_IPV6
673                 if (af==AF_INET6)
674                         INET6_displayroutes(*argv != NULL);
675                 else
676 #endif
677                 displayroutes ( noresolve, extended );
678                 return EXIT_SUCCESS;
679         } else {
680                 /* check verb */
681                 if (strcmp( argv [1], "add")==0)
682                         what = RTACTION_ADD;
683                 else if (strcmp( argv [1], "del")==0 || strcmp( argv [1], "delete")==0)
684                         what = RTACTION_DEL;
685                 else if (strcmp( argv [1], "flush")==0)
686                         what = RTACTION_FLUSH;
687                 else
688                         show_usage();
689         }
690
691 #if CONFIG_FEATURE_IPV6
692         if (af==AF_INET6)
693                 return INET6_setroute(what, 0, argv+2);
694 #endif
695         return INET_setroute(what, 0, argv+2 ); 
696 }