X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=tc%2Ff_flower.c;h=9659e894dd1f93b4f773525f38270ecadf174b67;hb=28747146622a49c3e7b5c5b36dc02c6a64124770;hp=934832e2bbe90562a90f3e739bc15e66f0c8da5d;hpb=f4745880289463b498350c72842d0029e8345b28;p=mirror_iproute2.git diff --git a/tc/f_flower.c b/tc/f_flower.c index 934832e2..9659e894 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -12,13 +12,14 @@ #include #include #include -#include #include #include +#include #include #include #include #include +#include #include "utils.h" #include "tc_util.h" @@ -41,7 +42,7 @@ enum flower_icmp_field { static void explain(void) { fprintf(stderr, - "Usage: ... flower [ MATCH-LIST ]\n" + "Usage: ... flower [ MATCH-LIST ] [ verbose ]\n" " [ skip_sw | skip_hw ]\n" " [ action ACTION-SPEC ] [ classid CLASSID ]\n" "\n" @@ -50,11 +51,18 @@ static void explain(void) " vlan_id VID |\n" " vlan_prio PRIORITY |\n" " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" + " cvlan_id VID |\n" + " cvlan_prio PRIORITY |\n" + " cvlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" " dst_mac MASKED-LLADDR |\n" " src_mac MASKED-LLADDR |\n" " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n" " ip_tos MASKED-IP_TOS |\n" " ip_ttl MASKED-IP_TTL |\n" + " mpls_label LABEL |\n" + " mpls_tc TC |\n" + " mpls_bos BOS |\n" + " mpls_ttl TTL |\n" " dst_ip PREFIX |\n" " src_ip PREFIX |\n" " dst_port PORT-NUMBER |\n" @@ -70,6 +78,9 @@ static void explain(void) " enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " enc_key_id [ KEY-ID ] |\n" + " enc_tos MASKED-IP_TOS |\n" + " enc_ttl MASKED-IP_TTL |\n" + " geneve_opts MASKED-OPTIONS |\n" " ip_flags IP-FLAGS | \n" " enc_dst_port [ port_number ] }\n" " FILTERID := X:Y:Z\n" @@ -124,15 +135,21 @@ err: return err; } +static bool eth_type_vlan(__be16 ethertype) +{ + return ethertype == htons(ETH_P_8021Q) || + ethertype == htons(ETH_P_8021AD); +} + static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type, __be16 *p_vlan_eth_type, struct nlmsghdr *n) { __be16 vlan_eth_type; - if (eth_type != htons(ETH_P_8021Q)) { - fprintf(stderr, - "Can't set \"vlan_ethtype\" if ethertype isn't 802.1Q\n"); + if (!eth_type_vlan(eth_type)) { + fprintf(stderr, "Can't set \"%s\" if ethertype isn't 802.1Q or 802.1AD\n", + type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "vlan_ethtype" : "cvlan_ethtype"); return -1; } @@ -151,6 +168,7 @@ struct flag_to_string { static struct flag_to_string flags_str[] = { { TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOWER_IP_FLAGS, "frag" }, + { TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, FLOWER_IP_FLAGS, "firstfrag" }, }; static int flower_parse_matching_flags(char *str, @@ -456,24 +474,57 @@ static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint) return -1; } +static int flower_port_range_attr_type(__u8 ip_proto, enum flower_endpoint type, + __be16 *min_port_type, + __be16 *max_port_type) +{ + if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP || + ip_proto == IPPROTO_SCTP) { + if (type == FLOWER_ENDPOINT_SRC) { + *min_port_type = TCA_FLOWER_KEY_PORT_SRC_MIN; + *max_port_type = TCA_FLOWER_KEY_PORT_SRC_MAX; + } else { + *min_port_type = TCA_FLOWER_KEY_PORT_DST_MIN; + *max_port_type = TCA_FLOWER_KEY_PORT_DST_MAX; + } + } else { + return -1; + } + return 0; +} + static int flower_parse_port(char *str, __u8 ip_proto, enum flower_endpoint endpoint, struct nlmsghdr *n) { + __u16 min, max; int ret; - int type; - __be16 port; - type = flower_port_attr_type(ip_proto, endpoint); - if (type < 0) - return -1; + ret = sscanf(str, "%hu-%hu", &min, &max); - ret = get_be16(&port, str, 10); - if (ret) - return -1; + if (ret == 1) { + int type; - addattr16(n, MAX_MSG, type, port); + type = flower_port_attr_type(ip_proto, endpoint); + if (type < 0) + return -1; + addattr16(n, MAX_MSG, type, htons(min)); + } else if (ret == 2) { + __be16 min_port_type, max_port_type; + + if (max <= min) { + fprintf(stderr, "max value should be greater than min value\n"); + return -1; + } + if (flower_port_range_attr_type(ip_proto, endpoint, + &min_port_type, &max_port_type)) + return -1; + addattr16(n, MAX_MSG, min_port_type, htons(min)); + addattr16(n, MAX_MSG, max_port_type, htons(max)); + } else { + return -1; + } return 0; } @@ -573,6 +624,179 @@ static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n) return 0; } +static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n) +{ + struct rtattr *nest; + char *token; + int i, err; + + nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_GENEVE); + + i = 1; + token = strsep(&str, ":"); + while (token) { + switch (i) { + case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS: + { + __be16 opt_class; + + if (!strlen(token)) + break; + err = get_be16(&opt_class, token, 16); + if (err) + return err; + + addattr16(n, MAX_MSG, i, opt_class); + break; + } + case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE: + { + __u8 opt_type; + + if (!strlen(token)) + break; + err = get_u8(&opt_type, token, 16); + if (err) + return err; + + addattr8(n, MAX_MSG, i, opt_type); + break; + } + case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA: + { + size_t token_len = strlen(token); + __u8 *opts; + + if (!token_len) + break; + opts = malloc(token_len / 2); + if (!opts) + return -1; + if (hex2mem(token, opts, token_len / 2) < 0) { + free(opts); + return -1; + } + addattr_l(n, MAX_MSG, i, opts, token_len / 2); + free(opts); + + break; + } + default: + fprintf(stderr, "Unknown \"geneve_opts\" type\n"); + return -1; + } + + token = strsep(&str, ":"); + i++; + } + addattr_nest_end(n, nest); + + return 0; +} + +static int flower_parse_enc_opt_part(char *str, struct nlmsghdr *n) +{ + char *token; + int err; + + token = strsep(&str, ","); + while (token) { + err = flower_parse_geneve_opts(token, n); + if (err) + return err; + + token = strsep(&str, ","); + } + + return 0; +} + +static int flower_check_enc_opt_key(char *key) +{ + int key_len, col_cnt = 0; + + key_len = strlen(key); + while ((key = strchr(key, ':'))) { + if (strlen(key) == key_len) + return -1; + + key_len = strlen(key) - 1; + col_cnt++; + key++; + } + + if (col_cnt != 2 || !key_len) + return -1; + + return 0; +} + +static int flower_parse_enc_opts(char *str, struct nlmsghdr *n) +{ + char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX]; + int data_len, key_len, mask_len, err; + char *token, *slash; + struct rtattr *nest; + + key_len = 0; + mask_len = 0; + token = strsep(&str, ","); + while (token) { + slash = strchr(token, '/'); + if (slash) + *slash = '\0'; + + if ((key_len + strlen(token) > XATTR_SIZE_MAX) || + flower_check_enc_opt_key(token)) + return -1; + + strcpy(&key[key_len], token); + key_len += strlen(token) + 1; + key[key_len - 1] = ','; + + if (!slash) { + /* Pad out mask when not provided */ + if (mask_len + strlen(token) > XATTR_SIZE_MAX) + return -1; + + data_len = strlen(rindex(token, ':')); + sprintf(&mask[mask_len], "ffff:ff:"); + mask_len += 8; + memset(&mask[mask_len], 'f', data_len - 1); + mask_len += data_len; + mask[mask_len - 1] = ','; + token = strsep(&str, ","); + continue; + } + + if (mask_len + strlen(slash + 1) > XATTR_SIZE_MAX) + return -1; + + strcpy(&mask[mask_len], slash + 1); + mask_len += strlen(slash + 1) + 1; + mask[mask_len - 1] = ','; + + *slash = '/'; + token = strsep(&str, ","); + } + key[key_len - 1] = '\0'; + mask[mask_len - 1] = '\0'; + + nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS); + err = flower_parse_enc_opt_part(key, n); + if (err) + return err; + addattr_nest_end(n, nest); + + nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK); + err = flower_parse_enc_opt_part(mask, n); + if (err) + return err; + addattr_nest_end(n, nest); + + return 0; +} + static int flower_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) { @@ -581,6 +805,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, struct rtattr *tail; __be16 eth_type = TC_H_MIN(t->tcm_info); __be16 vlan_ethtype = 0; + __be16 cvlan_ethtype = 0; __u8 ip_proto = 0xff; __u32 flags = 0; __u32 mtf = 0; @@ -614,6 +839,25 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, return -1; } addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); + } else if (matches(*argv, "hw_tc") == 0) { + unsigned int handle; + __u32 tc; + char *end; + + NEXT_ARG(); + tc = strtoul(*argv, &end, 0); + if (*end) { + fprintf(stderr, "Illegal TC index\n"); + return -1; + } + if (tc >= TC_QOPT_MAX_QUEUE) { + fprintf(stderr, "TC index exceeds max range\n"); + return -1; + } + handle = TC_H_MAKE(TC_H_MAJ(t->tcm_parent), + TC_H_MIN(tc + TC_H_MIN_PRIORITY)); + addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, + sizeof(handle)); } else if (matches(*argv, "ip_flags") == 0) { NEXT_ARG(); ret = flower_parse_matching_flags(*argv, @@ -624,23 +868,23 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, fprintf(stderr, "Illegal \"ip_flags\"\n"); return -1; } + } else if (matches(*argv, "verbose") == 0) { + flags |= TCA_CLS_FLAGS_VERBOSE; } else if (matches(*argv, "skip_hw") == 0) { flags |= TCA_CLS_FLAGS_SKIP_HW; } else if (matches(*argv, "skip_sw") == 0) { flags |= TCA_CLS_FLAGS_SKIP_SW; } else if (matches(*argv, "indev") == 0) { - char ifname[IFNAMSIZ] = {}; - NEXT_ARG(); - strncpy(ifname, *argv, sizeof(ifname) - 1); - addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname); + if (check_ifname(*argv)) + invarg("\"indev\" not a valid ifname", *argv); + addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, *argv); } else if (matches(*argv, "vlan_id") == 0) { __u16 vid; NEXT_ARG(); - if (eth_type != htons(ETH_P_8021Q)) { - fprintf(stderr, - "Can't set \"vlan_id\" if ethertype isn't 802.1Q\n"); + if (!eth_type_vlan(eth_type)) { + fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n"); return -1; } ret = get_u16(&vid, *argv, 10); @@ -653,9 +897,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, __u8 vlan_prio; NEXT_ARG(); - if (eth_type != htons(ETH_P_8021Q)) { - fprintf(stderr, - "Can't set \"vlan_prio\" if ethertype isn't 802.1Q\n"); + if (!eth_type_vlan(eth_type)) { + fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n"); return -1; } ret = get_u8(&vlan_prio, *argv, 10); @@ -672,6 +915,106 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, &vlan_ethtype, n); if (ret < 0) return -1; + } else if (matches(*argv, "cvlan_id") == 0) { + __u16 vid; + + NEXT_ARG(); + if (!eth_type_vlan(vlan_ethtype)) { + fprintf(stderr, "Can't set \"cvlan_id\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n"); + return -1; + } + ret = get_u16(&vid, *argv, 10); + if (ret < 0 || vid & ~0xfff) { + fprintf(stderr, "Illegal \"cvlan_id\"\n"); + return -1; + } + addattr16(n, MAX_MSG, TCA_FLOWER_KEY_CVLAN_ID, vid); + } else if (matches(*argv, "cvlan_prio") == 0) { + __u8 cvlan_prio; + + NEXT_ARG(); + if (!eth_type_vlan(vlan_ethtype)) { + fprintf(stderr, "Can't set \"cvlan_prio\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n"); + return -1; + } + ret = get_u8(&cvlan_prio, *argv, 10); + if (ret < 0 || cvlan_prio & ~0x7) { + fprintf(stderr, "Illegal \"cvlan_prio\"\n"); + return -1; + } + addattr8(n, MAX_MSG, + TCA_FLOWER_KEY_CVLAN_PRIO, cvlan_prio); + } else if (matches(*argv, "cvlan_ethtype") == 0) { + NEXT_ARG(); + ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype, + TCA_FLOWER_KEY_CVLAN_ETH_TYPE, + &cvlan_ethtype, n); + if (ret < 0) + return -1; + } else if (matches(*argv, "mpls_label") == 0) { + __u32 label; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_MPLS_UC) && + eth_type != htons(ETH_P_MPLS_MC)) { + fprintf(stderr, + "Can't set \"mpls_label\" if ethertype isn't MPLS\n"); + return -1; + } + ret = get_u32(&label, *argv, 10); + if (ret < 0 || label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) { + fprintf(stderr, "Illegal \"mpls_label\"\n"); + return -1; + } + addattr32(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_LABEL, label); + } else if (matches(*argv, "mpls_tc") == 0) { + __u8 tc; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_MPLS_UC) && + eth_type != htons(ETH_P_MPLS_MC)) { + fprintf(stderr, + "Can't set \"mpls_tc\" if ethertype isn't MPLS\n"); + return -1; + } + ret = get_u8(&tc, *argv, 10); + if (ret < 0 || tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) { + fprintf(stderr, "Illegal \"mpls_tc\"\n"); + return -1; + } + addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_TC, tc); + } else if (matches(*argv, "mpls_bos") == 0) { + __u8 bos; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_MPLS_UC) && + eth_type != htons(ETH_P_MPLS_MC)) { + fprintf(stderr, + "Can't set \"mpls_bos\" if ethertype isn't MPLS\n"); + return -1; + } + ret = get_u8(&bos, *argv, 10); + if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) { + fprintf(stderr, "Illegal \"mpls_bos\"\n"); + return -1; + } + addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_BOS, bos); + } else if (matches(*argv, "mpls_ttl") == 0) { + __u8 ttl; + + NEXT_ARG(); + if (eth_type != htons(ETH_P_MPLS_UC) && + eth_type != htons(ETH_P_MPLS_MC)) { + fprintf(stderr, + "Can't set \"mpls_ttl\" if ethertype isn't MPLS\n"); + return -1; + } + ret = get_u8(&ttl, *argv, 10); + if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) { + fprintf(stderr, "Illegal \"mpls_ttl\"\n"); + return -1; + } + addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_TTL, ttl); } else if (matches(*argv, "dst_mac") == 0) { NEXT_ARG(); ret = flower_parse_eth_addr(*argv, @@ -694,7 +1037,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, } } else if (matches(*argv, "ip_proto") == 0) { NEXT_ARG(); - ret = flower_parse_ip_proto(*argv, vlan_ethtype ? + ret = flower_parse_ip_proto(*argv, cvlan_ethtype ? + cvlan_ethtype : vlan_ethtype ? vlan_ethtype : eth_type, TCA_FLOWER_KEY_IP_PROTO, &ip_proto, n); @@ -724,7 +1068,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, } } else if (matches(*argv, "dst_ip") == 0) { NEXT_ARG(); - ret = flower_parse_ip_addr(*argv, vlan_ethtype ? + ret = flower_parse_ip_addr(*argv, cvlan_ethtype ? + cvlan_ethtype : vlan_ethtype ? vlan_ethtype : eth_type, TCA_FLOWER_KEY_IPV4_DST, TCA_FLOWER_KEY_IPV4_DST_MASK, @@ -737,7 +1082,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, } } else if (matches(*argv, "src_ip") == 0) { NEXT_ARG(); - ret = flower_parse_ip_addr(*argv, vlan_ethtype ? + ret = flower_parse_ip_addr(*argv, cvlan_ethtype ? + cvlan_ethtype : vlan_ethtype ? vlan_ethtype : eth_type, TCA_FLOWER_KEY_IPV4_SRC, TCA_FLOWER_KEY_IPV4_SRC_MASK, @@ -883,6 +1229,33 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, fprintf(stderr, "Illegal \"enc_dst_port\"\n"); return -1; } + } else if (matches(*argv, "enc_tos") == 0) { + NEXT_ARG(); + ret = flower_parse_ip_tos_ttl(*argv, + TCA_FLOWER_KEY_ENC_IP_TOS, + TCA_FLOWER_KEY_ENC_IP_TOS_MASK, + n); + if (ret < 0) { + fprintf(stderr, "Illegal \"enc_tos\"\n"); + return -1; + } + } else if (matches(*argv, "enc_ttl") == 0) { + NEXT_ARG(); + ret = flower_parse_ip_tos_ttl(*argv, + TCA_FLOWER_KEY_ENC_IP_TTL, + TCA_FLOWER_KEY_ENC_IP_TTL_MASK, + n); + if (ret < 0) { + fprintf(stderr, "Illegal \"enc_ttl\"\n"); + return -1; + } + } else if (matches(*argv, "geneve_opts") == 0) { + NEXT_ARG(); + ret = flower_parse_enc_opts(*argv, n); + if (ret < 0) { + fprintf(stderr, "Illegal \"geneve_opts\"\n"); + return -1; + } } else if (matches(*argv, "action") == 0) { NEXT_ARG(); ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); @@ -951,89 +1324,106 @@ static int __mask_bits(char *addr, size_t len) return bits; } -static void flower_print_eth_addr(FILE *f, char *name, - struct rtattr *addr_attr, +static void flower_print_eth_addr(char *name, struct rtattr *addr_attr, struct rtattr *mask_attr) { + SPRINT_BUF(namefrm); + SPRINT_BUF(out); SPRINT_BUF(b1); + size_t done; int bits; if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN) return; - fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, - 0, b1, sizeof(b1))); - if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN) - return; - bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); - if (bits < 0) - fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, - 0, b1, sizeof(b1))); - else if (bits < ETH_ALEN * 8) - fprintf(f, "/%d", bits); + done = sprintf(out, "%s", + ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, + 0, b1, sizeof(b1))); + if (mask_attr && RTA_PAYLOAD(mask_attr) == ETH_ALEN) { + bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); + if (bits < 0) + sprintf(out + done, "/%s", + ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, + 0, b1, sizeof(b1))); + else if (bits < ETH_ALEN * 8) + sprintf(out + done, "/%d", bits); + } + + sprintf(namefrm, "\n %s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); } -static void flower_print_eth_type(FILE *f, __be16 *p_eth_type, +static void flower_print_eth_type(__be16 *p_eth_type, struct rtattr *eth_type_attr) { + SPRINT_BUF(out); __be16 eth_type; if (!eth_type_attr) return; eth_type = rta_getattr_u16(eth_type_attr); - fprintf(f, "\n eth_type "); if (eth_type == htons(ETH_P_IP)) - fprintf(f, "ipv4"); + sprintf(out, "ipv4"); else if (eth_type == htons(ETH_P_IPV6)) - fprintf(f, "ipv6"); + sprintf(out, "ipv6"); else if (eth_type == htons(ETH_P_ARP)) - fprintf(f, "arp"); + sprintf(out, "arp"); else if (eth_type == htons(ETH_P_RARP)) - fprintf(f, "rarp"); + sprintf(out, "rarp"); else - fprintf(f, "%04x", ntohs(eth_type)); + sprintf(out, "%04x", ntohs(eth_type)); + + print_string(PRINT_ANY, "eth_type", "\n eth_type %s", out); *p_eth_type = eth_type; } -static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto, +static void flower_print_ip_proto(__u8 *p_ip_proto, struct rtattr *ip_proto_attr) { + SPRINT_BUF(out); __u8 ip_proto; if (!ip_proto_attr) return; ip_proto = rta_getattr_u8(ip_proto_attr); - fprintf(f, "\n ip_proto "); if (ip_proto == IPPROTO_TCP) - fprintf(f, "tcp"); + sprintf(out, "tcp"); else if (ip_proto == IPPROTO_UDP) - fprintf(f, "udp"); + sprintf(out, "udp"); else if (ip_proto == IPPROTO_SCTP) - fprintf(f, "sctp"); + sprintf(out, "sctp"); else if (ip_proto == IPPROTO_ICMP) - fprintf(f, "icmp"); + sprintf(out, "icmp"); else if (ip_proto == IPPROTO_ICMPV6) - fprintf(f, "icmpv6"); + sprintf(out, "icmpv6"); else - fprintf(f, "%02x", ip_proto); + sprintf(out, "%02x", ip_proto); + + print_string(PRINT_ANY, "ip_proto", "\n ip_proto %s", out); *p_ip_proto = ip_proto; } -static void flower_print_ip_attr(FILE *f, char *name, - struct rtattr *key_attr, +static void flower_print_ip_attr(const char *name, struct rtattr *key_attr, struct rtattr *mask_attr) { + SPRINT_BUF(namefrm); + SPRINT_BUF(out); + size_t done; + if (!key_attr) return; - fprintf(f, "\n %s %x", name, rta_getattr_u8(key_attr)); - if (!mask_attr) - return; - fprintf(f, "/%x", rta_getattr_u8(mask_attr)); + done = sprintf(out, "0x%x", rta_getattr_u8(key_attr)); + if (mask_attr) + sprintf(out + done, "/%x", rta_getattr_u8(mask_attr)); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + sprintf(namefrm, "%s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); } -static void flower_print_matching_flags(FILE *f, char *name, +static void flower_print_matching_flags(char *name, enum flower_matching_flags type, struct rtattr *attr, struct rtattr *mask_attr) @@ -1053,20 +1443,28 @@ static void flower_print_matching_flags(FILE *f, char *name, if (type != flags_str[i].type) continue; if (mtf_mask & flags_str[i].flag) { - if (++count == 1) - fprintf(f, "\n %s ", name); - else - fprintf(f, "/"); + if (++count == 1) { + print_string(PRINT_FP, NULL, "\n %s ", name); + open_json_object(name); + } else { + print_string(PRINT_FP, NULL, "/", NULL); + } + print_bool(PRINT_JSON, flags_str[i].string, NULL, + mtf & flags_str[i].flag); if (mtf & flags_str[i].flag) - fprintf(f, "%s", flags_str[i].string); + print_string(PRINT_FP, NULL, "%s", + flags_str[i].string); else - fprintf(f, "no%s", flags_str[i].string); + print_string(PRINT_FP, NULL, "no%s", + flags_str[i].string); } } + if (count) + close_json_object(); } -static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, +static void flower_print_ip_addr(char *name, __be16 eth_type, struct rtattr *addr4_attr, struct rtattr *mask4_attr, struct rtattr *addr6_attr, @@ -1074,6 +1472,9 @@ static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, { struct rtattr *addr_attr; struct rtattr *mask_attr; + SPRINT_BUF(namefrm); + SPRINT_BUF(out); + size_t done; int family; size_t len; int bits; @@ -1093,56 +1494,198 @@ static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, } if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) return; - fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr)); if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) return; + done = sprintf(out, "%s", rt_addr_n2a_rta(family, addr_attr)); bits = __mask_bits(RTA_DATA(mask_attr), len); if (bits < 0) - fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr)); + sprintf(out + done, "/%s", rt_addr_n2a_rta(family, mask_attr)); else if (bits < len * 8) - fprintf(f, "/%d", bits); + sprintf(out + done, "/%d", bits); + + sprintf(namefrm, "\n %s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); } -static void flower_print_ip4_addr(FILE *f, char *name, - struct rtattr *addr_attr, +static void flower_print_ip4_addr(char *name, struct rtattr *addr_attr, struct rtattr *mask_attr) { - return flower_print_ip_addr(f, name, htons(ETH_P_IP), + return flower_print_ip_addr(name, htons(ETH_P_IP), addr_attr, mask_attr, 0, 0); } -static void flower_print_port(FILE *f, char *name, struct rtattr *attr) +static void flower_print_port(char *name, struct rtattr *attr) { - if (attr) - fprintf(f, "\n %s %d", name, rta_getattr_be16(attr)); + SPRINT_BUF(namefrm); + + if (!attr) + return; + + sprintf(namefrm,"\n %s %%u", name); + print_hu(PRINT_ANY, name, namefrm, rta_getattr_be16(attr)); } -static void flower_print_tcp_flags(FILE *f, char *name, - struct rtattr *flags_attr, - struct rtattr *mask_attr) +static void flower_print_port_range(char *name, struct rtattr *min_attr, + struct rtattr *max_attr) +{ + if (!min_attr || !max_attr) + return; + + if (is_json_context()) { + open_json_object(name); + print_hu(PRINT_JSON, "start", NULL, rta_getattr_be16(min_attr)); + print_hu(PRINT_JSON, "end", NULL, rta_getattr_be16(max_attr)); + close_json_object(); + } else { + SPRINT_BUF(namefrm); + SPRINT_BUF(out); + size_t done; + + done = sprintf(out, "%u", rta_getattr_be16(min_attr)); + sprintf(out + done, "-%u", rta_getattr_be16(max_attr)); + sprintf(namefrm, "\n %s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); + } +} + +static void flower_print_tcp_flags(const char *name, struct rtattr *flags_attr, + struct rtattr *mask_attr) { + SPRINT_BUF(namefrm); + SPRINT_BUF(out); + size_t done; + if (!flags_attr) return; - fprintf(f, "\n %s %x", name, rta_getattr_be16(flags_attr)); - if (!mask_attr) + + done = sprintf(out, "0x%x", rta_getattr_be16(flags_attr)); + if (mask_attr) + sprintf(out + done, "/%x", rta_getattr_be16(mask_attr)); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + sprintf(namefrm, "%s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); +} + + +static void flower_print_key_id(const char *name, struct rtattr *attr) +{ + SPRINT_BUF(namefrm); + + if (!attr) return; - fprintf(f, "/%x", rta_getattr_be16(mask_attr)); + + sprintf(namefrm,"\n %s %%u", name); + print_uint(PRINT_ANY, name, namefrm, rta_getattr_be32(attr)); } +static void flower_print_geneve_opts(const char *name, struct rtattr *attr, + char *strbuf) +{ + struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1]; + int ii, data_len, offset = 0, slen = 0; + struct rtattr *i = RTA_DATA(attr); + int rem = RTA_PAYLOAD(attr); + __u8 type, data_r[rem]; + char data[rem * 2 + 1]; + __u16 class; + + open_json_array(PRINT_JSON, name); + while (rem) { + parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, i, rem); + class = rta_getattr_be16(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]); + type = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]); + data_len = RTA_PAYLOAD(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]); + hexstring_n2a(RTA_DATA(tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]), + data_len, data, sizeof(data)); + hex2mem(data, data_r, data_len); + offset += data_len + 20; + rem -= data_len + 20; + i = RTA_DATA(attr) + offset; + + open_json_object(NULL); + print_uint(PRINT_JSON, "class", NULL, class); + print_uint(PRINT_JSON, "type", NULL, type); + open_json_array(PRINT_JSON, "data"); + for (ii = 0; ii < data_len; ii++) + print_uint(PRINT_JSON, NULL, NULL, data_r[ii]); + close_json_array(PRINT_JSON, "data"); + close_json_object(); + + slen += sprintf(strbuf + slen, "%04x:%02x:%s", + class, type, data); + if (rem) + slen += sprintf(strbuf + slen, ","); + } + close_json_array(PRINT_JSON, name); +} -static void flower_print_key_id(FILE *f, const char *name, - struct rtattr *attr) +static void flower_print_geneve_parts(const char *name, struct rtattr *attr, + char *key, char *mask) { - if (attr) - fprintf(f, "\n %s %d", name, rta_getattr_be32(attr)); + char *namefrm = "\n geneve_opt %s"; + char *key_token, *mask_token, *out; + int len; + + out = malloc(RTA_PAYLOAD(attr) * 4 + 3); + if (!out) + return; + + len = 0; + key_token = strsep(&key, ","); + mask_token = strsep(&mask, ","); + while (key_token) { + len += sprintf(&out[len], "%s/%s,", key_token, mask_token); + mask_token = strsep(&mask, ","); + key_token = strsep(&key, ","); + } + + out[len - 1] = '\0'; + print_string(PRINT_FP, name, namefrm, out); + free(out); } -static void flower_print_masked_u8(FILE *f, const char *name, - struct rtattr *attr, +static void flower_print_enc_opts(const char *name, struct rtattr *attr, + struct rtattr *mask_attr) +{ + struct rtattr *key_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1]; + struct rtattr *msk_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1]; + char *key, *msk; + + if (!attr) + return; + + key = malloc(RTA_PAYLOAD(attr) * 2 + 1); + if (!key) + return; + + msk = malloc(RTA_PAYLOAD(attr) * 2 + 1); + if (!msk) + goto err_key_free; + + parse_rtattr_nested(key_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, attr); + flower_print_geneve_opts("geneve_opt_key", + key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key); + + parse_rtattr_nested(msk_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, mask_attr); + flower_print_geneve_opts("geneve_opt_mask", + msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk); + + flower_print_geneve_parts(name, attr, key, msk); + + free(msk); +err_key_free: + free(key); +} + +static void flower_print_masked_u8(const char *name, struct rtattr *attr, struct rtattr *mask_attr, const char *(*value_to_str)(__u8 value)) { const char *value_str = NULL; __u8 value, mask; + SPRINT_BUF(namefrm); + SPRINT_BUF(out); + size_t done; if (!attr) return; @@ -1152,22 +1695,39 @@ static void flower_print_masked_u8(FILE *f, const char *name, if (mask == UINT8_MAX && value_to_str) value_str = value_to_str(value); - fprintf(f, "\n %s ", name); - if (value_str) - fputs(value_str, f); + done = sprintf(out, "%s", value_str); else - fprintf(f, "%d", value); + done = sprintf(out, "%d", value); if (mask != UINT8_MAX) - fprintf(f, "/%d", mask); + sprintf(out + done, "/%d", mask); + + sprintf(namefrm,"\n %s %%s", name); + print_string(PRINT_ANY, name, namefrm, out); +} + +static void flower_print_u8(const char *name, struct rtattr *attr) +{ + flower_print_masked_u8(name, attr, NULL, NULL); } -static void flower_print_arp_op(FILE *f, const char *name, +static void flower_print_u32(const char *name, struct rtattr *attr) +{ + SPRINT_BUF(namefrm); + + if (!attr) + return; + + sprintf(namefrm,"\n %s %%u", name); + print_uint(PRINT_ANY, name, namefrm, rta_getattr_u32(attr)); +} + +static void flower_print_arp_op(const char *name, struct rtattr *op_attr, struct rtattr *mask_attr) { - flower_print_masked_u8(f, name, op_attr, mask_attr, + flower_print_masked_u8(name, op_attr, mask_attr, flower_print_arp_op_to_name); } @@ -1175,6 +1735,7 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) { struct rtattr *tb[TCA_FLOWER_MAX + 1]; + __be16 min_port_type, max_port_type; int nl_type, nl_mask_type; __be16 eth_type = 0; __u8 ip_proto = 0xff; @@ -1185,53 +1746,102 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt); if (handle) - fprintf(f, "handle 0x%x ", handle); + print_uint(PRINT_ANY, "handle", "handle 0x%x ", handle); if (tb[TCA_FLOWER_CLASSID]) { - SPRINT_BUF(b1); - fprintf(f, "classid %s ", - sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), - b1)); + __u32 h = rta_getattr_u32(tb[TCA_FLOWER_CLASSID]); + + if (TC_H_MIN(h) < TC_H_MIN_PRIORITY || + TC_H_MIN(h) > (TC_H_MIN_PRIORITY + TC_QOPT_MAX_QUEUE - 1)) { + SPRINT_BUF(b1); + print_string(PRINT_ANY, "classid", "classid %s ", + sprint_tc_classid(h, b1)); + } else { + print_uint(PRINT_ANY, "hw_tc", "hw_tc %u ", + TC_H_MIN(h) - TC_H_MIN_PRIORITY); + } } if (tb[TCA_FLOWER_INDEV]) { struct rtattr *attr = tb[TCA_FLOWER_INDEV]; - fprintf(f, "\n indev %s", rta_getattr_str(attr)); + print_string(PRINT_ANY, "indev", "\n indev %s", + rta_getattr_str(attr)); } + open_json_object("keys"); + if (tb[TCA_FLOWER_KEY_VLAN_ID]) { struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID]; - fprintf(f, "\n vlan_id %d", rta_getattr_u16(attr)); + print_uint(PRINT_ANY, "vlan_id", "\n vlan_id %u", + rta_getattr_u16(attr)); } if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_PRIO]; - fprintf(f, "\n vlan_prio %d", rta_getattr_u8(attr)); + print_uint(PRINT_ANY, "vlan_prio", "\n vlan_prio %d", + rta_getattr_u8(attr)); + } + + if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { + SPRINT_BUF(buf); + struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]; + + print_string(PRINT_ANY, "vlan_ethtype", "\n vlan_ethtype %s", + ll_proto_n2a(rta_getattr_u16(attr), + buf, sizeof(buf))); + } + + if (tb[TCA_FLOWER_KEY_CVLAN_ID]) { + struct rtattr *attr = tb[TCA_FLOWER_KEY_CVLAN_ID]; + + print_uint(PRINT_ANY, "cvlan_id", "\n cvlan_id %u", + rta_getattr_u16(attr)); + } + + if (tb[TCA_FLOWER_KEY_CVLAN_PRIO]) { + struct rtattr *attr = tb[TCA_FLOWER_KEY_CVLAN_PRIO]; + + print_uint(PRINT_ANY, "cvlan_prio", "\n cvlan_prio %d", + rta_getattr_u8(attr)); } - flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], + if (tb[TCA_FLOWER_KEY_CVLAN_ETH_TYPE]) { + SPRINT_BUF(buf); + struct rtattr *attr = tb[TCA_FLOWER_KEY_CVLAN_ETH_TYPE]; + + print_string(PRINT_ANY, "cvlan_ethtype", "\n cvlan_ethtype %s", + ll_proto_n2a(rta_getattr_u16(attr), + buf, sizeof(buf))); + } + + flower_print_eth_addr("dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], tb[TCA_FLOWER_KEY_ETH_DST_MASK]); - flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], + flower_print_eth_addr("src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], tb[TCA_FLOWER_KEY_ETH_SRC_MASK]); - flower_print_eth_type(f, ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); - flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); + flower_print_eth_type(ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); + flower_print_ip_proto(&ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); - flower_print_ip_attr(f, "ip_tos", tb[TCA_FLOWER_KEY_IP_TOS], + flower_print_ip_attr("ip_tos", tb[TCA_FLOWER_KEY_IP_TOS], tb[TCA_FLOWER_KEY_IP_TOS_MASK]); - flower_print_ip_attr(f, "ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL], + flower_print_ip_attr("ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL], tb[TCA_FLOWER_KEY_IP_TTL_MASK]); - flower_print_ip_addr(f, "dst_ip", eth_type, + flower_print_u32("mpls_label", tb[TCA_FLOWER_KEY_MPLS_LABEL]); + flower_print_u8("mpls_tc", tb[TCA_FLOWER_KEY_MPLS_TC]); + flower_print_u8("mpls_bos", tb[TCA_FLOWER_KEY_MPLS_BOS]); + flower_print_u8("mpls_ttl", tb[TCA_FLOWER_KEY_MPLS_TTL]); + + flower_print_ip_addr("dst_ip", eth_type, tb[TCA_FLOWER_KEY_IPV4_DST], tb[TCA_FLOWER_KEY_IPV4_DST_MASK], tb[TCA_FLOWER_KEY_IPV6_DST], tb[TCA_FLOWER_KEY_IPV6_DST_MASK]); - flower_print_ip_addr(f, "src_ip", eth_type, + flower_print_ip_addr("src_ip", eth_type, tb[TCA_FLOWER_KEY_IPV4_SRC], tb[TCA_FLOWER_KEY_IPV4_SRC_MASK], tb[TCA_FLOWER_KEY_IPV6_SRC], @@ -1239,12 +1849,22 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_DST); if (nl_type >= 0) - flower_print_port(f, "dst_port", tb[nl_type]); + flower_print_port("dst_port", tb[nl_type]); nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_SRC); if (nl_type >= 0) - flower_print_port(f, "src_port", tb[nl_type]); + flower_print_port("src_port", tb[nl_type]); - flower_print_tcp_flags(f, "tcp_flags", tb[TCA_FLOWER_KEY_TCP_FLAGS], + if (!flower_port_range_attr_type(ip_proto, FLOWER_ENDPOINT_DST, + &min_port_type, &max_port_type)) + flower_print_port_range("dst_port", + tb[min_port_type], tb[max_port_type]); + + if (!flower_port_range_attr_type(ip_proto, FLOWER_ENDPOINT_SRC, + &min_port_type, &max_port_type)) + flower_print_port_range("src_port", + tb[min_port_type], tb[max_port_type]); + + flower_print_tcp_flags("tcp_flags", tb[TCA_FLOWER_KEY_TCP_FLAGS], tb[TCA_FLOWER_KEY_TCP_FLAGS_MASK]); nl_type = flower_icmp_attr_type(eth_type, ip_proto, @@ -1252,7 +1872,7 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, FLOWER_ICMP_FIELD_TYPE); if (nl_type >= 0 && nl_mask_type >= 0) - flower_print_masked_u8(f, "icmp_type", tb[nl_type], + flower_print_masked_u8("icmp_type", tb[nl_type], tb[nl_mask_type], NULL); nl_type = flower_icmp_attr_type(eth_type, ip_proto, @@ -1260,21 +1880,21 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, FLOWER_ICMP_FIELD_CODE); if (nl_type >= 0 && nl_mask_type >= 0) - flower_print_masked_u8(f, "icmp_code", tb[nl_type], + flower_print_masked_u8("icmp_code", tb[nl_type], tb[nl_mask_type], NULL); - flower_print_ip4_addr(f, "arp_sip", tb[TCA_FLOWER_KEY_ARP_SIP], + flower_print_ip4_addr("arp_sip", tb[TCA_FLOWER_KEY_ARP_SIP], tb[TCA_FLOWER_KEY_ARP_SIP_MASK]); - flower_print_ip4_addr(f, "arp_tip", tb[TCA_FLOWER_KEY_ARP_TIP], + flower_print_ip4_addr("arp_tip", tb[TCA_FLOWER_KEY_ARP_TIP], tb[TCA_FLOWER_KEY_ARP_TIP_MASK]); - flower_print_arp_op(f, "arp_op", tb[TCA_FLOWER_KEY_ARP_OP], + flower_print_arp_op("arp_op", tb[TCA_FLOWER_KEY_ARP_OP], tb[TCA_FLOWER_KEY_ARP_OP_MASK]); - flower_print_eth_addr(f, "arp_sha", tb[TCA_FLOWER_KEY_ARP_SHA], + flower_print_eth_addr("arp_sha", tb[TCA_FLOWER_KEY_ARP_SHA], tb[TCA_FLOWER_KEY_ARP_SHA_MASK]); - flower_print_eth_addr(f, "arp_tha", tb[TCA_FLOWER_KEY_ARP_THA], + flower_print_eth_addr("arp_tha", tb[TCA_FLOWER_KEY_ARP_THA], tb[TCA_FLOWER_KEY_ARP_THA_MASK]); - flower_print_ip_addr(f, "enc_dst_ip", + flower_print_ip_addr("enc_dst_ip", tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ? htons(ETH_P_IP) : htons(ETH_P_IPV6), tb[TCA_FLOWER_KEY_ENC_IPV4_DST], @@ -1282,7 +1902,7 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, tb[TCA_FLOWER_KEY_ENC_IPV6_DST], tb[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]); - flower_print_ip_addr(f, "enc_src_ip", + flower_print_ip_addr("enc_src_ip", tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] ? htons(ETH_P_IP) : htons(ETH_P_IPV6), tb[TCA_FLOWER_KEY_ENC_IPV4_SRC], @@ -1290,29 +1910,43 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, tb[TCA_FLOWER_KEY_ENC_IPV6_SRC], tb[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]); - flower_print_key_id(f, "enc_key_id", - tb[TCA_FLOWER_KEY_ENC_KEY_ID]); + flower_print_key_id("enc_key_id", tb[TCA_FLOWER_KEY_ENC_KEY_ID]); - flower_print_port(f, "enc_dst_port", - tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); + flower_print_port("enc_dst_port", tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); - flower_print_matching_flags(f, "ip_flags", - FLOWER_IP_FLAGS, + flower_print_ip_attr("enc_tos", tb[TCA_FLOWER_KEY_ENC_IP_TOS], + tb[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]); + flower_print_ip_attr("enc_ttl", tb[TCA_FLOWER_KEY_ENC_IP_TTL], + tb[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]); + flower_print_enc_opts("enc_opt", tb[TCA_FLOWER_KEY_ENC_OPTS], + tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); + + flower_print_matching_flags("ip_flags", FLOWER_IP_FLAGS, tb[TCA_FLOWER_KEY_FLAGS], tb[TCA_FLOWER_KEY_FLAGS_MASK]); + close_json_object(); + if (tb[TCA_FLOWER_FLAGS]) { __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); if (flags & TCA_CLS_FLAGS_SKIP_HW) - fprintf(f, "\n skip_hw"); + print_bool(PRINT_ANY, "skip_hw", "\n skip_hw", true); if (flags & TCA_CLS_FLAGS_SKIP_SW) - fprintf(f, "\n skip_sw"); + print_bool(PRINT_ANY, "skip_sw", "\n skip_sw", true); - if (flags & TCA_CLS_FLAGS_IN_HW) - fprintf(f, "\n in_hw"); + if (flags & TCA_CLS_FLAGS_IN_HW) { + print_bool(PRINT_ANY, "in_hw", "\n in_hw", true); + + if (tb[TCA_FLOWER_IN_HW_COUNT]) { + __u32 count = rta_getattr_u32(tb[TCA_FLOWER_IN_HW_COUNT]); + + print_uint(PRINT_ANY, "in_hw_count", + " in_hw_count %u", count); + } + } else if (flags & TCA_CLS_FLAGS_NOT_IN_HW) - fprintf(f, "\n not_in_hw"); + print_bool(PRINT_ANY, "not_in_hw", "\n not_in_hw", true); } if (tb[TCA_FLOWER_ACT])