diff options
author | nobody <nobody@localhost> | 2004-10-13 09:42:10 +0000 |
---|---|---|
committer | nobody <nobody@localhost> | 2004-10-13 09:42:10 +0000 |
commit | 8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 (patch) | |
tree | 1826706cd4fd009fcd14f4f8021005ec8ec0fa59 /busybox/networking/route.c | |
download | busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.gz busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.bz2 busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.zip |
This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
Diffstat (limited to 'busybox/networking/route.c')
-rw-r--r-- | busybox/networking/route.c | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/busybox/networking/route.c b/busybox/networking/route.c new file mode 100644 index 000000000..5c092f452 --- /dev/null +++ b/busybox/networking/route.c | |||
@@ -0,0 +1,714 @@ | |||
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.26 2004/03/19 23:27:08 mjn3 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 | /* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org> | ||
27 | * | ||
28 | * Rewritten to fix several bugs, add additional error checking, and | ||
29 | * remove ridiculous amounts of bloat. | ||
30 | */ | ||
31 | |||
32 | #include <stdio.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <string.h> | ||
35 | #include <errno.h> | ||
36 | #include <assert.h> | ||
37 | #include <unistd.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> | ||
44 | #include "busybox.h" | ||
45 | #include "inet_common.h" | ||
46 | |||
47 | #ifndef RTF_UP | ||
48 | /* Keep this in sync with /usr/src/linux/include/linux/route.h */ | ||
49 | #define RTF_UP 0x0001 /* route usable */ | ||
50 | #define RTF_GATEWAY 0x0002 /* destination is a gateway */ | ||
51 | #define RTF_HOST 0x0004 /* host entry (net otherwise) */ | ||
52 | #define RTF_REINSTATE 0x0008 /* reinstate route after tmout */ | ||
53 | #define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */ | ||
54 | #define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */ | ||
55 | #define RTF_MTU 0x0040 /* specific MTU for this route */ | ||
56 | #ifndef RTF_MSS | ||
57 | #define RTF_MSS RTF_MTU /* Compatibility :-( */ | ||
58 | #endif | ||
59 | #define RTF_WINDOW 0x0080 /* per route window clamping */ | ||
60 | #define RTF_IRTT 0x0100 /* Initial round trip time */ | ||
61 | #define RTF_REJECT 0x0200 /* Reject route */ | ||
62 | #endif | ||
63 | |||
64 | #if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ | ||
65 | #define HAVE_NEW_ADDRT 1 | ||
66 | #endif | ||
67 | |||
68 | #if HAVE_NEW_ADDRT | ||
69 | #define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) | ||
70 | #define full_mask(x) (x) | ||
71 | #else | ||
72 | #define mask_in_addr(x) ((x).rt_genmask) | ||
73 | #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr) | ||
74 | #endif | ||
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 | }; | ||
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 | } | ||
163 | |||
164 | /* Add or delete a route, depending on action. */ | ||
165 | |||
166 | static void INET_setroute(int action, char **args) | ||
167 | { | ||
168 | struct rtentry rt; | ||
169 | const char *netmask; | ||
170 | int skfd, isnet, xflag; | ||
171 | |||
172 | assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); | ||
173 | |||
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) { | ||
179 | bb_show_usage(); | ||
180 | } | ||
181 | |||
182 | /* Clean out the RTREQ structure. */ | ||
183 | memset((char *) &rt, 0, sizeof(struct rtentry)); | ||
184 | |||
185 | { | ||
186 | const char *target = *args++; | ||
187 | |||
188 | /* Prefer hostname lookup is -host flag (xflag==1) was given. */ | ||
189 | isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst, | ||
190 | (xflag & HOST_FLAG)); | ||
191 | if (isnet < 0) { | ||
192 | bb_error_msg_and_die("resolving %s", target); | ||
193 | } | ||
194 | |||
195 | } | ||
196 | |||
197 | if (xflag) { /* Reinit isnet if -net or -host was specified. */ | ||
198 | isnet = (xflag & NET_FLAG); | ||
199 | } | ||
200 | |||
201 | /* Fill in the other fields. */ | ||
202 | rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST)); | ||
203 | |||
204 | netmask = bb_INET_default; | ||
205 | |||
206 | while (*args) { | ||
207 | int k = kw_lookup(tbl_ipvx, &args); | ||
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 | } | ||
214 | |||
215 | #if HAVE_NEW_ADDRT | ||
216 | if (k == KW_IPVx_METRIC) { | ||
217 | rt.rt_metric = bb_xgetularg10(args_m1) + 1; | ||
218 | continue; | ||
219 | } | ||
220 | #endif | ||
221 | |||
222 | if (k == KW_IPVx_NETMASK) { | ||
223 | struct sockaddr mask; | ||
224 | |||
225 | if (mask_in_addr(rt)) { | ||
226 | bb_show_usage(); | ||
227 | } | ||
228 | |||
229 | netmask = args_m1; | ||
230 | isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0); | ||
231 | if (isnet < 0) { | ||
232 | bb_error_msg_and_die("resolving %s", netmask); | ||
233 | } | ||
234 | rt.rt_genmask = full_mask(mask); | ||
235 | continue; | ||
236 | } | ||
237 | |||
238 | if (k == KW_IPVx_GATEWAY) { | ||
239 | if (rt.rt_flags & RTF_GATEWAY) { | ||
240 | bb_show_usage(); | ||
241 | } | ||
242 | |||
243 | isnet = INET_resolve(args_m1, | ||
244 | (struct sockaddr_in *) &rt.rt_gateway, 1); | ||
245 | rt.rt_flags |= RTF_GATEWAY; | ||
246 | |||
247 | if (isnet) { | ||
248 | if (isnet < 0) { | ||
249 | bb_error_msg_and_die("resolving %s", args_m1); | ||
250 | } | ||
251 | bb_error_msg_and_die("gateway %s is a NETWORK", args_m1); | ||
252 | } | ||
253 | continue; | ||
254 | } | ||
255 | |||
256 | if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */ | ||
257 | rt.rt_flags |= RTF_MSS; | ||
258 | rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768); | ||
259 | continue; | ||
260 | } | ||
261 | |||
262 | if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */ | ||
263 | rt.rt_flags |= RTF_WINDOW; | ||
264 | rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX); | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | #ifdef RTF_IRTT | ||
269 | if (k == KW_IPVx_IRTT) { | ||
270 | rt.rt_flags |= RTF_IRTT; | ||
271 | rt.rt_irtt = bb_xgetularg10(args_m1); | ||
272 | rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ | ||
273 | #if 0 /* FIXME: do we need to check anything of this? */ | ||
274 | if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { | ||
275 | bb_error_msg_and_die("bad irtt"); | ||
276 | } | ||
277 | #endif | ||
278 | continue; | ||
279 | } | ||
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 | /* Don't use args_m1 here since args may have changed! */ | ||
286 | rt.rt_dev = args[-1]; | ||
287 | continue; | ||
288 | } | ||
289 | |||
290 | /* Nothing matched. */ | ||
291 | bb_show_usage(); | ||
292 | } | ||
293 | |||
294 | #ifdef RTF_REJECT | ||
295 | if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) { | ||
296 | rt.rt_dev = "lo"; | ||
297 | } | ||
298 | #endif | ||
299 | |||
300 | /* sanity checks.. */ | ||
301 | if (mask_in_addr(rt)) { | ||
302 | unsigned long mask = mask_in_addr(rt); | ||
303 | |||
304 | mask = ~ntohl(mask); | ||
305 | if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) { | ||
306 | bb_error_msg_and_die("netmask %.8x and host route conflict", | ||
307 | (unsigned int) mask); | ||
308 | } | ||
309 | if (mask & (mask + 1)) { | ||
310 | bb_error_msg_and_die("bogus netmask %s", netmask); | ||
311 | } | ||
312 | mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; | ||
313 | if (mask & ~mask_in_addr(rt)) { | ||
314 | bb_error_msg_and_die("netmask and route address conflict"); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /* Fill out netmask if still unset */ | ||
319 | if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) { | ||
320 | mask_in_addr(rt) = 0xffffffff; | ||
321 | } | ||
322 | |||
323 | /* Create a socket to the INET kernel. */ | ||
324 | if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | ||
325 | bb_perror_msg_and_die("socket"); | ||
326 | } | ||
327 | |||
328 | if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { | ||
329 | bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); | ||
330 | } | ||
331 | |||
332 | /* Don't bother closing, as we're exiting after we return anyway. */ | ||
333 | /* close(skfd); */ | ||
334 | } | ||
335 | |||
336 | #ifdef CONFIG_FEATURE_IPV6 | ||
337 | |||
338 | static void INET6_setroute(int action, char **args) | ||
339 | { | ||
340 | struct sockaddr_in6 sa6; | ||
341 | struct in6_rtmsg rt; | ||
342 | int prefix_len, skfd; | ||
343 | const char *devname; | ||
344 | |||
345 | assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); | ||
346 | |||
347 | { | ||
348 | /* We know args isn't NULL from the check in route_main. */ | ||
349 | const char *target = *args++; | ||
350 | |||
351 | if (strcmp(target, "default") == 0) { | ||
352 | prefix_len = 0; | ||
353 | memset(&sa6, 0, sizeof(sa6)); | ||
354 | } else { | ||
355 | char *cp; | ||
356 | if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */ | ||
357 | *cp = 0; | ||
358 | prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128); | ||
359 | } else { | ||
360 | prefix_len = 128; | ||
361 | } | ||
362 | if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) { | ||
363 | bb_error_msg_and_die("resolving %s", target); | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* Clean out the RTREQ structure. */ | ||
369 | memset((char *) &rt, 0, sizeof(struct in6_rtmsg)); | ||
370 | |||
371 | memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); | ||
372 | |||
373 | /* Fill in the other fields. */ | ||
374 | rt.rtmsg_dst_len = prefix_len; | ||
375 | rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP); | ||
376 | rt.rtmsg_metric = 1; | ||
377 | |||
378 | devname = NULL; | ||
379 | |||
380 | while (*args) { | ||
381 | int k = kw_lookup(tbl_ipvx, &args); | ||
382 | const char *args_m1 = args[-1]; | ||
383 | |||
384 | if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) { | ||
385 | rt.rtmsg_flags |= flags_ipvx[k & 3]; | ||
386 | continue; | ||
387 | } | ||
388 | |||
389 | if (k == KW_IPVx_METRIC) { | ||
390 | rt.rtmsg_metric = bb_xgetularg10(args_m1); | ||
391 | continue; | ||
392 | } | ||
393 | |||
394 | if (k == KW_IPVx_GATEWAY) { | ||
395 | if (rt.rtmsg_flags & RTF_GATEWAY) { | ||
396 | bb_show_usage(); | ||
397 | } | ||
398 | |||
399 | if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) { | ||
400 | bb_error_msg_and_die("resolving %s", args_m1); | ||
401 | } | ||
402 | memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, | ||
403 | sizeof(struct in6_addr)); | ||
404 | rt.rtmsg_flags |= RTF_GATEWAY; | ||
405 | continue; | ||
406 | } | ||
407 | |||
408 | /* Device is special in that it can be the last arg specified | ||
409 | * and doesn't requre the dev/device keyword in that case. */ | ||
410 | if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { | ||
411 | /* Don't use args_m1 here since args may have changed! */ | ||
412 | devname = args[-1]; | ||
413 | continue; | ||
414 | } | ||
415 | |||
416 | /* Nothing matched. */ | ||
417 | bb_show_usage(); | ||
418 | } | ||
419 | |||
420 | /* Create a socket to the INET6 kernel. */ | ||
421 | if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { | ||
422 | bb_perror_msg_and_die("socket"); | ||
423 | } | ||
424 | |||
425 | rt.rtmsg_ifindex = 0; | ||
426 | |||
427 | if (devname) { | ||
428 | struct ifreq ifr; | ||
429 | memset(&ifr, 0, sizeof(ifr)); | ||
430 | strcpy(ifr.ifr_name, devname); | ||
431 | |||
432 | if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { | ||
433 | bb_perror_msg_and_die("SIOGIFINDEX"); | ||
434 | } | ||
435 | rt.rtmsg_ifindex = ifr.ifr_ifindex; | ||
436 | } | ||
437 | |||
438 | /* Tell the kernel to accept this route. */ | ||
439 | if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { | ||
440 | bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); | ||
441 | } | ||
442 | |||
443 | /* Don't bother closing, as we're exiting after we return anyway. */ | ||
444 | /* close(skfd); */ | ||
445 | } | ||
446 | #endif | ||
447 | |||
448 | static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */ | ||
449 | RTF_GATEWAY, | ||
450 | RTF_HOST, | ||
451 | RTF_REINSTATE, | ||
452 | RTF_DYNAMIC, | ||
453 | RTF_MODIFIED, | ||
454 | #ifdef CONFIG_FEATURE_IPV6 | ||
455 | RTF_DEFAULT, | ||
456 | RTF_ADDRCONF, | ||
457 | RTF_CACHE | ||
458 | #endif | ||
459 | }; | ||
460 | |||
461 | #define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) | ||
462 | #define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) | ||
463 | |||
464 | static const char flagchars[] = /* Must agree with flagvals[]. */ | ||
465 | "GHRDM" | ||
466 | #ifdef CONFIG_FEATURE_IPV6 | ||
467 | "DAC" | ||
468 | #endif | ||
469 | ; | ||
470 | |||
471 | static | ||
472 | #ifndef CONFIG_FEATURE_IPV6 | ||
473 | __inline | ||
474 | #endif | ||
475 | void set_flags(char *flagstr, int flags) | ||
476 | { | ||
477 | int i; | ||
478 | |||
479 | *flagstr++ = 'U'; | ||
480 | |||
481 | for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) { | ||
482 | if (flags & flagvals[i]) { | ||
483 | ++flagstr; | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | |||
488 | void displayroutes(int noresolve, int netstatfmt) | ||
489 | { | ||
490 | char devname[64], flags[16], sdest[16], sgw[16]; | ||
491 | unsigned long int d, g, m; | ||
492 | int flgs, ref, use, metric, mtu, win, ir; | ||
493 | struct sockaddr_in s_addr; | ||
494 | struct in_addr mask; | ||
495 | |||
496 | FILE *fp = bb_xfopen("/proc/net/route", "r"); | ||
497 | |||
498 | bb_printf("Kernel IP routing table\n" | ||
499 | "Destination Gateway Genmask" | ||
500 | " Flags %s Iface\n", | ||
501 | netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); | ||
502 | |||
503 | if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ | ||
504 | goto ERROR; /* Empty or missing line, or read error. */ | ||
505 | } | ||
506 | while (1) { | ||
507 | int r; | ||
508 | r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", | ||
509 | devname, &d, &g, &flgs, &ref, &use, &metric, &m, | ||
510 | &mtu, &win, &ir); | ||
511 | if (r != 11) { | ||
512 | if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ | ||
513 | break; | ||
514 | } | ||
515 | ERROR: | ||
516 | bb_error_msg_and_die("fscanf"); | ||
517 | } | ||
518 | |||
519 | if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ | ||
520 | continue; | ||
521 | } | ||
522 | |||
523 | set_flags(flags, (flgs & IPV4_MASK)); | ||
524 | #ifdef RTF_REJECT | ||
525 | if (flgs & RTF_REJECT) { | ||
526 | flags[0] = '!'; | ||
527 | } | ||
528 | #endif | ||
529 | |||
530 | memset(&s_addr, 0, sizeof(struct sockaddr_in)); | ||
531 | s_addr.sin_family = AF_INET; | ||
532 | s_addr.sin_addr.s_addr = d; | ||
533 | INET_rresolve(sdest, sizeof(sdest), &s_addr, | ||
534 | (noresolve | 0x8000), m); /* Default instead of *. */ | ||
535 | |||
536 | s_addr.sin_addr.s_addr = g; | ||
537 | INET_rresolve(sgw, sizeof(sgw), &s_addr, | ||
538 | (noresolve | 0x4000), m); /* Host instead of net. */ | ||
539 | |||
540 | mask.s_addr = m; | ||
541 | bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags); | ||
542 | if (netstatfmt) { | ||
543 | bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname); | ||
544 | } else { | ||
545 | bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname); | ||
546 | } | ||
547 | } | ||
548 | } | ||
549 | |||
550 | #ifdef CONFIG_FEATURE_IPV6 | ||
551 | |||
552 | static void INET6_displayroutes(int noresolve) | ||
553 | { | ||
554 | char addr6[128], naddr6[128]; | ||
555 | /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses. | ||
556 | * We read the non-delimited strings into the tail of the buffer | ||
557 | * using fscanf and then modify the buffer by shifting forward | ||
558 | * while inserting ':'s and the nul terminator for the first string. | ||
559 | * Hence the strings are at addr6x and addr6x+40. This generates | ||
560 | * _much_ less code than the previous (upstream) approach. */ | ||
561 | char addr6x[80]; | ||
562 | char iface[16], flags[16]; | ||
563 | int iflags, metric, refcnt, use, prefix_len, slen; | ||
564 | struct sockaddr_in6 snaddr6; | ||
565 | |||
566 | FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r"); | ||
567 | |||
568 | bb_printf("Kernel IPv6 routing table\n%-44s%-40s" | ||
569 | "Flags Metric Ref Use Iface\n", | ||
570 | "Destination", "Next Hop"); | ||
571 | |||
572 | while (1) { | ||
573 | int r; | ||
574 | r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", | ||
575 | addr6x+14, &prefix_len, &slen, addr6x+40+7, | ||
576 | &metric, &use, &refcnt, &iflags, iface); | ||
577 | if (r != 9) { | ||
578 | if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ | ||
579 | break; | ||
580 | } | ||
581 | ERROR: | ||
582 | bb_error_msg_and_die("fscanf"); | ||
583 | } | ||
584 | |||
585 | /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. | ||
586 | * For now, always do this to validate the proc route format, even | ||
587 | * if the interface is down. */ | ||
588 | { | ||
589 | int i = 0; | ||
590 | char *p = addr6x+14; | ||
591 | |||
592 | do { | ||
593 | if (!*p) { | ||
594 | if (i==40) { /* nul terminator for 1st address? */ | ||
595 | addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */ | ||
596 | ++p; /* Skip and continue. */ | ||
597 | continue; | ||
598 | } | ||
599 | goto ERROR; | ||
600 | } | ||
601 | addr6x[i++] = *p++; | ||
602 | if (!((i+1)%5)) { | ||
603 | addr6x[i++] = ':'; | ||
604 | } | ||
605 | } while (i < 40+28+7); | ||
606 | } | ||
607 | |||
608 | if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ | ||
609 | continue; | ||
610 | } | ||
611 | |||
612 | set_flags(flags, (iflags & IPV6_MASK)); | ||
613 | |||
614 | r = 0; | ||
615 | do { | ||
616 | inet_pton(AF_INET6, addr6x + r, | ||
617 | (struct sockaddr *) &snaddr6.sin6_addr); | ||
618 | snaddr6.sin6_family = AF_INET6; | ||
619 | INET6_rresolve(naddr6, sizeof(naddr6), | ||
620 | (struct sockaddr_in6 *) &snaddr6, | ||
621 | #if 0 | ||
622 | (noresolve | 0x8000) /* Default instead of *. */ | ||
623 | #else | ||
624 | 0x0fff /* Apparently, upstream never resolves. */ | ||
625 | #endif | ||
626 | ); | ||
627 | |||
628 | if (!r) { /* 1st pass */ | ||
629 | snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len); | ||
630 | r += 40; | ||
631 | } else { /* 2nd pass */ | ||
632 | /* Print the info. */ | ||
633 | bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n", | ||
634 | addr6, naddr6, flags, metric, refcnt, use, iface); | ||
635 | break; | ||
636 | } | ||
637 | } while (1); | ||
638 | } | ||
639 | } | ||
640 | |||
641 | #endif | ||
642 | |||
643 | #define ROUTE_OPT_A 0x01 | ||
644 | #define ROUTE_OPT_n 0x02 | ||
645 | #define ROUTE_OPT_e 0x04 | ||
646 | #define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */ | ||
647 | |||
648 | /* 1st byte is offset to next entry offset. 2nd byte is return value. */ | ||
649 | static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */ | ||
650 | "\006\001add\0" | ||
651 | "\006\002del\0" | ||
652 | /* "\011\002delete\0" */ | ||
653 | "\010\002delete" /* Since last, we can save a byte. */ | ||
654 | ; | ||
655 | |||
656 | int route_main(int argc, char **argv) | ||
657 | { | ||
658 | unsigned long opt; | ||
659 | int what; | ||
660 | char *family; | ||
661 | |||
662 | /* First, remap '-net' and '-host' to avoid getopt problems. */ | ||
663 | { | ||
664 | char **p = argv; | ||
665 | |||
666 | while (*++p) { | ||
667 | if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) { | ||
668 | p[0][0] = '#'; | ||
669 | } | ||
670 | } | ||
671 | } | ||
672 | |||
673 | opt = bb_getopt_ulflags(argc, argv, "A:ne", &family); | ||
674 | |||
675 | if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) { | ||
676 | #ifdef CONFIG_FEATURE_IPV6 | ||
677 | if (strcmp(family, "inet6") == 0) { | ||
678 | opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ | ||
679 | } else | ||
680 | #endif | ||
681 | bb_show_usage(); | ||
682 | } | ||
683 | |||
684 | argv += optind; | ||
685 | |||
686 | /* No more args means display the routing table. */ | ||
687 | if (!*argv) { | ||
688 | int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; | ||
689 | #ifdef CONFIG_FEATURE_IPV6 | ||
690 | if (opt & ROUTE_OPT_INET6) | ||
691 | INET6_displayroutes(noresolve); | ||
692 | else | ||
693 | #endif | ||
694 | displayroutes(noresolve, opt & ROUTE_OPT_e); | ||
695 | |||
696 | bb_xferror_stdout(); | ||
697 | bb_fflush_stdout_and_exit(EXIT_SUCCESS); | ||
698 | } | ||
699 | |||
700 | /* Check verb. At the moment, must be add, del, or delete. */ | ||
701 | what = kw_lookup(tbl_verb, &argv); | ||
702 | if (!what || !*argv) { /* Unknown verb or no more args. */ | ||
703 | bb_show_usage(); | ||
704 | } | ||
705 | |||
706 | #ifdef CONFIG_FEATURE_IPV6 | ||
707 | if (opt & ROUTE_OPT_INET6) | ||
708 | INET6_setroute(what, argv); | ||
709 | else | ||
710 | #endif | ||
711 | INET_setroute(what, argv); | ||
712 | |||
713 | return EXIT_SUCCESS; | ||
714 | } | ||