b42107f215
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>
323 lines
8.1 KiB
C
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);
|
|
}
|