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 <jian...@mellanox.com> Acked-by: Jiri Pirko <j...@mellanox.com> --- include/uapi/linux/pkt_cls.h | 4 ++ man/man8/tc-flower.8 | 23 ++++++++++ tc/f_flower.c | 103 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 118 insertions(+), 12 deletions(-) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index be05e66..e7f7c52 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -468,6 +468,10 @@ enum { TCA_FLOWER_KEY_IP_TTL, /* u8 */ TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */ + TCA_FLOWER_KEY_CVLAN_ID, /* be16 */ + TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */ + __TCA_FLOWER_MAX, }; diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index a561443..c684652 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -32,6 +32,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 @@ -132,6 +138,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 ba8eb66..71f2bc1 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; @@ -661,9 +671,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); @@ -676,9 +685,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); @@ -695,6 +703,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; @@ -781,7 +825,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); @@ -811,7 +856,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, @@ -824,7 +870,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, @@ -1374,6 +1421,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.9.5