From: Jianbo Liu Date: Sat, 30 Jun 2018 10:01:33 +0000 (+0000) Subject: tc: flower: Add support for QinQ X-Git-Tag: v4.19.0~101 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f0a5dfd388cd5c25f6a24247667e04b2346e568;p=thirdparty%2Fiproute2.git tc: flower: Add support for QinQ To support matching on both outer and inner vlan headers, we add new cvlan_id/cvlan_prio/cvlan_ethtype for inner vlan header. Example: # tc filter add dev eth0 protocol 802.1ad parent ffff: \ flower vlan_id 1000 vlan_ethtype 802.1q \ cvlan_id 100 cvlan_ethtype ipv4 \ action vlan pop \ action vlan pop \ action mirred egress redirect dev eth1 # tc filter show dev eth0 ingress filter protocol 802.1ad pref 1 flower chain 0 filter protocol 802.1ad pref 1 flower chain 0 handle 0x1   vlan_id 1000   vlan_ethtype 802.1Q   cvlan_id 100   cvlan_ethtype ip   eth_type ipv4   in_hw Signed-off-by: Jianbo Liu Acked-by: Jiri Pirko Signed-off-by: David Ahern --- diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index 4f3714b79..bfa66d8a2 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -34,6 +34,12 @@ flower \- flow based traffic control filter .IR PRIORITY " | " .BR vlan_ethtype " { " ipv4 " | " ipv6 " | " .IR ETH_TYPE " } | " +.B cvlan_id +.IR VID " | " +.B cvlan_prio +.IR PRIORITY " | " +.BR cvlan_ethtype " { " ipv4 " | " ipv6 " | " +.IR ETH_TYPE " } | " .B mpls_label .IR LABEL " | " .B mpls_tc @@ -139,6 +145,23 @@ Match on layer three protocol. .I VLAN_ETH_TYPE may be either .BR ipv4 ", " ipv6 +or an unsigned 16bit value in hexadecimal format. To match on QinQ packet, it must be 802.1Q or 802.1AD. +.TP +.BI cvlan_id " VID" +Match on QinQ inner vlan tag id. +.I VID +is an unsigned 12bit value in decimal format. +.TP +.BI cvlan_prio " PRIORITY" +Match on QinQ inner vlan tag priority. +.I PRIORITY +is an unsigned 3bit value in decimal format. +.TP +.BI cvlan_ethtype " VLAN_ETH_TYPE" +Match on QinQ layer three protocol. +.I VLAN_ETH_TYPE +may be either +.BR ipv4 ", " ipv6 or an unsigned 16bit value in hexadecimal format. .TP .BI mpls_label " LABEL" diff --git a/tc/f_flower.c b/tc/f_flower.c index c71076517..40b402651 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -50,6 +50,9 @@ 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" @@ -128,15 +131,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; } @@ -586,6 +595,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; @@ -663,9 +673,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, __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); @@ -678,9 +687,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); @@ -697,6 +705,42 @@ 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; @@ -783,7 +827,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); @@ -813,7 +858,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, @@ -826,7 +872,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, @@ -1376,6 +1423,38 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, 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)); + } + + 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("src_mac", tb[TCA_FLOWER_KEY_ETH_SRC],