source: MondoRescue/branches/3.3/mindi-busybox/networking/libiproute/iproute.c@ 3837

Last change on this file since 3837 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 22.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 *
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 *
7 * Changes:
8 *
9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
10 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
11 */
12
13#include "ip_common.h" /* #include "libbb.h" is inside */
14#include "common_bufsiz.h"
15#include "rt_names.h"
16#include "utils.h"
17
18#ifndef RTAX_RTTVAR
19#define RTAX_RTTVAR RTAX_HOPS
20#endif
21
22
23struct filter_t {
24 int tb;
25 smallint flushed;
26 char *flushb;
27 int flushp;
28 int flushe;
29 struct rtnl_handle *rth;
30 //int protocol, protocolmask; - write-only fields?!
31 //int scope, scopemask; - unused
32 //int type; - read-only
33 //int typemask; - unused
34 //int tos, tosmask; - unused
35 int iif;
36 int oif;
37 //int realm, realmmask; - unused
38 //inet_prefix rprefsrc; - read-only
39 inet_prefix rvia;
40 inet_prefix rdst;
41 inet_prefix mdst;
42 inet_prefix rsrc;
43 inet_prefix msrc;
44} FIX_ALIASING;
45typedef struct filter_t filter_t;
46
47#define G_filter (*(filter_t*)bb_common_bufsiz1)
48#define INIT_G() do { setup_common_bufsiz(); } while (0)
49
50static int flush_update(void)
51{
52 if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
53 bb_perror_msg("can't send flush request");
54 return -1;
55 }
56 G_filter.flushp = 0;
57 return 0;
58}
59
60static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
61 struct nlmsghdr *n, void *arg UNUSED_PARAM)
62{
63 struct rtmsg *r = NLMSG_DATA(n);
64 int len = n->nlmsg_len;
65 struct rtattr *tb[RTA_MAX+1];
66 inet_prefix dst;
67 inet_prefix src;
68 int host_len = -1;
69
70 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
71 fprintf(stderr, "Not a route: %08x %08x %08x\n",
72 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
73 return 0;
74 }
75 if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
76 return 0;
77 len -= NLMSG_LENGTH(sizeof(*r));
78 if (len < 0)
79 bb_error_msg_and_die("wrong nlmsg len %d", len);
80
81 if (r->rtm_family == AF_INET6)
82 host_len = 128;
83 else if (r->rtm_family == AF_INET)
84 host_len = 32;
85
86 if (r->rtm_family == AF_INET6) {
87 if (G_filter.tb) {
88 if (G_filter.tb < 0) {
89 if (!(r->rtm_flags & RTM_F_CLONED)) {
90 return 0;
91 }
92 } else {
93 if (r->rtm_flags & RTM_F_CLONED) {
94 return 0;
95 }
96 if (G_filter.tb == RT_TABLE_LOCAL) {
97 if (r->rtm_type != RTN_LOCAL) {
98 return 0;
99 }
100 } else if (G_filter.tb == RT_TABLE_MAIN) {
101 if (r->rtm_type == RTN_LOCAL) {
102 return 0;
103 }
104 } else {
105 return 0;
106 }
107 }
108 }
109 } else {
110 if (G_filter.tb > 0 && G_filter.tb != r->rtm_table) {
111 return 0;
112 }
113 }
114 if (G_filter.rdst.family
115 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
116 ) {
117 return 0;
118 }
119 if (G_filter.mdst.family
120 && (r->rtm_family != G_filter.mdst.family
121 || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
122 )
123 ) {
124 return 0;
125 }
126 if (G_filter.rsrc.family
127 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
128 ) {
129 return 0;
130 }
131 if (G_filter.msrc.family
132 && (r->rtm_family != G_filter.msrc.family
133 || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
134 )
135 ) {
136 return 0;
137 }
138
139 memset(tb, 0, sizeof(tb));
140 memset(&src, 0, sizeof(src));
141 memset(&dst, 0, sizeof(dst));
142 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
143
144 if (tb[RTA_SRC]) {
145 src.bitlen = r->rtm_src_len;
146 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
147 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
148 }
149 if (tb[RTA_DST]) {
150 dst.bitlen = r->rtm_dst_len;
151 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
152 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
153 }
154
155 if (G_filter.rdst.family
156 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
157 ) {
158 return 0;
159 }
160 if (G_filter.mdst.family
161 && G_filter.mdst.bitlen >= 0
162 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
163 ) {
164 return 0;
165 }
166 if (G_filter.rsrc.family
167 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
168 ) {
169 return 0;
170 }
171 if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
172 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
173 ) {
174 return 0;
175 }
176 if (G_filter.oif != 0) {
177 if (!tb[RTA_OIF])
178 return 0;
179 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
180 return 0;
181 }
182
183 if (G_filter.flushb) {
184 struct nlmsghdr *fn;
185
186 /* We are creating route flush commands */
187
188 if (r->rtm_family == AF_INET6
189 && r->rtm_dst_len == 0
190 && r->rtm_type == RTN_UNREACHABLE
191 && tb[RTA_PRIORITY]
192 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
193 ) {
194 return 0;
195 }
196
197 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
198 if (flush_update())
199 xfunc_die();
200 }
201 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
202 memcpy(fn, n, n->nlmsg_len);
203 fn->nlmsg_type = RTM_DELROUTE;
204 fn->nlmsg_flags = NLM_F_REQUEST;
205 fn->nlmsg_seq = ++G_filter.rth->seq;
206 G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
207 G_filter.flushed = 1;
208 return 0;
209 }
210
211 /* We are printing routes */
212
213 if (n->nlmsg_type == RTM_DELROUTE) {
214 printf("Deleted ");
215 }
216 if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
217 printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
218 }
219
220 if (tb[RTA_DST]) {
221 if (r->rtm_dst_len != host_len) {
222 printf("%s/%u ",
223 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
224 r->rtm_dst_len
225 );
226 } else {
227 printf("%s ", format_host(r->rtm_family,
228 RTA_PAYLOAD(tb[RTA_DST]),
229 RTA_DATA(tb[RTA_DST]))
230 );
231 }
232 } else if (r->rtm_dst_len) {
233 printf("0/%d ", r->rtm_dst_len);
234 } else {
235 printf("default ");
236 }
237 if (tb[RTA_SRC]) {
238 if (r->rtm_src_len != host_len) {
239 printf("from %s/%u ",
240 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
241 r->rtm_src_len
242 );
243 } else {
244 printf("from %s ", format_host(r->rtm_family,
245 RTA_PAYLOAD(tb[RTA_SRC]),
246 RTA_DATA(tb[RTA_SRC]))
247 );
248 }
249 } else if (r->rtm_src_len) {
250 printf("from 0/%u ", r->rtm_src_len);
251 }
252 if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
253 printf("via %s ", format_host(r->rtm_family,
254 RTA_PAYLOAD(tb[RTA_GATEWAY]),
255 RTA_DATA(tb[RTA_GATEWAY]))
256 );
257 }
258 if (tb[RTA_OIF]) {
259 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
260 }
261
262 /* Todo: parse & show "proto kernel", "scope link" here */
263
264 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
265 /* Do not use format_host(). It is our local addr
266 and symbolic name will not be useful.
267 */
268 printf(" src %s ", rt_addr_n2a(r->rtm_family,
269 RTA_DATA(tb[RTA_PREFSRC])));
270 }
271 if (tb[RTA_PRIORITY]) {
272 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
273 }
274 if (r->rtm_flags & RTNH_F_DEAD) {
275 printf("dead ");
276 }
277 if (r->rtm_flags & RTNH_F_ONLINK) {
278 printf("onlink ");
279 }
280 if (r->rtm_flags & RTNH_F_PERVASIVE) {
281 printf("pervasive ");
282 }
283 if (r->rtm_flags & RTM_F_NOTIFY) {
284 printf("notify ");
285 }
286
287 if (r->rtm_family == AF_INET6) {
288 struct rta_cacheinfo *ci = NULL;
289 if (tb[RTA_CACHEINFO]) {
290 ci = RTA_DATA(tb[RTA_CACHEINFO]);
291 }
292 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
293 if (r->rtm_flags & RTM_F_CLONED) {
294 printf("%c cache ", _SL_);
295 }
296 if (ci->rta_expires) {
297 printf(" expires %dsec", ci->rta_expires / get_hz());
298 }
299 if (ci->rta_error != 0) {
300 printf(" error %d", ci->rta_error);
301 }
302 } else if (ci) {
303 if (ci->rta_error != 0)
304 printf(" error %d", ci->rta_error);
305 }
306 }
307 if (tb[RTA_IIF] && G_filter.iif == 0) {
308 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
309 }
310 bb_putchar('\n');
311 return 0;
312}
313
314/* Return value becomes exitcode. It's okay to not return at all */
315static int iproute_modify(int cmd, unsigned flags, char **argv)
316{
317 static const char keywords[] ALIGN1 =
318 "src\0""via\0""mtu\0""lock\0""scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
319 "dev\0""oif\0""to\0""metric\0""onlink\0";
320 enum {
321 ARG_src,
322 ARG_via,
323 ARG_mtu, PARM_lock,
324 ARG_scope,
325 ARG_protocol,
326IF_FEATURE_IP_RULE(ARG_table,)
327 ARG_dev,
328 ARG_oif,
329 ARG_to,
330 ARG_metric,
331 ARG_onlink,
332 };
333 enum {
334 gw_ok = 1 << 0,
335 dst_ok = 1 << 1,
336 proto_ok = 1 << 2,
337 type_ok = 1 << 3
338 };
339 struct rtnl_handle rth;
340 struct {
341 struct nlmsghdr n;
342 struct rtmsg r;
343 char buf[1024];
344 } req;
345 char mxbuf[256];
346 struct rtattr * mxrta = (void*)mxbuf;
347 unsigned mxlock = 0;
348 char *d = NULL;
349 smalluint ok = 0;
350 smalluint scope_ok = 0;
351 int arg;
352
353 memset(&req, 0, sizeof(req));
354
355 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
356 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
357 req.n.nlmsg_type = cmd;
358 req.r.rtm_family = preferred_family;
359 if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
360 req.r.rtm_table = RT_TABLE_MAIN;
361 if (RT_SCOPE_NOWHERE != 0)
362 req.r.rtm_scope = RT_SCOPE_NOWHERE;
363
364 if (cmd != RTM_DELROUTE) {
365 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
366 if (RTPROT_BOOT != 0)
367 req.r.rtm_protocol = RTPROT_BOOT;
368 if (RTN_UNICAST != 0)
369 req.r.rtm_type = RTN_UNICAST;
370 }
371
372 mxrta->rta_type = RTA_METRICS;
373 mxrta->rta_len = RTA_LENGTH(0);
374
375 while (*argv) {
376 arg = index_in_substrings(keywords, *argv);
377 if (arg == ARG_src) {
378 inet_prefix addr;
379 NEXT_ARG();
380 get_addr(&addr, *argv, req.r.rtm_family);
381 if (req.r.rtm_family == AF_UNSPEC)
382 req.r.rtm_family = addr.family;
383 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
384 } else if (arg == ARG_via) {
385 inet_prefix addr;
386 ok |= gw_ok;
387 NEXT_ARG();
388 get_addr(&addr, *argv, req.r.rtm_family);
389 if (req.r.rtm_family == AF_UNSPEC) {
390 req.r.rtm_family = addr.family;
391 }
392 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
393 } else if (arg == ARG_mtu) {
394 unsigned mtu;
395 NEXT_ARG();
396 if (index_in_strings(keywords, *argv) == PARM_lock) {
397 mxlock |= (1 << RTAX_MTU);
398 NEXT_ARG();
399 }
400 mtu = get_unsigned(*argv, "mtu");
401 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
402 } else if (arg == ARG_scope) {
403 uint32_t scope;
404 NEXT_ARG();
405 if (rtnl_rtscope_a2n(&scope, *argv))
406 invarg_1_to_2(*argv, "scope");
407 req.r.rtm_scope = scope;
408 scope_ok = 1;
409 } else if (arg == ARG_protocol) {
410 uint32_t prot;
411 NEXT_ARG();
412 if (rtnl_rtprot_a2n(&prot, *argv))
413 invarg_1_to_2(*argv, "protocol");
414 req.r.rtm_protocol = prot;
415 ok |= proto_ok;
416#if ENABLE_FEATURE_IP_RULE
417 } else if (arg == ARG_table) {
418 uint32_t tid;
419 NEXT_ARG();
420 if (rtnl_rttable_a2n(&tid, *argv))
421 invarg_1_to_2(*argv, "table");
422 req.r.rtm_table = tid;
423#endif
424 } else if (arg == ARG_dev || arg == ARG_oif) {
425 NEXT_ARG();
426 d = *argv;
427 } else if (arg == ARG_metric) {
428 uint32_t metric;
429 NEXT_ARG();
430 metric = get_u32(*argv, "metric");
431 addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
432 } else if (arg == ARG_onlink) {
433 req.r.rtm_flags |= RTNH_F_ONLINK;
434 } else {
435 int type;
436 inet_prefix dst;
437
438 if (arg == ARG_to) {
439 NEXT_ARG();
440 }
441 if ((**argv < '0' || **argv > '9')
442 && rtnl_rtntype_a2n(&type, *argv) == 0
443 ) {
444 NEXT_ARG();
445 req.r.rtm_type = type;
446 ok |= type_ok;
447 }
448
449 if (ok & dst_ok) {
450 duparg2("to", *argv);
451 }
452 get_prefix(&dst, *argv, req.r.rtm_family);
453 if (req.r.rtm_family == AF_UNSPEC) {
454 req.r.rtm_family = dst.family;
455 }
456 req.r.rtm_dst_len = dst.bitlen;
457 ok |= dst_ok;
458 if (dst.bytelen) {
459 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
460 }
461 }
462 argv++;
463 }
464
465 xrtnl_open(&rth);
466
467 if (d) {
468 int idx;
469
470 ll_init_map(&rth);
471
472 if (d) {
473 idx = xll_name_to_index(d);
474 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
475 }
476 }
477
478 if (mxrta->rta_len > RTA_LENGTH(0)) {
479 if (mxlock) {
480 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
481 }
482 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
483 }
484
485 if (!scope_ok) {
486 if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
487 req.r.rtm_scope = RT_SCOPE_HOST;
488 else
489 if (req.r.rtm_type == RTN_BROADCAST
490 || req.r.rtm_type == RTN_MULTICAST
491 || req.r.rtm_type == RTN_ANYCAST
492 ) {
493 req.r.rtm_scope = RT_SCOPE_LINK;
494 }
495 else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
496 if (cmd == RTM_DELROUTE)
497 req.r.rtm_scope = RT_SCOPE_NOWHERE;
498 else if (!(ok & gw_ok))
499 req.r.rtm_scope = RT_SCOPE_LINK;
500 }
501 }
502
503 if (req.r.rtm_family == AF_UNSPEC) {
504 req.r.rtm_family = AF_INET;
505 }
506
507 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
508 return 2;
509 }
510
511 return 0;
512}
513
514static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
515{
516 struct {
517 struct nlmsghdr nlh;
518 struct rtmsg rtm;
519 } req;
520 struct sockaddr_nl nladdr;
521
522 memset(&nladdr, 0, sizeof(nladdr));
523 memset(&req, 0, sizeof(req));
524 nladdr.nl_family = AF_NETLINK;
525
526 req.nlh.nlmsg_len = sizeof(req);
527 if (RTM_GETROUTE)
528 req.nlh.nlmsg_type = RTM_GETROUTE;
529 if (NLM_F_ROOT | NLM_F_REQUEST)
530 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
531 /*req.nlh.nlmsg_pid = 0; - memset did it already */
532 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
533 req.rtm.rtm_family = family;
534 if (RTM_F_CLONED)
535 req.rtm.rtm_flags = RTM_F_CLONED;
536
537 return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
538}
539
540static void iproute_flush_cache(void)
541{
542 static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
543 int flush_fd = open_or_warn(fn, O_WRONLY);
544
545 if (flush_fd < 0) {
546 return;
547 }
548
549 if (write(flush_fd, "-1", 2) < 2) {
550 bb_perror_msg("can't flush routing cache");
551 return;
552 }
553 close(flush_fd);
554}
555
556static void iproute_reset_filter(void)
557{
558 memset(&G_filter, 0, sizeof(G_filter));
559 G_filter.mdst.bitlen = -1;
560 G_filter.msrc.bitlen = -1;
561}
562
563/* Return value becomes exitcode. It's okay to not return at all */
564static int iproute_list_or_flush(char **argv, int flush)
565{
566 int do_ipv6 = preferred_family;
567 struct rtnl_handle rth;
568 char *id = NULL;
569 char *od = NULL;
570 static const char keywords[] ALIGN1 =
571 /* "ip route list/flush" parameters: */
572 "protocol\0" "dev\0" "oif\0" "iif\0"
573 "via\0" "table\0" "cache\0"
574 "from\0" "to\0"
575 /* and possible further keywords */
576 "all\0"
577 "root\0"
578 "match\0"
579 "exact\0"
580 "main\0"
581 ;
582 enum {
583 KW_proto, KW_dev, KW_oif, KW_iif,
584 KW_via, KW_table, KW_cache,
585 KW_from, KW_to,
586 /* */
587 KW_all,
588 KW_root,
589 KW_match,
590 KW_exact,
591 KW_main,
592 };
593 int arg, parm;
594
595 iproute_reset_filter();
596 G_filter.tb = RT_TABLE_MAIN;
597
598 if (flush && !*argv)
599 bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
600
601 while (*argv) {
602 arg = index_in_substrings(keywords, *argv);
603 if (arg == KW_proto) {
604 uint32_t prot = 0;
605 NEXT_ARG();
606 //G_filter.protocolmask = -1;
607 if (rtnl_rtprot_a2n(&prot, *argv)) {
608 if (index_in_strings(keywords, *argv) != KW_all)
609 invarg_1_to_2(*argv, "protocol");
610 prot = 0;
611 //G_filter.protocolmask = 0;
612 }
613 //G_filter.protocol = prot;
614 } else if (arg == KW_dev || arg == KW_oif) {
615 NEXT_ARG();
616 od = *argv;
617 } else if (arg == KW_iif) {
618 NEXT_ARG();
619 id = *argv;
620 } else if (arg == KW_via) {
621 NEXT_ARG();
622 get_prefix(&G_filter.rvia, *argv, do_ipv6);
623 } else if (arg == KW_table) { /* table all/cache/main */
624 NEXT_ARG();
625 parm = index_in_substrings(keywords, *argv);
626 if (parm == KW_cache)
627 G_filter.tb = -1;
628 else if (parm == KW_all)
629 G_filter.tb = 0;
630 else if (parm != KW_main) {
631#if ENABLE_FEATURE_IP_RULE
632 uint32_t tid;
633 if (rtnl_rttable_a2n(&tid, *argv))
634 invarg_1_to_2(*argv, "table");
635 G_filter.tb = tid;
636#else
637 invarg_1_to_2(*argv, "table");
638#endif
639 }
640 } else if (arg == KW_cache) {
641 /* The command 'ip route flush cache' is used by OpenSWAN.
642 * Assuming it's a synonym for 'ip route flush table cache' */
643 G_filter.tb = -1;
644 } else if (arg == KW_from) {
645 NEXT_ARG();
646 parm = index_in_substrings(keywords, *argv);
647 if (parm == KW_root) {
648 NEXT_ARG();
649 get_prefix(&G_filter.rsrc, *argv, do_ipv6);
650 } else if (parm == KW_match) {
651 NEXT_ARG();
652 get_prefix(&G_filter.msrc, *argv, do_ipv6);
653 } else {
654 if (parm == KW_exact)
655 NEXT_ARG();
656 get_prefix(&G_filter.msrc, *argv, do_ipv6);
657 G_filter.rsrc = G_filter.msrc;
658 }
659 } else { /* "to" is the default parameter */
660 if (arg == KW_to) {
661 NEXT_ARG();
662 arg = index_in_substrings(keywords, *argv);
663 }
664 /* parm = arg; - would be more plausible, but we reuse 'arg' here */
665 if (arg == KW_root) {
666 NEXT_ARG();
667 get_prefix(&G_filter.rdst, *argv, do_ipv6);
668 } else if (arg == KW_match) {
669 NEXT_ARG();
670 get_prefix(&G_filter.mdst, *argv, do_ipv6);
671 } else { /* "to exact" is the default */
672 if (arg == KW_exact)
673 NEXT_ARG();
674 get_prefix(&G_filter.mdst, *argv, do_ipv6);
675 G_filter.rdst = G_filter.mdst;
676 }
677 }
678 argv++;
679 }
680
681 if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
682 do_ipv6 = AF_INET;
683 }
684
685 xrtnl_open(&rth);
686 ll_init_map(&rth);
687
688 if (id || od) {
689 int idx;
690
691 if (id) {
692 idx = xll_name_to_index(id);
693 G_filter.iif = idx;
694 }
695 if (od) {
696 idx = xll_name_to_index(od);
697 G_filter.oif = idx;
698 }
699 }
700
701 if (flush) {
702 char flushb[4096-512];
703
704 if (G_filter.tb == -1) { /* "flush table cache" */
705 if (do_ipv6 != AF_INET6)
706 iproute_flush_cache();
707 if (do_ipv6 == AF_INET)
708 return 0;
709 }
710
711 G_filter.flushb = flushb;
712 G_filter.flushp = 0;
713 G_filter.flushe = sizeof(flushb);
714 G_filter.rth = &rth;
715
716 for (;;) {
717 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
718 G_filter.flushed = 0;
719 xrtnl_dump_filter(&rth, print_route, NULL);
720 if (G_filter.flushed == 0)
721 return 0;
722 if (flush_update())
723 return 1;
724 }
725 }
726
727 if (G_filter.tb != -1) {
728 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
729 } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
730 bb_perror_msg_and_die("can't send dump request");
731 }
732 xrtnl_dump_filter(&rth, print_route, NULL);
733
734 return 0;
735}
736
737
738/* Return value becomes exitcode. It's okay to not return at all */
739static int iproute_get(char **argv)
740{
741 struct rtnl_handle rth;
742 struct {
743 struct nlmsghdr n;
744 struct rtmsg r;
745 char buf[1024];
746 } req;
747 char *idev = NULL;
748 char *odev = NULL;
749 bool connected = 0;
750 bool from_ok = 0;
751 static const char options[] ALIGN1 =
752 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
753
754 memset(&req, 0, sizeof(req));
755
756 iproute_reset_filter();
757
758 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
759 if (NLM_F_REQUEST)
760 req.n.nlmsg_flags = NLM_F_REQUEST;
761 if (RTM_GETROUTE)
762 req.n.nlmsg_type = RTM_GETROUTE;
763 req.r.rtm_family = preferred_family;
764 /*req.r.rtm_table = 0; - memset did this already */
765 /*req.r.rtm_protocol = 0;*/
766 /*req.r.rtm_scope = 0;*/
767 /*req.r.rtm_type = 0;*/
768 /*req.r.rtm_src_len = 0;*/
769 /*req.r.rtm_dst_len = 0;*/
770 /*req.r.rtm_tos = 0;*/
771
772 while (*argv) {
773 switch (index_in_strings(options, *argv)) {
774 case 0: /* from */
775 {
776 inet_prefix addr;
777 NEXT_ARG();
778 from_ok = 1;
779 get_prefix(&addr, *argv, req.r.rtm_family);
780 if (req.r.rtm_family == AF_UNSPEC) {
781 req.r.rtm_family = addr.family;
782 }
783 if (addr.bytelen) {
784 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
785 }
786 req.r.rtm_src_len = addr.bitlen;
787 break;
788 }
789 case 1: /* iif */
790 NEXT_ARG();
791 idev = *argv;
792 break;
793 case 2: /* oif */
794 case 3: /* dev */
795 NEXT_ARG();
796 odev = *argv;
797 break;
798 case 4: /* notify */
799 req.r.rtm_flags |= RTM_F_NOTIFY;
800 break;
801 case 5: /* connected */
802 connected = 1;
803 break;
804 case 6: /* to */
805 NEXT_ARG();
806 default:
807 {
808 inet_prefix addr;
809 get_prefix(&addr, *argv, req.r.rtm_family);
810 if (req.r.rtm_family == AF_UNSPEC) {
811 req.r.rtm_family = addr.family;
812 }
813 if (addr.bytelen) {
814 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
815 }
816 req.r.rtm_dst_len = addr.bitlen;
817 }
818 }
819 argv++;
820 }
821
822 if (req.r.rtm_dst_len == 0) {
823 bb_error_msg_and_die("need at least destination address");
824 }
825
826 xrtnl_open(&rth);
827
828 ll_init_map(&rth);
829
830 if (idev || odev) {
831 int idx;
832
833 if (idev) {
834 idx = xll_name_to_index(idev);
835 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
836 }
837 if (odev) {
838 idx = xll_name_to_index(odev);
839 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
840 }
841 }
842
843 if (req.r.rtm_family == AF_UNSPEC) {
844 req.r.rtm_family = AF_INET;
845 }
846
847 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
848 return 2;
849 }
850
851 if (connected && !from_ok) {
852 struct rtmsg *r = NLMSG_DATA(&req.n);
853 int len = req.n.nlmsg_len;
854 struct rtattr * tb[RTA_MAX+1];
855
856 print_route(NULL, &req.n, NULL);
857
858 if (req.n.nlmsg_type != RTM_NEWROUTE) {
859 bb_error_msg_and_die("not a route?");
860 }
861 len -= NLMSG_LENGTH(sizeof(*r));
862 if (len < 0) {
863 bb_error_msg_and_die("wrong len %d", len);
864 }
865
866 memset(tb, 0, sizeof(tb));
867 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
868
869 if (tb[RTA_PREFSRC]) {
870 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
871 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
872 } else if (!tb[RTA_SRC]) {
873 bb_error_msg_and_die("can't connect the route");
874 }
875 if (!odev && tb[RTA_OIF]) {
876 tb[RTA_OIF]->rta_type = 0;
877 }
878 if (tb[RTA_GATEWAY]) {
879 tb[RTA_GATEWAY]->rta_type = 0;
880 }
881 if (!idev && tb[RTA_IIF]) {
882 tb[RTA_IIF]->rta_type = 0;
883 }
884 req.n.nlmsg_flags = NLM_F_REQUEST;
885 req.n.nlmsg_type = RTM_GETROUTE;
886
887 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
888 return 2;
889 }
890 }
891 print_route(NULL, &req.n, NULL);
892 return 0;
893}
894
895/* Return value becomes exitcode. It's okay to not return at all */
896int FAST_FUNC do_iproute(char **argv)
897{
898 static const char ip_route_commands[] ALIGN1 =
899 /*0-3*/ "add\0""append\0""change\0""chg\0"
900 /*4-7*/ "delete\0""get\0""list\0""show\0"
901 /*8..*/ "prepend\0""replace\0""test\0""flush\0";
902 int command_num;
903 unsigned flags = 0;
904 int cmd = RTM_NEWROUTE;
905
906 INIT_G();
907
908 if (!*argv)
909 return iproute_list_or_flush(argv, 0);
910
911 /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
912 /* It probably means that it is using "first match" rule */
913 command_num = index_in_substrings(ip_route_commands, *argv);
914
915 switch (command_num) {
916 case 0: /* add */
917 flags = NLM_F_CREATE|NLM_F_EXCL;
918 break;
919 case 1: /* append */
920 flags = NLM_F_CREATE|NLM_F_APPEND;
921 break;
922 case 2: /* change */
923 case 3: /* chg */
924 flags = NLM_F_REPLACE;
925 break;
926 case 4: /* delete */
927 cmd = RTM_DELROUTE;
928 break;
929 case 5: /* get */
930 return iproute_get(argv+1);
931 case 6: /* list */
932 case 7: /* show */
933 return iproute_list_or_flush(argv+1, 0);
934 case 8: /* prepend */
935 flags = NLM_F_CREATE;
936 break;
937 case 9: /* replace */
938 flags = NLM_F_CREATE|NLM_F_REPLACE;
939 break;
940 case 10: /* test */
941 flags = NLM_F_EXCL;
942 break;
943 case 11: /* flush */
944 return iproute_list_or_flush(argv+1, 1);
945 default:
946 invarg_1_to_2(*argv, applet_name);
947 }
948
949 return iproute_modify(cmd, flags, argv+1);
950}
Note: See TracBrowser for help on using the repository browser.