From 921f5df25fdb9d89bd02ac0030ebb6ca7e003f0d Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Tue, 21 Nov 2006 15:36:08 +0000 Subject: - add 'ip rule' support. First take.. text data bss dec hex filename 2999 0 0 2999 bb7 networking/libiproute/iprule.o --- networking/Config.in | 12 ++ networking/Kbuild | 1 + networking/ip.c | 20 +-- networking/iprule.c | 25 +++ networking/libiproute/Kbuild | 8 + networking/libiproute/ipaddress.c | 2 +- networking/libiproute/iprule.c | 331 +++++++++++++++++++++++++++++++++++++ networking/libiproute/libnetlink.c | 8 +- networking/libiproute/libnetlink.h | 14 +- networking/libiproute/rt_names.c | 81 ++++++++- networking/libiproute/rt_names.h | 3 + 11 files changed, 480 insertions(+), 25 deletions(-) create mode 100644 networking/iprule.c create mode 100644 networking/libiproute/iprule.c (limited to 'networking') diff --git a/networking/Config.in b/networking/Config.in index 34847a1..ba41ab1 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -364,6 +364,13 @@ config FEATURE_IP_TUNNEL help Add support for tunneling commands to "ip". +config FEATURE_IP_RULE + bool "ip rule" + default n + depends on IP + help + Add support for rule commands to "ip". + config FEATURE_IP_SHORT_FORMS bool "Support short forms of ip commands." default n @@ -398,6 +405,11 @@ config IPTUNNEL default y depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL +config IPRULE + bool + default y + depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE + config IPCALC bool "ipcalc" default n diff --git a/networking/Kbuild b/networking/Kbuild index 0664759..a9a51fc 100644 --- a/networking/Kbuild +++ b/networking/Kbuild @@ -21,6 +21,7 @@ lib-$(CONFIG_IPCALC) += ipcalc.o lib-$(CONFIG_IPADDR) += ipaddr.o lib-$(CONFIG_IPLINK) += iplink.o lib-$(CONFIG_IPROUTE) += iproute.o +lib-$(CONFIG_IPRULE) += iprule.o lib-$(CONFIG_IPTUNNEL) += iptunnel.o lib-$(CONFIG_NAMEIF) += nameif.o lib-$(CONFIG_NC) += nc.o diff --git a/networking/ip.c b/networking/ip.c index 6363155..4c8b89e 100644 --- a/networking/ip.c +++ b/networking/ip.c @@ -24,26 +24,22 @@ int ip_main(int argc, char **argv) ip_parse_common_args(&argc, &argv); if (argc > 1) { -#ifdef CONFIG_FEATURE_IP_ADDRESS - if (matches(argv[1], "address") == 0) { + if (ENABLE_FEATURE_IP_ADDRESS && matches(argv[1], "address") == 0) { ret = do_ipaddr(argc-2, argv+2); } -#endif -#ifdef CONFIG_FEATURE_IP_ROUTE - if (matches(argv[1], "route") == 0) { + if (ENABLE_FEATURE_IP_ROUTE && matches(argv[1], "route") == 0) { ret = do_iproute(argc-2, argv+2); } -#endif -#ifdef CONFIG_FEATURE_IP_LINK - if (matches(argv[1], "link") == 0) { + if (ENABLE_FEATURE_IP_LINK && matches(argv[1], "link") == 0) { ret = do_iplink(argc-2, argv+2); } -#endif -#ifdef CONFIG_FEATURE_IP_TUNNEL - if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) { + if (ENABLE_FEATURE_IP_TUNNEL && + (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0)) { ret = do_iptunnel(argc-2, argv+2); } -#endif + if (ENABLE_FEATURE_IP_RULE && matches(argv[1], "rule") == 0) { + ret = do_iprule(argc-2, argv+2); + } } if (ret) { bb_show_usage(); diff --git a/networking/iprule.c b/networking/iprule.c new file mode 100644 index 0000000..43eaea6 --- /dev/null +++ b/networking/iprule.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * ip.c "ip" utility frontend. + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "libiproute/utils.h" +#include "libiproute/ip_common.h" + +#include "busybox.h" + +int iprule_main(int argc, char **argv) +{ + ip_parse_common_args(&argc, &argv); + + return do_iprule(argc-1, argv+1); +} diff --git a/networking/libiproute/Kbuild b/networking/libiproute/Kbuild index 476374e..8383630 100644 --- a/networking/libiproute/Kbuild +++ b/networking/libiproute/Kbuild @@ -12,6 +12,7 @@ lib-$(CONFIG_IP) += \ iplink.o \ iproute.o \ iptunnel.o \ + iprule.o \ libnetlink.o \ ll_addr.o \ ll_map.o \ @@ -56,3 +57,10 @@ lib-$(CONFIG_IPTUNNEL) += \ iptunnel.o \ rt_names.o \ utils.o + +lib-$(CONFIG_IPRULE) += \ + ip_parse_common_args.o \ + iprule.o \ + rt_names.o \ + utils.o + diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c index fc6cf7b..42cf629 100644 --- a/networking/libiproute/ipaddress.c +++ b/networking/libiproute/ipaddress.c @@ -99,7 +99,7 @@ static void print_queuelen(char *name) } static int print_linkinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who, - struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg) + const struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg) { FILE *fp = (FILE*)arg; struct ifinfomsg *ifi = NLMSG_DATA(n); diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c new file mode 100644 index 0000000..1933877 --- /dev/null +++ b/networking/libiproute/iprule.c @@ -0,0 +1,331 @@ +/* vi: set sw=4 ts=4: */ +/* + * iprule.c "ip rule". + * + * 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, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "libbb.h" +#include +#include +#include +#include +#include + +#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 print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + int host_len = -1; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + 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; +*/ + if (tb[RTA_PRIORITY]) + fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])); + else + fprintf(fp, "0:\t"); + + fprintf(fp, "from "); + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "%s/%u", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "%s", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "0/%d", r->rtm_src_len); + } else { + fprintf(fp, "all"); + } + fprintf(fp, " "); + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "to %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf))); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "to 0/%d ", r->rtm_dst_len); + } + + if (r->rtm_tos) { + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_PROTOINFO]) { + fprintf(fp, "fwmark %#x ", *(__u32*)RTA_DATA(tb[RTA_PROTOINFO])); + } + + if (tb[RTA_IIF]) { + fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF])); + } + + if (r->rtm_table) + fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + + if (tb[RTA_FLOW]) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + if (from) { + fprintf(fp, "realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + + if (r->rtm_type == RTN_NAT) { + if (tb[RTA_GATEWAY]) { + fprintf(fp, "map-to %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } else + fprintf(fp, "masquerade"); + } else if (r->rtm_type != RTN_UNICAST) + fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +int iprule_list(int argc, char **argv) +{ + struct rtnl_handle rth; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (argc > 0) { + bb_error_msg("\"rule show\" needs no arguments"); + return -1; + } + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + bb_perror_msg("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) { + bb_error_msg("Dump terminated"); + return 1; + } + + return 0; +} + + +int iprule_modify(int cmd, int argc, char **argv) +{ + int table_ok = 0; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + + 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; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_table = 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 (argc > 0) { + if (strcmp(*argv, "from") == 0) { + 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 (strcmp(*argv, "to") == 0) { + 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 (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value", *argv); + req.r.rtm_tos = tos; + } else if (strcmp(*argv, "fwmark") == 0) { + __u32 fwmark; + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value", *argv); + addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("realms", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (matches(*argv, "table") == 0 || + strcmp(*argv, "lookup") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("table ID", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "nat") == 0 || + matches(*argv, "map-to") == 0) { + NEXT_ARG(); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); + req.r.rtm_type = RTN_NAT; + } else { + int type; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + bb_show_usage(); + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("Failed to parse rule type", *argv); + req.r.rtm_type = type; + } + argc--; + 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; + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + +int do_iprule(int argc, char **argv) +{ + static const char * const ip_rule_commands[] = + {"add", "delete", "list", "show", 0}; + int command_num = 2; + int cmd; + + if (argc < 1) + return iprule_list(0, NULL); + if (*argv) + command_num = index_in_substr_array(ip_rule_commands, *argv); + switch (command_num) { + case 0: /* add */ + cmd = RTM_NEWRULE; + break; + case 1: /* delete */ + cmd = RTM_DELRULE; + break; + case 2: /* list */ + case 3: /* show */ + return iprule_list(argc-1, argv+1); + break; + default: + bb_error_msg_and_die("unknown command %s", *argv); + } + return iprule_modify(cmd, argc-1, argv+1); +} + diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c index ec52cff..c7e17e1 100644 --- a/networking/libiproute/libnetlink.c +++ b/networking/libiproute/libnetlink.c @@ -121,10 +121,10 @@ int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) } int rtnl_dump_filter(struct rtnl_handle *rth, - int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), - void *arg1, - int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), - void *arg2) + int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg1, + int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg2) { char buf[8192]; struct sockaddr_nl nladdr; diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h index c2ef984..7cfcd20 100644 --- a/networking/libiproute/libnetlink.h +++ b/networking/libiproute/libnetlink.h @@ -20,14 +20,14 @@ extern void rtnl_close(struct rtnl_handle *rth); extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); extern int rtnl_dump_filter(struct rtnl_handle *rth, - int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), - void *arg1, - int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), - void *arg2); + int (*filter)(struct sockaddr_nl*, struct nlmsghdr *n, void*), + void *arg1, + int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg2); extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, - unsigned groups, struct nlmsghdr *answer, - int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), - void *jarg); + unsigned groups, struct nlmsghdr *answer, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg); extern int rtnl_send(struct rtnl_handle *rth, char *buf, int); diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c index c0f7907..ed21fbe 100644 --- a/networking/libiproute/rt_names.c +++ b/networking/libiproute/rt_names.c @@ -240,7 +240,23 @@ int rtnl_rtrealm_a2n(uint32_t *id, char *arg) return 0; } - +#if ENABLE_FEATURE_IP_RULE +const char * rtnl_rtrealm_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtrealm_tab[id]) { + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + } + if (rtnl_rtrealm_tab[id]) + return rtnl_rtrealm_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} +#endif static const char * rtnl_rtdsfield_tab[256] = { "0", @@ -303,3 +319,66 @@ int rtnl_dsfield_a2n(uint32_t *id, char *arg) *id = res; return 0; } + +#if ENABLE_FEATURE_IP_RULE +static int rtnl_rttable_init; +static const char * rtnl_rttable_tab[256] = { + "unspec", +}; +static void rtnl_rttable_initialize(void) +{ + rtnl_rttable_init = 1; + rtnl_rttable_tab[255] = "local"; + rtnl_rttable_tab[254] = "main"; + rtnl_rttable_tab[253] = "default"; + rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256); +} + +const char *rtnl_rttable_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rttable_tab[id]) { + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + } + if (rtnl_rttable_tab[id]) + return rtnl_rttable_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rttable_a2n(uint32_t * id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i = 0; i < 256; i++) { + if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) { + cache = (char*)rtnl_rttable_tab[i]; + res = i; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > 255) + return -1; + *id = i; + return 0; +} + +#endif diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h index 41afeed..92b807f 100644 --- a/networking/libiproute/rt_names.h +++ b/networking/libiproute/rt_names.h @@ -6,11 +6,14 @@ extern const char* rtnl_rtprot_n2a(int id, char *buf, int len); extern const char* rtnl_rtscope_n2a(int id, char *buf, int len); +extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len); extern const char* rtnl_dsfield_n2a(int id, char *buf, int len); +extern const char* rtnl_rttable_n2a(int id, char *buf, int len); extern int rtnl_rtprot_a2n(uint32_t *id, char *arg); extern int rtnl_rtscope_a2n(uint32_t *id, char *arg); extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg); extern int rtnl_dsfield_a2n(uint32_t *id, char *arg); +extern int rtnl_rttable_a2n(uint32_t *id, char *arg); extern const char * ll_type_n2a(int type, char *buf, int len); -- cgit v1.1