From b485126fd0a84a09f3d61bb4d634011be92fb6a4 Mon Sep 17 00:00:00 2001 From: Andrea Claudi Date: Wed, 29 May 2019 18:28:17 +0200 Subject: [PATCH] tc: flower: Add support for QinQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1615928 Upstream Status: iproute2.git commit 1f0a5dfd388cd commit 1f0a5dfd388cd5c25f6a24247667e04b2346e568 Author: Jianbo Liu Date: Sat Jun 30 10:01:33 2018 +0000 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 --- man/man8/tc-flower.8 | 23 ++++++++++ tc/f_flower.c | 103 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 114 insertions(+), 12 deletions(-) diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index 276b5271cf013..8be8882592eaa 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 @@ -145,6 +151,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 43102c86d1597..634bb81af7dbb 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" @@ -131,15 +134,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; } @@ -762,6 +771,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; @@ -839,9 +849,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); @@ -854,9 +863,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); @@ -873,6 +881,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; @@ -959,7 +1003,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); @@ -989,7 +1034,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, @@ -1002,7 +1048,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, @@ -1678,6 +1725,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], -- 2.20.1