busybox/networking/libiproute/iproute.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   4 *
   5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 *
   7 * Changes:
   8 *
   9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  10 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
  11 */
  12
  13#include "ip_common.h"  /* #include "libbb.h" is inside */
  14#include "common_bufsiz.h"
  15#include "rt_names.h"
  16#include "utils.h"
  17
  18#ifndef RTAX_RTTVAR
  19#define RTAX_RTTVAR RTAX_HOPS
  20#endif
  21
  22
  23struct filter_t {
  24        int tb;
  25        smallint flushed;
  26        char *flushb;
  27        int flushp;
  28        int flushe;
  29        struct rtnl_handle *rth;
  30        //int protocol, protocolmask; - write-only fields?!
  31        int scope, scopemask;
  32        //int type; - read-only
  33        //int typemask; - unused
  34        //int tos, tosmask; - unused
  35        int iif;
  36        int oif;
  37        //int realm, realmmask; - unused
  38        //inet_prefix rprefsrc; - read-only
  39        inet_prefix rvia;
  40        inet_prefix rdst;
  41        inet_prefix mdst;
  42        inet_prefix rsrc;
  43        inet_prefix msrc;
  44} FIX_ALIASING;
  45typedef struct filter_t filter_t;
  46
  47#define G_filter (*(filter_t*)bb_common_bufsiz1)
  48#define INIT_G() do { setup_common_bufsiz(); } while (0)
  49
  50static int flush_update(void)
  51{
  52        if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
  53                bb_perror_msg("can't send flush request");
  54                return -1;
  55        }
  56        G_filter.flushp = 0;
  57        return 0;
  58}
  59
  60static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
  61                struct nlmsghdr *n, void *arg UNUSED_PARAM)
  62{
  63        struct rtmsg *r = NLMSG_DATA(n);
  64        int len = n->nlmsg_len;
  65        struct rtattr *tb[RTA_MAX+1];
  66        inet_prefix dst;
  67        inet_prefix src;
  68        int host_len = -1;
  69        uint32_t tid;
  70
  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);
  74                return 0;
  75        }
  76        if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
  77                return 0;
  78        len -= NLMSG_LENGTH(sizeof(*r));
  79        if (len < 0)
  80                bb_error_msg_and_die("wrong nlmsg len %d", len);
  81
  82        memset(tb, 0, sizeof(tb));
  83        parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  84
  85        if (tb[RTA_TABLE])
  86                tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
  87        else
  88                tid = r->rtm_table;
  89
  90        if (r->rtm_family == AF_INET6)
  91                host_len = 128;
  92        else if (r->rtm_family == AF_INET)
  93                host_len = 32;
  94
  95        if (r->rtm_family == AF_INET6) {
  96                if (G_filter.tb) {
  97                        if (G_filter.tb < 0) {
  98                                if (!(r->rtm_flags & RTM_F_CLONED)) {
  99                                        return 0;
 100                                }
 101                        } else {
 102                                if (r->rtm_flags & RTM_F_CLONED) {
 103                                        return 0;
 104                                }
 105                                if (G_filter.tb == RT_TABLE_LOCAL) {
 106                                        if (r->rtm_type != RTN_LOCAL) {
 107                                                return 0;
 108                                        }
 109                                } else if (G_filter.tb == RT_TABLE_MAIN) {
 110                                        if (r->rtm_type == RTN_LOCAL) {
 111                                                return 0;
 112                                        }
 113                                } else {
 114                                        return 0;
 115                                }
 116                        }
 117                }
 118        } else {
 119                if (G_filter.tb > 0 && G_filter.tb != tid) {
 120                        return 0;
 121                }
 122        }
 123        if ((G_filter.scope ^ r->rtm_scope) & G_filter.scopemask)
 124                return 0;
 125        if (G_filter.rdst.family
 126         && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
 127        ) {
 128                return 0;
 129        }
 130        if (G_filter.mdst.family
 131         && (r->rtm_family != G_filter.mdst.family
 132            || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
 133            )
 134        ) {
 135                return 0;
 136        }
 137        if (G_filter.rsrc.family
 138         && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
 139        ) {
 140                return 0;
 141        }
 142        if (G_filter.msrc.family
 143         && (r->rtm_family != G_filter.msrc.family
 144            || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
 145            )
 146        ) {
 147                return 0;
 148        }
 149
 150        memset(&src, 0, sizeof(src));
 151        memset(&dst, 0, sizeof(dst));
 152
 153        if (tb[RTA_SRC]) {
 154                src.bitlen = r->rtm_src_len;
 155                src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
 156                memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
 157        }
 158        if (tb[RTA_DST]) {
 159                dst.bitlen = r->rtm_dst_len;
 160                dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
 161                memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
 162        }
 163
 164        if (G_filter.rdst.family
 165         && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
 166        ) {
 167                return 0;
 168        }
 169        if (G_filter.mdst.family
 170         && G_filter.mdst.bitlen >= 0
 171         && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
 172        ) {
 173                return 0;
 174        }
 175        if (G_filter.rsrc.family
 176         && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
 177        ) {
 178                return 0;
 179        }
 180        if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
 181         && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
 182        ) {
 183                return 0;
 184        }
 185        if (G_filter.oif != 0) {
 186                if (!tb[RTA_OIF])
 187                        return 0;
 188                if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
 189                        return 0;
 190        }
 191
 192        if (G_filter.flushb) {
 193                struct nlmsghdr *fn;
 194
 195                /* We are creating route flush commands */
 196
 197                if (r->rtm_family == AF_INET6
 198                 && r->rtm_dst_len == 0
 199                 && r->rtm_type == RTN_UNREACHABLE
 200                 && tb[RTA_PRIORITY]
 201                 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
 202                ) {
 203                        return 0;
 204                }
 205
 206                if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
 207                        if (flush_update())
 208                                xfunc_die();
 209                }
 210                fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
 211                memcpy(fn, n, n->nlmsg_len);
 212                fn->nlmsg_type = RTM_DELROUTE;
 213                fn->nlmsg_flags = NLM_F_REQUEST;
 214                fn->nlmsg_seq = ++G_filter.rth->seq;
 215                G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
 216                G_filter.flushed = 1;
 217                return 0;
 218        }
 219
 220        /* We are printing routes */
 221
 222        if (n->nlmsg_type == RTM_DELROUTE) {
 223                printf("Deleted ");
 224        }
 225        if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
 226                printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
 227        }
 228
 229        if (tb[RTA_DST]) {
 230                if (r->rtm_dst_len != host_len) {
 231                        printf("%s/%u ",
 232                                rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
 233                                r->rtm_dst_len
 234                        );
 235                } else {
 236                        printf("%s ", format_host(r->rtm_family,
 237                                                RTA_PAYLOAD(tb[RTA_DST]),
 238                                                RTA_DATA(tb[RTA_DST]))
 239                        );
 240                }
 241        } else if (r->rtm_dst_len) {
 242                printf("0/%d ", r->rtm_dst_len);
 243        } else {
 244                printf("default ");
 245        }
 246        if (tb[RTA_SRC]) {
 247                if (r->rtm_src_len != host_len) {
 248                        printf("from %s/%u ",
 249                                rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
 250                                r->rtm_src_len
 251                        );
 252                } else {
 253                        printf("from %s ", format_host(r->rtm_family,
 254                                                RTA_PAYLOAD(tb[RTA_SRC]),
 255                                                RTA_DATA(tb[RTA_SRC]))
 256                        );
 257                }
 258        } else if (r->rtm_src_len) {
 259                printf("from 0/%u ", r->rtm_src_len);
 260        }
 261        if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
 262                printf("via %s ", format_host(r->rtm_family,
 263                                        RTA_PAYLOAD(tb[RTA_GATEWAY]),
 264                                        RTA_DATA(tb[RTA_GATEWAY]))
 265                );
 266        }
 267        if (tb[RTA_OIF]) {
 268                printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
 269        }
 270#if ENABLE_FEATURE_IP_RULE
 271        if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
 272                printf("table %s ", rtnl_rttable_n2a(tid));
 273#endif
 274
 275        /* Todo: parse & show "proto kernel" here */
 276        if (!(r->rtm_flags & RTM_F_CLONED)) {
 277                if ((r->rtm_scope != RT_SCOPE_UNIVERSE) && G_filter.scopemask != -1)
 278                        printf("scope %s ", rtnl_rtscope_n2a(r->rtm_scope));
 279        }
 280
 281        if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
 282                /* Do not use format_host(). It is our local addr
 283                   and symbolic name will not be useful.
 284                 */
 285                printf(" src %s ", rt_addr_n2a(r->rtm_family,
 286                                        RTA_DATA(tb[RTA_PREFSRC])));
 287        }
 288        if (tb[RTA_PRIORITY]) {
 289                printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
 290        }
 291        if (r->rtm_flags & RTNH_F_DEAD) {
 292                printf("dead ");
 293        }
 294        if (r->rtm_flags & RTNH_F_ONLINK) {
 295                printf("onlink ");
 296        }
 297        if (r->rtm_flags & RTNH_F_PERVASIVE) {
 298                printf("pervasive ");
 299        }
 300        if (r->rtm_flags & RTM_F_NOTIFY) {
 301                printf("notify ");
 302        }
 303
 304        if (r->rtm_family == AF_INET6) {
 305                struct rta_cacheinfo *ci = NULL;
 306                if (tb[RTA_CACHEINFO]) {
 307                        ci = RTA_DATA(tb[RTA_CACHEINFO]);
 308                }
 309                if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
 310                        if (r->rtm_flags & RTM_F_CLONED) {
 311                                printf("%c    cache ", _SL_);
 312                        }
 313                        if (ci->rta_expires) {
 314                                printf(" expires %dsec", ci->rta_expires / get_hz());
 315                        }
 316                        if (ci->rta_error != 0) {
 317                                printf(" error %d", ci->rta_error);
 318                        }
 319                } else if (ci) {
 320                        if (ci->rta_error != 0)
 321                                printf(" error %d", ci->rta_error);
 322                }
 323        }
 324        if (tb[RTA_IIF] && G_filter.iif == 0) {
 325                printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
 326        }
 327        bb_putchar('\n');
 328        return 0;
 329}
 330
 331static int str_is_lock(const char *str)
 332{
 333        return strcmp(str, "lock") == 0;
 334}
 335
 336/* Return value becomes exitcode. It's okay to not return at all */
 337static int iproute_modify(int cmd, unsigned flags, char **argv)
 338{
 339        /* If you add stuff here, update iproute_full_usage */
 340        static const char keywords[] ALIGN1 =
 341                "src\0""via\0"
 342                "mtu\0""advmss\0"
 343                "scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
 344                "dev\0""oif\0""to\0""metric\0""onlink\0";
 345#define keyword_via    (keywords       + sizeof("src"))
 346#define keyword_mtu    (keyword_via    + sizeof("via"))
 347#define keyword_advmss (keyword_mtu    + sizeof("mtu"))
 348#define keyword_scope  (keyword_advmss + sizeof("advmss"))
 349#define keyword_proto  (keyword_scope  + sizeof("scope"))
 350#define keyword_table  (keyword_proto  + sizeof("protocol"))
 351        enum {
 352                ARG_src,
 353                ARG_via,
 354                ARG_mtu,
 355                ARG_advmss,
 356                ARG_scope,
 357                ARG_protocol,
 358IF_FEATURE_IP_RULE(ARG_table,)
 359                ARG_dev,
 360                ARG_oif,
 361                ARG_to,
 362                ARG_metric,
 363                ARG_onlink,
 364        };
 365        enum {
 366                gw_ok = 1 << 0,
 367                dst_ok = 1 << 1,
 368                proto_ok = 1 << 2,
 369                type_ok = 1 << 3
 370        };
 371        struct rtnl_handle rth;
 372        struct {
 373                struct nlmsghdr n;
 374                struct rtmsg    r;
 375                char            buf[1024];
 376        } req;
 377        char mxbuf[256];
 378        struct rtattr * mxrta = (void*)mxbuf;
 379        unsigned mxlock = 0;
 380        char *d = NULL;
 381        smalluint ok = 0;
 382        smalluint scope_ok = 0;
 383        int arg;
 384
 385        memset(&req, 0, sizeof(req));
 386
 387        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 388        req.n.nlmsg_flags = NLM_F_REQUEST | flags;
 389        req.n.nlmsg_type = cmd;
 390        req.r.rtm_family = preferred_family;
 391        if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
 392                req.r.rtm_table = RT_TABLE_MAIN;
 393        if (RT_SCOPE_NOWHERE != 0)
 394                req.r.rtm_scope = RT_SCOPE_NOWHERE;
 395
 396        if (cmd != RTM_DELROUTE) {
 397                req.r.rtm_scope = RT_SCOPE_UNIVERSE;
 398                if (RTPROT_BOOT != 0)
 399                        req.r.rtm_protocol = RTPROT_BOOT;
 400                if (RTN_UNICAST != 0)
 401                        req.r.rtm_type = RTN_UNICAST;
 402        }
 403
 404        mxrta->rta_type = RTA_METRICS;
 405        mxrta->rta_len = RTA_LENGTH(0);
 406
 407        while (*argv) {
 408                arg = index_in_substrings(keywords, *argv);
 409                if (arg == ARG_src) {
 410                        inet_prefix addr;
 411                        NEXT_ARG();
 412                        get_addr(&addr, *argv, req.r.rtm_family);
 413                        if (req.r.rtm_family == AF_UNSPEC)
 414                                req.r.rtm_family = addr.family;
 415                        addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
 416                } else if (arg == ARG_via) {
 417                        inet_prefix addr;
 418                        ok |= gw_ok;
 419                        NEXT_ARG();
 420                        get_addr(&addr, *argv, req.r.rtm_family);
 421                        if (req.r.rtm_family == AF_UNSPEC) {
 422                                req.r.rtm_family = addr.family;
 423                        }
 424                        addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
 425                } else if (arg == ARG_mtu) {
 426                        unsigned mtu;
 427                        NEXT_ARG();
 428                        if (str_is_lock(*argv)) {
 429                                mxlock |= (1 << RTAX_MTU);
 430                                NEXT_ARG();
 431                        }
 432                        mtu = get_unsigned(*argv, keyword_mtu);
 433                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
 434                } else if (arg == ARG_advmss) {
 435                        unsigned mss;
 436                        NEXT_ARG();
 437                        if (str_is_lock(*argv)) {
 438                                mxlock |= (1 << RTAX_ADVMSS);
 439                                NEXT_ARG();
 440                        }
 441                        mss = get_unsigned(*argv, keyword_advmss);
 442                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
 443                } else if (arg == ARG_scope) {
 444                        uint32_t scope;
 445                        NEXT_ARG();
 446                        if (rtnl_rtscope_a2n(&scope, *argv))
 447                                invarg_1_to_2(*argv, keyword_scope);
 448                        req.r.rtm_scope = scope;
 449                        scope_ok = 1;
 450                } else if (arg == ARG_protocol) {
 451                        uint32_t prot;
 452                        NEXT_ARG();
 453                        if (rtnl_rtprot_a2n(&prot, *argv))
 454                                invarg_1_to_2(*argv, keyword_proto);
 455                        req.r.rtm_protocol = prot;
 456                        ok |= proto_ok;
 457#if ENABLE_FEATURE_IP_RULE
 458                } else if (arg == ARG_table) {
 459                        uint32_t tid;
 460                        NEXT_ARG();
 461                        if (rtnl_rttable_a2n(&tid, *argv))
 462                                invarg_1_to_2(*argv, keyword_table);
 463                        if (tid < 256)
 464                                req.r.rtm_table = tid;
 465                        else {
 466                                req.r.rtm_table = RT_TABLE_UNSPEC;
 467                                addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
 468                        }
 469#endif
 470                } else if (arg == ARG_dev || arg == ARG_oif) {
 471                        NEXT_ARG();
 472                        d = *argv;
 473                } else if (arg == ARG_metric) {
 474//TODO: "metric", "priority" and "preference" are synonyms
 475                        uint32_t metric;
 476                        NEXT_ARG();
 477                        metric = get_u32(*argv, "metric");
 478                        addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
 479                } else if (arg == ARG_onlink) {
 480                        req.r.rtm_flags |= RTNH_F_ONLINK;
 481                } else {
 482                        int type;
 483                        inet_prefix dst;
 484
 485                        if (arg == ARG_to) {
 486                                NEXT_ARG();
 487                        }
 488                        if ((**argv < '0' || **argv > '9')
 489                         && rtnl_rtntype_a2n(&type, *argv) == 0
 490                        ) {
 491                                NEXT_ARG();
 492                                req.r.rtm_type = type;
 493                                ok |= type_ok;
 494                        }
 495
 496                        if (ok & dst_ok) {
 497                                duparg2("to", *argv);
 498                        }
 499                        get_prefix(&dst, *argv, req.r.rtm_family);
 500                        if (req.r.rtm_family == AF_UNSPEC) {
 501                                req.r.rtm_family = dst.family;
 502                        }
 503                        req.r.rtm_dst_len = dst.bitlen;
 504                        ok |= dst_ok;
 505                        if (dst.bytelen) {
 506                                addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
 507                        }
 508                }
 509/* Other keywords recognized by iproute2-3.19.0: */
 510#if 0
 511                } else if (strcmp(*argv, "from") == 0) {
 512                        inet_prefix addr;
 513                        NEXT_ARG();
 514                        get_prefix(&addr, *argv, req.r.rtm_family);
 515                        if (req.r.rtm_family == AF_UNSPEC)
 516                                req.r.rtm_family = addr.family;
 517                        if (addr.bytelen)
 518                                addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
 519                        req.r.rtm_src_len = addr.bitlen;
 520                } else if (strcmp(*argv, "tos") == 0 ||
 521                           matches(*argv, "dsfield") == 0) {
 522                        __u32 tos;
 523                        NEXT_ARG();
 524                        if (rtnl_dsfield_a2n(&tos, *argv))
 525                                invarg("\"tos\" value is invalid\n", *argv);
 526                        req.r.rtm_tos = tos;
 527                } else if (strcmp(*argv, "hoplimit") == 0) {
 528                        unsigned hoplimit;
 529                        NEXT_ARG();
 530                        if (strcmp(*argv, "lock") == 0) {
 531                                mxlock |= (1<<RTAX_HOPLIMIT);
 532                                NEXT_ARG();
 533                        }
 534                        if (get_unsigned(&hoplimit, *argv, 0))
 535                                invarg("\"hoplimit\" value is invalid\n", *argv);
 536                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
 537                } else if (matches(*argv, "reordering") == 0) {
 538                        unsigned reord;
 539                        NEXT_ARG();
 540                        if (strcmp(*argv, "lock") == 0) {
 541                                mxlock |= (1<<RTAX_REORDERING);
 542                                NEXT_ARG();
 543                        }
 544                        if (get_unsigned(&reord, *argv, 0))
 545                                invarg("\"reordering\" value is invalid\n", *argv);
 546                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
 547                } else if (strcmp(*argv, "rtt") == 0) {
 548                        unsigned rtt;
 549                        NEXT_ARG();
 550                        if (strcmp(*argv, "lock") == 0) {
 551                                mxlock |= (1<<RTAX_RTT);
 552                                NEXT_ARG();
 553                        }
 554                        if (get_time_rtt(&rtt, *argv, &raw))
 555                                invarg("\"rtt\" value is invalid\n", *argv);
 556                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
 557                                (raw) ? rtt : rtt * 8);
 558                } else if (strcmp(*argv, "rto_min") == 0) {
 559                        unsigned rto_min;
 560                        NEXT_ARG();
 561                        mxlock |= (1<<RTAX_RTO_MIN);
 562                        if (get_time_rtt(&rto_min, *argv, &raw))
 563                                invarg("\"rto_min\" value is invalid\n",
 564                                       *argv);
 565                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
 566                                      rto_min);
 567                } else if (matches(*argv, "window") == 0) {
 568                        unsigned win;
 569                        NEXT_ARG();
 570                        if (strcmp(*argv, "lock") == 0) {
 571                                mxlock |= (1<<RTAX_WINDOW);
 572                                NEXT_ARG();
 573                        }
 574                        if (get_unsigned(&win, *argv, 0))
 575                                invarg("\"window\" value is invalid\n", *argv);
 576                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
 577                } else if (matches(*argv, "cwnd") == 0) {
 578                        unsigned win;
 579                        NEXT_ARG();
 580                        if (strcmp(*argv, "lock") == 0) {
 581                                mxlock |= (1<<RTAX_CWND);
 582                                NEXT_ARG();
 583                        }
 584                        if (get_unsigned(&win, *argv, 0))
 585                                invarg("\"cwnd\" value is invalid\n", *argv);
 586                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
 587                } else if (matches(*argv, "initcwnd") == 0) {
 588                        unsigned win;
 589                        NEXT_ARG();
 590                        if (strcmp(*argv, "lock") == 0) {
 591                                mxlock |= (1<<RTAX_INITCWND);
 592                                NEXT_ARG();
 593                        }
 594                        if (get_unsigned(&win, *argv, 0))
 595                                invarg("\"initcwnd\" value is invalid\n", *argv);
 596                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
 597                } else if (matches(*argv, "initrwnd") == 0) {
 598                        unsigned win;
 599                        NEXT_ARG();
 600                        if (strcmp(*argv, "lock") == 0) {
 601                                mxlock |= (1<<RTAX_INITRWND);
 602                                NEXT_ARG();
 603                        }
 604                        if (get_unsigned(&win, *argv, 0))
 605                                invarg("\"initrwnd\" value is invalid\n", *argv);
 606                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
 607                } else if (matches(*argv, "features") == 0) {
 608                        unsigned int features = 0;
 609
 610                        while (argc > 0) {
 611                                NEXT_ARG();
 612
 613                                if (strcmp(*argv, "ecn") == 0)
 614                                        features |= RTAX_FEATURE_ECN;
 615                                else
 616                                        invarg("\"features\" value not valid\n", *argv);
 617                                break;
 618                        }
 619
 620                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
 621                } else if (matches(*argv, "quickack") == 0) {
 622                        unsigned quickack;
 623                        NEXT_ARG();
 624                        if (get_unsigned(&quickack, *argv, 0))
 625                                invarg("\"quickack\" value is invalid\n", *argv);
 626                        if (quickack != 1 && quickack != 0)
 627                                invarg("\"quickack\" value should be 0 or 1\n", *argv);
 628                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
 629                } else if (matches(*argv, "rttvar") == 0) {
 630                        unsigned win;
 631                        NEXT_ARG();
 632                        if (strcmp(*argv, "lock") == 0) {
 633                                mxlock |= (1<<RTAX_RTTVAR);
 634                                NEXT_ARG();
 635                        }
 636                        if (get_time_rtt(&win, *argv, &raw))
 637                                invarg("\"rttvar\" value is invalid\n", *argv);
 638                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
 639                                (raw) ? win : win * 4);
 640                } else if (matches(*argv, "ssthresh") == 0) {
 641                        unsigned win;
 642                        NEXT_ARG();
 643                        if (strcmp(*argv, "lock") == 0) {
 644                                mxlock |= (1<<RTAX_SSTHRESH);
 645                                NEXT_ARG();
 646                        }
 647                        if (get_unsigned(&win, *argv, 0))
 648                                invarg("\"ssthresh\" value is invalid\n", *argv);
 649                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
 650                } else if (matches(*argv, "realms") == 0) {
 651                        __u32 realm;
 652                        NEXT_ARG();
 653                        if (get_rt_realms(&realm, *argv))
 654                                invarg("\"realm\" value is invalid\n", *argv);
 655                        addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
 656                } else if (strcmp(*argv, "nexthop") == 0) {
 657                        nhs_ok = 1;
 658                        break;
 659                }
 660#endif
 661                argv++;
 662        }
 663
 664        xrtnl_open(&rth);
 665
 666        if (d)  {
 667                int idx;
 668
 669                ll_init_map(&rth);
 670
 671                if (d) {
 672                        idx = xll_name_to_index(d);
 673                        addattr32(&req.n, sizeof(req), RTA_OIF, idx);
 674                }
 675        }
 676
 677        if (mxrta->rta_len > RTA_LENGTH(0)) {
 678                if (mxlock) {
 679                        rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
 680                }
 681                addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
 682        }
 683
 684        if (!scope_ok) {
 685                if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
 686                        req.r.rtm_scope = RT_SCOPE_HOST;
 687                else
 688                if (req.r.rtm_type == RTN_BROADCAST
 689                 || req.r.rtm_type == RTN_MULTICAST
 690                 || req.r.rtm_type == RTN_ANYCAST
 691                ) {
 692                        req.r.rtm_scope = RT_SCOPE_LINK;
 693                }
 694                else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
 695                        if (cmd == RTM_DELROUTE)
 696                                req.r.rtm_scope = RT_SCOPE_NOWHERE;
 697                        else if (!(ok & gw_ok))
 698                                req.r.rtm_scope = RT_SCOPE_LINK;
 699                }
 700        }
 701
 702        if (req.r.rtm_family == AF_UNSPEC) {
 703                req.r.rtm_family = AF_INET;
 704        }
 705
 706        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
 707                return 2;
 708        }
 709
 710        return 0;
 711}
 712
 713static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
 714{
 715        struct {
 716                struct nlmsghdr nlh;
 717                struct rtmsg rtm;
 718        } req;
 719        struct sockaddr_nl nladdr;
 720
 721        memset(&nladdr, 0, sizeof(nladdr));
 722        memset(&req, 0, sizeof(req));
 723        nladdr.nl_family = AF_NETLINK;
 724
 725        req.nlh.nlmsg_len = sizeof(req);
 726        if (RTM_GETROUTE)
 727                req.nlh.nlmsg_type = RTM_GETROUTE;
 728        if (NLM_F_ROOT | NLM_F_REQUEST)
 729                req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
 730        /*req.nlh.nlmsg_pid = 0; - memset did it already */
 731        req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
 732        req.rtm.rtm_family = family;
 733        if (RTM_F_CLONED)
 734                req.rtm.rtm_flags = RTM_F_CLONED;
 735
 736        return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
 737}
 738
 739static void iproute_flush_cache(void)
 740{
 741        static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
 742        int flush_fd = open_or_warn(fn, O_WRONLY);
 743
 744        if (flush_fd < 0) {
 745                return;
 746        }
 747
 748        if (write(flush_fd, "-1", 2) < 2) {
 749                bb_perror_msg("can't flush routing cache");
 750                return;
 751        }
 752        close(flush_fd);
 753}
 754
 755static void iproute_reset_filter(void)
 756{
 757        memset(&G_filter, 0, sizeof(G_filter));
 758        G_filter.mdst.bitlen = -1;
 759        G_filter.msrc.bitlen = -1;
 760}
 761
 762/* Return value becomes exitcode. It's okay to not return at all */
 763static int iproute_list_or_flush(char **argv, int flush)
 764{
 765        int do_ipv6 = preferred_family;
 766        struct rtnl_handle rth;
 767        char *id = NULL;
 768        char *od = NULL;
 769        static const char keywords[] ALIGN1 =
 770                /* If you add stuff here, update iproute_full_usage */
 771                /* "ip route list/flush" parameters: */
 772                "protocol\0" "dev\0"   "oif\0"   "iif\0"
 773                "via\0"      "table\0" "cache\0"
 774                "from\0"     "to\0"    "scope\0"
 775                /* and possible further keywords */
 776                "all\0"
 777                "root\0"
 778                "match\0"
 779                "exact\0"
 780                "main\0"
 781                ;
 782        enum {
 783                KW_proto, KW_dev,   KW_oif,  KW_iif,
 784                KW_via,   KW_table, KW_cache,
 785                KW_from,  KW_to,    KW_scope,
 786                /* */
 787                KW_all,
 788                KW_root,
 789                KW_match,
 790                KW_exact,
 791                KW_main,
 792        };
 793        int arg, parm;
 794
 795        iproute_reset_filter();
 796        G_filter.tb = RT_TABLE_MAIN;
 797
 798        if (flush && !*argv)
 799                bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
 800
 801        while (*argv) {
 802                arg = index_in_substrings(keywords, *argv);
 803                if (arg == KW_proto) {
 804                        uint32_t prot = 0;
 805                        NEXT_ARG();
 806                        //G_filter.protocolmask = -1;
 807                        if (rtnl_rtprot_a2n(&prot, *argv)) {
 808                                if (index_in_strings(keywords, *argv) != KW_all)
 809                                        invarg_1_to_2(*argv, "protocol");
 810                                prot = 0;
 811                                //G_filter.protocolmask = 0;
 812                        }
 813                        //G_filter.protocol = prot;
 814                } else if (arg == KW_dev || arg == KW_oif) {
 815                        NEXT_ARG();
 816                        od = *argv;
 817                } else if (arg == KW_iif) {
 818                        NEXT_ARG();
 819                        id = *argv;
 820                } else if (arg == KW_via) {
 821                        NEXT_ARG();
 822                        get_prefix(&G_filter.rvia, *argv, do_ipv6);
 823                } else if (arg == KW_table) { /* table all/cache/main */
 824                        NEXT_ARG();
 825                        parm = index_in_substrings(keywords, *argv);
 826                        if (parm == KW_cache)
 827                                G_filter.tb = -1;
 828                        else if (parm == KW_all)
 829                                G_filter.tb = 0;
 830                        else if (parm != KW_main) {
 831#if ENABLE_FEATURE_IP_RULE
 832                                uint32_t tid;
 833                                if (rtnl_rttable_a2n(&tid, *argv))
 834                                        invarg_1_to_2(*argv, "table");
 835                                G_filter.tb = tid;
 836#else
 837                                invarg_1_to_2(*argv, "table");
 838#endif
 839                        }
 840                } else if (arg == KW_cache) {
 841                        /* The command 'ip route flush cache' is used by OpenSWAN.
 842                         * Assuming it's a synonym for 'ip route flush table cache' */
 843                        G_filter.tb = -1;
 844                } else if (arg == KW_scope) {
 845                        uint32_t scope;
 846                        NEXT_ARG();
 847                        G_filter.scopemask = -1;
 848                        if (rtnl_rtscope_a2n(&scope, *argv)) {
 849                                if (strcmp(*argv, "all") != 0)
 850                                        invarg_1_to_2(*argv, "scope");
 851                                scope = RT_SCOPE_NOWHERE;
 852                                G_filter.scopemask = 0;
 853                        }
 854                        G_filter.scope = scope;
 855                } else if (arg == KW_from) {
 856                        NEXT_ARG();
 857                        parm = index_in_substrings(keywords, *argv);
 858                        if (parm == KW_root) {
 859                                NEXT_ARG();
 860                                get_prefix(&G_filter.rsrc, *argv, do_ipv6);
 861                        } else if (parm == KW_match) {
 862                                NEXT_ARG();
 863                                get_prefix(&G_filter.msrc, *argv, do_ipv6);
 864                        } else {
 865                                if (parm == KW_exact)
 866                                        NEXT_ARG();
 867                                get_prefix(&G_filter.msrc, *argv, do_ipv6);
 868                                G_filter.rsrc = G_filter.msrc;
 869                        }
 870                } else { /* "to" is the default parameter */
 871                        if (arg == KW_to) {
 872                                NEXT_ARG();
 873                                arg = index_in_substrings(keywords, *argv);
 874                        }
 875                        /* parm = arg; - would be more plausible, but we reuse 'arg' here */
 876                        if (arg == KW_root) {
 877                                NEXT_ARG();
 878                                get_prefix(&G_filter.rdst, *argv, do_ipv6);
 879                        } else if (arg == KW_match) {
 880                                NEXT_ARG();
 881                                get_prefix(&G_filter.mdst, *argv, do_ipv6);
 882                        } else { /* "to exact" is the default */
 883                                if (arg == KW_exact)
 884                                        NEXT_ARG();
 885                                get_prefix(&G_filter.mdst, *argv, do_ipv6);
 886                                G_filter.rdst = G_filter.mdst;
 887                        }
 888                }
 889                argv++;
 890        }
 891
 892        if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
 893                do_ipv6 = AF_INET;
 894        }
 895
 896        xrtnl_open(&rth);
 897        ll_init_map(&rth);
 898
 899        if (id || od)  {
 900                int idx;
 901
 902                if (id) {
 903                        idx = xll_name_to_index(id);
 904                        G_filter.iif = idx;
 905                }
 906                if (od) {
 907                        idx = xll_name_to_index(od);
 908                        G_filter.oif = idx;
 909                }
 910        }
 911
 912        if (flush) {
 913                char flushb[4096-512];
 914
 915                if (G_filter.tb == -1) { /* "flush table cache" */
 916                        if (do_ipv6 != AF_INET6)
 917                                iproute_flush_cache();
 918                        if (do_ipv6 == AF_INET)
 919                                return 0;
 920                }
 921
 922                G_filter.flushb = flushb;
 923                G_filter.flushp = 0;
 924                G_filter.flushe = sizeof(flushb);
 925                G_filter.rth = &rth;
 926
 927                for (;;) {
 928                        xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
 929                        G_filter.flushed = 0;
 930                        xrtnl_dump_filter(&rth, print_route, NULL);
 931                        if (G_filter.flushed == 0)
 932                                return 0;
 933                        if (flush_update())
 934                                return 1;
 935                }
 936        }
 937
 938        if (G_filter.tb != -1) {
 939                xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
 940        } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
 941                bb_perror_msg_and_die("can't send dump request");
 942        }
 943        xrtnl_dump_filter(&rth, print_route, NULL);
 944
 945        return 0;
 946}
 947
 948
 949/* Return value becomes exitcode. It's okay to not return at all */
 950static int iproute_get(char **argv)
 951{
 952        struct rtnl_handle rth;
 953        struct {
 954                struct nlmsghdr n;
 955                struct rtmsg    r;
 956                char            buf[1024];
 957        } req;
 958        char *idev = NULL;
 959        char *odev = NULL;
 960        bool connected = 0;
 961        bool from_ok = 0;
 962        static const char options[] ALIGN1 =
 963                "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
 964
 965        memset(&req, 0, sizeof(req));
 966
 967        iproute_reset_filter();
 968
 969        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 970        if (NLM_F_REQUEST)
 971                req.n.nlmsg_flags = NLM_F_REQUEST;
 972        if (RTM_GETROUTE)
 973                req.n.nlmsg_type = RTM_GETROUTE;
 974        req.r.rtm_family = preferred_family;
 975        /*req.r.rtm_table = 0; - memset did this already */
 976        /*req.r.rtm_protocol = 0;*/
 977        /*req.r.rtm_scope = 0;*/
 978        /*req.r.rtm_type = 0;*/
 979        /*req.r.rtm_src_len = 0;*/
 980        /*req.r.rtm_dst_len = 0;*/
 981        /*req.r.rtm_tos = 0;*/
 982
 983        while (*argv) {
 984                switch (index_in_strings(options, *argv)) {
 985                        case 0: /* from */
 986                        {
 987                                inet_prefix addr;
 988                                NEXT_ARG();
 989                                from_ok = 1;
 990                                get_prefix(&addr, *argv, req.r.rtm_family);
 991                                if (req.r.rtm_family == AF_UNSPEC) {
 992                                        req.r.rtm_family = addr.family;
 993                                }
 994                                if (addr.bytelen) {
 995                                        addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
 996                                }
 997                                req.r.rtm_src_len = addr.bitlen;
 998                                break;
 999                        }
1000                        case 1: /* iif */
1001                                NEXT_ARG();
1002                                idev = *argv;
1003                                break;
1004                        case 2: /* oif */
1005                        case 3: /* dev */
1006                                NEXT_ARG();
1007                                odev = *argv;
1008                                break;
1009                        case 4: /* notify */
1010                                req.r.rtm_flags |= RTM_F_NOTIFY;
1011                                break;
1012                        case 5: /* connected */
1013                                connected = 1;
1014                                break;
1015                        case 6: /* to */
1016                                NEXT_ARG();
1017                        default:
1018                        {
1019                                inet_prefix addr;
1020                                get_prefix(&addr, *argv, req.r.rtm_family);
1021                                if (req.r.rtm_family == AF_UNSPEC) {
1022                                        req.r.rtm_family = addr.family;
1023                                }
1024                                if (addr.bytelen) {
1025                                        addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
1026                                }
1027                                req.r.rtm_dst_len = addr.bitlen;
1028                        }
1029                }
1030                argv++;
1031        }
1032
1033        if (req.r.rtm_dst_len == 0) {
1034                bb_error_msg_and_die("need at least destination address");
1035        }
1036
1037        xrtnl_open(&rth);
1038
1039        ll_init_map(&rth);
1040
1041        if (idev || odev)  {
1042                int idx;
1043
1044                if (idev) {
1045                        idx = xll_name_to_index(idev);
1046                        addattr32(&req.n, sizeof(req), RTA_IIF, idx);
1047                }
1048                if (odev) {
1049                        idx = xll_name_to_index(odev);
1050                        addattr32(&req.n, sizeof(req), RTA_OIF, idx);
1051                }
1052        }
1053
1054        if (req.r.rtm_family == AF_UNSPEC) {
1055                req.r.rtm_family = AF_INET;
1056        }
1057
1058        if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1059                return 2;
1060        }
1061
1062        if (connected && !from_ok) {
1063                struct rtmsg *r = NLMSG_DATA(&req.n);
1064                int len = req.n.nlmsg_len;
1065                struct rtattr * tb[RTA_MAX+1];
1066
1067                print_route(NULL, &req.n, NULL);
1068
1069                if (req.n.nlmsg_type != RTM_NEWROUTE) {
1070                        bb_error_msg_and_die("not a route?");
1071                }
1072                len -= NLMSG_LENGTH(sizeof(*r));
1073                if (len < 0) {
1074                        bb_error_msg_and_die("wrong len %d", len);
1075                }
1076
1077                memset(tb, 0, sizeof(tb));
1078                parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
1079
1080                if (tb[RTA_PREFSRC]) {
1081                        tb[RTA_PREFSRC]->rta_type = RTA_SRC;
1082                        r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
1083                } else if (!tb[RTA_SRC]) {
1084                        bb_error_msg_and_die("can't connect the route");
1085                }
1086                if (!odev && tb[RTA_OIF]) {
1087                        tb[RTA_OIF]->rta_type = 0;
1088                }
1089                if (tb[RTA_GATEWAY]) {
1090                        tb[RTA_GATEWAY]->rta_type = 0;
1091                }
1092                if (!idev && tb[RTA_IIF]) {
1093                        tb[RTA_IIF]->rta_type = 0;
1094                }
1095                req.n.nlmsg_flags = NLM_F_REQUEST;
1096                req.n.nlmsg_type = RTM_GETROUTE;
1097
1098                if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1099                        return 2;
1100                }
1101        }
1102        print_route(NULL, &req.n, NULL);
1103        return 0;
1104}
1105
1106/* Return value becomes exitcode. It's okay to not return at all */
1107int FAST_FUNC do_iproute(char **argv)
1108{
1109        static const char ip_route_commands[] ALIGN1 =
1110                "a\0""add\0""append\0""change\0""chg\0"
1111                "delete\0""get\0""list\0""show\0"
1112                "prepend\0""replace\0""test\0""flush\0"
1113        ;
1114        enum {
1115                CMD_a = 0, CMD_add, CMD_append, CMD_change, CMD_chg,
1116                CMD_delete, CMD_get, CMD_list, CMD_show,
1117                CMD_prepend, CMD_replace, CMD_test, CMD_flush,
1118        };
1119        int command_num;
1120        unsigned flags = 0;
1121        int cmd = RTM_NEWROUTE;
1122
1123        INIT_G();
1124
1125        if (!*argv)
1126                return iproute_list_or_flush(argv, 0);
1127
1128        /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
1129        /* It probably means that it is using "first match" rule */
1130        command_num = index_in_substrings(ip_route_commands, *argv);
1131
1132        switch (command_num) {
1133                case CMD_a:
1134                case CMD_add:
1135                        flags = NLM_F_CREATE|NLM_F_EXCL;
1136                        break;
1137                case CMD_append:
1138                        flags = NLM_F_CREATE|NLM_F_APPEND;
1139                        break;
1140                case CMD_change:
1141                case CMD_chg:
1142                        flags = NLM_F_REPLACE;
1143                        break;
1144                case CMD_delete:
1145                        cmd = RTM_DELROUTE;
1146                        break;
1147                case CMD_get:
1148                        return iproute_get(argv + 1);
1149                case CMD_list:
1150                case CMD_show:
1151                        return iproute_list_or_flush(argv + 1, 0);
1152                case CMD_prepend:
1153                        flags = NLM_F_CREATE;
1154                        break;
1155                case CMD_replace:
1156                        flags = NLM_F_CREATE|NLM_F_REPLACE;
1157                        break;
1158                case CMD_test:
1159                        flags = NLM_F_EXCL;
1160                        break;
1161                case CMD_flush:
1162                        return iproute_list_or_flush(argv + 1, 1);
1163                default:
1164                        invarg_1_to_2(*argv, applet_name);
1165        }
1166
1167        return iproute_modify(cmd, flags, argv + 1);
1168}
1169