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