busybox/networking/libiproute/iprule.c
Lukasz Nowak b42107f215 libiproute: handle table ids larger than 255
Linux kernel, starting from 2.6.19 allows ip table ids to have 32-bit values.
In order to preserve compatibility, the old 8-bit field: rtm_table is still
in use when table id is lower than 256.

Add support for the 32-bit table id (RTA_TABLE attribute) in:
- ip route print
- ip route modify
- ip rule print
- ip rule modify

Add printing of table ids to ip route.

Changes are compatible with the mainline iproute2 utilities.

These changes are required for compatibility with ConnMan, which by default
uses table ids greater than 255.

function                                             old     new   delta
print_route                                         1588    1637     +49
do_iproute                                          2187    2222     +35
do_iprule                                            955     987     +32
print_rule                                           617     630     +13
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/0 up/down: 129/0)             Total: 129 bytes

Signed-off-by: Lukasz Nowak <lnowak@tycoint.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2016-12-18 18:56:49 +01:00

323 lines
8.1 KiB
C

/* vi: set sw=4 ts=4: */
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
* Changes:
*
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
* initially integrated into busybox by Bernhard Reutner-Fischer
*/
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "ip_common.h" /* #include "libbb.h" is inside */
#include "rt_names.h"
#include "utils.h"
/*
static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
exit(-1);
}
*/
static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
struct nlmsghdr *n, void *arg UNUSED_PARAM)
{
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
int host_len = -1;
struct rtattr * tb[RTA_MAX+1];
if (n->nlmsg_type != RTM_NEWRULE)
return 0;
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0)
return -1;
memset(tb, 0, sizeof(tb));
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
if (r->rtm_family == AF_INET)
host_len = 32;
else if (r->rtm_family == AF_INET6)
host_len = 128;
/* else if (r->rtm_family == AF_DECnet)
host_len = 16;
else if (r->rtm_family == AF_IPX)
host_len = 80;
*/
printf("%u:\t", tb[RTA_PRIORITY] ?
*(unsigned*)RTA_DATA(tb[RTA_PRIORITY])
: 0);
printf("from ");
if (tb[RTA_SRC]) {
if (r->rtm_src_len != host_len) {
printf("%s/%u",
rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
r->rtm_src_len
);
} else {
fputs(format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_SRC]),
RTA_DATA(tb[RTA_SRC])),
stdout
);
}
} else if (r->rtm_src_len) {
printf("0/%d", r->rtm_src_len);
} else {
printf("all");
}
bb_putchar(' ');
if (tb[RTA_DST]) {
if (r->rtm_dst_len != host_len) {
printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
RTA_DATA(tb[RTA_DST])),
r->rtm_dst_len
);
} else {
printf("to %s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_DST]),
RTA_DATA(tb[RTA_DST])));
}
} else if (r->rtm_dst_len) {
printf("to 0/%d ", r->rtm_dst_len);
}
if (r->rtm_tos) {
printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos));
}
if (tb[RTA_PROTOINFO]) {
printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
}
if (tb[RTA_IIF]) {
printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
}
if (tb[RTA_TABLE])
printf("lookup %s ", rtnl_rttable_n2a(*(uint32_t*)RTA_DATA(tb[RTA_TABLE])));
else if (r->rtm_table)
printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table));
if (tb[RTA_FLOW]) {
uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
uint32_t from = to>>16;
to &= 0xFFFF;
if (from) {
printf("realms %s/",
rtnl_rtrealm_n2a(from));
}
printf("%s ",
rtnl_rtrealm_n2a(to));
}
if (r->rtm_type == RTN_NAT) {
if (tb[RTA_GATEWAY]) {
printf("map-to %s ",
format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_GATEWAY]),
RTA_DATA(tb[RTA_GATEWAY]))
);
} else
printf("masquerade");
} else if (r->rtm_type != RTN_UNICAST)
fputs(rtnl_rtntype_n2a(r->rtm_type), stdout);
bb_putchar('\n');
/*fflush_all();*/
return 0;
}
/* Return value becomes exitcode. It's okay to not return at all */
static int iprule_list(char **argv)
{
struct rtnl_handle rth;
int af = preferred_family;
if (af == AF_UNSPEC)
af = AF_INET;
if (*argv) {
//bb_error_msg("\"rule show\" needs no arguments");
bb_warn_ignoring_args(*argv);
return -1;
}
xrtnl_open(&rth);
xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
xrtnl_dump_filter(&rth, print_rule, NULL);
return 0;
}
/* Return value becomes exitcode. It's okay to not return at all */
static int iprule_modify(int cmd, char **argv)
{
static const char keywords[] ALIGN1 =
"from\0""to\0""preference\0""order\0""priority\0"
"tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
"iif\0""nat\0""map-to\0""type\0""help\0";
enum {
ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
};
bool table_ok = 0;
struct rtnl_handle rth;
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
smalluint key;
memset(&req, 0, sizeof(req));
req.n.nlmsg_type = cmd;
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.r.rtm_family = preferred_family;
req.r.rtm_protocol = RTPROT_BOOT;
if (RT_SCOPE_UNIVERSE != 0)
req.r.rtm_scope = RT_SCOPE_UNIVERSE;
/*req.r.rtm_table = 0; - already is */
if (RTN_UNSPEC != 0)
req.r.rtm_type = RTN_UNSPEC;
if (cmd == RTM_NEWRULE) {
req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
req.r.rtm_type = RTN_UNICAST;
}
while (*argv) {
key = index_in_substrings(keywords, *argv) + 1;
if (key == 0) /* no match found in keywords array, bail out. */
invarg_1_to_2(*argv, applet_name);
if (key == ARG_from) {
inet_prefix dst;
NEXT_ARG();
get_prefix(&dst, *argv, req.r.rtm_family);
req.r.rtm_src_len = dst.bitlen;
addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
} else if (key == ARG_to) {
inet_prefix dst;
NEXT_ARG();
get_prefix(&dst, *argv, req.r.rtm_family);
req.r.rtm_dst_len = dst.bitlen;
addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
} else if (key == ARG_preference ||
key == ARG_order ||
key == ARG_priority
) {
uint32_t pref;
NEXT_ARG();
pref = get_u32(*argv, "preference");
addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
} else if (key == ARG_tos) {
uint32_t tos;
NEXT_ARG();
if (rtnl_dsfield_a2n(&tos, *argv))
invarg_1_to_2(*argv, "TOS");
req.r.rtm_tos = tos;
} else if (key == ARG_fwmark) {
uint32_t fwmark;
NEXT_ARG();
fwmark = get_u32(*argv, "fwmark");
addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
} else if (key == ARG_realms) {
uint32_t realm;
NEXT_ARG();
if (get_rt_realms(&realm, *argv))
invarg_1_to_2(*argv, "realms");
addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
} else if (key == ARG_table ||
key == ARG_lookup
) {
uint32_t tid;
NEXT_ARG();
if (rtnl_rttable_a2n(&tid, *argv))
invarg_1_to_2(*argv, "table ID");
if (tid < 256)
req.r.rtm_table = tid;
else {
req.r.rtm_table = RT_TABLE_UNSPEC;
addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
}
table_ok = 1;
} else if (key == ARG_dev ||
key == ARG_iif
) {
NEXT_ARG();
addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
} else if (key == ARG_nat ||
key == ARG_map_to
) {
NEXT_ARG();
addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
req.r.rtm_type = RTN_NAT;
} else {
int type;
if (key == ARG_type) {
NEXT_ARG();
}
if (key == ARG_help)
bb_show_usage();
if (rtnl_rtntype_a2n(&type, *argv))
invarg_1_to_2(*argv, "type");
req.r.rtm_type = type;
}
argv++;
}
if (req.r.rtm_family == AF_UNSPEC)
req.r.rtm_family = AF_INET;
if (!table_ok && cmd == RTM_NEWRULE)
req.r.rtm_table = RT_TABLE_MAIN;
xrtnl_open(&rth);
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
return 2;
return 0;
}
/* Return value becomes exitcode. It's okay to not return at all */
int FAST_FUNC do_iprule(char **argv)
{
static const char ip_rule_commands[] ALIGN1 =
"add\0""delete\0""list\0""show\0";
if (*argv) {
int cmd = index_in_substrings(ip_rule_commands, *argv);
if (cmd < 0)
invarg_1_to_2(*argv, applet_name);
argv++;
if (cmd < 2)
return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
}
return iprule_list(argv);
}