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