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