busybox/networking/libiproute/iprule.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * This program is free software; you can redistribute it and/or
   4 * modify it under the terms of the GNU General Public License
   5 * as published by the Free Software Foundation; either version
   6 * 2 of the License, or (at your option) any later version.
   7 *
   8 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   9 *
  10 * Changes:
  11 *
  12 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  13 * initially integrated into busybox by Bernhard Reutner-Fischer
  14 */
  15#include <netinet/in.h>
  16#include <netinet/ip.h>
  17#include <arpa/inet.h>
  18
  19/* from <linux/fib_rules.h>: */
  20#define FRA_SUPPRESS_IFGROUP   13
  21#define FRA_SUPPRESS_PREFIXLEN 14
  22
  23#include "ip_common.h"  /* #include "libbb.h" is inside */
  24#include "rt_names.h"
  25#include "utils.h"
  26
  27#include <linux/version.h>
  28/* RTA_TABLE is not a define, can't test with ifdef. */
  29/* As a proxy, test which kernels toolchain expects: */
  30#define HAVE_RTA_TABLE (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
  31
  32/* If you add stuff here, update iprule_full_usage */
  33static const char keywords[] ALIGN1 =
  34        "from\0""to\0""preference\0""order\0""priority\0"
  35        "tos\0""fwmark\0""realms\0""table\0""lookup\0"
  36        "suppress_prefixlength\0""suppress_ifgroup\0"
  37        "dev\0""iif\0""nat\0""map-to\0""type\0""help\0"
  38        ;
  39#define keyword_preference            (keywords           + sizeof("from") + sizeof("to"))
  40#define keyword_fwmark                (keyword_preference + sizeof("preference") + sizeof("order") + sizeof("priority") + sizeof("tos"))
  41#define keyword_realms                (keyword_fwmark     + sizeof("fwmark"))
  42#define keyword_suppress_prefixlength (keyword_realms     + sizeof("realms") + sizeof("table") + sizeof("lookup"))
  43#define keyword_suppress_ifgroup      (keyword_suppress_prefixlength + sizeof("suppress_prefixlength"))
  44enum {
  45        ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
  46        ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup,
  47        ARG_suppress_prefixlength, ARG_suppress_ifgroup,
  48        ARG_dev, ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help,
  49};
  50
  51static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
  52                                        struct nlmsghdr *n, void *arg UNUSED_PARAM)
  53{
  54        struct rtmsg *r = NLMSG_DATA(n);
  55        int len = n->nlmsg_len;
  56        int host_len = -1;
  57        struct rtattr * tb[RTA_MAX+1];
  58
  59        if (n->nlmsg_type != RTM_NEWRULE)
  60                return 0;
  61
  62        len -= NLMSG_LENGTH(sizeof(*r));
  63        if (len < 0)
  64                return -1;
  65
  66        //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
  67        parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  68
  69        if (r->rtm_family == AF_INET)
  70                host_len = 32;
  71        else if (r->rtm_family == AF_INET6)
  72                host_len = 128;
  73/*      else if (r->rtm_family == AF_DECnet)
  74                host_len = 16;
  75        else if (r->rtm_family == AF_IPX)
  76                host_len = 80;
  77*/
  78        printf("%u:\t", tb[RTA_PRIORITY] ?
  79                                        *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])
  80                                        : 0);
  81        printf("from ");
  82        if (tb[RTA_SRC]) {
  83                if (r->rtm_src_len != host_len) {
  84                        printf("%s/%u",
  85                                rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
  86                                r->rtm_src_len
  87                        );
  88                } else {
  89                        fputs(format_host(r->rtm_family,
  90                                                RTA_PAYLOAD(tb[RTA_SRC]),
  91                                                RTA_DATA(tb[RTA_SRC])),
  92                                stdout
  93                        );
  94                }
  95        } else if (r->rtm_src_len) {
  96                printf("0/%d", r->rtm_src_len);
  97        } else {
  98                printf("all");
  99        }
 100        bb_putchar(' ');
 101
 102        if (tb[RTA_DST]) {
 103                if (r->rtm_dst_len != host_len) {
 104                        printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
 105                                                         RTA_DATA(tb[RTA_DST])),
 106                                r->rtm_dst_len
 107                                );
 108                } else {
 109                        printf("to %s ", format_host(r->rtm_family,
 110                                                       RTA_PAYLOAD(tb[RTA_DST]),
 111                                                       RTA_DATA(tb[RTA_DST])));
 112                }
 113        } else if (r->rtm_dst_len) {
 114                printf("to 0/%d ", r->rtm_dst_len);
 115        }
 116
 117        if (r->rtm_tos) {
 118                printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos));
 119        }
 120        if (tb[RTA_PROTOINFO]) {
 121                printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
 122        }
 123
 124        if (tb[RTA_IIF]) {
 125                printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
 126        }
 127
 128#if HAVE_RTA_TABLE
 129        if (tb[RTA_TABLE])
 130                printf("lookup %s ", rtnl_rttable_n2a(*(uint32_t*)RTA_DATA(tb[RTA_TABLE])));
 131        else
 132#endif
 133        if (r->rtm_table)
 134                printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table));
 135
 136        if (tb[FRA_SUPPRESS_PREFIXLEN]) {
 137                int pl = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_PREFIXLEN]);
 138                if (pl != -1)
 139                        printf("%s %d ", keyword_suppress_prefixlength, pl);
 140        }
 141        if (tb[FRA_SUPPRESS_IFGROUP]) {
 142                int grp = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_IFGROUP]);
 143                if (grp != -1)
 144                        printf("%s %d ", keyword_suppress_ifgroup, grp);
 145        }
 146
 147        if (tb[RTA_FLOW]) {
 148                uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
 149                uint32_t from = to>>16;
 150                to &= 0xFFFF;
 151                if (from) {
 152                        printf("realms %s/",
 153                                rtnl_rtrealm_n2a(from));
 154                }
 155                printf("%s ",
 156                        rtnl_rtrealm_n2a(to));
 157        }
 158
 159        if (r->rtm_type == RTN_NAT) {
 160                if (tb[RTA_GATEWAY]) {
 161                        printf("map-to %s ",
 162                                format_host(r->rtm_family,
 163                                            RTA_PAYLOAD(tb[RTA_GATEWAY]),
 164                                            RTA_DATA(tb[RTA_GATEWAY]))
 165                        );
 166                } else
 167                        printf("masquerade");
 168        } else if (r->rtm_type != RTN_UNICAST)
 169                fputs(rtnl_rtntype_n2a(r->rtm_type), stdout);
 170
 171        bb_putchar('\n');
 172        /*fflush_all();*/
 173        return 0;
 174}
 175
 176/* Return value becomes exitcode. It's okay to not return at all */
 177static int iprule_list(char **argv)
 178{
 179        struct rtnl_handle rth;
 180        int af = preferred_family;
 181
 182        if (af == AF_UNSPEC)
 183                af = AF_INET;
 184
 185        if (*argv) {
 186                //bb_error_msg("\"rule show\" needs no arguments");
 187                bb_warn_ignoring_args(*argv);
 188                return -1;
 189        }
 190
 191        xrtnl_open(&rth);
 192
 193        xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
 194        xrtnl_dump_filter(&rth, print_rule, NULL);
 195
 196        return 0;
 197}
 198
 199/* Return value becomes exitcode. It's okay to not return at all */
 200static int iprule_modify(int cmd, char **argv)
 201{
 202        bool table_ok = 0;
 203        struct rtnl_handle rth;
 204        struct {
 205                struct nlmsghdr n;
 206                struct rtmsg    r;
 207                char            buf[1024];
 208        } req;
 209        smalluint key;
 210
 211        memset(&req, 0, sizeof(req));
 212
 213        req.n.nlmsg_type = cmd;
 214        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 215        req.n.nlmsg_flags = NLM_F_REQUEST;
 216        req.r.rtm_family = preferred_family;
 217        req.r.rtm_protocol = RTPROT_BOOT;
 218        if (RT_SCOPE_UNIVERSE != 0)
 219                req.r.rtm_scope = RT_SCOPE_UNIVERSE;
 220        /*req.r.rtm_table = 0; - already is */
 221        if (RTN_UNSPEC != 0)
 222                req.r.rtm_type = RTN_UNSPEC;
 223
 224        if (cmd == RTM_NEWRULE) {
 225                req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
 226                req.r.rtm_type = RTN_UNICAST;
 227        }
 228
 229        while (*argv) {
 230                key = index_in_substrings(keywords, *argv) + 1;
 231                if (key == 0) /* no match found in keywords array, bail out. */
 232                        invarg_1_to_2(*argv, applet_name);
 233                if (key == ARG_from) {
 234                        inet_prefix dst;
 235                        NEXT_ARG();
 236                        get_prefix(&dst, *argv, req.r.rtm_family);
 237                        req.r.rtm_src_len = dst.bitlen;
 238                        addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
 239                } else if (key == ARG_to) {
 240                        inet_prefix dst;
 241                        NEXT_ARG();
 242                        get_prefix(&dst, *argv, req.r.rtm_family);
 243                        req.r.rtm_dst_len = dst.bitlen;
 244                        addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
 245                } else if (key == ARG_preference ||
 246                           key == ARG_order ||
 247                           key == ARG_priority
 248                ) {
 249                        uint32_t pref;
 250                        NEXT_ARG();
 251                        pref = get_u32(*argv, keyword_preference);
 252                        addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
 253                } else if (key == ARG_tos) {
 254                        uint32_t tos;
 255                        NEXT_ARG();
 256                        if (rtnl_dsfield_a2n(&tos, *argv))
 257                                invarg_1_to_2(*argv, "TOS");
 258                        req.r.rtm_tos = tos;
 259                } else if (key == ARG_fwmark) {
 260                        uint32_t fwmark;
 261                        NEXT_ARG();
 262                        fwmark = get_u32(*argv, keyword_fwmark);
 263                        addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
 264                } else if (key == ARG_realms) {
 265                        uint32_t realm;
 266                        NEXT_ARG();
 267                        if (get_rt_realms(&realm, *argv))
 268                                invarg_1_to_2(*argv, keyword_realms);
 269                        addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
 270                } else if (key == ARG_table ||
 271                           key == ARG_lookup
 272                ) {
 273                        uint32_t tid;
 274                        NEXT_ARG();
 275                        if (rtnl_rttable_a2n(&tid, *argv))
 276                                invarg_1_to_2(*argv, "table ID");
 277
 278#if HAVE_RTA_TABLE
 279                        if (tid > 255) {
 280                                req.r.rtm_table = RT_TABLE_UNSPEC;
 281                                addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
 282                        } else
 283#endif
 284                                req.r.rtm_table = tid;
 285
 286                        table_ok = 1;
 287                } else if (key == ARG_suppress_prefixlength) {
 288                        int prefix_length;
 289                        NEXT_ARG();
 290                        prefix_length = get_u32(*argv, keyword_suppress_prefixlength);
 291                        addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, prefix_length);
 292                } else if (key == ARG_suppress_ifgroup) {
 293                        int grp;
 294                        NEXT_ARG();
 295                        grp = get_u32(*argv, keyword_suppress_ifgroup);
 296                        addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, grp);
 297                } else if (key == ARG_dev ||
 298                           key == ARG_iif
 299                ) {
 300                        NEXT_ARG();
 301                        addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
 302                } else if (key == ARG_nat ||
 303                           key == ARG_map_to
 304                ) {
 305                        NEXT_ARG();
 306                        addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
 307                        req.r.rtm_type = RTN_NAT;
 308                } else {
 309                        int type;
 310
 311                        if (key == ARG_type) {
 312                                NEXT_ARG();
 313                        }
 314                        if (key == ARG_help)
 315                                bb_show_usage();
 316                        if (rtnl_rtntype_a2n(&type, *argv))
 317                                invarg_1_to_2(*argv, "type");
 318                        req.r.rtm_type = type;
 319                }
 320                argv++;
 321        }
 322
 323        if (req.r.rtm_family == AF_UNSPEC)
 324                req.r.rtm_family = AF_INET;
 325
 326        if (!table_ok && cmd == RTM_NEWRULE)
 327                req.r.rtm_table = RT_TABLE_MAIN;
 328
 329        xrtnl_open(&rth);
 330
 331        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
 332                return 2;
 333
 334        return 0;
 335}
 336
 337/* Return value becomes exitcode. It's okay to not return at all */
 338int FAST_FUNC do_iprule(char **argv)
 339{
 340        static const char ip_rule_commands[] ALIGN1 =
 341                "add\0""delete\0""list\0""show\0";
 342        if (*argv) {
 343                int cmd = index_in_substrings(ip_rule_commands, *argv);
 344                if (cmd < 0)
 345                        invarg_1_to_2(*argv, applet_name);
 346                argv++;
 347                if (cmd < 2)
 348                        return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
 349        }
 350        return iprule_list(argv);
 351}
 352