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

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 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.