]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
tc: flower: Add support for QinQ
authorJianbo Liu <jianbol@mellanox.com>
Sat, 30 Jun 2018 10:01:33 +0000 (10:01 +0000)
committerDavid Ahern <dsahern@gmail.com>
Sun, 15 Jul 2018 20:03:50 +0000 (13:03 -0700)
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 <jianbol@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
man/man8/tc-flower.8
tc/f_flower.c

index 4f3714b79544b33f198d711fa9006806685d43a8..bfa66d8a2e503ede72bfd4df8eee25f57c358d0e 100644 (file)
@@ -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"
index c710765179fb08ac94e04ec664fac0d30cf04931..40b402651d4af9b0dbea3d3910484c9f6d99dce5 100644 (file)
@@ -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],