Currently the inner VLAN header is ignored when using the TC data-path. As TC flower supports QinQ, now we can offload the rules to match on both outer and inner VLAN headers.
Signed-off-by: Jianbo Liu <jian...@mellanox.com> Reviewed-by: Roi Dayan <r...@mellanox.com> --- acinclude.m4 | 6 ++--- include/linux/pkt_cls.h | 6 ++++- lib/dpif-netlink.c | 2 +- lib/netdev-tc-offloads.c | 57 ++++++++++++++++++++++++++++++++++++-------- lib/tc.c | 62 +++++++++++++++++++++++++++++++++++++++++------- lib/tc.h | 7 +++--- 6 files changed, 113 insertions(+), 27 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 991a627..d6e0d33 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -178,10 +178,10 @@ dnl Configure Linux tc compat. AC_DEFUN([OVS_CHECK_LINUX_TC], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([#include <linux/pkt_cls.h>], [ - int x = TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST; + int x = TCA_FLOWER_KEY_CVLAN_PRIO; ])], - [AC_DEFINE([HAVE_TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST], [1], - [Define to 1 if TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST is avaiable.])]) + [AC_DEFINE([HAVE_TCA_FLOWER_KEY_CVLAN_PRIO], [1], + [Define to 1 if TCA_FLOWER_KEY_CVLAN_PRIO is avaiable.])]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([#include <linux/tc_act/tc_vlan.h>], [ diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index 60976f3..442323d 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -1,7 +1,7 @@ #ifndef __LINUX_PKT_CLS_WRAPPER_H #define __LINUX_PKT_CLS_WRAPPER_H 1 -#if defined(__KERNEL__) || defined(HAVE_TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST) +#if defined(__KERNEL__) || defined(HAVE_TCA_FLOWER_KEY_CVLAN_PRIO) #include_next <linux/pkt_cls.h> #else @@ -196,6 +196,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/lib/dpif-netlink.c b/lib/dpif-netlink.c index aa9bbd9..62c7120 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1709,7 +1709,7 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match, .flow = &match->flow, .mask = &match->wc.masks, .support = { - .max_vlan_headers = 1, + .max_vlan_headers = 2, }, }; size_t offset; diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c index 3405458..272e86e 100644 --- a/lib/netdev-tc-offloads.c +++ b/lib/netdev-tc-offloads.c @@ -431,9 +431,20 @@ parse_tc_flower_to_match(struct tc_flower *flower, match_set_dl_dst_masked(match, key->dst_mac, mask->dst_mac); if (eth_type_vlan(key->eth_type)) { - match_set_dl_vlan(match, htons(key->vlan_id)); - match_set_dl_vlan_pcp(match, key->vlan_prio, 0); - match_set_dl_type(match, key->encap_eth_type, 0); + match->flow.vlans[0].tpid = key->eth_type; + match->wc.masks.vlans[0].tpid = OVS_BE16_MAX; + match_set_dl_vlan(match, htons(key->vlan_id[0]), 0); + match_set_dl_vlan_pcp(match, key->vlan_prio[0], 0); + + if (eth_type_vlan(key->encap_eth_type[0])) { + match_set_dl_vlan(match, htons(key->vlan_id[1]), 1); + match_set_dl_vlan_pcp(match, key->vlan_prio[1], 1); + match_set_dl_type(match, key->encap_eth_type[1]); + match->flow.vlans[1].tpid = key->encap_eth_type[0]; + match->wc.masks.vlans[1].tpid = OVS_BE16_MAX; + } else { + match_set_dl_type(match, key->encap_eth_type[0]); + } flow_fix_vlan_tpid(&match->flow); } else { match_set_dl_type(match, key->eth_type); @@ -954,14 +965,14 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK)) && (vid_mask || pcp_mask)) { if (vid_mask) { - flower.key.vlan_id = vlan_tci_to_vid(key->vlans[0].tci); - VLOG_DBG_RL(&rl, "vlan_id: %d\n", flower.key.vlan_id); + flower.key.vlan_id[0] = vlan_tci_to_vid(key->vlans[0].tci); + VLOG_DBG_RL(&rl, "vlan_id[0]: %d\n", flower.key.vlan_id[0]); } if (pcp_mask) { - flower.key.vlan_prio = vlan_tci_to_pcp(key->vlans[0].tci); - VLOG_DBG_RL(&rl, "vlan_prio: %d\n", flower.key.vlan_prio); + flower.key.vlan_prio[0] = vlan_tci_to_pcp(key->vlans[0].tci); + VLOG_DBG_RL(&rl, "vlan_prio[0]: %d\n", flower.key.vlan_prio[0]); } - flower.key.encap_eth_type = flower.key.eth_type; + flower.key.encap_eth_type[0] = flower.key.eth_type; flower.key.eth_type = key->vlans[0].tpid; } else if (mask->vlans[0].tci == htons(0xffff) && ntohs(key->vlans[0].tci) == 0) { @@ -970,8 +981,34 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, /* partial mask */ return EOPNOTSUPP; } - } else if (mask->vlans[1].tci) { - return EOPNOTSUPP; + } + + if (mask->vlans[1].tci) { + ovs_be16 vid_mask = mask->vlans[1].tci & htons(VLAN_VID_MASK); + ovs_be16 pcp_mask = mask->vlans[1].tci & htons(VLAN_PCP_MASK); + ovs_be16 cfi = mask->vlans[1].tci & htons(VLAN_CFI); + + if (cfi && key->vlans[1].tci & htons(VLAN_CFI) + && (!vid_mask || vid_mask == htons(VLAN_VID_MASK)) + && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK)) + && (vid_mask || pcp_mask)) { + if (vid_mask) { + flower.key.vlan_id[1] = vlan_tci_to_vid(key->vlans[1].tci); + VLOG_DBG_RL(&rl, "vlan_id[1]: %d", flower.key.vlan_id[1]); + } + if (pcp_mask) { + flower.key.vlan_prio[1] = vlan_tci_to_pcp(key->vlans[1].tci); + VLOG_DBG_RL(&rl, "vlan_prio[1]: %d", flower.key.vlan_prio[1]); + } + flower.key.encap_eth_type[1] = flower.key.encap_eth_type[0]; + flower.key.encap_eth_type[0] = key->vlans[1].tpid; + } else if (mask->vlans[1].tci == htons(0xffff) && + ntohs(key->vlans[1].tci) == 0) { + /* exact && no vlan */ + } else { + /* partial mask */ + return EOPNOTSUPP; + } } memset(mask->vlans, 0, sizeof mask->vlans); diff --git a/lib/tc.c b/lib/tc.c index 5030399..2157135 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -306,6 +306,9 @@ static const struct nl_policy tca_flower_policy[] = { .optional = true, }, [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, }, + [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, }, }; static void @@ -332,21 +335,44 @@ nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower) static void nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower) { + ovs_be16 encap_ethtype; + if (!eth_type_vlan(flower->key.eth_type)) { return; } - flower->key.encap_eth_type = + flower->key.encap_eth_type[0] = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]); if (attrs[TCA_FLOWER_KEY_VLAN_ID]) { - flower->key.vlan_id = + flower->key.vlan_id[0] = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]); } if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) { - flower->key.vlan_prio = + flower->key.vlan_prio[0] = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]); } + + if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { + return; + } + + encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); + if (!eth_type_vlan(encap_ethtype)) { + return; + } + + flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0]; + flower->key.encap_eth_type[0] = encap_ethtype; + + if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) { + flower->key.vlan_id[1] = + nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]); + } + if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) { + flower->key.vlan_prio[1] = + nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]); + } } static void @@ -1577,6 +1603,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) uint16_t host_eth_type = ntohs(flower->key.eth_type); bool is_vlan = eth_type_vlan(flower->key.eth_type); + bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]); int err; /* need to parse acts first as some acts require changing the matching @@ -1587,7 +1614,11 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) } if (is_vlan) { - host_eth_type = ntohs(flower->key.encap_eth_type); + if (is_qinq) { + host_eth_type = ntohs(flower->key.encap_eth_type[1]); + } else { + host_eth_type = ntohs(flower->key.encap_eth_type[0]); + } } FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST); @@ -1631,15 +1662,28 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type); if (is_vlan) { - if (flower->key.vlan_id || flower->key.vlan_prio) { + if (flower->key.vlan_id[0] || flower->key.vlan_prio[0]) { nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID, - flower->key.vlan_id); + flower->key.vlan_id[0]); nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO, - flower->key.vlan_prio); + flower->key.vlan_prio[0]); } - if (flower->key.encap_eth_type) { + if (flower->key.encap_eth_type[0]) { nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE, - flower->key.encap_eth_type); + flower->key.encap_eth_type[0]); + } + + if (is_qinq) { + if (flower->key.vlan_id[1] || flower->key.vlan_prio[1]) { + nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID, + flower->key.vlan_id[1]); + nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO, + flower->key.vlan_prio[1]); + } + if (flower->key.encap_eth_type[1]) { + nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, + flower->key.encap_eth_type[1]); + } } } diff --git a/lib/tc.h b/lib/tc.h index d954819..447d85f 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -26,6 +26,7 @@ #include "netlink-socket.h" #include "odp-netlink.h" #include "openvswitch/ofpbuf.h" +#include "openvswitch/flow.h" /* For backwards compatability with older kernels */ #ifndef TC_H_CLSACT @@ -87,10 +88,10 @@ struct tc_flower_key { ovs_be16 sctp_src; ovs_be16 sctp_dst; - uint16_t vlan_id; - uint8_t vlan_prio; + uint16_t vlan_id[FLOW_MAX_VLAN_HEADERS]; + uint8_t vlan_prio[FLOW_MAX_VLAN_HEADERS]; - ovs_be16 encap_eth_type; + ovs_be16 encap_eth_type[FLOW_MAX_VLAN_HEADERS]; uint8_t flags; uint8_t ip_ttl; -- 2.9.5 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev