diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2004-03-10 07:42:38 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2004-03-10 07:42:38 +0000 |
commit | 7d0c51919ce4d0be00fd8ae92d6f16b86aad9fd2 (patch) | |
tree | d19379419a02c8f77fc864db6b08450f016264dc | |
parent | 2715fa147a36662b59b58b78ff31e225e785fef9 (diff) | |
download | busybox-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.h | 4 | ||||
-rw-r--r-- | include/usage.h | 14 | ||||
-rw-r--r-- | libbb/inet_common.c | 4 | ||||
-rw-r--r-- | networking/route.c | 964 |
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 | |||
29 | extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, | 29 | extern 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 | ||
32 | extern int INET6_resolve(char *name, struct sockaddr_in6 *sin6); | 32 | extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6); |
33 | extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric); | 33 | extern 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 | ||
189 | int INET6_resolve(char *name, struct sockaddr_in6 *sin6) | 189 | int 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. */ | ||
87 | static 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 | |||
110 | static 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 | |||
135 | static 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 | ||
144 | static 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 | ||
80 | static int INET_setroute(int action, int options, char **args) | 166 | static 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 |
334 | static int INET6_setroute(int action, int options, char **args) | 336 | |
337 | static 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 | 446 | static 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 | |||
462 | static const char flagchars[] = /* Must agree with flagvals[]. */ | ||
463 | "GHRDM" | ||
464 | #ifdef CONFIG_FEATURE_IPV6 | ||
465 | "DAC" | ||
480 | #endif | 466 | #endif |
467 | ; | ||
468 | |||
469 | static | ||
470 | #ifndef CONFIG_FEATURE_IPV6 | ||
471 | __inline | ||
472 | #endif | ||
473 | void 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 | ||
482 | void displayroutes(int noresolve, int netstatfmt) | 486 | void 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 | |||
561 | static void INET6_displayroutes(int noresolve) | 550 | static 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. */ | ||
647 | static 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 | |||
646 | int route_main(int argc, char **argv) | 654 | int 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 | } |