X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ip%2Fiprule.c;h=2f58d8c2acd8744839dcf103018362db1352159f;hb=c2f9dc14c41f388764f7634d36c3d05e354f053a;hp=94356bf888378200aa916df845f8c1b29149bc43;hpb=5baaf07cb3130847ea1fa97d3f077951321c7662;p=mirror_iproute2.git diff --git a/ip/iprule.c b/ip/iprule.c index 94356bf8..2f58d8c2 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -26,6 +26,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" enum list_action { IPRULE_LIST, @@ -46,7 +47,11 @@ static void usage(void) "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n" " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n" " [ uidrange NUMBER-NUMBER ]\n" + " [ ipproto PROTOCOL ]\n" + " [ sport [ NUMBER | NUMBER-NUMBER ]\n" + " [ dport [ NUMBER | NUMBER-NUMBER ] ]\n" "ACTION := [ table TABLE_ID ]\n" + " [ protocol PROTO ]\n" " [ nat ADDRESS ]\n" " [ realms [SRCREALM/]DSTREALM ]\n" " [ goto NUMBER ]\n" @@ -66,11 +71,17 @@ static struct unsigned int tos, tosmask; unsigned int pref, prefmask; unsigned int fwmark, fwmask; + uint64_t tun_id; char iif[IFNAMSIZ]; char oif[IFNAMSIZ]; struct fib_rule_uid_range range; inet_prefix src; inet_prefix dst; + int protocol; + int protocolmask; + struct fib_rule_port_range sport; + struct fib_rule_port_range dport; + __u8 ipproto; } filter; static inline int frh_get_table(struct fib_rule_hdr *frh, struct rtattr **tb) @@ -167,6 +178,51 @@ static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return false; } + if (filter.ipproto) { + __u8 ipproto = 0; + + if (tb[FRA_IP_PROTO]) + ipproto = rta_getattr_u8(tb[FRA_IP_PROTO]); + if (filter.ipproto != ipproto) + return false; + } + + if (filter.sport.start) { + const struct fib_rule_port_range *r; + + if (!tb[FRA_SPORT_RANGE]) + return false; + + r = RTA_DATA(tb[FRA_SPORT_RANGE]); + if (r->start != filter.sport.start || + r->end != filter.sport.end) + return false; + } + + if (filter.dport.start) { + const struct fib_rule_port_range *r; + + if (!tb[FRA_DPORT_RANGE]) + return false; + + r = RTA_DATA(tb[FRA_DPORT_RANGE]); + if (r->start != filter.dport.start || + r->end != filter.dport.end) + return false; + } + + if (filter.tun_id) { + __u64 tun_id = 0; + + if (tb[FRA_TUN_ID]) { + tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID])); + if (filter.tun_id != tun_id) + return false; + } else { + return false; + } + } + table = frh_get_table(frh, tb); if (filter.tb > 0 && filter.tb ^ table) return false; @@ -174,15 +230,14 @@ static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return true; } -int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +int print_rule(struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE *)arg; + FILE *fp = arg; struct fib_rule_hdr *frh = NLMSG_DATA(n); int len = n->nlmsg_len; int host_len = -1; - __u32 table; + __u32 table, prio = 0; struct rtattr *tb[FRA_MAX+1]; - SPRINT_BUF(b1); if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE) @@ -199,50 +254,54 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (!filter_nlmsg(n, tb, host_len)) return 0; + open_json_object(NULL); if (n->nlmsg_type == RTM_DELRULE) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (tb[FRA_PRIORITY]) - fprintf(fp, "%u:\t", - rta_getattr_u32(tb[FRA_PRIORITY])); - else - fprintf(fp, "0:\t"); + prio = rta_getattr_u32(tb[FRA_PRIORITY]); + + print_uint(PRINT_ANY, "priority", "%u:\t", prio); if (frh->flags & FIB_RULE_INVERT) - fprintf(fp, "not "); + print_null(PRINT_ANY, "not", "not ", NULL); if (tb[FRA_SRC]) { - if (frh->src_len != host_len) { - fprintf(fp, "from %s/%u ", - rt_addr_n2a_rta(frh->family, tb[FRA_SRC]), - frh->src_len); - } else { - fprintf(fp, "from %s ", - format_host_rta(frh->family, tb[FRA_SRC])); - } + const char *src = rt_addr_n2a_rta(frh->family, tb[FRA_SRC]); + + print_string(PRINT_FP, NULL, "from ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "src", "%s", src); + if (frh->src_len != host_len) + print_uint(PRINT_ANY, "srclen", "/%u ", frh->src_len); + else + print_string(PRINT_FP, NULL, " ", NULL); } else if (frh->src_len) { - fprintf(fp, "from 0/%d ", frh->src_len); + print_string(PRINT_ANY, "src", "from %s", "0"); + print_uint(PRINT_ANY, "srclen", "/%u ", frh->src_len); } else { - fprintf(fp, "from all "); + print_string(PRINT_ANY, "src", "from %s ", "all"); } if (tb[FRA_DST]) { - if (frh->dst_len != host_len) { - fprintf(fp, "to %s/%u ", - rt_addr_n2a_rta(frh->family, tb[FRA_DST]), - frh->dst_len); - } else { - fprintf(fp, "to %s ", - format_host_rta(frh->family, tb[FRA_DST])); - } + const char *dst = rt_addr_n2a_rta(frh->family, tb[FRA_DST]); + + print_string(PRINT_FP, NULL, "to ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "dst", "%s", dst); + if (frh->dst_len != host_len) + print_uint(PRINT_ANY, "dstlen", "/%u ", frh->dst_len); + else + print_string(PRINT_FP, NULL, " ", NULL); } else if (frh->dst_len) { - fprintf(fp, "to 0/%d ", frh->dst_len); + print_string(PRINT_ANY, "dst", "to %s", "0"); + print_uint(PRINT_ANY, "dstlen", "/%u ", frh->dst_len); } if (frh->tos) { - SPRINT_BUF(b1); - fprintf(fp, "tos %s ", - rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1))); + print_string(PRINT_ANY, "tos", + "tos %s ", + rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1))); } if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { @@ -252,53 +311,113 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) mark = rta_getattr_u32(tb[FRA_FWMARK]); if (tb[FRA_FWMASK] && - (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) - fprintf(fp, "fwmark 0x%x/0x%x ", mark, mask); - else - fprintf(fp, "fwmark 0x%x ", mark); + (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) { + print_0xhex(PRINT_ANY, "fwmark", "fwmark %#llx", mark); + print_0xhex(PRINT_ANY, "fwmask", "/%#llx ", mask); + } else { + print_0xhex(PRINT_ANY, "fwmark", "fwmark %#llx ", mark); + } } if (tb[FRA_IFNAME]) { - fprintf(fp, "iif %s ", rta_getattr_str(tb[FRA_IFNAME])); + if (!is_json_context()) + fprintf(fp, "iif "); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "iif", "%s ", + rta_getattr_str(tb[FRA_IFNAME])); + if (frh->flags & FIB_RULE_IIF_DETACHED) - fprintf(fp, "[detached] "); + print_null(PRINT_ANY, "iif_detached", "[detached] ", + NULL); } if (tb[FRA_OIFNAME]) { - fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME])); + if (!is_json_context()) + fprintf(fp, "oif "); + + print_color_string(PRINT_ANY, COLOR_IFNAME, "oif", "%s ", + rta_getattr_str(tb[FRA_OIFNAME])); + if (frh->flags & FIB_RULE_OIF_DETACHED) - fprintf(fp, "[detached] "); + print_null(PRINT_ANY, "oif_detached", "[detached] ", + NULL); } if (tb[FRA_L3MDEV]) { - if (rta_getattr_u8(tb[FRA_L3MDEV])) - fprintf(fp, "lookup [l3mdev-table] "); + __u8 mdev = rta_getattr_u8(tb[FRA_L3MDEV]); + + if (mdev) + print_null(PRINT_ANY, "l3mdev", + "lookup [l3mdev-table] ", NULL); } if (tb[FRA_UID_RANGE]) { struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); - fprintf(fp, "uidrange %u-%u ", r->start, r->end); + print_uint(PRINT_ANY, "uid_start", "uidrange %u", r->start); + print_uint(PRINT_ANY, "uid_end", "-%u ", r->end); + } + + if (tb[FRA_IP_PROTO]) { + SPRINT_BUF(pbuf); + print_string(PRINT_ANY, "ipproto", "ipproto %s ", + inet_proto_n2a(rta_getattr_u8(tb[FRA_IP_PROTO]), + pbuf, sizeof(pbuf))); + } + + if (tb[FRA_SPORT_RANGE]) { + struct fib_rule_port_range *r = RTA_DATA(tb[FRA_SPORT_RANGE]); + + if (r->start == r->end) { + print_uint(PRINT_ANY, "sport", "sport %u ", r->start); + } else { + print_uint(PRINT_ANY, "sport_start", "sport %u", + r->start); + print_uint(PRINT_ANY, "sport_end", "-%u ", r->end); + } + } + + if (tb[FRA_DPORT_RANGE]) { + struct fib_rule_port_range *r = RTA_DATA(tb[FRA_DPORT_RANGE]); + + if (r->start == r->end) { + print_uint(PRINT_ANY, "dport", "dport %u ", r->start); + } else { + print_uint(PRINT_ANY, "dport_start", "dport %u", + r->start); + print_uint(PRINT_ANY, "dport_end", "-%u ", r->end); + } + } + + if (tb[FRA_TUN_ID]) { + __u64 tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID])); + + print_u64(PRINT_ANY, "tun_id", "tun_id %llu ", tun_id); } table = frh_get_table(frh, tb); if (table) { - fprintf(fp, "lookup %s ", - rtnl_rttable_n2a(table, b1, sizeof(b1))); + print_string(PRINT_ANY, "table", + "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) - fprintf(fp, "suppress_prefixlength %d ", pl); + print_int(PRINT_ANY, "suppress_prefixlen", + "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))); + const char *grname + = rtnl_group_n2a(group, b1, sizeof(b1)); + + print_string(PRINT_ANY, "suppress_ifgroup", + "suppress_ifgroup %s ", grname); } } } @@ -308,37 +427,52 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) __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 (from) + print_string(PRINT_ANY, + "flow_from", "realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + + print_string(PRINT_ANY, "flow_to", "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); } if (frh->action == RTN_NAT) { if (tb[RTA_GATEWAY]) { - fprintf(fp, "map-to %s ", - format_host_rta(frh->family, - tb[RTA_GATEWAY])); - } else - fprintf(fp, "masquerade"); + const char *gateway; + + gateway = format_host_rta(frh->family, tb[RTA_GATEWAY]); + + print_string(PRINT_ANY, "nat_gateway", + "map-to %s ", gateway); + } else { + print_null(PRINT_ANY, "masquerade", "masquerade", NULL); + } } else if (frh->action == FR_ACT_GOTO) { - fprintf(fp, "goto "); if (tb[FRA_GOTO]) - fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO])); + print_uint(PRINT_ANY, "goto", "goto %u", + rta_getattr_u32(tb[FRA_GOTO])); else - fprintf(fp, "none"); + print_string(PRINT_ANY, "goto", "goto %s", "none"); + if (frh->flags & FIB_RULE_UNRESOLVED) - fprintf(fp, " [unresolved]"); - } else if (frh->action == FR_ACT_NOP) - fprintf(fp, "nop"); - else if (frh->action != FR_ACT_TO_TBL) - fprintf(fp, "%s", - rtnl_rtntype_n2a(frh->action, - b1, sizeof(b1))); - - fprintf(fp, "\n"); + print_null(PRINT_ANY, "unresolved", "unresolved", NULL); + } else if (frh->action == FR_ACT_NOP) { + print_null(PRINT_ANY, "nop", "nop", NULL); + } else if (frh->action != FR_ACT_TO_TBL) { + print_string(PRINT_ANY, "to_tbl", "%s", + rtnl_rtntype_n2a(frh->action, b1, sizeof(b1))); + } + + if (tb[FRA_PROTOCOL]) { + __u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]); + + if ((protocol && protocol != RTPROT_KERNEL) || show_details > 0) { + print_string(PRINT_ANY, "protocol", " proto %s ", + rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); + } + } + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); fflush(fp); return 0; } @@ -363,8 +497,7 @@ 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(struct nlmsghdr *n, void *arg) { int ret; @@ -377,13 +510,13 @@ static int save_rule(const struct sockaddr_nl *who, return ret == n->nlmsg_len ? 0 : ret; } -static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, - void *arg) +static int flush_rule(struct nlmsghdr *n, void *arg) { struct rtnl_handle rth2; struct fib_rule_hdr *frh = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[FRA_MAX+1]; + int host_len = -1; len -= NLMSG_LENGTH(sizeof(*frh)); if (len < 0) @@ -391,6 +524,17 @@ static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); + host_len = af_bit_len(frh->family); + if (!filter_nlmsg(n, tb, host_len)) + return 0; + + if (tb[FRA_PROTOCOL]) { + __u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]); + + if ((filter.protocol ^ protocol) & filter.protocolmask) + return 0; + } + if (tb[FRA_PRIORITY]) { n->nlmsg_type = RTM_DELRULE; n->nlmsg_flags = NLM_F_REQUEST; @@ -415,9 +559,8 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) if (af == AF_UNSPEC) af = AF_INET; - if (action != IPRULE_LIST && argc > 0) { - fprintf(stderr, "\"ip rule %s\" does not take any arguments.\n", - action == IPRULE_SAVE ? "save" : "flush"); + if (action == IPRULE_SAVE && argc > 0) { + fprintf(stderr, "\"ip rule save\" does not take any arguments.\n"); return -1; } @@ -495,6 +638,13 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) &filter.range.end) != 2) invarg("invalid UID range\n", *argv); + } else if (matches(*argv, "tun_id") == 0) { + __u64 tun_id; + + NEXT_ARG(); + if (get_u64(&tun_id, *argv, 0)) + invarg("\"tun_id\" value is invalid\n", *argv); + filter.tun_id = tun_id; } else if (matches(*argv, "lookup") == 0 || matches(*argv, "table") == 0) { __u32 tid; @@ -508,7 +658,48 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) NEXT_ARG(); if (get_prefix(&filter.src, *argv, af)) invarg("from value is invalid\n", *argv); - } else { + } else if (matches(*argv, "protocol") == 0) { + __u32 prot; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (strcmp(*argv, "ipproto") == 0) { + int ipproto; + + NEXT_ARG(); + ipproto = inet_proto_a2n(*argv); + if (ipproto < 0) + invarg("Invalid \"ipproto\" value\n", *argv); + filter.ipproto = ipproto; + } else if (strcmp(*argv, "sport") == 0) { + struct fib_rule_port_range r; + int ret; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid port range\n", *argv); + filter.sport = r; + } else if (strcmp(*argv, "dport") == 0) { + struct fib_rule_port_range r; + int ret; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid dport range\n", *argv); + filter.dport = r; + } else{ if (matches(*argv, "dst") == 0 || matches(*argv, "to") == 0) { NEXT_ARG(); @@ -519,15 +710,17 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) argc--; argv++; } - if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + if (rtnl_ruledump_req(&rth, af) < 0) { perror("Cannot send dump request"); return 1; } + new_json_obj(json); if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } + delete_json_obj(); return 0; } @@ -552,8 +745,7 @@ static int rule_dump_check_magic(void) return 0; } -static int restore_handler(const struct sockaddr_nl *nl, - struct rtnl_ctrl_data *ctrl, +static int restore_handler(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { int ret; @@ -596,6 +788,11 @@ static int iprule_modify(int cmd, int argc, char **argv) }; if (cmd == RTM_NEWRULE) { + if (argc == 0) { + fprintf(stderr, + "\"ip rule add\" requires arguments.\n"); + return -1; + } req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; req.frh.action = FR_ACT_TO_TBL; } @@ -667,6 +864,20 @@ static int iprule_modify(int cmd, int argc, char **argv) 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, "protocol") == 0) { + __u32 proto; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&proto, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + addattr8(&req.n, sizeof(req), FRA_PROTOCOL, proto); + } else if (matches(*argv, "tun_id") == 0) { + __u64 tun_id; + + NEXT_ARG(); + if (get_be64(&tun_id, *argv, 0)) + invarg("\"tun_id\" value is invalid\n", *argv); + addattr64(&req.n, sizeof(req), FRA_TUN_ID, tun_id); } else if (matches(*argv, "table") == 0 || strcmp(*argv, "lookup") == 0) { NEXT_ARG(); @@ -731,6 +942,39 @@ static int iprule_modify(int cmd, int argc, char **argv) addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); req.frh.action = RTN_NAT; + } else if (strcmp(*argv, "ipproto") == 0) { + int ipproto; + + NEXT_ARG(); + ipproto = inet_proto_a2n(*argv); + if (ipproto < 0) + invarg("Invalid \"ipproto\" value\n", + *argv); + addattr8(&req.n, sizeof(req), FRA_IP_PROTO, ipproto); + } else if (strcmp(*argv, "sport") == 0) { + struct fib_rule_port_range r; + int ret = 0; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid port range\n", *argv); + addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &r, + sizeof(r)); + } else if (strcmp(*argv, "dport") == 0) { + struct fib_rule_port_range r; + int ret = 0; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid dport range\n", *argv); + addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r, + sizeof(r)); } else { int type;