1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
10 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
13 #include "ip_common.h" /* #include "libbb.h" is inside */
14 #include "common_bufsiz.h"
19 #define RTAX_RTTVAR RTAX_HOPS
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
37 //int realm, realmmask; - unused
38 //inet_prefix rprefsrc; - read-only
45 typedef struct filter_t filter_t;
47 #define G_filter (*(filter_t*)bb_common_bufsiz1)
48 #define INIT_G() do { setup_common_bufsiz(); } while (0)
50 static int flush_update(void)
52 if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
53 bb_perror_msg("can't send flush request");
60 static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
61 struct nlmsghdr *n, void *arg UNUSED_PARAM)
63 struct rtmsg *r = NLMSG_DATA(n);
64 int len = n->nlmsg_len;
65 struct rtattr *tb[RTA_MAX+1];
71 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
72 fprintf(stderr, "Not a route: %08x %08x %08x\n",
73 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
76 if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
78 len -= NLMSG_LENGTH(sizeof(*r));
80 bb_error_msg_and_die("wrong nlmsg len %d", len);
82 memset(tb, 0, sizeof(tb));
83 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
86 tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
90 if (r->rtm_family == AF_INET6)
92 else if (r->rtm_family == AF_INET)
95 if (r->rtm_family == AF_INET6) {
97 if (G_filter.tb < 0) {
98 if (!(r->rtm_flags & RTM_F_CLONED)) {
102 if (r->rtm_flags & RTM_F_CLONED) {
105 if (G_filter.tb == RT_TABLE_LOCAL) {
106 if (r->rtm_type != RTN_LOCAL) {
109 } else if (G_filter.tb == RT_TABLE_MAIN) {
110 if (r->rtm_type == RTN_LOCAL) {
119 if (G_filter.tb > 0 && G_filter.tb != tid) {
123 if (G_filter.rdst.family
124 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
128 if (G_filter.mdst.family
129 && (r->rtm_family != G_filter.mdst.family
130 || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
135 if (G_filter.rsrc.family
136 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
140 if (G_filter.msrc.family
141 && (r->rtm_family != G_filter.msrc.family
142 || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
148 memset(&src, 0, sizeof(src));
149 memset(&dst, 0, sizeof(dst));
152 src.bitlen = r->rtm_src_len;
153 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
154 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
157 dst.bitlen = r->rtm_dst_len;
158 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
159 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
162 if (G_filter.rdst.family
163 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
167 if (G_filter.mdst.family
168 && G_filter.mdst.bitlen >= 0
169 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
173 if (G_filter.rsrc.family
174 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
178 if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
179 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
183 if (G_filter.oif != 0) {
186 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
190 if (G_filter.flushb) {
193 /* We are creating route flush commands */
195 if (r->rtm_family == AF_INET6
196 && r->rtm_dst_len == 0
197 && r->rtm_type == RTN_UNREACHABLE
199 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
204 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
208 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
209 memcpy(fn, n, n->nlmsg_len);
210 fn->nlmsg_type = RTM_DELROUTE;
211 fn->nlmsg_flags = NLM_F_REQUEST;
212 fn->nlmsg_seq = ++G_filter.rth->seq;
213 G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
214 G_filter.flushed = 1;
218 /* We are printing routes */
220 if (n->nlmsg_type == RTM_DELROUTE) {
223 if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
224 printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
228 if (r->rtm_dst_len != host_len) {
230 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
234 printf("%s ", format_host(r->rtm_family,
235 RTA_PAYLOAD(tb[RTA_DST]),
236 RTA_DATA(tb[RTA_DST]))
239 } else if (r->rtm_dst_len) {
240 printf("0/%d ", r->rtm_dst_len);
245 if (r->rtm_src_len != host_len) {
246 printf("from %s/%u ",
247 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
251 printf("from %s ", format_host(r->rtm_family,
252 RTA_PAYLOAD(tb[RTA_SRC]),
253 RTA_DATA(tb[RTA_SRC]))
256 } else if (r->rtm_src_len) {
257 printf("from 0/%u ", r->rtm_src_len);
259 if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
260 printf("via %s ", format_host(r->rtm_family,
261 RTA_PAYLOAD(tb[RTA_GATEWAY]),
262 RTA_DATA(tb[RTA_GATEWAY]))
266 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
268 #if ENABLE_FEATURE_IP_RULE
269 if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
270 printf("table %s ", rtnl_rttable_n2a(tid));
273 /* Todo: parse & show "proto kernel", "scope link" here */
275 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
276 /* Do not use format_host(). It is our local addr
277 and symbolic name will not be useful.
279 printf(" src %s ", rt_addr_n2a(r->rtm_family,
280 RTA_DATA(tb[RTA_PREFSRC])));
282 if (tb[RTA_PRIORITY]) {
283 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
285 if (r->rtm_flags & RTNH_F_DEAD) {
288 if (r->rtm_flags & RTNH_F_ONLINK) {
291 if (r->rtm_flags & RTNH_F_PERVASIVE) {
292 printf("pervasive ");
294 if (r->rtm_flags & RTM_F_NOTIFY) {
298 if (r->rtm_family == AF_INET6) {
299 struct rta_cacheinfo *ci = NULL;
300 if (tb[RTA_CACHEINFO]) {
301 ci = RTA_DATA(tb[RTA_CACHEINFO]);
303 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
304 if (r->rtm_flags & RTM_F_CLONED) {
305 printf("%c cache ", _SL_);
307 if (ci->rta_expires) {
308 printf(" expires %dsec", ci->rta_expires / get_hz());
310 if (ci->rta_error != 0) {
311 printf(" error %d", ci->rta_error);
314 if (ci->rta_error != 0)
315 printf(" error %d", ci->rta_error);
318 if (tb[RTA_IIF] && G_filter.iif == 0) {
319 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
325 static int str_is_lock(const char *str)
327 return strcmp(str, "lock") == 0;
330 /* Return value becomes exitcode. It's okay to not return at all */
331 static int iproute_modify(int cmd, unsigned flags, char **argv)
333 /* If you add stuff here, update iproute_full_usage */
334 static const char keywords[] ALIGN1 =
337 "scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
338 "dev\0""oif\0""to\0""metric\0""onlink\0";
346 IF_FEATURE_IP_RULE(ARG_table,)
359 struct rtnl_handle rth;
366 struct rtattr * mxrta = (void*)mxbuf;
370 smalluint scope_ok = 0;
373 memset(&req, 0, sizeof(req));
375 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
376 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
377 req.n.nlmsg_type = cmd;
378 req.r.rtm_family = preferred_family;
379 if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
380 req.r.rtm_table = RT_TABLE_MAIN;
381 if (RT_SCOPE_NOWHERE != 0)
382 req.r.rtm_scope = RT_SCOPE_NOWHERE;
384 if (cmd != RTM_DELROUTE) {
385 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
386 if (RTPROT_BOOT != 0)
387 req.r.rtm_protocol = RTPROT_BOOT;
388 if (RTN_UNICAST != 0)
389 req.r.rtm_type = RTN_UNICAST;
392 mxrta->rta_type = RTA_METRICS;
393 mxrta->rta_len = RTA_LENGTH(0);
396 arg = index_in_substrings(keywords, *argv);
397 if (arg == ARG_src) {
400 get_addr(&addr, *argv, req.r.rtm_family);
401 if (req.r.rtm_family == AF_UNSPEC)
402 req.r.rtm_family = addr.family;
403 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
404 } else if (arg == ARG_via) {
408 get_addr(&addr, *argv, req.r.rtm_family);
409 if (req.r.rtm_family == AF_UNSPEC) {
410 req.r.rtm_family = addr.family;
412 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
413 } else if (arg == ARG_mtu) {
416 if (str_is_lock(*argv)) {
417 mxlock |= (1 << RTAX_MTU);
420 mtu = get_unsigned(*argv, "mtu");
421 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
422 } else if (arg == ARG_advmss) {
425 if (str_is_lock(*argv)) {
426 mxlock |= (1 << RTAX_ADVMSS);
429 mss = get_unsigned(*argv, "advmss");
430 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
431 } else if (arg == ARG_scope) {
434 if (rtnl_rtscope_a2n(&scope, *argv))
435 invarg_1_to_2(*argv, "scope");
436 req.r.rtm_scope = scope;
438 } else if (arg == ARG_protocol) {
441 if (rtnl_rtprot_a2n(&prot, *argv))
442 invarg_1_to_2(*argv, "protocol");
443 req.r.rtm_protocol = prot;
445 #if ENABLE_FEATURE_IP_RULE
446 } else if (arg == ARG_table) {
449 if (rtnl_rttable_a2n(&tid, *argv))
450 invarg_1_to_2(*argv, "table");
452 req.r.rtm_table = tid;
454 req.r.rtm_table = RT_TABLE_UNSPEC;
455 addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
458 } else if (arg == ARG_dev || arg == ARG_oif) {
461 } else if (arg == ARG_metric) {
462 //TODO: "metric", "priority" and "preference" are synonyms
465 metric = get_u32(*argv, "metric");
466 addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
467 } else if (arg == ARG_onlink) {
468 req.r.rtm_flags |= RTNH_F_ONLINK;
476 if ((**argv < '0' || **argv > '9')
477 && rtnl_rtntype_a2n(&type, *argv) == 0
480 req.r.rtm_type = type;
485 duparg2("to", *argv);
487 get_prefix(&dst, *argv, req.r.rtm_family);
488 if (req.r.rtm_family == AF_UNSPEC) {
489 req.r.rtm_family = dst.family;
491 req.r.rtm_dst_len = dst.bitlen;
494 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
497 /* Other keywords recognized by iproute2-3.19.0: */
499 } else if (strcmp(*argv, "from") == 0) {
502 get_prefix(&addr, *argv, req.r.rtm_family);
503 if (req.r.rtm_family == AF_UNSPEC)
504 req.r.rtm_family = addr.family;
506 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
507 req.r.rtm_src_len = addr.bitlen;
508 } else if (strcmp(*argv, "tos") == 0 ||
509 matches(*argv, "dsfield") == 0) {
512 if (rtnl_dsfield_a2n(&tos, *argv))
513 invarg("\"tos\" value is invalid\n", *argv);
515 } else if (strcmp(*argv, "hoplimit") == 0) {
518 if (strcmp(*argv, "lock") == 0) {
519 mxlock |= (1<<RTAX_HOPLIMIT);
522 if (get_unsigned(&hoplimit, *argv, 0))
523 invarg("\"hoplimit\" value is invalid\n", *argv);
524 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
525 } else if (matches(*argv, "reordering") == 0) {
528 if (strcmp(*argv, "lock") == 0) {
529 mxlock |= (1<<RTAX_REORDERING);
532 if (get_unsigned(&reord, *argv, 0))
533 invarg("\"reordering\" value is invalid\n", *argv);
534 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
535 } else if (strcmp(*argv, "rtt") == 0) {
538 if (strcmp(*argv, "lock") == 0) {
539 mxlock |= (1<<RTAX_RTT);
542 if (get_time_rtt(&rtt, *argv, &raw))
543 invarg("\"rtt\" value is invalid\n", *argv);
544 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
545 (raw) ? rtt : rtt * 8);
546 } else if (strcmp(*argv, "rto_min") == 0) {
549 mxlock |= (1<<RTAX_RTO_MIN);
550 if (get_time_rtt(&rto_min, *argv, &raw))
551 invarg("\"rto_min\" value is invalid\n",
553 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
555 } else if (matches(*argv, "window") == 0) {
558 if (strcmp(*argv, "lock") == 0) {
559 mxlock |= (1<<RTAX_WINDOW);
562 if (get_unsigned(&win, *argv, 0))
563 invarg("\"window\" value is invalid\n", *argv);
564 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
565 } else if (matches(*argv, "cwnd") == 0) {
568 if (strcmp(*argv, "lock") == 0) {
569 mxlock |= (1<<RTAX_CWND);
572 if (get_unsigned(&win, *argv, 0))
573 invarg("\"cwnd\" value is invalid\n", *argv);
574 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
575 } else if (matches(*argv, "initcwnd") == 0) {
578 if (strcmp(*argv, "lock") == 0) {
579 mxlock |= (1<<RTAX_INITCWND);
582 if (get_unsigned(&win, *argv, 0))
583 invarg("\"initcwnd\" value is invalid\n", *argv);
584 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
585 } else if (matches(*argv, "initrwnd") == 0) {
588 if (strcmp(*argv, "lock") == 0) {
589 mxlock |= (1<<RTAX_INITRWND);
592 if (get_unsigned(&win, *argv, 0))
593 invarg("\"initrwnd\" value is invalid\n", *argv);
594 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
595 } else if (matches(*argv, "features") == 0) {
596 unsigned int features = 0;
601 if (strcmp(*argv, "ecn") == 0)
602 features |= RTAX_FEATURE_ECN;
604 invarg("\"features\" value not valid\n", *argv);
608 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
609 } else if (matches(*argv, "quickack") == 0) {
612 if (get_unsigned(&quickack, *argv, 0))
613 invarg("\"quickack\" value is invalid\n", *argv);
614 if (quickack != 1 && quickack != 0)
615 invarg("\"quickack\" value should be 0 or 1\n", *argv);
616 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
617 } else if (matches(*argv, "rttvar") == 0) {
620 if (strcmp(*argv, "lock") == 0) {
621 mxlock |= (1<<RTAX_RTTVAR);
624 if (get_time_rtt(&win, *argv, &raw))
625 invarg("\"rttvar\" value is invalid\n", *argv);
626 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
627 (raw) ? win : win * 4);
628 } else if (matches(*argv, "ssthresh") == 0) {
631 if (strcmp(*argv, "lock") == 0) {
632 mxlock |= (1<<RTAX_SSTHRESH);
635 if (get_unsigned(&win, *argv, 0))
636 invarg("\"ssthresh\" value is invalid\n", *argv);
637 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
638 } else if (matches(*argv, "realms") == 0) {
641 if (get_rt_realms(&realm, *argv))
642 invarg("\"realm\" value is invalid\n", *argv);
643 addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
644 } else if (strcmp(*argv, "nexthop") == 0) {
660 idx = xll_name_to_index(d);
661 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
665 if (mxrta->rta_len > RTA_LENGTH(0)) {
667 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
669 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
673 if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
674 req.r.rtm_scope = RT_SCOPE_HOST;
676 if (req.r.rtm_type == RTN_BROADCAST
677 || req.r.rtm_type == RTN_MULTICAST
678 || req.r.rtm_type == RTN_ANYCAST
680 req.r.rtm_scope = RT_SCOPE_LINK;
682 else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
683 if (cmd == RTM_DELROUTE)
684 req.r.rtm_scope = RT_SCOPE_NOWHERE;
685 else if (!(ok & gw_ok))
686 req.r.rtm_scope = RT_SCOPE_LINK;
690 if (req.r.rtm_family == AF_UNSPEC) {
691 req.r.rtm_family = AF_INET;
694 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
701 static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
707 struct sockaddr_nl nladdr;
709 memset(&nladdr, 0, sizeof(nladdr));
710 memset(&req, 0, sizeof(req));
711 nladdr.nl_family = AF_NETLINK;
713 req.nlh.nlmsg_len = sizeof(req);
715 req.nlh.nlmsg_type = RTM_GETROUTE;
716 if (NLM_F_ROOT | NLM_F_REQUEST)
717 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
718 /*req.nlh.nlmsg_pid = 0; - memset did it already */
719 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
720 req.rtm.rtm_family = family;
722 req.rtm.rtm_flags = RTM_F_CLONED;
724 return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
727 static void iproute_flush_cache(void)
729 static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
730 int flush_fd = open_or_warn(fn, O_WRONLY);
736 if (write(flush_fd, "-1", 2) < 2) {
737 bb_perror_msg("can't flush routing cache");
743 static void iproute_reset_filter(void)
745 memset(&G_filter, 0, sizeof(G_filter));
746 G_filter.mdst.bitlen = -1;
747 G_filter.msrc.bitlen = -1;
750 /* Return value becomes exitcode. It's okay to not return at all */
751 static int iproute_list_or_flush(char **argv, int flush)
753 int do_ipv6 = preferred_family;
754 struct rtnl_handle rth;
757 static const char keywords[] ALIGN1 =
758 /* "ip route list/flush" parameters: */
759 "protocol\0" "dev\0" "oif\0" "iif\0"
760 "via\0" "table\0" "cache\0"
762 /* and possible further keywords */
770 KW_proto, KW_dev, KW_oif, KW_iif,
771 KW_via, KW_table, KW_cache,
782 iproute_reset_filter();
783 G_filter.tb = RT_TABLE_MAIN;
786 bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
789 arg = index_in_substrings(keywords, *argv);
790 if (arg == KW_proto) {
793 //G_filter.protocolmask = -1;
794 if (rtnl_rtprot_a2n(&prot, *argv)) {
795 if (index_in_strings(keywords, *argv) != KW_all)
796 invarg_1_to_2(*argv, "protocol");
798 //G_filter.protocolmask = 0;
800 //G_filter.protocol = prot;
801 } else if (arg == KW_dev || arg == KW_oif) {
804 } else if (arg == KW_iif) {
807 } else if (arg == KW_via) {
809 get_prefix(&G_filter.rvia, *argv, do_ipv6);
810 } else if (arg == KW_table) { /* table all/cache/main */
812 parm = index_in_substrings(keywords, *argv);
813 if (parm == KW_cache)
815 else if (parm == KW_all)
817 else if (parm != KW_main) {
818 #if ENABLE_FEATURE_IP_RULE
820 if (rtnl_rttable_a2n(&tid, *argv))
821 invarg_1_to_2(*argv, "table");
824 invarg_1_to_2(*argv, "table");
827 } else if (arg == KW_cache) {
828 /* The command 'ip route flush cache' is used by OpenSWAN.
829 * Assuming it's a synonym for 'ip route flush table cache' */
831 } else if (arg == KW_from) {
833 parm = index_in_substrings(keywords, *argv);
834 if (parm == KW_root) {
836 get_prefix(&G_filter.rsrc, *argv, do_ipv6);
837 } else if (parm == KW_match) {
839 get_prefix(&G_filter.msrc, *argv, do_ipv6);
841 if (parm == KW_exact)
843 get_prefix(&G_filter.msrc, *argv, do_ipv6);
844 G_filter.rsrc = G_filter.msrc;
846 } else { /* "to" is the default parameter */
849 arg = index_in_substrings(keywords, *argv);
851 /* parm = arg; - would be more plausible, but we reuse 'arg' here */
852 if (arg == KW_root) {
854 get_prefix(&G_filter.rdst, *argv, do_ipv6);
855 } else if (arg == KW_match) {
857 get_prefix(&G_filter.mdst, *argv, do_ipv6);
858 } else { /* "to exact" is the default */
861 get_prefix(&G_filter.mdst, *argv, do_ipv6);
862 G_filter.rdst = G_filter.mdst;
868 if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
879 idx = xll_name_to_index(id);
883 idx = xll_name_to_index(od);
889 char flushb[4096-512];
891 if (G_filter.tb == -1) { /* "flush table cache" */
892 if (do_ipv6 != AF_INET6)
893 iproute_flush_cache();
894 if (do_ipv6 == AF_INET)
898 G_filter.flushb = flushb;
900 G_filter.flushe = sizeof(flushb);
904 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
905 G_filter.flushed = 0;
906 xrtnl_dump_filter(&rth, print_route, NULL);
907 if (G_filter.flushed == 0)
914 if (G_filter.tb != -1) {
915 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
916 } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
917 bb_perror_msg_and_die("can't send dump request");
919 xrtnl_dump_filter(&rth, print_route, NULL);
925 /* Return value becomes exitcode. It's okay to not return at all */
926 static int iproute_get(char **argv)
928 struct rtnl_handle rth;
938 static const char options[] ALIGN1 =
939 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
941 memset(&req, 0, sizeof(req));
943 iproute_reset_filter();
945 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
947 req.n.nlmsg_flags = NLM_F_REQUEST;
949 req.n.nlmsg_type = RTM_GETROUTE;
950 req.r.rtm_family = preferred_family;
951 /*req.r.rtm_table = 0; - memset did this already */
952 /*req.r.rtm_protocol = 0;*/
953 /*req.r.rtm_scope = 0;*/
954 /*req.r.rtm_type = 0;*/
955 /*req.r.rtm_src_len = 0;*/
956 /*req.r.rtm_dst_len = 0;*/
957 /*req.r.rtm_tos = 0;*/
960 switch (index_in_strings(options, *argv)) {
966 get_prefix(&addr, *argv, req.r.rtm_family);
967 if (req.r.rtm_family == AF_UNSPEC) {
968 req.r.rtm_family = addr.family;
971 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
973 req.r.rtm_src_len = addr.bitlen;
986 req.r.rtm_flags |= RTM_F_NOTIFY;
988 case 5: /* connected */
996 get_prefix(&addr, *argv, req.r.rtm_family);
997 if (req.r.rtm_family == AF_UNSPEC) {
998 req.r.rtm_family = addr.family;
1001 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
1003 req.r.rtm_dst_len = addr.bitlen;
1009 if (req.r.rtm_dst_len == 0) {
1010 bb_error_msg_and_die("need at least destination address");
1021 idx = xll_name_to_index(idev);
1022 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
1025 idx = xll_name_to_index(odev);
1026 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
1030 if (req.r.rtm_family == AF_UNSPEC) {
1031 req.r.rtm_family = AF_INET;
1034 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1038 if (connected && !from_ok) {
1039 struct rtmsg *r = NLMSG_DATA(&req.n);
1040 int len = req.n.nlmsg_len;
1041 struct rtattr * tb[RTA_MAX+1];
1043 print_route(NULL, &req.n, NULL);
1045 if (req.n.nlmsg_type != RTM_NEWROUTE) {
1046 bb_error_msg_and_die("not a route?");
1048 len -= NLMSG_LENGTH(sizeof(*r));
1050 bb_error_msg_and_die("wrong len %d", len);
1053 memset(tb, 0, sizeof(tb));
1054 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
1056 if (tb[RTA_PREFSRC]) {
1057 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
1058 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
1059 } else if (!tb[RTA_SRC]) {
1060 bb_error_msg_and_die("can't connect the route");
1062 if (!odev && tb[RTA_OIF]) {
1063 tb[RTA_OIF]->rta_type = 0;
1065 if (tb[RTA_GATEWAY]) {
1066 tb[RTA_GATEWAY]->rta_type = 0;
1068 if (!idev && tb[RTA_IIF]) {
1069 tb[RTA_IIF]->rta_type = 0;
1071 req.n.nlmsg_flags = NLM_F_REQUEST;
1072 req.n.nlmsg_type = RTM_GETROUTE;
1074 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1078 print_route(NULL, &req.n, NULL);
1082 /* Return value becomes exitcode. It's okay to not return at all */
1083 int FAST_FUNC do_iproute(char **argv)
1085 static const char ip_route_commands[] ALIGN1 =
1086 /*0-3*/ "add\0""append\0""change\0""chg\0"
1087 /*4-7*/ "delete\0""get\0""list\0""show\0"
1088 /*8..*/ "prepend\0""replace\0""test\0""flush\0";
1091 int cmd = RTM_NEWROUTE;
1096 return iproute_list_or_flush(argv, 0);
1098 /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
1099 /* It probably means that it is using "first match" rule */
1100 command_num = index_in_substrings(ip_route_commands, *argv);
1102 switch (command_num) {
1104 flags = NLM_F_CREATE|NLM_F_EXCL;
1106 case 1: /* append */
1107 flags = NLM_F_CREATE|NLM_F_APPEND;
1109 case 2: /* change */
1111 flags = NLM_F_REPLACE;
1113 case 4: /* delete */
1117 return iproute_get(argv+1);
1120 return iproute_list_or_flush(argv+1, 0);
1121 case 8: /* prepend */
1122 flags = NLM_F_CREATE;
1124 case 9: /* replace */
1125 flags = NLM_F_CREATE|NLM_F_REPLACE;
1130 case 11: /* flush */
1131 return iproute_list_or_flush(argv+1, 1);
1133 invarg_1_to_2(*argv, applet_name);
1136 return iproute_modify(cmd, flags, argv+1);