aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2004-03-10 07:42:38 +0000
committerManuel Novoa III <mjn3@codepoet.org>2004-03-10 07:42:38 +0000
commit7d0c51919ce4d0be00fd8ae92d6f16b86aad9fd2 (patch)
treed19379419a02c8f77fc864db6b08450f016264dc
parent2715fa147a36662b59b58b78ff31e225e785fef9 (diff)
downloadbusybox-w32-7d0c51919ce4d0be00fd8ae92d6f16b86aad9fd2.tar.gz
busybox-w32-7d0c51919ce4d0be00fd8ae92d6f16b86aad9fd2.tar.bz2
busybox-w32-7d0c51919ce4d0be00fd8ae92d6f16b86aad9fd2.zip
In spite of the feature freeze, check in a complete rework of route which
fixes some bugs, adds some error checking, and removes _lots_ of bloat. Text size on i386... old new ipv6 5425 3523 no ipv6 3143 2193
-rw-r--r--include/inet_common.h4
-rw-r--r--include/usage.h14
-rw-r--r--libbb/inet_common.c4
-rw-r--r--networking/route.c964
4 files changed, 501 insertions, 485 deletions
diff --git a/include/inet_common.h b/include/inet_common.h
index 0375c1ce2..afea5deaa 100644
--- a/include/inet_common.h
+++ b/include/inet_common.h
@@ -4,7 +4,7 @@
4 * 4 *
5 * Heavily modified by Manuel Novoa III Mar 12, 2001 5 * Heavily modified by Manuel Novoa III Mar 12, 2001
6 * 6 *
7 * Version: $Id: inet_common.h,v 1.3 2003/07/14 21:20:52 andersen Exp $ 7 * Version: $Id: inet_common.h,v 1.4 2004/03/10 07:42:37 mjn3 Exp $
8 * 8 *
9 */ 9 */
10 10
@@ -29,5 +29,5 @@ extern int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirs
29extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, 29extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
30 int numeric, unsigned int netmask); 30 int numeric, unsigned int netmask);
31 31
32extern int INET6_resolve(char *name, struct sockaddr_in6 *sin6); 32extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6);
33extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric); 33extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric);
diff --git a/include/usage.h b/include/usage.h
index 9390e3415..37f88410f 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2039,13 +2039,21 @@
2039#define rmmod_example_usage \ 2039#define rmmod_example_usage \
2040 "$ rmmod tulip\n" 2040 "$ rmmod tulip\n"
2041 2041
2042#ifdef CONFIG_FEATURE_IPV6
2043 #define USAGE_ROUTE_IPV6(a) a
2044#else
2045 #define USAGE_ROUTE_IPV6(a) "\t"
2046#endif
2047
2048
2042#define route_trivial_usage \ 2049#define route_trivial_usage \
2043 "[{add|del|flush}]" 2050 "[{add|del|delete}]"
2044#define route_full_usage \ 2051#define route_full_usage \
2045 "Edit the kernel's routing tables.\n\n" \ 2052 "Edit the kernel's routing tables.\n\n" \
2046 "Options:\n" \ 2053 "Options:\n" \
2047 "\t-n\tDont resolve names.\n" \ 2054 "\t-n\t\tDont resolve names.\n" \
2048 "\t-e\tDisplay other/more information" 2055 "\t-e\t\tDisplay other/more information.\n" \
2056 "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family."
2049 2057
2050#define rpm_trivial_usage \ 2058#define rpm_trivial_usage \
2051 "-i -q[ildc]p package.rpm" 2059 "-i -q[ildc]p package.rpm"
diff --git a/libbb/inet_common.c b/libbb/inet_common.c
index 8b0ec8a0b..321322d1f 100644
--- a/libbb/inet_common.c
+++ b/libbb/inet_common.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * Heavily modified by Manuel Novoa III Mar 12, 2001 5 * Heavily modified by Manuel Novoa III Mar 12, 2001
6 * 6 *
7 * Version: $Id: inet_common.c,v 1.7 2003/07/14 21:20:55 andersen Exp $ 7 * Version: $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $
8 * 8 *
9 */ 9 */
10 10
@@ -186,7 +186,7 @@ int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
186 186
187#ifdef CONFIG_FEATURE_IPV6 187#ifdef CONFIG_FEATURE_IPV6
188 188
189int INET6_resolve(char *name, struct sockaddr_in6 *sin6) 189int INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
190{ 190{
191 struct addrinfo req, *ai; 191 struct addrinfo req, *ai;
192 int s; 192 int s;
diff --git a/networking/route.c b/networking/route.c
index 19942f421..111900d33 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -15,7 +15,7 @@
15 * Foundation; either version 2 of the License, or (at 15 * Foundation; either version 2 of the License, or (at
16 * your option) any later version. 16 * your option) any later version.
17 * 17 *
18 * $Id: route.c,v 1.23 2004/03/06 22:11:44 andersen Exp $ 18 * $Id: route.c,v 1.24 2004/03/10 07:42:38 mjn3 Exp $
19 * 19 *
20 * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru> 20 * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
21 * adjustments by Larry Doolittle <LRDoolittle@lbl.gov> 21 * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
@@ -23,47 +23,47 @@
23 * IPV6 support added by Bart Visscher <magick@linux-fan.com> 23 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
24 */ 24 */
25 25
26#include <sys/types.h> 26/* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org>
27#include <sys/ioctl.h> 27 *
28#include "inet_common.h" 28 * Rewritten to fix several bugs, add additional error checking, and
29#include <net/route.h> 29 * remove ridiculous amounts of bloat.
30#include <net/if.h> 30 */
31
31#include <stdio.h> 32#include <stdio.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <stdlib.h> 33#include <stdlib.h>
35#include <string.h> 34#include <string.h>
36#include <getopt.h> 35#include <errno.h>
36#include <assert.h>
37#include <unistd.h> 37#include <unistd.h>
38#include <ctype.h> 38#include <fcntl.h>
39#include <getopt.h>
40#include <sys/types.h>
41#include <sys/ioctl.h>
42#include <net/route.h>
43#include <net/if.h>
39#include "busybox.h" 44#include "busybox.h"
45#include "inet_common.h"
40 46
41#define _(x) x 47#ifndef RTF_UP
42 48/* Keep this in sync with /usr/src/linux/include/linux/route.h */
43#define RTACTION_ADD 1 49#define RTF_UP 0x0001 /* route usable */
44#define RTACTION_DEL 2 50#define RTF_GATEWAY 0x0002 /* destination is a gateway */
45#define RTACTION_HELP 3 51#define RTF_HOST 0x0004 /* host entry (net otherwise) */
46#define RTACTION_FLUSH 4 52#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
47#define RTACTION_SHOW 5 53#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
48 54#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
49#define E_NOTFOUND 8 55#define RTF_MTU 0x0040 /* specific MTU for this route */
50#define E_SOCK 7 56#ifndef RTF_MSS
51#define E_LOOKUP 6 57#define RTF_MSS RTF_MTU /* Compatibility :-( */
52#define E_VERSION 5 58#endif
53#define E_USAGE 4 59#define RTF_WINDOW 0x0080 /* per route window clamping */
54#define E_OPTERR 3 60#define RTF_IRTT 0x0100 /* Initial round trip time */
55#define E_INTERN 2 61#define RTF_REJECT 0x0200 /* Reject route */
56#define E_NOSUPP 1 62#endif
57 63
58#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ 64#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
59#define HAVE_NEW_ADDRT 1 65#define HAVE_NEW_ADDRT 1
60#endif 66#endif
61#ifdef RTF_IRTT /* route */
62#define HAVE_RTF_IRTT 1
63#endif
64#ifdef RTF_REJECT /* route */
65#define HAVE_RTF_REJECT 1
66#endif
67 67
68#if HAVE_NEW_ADDRT 68#if HAVE_NEW_ADDRT
69#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) 69#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
@@ -73,212 +73,227 @@
73#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr) 73#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
74#endif 74#endif
75 75
76/* The RTACTION entries must agree with tbl_verb[] below! */
77#define RTACTION_ADD 1
78#define RTACTION_DEL 2
79
80/* For the various tbl_*[] arrays, the 1st byte is the offset to
81 * the next entry and the 2nd byte is return value. */
82
83#define NET_FLAG 1
84#define HOST_FLAG 2
85
86/* We remap '-' to '#' to avoid problems with getopt. */
87static const char tbl_hash_net_host[] =
88 "\007\001#net\0"
89/* "\010\002#host\0" */
90 "\007\002#host" /* Since last, we can save a byte. */
91;
92
93#define KW_TAKES_ARG 020
94#define KW_SETS_FLAG 040
95
96#define KW_IPVx_METRIC 020
97#define KW_IPVx_NETMASK 021
98#define KW_IPVx_GATEWAY 022
99#define KW_IPVx_MSS 023
100#define KW_IPVx_WINDOW 024
101#define KW_IPVx_IRTT 025
102#define KW_IPVx_DEVICE 026
103
104#define KW_IPVx_FLAG_ONLY 040
105#define KW_IPVx_REJECT 040
106#define KW_IPVx_MOD 041
107#define KW_IPVx_DYN 042
108#define KW_IPVx_REINSTATE 043
109
110static const char tbl_ipvx[] =
111 /* 020 is the "takes an arg" bit */
112#if HAVE_NEW_ADDRT
113 "\011\020metric\0"
114#endif
115 "\012\021netmask\0"
116 "\005\022gw\0"
117 "\012\022gateway\0"
118 "\006\023mss\0"
119 "\011\024window\0"
120#ifdef RTF_IRTT
121 "\007\025irtt\0"
122#endif
123 "\006\026dev\0"
124 "\011\026device\0"
125 /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
126#ifdef RTF_REJECT
127 "\011\040reject\0"
128#endif
129 "\006\041mod\0"
130 "\006\042dyn\0"
131/* "\014\043reinstate\0" */
132 "\013\043reinstate" /* Since last, we can save a byte. */
133;
134
135static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
136#ifdef RTF_REJECT
137 RTF_REJECT,
138#endif
139 RTF_MODIFIED,
140 RTF_DYNAMIC,
141 RTF_REINSTATE
142};
76 143
144static int kw_lookup(const char *kwtbl, char ***pargs)
145{
146 if (**pargs) {
147 do {
148 if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
149 *pargs += 1;
150 if (kwtbl[1] & KW_TAKES_ARG) {
151 if (!**pargs) { /* No more args! */
152 bb_show_usage();
153 }
154 *pargs += 1; /* Calling routine will use args[-1]. */
155 }
156 return kwtbl[1];
157 }
158 kwtbl += *kwtbl;
159 } while (*kwtbl);
160 }
161 return 0;
162}
77 163
78/* add or delete a route depending on action */ 164/* Add or delete a route, depending on action. */
79 165
80static int INET_setroute(int action, int options, char **args) 166static void INET_setroute(int action, char **args)
81{ 167{
82 struct rtentry rt; 168 struct rtentry rt;
83 char target[128], gateway[128] = "NONE"; 169 const char *netmask;
84 const char *netmask = bb_INET_default; 170 int skfd, isnet, xflag;
85 int xflag, isnet;
86 int skfd;
87 171
88 xflag = 0; 172 assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
89 173
90 if (*args == NULL) 174 /* Grab the -net or -host options. Remember they were transformed. */
175 xflag = kw_lookup(tbl_hash_net_host, &args);
176
177 /* If we did grab -net or -host, make sure we still have an arg left. */
178 if (*args == NULL) {
91 bb_show_usage(); 179 bb_show_usage();
92 if (strcmp(*args, "-net") == 0) {
93 xflag = 1;
94 args++;
95 } else if (strcmp(*args, "-host") == 0) {
96 xflag = 2;
97 args++;
98 } 180 }
99 if (*args == NULL)
100 bb_show_usage();
101 safe_strncpy(target, *args++, (sizeof target));
102 181
103 /* Clean out the RTREQ structure. */ 182 /* Clean out the RTREQ structure. */
104 memset((char *) &rt, 0, sizeof(struct rtentry)); 183 memset((char *) &rt, 0, sizeof(struct rtentry));
105 184
185 {
186 const char *target = *args++;
106 187
107 if ((isnet = 188 /* Prefer hostname lookup is -host flag (xflag==1) was given. */
108 INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst, 189 isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
109 xflag != 1)) < 0) { 190 (xflag & HOST_FLAG));
110 bb_error_msg(_("can't resolve %s"), target); 191 if (isnet < 0) {
111 return EXIT_FAILURE; /* XXX change to E_something */ 192 bb_error_msg_and_die("resolving %s", target);
112 } 193 }
113
114 switch (xflag) {
115 case 1:
116 isnet = 1;
117 break;
118 194
119 case 2: 195 }
120 isnet = 0;
121 break;
122 196
123 default: 197 if (xflag) { /* Reinit isnet if -net or -host was specified. */
124 break; 198 isnet = (xflag & NET_FLAG);
125 } 199 }
126 200
127 /* Fill in the other fields. */ 201 /* Fill in the other fields. */
128 rt.rt_flags = (RTF_UP | RTF_HOST); 202 rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
129 if (isnet) 203
130 rt.rt_flags &= ~RTF_HOST; 204 netmask = bb_INET_default;
131 205
132 while (*args) { 206 while (*args) {
133 if (strcmp(*args, "metric") == 0) { 207 int k = kw_lookup(tbl_ipvx, &args);
134 int metric; 208 const char *args_m1 = args[-1];
209
210 if (k & KW_IPVx_FLAG_ONLY) {
211 rt.rt_flags |= flags_ipvx[k & 3];
212 continue;
213 }
135 214
136 args++;
137 if (!*args || !isdigit(**args))
138 bb_show_usage();
139 metric = atoi(*args);
140#if HAVE_NEW_ADDRT 215#if HAVE_NEW_ADDRT
141 rt.rt_metric = metric + 1; 216 if (k == KW_IPVx_METRIC) {
142#else 217 rt.rt_metric = bb_xgetularg10(args_m1) + 1;
143 ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
144#endif
145 args++;
146 continue; 218 continue;
147 } 219 }
220#endif
148 221
149 if (strcmp(*args, "netmask") == 0) { 222 if (k == KW_IPVx_NETMASK) {
150 struct sockaddr mask; 223 struct sockaddr mask;
151 224
152 args++; 225 if (mask_in_addr(rt)) {
153 if (!*args || mask_in_addr(rt))
154 bb_show_usage(); 226 bb_show_usage();
155 netmask = *args; 227 }
156 if ((isnet = 228
157 INET_resolve(netmask, (struct sockaddr_in *) &mask, 229 netmask = args_m1;
158 0)) < 0) { 230 isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
159 bb_error_msg(_("can't resolve netmask %s"), netmask); 231 if (isnet < 0) {
160 return E_LOOKUP; 232 bb_error_msg_and_die("resolving %s", netmask);
161 } 233 }
162 rt.rt_genmask = full_mask(mask); 234 rt.rt_genmask = full_mask(mask);
163 args++;
164 continue; 235 continue;
165 } 236 }
166 237
167 if (strcmp(*args, "gw") == 0 || strcmp(*args, "gateway") == 0) { 238 if (k == KW_IPVx_GATEWAY) {
168 args++; 239 if (rt.rt_flags & RTF_GATEWAY) {
169 if (!*args)
170 bb_show_usage(); 240 bb_show_usage();
171 if (rt.rt_flags & RTF_GATEWAY)
172 bb_show_usage();
173 safe_strncpy(gateway, *args, (sizeof gateway));
174 if ((isnet =
175 INET_resolve(gateway, (struct sockaddr_in *) &rt.rt_gateway,
176 1)) < 0) {
177 bb_error_msg(_("can't resolve gw %s"), gateway);
178 return E_LOOKUP;
179 } 241 }
242
243 isnet = INET_resolve(args_m1,
244 (struct sockaddr_in *) &rt.rt_gateway, 1);
245 rt.rt_flags |= RTF_GATEWAY;
246
180 if (isnet) { 247 if (isnet) {
181 bb_error_msg(_("%s: cannot use a NETWORK as gateway!"), gateway); 248 if (isnet < 0) {
182 return E_OPTERR; 249 bb_error_msg_and_die("resolving %s", args_m1);
250 }
251 bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
183 } 252 }
184 rt.rt_flags |= RTF_GATEWAY;
185 args++;
186 continue; 253 continue;
187 } 254 }
188 255
189 if (strcmp(*args, "mss") == 0) { 256 if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
190 args++;
191 rt.rt_flags |= RTF_MSS; 257 rt.rt_flags |= RTF_MSS;
192 if (!*args) 258 rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768);
193 bb_show_usage();
194 rt.rt_mss = atoi(*args);
195 args++;
196 if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
197 bb_error_msg(_("Invalid MSS."));
198 return E_OPTERR;
199 }
200 continue; 259 continue;
201 } 260 }
202 261
203 if (strcmp(*args, "window") == 0) { 262 if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */
204 args++;
205 if (!*args)
206 bb_show_usage();
207 rt.rt_flags |= RTF_WINDOW; 263 rt.rt_flags |= RTF_WINDOW;
208 rt.rt_window = atoi(*args); 264 rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX);
209 args++;
210 if (rt.rt_window < 128) {
211 bb_error_msg(_("Invalid window."));
212 return E_OPTERR;
213 }
214 continue; 265 continue;
215 } 266 }
216 267
217 if (strcmp(*args, "irtt") == 0) { 268#ifdef RTF_IRTT
218 args++; 269 if (k == KW_IPVx_IRTT) {
219 if (!*args)
220 bb_show_usage();
221 args++;
222#if HAVE_RTF_IRTT
223 rt.rt_flags |= RTF_IRTT; 270 rt.rt_flags |= RTF_IRTT;
224 rt.rt_irtt = atoi(*(args - 1)); 271 rt.rt_irtt = bb_xgetularg10(args_m1);
225 rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ 272 rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
226#if 0 /* FIXME: do we need to check anything of this? */ 273#if 0 /* FIXME: do we need to check anything of this? */
227 if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { 274 if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
228 bb_error_msg(_("Invalid initial rtt.")); 275 bb_error_msg_and_die("bad irtt");
229 return E_OPTERR;
230 } 276 }
231#endif 277#endif
232#else
233 ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
234#endif
235 continue; 278 continue;
236 } 279 }
237
238 if (strcmp(*args, "reject") == 0) {
239 args++;
240#if HAVE_RTF_REJECT
241 rt.rt_flags |= RTF_REJECT;
242#else
243 ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
244#endif 280#endif
281
282 /* Device is special in that it can be the last arg specified
283 * and doesn't requre the dev/device keyword in that case. */
284 if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
285 rt.rt_dev = (char *) args_m1;
245 continue; 286 continue;
246 } 287 }
247 if (strcmp(*args, "mod") == 0) { 288
248 args++; 289 /* Nothing matched. */
249 rt.rt_flags |= RTF_MODIFIED; 290 bb_show_usage();
250 continue;
251 }
252 if (strcmp(*args, "dyn") == 0) {
253 args++;
254 rt.rt_flags |= RTF_DYNAMIC;
255 continue;
256 }
257 if (strcmp(*args, "reinstate") == 0) {
258 args++;
259 rt.rt_flags |= RTF_REINSTATE;
260 continue;
261 }
262 if (strcmp(*args, "device") == 0 || strcmp(*args, "dev") == 0) {
263 args++;
264 if (rt.rt_dev || *args == NULL)
265 bb_show_usage();
266 rt.rt_dev = *args++;
267 continue;
268 }
269 /* nothing matches */
270 if (!rt.rt_dev) {
271 rt.rt_dev = *args++;
272 if (*args)
273 bb_show_usage(); /* must be last to catch typos */
274 } else {
275 bb_show_usage();
276 }
277 } 291 }
278 292
279#if HAVE_RTF_REJECT 293#ifdef RTF_REJECT
280 if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) 294 if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
281 rt.rt_dev = "lo"; 295 rt.rt_dev = "lo";
296 }
282#endif 297#endif
283 298
284 /* sanity checks.. */ 299 /* sanity checks.. */
@@ -287,79 +302,65 @@ static int INET_setroute(int action, int options, char **args)
287 302
288 mask = ~ntohl(mask); 303 mask = ~ntohl(mask);
289 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) { 304 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
290 bb_error_msg(_("netmask %.8x doesn't make sense with host route"), 305 bb_error_msg_and_die("netmask %.8x and host route conflict",
291 (unsigned int) mask); 306 (unsigned int) mask);
292 return E_OPTERR;
293 } 307 }
294 if (mask & (mask + 1)) { 308 if (mask & (mask + 1)) {
295 bb_error_msg(_("bogus netmask %s"), netmask); 309 bb_error_msg_and_die("bogus netmask %s", netmask);
296 return E_OPTERR;
297 } 310 }
298 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; 311 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
299 if (mask & ~mask_in_addr(rt)) { 312 if (mask & ~mask_in_addr(rt)) {
300 bb_error_msg(_("netmask doesn't match route address")); 313 bb_error_msg_and_die("netmask and route address conflict");
301 return E_OPTERR;
302 } 314 }
303 } 315 }
316
304 /* Fill out netmask if still unset */ 317 /* Fill out netmask if still unset */
305 if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST) 318 if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
306 mask_in_addr(rt) = 0xffffffff; 319 mask_in_addr(rt) = 0xffffffff;
320 }
307 321
308 /* Create a socket to the INET kernel. */ 322 /* Create a socket to the INET kernel. */
309 if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 323 if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
310 perror("socket"); 324 bb_perror_msg_and_die("socket");
311 return E_SOCK;
312 } 325 }
313 /* Tell the kernel to accept this route. */ 326
314 if (action == RTACTION_DEL) { 327 if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
315 if (ioctl(skfd, SIOCDELRT, &rt) < 0) { 328 bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
316 perror("SIOCDELRT");
317 close(skfd);
318 return E_SOCK;
319 }
320 } else {
321 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
322 perror("SIOCADDRT");
323 close(skfd);
324 return E_SOCK;
325 }
326 } 329 }
327 330
328 /* Close the socket. */ 331 /* Don't bother closing, as we're exiting after we return anyway. */
329 (void) close(skfd); 332 /* close(skfd); */
330 return EXIT_SUCCESS;
331} 333}
332 334
333#ifdef CONFIG_FEATURE_IPV6 335#ifdef CONFIG_FEATURE_IPV6
334static int INET6_setroute(int action, int options, char **args) 336
337static void INET6_setroute(int action, char **args)
335{ 338{
336 struct in6_rtmsg rt;
337 struct ifreq ifr;
338 struct sockaddr_in6 sa6; 339 struct sockaddr_in6 sa6;
339 char target[128], gateway[128] = "NONE"; 340 struct in6_rtmsg rt;
340 int metric, prefix_len; 341 int prefix_len, skfd;
341 char *devname = NULL; 342 const char *devname;
342 char *cp;
343 int skfd;
344 343
345 if (*args == NULL) 344 assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
346 bb_show_usage();
347 345
348 strcpy(target, *args++); 346 {
349 if (!strcmp(target, "default")) { 347 /* We know args isn't NULL from the check in route_main. */
350 prefix_len = 0; 348 const char *target = *args++;
351 memset(&sa6, 0, sizeof(sa6)); 349
352 } else { 350 if (strcmp(target, "default") == 0) {
353 if ((cp = strchr(target, '/'))) { 351 prefix_len = 0;
354 if (safe_strtod(cp + 1, &prefix_len) || (prefix_len < 0) || (prefix_len > 128)) 352 memset(&sa6, 0, sizeof(sa6));
355 bb_show_usage();
356 *cp = 0;
357 } else { 353 } else {
358 prefix_len = 128; 354 char *cp;
359 } 355 if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
360 if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) { 356 *cp = 0;
361 bb_error_msg(_("can't resolve %s"), target); 357 prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);
362 return EXIT_FAILURE; /* XXX change to E_something */ 358 } else {
359 prefix_len = 128;
360 }
361 if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
362 bb_error_msg_and_die("resolving %s", target);
363 }
363 } 364 }
364 } 365 }
365 366
@@ -369,336 +370,343 @@ static int INET6_setroute(int action, int options, char **args)
369 memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); 370 memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
370 371
371 /* Fill in the other fields. */ 372 /* Fill in the other fields. */
372 rt.rtmsg_flags = RTF_UP;
373 if (prefix_len == 128)
374 rt.rtmsg_flags |= RTF_HOST;
375 rt.rtmsg_metric = 1;
376 rt.rtmsg_dst_len = prefix_len; 373 rt.rtmsg_dst_len = prefix_len;
374 rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
375 rt.rtmsg_metric = 1;
376
377 devname = NULL;
377 378
378 while (*args) { 379 while (*args) {
379 if (!strcmp(*args, "metric")) { 380 int k = kw_lookup(tbl_ipvx, &args);
381 const char *args_m1 = args[-1];
380 382
381 args++; 383 if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
382 if (!*args || !isdigit(**args)) 384 rt.rtmsg_flags |= flags_ipvx[k & 3];
383 bb_show_usage();
384 metric = atoi(*args);
385 rt.rtmsg_metric = metric;
386 args++;
387 continue; 385 continue;
388 } 386 }
389 if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) { 387
390 args++; 388 if (k == KW_IPVx_METRIC) {
391 if (!*args) 389 rt.rtmsg_metric = bb_xgetularg10(args_m1);
392 bb_show_usage(); 390 continue;
393 if (rt.rtmsg_flags & RTF_GATEWAY) 391 }
392
393 if (k == KW_IPVx_GATEWAY) {
394 if (rt.rtmsg_flags & RTF_GATEWAY) {
394 bb_show_usage(); 395 bb_show_usage();
395 strcpy(gateway, *args); 396 }
396 if (INET6_resolve(gateway, (struct sockaddr_in6 *) &sa6) < 0) { 397
397 bb_error_msg(_("can't resolve gw %s"), gateway); 398 if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
398 return (E_LOOKUP); 399 bb_error_msg_and_die("resolving %s", args_m1);
399 } 400 }
400 memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, 401 memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
401 sizeof(struct in6_addr)); 402 sizeof(struct in6_addr));
402 rt.rtmsg_flags |= RTF_GATEWAY; 403 rt.rtmsg_flags |= RTF_GATEWAY;
403 args++;
404 continue;
405 }
406 if (!strcmp(*args, "mod")) {
407 args++;
408 rt.rtmsg_flags |= RTF_MODIFIED;
409 continue; 404 continue;
410 } 405 }
411 if (!strcmp(*args, "dyn")) { 406
412 args++; 407 /* Device is special in that it can be the last arg specified
413 rt.rtmsg_flags |= RTF_DYNAMIC; 408 * and doesn't requre the dev/device keyword in that case. */
409 if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
410 devname = args_m1;
414 continue; 411 continue;
415 } 412 }
416 if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
417 args++;
418 if (!*args)
419 bb_show_usage();
420 } else if (args[1])
421 bb_show_usage();
422 413
423 devname = *args; 414 /* Nothing matched. */
424 args++; 415 bb_show_usage();
425 } 416 }
426 417
427 /* Create a socket to the INET6 kernel. */ 418 /* Create a socket to the INET6 kernel. */
428 if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 419 if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
429 perror("socket"); 420 bb_perror_msg_and_die("socket");
430 return (E_SOCK);
431 } 421 }
422
423 rt.rtmsg_ifindex = 0;
424
432 if (devname) { 425 if (devname) {
426 struct ifreq ifr;
433 memset(&ifr, 0, sizeof(ifr)); 427 memset(&ifr, 0, sizeof(ifr));
434 strcpy(ifr.ifr_name, devname); 428 strcpy(ifr.ifr_name, devname);
435 429
436 if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { 430 if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
437 perror("SIOGIFINDEX"); 431 bb_perror_msg_and_die("SIOGIFINDEX");
438 return (E_SOCK);
439 } 432 }
440 rt.rtmsg_ifindex = ifr.ifr_ifindex; 433 rt.rtmsg_ifindex = ifr.ifr_ifindex;
441 } else 434 }
442 rt.rtmsg_ifindex = 0;
443 435
444 /* Tell the kernel to accept this route. */ 436 /* Tell the kernel to accept this route. */
445 if (action == RTACTION_DEL) { 437 if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
446 if (ioctl(skfd, SIOCDELRT, &rt) < 0) { 438 bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
447 perror("SIOCDELRT");
448 close(skfd);
449 return (E_SOCK);
450 }
451 } else {
452 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
453 perror("SIOCADDRT");
454 close(skfd);
455 return (E_SOCK);
456 }
457 } 439 }
458 440
459 /* Close the socket. */ 441 /* Don't bother closing, as we're exiting after we return anyway. */
460 (void) close(skfd); 442 /* close(skfd); */
461 return (0);
462} 443}
463#endif 444#endif
464 445
465#ifndef RTF_UP 446static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
466/* Keep this in sync with /usr/src/linux/include/linux/route.h */ 447 RTF_GATEWAY,
467#define RTF_UP 0x0001 /* route usable */ 448 RTF_HOST,
468#define RTF_GATEWAY 0x0002 /* destination is a gateway */ 449 RTF_REINSTATE,
469#define RTF_HOST 0x0004 /* host entry (net otherwise) */ 450 RTF_DYNAMIC,
470#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */ 451 RTF_MODIFIED,
471#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */ 452#ifdef CONFIG_FEATURE_IPV6
472#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */ 453 RTF_DEFAULT,
473#define RTF_MTU 0x0040 /* specific MTU for this route */ 454 RTF_ADDRCONF,
474#ifndef RTF_MSS 455 RTF_CACHE
475#define RTF_MSS RTF_MTU /* Compatibility :-( */
476#endif 456#endif
477#define RTF_WINDOW 0x0080 /* per route window clamping */ 457};
478#define RTF_IRTT 0x0100 /* Initial round trip time */ 458
479#define RTF_REJECT 0x0200 /* Reject route */ 459#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
460#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
461
462static const char flagchars[] = /* Must agree with flagvals[]. */
463 "GHRDM"
464#ifdef CONFIG_FEATURE_IPV6
465 "DAC"
480#endif 466#endif
467;
468
469static
470#ifndef CONFIG_FEATURE_IPV6
471__inline
472#endif
473void set_flags(char *flagstr, int flags)
474{
475 int i;
476
477 *flagstr++ = 'U';
478
479 for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) {
480 if (flags & flagvals[i]) {
481 ++flagstr;
482 }
483 }
484}
481 485
482void displayroutes(int noresolve, int netstatfmt) 486void displayroutes(int noresolve, int netstatfmt)
483{ 487{
484 char buff[256]; 488 char devname[64], flags[16], sdest[16], sgw[16];
485 int nl = 0;
486 struct in_addr dest;
487 struct in_addr gw;
488 struct in_addr mask;
489 int flgs, ref, use, metric, mtu, win, ir;
490 char flags[64];
491 unsigned long int d, g, m; 489 unsigned long int d, g, m;
492 490 int flgs, ref, use, metric, mtu, win, ir;
493 char sdest[16], sgw[16]; 491 struct sockaddr_in s_addr;
492 struct in_addr mask;
494 493
495 FILE *fp = bb_xfopen("/proc/net/route", "r"); 494 FILE *fp = bb_xfopen("/proc/net/route", "r");
496 495
497 if (noresolve) 496 bb_printf("Kernel IP routing table\n"
498 noresolve = 0x0fff; 497 "Destination Gateway Genmask"
499 498 " Flags %s Iface\n",
500 printf("Kernel IP routing table\n"); 499 netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
501 printf 500
502 ("Destination Gateway Genmask Flags %s Iface\n", 501 if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
503 netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); 502 goto ERROR; /* Empty or missing line, or read error. */
504 503 }
505 while (fgets(buff, sizeof(buff), fp) != NULL) { 504 while (1) {
506 if (nl) { 505 int r;
507 int ifl = 0; 506 r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
508 int numeric; 507 devname, &d, &g, &flgs, &ref, &use, &metric, &m,
509 struct sockaddr_in s_addr; 508 &mtu, &win, &ir);
510 509 if (r != 11) {
511 while (buff[ifl] != ' ' && buff[ifl] != '\t' && buff[ifl] != '\0') 510 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
512 ifl++; 511 break;
513 buff[ifl] = 0; /* interface */
514 if (sscanf(buff + ifl + 1, "%lx%lx%X%d%d%d%lx%d%d%d",
515 &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win,
516 &ir) != 10) {
517 bb_error_msg_and_die("Unsuported kernel route format\n");
518 }
519 ifl = 0; /* parse flags */
520 if (flgs & RTF_UP) {
521 if (flgs & RTF_REJECT)
522 flags[ifl++] = '!';
523 else
524 flags[ifl++] = 'U';
525 if (flgs & RTF_GATEWAY)
526 flags[ifl++] = 'G';
527 if (flgs & RTF_HOST)
528 flags[ifl++] = 'H';
529 if (flgs & RTF_REINSTATE)
530 flags[ifl++] = 'R';
531 if (flgs & RTF_DYNAMIC)
532 flags[ifl++] = 'D';
533 if (flgs & RTF_MODIFIED)
534 flags[ifl++] = 'M';
535 flags[ifl] = 0;
536 dest.s_addr = d;
537 gw.s_addr = g;
538 mask.s_addr = m;
539 memset(&s_addr, 0, sizeof(struct sockaddr_in));
540 s_addr.sin_family = AF_INET;
541 s_addr.sin_addr = dest;
542 numeric = noresolve | 0x8000; /* default instead of * */
543 INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
544 numeric = noresolve | 0x4000; /* host instead of net */
545 s_addr.sin_addr = gw;
546 INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
547
548 printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask),
549 flags);
550 if (netstatfmt)
551 printf("%5d %-5d %6d %s\n", mtu, win, ir, buff);
552 else
553 printf("%-6d %-2d %7d %s\n", metric, ref, use, buff);
554 } 512 }
513 ERROR:
514 bb_error_msg_and_die("fscanf");
515 }
516
517 if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
518 continue;
519 }
520
521 set_flags(flags, (flgs & IPV4_MASK));
522#ifdef RTF_REJECT
523 if (flgs & RTF_REJECT) {
524 flags[0] = '!';
525 }
526#endif
527
528 memset(&s_addr, 0, sizeof(struct sockaddr_in));
529 s_addr.sin_family = AF_INET;
530 s_addr.sin_addr.s_addr = d;
531 INET_rresolve(sdest, sizeof(sdest), &s_addr,
532 (noresolve | 0x8000), m); /* Default instead of *. */
533
534 s_addr.sin_addr.s_addr = g;
535 INET_rresolve(sgw, sizeof(sgw), &s_addr,
536 (noresolve | 0x4000), m); /* Host instead of net. */
537
538 mask.s_addr = m;
539 bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
540 if (netstatfmt) {
541 bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
542 } else {
543 bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
555 } 544 }
556 nl++;
557 } 545 }
558} 546}
559 547
560#ifdef CONFIG_FEATURE_IPV6 548#ifdef CONFIG_FEATURE_IPV6
549
561static void INET6_displayroutes(int noresolve) 550static void INET6_displayroutes(int noresolve)
562{ 551{
563 char buff[256];
564 char iface[16], flags[16];
565 char addr6[128], naddr6[128]; 552 char addr6[128], naddr6[128];
566 struct sockaddr_in6 saddr6, snaddr6; 553 /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
554 * We read the non-delimited strings into the tail of the buffer
555 * using fscanf and then modify the buffer by shifting forward
556 * while inserting ':'s and the nul terminator for the first string.
557 * Hence the strings are at addr6x and addr6x+40. This generates
558 * _much_ less code than the previous (upstream) approach. */
559 char addr6x[80];
560 char iface[16], flags[16];
567 int iflags, metric, refcnt, use, prefix_len, slen; 561 int iflags, metric, refcnt, use, prefix_len, slen;
568 int numeric; 562 struct sockaddr_in6 snaddr6;
569
570 char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
571 563
572 FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r"); 564 FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
573 565
574 flags[0] = 'U'; 566 bb_printf("Kernel IPv6 routing table\n%-44s%-40s"
575 567 "Flags Metric Ref Use Iface\n",
576 if (noresolve) 568 "Destination", "Next Hop");
577 noresolve = 0x0fff; 569
578 numeric = noresolve | 0x8000; /* default instead of * */ 570 while (1) {
579 571 int r;
580 printf("Kernel IPv6 routing table\n" 572 r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
581 "Destination " 573 addr6x+14, &prefix_len, &slen, addr6x+40+7,
582 "Next Hop " 574 &metric, &use, &refcnt, &iflags, iface);
583 "Flags Metric Ref Use Iface\n"); 575 if (r != 9) {
584 576 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
585 while (fgets(buff, sizeof(buff), fp) != NULL) { 577 break;
586 int ifl; 578 }
587 579 ERROR:
588 if (sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x " 580 bb_error_msg_and_die("fscanf");
589 "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
590 "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
591 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
592 addr6p[4], addr6p[5], addr6p[6], addr6p[7],
593 &prefix_len,
594 saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
595 saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
596 &slen,
597 naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
598 naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
599 &metric, &use, &refcnt, &iflags, iface) != 31) {
600 bb_error_msg_and_die("Unsuported kernel route format\n");
601 } 581 }
602 582
603 ifl = 1; /* parse flags */ 583 /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
604 if (!(iflags & RTF_UP)) 584 * For now, always do this to validate the proc route format, even
585 * if the interface is down. */
586 {
587 int i = 0;
588 char *p = addr6x+14;
589
590 do {
591 if (!*p) {
592 if (i==40) { /* nul terminator for 1st address? */
593 addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
594 ++p; /* Skip and continue. */
595 continue;
596 }
597 goto ERROR;
598 }
599 addr6x[i++] = *p++;
600 if (!((i+1)%5)) {
601 addr6x[i++] = ':';
602 }
603 } while (i < 40+28+7);
604 }
605
606 if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
605 continue; 607 continue;
606 if (iflags & RTF_GATEWAY) 608 }
607 flags[ifl++] = 'G'; 609
608 if (iflags & RTF_HOST) 610 set_flags(flags, (iflags & IPV6_MASK));
609 flags[ifl++] = 'H'; 611
610 if (iflags & RTF_DEFAULT) 612 r = 0;
611 flags[ifl++] = 'D'; 613 do {
612 if (iflags & RTF_ADDRCONF) 614 inet_pton(AF_INET6, addr6x + r,
613 flags[ifl++] = 'A'; 615 (struct sockaddr *) &snaddr6.sin6_addr);
614 if (iflags & RTF_CACHE) 616 snaddr6.sin6_family = AF_INET6;
615 flags[ifl++] = 'C'; 617 INET6_rresolve(naddr6, sizeof(naddr6),
616 flags[ifl] = 0; 618 (struct sockaddr_in6 *) &snaddr6,
617 619#if 0
618 /* Fetch and resolve the target address. */ 620 (noresolve | 0x8000) /* Default instead of *. */
619 snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s", 621#else
620 addr6p[0], addr6p[1], addr6p[2], addr6p[3], 622 0x0fff /* Apparently, upstream never resolves. */
621 addr6p[4], addr6p[5], addr6p[6], addr6p[7]); 623#endif
622 inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr); 624 );
623 saddr6.sin6_family = AF_INET6; 625
624 626 if (!r) { /* 1st pass */
625 INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6, 627 snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
626 numeric); 628 r += 40;
627 snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len); 629 } else { /* 2nd pass */
628 630 /* Print the info. */
629 /* Fetch and resolve the nexthop address. */ 631 bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
630 snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s", 632 addr6, naddr6, flags, metric, refcnt, use, iface);
631 naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3], 633 break;
632 naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]); 634 }
633 inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr); 635 } while (1);
634 snaddr6.sin6_family = AF_INET6;
635
636 INET6_rresolve(naddr6, sizeof(naddr6),
637 (struct sockaddr_in6 *) &snaddr6, numeric);
638
639 /* Print the info. */
640 printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
641 addr6, naddr6, flags, metric, refcnt, use, iface);
642 } 636 }
643} 637}
638
644#endif 639#endif
645 640
641#define ROUTE_OPT_A 0x01
642#define ROUTE_OPT_n 0x02
643#define ROUTE_OPT_e 0x04
644#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
645
646/* 1st byte is offset to next entry offset. 2nd byte is return value. */
647static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */
648 "\006\001add\0"
649 "\006\002del\0"
650/* "\011\002delete\0" */
651 "\010\002delete" /* Since last, we can save a byte. */
652;
653
646int route_main(int argc, char **argv) 654int route_main(int argc, char **argv)
647{ 655{
648 int opt; 656 unsigned long opt;
649 int what = 0; 657 int what;
658 char *family;
650 659
651#ifdef CONFIG_FEATURE_IPV6 660 /* First, remap '-net' and '-host' to avoid getopt problems. */
652 int af = AF_INET; 661 {
653#endif 662 char **p = argv;
654 663
655 if (!argv[1] || (argv[1][0] == '-')) { 664 while (*++p) {
656 /* check options */ 665 if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) {
657 int noresolve = 0; 666 p[0][0] = '#';
658 int extended = 0; 667 }
668 }
669 }
659 670
660 while ((opt = getopt(argc, argv, "A:ne")) > 0) { 671 opt = bb_getopt_ulflags(argc, argv, "A:ne", &family);
661 switch (opt) { 672
662 case 'n': 673 if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) {
663 noresolve = 1;
664 break;
665 case 'e':
666 extended = 1;
667 break;
668 case 'A':
669#ifdef CONFIG_FEATURE_IPV6 674#ifdef CONFIG_FEATURE_IPV6
670 if (strcmp(optarg, "inet6") == 0) 675 if (strcmp(family, "inet6") == 0) {
671 af = AF_INET6; 676 opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
672 break; 677 } else
673#endif 678#endif
674 default: 679 bb_show_usage();
675 bb_show_usage(); 680 }
676 } 681
677 } 682 argv += optind;
678 683
684 /* No more args means display the routing table. */
685 if (!*argv) {
686 int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
679#ifdef CONFIG_FEATURE_IPV6 687#ifdef CONFIG_FEATURE_IPV6
680 if (af == AF_INET6) 688 if (opt & ROUTE_OPT_INET6)
681 INET6_displayroutes(*argv != NULL); 689 INET6_displayroutes(noresolve);
682 else 690 else
683#endif 691#endif
684 displayroutes(noresolve, extended); 692 displayroutes(noresolve, opt & ROUTE_OPT_e);
685 return EXIT_SUCCESS; 693
686 } else { 694 bb_xferror_stdout();
687 /* check verb */ 695 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
688 if (strcmp(argv[1], "add") == 0) 696 }
689 what = RTACTION_ADD; 697
690 else if (strcmp(argv[1], "del") == 0 698 /* Check verb. At the moment, must be add, del, or delete. */
691 || strcmp(argv[1], "delete") == 0) 699 what = kw_lookup(tbl_verb, &argv);
692 what = RTACTION_DEL; 700 if (!what || !*argv) { /* Unknown verb or no more args. */
693 else if (strcmp(argv[1], "flush") == 0) 701 bb_show_usage();
694 what = RTACTION_FLUSH;
695 else
696 bb_show_usage();
697 } 702 }
698 703
699#ifdef CONFIG_FEATURE_IPV6 704#ifdef CONFIG_FEATURE_IPV6
700 if (af == AF_INET6) 705 if (opt & ROUTE_OPT_INET6)
701 return INET6_setroute(what, 0, argv + 2); 706 INET6_setroute(what, argv);
707 else
702#endif 708#endif
703 return INET_setroute(what, 0, argv + 2); 709 INET_setroute(what, argv);
710
711 return EXIT_SUCCESS;
704} 712}