diff options
Diffstat (limited to 'ip/iprule.c')
-rw-r--r-- | ip/iprule.c | 525 |
1 files changed, 379 insertions, 146 deletions
diff --git a/ip/iprule.c b/ip/iprule.c index ac835ab8..36c57fa7 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -20,6 +20,7 @@ #include <netinet/ip.h> #include <arpa/inet.h> #include <string.h> +#include <linux/if.h> #include <linux/fib_rules.h> #include <errno.h> @@ -27,35 +28,156 @@ #include "utils.h" #include "ip_common.h" +enum list_action { + IPRULE_LIST, + IPRULE_FLUSH, + IPRULE_SAVE, +}; + extern struct rtnl_handle rth; static void usage(void) __attribute__((noreturn)); static void usage(void) { - fprintf(stderr, "Usage: ip rule [ list | add | del | flush | save ] SELECTOR ACTION\n"); - fprintf(stderr, " ip rule restore\n"); - fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n"); - fprintf(stderr, " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ uidrange UID1-UID2 ]\n"); - fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); - fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); - fprintf(stderr, " [ goto NUMBER ]\n"); - fprintf(stderr, " SUPPRESSOR\n"); - fprintf(stderr, "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n"); - fprintf(stderr, " [ suppress_ifgroup DEVGROUP ]\n"); - fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); + fprintf(stderr, + "Usage: ip rule { add | del } SELECTOR ACTION\n" + " ip rule { flush | save | restore }\n" + " ip rule [ list [ SELECTOR ]]\n" + "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n" + " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n" + " [ uidrange NUMBER-NUMBER ]\n" + "ACTION := [ table TABLE_ID ]\n" + " [ nat ADDRESS ]\n" + " [ realms [SRCREALM/]DSTREALM ]\n" + " [ goto NUMBER ]\n" + " SUPPRESSOR\n" + "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n" + " [ suppress_ifgroup DEVGROUP ]\n" + "TABLE_ID := [ local | main | default | NUMBER ]\n"); exit(-1); } +static struct +{ + int not; + int l3mdev; + int iifmask, oifmask, uidrange; + unsigned int tb; + unsigned int tos, tosmask; + unsigned int pref, prefmask; + unsigned int fwmark, fwmask; + char iif[IFNAMSIZ]; + char oif[IFNAMSIZ]; + struct fib_rule_uid_range range; + inet_prefix src; + inet_prefix dst; +} filter; + +static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) +{ + struct rtmsg *r = NLMSG_DATA(n); + inet_prefix src = { .family = r->rtm_family }; + inet_prefix dst = { .family = r->rtm_family }; + __u32 table; + + if (preferred_family != AF_UNSPEC && r->rtm_family != preferred_family) + return false; + + if (filter.prefmask && + filter.pref ^ (tb[FRA_PRIORITY] ? rta_getattr_u32(tb[FRA_PRIORITY]) : 0)) + return false; + if (filter.not && !(r->rtm_flags & FIB_RULE_INVERT)) + return false; + + if (filter.src.family) { + if (tb[FRA_SRC]) { + memcpy(&src.data, RTA_DATA(tb[FRA_SRC]), + (r->rtm_src_len + 7) / 8); + } + if (filter.src.family != r->rtm_family || + filter.src.bitlen > r->rtm_src_len || + inet_addr_match(&src, &filter.src, filter.src.bitlen)) + return false; + } + + if (filter.dst.family) { + if (tb[FRA_DST]) { + memcpy(&dst.data, RTA_DATA(tb[FRA_DST]), + (r->rtm_dst_len + 7) / 8); + } + if (filter.dst.family != r->rtm_family || + filter.dst.bitlen > r->rtm_dst_len || + inet_addr_match(&dst, &filter.dst, filter.dst.bitlen)) + return false; + } + + if (filter.tosmask && filter.tos ^ r->rtm_tos) + return false; + + if (filter.fwmark) { + __u32 mark = 0; + + if (tb[FRA_FWMARK]) + mark = rta_getattr_u32(tb[FRA_FWMARK]); + if (filter.fwmark ^ mark) + return false; + } + if (filter.fwmask) { + __u32 mask = 0; + + if (tb[FRA_FWMASK]) + mask = rta_getattr_u32(tb[FRA_FWMASK]); + if (filter.fwmask ^ mask) + return false; + } + + if (filter.iifmask) { + if (tb[FRA_IFNAME]) { + if (strcmp(filter.iif, rta_getattr_str(tb[FRA_IFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.oifmask) { + if (tb[FRA_OIFNAME]) { + if (strcmp(filter.oif, rta_getattr_str(tb[FRA_OIFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.l3mdev && !(tb[FRA_L3MDEV] && rta_getattr_u8(tb[FRA_L3MDEV]))) + return false; + + if (filter.uidrange) { + struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); + + if (!tb[FRA_UID_RANGE] || + r->start != filter.range.start || + r->end != filter.range.end) + return false; + } + + table = rtm_get_table(r, tb); + if (filter.tb > 0 && filter.tb ^ table) + return false; + + return true; +} + int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; int host_len = -1; __u32 table; - struct rtattr * tb[FRA_MAX+1]; - char abuf[256]; + struct rtattr *tb[FRA_MAX+1]; + SPRINT_BUF(b1); if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE) @@ -69,11 +191,15 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) host_len = af_bit_len(r->rtm_family); + if (!filter_nlmsg(n, tb, host_len)) + return 0; + if (n->nlmsg_type == RTM_DELRULE) fprintf(fp, "Deleted "); if (tb[FRA_PRIORITY]) - fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[FRA_PRIORITY])); + fprintf(fp, "%u:\t", + rta_getattr_u32(tb[FRA_PRIORITY])); else fprintf(fp, "0:\t"); @@ -82,18 +208,12 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (tb[FRA_SRC]) { if (r->rtm_src_len != host_len) { - fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[FRA_SRC]), - RTA_DATA(tb[FRA_SRC]), - abuf, sizeof(abuf)), - r->rtm_src_len - ); + fprintf(fp, "from %s/%u ", + rt_addr_n2a_rta(r->rtm_family, tb[FRA_SRC]), + r->rtm_src_len); } else { - fprintf(fp, "from %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[FRA_SRC]), - RTA_DATA(tb[FRA_SRC]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "from %s ", + format_host_rta(r->rtm_family, tb[FRA_SRC])); } } else if (r->rtm_src_len) { fprintf(fp, "from 0/%d ", r->rtm_src_len); @@ -103,17 +223,12 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (tb[FRA_DST]) { if (r->rtm_dst_len != host_len) { - fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[FRA_DST]), - RTA_DATA(tb[FRA_DST]), - abuf, sizeof(abuf)), - r->rtm_dst_len - ); + fprintf(fp, "to %s/%u ", + rt_addr_n2a_rta(r->rtm_family, tb[FRA_DST]), + r->rtm_dst_len); } else { - fprintf(fp, "to %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[FRA_DST]), - RTA_DATA(tb[FRA_DST]), - abuf, sizeof(abuf))); + fprintf(fp, "to %s ", + format_host_rta(r->rtm_family, tb[FRA_DST])); } } else if (r->rtm_dst_len) { fprintf(fp, "to 0/%d ", r->rtm_dst_len); @@ -121,7 +236,8 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (r->rtm_tos) { SPRINT_BUF(b1); - fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + fprintf(fp, "tos %s ", + rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); } if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { @@ -149,6 +265,11 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "[detached] "); } + if (tb[FRA_L3MDEV]) { + if (rta_getattr_u8(tb[FRA_L3MDEV])) + fprintf(fp, "lookup [l3mdev-table] "); + } + if (tb[FRA_UID_RANGE]) { struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); @@ -157,19 +278,22 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) table = rtm_get_table(r, tb); if (table) { - fprintf(fp, "lookup %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); + fprintf(fp, "lookup %s ", + rtnl_rttable_n2a(table, b1, sizeof(b1))); if (tb[FRA_SUPPRESS_PREFIXLEN]) { int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]); - if (pl != -1) { + + if (pl != -1) fprintf(fp, "suppress_prefixlength %d ", pl); - } } if (tb[FRA_SUPPRESS_IFGROUP]) { int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]); + if (group != -1) { SPRINT_BUF(b1); - fprintf(fp, "suppress_ifgroup %s ", rtnl_group_n2a(group, b1, sizeof(b1))); + fprintf(fp, "suppress_ifgroup %s ", + rtnl_group_n2a(group, b1, sizeof(b1))); } } } @@ -177,6 +301,7 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (tb[FRA_FLOW]) { __u32 to = rta_getattr_u32(tb[FRA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; if (from) { fprintf(fp, "realms %s/", @@ -189,10 +314,8 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 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))); + format_host_rta(r->rtm_family, + tb[RTA_GATEWAY])); } else fprintf(fp, "masquerade"); } else if (r->rtm_type == FR_ACT_GOTO) { @@ -206,7 +329,9 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) } else if (r->rtm_type == FR_ACT_NOP) fprintf(fp, "nop"); else if (r->rtm_type != RTN_UNICAST) - fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + fprintf(fp, "%s", + rtnl_rtntype_n2a(r->rtm_type, + b1, sizeof(b1))); fprintf(fp, "\n"); fflush(fp); @@ -233,7 +358,8 @@ static int save_rule_prep(void) return 0; } -static int save_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int save_rule(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret; @@ -246,24 +372,144 @@ static int save_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *ar return ret == n->nlmsg_len ? 0 : ret; } -static int iprule_list_or_save(int argc, char **argv, int save) +static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) { - rtnl_filter_t filter = print_rule; + struct rtnl_handle rth2; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[FRA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len); + + if (tb[FRA_PRIORITY]) { + n->nlmsg_type = RTM_DELRULE; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth2, 0) < 0) + return -1; + + if (rtnl_talk(&rth2, n, NULL, 0) < 0) + return -2; + + rtnl_close(&rth2); + } + + return 0; +} + +static int iprule_list_flush_or_save(int argc, char **argv, int action) +{ + rtnl_filter_t filter_fn; int af = preferred_family; if (af == AF_UNSPEC) af = AF_INET; - if (argc > 0) { + if (action != IPRULE_LIST && argc > 0) { fprintf(stderr, "\"ip rule %s\" does not take any arguments.\n", - save ? "save" : "show"); + action == IPRULE_SAVE ? "save" : "flush"); return -1; } - if (save) { + switch (action) { + case IPRULE_SAVE: if (save_rule_prep()) return -1; - filter = save_rule; + filter_fn = save_rule; + break; + case IPRULE_FLUSH: + filter_fn = flush_rule; + break; + default: + filter_fn = print_rule; + } + + memset(&filter, 0, sizeof(filter)); + + while (argc > 0) { + 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 is invalid\n", *argv); + filter.pref = pref; + filter.prefmask = 1; + } else if (strcmp(*argv, "not") == 0) { + filter.not = 1; + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = 1; + } else if (strcmp(*argv, "fwmark") == 0) { + char *slash; + __u32 fwmark, fwmask; + + NEXT_ARG(); + slash = strchr(*argv, '/'); + if (slash != NULL) + *slash = '\0'; + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + filter.fwmark = fwmark; + if (slash) { + if (get_u32(&fwmask, slash+1, 0)) + invarg("fwmask value is invalid\n", + slash+1); + filter.fwmask = fwmask; + } + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + if (get_ifname(filter.iif, *argv)) + invarg("\"iif\"/\"dev\" not a valid ifname", *argv); + filter.iifmask = 1; + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + if (get_ifname(filter.oif, *argv)) + invarg("\"oif\" not a valid ifname", *argv); + filter.oifmask = 1; + } else if (strcmp(*argv, "l3mdev") == 0) { + filter.l3mdev = 1; + } else if (strcmp(*argv, "uidrange") == 0) { + NEXT_ARG(); + filter.uidrange = 1; + if (sscanf(*argv, "%u-%u", + &filter.range.start, + &filter.range.end) != 2) + invarg("invalid UID range\n", *argv); + + } else if (matches(*argv, "lookup") == 0 || + matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("table id value is invalid\n", *argv); + filter.tb = tid; + } else if (matches(*argv, "from") == 0 || + matches(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.src, *argv, af); + } else { + if (matches(*argv, "dst") == 0 || + matches(*argv, "to") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.dst, *argv, af); + } + argc--; argv++; } if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { @@ -271,7 +517,7 @@ static int iprule_list_or_save(int argc, char **argv, int save) return 1; } - if (rtnl_dump_filter(&rth, filter, stdout) < 0) { + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } @@ -291,7 +537,8 @@ static int rule_dump_check_magic(void) ret = fread(&magic, sizeof(magic), 1, stdin); if (magic != rule_dump_magic) { - fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic); + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", + ret, magic); return -1; } @@ -326,49 +573,57 @@ static int iprule_restore(void) static int iprule_modify(int cmd, int argc, char **argv) { + int l3mdev_rule = 0; int table_ok = 0; + __u32 tid = 0; 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; - req.r.rtm_flags = 0; + char buf[1024]; + } req = { + .n.nlmsg_type = cmd, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .r.rtm_family = preferred_family, + .r.rtm_protocol = RTPROT_BOOT, + .r.rtm_scope = RT_SCOPE_UNIVERSE, + .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; } + if (cmd == RTM_DELRULE && argc == 0) { + fprintf(stderr, "\"ip rule del\" requires arguments.\n"); + return -1; + } + while (argc > 0) { if (strcmp(*argv, "not") == 0) { req.r.rtm_flags |= FIB_RULE_INVERT; } else 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), FRA_SRC, &dst.data, dst.bytelen); + addattr_l(&req.n, sizeof(req), FRA_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), FRA_DST, &dst.data, dst.bytelen); + addattr_l(&req.n, sizeof(req), FRA_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 is invalid\n", *argv); @@ -376,6 +631,7 @@ static int iprule_modify(int cmd, int argc, char **argv) } else if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("TOS value is invalid\n", *argv); @@ -383,26 +639,31 @@ static int iprule_modify(int cmd, int argc, char **argv) } else if (strcmp(*argv, "fwmark") == 0) { char *slash; __u32 fwmark, fwmask; + NEXT_ARG(); - if ((slash = strchr(*argv, '/')) != NULL) + + slash = strchr(*argv, '/'); + if (slash != NULL) *slash = '\0'; if (get_u32(&fwmark, *argv, 0)) invarg("fwmark value is invalid\n", *argv); addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark); if (slash) { if (get_u32(&fwmask, slash+1, 0)) - invarg("fwmask value is invalid\n", slash+1); - addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask); + invarg("fwmask value is invalid\n", + slash+1); + addattr32(&req.n, sizeof(req), + FRA_FWMASK, fwmask); } } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("invalid realms\n", *argv); addattr32(&req.n, sizeof(req), FRA_FLOW, realm); } else if (matches(*argv, "table") == 0 || strcmp(*argv, "lookup") == 0) { - __u32 tid; NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) invarg("invalid table ID\n", *argv); @@ -416,24 +677,40 @@ static int iprule_modify(int cmd, int argc, char **argv) } else if (matches(*argv, "suppress_prefixlength") == 0 || strcmp(*argv, "sup_pl") == 0) { int pl; + NEXT_ARG(); if (get_s32(&pl, *argv, 0) || pl < 0) - invarg("suppress_prefixlength value is invalid\n", *argv); - addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, pl); + invarg("suppress_prefixlength value is invalid\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_PREFIXLEN, pl); } else if (matches(*argv, "suppress_ifgroup") == 0 || strcmp(*argv, "sup_group") == 0) { NEXT_ARG(); int group; + if (rtnl_group_a2n(&group, *argv)) - invarg("Invalid \"suppress_ifgroup\" value\n", *argv); - addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, group); + invarg("Invalid \"suppress_ifgroup\" value\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_IFGROUP, group); } else if (strcmp(*argv, "dev") == 0 || strcmp(*argv, "iif") == 0) { NEXT_ARG(); - addattr_l(&req.n, sizeof(req), FRA_IFNAME, *argv, strlen(*argv)+1); + if (check_ifname(*argv)) + invarg("\"iif\"/\"dev\" not a valid ifname", *argv); + addattr_l(&req.n, sizeof(req), FRA_IFNAME, + *argv, strlen(*argv)+1); } else if (strcmp(*argv, "oif") == 0) { NEXT_ARG(); - addattr_l(&req.n, sizeof(req), FRA_OIFNAME, *argv, strlen(*argv)+1); + if (check_ifname(*argv)) + invarg("\"oif\" not a valid ifname", *argv); + addattr_l(&req.n, sizeof(req), FRA_OIFNAME, + *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "l3mdev") == 0) { + addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1); + table_ok = 1; + l3mdev_rule = 1; } else if (strcmp(*argv, "uidrange") == 0) { struct fib_rule_uid_range r; @@ -446,23 +723,26 @@ static int iprule_modify(int cmd, int argc, char **argv) matches(*argv, "map-to") == 0) { NEXT_ARG(); fprintf(stderr, "Warning: route NAT is deprecated\n"); - addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, + get_addr32(*argv)); req.r.rtm_type = RTN_NAT; } else { int type; - if (strcmp(*argv, "type") == 0) { + if (strcmp(*argv, "type") == 0) NEXT_ARG(); - } + if (matches(*argv, "help") == 0) usage(); else if (matches(*argv, "goto") == 0) { __u32 target; + type = FR_ACT_GOTO; NEXT_ARG(); if (get_u32(&target, *argv, 0)) invarg("invalid target\n", *argv); - addattr32(&req.n, sizeof(req), FRA_GOTO, target); + addattr32(&req.n, sizeof(req), + FRA_GOTO, target); } else if (matches(*argv, "nop") == 0) type = FR_ACT_NOP; else if (rtnl_rtntype_a2n(&type, *argv)) @@ -474,6 +754,12 @@ static int iprule_modify(int cmd, int argc, char **argv) argv++; } + if (l3mdev_rule && tid != 0) { + fprintf(stderr, + "table can not be specified for l3mdev rules\n"); + return -EINVAL; + } + if (req.r.rtm_family == AF_UNSPEC) req.r.rtm_family = AF_INET; @@ -486,71 +772,16 @@ static int iprule_modify(int cmd, int argc, char **argv) return 0; } - -static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) -{ - struct rtnl_handle rth2; - struct rtmsg *r = NLMSG_DATA(n); - int len = n->nlmsg_len; - struct rtattr * tb[FRA_MAX+1]; - - len -= NLMSG_LENGTH(sizeof(*r)); - if (len < 0) - return -1; - - parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len); - - if (tb[FRA_PRIORITY]) { - n->nlmsg_type = RTM_DELRULE; - n->nlmsg_flags = NLM_F_REQUEST; - - if (rtnl_open(&rth2, 0) < 0) - return -1; - - if (rtnl_talk(&rth2, n, NULL, 0) < 0) - return -2; - - rtnl_close(&rth2); - } - - return 0; -} - -static int iprule_flush(int argc, char **argv) -{ - int af = preferred_family; - - if (af == AF_UNSPEC) - af = AF_INET; - - if (argc > 0) { - fprintf(stderr, "\"ip rule flush\" does not allow arguments\n"); - return -1; - } - - if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { - perror("Cannot send dump request"); - return 1; - } - - if (rtnl_dump_filter(&rth, flush_rule, NULL) < 0) { - fprintf(stderr, "Flush terminated\n"); - return 1; - } - - return 0; -} - int do_iprule(int argc, char **argv) { if (argc < 1) { - return iprule_list_or_save(0, NULL, 0); + return iprule_list_flush_or_save(0, NULL, IPRULE_LIST); } else if (matches(argv[0], "list") == 0 || matches(argv[0], "lst") == 0 || matches(argv[0], "show") == 0) { - return iprule_list_or_save(argc-1, argv+1, 0); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_LIST); } else if (matches(argv[0], "save") == 0) { - return iprule_list_or_save(argc-1, argv+1, 1); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_SAVE); } else if (matches(argv[0], "restore") == 0) { return iprule_restore(); } else if (matches(argv[0], "add") == 0) { @@ -558,11 +789,12 @@ int do_iprule(int argc, char **argv) } else if (matches(argv[0], "delete") == 0) { return iprule_modify(RTM_DELRULE, argc-1, argv+1); } else if (matches(argv[0], "flush") == 0) { - return iprule_flush(argc-1, argv+1); + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_FLUSH); } else if (matches(argv[0], "help") == 0) usage(); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); exit(-1); } @@ -580,7 +812,8 @@ int do_multirule(int argc, char **argv) case RTNL_FAMILY_IP6MR: break; default: - fprintf(stderr, "Multicast rules are only supported for IPv4/IPv6, was: %i\n", + fprintf(stderr, + "Multicast rules are only supported for IPv4/IPv6, was: %i\n", preferred_family); exit(-1); } |