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
12 #include "ip_common.h" /* #include "libbb.h" is inside */
13 #include "common_bufsiz.h"
17 #include <linux/version.h>
18 /* RTA_TABLE is not a define, can't test with ifdef. */
19 /* As a proxy, test which kernels toolchain expects: */
20 #define HAVE_RTA_TABLE (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
23 #define RTAX_RTTVAR RTAX_HOPS
33 struct rtnl_handle *rth;
34 //int protocol, protocolmask; - write-only fields?!
36 //int type; - read-only
37 //int typemask; - unused
38 //int tos, tosmask; - unused
41 //int realm, realmmask; - unused
42 //inet_prefix rprefsrc; - read-only
49 typedef struct filter_t filter_t;
51 #define G_filter (*(filter_t*)bb_common_bufsiz1)
52 #define INIT_G() do { setup_common_bufsiz(); } while (0)
54 static int flush_update(void)
56 if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
57 bb_perror_msg("can't send flush request");
64 static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
65 struct nlmsghdr *n, void *arg UNUSED_PARAM)
67 struct rtmsg *r = NLMSG_DATA(n);
68 int len = n->nlmsg_len;
69 struct rtattr *tb[RTA_MAX+1];
75 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
76 fprintf(stderr, "Not a route: %08x %08x %08x\n",
77 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
80 if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
82 len -= NLMSG_LENGTH(sizeof(*r));
84 bb_error_msg_and_die("wrong nlmsg len %d", len);
86 //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
87 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
91 tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
96 if (r->rtm_family == AF_INET6)
98 else if (r->rtm_family == AF_INET)
101 if (r->rtm_family == AF_INET6) {
103 if (G_filter.tb < 0) {
104 if (!(r->rtm_flags & RTM_F_CLONED)) {
108 if (r->rtm_flags & RTM_F_CLONED) {
111 if (G_filter.tb == RT_TABLE_LOCAL) {
112 if (r->rtm_type != RTN_LOCAL) {
115 } else if (G_filter.tb == RT_TABLE_MAIN) {
116 if (r->rtm_type == RTN_LOCAL) {
125 if (G_filter.tb > 0 && G_filter.tb != tid) {
129 if ((G_filter.scope ^ r->rtm_scope) & G_filter.scopemask)
131 if (G_filter.rdst.family
132 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
136 if (G_filter.mdst.family
137 && (r->rtm_family != G_filter.mdst.family
138 || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
143 if (G_filter.rsrc.family
144 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
148 if (G_filter.msrc.family
149 && (r->rtm_family != G_filter.msrc.family
150 || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
156 memset(&src, 0, sizeof(src));
157 memset(&dst, 0, sizeof(dst));
160 src.bitlen = r->rtm_src_len;
161 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
162 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
165 dst.bitlen = r->rtm_dst_len;
166 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
167 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
170 if (G_filter.rdst.family
171 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
175 if (G_filter.mdst.family
176 && G_filter.mdst.bitlen >= 0
177 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
181 if (G_filter.rsrc.family
182 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
186 if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
187 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
191 if (G_filter.oif != 0) {
194 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
198 if (G_filter.flushb) {
201 /* We are creating route flush commands */
203 if (r->rtm_family == AF_INET6
204 && r->rtm_dst_len == 0
205 && r->rtm_type == RTN_UNREACHABLE
207 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
212 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
216 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
217 memcpy(fn, n, n->nlmsg_len);
218 fn->nlmsg_type = RTM_DELROUTE;
219 fn->nlmsg_flags = NLM_F_REQUEST;
220 fn->nlmsg_seq = ++G_filter.rth->seq;
221 G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
222 G_filter.flushed = 1;
226 /* We are printing routes */
228 if (n->nlmsg_type == RTM_DELROUTE) {
231 if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
232 printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
236 if (r->rtm_dst_len != host_len) {
238 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
242 printf("%s ", format_host(r->rtm_family,
243 RTA_PAYLOAD(tb[RTA_DST]),
244 RTA_DATA(tb[RTA_DST]))
247 } else if (r->rtm_dst_len) {
248 printf("0/%d ", r->rtm_dst_len);
253 if (r->rtm_src_len != host_len) {
254 printf("from %s/%u ",
255 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
259 printf("from %s ", format_host(r->rtm_family,
260 RTA_PAYLOAD(tb[RTA_SRC]),
261 RTA_DATA(tb[RTA_SRC]))
264 } else if (r->rtm_src_len) {
265 printf("from 0/%u ", r->rtm_src_len);
267 if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
268 printf("via %s ", format_host(r->rtm_family,
269 RTA_PAYLOAD(tb[RTA_GATEWAY]),
270 RTA_DATA(tb[RTA_GATEWAY]))
274 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
276 #if ENABLE_FEATURE_IP_RULE
277 if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
278 printf("table %s ", rtnl_rttable_n2a(tid));
281 /* Todo: parse & show "proto kernel" here */
282 if (!(r->rtm_flags & RTM_F_CLONED)) {
283 if ((r->rtm_scope != RT_SCOPE_UNIVERSE) && G_filter.scopemask != -1)
284 printf("scope %s ", rtnl_rtscope_n2a(r->rtm_scope));
287 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
288 /* Do not use format_host(). It is our local addr
289 and symbolic name will not be useful.
291 printf(" src %s ", rt_addr_n2a(r->rtm_family,
292 RTA_DATA(tb[RTA_PREFSRC])));
294 if (tb[RTA_PRIORITY]) {
295 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
297 if (r->rtm_flags & RTNH_F_DEAD) {
300 if (r->rtm_flags & RTNH_F_ONLINK) {
303 if (r->rtm_flags & RTNH_F_PERVASIVE) {
304 printf("pervasive ");
306 if (r->rtm_flags & RTM_F_NOTIFY) {
310 if (r->rtm_family == AF_INET6) {
311 struct rta_cacheinfo *ci = NULL;
312 if (tb[RTA_CACHEINFO]) {
313 ci = RTA_DATA(tb[RTA_CACHEINFO]);
315 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
316 if (r->rtm_flags & RTM_F_CLONED) {
317 printf("%c cache ", _SL_);
319 if (ci->rta_expires) {
320 printf(" expires %dsec", ci->rta_expires / get_hz());
322 if (ci->rta_error != 0) {
323 printf(" error %d", ci->rta_error);
326 if (ci->rta_error != 0)
327 printf(" error %d", ci->rta_error);
330 if (tb[RTA_IIF] && G_filter.iif == 0) {
331 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
337 static int str_is_lock(const char *str)
339 return strcmp(str, "lock") == 0;
342 /* Return value becomes exitcode. It's okay to not return at all */
343 static int iproute_modify(int cmd, unsigned flags, char **argv)
345 /* If you add stuff here, update iproute_full_usage */
346 static const char keywords[] ALIGN1 =
349 "scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
350 "dev\0""oif\0""to\0""metric\0""onlink\0";
351 #define keyword_via (keywords + sizeof("src"))
352 #define keyword_mtu (keyword_via + sizeof("via"))
353 #define keyword_advmss (keyword_mtu + sizeof("mtu"))
354 #define keyword_scope (keyword_advmss + sizeof("advmss"))
355 #define keyword_proto (keyword_scope + sizeof("scope"))
356 #define keyword_table (keyword_proto + sizeof("protocol"))
364 IF_FEATURE_IP_RULE(ARG_table,)
377 struct rtnl_handle rth;
384 struct rtattr * mxrta = (void*)mxbuf;
388 smalluint scope_ok = 0;
391 memset(&req, 0, sizeof(req));
393 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
394 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
395 req.n.nlmsg_type = cmd;
396 req.r.rtm_family = preferred_family;
397 if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
398 req.r.rtm_table = RT_TABLE_MAIN;
399 if (RT_SCOPE_NOWHERE != 0)
400 req.r.rtm_scope = RT_SCOPE_NOWHERE;
402 if (cmd != RTM_DELROUTE) {
403 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
404 if (RTPROT_BOOT != 0)
405 req.r.rtm_protocol = RTPROT_BOOT;
406 if (RTN_UNICAST != 0)
407 req.r.rtm_type = RTN_UNICAST;
410 mxrta->rta_type = RTA_METRICS;
411 mxrta->rta_len = RTA_LENGTH(0);
414 arg = index_in_substrings(keywords, *argv);
415 if (arg == ARG_src) {
418 get_addr(&addr, *argv, req.r.rtm_family);
419 if (req.r.rtm_family == AF_UNSPEC)
420 req.r.rtm_family = addr.family;
421 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
422 } else if (arg == ARG_via) {
426 get_addr(&addr, *argv, req.r.rtm_family);
427 if (req.r.rtm_family == AF_UNSPEC) {
428 req.r.rtm_family = addr.family;
430 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
431 } else if (arg == ARG_mtu) {
434 if (str_is_lock(*argv)) {
435 mxlock |= (1 << RTAX_MTU);
438 mtu = get_unsigned(*argv, keyword_mtu);
439 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
440 } else if (arg == ARG_advmss) {
443 if (str_is_lock(*argv)) {
444 mxlock |= (1 << RTAX_ADVMSS);
447 mss = get_unsigned(*argv, keyword_advmss);
448 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
449 } else if (arg == ARG_scope) {
452 if (rtnl_rtscope_a2n(&scope, *argv))
453 invarg_1_to_2(*argv, keyword_scope);
454 req.r.rtm_scope = scope;
456 } else if (arg == ARG_protocol) {
459 if (rtnl_rtprot_a2n(&prot, *argv))
460 invarg_1_to_2(*argv, keyword_proto);
461 req.r.rtm_protocol = prot;
463 #if ENABLE_FEATURE_IP_RULE
464 } else if (arg == ARG_table) {
467 if (rtnl_rttable_a2n(&tid, *argv))
468 invarg_1_to_2(*argv, keyword_table);
471 req.r.rtm_table = RT_TABLE_UNSPEC;
472 addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
475 req.r.rtm_table = tid;
477 } else if (arg == ARG_dev || arg == ARG_oif) {
480 } else if (arg == ARG_metric) {
481 //TODO: "metric", "priority" and "preference" are synonyms
484 metric = get_u32(*argv, "metric");
485 addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
486 } else if (arg == ARG_onlink) {
487 req.r.rtm_flags |= RTNH_F_ONLINK;
495 if ((**argv < '0' || **argv > '9')
496 && rtnl_rtntype_a2n(&type, *argv) == 0
499 req.r.rtm_type = type;
504 duparg2("to", *argv);
506 get_prefix(&dst, *argv, req.r.rtm_family);
507 if (req.r.rtm_family == AF_UNSPEC) {
508 req.r.rtm_family = dst.family;
510 req.r.rtm_dst_len = dst.bitlen;
513 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
516 /* Other keywords recognized by iproute2-3.19.0: */
518 } else if (strcmp(*argv, "from") == 0) {
521 get_prefix(&addr, *argv, req.r.rtm_family);
522 if (req.r.rtm_family == AF_UNSPEC)
523 req.r.rtm_family = addr.family;
525 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
526 req.r.rtm_src_len = addr.bitlen;
527 } else if (strcmp(*argv, "tos") == 0 ||
528 matches(*argv, "dsfield") == 0) {
531 if (rtnl_dsfield_a2n(&tos, *argv))
532 invarg("\"tos\" value is invalid\n", *argv);
534 } else if (strcmp(*argv, "hoplimit") == 0) {
537 if (strcmp(*argv, "lock") == 0) {
538 mxlock |= (1<<RTAX_HOPLIMIT);
541 if (get_unsigned(&hoplimit, *argv, 0))
542 invarg("\"hoplimit\" value is invalid\n", *argv);
543 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
544 } else if (matches(*argv, "reordering") == 0) {
547 if (strcmp(*argv, "lock") == 0) {
548 mxlock |= (1<<RTAX_REORDERING);
551 if (get_unsigned(&reord, *argv, 0))
552 invarg("\"reordering\" value is invalid\n", *argv);
553 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
554 } else if (strcmp(*argv, "rtt") == 0) {
557 if (strcmp(*argv, "lock") == 0) {
558 mxlock |= (1<<RTAX_RTT);
561 if (get_time_rtt(&rtt, *argv, &raw))
562 invarg("\"rtt\" value is invalid\n", *argv);
563 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
564 (raw) ? rtt : rtt * 8);
565 } else if (strcmp(*argv, "rto_min") == 0) {
568 mxlock |= (1<<RTAX_RTO_MIN);
569 if (get_time_rtt(&rto_min, *argv, &raw))
570 invarg("\"rto_min\" value is invalid\n",
572 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
574 } else if (matches(*argv, "window") == 0) {
577 if (strcmp(*argv, "lock") == 0) {
578 mxlock |= (1<<RTAX_WINDOW);
581 if (get_unsigned(&win, *argv, 0))
582 invarg("\"window\" value is invalid\n", *argv);
583 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
584 } else if (matches(*argv, "cwnd") == 0) {
587 if (strcmp(*argv, "lock") == 0) {
588 mxlock |= (1<<RTAX_CWND);
591 if (get_unsigned(&win, *argv, 0))
592 invarg("\"cwnd\" value is invalid\n", *argv);
593 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
594 } else if (matches(*argv, "initcwnd") == 0) {
597 if (strcmp(*argv, "lock") == 0) {
598 mxlock |= (1<<RTAX_INITCWND);
601 if (get_unsigned(&win, *argv, 0))
602 invarg("\"initcwnd\" value is invalid\n", *argv);
603 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
604 } else if (matches(*argv, "initrwnd") == 0) {
607 if (strcmp(*argv, "lock") == 0) {
608 mxlock |= (1<<RTAX_INITRWND);
611 if (get_unsigned(&win, *argv, 0))
612 invarg("\"initrwnd\" value is invalid\n", *argv);
613 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
614 } else if (matches(*argv, "features") == 0) {
615 unsigned int features = 0;
620 if (strcmp(*argv, "ecn") == 0)
621 features |= RTAX_FEATURE_ECN;
623 invarg("\"features\" value not valid\n", *argv);
627 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
628 } else if (matches(*argv, "quickack") == 0) {
631 if (get_unsigned(&quickack, *argv, 0))
632 invarg("\"quickack\" value is invalid\n", *argv);
633 if (quickack != 1 && quickack != 0)
634 invarg("\"quickack\" value should be 0 or 1\n", *argv);
635 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
636 } else if (matches(*argv, "rttvar") == 0) {
639 if (strcmp(*argv, "lock") == 0) {
640 mxlock |= (1<<RTAX_RTTVAR);
643 if (get_time_rtt(&win, *argv, &raw))
644 invarg("\"rttvar\" value is invalid\n", *argv);
645 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
646 (raw) ? win : win * 4);
647 } else if (matches(*argv, "ssthresh") == 0) {
650 if (strcmp(*argv, "lock") == 0) {
651 mxlock |= (1<<RTAX_SSTHRESH);
654 if (get_unsigned(&win, *argv, 0))
655 invarg("\"ssthresh\" value is invalid\n", *argv);
656 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
657 } else if (matches(*argv, "realms") == 0) {
660 if (get_rt_realms(&realm, *argv))
661 invarg("\"realm\" value is invalid\n", *argv);
662 addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
663 } else if (strcmp(*argv, "nexthop") == 0) {
679 idx = xll_name_to_index(d);
680 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
684 if (mxrta->rta_len > RTA_LENGTH(0)) {
686 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
688 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
692 if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
693 req.r.rtm_scope = RT_SCOPE_HOST;
695 if (req.r.rtm_type == RTN_BROADCAST
696 || req.r.rtm_type == RTN_MULTICAST
697 || req.r.rtm_type == RTN_ANYCAST
699 req.r.rtm_scope = RT_SCOPE_LINK;
701 else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
702 if (cmd == RTM_DELROUTE)
703 req.r.rtm_scope = RT_SCOPE_NOWHERE;
704 else if (!(ok & gw_ok))
705 req.r.rtm_scope = RT_SCOPE_LINK;
709 if (req.r.rtm_family == AF_UNSPEC) {
710 req.r.rtm_family = AF_INET;
713 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
720 static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
726 struct sockaddr_nl nladdr;
728 memset(&nladdr, 0, sizeof(nladdr));
729 memset(&req, 0, sizeof(req));
730 nladdr.nl_family = AF_NETLINK;
732 req.nlh.nlmsg_len = sizeof(req);
734 req.nlh.nlmsg_type = RTM_GETROUTE;
735 if (NLM_F_ROOT | NLM_F_REQUEST)
736 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
737 /*req.nlh.nlmsg_pid = 0; - memset did it already */
738 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
739 req.rtm.rtm_family = family;
741 req.rtm.rtm_flags = RTM_F_CLONED;
743 return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
746 static void iproute_flush_cache(void)
748 static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
749 int flush_fd = open_or_warn(fn, O_WRONLY);
755 if (write(flush_fd, "-1", 2) < 2) {
756 bb_perror_msg("can't flush routing cache");
762 static void iproute_reset_filter(void)
764 memset(&G_filter, 0, sizeof(G_filter));
765 G_filter.mdst.bitlen = -1;
766 G_filter.msrc.bitlen = -1;
769 /* Return value becomes exitcode. It's okay to not return at all */
770 static int iproute_list_or_flush(char **argv, int flush)
772 int do_ipv6 = preferred_family;
773 struct rtnl_handle rth;
776 static const char keywords[] ALIGN1 =
777 /* If you add stuff here, update iproute_full_usage */
778 /* "ip route list/flush" parameters: */
779 "protocol\0" "dev\0" "oif\0" "iif\0"
780 "via\0" "table\0" "cache\0"
781 "from\0" "to\0" "scope\0"
782 /* and possible further keywords */
790 KW_proto, KW_dev, KW_oif, KW_iif,
791 KW_via, KW_table, KW_cache,
792 KW_from, KW_to, KW_scope,
802 iproute_reset_filter();
803 G_filter.tb = RT_TABLE_MAIN;
806 bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
809 arg = index_in_substrings(keywords, *argv);
810 if (arg == KW_proto) {
813 //G_filter.protocolmask = -1;
814 if (rtnl_rtprot_a2n(&prot, *argv)) {
815 if (index_in_strings(keywords, *argv) != KW_all)
816 invarg_1_to_2(*argv, "protocol");
818 //G_filter.protocolmask = 0;
820 //G_filter.protocol = prot;
821 } else if (arg == KW_dev || arg == KW_oif) {
824 } else if (arg == KW_iif) {
827 } else if (arg == KW_via) {
829 get_prefix(&G_filter.rvia, *argv, do_ipv6);
830 } else if (arg == KW_table) { /* table all/cache/main */
832 parm = index_in_substrings(keywords, *argv);
833 if (parm == KW_cache)
835 else if (parm == KW_all)
837 else if (parm != KW_main) {
838 #if ENABLE_FEATURE_IP_RULE
840 if (rtnl_rttable_a2n(&tid, *argv))
841 invarg_1_to_2(*argv, "table");
844 invarg_1_to_2(*argv, "table");
847 } else if (arg == KW_cache) {
848 /* The command 'ip route flush cache' is used by OpenSWAN.
849 * Assuming it's a synonym for 'ip route flush table cache' */
851 } else if (arg == KW_scope) {
854 G_filter.scopemask = -1;
855 if (rtnl_rtscope_a2n(&scope, *argv)) {
856 if (strcmp(*argv, "all") != 0)
857 invarg_1_to_2(*argv, "scope");
858 scope = RT_SCOPE_NOWHERE;
859 G_filter.scopemask = 0;
861 G_filter.scope = scope;
862 } else if (arg == KW_from) {
864 parm = index_in_substrings(keywords, *argv);
865 if (parm == KW_root) {
867 get_prefix(&G_filter.rsrc, *argv, do_ipv6);
868 } else if (parm == KW_match) {
870 get_prefix(&G_filter.msrc, *argv, do_ipv6);
872 if (parm == KW_exact)
874 get_prefix(&G_filter.msrc, *argv, do_ipv6);
875 G_filter.rsrc = G_filter.msrc;
877 } else { /* "to" is the default parameter */
880 arg = index_in_substrings(keywords, *argv);
882 /* parm = arg; - would be more plausible, but we reuse 'arg' here */
883 if (arg == KW_root) {
885 get_prefix(&G_filter.rdst, *argv, do_ipv6);
886 } else if (arg == KW_match) {
888 get_prefix(&G_filter.mdst, *argv, do_ipv6);
889 } else { /* "to exact" is the default */
892 get_prefix(&G_filter.mdst, *argv, do_ipv6);
893 G_filter.rdst = G_filter.mdst;
899 if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
910 idx = xll_name_to_index(id);
914 idx = xll_name_to_index(od);
920 char flushb[4096-512];
922 if (G_filter.tb == -1) { /* "flush table cache" */
923 if (do_ipv6 != AF_INET6)
924 iproute_flush_cache();
925 if (do_ipv6 == AF_INET)
929 G_filter.flushb = flushb;
931 G_filter.flushe = sizeof(flushb);
935 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
936 G_filter.flushed = 0;
937 xrtnl_dump_filter(&rth, print_route, NULL);
938 if (G_filter.flushed == 0)
945 if (G_filter.tb != -1) {
946 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
947 } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
948 bb_perror_msg_and_die("can't send dump request");
950 xrtnl_dump_filter(&rth, print_route, NULL);
956 /* Return value becomes exitcode. It's okay to not return at all */
957 static int iproute_get(char **argv)
959 struct rtnl_handle rth;
969 static const char options[] ALIGN1 =
970 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
972 memset(&req, 0, sizeof(req));
974 iproute_reset_filter();
976 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
978 req.n.nlmsg_flags = NLM_F_REQUEST;
980 req.n.nlmsg_type = RTM_GETROUTE;
981 req.r.rtm_family = preferred_family;
982 /*req.r.rtm_table = 0; - memset did this already */
983 /*req.r.rtm_protocol = 0;*/
984 /*req.r.rtm_scope = 0;*/
985 /*req.r.rtm_type = 0;*/
986 /*req.r.rtm_src_len = 0;*/
987 /*req.r.rtm_dst_len = 0;*/
988 /*req.r.rtm_tos = 0;*/
991 switch (index_in_strings(options, *argv)) {
997 get_prefix(&addr, *argv, req.r.rtm_family);
998 if (req.r.rtm_family == AF_UNSPEC) {
999 req.r.rtm_family = addr.family;
1002 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
1004 req.r.rtm_src_len = addr.bitlen;
1016 case 4: /* notify */
1017 req.r.rtm_flags |= RTM_F_NOTIFY;
1019 case 5: /* connected */
1027 get_prefix(&addr, *argv, req.r.rtm_family);
1028 if (req.r.rtm_family == AF_UNSPEC) {
1029 req.r.rtm_family = addr.family;
1032 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
1034 req.r.rtm_dst_len = addr.bitlen;
1040 if (req.r.rtm_dst_len == 0) {
1041 bb_error_msg_and_die("need at least destination address");
1052 idx = xll_name_to_index(idev);
1053 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
1056 idx = xll_name_to_index(odev);
1057 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
1061 if (req.r.rtm_family == AF_UNSPEC) {
1062 req.r.rtm_family = AF_INET;
1065 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1069 if (connected && !from_ok) {
1070 struct rtmsg *r = NLMSG_DATA(&req.n);
1071 int len = req.n.nlmsg_len;
1072 struct rtattr * tb[RTA_MAX+1];
1074 print_route(NULL, &req.n, NULL);
1076 if (req.n.nlmsg_type != RTM_NEWROUTE) {
1077 bb_error_msg_and_die("not a route?");
1079 len -= NLMSG_LENGTH(sizeof(*r));
1081 bb_error_msg_and_die("wrong len %d", len);
1084 //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
1085 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
1087 if (tb[RTA_PREFSRC]) {
1088 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
1089 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
1090 } else if (!tb[RTA_SRC]) {
1091 bb_error_msg_and_die("can't connect the route");
1093 if (!odev && tb[RTA_OIF]) {
1094 tb[RTA_OIF]->rta_type = 0;
1096 if (tb[RTA_GATEWAY]) {
1097 tb[RTA_GATEWAY]->rta_type = 0;
1099 if (!idev && tb[RTA_IIF]) {
1100 tb[RTA_IIF]->rta_type = 0;
1102 req.n.nlmsg_flags = NLM_F_REQUEST;
1103 req.n.nlmsg_type = RTM_GETROUTE;
1105 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1109 print_route(NULL, &req.n, NULL);
1113 /* Return value becomes exitcode. It's okay to not return at all */
1114 int FAST_FUNC do_iproute(char **argv)
1116 static const char ip_route_commands[] ALIGN1 =
1117 "a\0""add\0""append\0""change\0""chg\0"
1118 "delete\0""get\0""list\0""show\0"
1119 "prepend\0""replace\0""test\0""flush\0"
1122 CMD_a = 0, CMD_add, CMD_append, CMD_change, CMD_chg,
1123 CMD_delete, CMD_get, CMD_list, CMD_show,
1124 CMD_prepend, CMD_replace, CMD_test, CMD_flush,
1128 int cmd = RTM_NEWROUTE;
1133 return iproute_list_or_flush(argv, 0);
1135 /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
1136 /* It probably means that it is using "first match" rule */
1137 command_num = index_in_substrings(ip_route_commands, *argv);
1139 switch (command_num) {
1142 flags = NLM_F_CREATE|NLM_F_EXCL;
1145 flags = NLM_F_CREATE|NLM_F_APPEND;
1149 flags = NLM_F_REPLACE;
1155 return iproute_get(argv + 1);
1158 return iproute_list_or_flush(argv + 1, 0);
1160 flags = NLM_F_CREATE;
1163 flags = NLM_F_CREATE|NLM_F_REPLACE;
1169 return iproute_list_or_flush(argv + 1, 1);
1171 invarg_1_to_2(*argv, applet_name);
1174 return iproute_modify(cmd, flags, argv + 1);