From dfbec1b67fc02a5af0d5cc30328b918902f20717 Mon Sep 17 00:00:00 2001 From: Andrea Claudi Date: Mon, 25 Mar 2019 12:19:05 +0100 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=1642347 Upstream Status: iproute2.git commit 1f0a5dfd388cd Conflicts: context change due to missing commits 7638ee13c1586 ("tc: flower: support for matching MPLS labels") e28b88a464c49 ("tc: jsonify flower filter") also adjust code to use fprintf instead of print_string due to missing commit d0e720111aad2 ("ip: ipaddress.c: add support for json output") 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 | 99 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 12 deletions(-) diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index af19708d9649e..387f73f5cd2e9 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -29,6 +29,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 " } | " .BR ip_proto " { " tcp " | " udp " | " sctp " | " icmp " | " icmpv6 " | " .IR IP_PROTO " } | " .B ip_tos @@ -121,6 +127,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 ip_proto " IP_PROTO" diff --git a/tc/f_flower.c b/tc/f_flower.c index 5f5236ca523f8..40dcfbd687a20 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" @@ -126,15 +129,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; } @@ -583,6 +592,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; @@ -640,9 +650,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); @@ -655,9 +664,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); @@ -674,6 +682,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, "dst_mac") == 0) { NEXT_ARG(); ret = flower_parse_eth_addr(*argv, @@ -696,7 +740,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); @@ -726,7 +771,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, @@ -739,7 +785,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, @@ -1234,6 +1281,34 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, fprintf(f, "\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]; + + fprintf(f, "\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]; + + fprintf(f, "\n cvlan_id %u", rta_getattr_u16(attr)); + } + + if (tb[TCA_FLOWER_KEY_CVLAN_PRIO]) { + struct rtattr *attr = tb[TCA_FLOWER_KEY_CVLAN_PRIO]; + + fprintf(f, "\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]; + + fprintf(f, "\n cvlan_ethtype %s", + ll_proto_n2a(rta_getattr_u16(attr), buf, sizeof(buf))); + } + flower_print_eth_addr(f, "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], -- 2.21.0