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