From patchwork Thu Jul 4 14:28:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127584 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgS84Pykz9s4Y for ; Fri, 5 Jul 2019 00:32:56 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 508AF12B1; Thu, 4 Jul 2019 14:28:37 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 85D591169 for ; Thu, 4 Jul 2019 14:28:35 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 86C1B891 for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:32 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWos010477; Thu, 4 Jul 2019 17:28:32 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:20 +0300 Message-Id: <1562250507-20335-2-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 1/8] match: Add match_set_ct_zone_masked helper X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Sets zone in match. Signed-off-by: Paul Blakey --- include/openvswitch/match.h | 1 + lib/match.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h index 05ecee7..219de00 100644 --- a/include/openvswitch/match.h +++ b/include/openvswitch/match.h @@ -127,6 +127,7 @@ void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask) void match_set_ct_state(struct match *, uint32_t ct_state); void match_set_ct_state_masked(struct match *, uint32_t ct_state, uint32_t mask); void match_set_ct_zone(struct match *, uint16_t ct_zone); +void match_set_ct_zone_masked(struct match *match, uint16_t ct_zone, uint16_t mask); void match_set_ct_mark(struct match *, uint32_t ct_mark); void match_set_ct_mark_masked(struct match *, uint32_t ct_mark, uint32_t mask); void match_set_ct_label(struct match *, ovs_u128 ct_label); diff --git a/lib/match.c b/lib/match.c index 052daee..5415251 100644 --- a/lib/match.c +++ b/lib/match.c @@ -417,8 +417,14 @@ match_set_ct_state_masked(struct match *match, uint32_t ct_state, uint32_t mask) void match_set_ct_zone(struct match *match, uint16_t ct_zone) { - match->flow.ct_zone = ct_zone; - match->wc.masks.ct_zone = UINT16_MAX; + match_set_ct_zone_masked(match, ct_zone, UINT16_MAX); +} + +void +match_set_ct_zone_masked(struct match *match, uint16_t ct_zone, uint16_t mask) +{ + match->flow.ct_zone = ct_zone & mask; + match->wc.masks.ct_zone = mask; } void From patchwork Thu Jul 4 14:28:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127587 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgVk5pDvz9sPJ for ; Fri, 5 Jul 2019 00:35:10 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 3842112E0; Thu, 4 Jul 2019 14:28:39 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 33C891169 for ; Thu, 4 Jul 2019 14:28:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id ADE2A87C for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:32 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWot010477; Thu, 4 Jul 2019 17:28:32 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:21 +0300 Message-Id: <1562250507-20335-3-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 2/8] compat: Add tc ct action and flower matches defines for older kernels X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Update kernel UAPI to support conntrack matches, and the tc actions ct and goto chain. Signed-off-by: Paul Blakey --- acinclude.m4 | 6 +++--- include/linux/automake.mk | 3 ++- include/linux/pkt_cls.h | 50 +++++++++++++++++++++++++++++++++++++++----- include/linux/tc_act/tc_ct.h | 41 ++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 include/linux/tc_act/tc_ct.h diff --git a/acinclude.m4 b/acinclude.m4 index 0868604..3abfff4 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 ], [ - int x = TCA_FLOWER_KEY_ENC_IP_TTL_MASK; + int x = TCA_FLOWER_KEY_CT_LABELS; ])], - [AC_DEFINE([HAVE_TCA_FLOWER_KEY_ENC_IP_TTL_MASK], [1], - [Define to 1 if TCA_FLOWER_KEY_ENC_IP_TTL_MASK is available.])]) + [AC_DEFINE([HAVE_TCA_FLOWER_KEY_CT_LABELS], [1], + [Define to 1 if TCA_FLOWER_KEY_CT_LABELS is avaiable.])]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([#include ], [ diff --git a/include/linux/automake.mk b/include/linux/automake.mk index 24f8520..099192e 100644 --- a/include/linux/automake.mk +++ b/include/linux/automake.mk @@ -4,4 +4,5 @@ noinst_HEADERS += \ include/linux/tc_act/tc_pedit.h \ include/linux/tc_act/tc_skbedit.h \ include/linux/tc_act/tc_tunnel_key.h \ - include/linux/tc_act/tc_vlan.h + include/linux/tc_act/tc_vlan.h \ + include/linux/tc_act/tc_ct.h diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index 4adea59..95f8c96 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_ENC_IP_TTL_MASK) +#if defined(__KERNEL__) || defined(HAVE_TCA_FLOWER_KEY_CT_LABELS) #include_next #else @@ -39,7 +39,21 @@ enum { #define TC_ACT_QUEUED 5 #define TC_ACT_REPEAT 6 #define TC_ACT_REDIRECT 7 -#define TC_ACT_JUMP 0x10000000 + +/* There is a special kind of actions called "extended actions", + * which need a value parameter. These have a local opcode located in + * the highest nibble, starting from 1. The rest of the bits + * are used to carry the value. These two parts together make + * a combined opcode. + */ +#define __TC_ACT_EXT_SHIFT 28 +#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT) +#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1) +#define TC_ACT_EXT_CMP(combined, opcode) \ + (((combined) & (~TC_ACT_EXT_VAL_MASK)) == opcode) + +#define TC_ACT_JUMP __TC_ACT_EXT(1) +#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2) struct tc_police { __u32 index; @@ -200,16 +214,42 @@ enum { TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */ TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */ - TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */ TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */ - TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */ TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */ + TCA_FLOWER_KEY_ENC_OPTS, TCA_FLOWER_KEY_ENC_OPTS_MASK, + TCA_FLOWER_IN_HW_COUNT, + + TCA_FLOWER_KEY_PORT_SRC_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_SRC_MAX, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MAX, /* be16 */ + + TCA_FLOWER_KEY_CT_STATE, /* u16 */ + TCA_FLOWER_KEY_CT_STATE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_MARK, /* u32 */ + TCA_FLOWER_KEY_CT_MARK_MASK, /* u32 */ + TCA_FLOWER_KEY_CT_LABELS, /* u128 */ + TCA_FLOWER_KEY_CT_LABELS_MASK, /* u128 */ + __TCA_FLOWER_MAX, }; +#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1) + +enum { + TCA_FLOWER_KEY_CT_FLAGS_NEW = 1 << 0, /* Beginning of a new connection. */ + TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 1 << 1, /* Part of an existing connection. */ + TCA_FLOWER_KEY_CT_FLAGS_RELATED = 1 << 2, /* Related to an established connection. */ + TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 1 << 3, /* Conntrack has occurred. */ +}; + enum { TCA_FLOWER_KEY_ENC_OPTS_UNSPEC, TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested @@ -250,6 +290,6 @@ enum { #define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1) -#endif /* __KERNEL__ || !HAVE_TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST */ +#endif /* __KERNEL__ || !HAVE_TCA_FLOWER_KEY_CT_LABELS */ #endif /* __LINUX_PKT_CLS_WRAPPER_H */ diff --git a/include/linux/tc_act/tc_ct.h b/include/linux/tc_act/tc_ct.h new file mode 100644 index 0000000..5fb1d7a --- /dev/null +++ b/include/linux/tc_act/tc_ct.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_TC_CT_H +#define __UAPI_TC_CT_H + +#include +#include + +enum { + TCA_CT_UNSPEC, + TCA_CT_PARMS, + TCA_CT_TM, + TCA_CT_ACTION, /* u16 */ + TCA_CT_ZONE, /* u16 */ + TCA_CT_MARK, /* u32 */ + TCA_CT_MARK_MASK, /* u32 */ + TCA_CT_LABELS, /* u128 */ + TCA_CT_LABELS_MASK, /* u128 */ + TCA_CT_NAT_IPV4_MIN, /* be32 */ + TCA_CT_NAT_IPV4_MAX, /* be32 */ + TCA_CT_NAT_IPV6_MIN, /* struct in6_addr */ + TCA_CT_NAT_IPV6_MAX, /* struct in6_addr */ + TCA_CT_NAT_PORT_MIN, /* be16 */ + TCA_CT_NAT_PORT_MAX, /* be16 */ + TCA_CT_PAD, + __TCA_CT_MAX +}; + +#define TCA_CT_MAX (__TCA_CT_MAX - 1) + +#define TCA_CT_ACT_COMMIT (1 << 0) +#define TCA_CT_ACT_FORCE (1 << 1) +#define TCA_CT_ACT_CLEAR (1 << 2) +#define TCA_CT_ACT_NAT (1 << 3) +#define TCA_CT_ACT_NAT_SRC (1 << 4) +#define TCA_CT_ACT_NAT_DST (1 << 5) + +struct tc_ct { + tc_gen; +}; + +#endif /* __UAPI_TC_CT_H */ From patchwork Thu Jul 4 14:28:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127590 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgYy4jvCz9s4Y for ; Fri, 5 Jul 2019 00:37:58 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 6E7A01318; Thu, 4 Jul 2019 14:28:41 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id F15FA12A3 for ; Thu, 4 Jul 2019 14:28:37 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 9C041892 for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWou010477; Thu, 4 Jul 2019 17:28:32 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:22 +0300 Message-Id: <1562250507-20335-4-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 3/8] tc: Introduce tc_id to specify a tc filter X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Move all that is needed to identify a tc filter to a new structure, tc_id. This removes a lot of duplication in accessing/creating tc filters. Signed-off-by: Paul Blakey Signed-off-by: Paul Blakey --- lib/netdev-linux.c | 6 +- lib/netdev-offload-tc.c | 207 ++++++++++++++++++++---------------------------- lib/tc.c | 122 +++++++++++----------------- lib/tc.h | 28 ++++--- 4 files changed, 151 insertions(+), 212 deletions(-) diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index e4ea94c..69e0982 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -2421,6 +2421,8 @@ static int tc_del_matchall_policer(struct netdev *netdev) { uint32_t block_id = 0; + int prio = TC_RESERVED_PRIORITY_POLICE; + struct tc_id id; int ifindex; int err; @@ -2429,8 +2431,8 @@ tc_del_matchall_policer(struct netdev *netdev) return err; } - err = tc_del_filter(ifindex, TC_RESERVED_PRIORITY_POLICE, 1, block_id, - TC_INGRESS); + id = make_tc_id(ifindex, block_id, prio, TC_INGRESS); + err = tc_del_filter(&id); if (err) { return err; } diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 2af0f10..e37049c 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -145,20 +145,16 @@ static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER; /** * struct ufid_tc_data - data entry for ufid_tc hmap. * @ufid_node: Element in @ufid_tc hash table by ufid key. - * @tc_node: Element in @ufid_tc hash table by prio/handle/ifindex key. + * @tc_node: Element in @ufid_tc hash table by tc_id key. * @ufid: ufid assigned to the flow - * @prio: tc priority - * @handle: tc handle - * @ifindex: netdev ifindex. + * @id: tc id * @netdev: netdev associated with the tc rule */ struct ufid_tc_data { struct hmap_node ufid_node; struct hmap_node tc_node; ovs_u128 ufid; - uint16_t prio; - uint32_t handle; - int ifindex; + struct tc_id id; struct netdev *netdev; }; @@ -190,32 +186,27 @@ del_ufid_tc_mapping(const ovs_u128 *ufid) /* Wrapper function to delete filter and ufid tc mapping */ static int -del_filter_and_ufid_mapping(int ifindex, int prio, int handle, - uint32_t block_id, const ovs_u128 *ufid, - enum tc_qdisc_hook hook) +del_filter_and_ufid_mapping(struct tc_id *id, const ovs_u128 *ufid) { int err; - err = tc_del_filter(ifindex, prio, handle, block_id, hook); + err = tc_del_filter(id); del_ufid_tc_mapping(ufid); - return err; } /* Add ufid entry to ufid_tc hashmap. */ static void -add_ufid_tc_mapping(const ovs_u128 *ufid, int prio, int handle, - struct netdev *netdev, int ifindex) +add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid, + struct tc_id *id) { size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); - size_t tc_hash = hash_int(hash_int(prio, handle), ifindex); + size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); struct ufid_tc_data *new_data = xzalloc(sizeof *new_data); new_data->ufid = *ufid; - new_data->prio = prio; - new_data->handle = handle; + new_data->id = *id; new_data->netdev = netdev_ref(netdev); - new_data->ifindex = ifindex; ovs_mutex_lock(&ufid_lock); hmap_insert(&ufid_tc, &new_data->ufid_node, ufid_hash); @@ -223,56 +214,49 @@ add_ufid_tc_mapping(const ovs_u128 *ufid, int prio, int handle, ovs_mutex_unlock(&ufid_lock); } -/* Get ufid from ufid_tc hashmap. +/* Get tc id from ufid_tc hashmap. * - * If netdev output param is not NULL then the function will return - * associated netdev on success and a refcount is taken on that netdev. - * The caller is then responsible to close the netdev. - * - * Returns handle if successful and fill prio and netdev for that ufid. - * Otherwise returns 0. + * Returns 0 if successful and fills id. + * Otherwise returns the error. */ static int -get_ufid_tc_mapping(const ovs_u128 *ufid, int *prio, struct netdev **netdev) +get_ufid_tc_mapping(const ovs_u128 *ufid, struct tc_id *id) { size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); struct ufid_tc_data *data; - int handle = 0; ovs_mutex_lock(&ufid_lock); HMAP_FOR_EACH_WITH_HASH(data, ufid_node, ufid_hash, &ufid_tc) { if (ovs_u128_equals(*ufid, data->ufid)) { - if (prio) { - *prio = data->prio; - } - if (netdev) { - *netdev = netdev_ref(data->netdev); - } - handle = data->handle; - break; + *id = data->id; + ovs_mutex_unlock(&ufid_lock); + return 0; } } ovs_mutex_unlock(&ufid_lock); - return handle; + return ENOENT; } -/* Find ufid entry in ufid_tc hashmap using prio, handle and netdev. +/* Find ufid entry in ufid_tc hashmap using tc_id id. * The result is saved in ufid. * * Returns true on success. */ static bool -find_ufid(int prio, int handle, struct netdev *netdev, ovs_u128 *ufid) +find_ufid(struct netdev *netdev, struct tc_id *id, ovs_u128 *ufid) { - int ifindex = netdev_get_ifindex(netdev); + size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); struct ufid_tc_data *data; - size_t tc_hash = hash_int(hash_int(prio, handle), ifindex); ovs_mutex_lock(&ufid_lock); HMAP_FOR_EACH_WITH_HASH(data, tc_node, tc_hash, &ufid_tc) { - if (data->prio == prio && data->handle == handle - && data->ifindex == ifindex) { + if (netdev == data->netdev + && data->id.prio == id->prio + && data->id.handle == id->handle + && data->id.hook == id->hook + && data->id.block_id == id->block_id + && data->id.ifindex == id->ifindex) { *ufid = data->ufid; break; } @@ -356,6 +340,8 @@ netdev_tc_flow_flush(struct netdev *netdev) enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev); int ifindex = netdev_get_ifindex(netdev); uint32_t block_id = 0; + struct tc_id id; + int prio = 0; if (ifindex < 0) { VLOG_ERR_RL(&error_rl, "flow_flush: failed to get ifindex for %s: %s", @@ -364,8 +350,8 @@ netdev_tc_flow_flush(struct netdev *netdev) } block_id = get_block_id_from_netdev(netdev); - - return tc_flush(ifindex, block_id, hook); + id = make_tc_id(ifindex, block_id, prio, hook); + return tc_del_filter(&id); } static int @@ -375,6 +361,8 @@ netdev_tc_flow_dump_create(struct netdev *netdev, enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev); struct netdev_flow_dump *dump; uint32_t block_id = 0; + struct tc_id id; + int prio = 0; int ifindex; ifindex = netdev_get_ifindex(netdev); @@ -388,7 +376,9 @@ netdev_tc_flow_dump_create(struct netdev *netdev, dump = xzalloc(sizeof *dump); dump->nl_dump = xzalloc(sizeof *dump->nl_dump); dump->netdev = netdev_ref(netdev); - tc_dump_flower_start(ifindex, dump->nl_dump, block_id, hook); + + id = make_tc_id(ifindex, block_id, prio, hook); + tc_dump_flower_start(&id, dump->nl_dump); *dump_out = dump; @@ -739,13 +729,18 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, struct ofpbuf *rbuffer, struct ofpbuf *wbuffer) { + struct netdev *netdev = dump->netdev; struct ofpbuf nl_flow; + struct tc_id id; + + id.hook = get_tc_qdisc_hook(netdev); + id.block_id = get_block_id_from_netdev(netdev); + id.ifindex = netdev_get_ifindex(netdev); while (nl_dump_next(dump->nl_dump, &nl_flow, rbuffer)) { struct tc_flower flower; - struct netdev *netdev = dump->netdev; - if (parse_netlink_to_tc_flower(&nl_flow, &flower)) { + if (parse_netlink_to_tc_flower(&nl_flow, &id, &flower)) { continue; } @@ -756,7 +751,7 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, if (flower.act_cookie.len) { *ufid = *((ovs_u128 *) flower.act_cookie.data); - } else if (!find_ufid(flower.prio, flower.handle, netdev, ufid)) { + } else if (!find_ufid(netdev, &id, ufid)) { continue; } @@ -1098,9 +1093,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, struct tc_action *action; uint32_t block_id = 0; struct nlattr *nla; + struct tc_id id; size_t left; int prio = 0; - int handle; int ifindex; int err; @@ -1352,35 +1347,32 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, } } - block_id = get_block_id_from_netdev(netdev); - handle = get_ufid_tc_mapping(ufid, &prio, NULL); - if (handle && prio) { - VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d", handle, prio); - del_filter_and_ufid_mapping(ifindex, prio, handle, block_id, ufid, - hook); - } - - if (!prio) { - prio = get_prio_for_tc_flower(&flower); - if (prio == 0) { - VLOG_ERR_RL(&rl, "couldn't get tc prio: %s", ovs_strerror(ENOSPC)); - return ENOSPC; - } + if (!get_ufid_tc_mapping(ufid, &id)) { + VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d", + id.handle, id.prio); + del_filter_and_ufid_mapping(&id, ufid); + } + + prio = get_prio_for_tc_flower(&flower); + if (prio == 0) { + VLOG_ERR_RL(&rl, "couldn't get tc prio: %s", ovs_strerror(ENOSPC)); + return ENOSPC; } flower.act_cookie.data = ufid; flower.act_cookie.len = sizeof *ufid; - err = tc_replace_flower(ifindex, prio, handle, &flower, block_id, hook); + id = make_tc_id(ifindex, block_id, prio, hook); + err = tc_replace_flower(&id, &flower); if (!err) { - add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex); + add_ufid_tc_mapping(netdev, ufid, &id); } return err; } static int -netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED, +netdev_tc_flow_get(struct netdev *netdev, struct match *match, struct nlattr **actions, const ovs_u128 *ufid, @@ -1389,43 +1381,27 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED, struct ofpbuf *buf) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); - struct netdev *dev; struct tc_flower flower; - enum tc_qdisc_hook hook; - uint32_t block_id = 0; odp_port_t in_port; - int prio = 0; - int ifindex; - int handle; + struct tc_id id; int err; - handle = get_ufid_tc_mapping(ufid, &prio, &dev); - if (!handle) { - return ENOENT; - } - - hook = get_tc_qdisc_hook(dev); - - ifindex = netdev_get_ifindex(dev); - if (ifindex < 0) { - VLOG_ERR_RL(&error_rl, "flow_get: failed to get ifindex for %s: %s", - netdev_get_name(dev), ovs_strerror(-ifindex)); - netdev_close(dev); - return -ifindex; + err = get_ufid_tc_mapping(ufid, &id); + if (err) { + return err; } - block_id = get_block_id_from_netdev(dev); VLOG_DBG_RL(&rl, "flow get (dev %s prio %d handle %d block_id %d)", - netdev_get_name(dev), prio, handle, block_id); - err = tc_get_flower(ifindex, prio, handle, &flower, block_id, hook); - netdev_close(dev); + netdev_get_name(netdev), id.prio, id.handle, id.block_id); + + err = tc_get_flower(&id, &flower); if (err) { VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s", - netdev_get_name(dev), prio, handle, ovs_strerror(err)); + netdev_get_name(netdev), id.prio, id.handle, ovs_strerror(err)); return err; } - in_port = netdev_ifindex_to_odp_port(ifindex); + in_port = netdev_ifindex_to_odp_port(id.ifindex); parse_tc_flower_to_match(&flower, match, actions, stats, attrs, buf); match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX); @@ -1440,44 +1416,24 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED, struct dpif_flow_stats *stats) { struct tc_flower flower; - enum tc_qdisc_hook hook; - uint32_t block_id = 0; - struct netdev *dev; - int prio = 0; - int ifindex; - int handle; + struct tc_id id; int error; - handle = get_ufid_tc_mapping(ufid, &prio, &dev); - if (!handle) { - return ENOENT; - } - - hook = get_tc_qdisc_hook(dev); - - ifindex = netdev_get_ifindex(dev); - if (ifindex < 0) { - VLOG_ERR_RL(&error_rl, "flow_del: failed to get ifindex for %s: %s", - netdev_get_name(dev), ovs_strerror(-ifindex)); - netdev_close(dev); - return -ifindex; + error = get_ufid_tc_mapping(ufid, &id); + if (error) { + return error; } - block_id = get_block_id_from_netdev(dev); - if (stats) { memset(stats, 0, sizeof *stats); - if (!tc_get_flower(ifindex, prio, handle, &flower, block_id, hook)) { + if (!tc_get_flower(&id, &flower)) { stats->n_packets = get_32aligned_u64(&flower.stats.n_packets); stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes); stats->used = flower.lastused; } } - error = del_filter_and_ufid_mapping(ifindex, prio, handle, block_id, ufid, - hook); - - netdev_close(dev); + error = del_filter_and_ufid_mapping(&id, ufid); return error; } @@ -1486,7 +1442,9 @@ static void probe_multi_mask_per_prio(int ifindex) { struct tc_flower flower; + struct tc_id id1, id2; int block_id = 0; + int prio = 1; int error; error = tc_add_del_qdisc(ifindex, true, block_id, TC_INGRESS); @@ -1501,7 +1459,8 @@ probe_multi_mask_per_prio(int ifindex) memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac); memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac); - error = tc_replace_flower(ifindex, 1, 1, &flower, block_id, TC_INGRESS); + id1 = make_tc_id(ifindex, block_id, prio, TC_INGRESS); + error = tc_replace_flower(&id1, &flower); if (error) { goto out; } @@ -1509,14 +1468,15 @@ probe_multi_mask_per_prio(int ifindex) memset(&flower.key.src_mac, 0x11, sizeof flower.key.src_mac); memset(&flower.mask.src_mac, 0xff, sizeof flower.mask.src_mac); - error = tc_replace_flower(ifindex, 1, 2, &flower, block_id, TC_INGRESS); - tc_del_filter(ifindex, 1, 1, block_id, TC_INGRESS); + id2 = make_tc_id(ifindex, block_id, prio, TC_INGRESS); + error = tc_replace_flower(&id2, &flower); + tc_del_filter(&id1); if (error) { goto out; } - tc_del_filter(ifindex, 1, 2, block_id, TC_INGRESS); + tc_del_filter(&id2); multi_mask_per_prio = true; VLOG_INFO("probe tc: multiple masks on single tc prio is supported."); @@ -1530,6 +1490,8 @@ probe_tc_block_support(int ifindex) { struct tc_flower flower; uint32_t block_id = 1; + struct tc_id id; + int prio = 0; int error; error = tc_add_del_qdisc(ifindex, true, block_id, TC_INGRESS); @@ -1544,7 +1506,8 @@ probe_tc_block_support(int ifindex) memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac); memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac); - error = tc_replace_flower(ifindex, 1, 1, &flower, block_id, TC_INGRESS); + id = make_tc_id(ifindex, block_id, prio, TC_INGRESS); + error = tc_replace_flower(&id, &flower); tc_add_del_qdisc(ifindex, false, block_id, TC_INGRESS); diff --git a/lib/tc.c b/lib/tc.c index 1eca356..d7cb43e 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -193,6 +193,21 @@ tc_make_request(int ifindex, int type, unsigned int flags, return tcmsg; } +static void request_from_tc_id(struct tc_id *id, uint16_t eth_type, + int type, unsigned int flags, + struct ofpbuf *request) +{ + int ifindex = id->block_id ? TCM_IFINDEX_MAGIC_BLOCK : id->ifindex; + struct tcmsg *tcmsg; + + tcmsg = tc_make_request(ifindex, type, flags, request); + tcmsg->tcm_parent = (id->hook == TC_EGRESS) ? + TC_EGRESS_PARENT : (id->block_id ? : TC_INGRESS_PARENT); + tcmsg->tcm_info = tc_make_handle(id->prio, eth_type); + tcmsg->tcm_handle = id->handle; +} + + int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp) { @@ -1429,7 +1444,8 @@ nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower) } int -parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower) +parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_id *id, + struct tc_flower *flower) { struct tcmsg *tc; struct nlattr *ta[ARRAY_SIZE(tca_policy)]; @@ -1442,16 +1458,17 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower) memset(flower, 0, sizeof *flower); tc = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc); - flower->handle = tc->tcm_handle; + flower->key.eth_type = (OVS_FORCE ovs_be16) tc_get_minor(tc->tcm_info); flower->mask.eth_type = OVS_BE16_MAX; - flower->prio = tc_get_major(tc->tcm_info); + id->prio = tc_get_major(tc->tcm_info); + id->handle = tc->tcm_handle; - if (flower->prio == TC_RESERVED_PRIORITY_POLICE) { + if (id->prio == TC_RESERVED_PRIORITY_POLICE) { return 0; } - if (!flower->handle) { + if (!id->handle) { return EAGAIN; } @@ -1471,20 +1488,11 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower) } int -tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id, - enum tc_qdisc_hook hook) +tc_dump_flower_start(struct tc_id *id, struct nl_dump *dump) { struct ofpbuf request; - struct tcmsg *tcmsg; - int index; - - index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex; - tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_DUMP, &request); - tcmsg->tcm_parent = (hook == TC_EGRESS) ? - TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT); - tcmsg->tcm_info = TC_H_UNSPEC; - tcmsg->tcm_handle = 0; + request_from_tc_id(id, 0, RTM_GETTFILTER, NLM_F_DUMP, &request); nl_dump_start(dump, NETLINK_ROUTE, &request); ofpbuf_uninit(&request); @@ -1492,68 +1500,28 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id, } int -tc_flush(int ifindex, uint32_t block_id, enum tc_qdisc_hook hook) +tc_del_filter(struct tc_id *id) { struct ofpbuf request; - struct tcmsg *tcmsg; - int index; - - index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex; - tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ACK, &request); - tcmsg->tcm_parent = (hook == TC_EGRESS) ? - TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT); - tcmsg->tcm_info = TC_H_UNSPEC; + request_from_tc_id(id, 0, RTM_DELTFILTER, NLM_F_ACK, &request); return tc_transact(&request, NULL); } int -tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id, - enum tc_qdisc_hook hook) -{ - struct ofpbuf request; - struct tcmsg *tcmsg; - struct ofpbuf *reply; - int error; - int index; - - index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex; - tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ECHO, &request); - tcmsg->tcm_parent = (hook == TC_EGRESS) ? - TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT); - tcmsg->tcm_info = tc_make_handle(prio, 0); - tcmsg->tcm_handle = handle; - - error = tc_transact(&request, &reply); - if (!error) { - ofpbuf_delete(reply); - } - return error; -} - -int -tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower, - uint32_t block_id, enum tc_qdisc_hook hook) +tc_get_flower(struct tc_id *id, struct tc_flower *flower) { struct ofpbuf request; - struct tcmsg *tcmsg; struct ofpbuf *reply; int error; - int index; - - index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex; - tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_ECHO, &request); - tcmsg->tcm_parent = (hook == TC_EGRESS) ? - TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT); - tcmsg->tcm_info = tc_make_handle(prio, 0); - tcmsg->tcm_handle = handle; + request_from_tc_id(id, 0, RTM_GETTFILTER, NLM_F_ECHO, &request); error = tc_transact(&request, &reply); if (error) { return error; } - error = parse_netlink_to_tc_flower(reply, flower); + error = parse_netlink_to_tc_flower(reply, id, flower); ofpbuf_delete(reply); return error; } @@ -2299,26 +2267,30 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) return 0; } +struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio, + enum tc_qdisc_hook hook) +{ + struct tc_id id = { .handle = 0, }; + + id.ifindex = ifindex; + id.block_id = block_id; + id.prio = prio; + id.hook = hook; + + return id; +} + int -tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle, - struct tc_flower *flower, uint32_t block_id, - enum tc_qdisc_hook hook) +tc_replace_flower(struct tc_id *id, struct tc_flower *flower) { struct ofpbuf request; - struct tcmsg *tcmsg; struct ofpbuf *reply; int error = 0; size_t basic_offset; uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type; - int index; - index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex; - tcmsg = tc_make_request(index, RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_ECHO, - &request); - tcmsg->tcm_parent = (hook == TC_EGRESS) ? - TC_EGRESS_PARENT : (block_id ? : TC_INGRESS_PARENT); - tcmsg->tcm_info = tc_make_handle(prio, eth_type); - tcmsg->tcm_handle = handle; + request_from_tc_id(id, eth_type, RTM_NEWTFILTER, + NLM_F_CREATE | NLM_F_ECHO, &request); nl_msg_put_string(&request, TCA_KIND, "flower"); basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS); @@ -2337,8 +2309,8 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle, struct tcmsg *tc = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc); - flower->prio = tc_get_major(tc->tcm_info); - flower->handle = tc->tcm_handle; + id->prio = tc_get_major(tc->tcm_info); + id->handle = tc->tcm_handle; ofpbuf_delete(reply); } diff --git a/lib/tc.h b/lib/tc.h index 2e0f5e3..00acc41 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -197,10 +197,18 @@ enum tc_offloaded_state { TC_OFFLOADED_STATE_NOT_IN_HW, }; -struct tc_flower { +struct tc_id { + enum tc_qdisc_hook hook; + uint32_t block_id; + int ifindex; + uint16_t prio; uint32_t handle; - uint32_t prio; +}; +struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio, + enum tc_qdisc_hook hook); + +struct tc_flower { struct tc_flower_key key; struct tc_flower_key mask; @@ -234,18 +242,12 @@ BUILD_ASSERT_DECL(offsetof(struct tc_flower, rewrite) + MEMBER_SIZEOF(struct tc_flower, rewrite) + sizeof(uint32_t) - 2 < sizeof(struct tc_flower)); -int tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle, - struct tc_flower *flower, uint32_t block_id, - enum tc_qdisc_hook hook); -int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id, - enum tc_qdisc_hook hook); -int tc_get_flower(int ifindex, int prio, int handle, - struct tc_flower *flower, uint32_t block_id, - enum tc_qdisc_hook hook); -int tc_flush(int ifindex, uint32_t block_id, enum tc_qdisc_hook hook); -int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id, - enum tc_qdisc_hook hook); +int tc_replace_flower(struct tc_id *id, struct tc_flower *flower); +int tc_del_filter(struct tc_id *id); +int tc_get_flower(struct tc_id *id, struct tc_flower *flower); +int tc_dump_flower_start(struct tc_id *id, struct nl_dump *dump); int parse_netlink_to_tc_flower(struct ofpbuf *reply, + struct tc_id *id, struct tc_flower *flower); void tc_set_policy(const char *policy); From patchwork Thu Jul 4 14:28:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127586 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgTs4LGfz9s4Y for ; Fri, 5 Jul 2019 00:34:25 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A22D212D2; Thu, 4 Jul 2019 14:28:38 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 0D2F01169 for ; Thu, 4 Jul 2019 14:28:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id A7420893 for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWov010477; Thu, 4 Jul 2019 17:28:33 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:23 +0300 Message-Id: <1562250507-20335-5-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 4/8] netdev-offload-tc: Implement netdev tc flush via tc filter del X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org To be consistent with our tc-ufid mapping after flush, and to support tc chains flushing in the next commit, implement flush operation via deleting all the filters we actually added and delete their mappings. This will also not delete the configured qos policing via matchall filters, while old code did. Signed-off-by: Paul Blakey --- lib/netdev-offload-tc.c | 74 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index e37049c..f4cc2db 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -43,7 +43,8 @@ VLOG_DEFINE_THIS_MODULE(netdev_offload_tc); static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5); -static struct hmap ufid_tc = HMAP_INITIALIZER(&ufid_tc); +static struct hmap ufid_to_tc = HMAP_INITIALIZER(&ufid_to_tc); +static struct hmap tc_to_ufid = HMAP_INITIALIZER(&tc_to_ufid); static bool multi_mask_per_prio = false; static bool block_support = false; @@ -143,45 +144,50 @@ static struct netlink_field set_flower_map[][4] = { static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER; /** - * struct ufid_tc_data - data entry for ufid_tc hmap. - * @ufid_node: Element in @ufid_tc hash table by ufid key. - * @tc_node: Element in @ufid_tc hash table by tc_id key. + * struct ufid_tc_data - data entry for ufid-tc hashmaps. + * @ufid_to_tc_node: Element in @ufid_to_tc hash table by ufid key. + * @tc_to_ufid_node: Element in @tc_to_ufid hash table by tc_id key. * @ufid: ufid assigned to the flow * @id: tc id * @netdev: netdev associated with the tc rule */ struct ufid_tc_data { - struct hmap_node ufid_node; - struct hmap_node tc_node; + struct hmap_node ufid_to_tc_node; + struct hmap_node tc_to_ufid_node; ovs_u128 ufid; struct tc_id id; struct netdev *netdev; }; -/* Remove matching ufid entry from ufid_tc hashmap. */ static void -del_ufid_tc_mapping(const ovs_u128 *ufid) +del_ufid_tc_mapping_unlocked(const ovs_u128 *ufid) { size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); struct ufid_tc_data *data; - ovs_mutex_lock(&ufid_lock); - HMAP_FOR_EACH_WITH_HASH(data, ufid_node, ufid_hash, &ufid_tc) { + HMAP_FOR_EACH_WITH_HASH(data, ufid_to_tc_node, ufid_hash, &ufid_to_tc) { if (ovs_u128_equals(*ufid, data->ufid)) { break; } } if (!data) { - ovs_mutex_unlock(&ufid_lock); return; } - hmap_remove(&ufid_tc, &data->ufid_node); - hmap_remove(&ufid_tc, &data->tc_node); + hmap_remove(&ufid_to_tc, &data->ufid_to_tc_node); + hmap_remove(&tc_to_ufid, &data->tc_to_ufid_node); netdev_close(data->netdev); free(data); - ovs_mutex_unlock(&ufid_lock); +} + +/* Remove matching ufid entry from ufid-tc hashmaps. */ +static void +del_ufid_tc_mapping(const ovs_u128 *ufid) +{ + ovs_mutex_lock(&ufid_lock); + del_ufid_tc_mapping_unlocked(ufid); + ovs_mutex_lock(&ufid_lock); } /* Wrapper function to delete filter and ufid tc mapping */ @@ -195,7 +201,7 @@ del_filter_and_ufid_mapping(struct tc_id *id, const ovs_u128 *ufid) return err; } -/* Add ufid entry to ufid_tc hashmap. */ +/* Add ufid entry to ufid_to_tc hashmap. */ static void add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid, struct tc_id *id) @@ -209,12 +215,12 @@ add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid, new_data->netdev = netdev_ref(netdev); ovs_mutex_lock(&ufid_lock); - hmap_insert(&ufid_tc, &new_data->ufid_node, ufid_hash); - hmap_insert(&ufid_tc, &new_data->tc_node, tc_hash); + hmap_insert(&ufid_to_tc, &new_data->ufid_to_tc_node, ufid_hash); + hmap_insert(&tc_to_ufid, &new_data->tc_to_ufid_node, tc_hash); ovs_mutex_unlock(&ufid_lock); } -/* Get tc id from ufid_tc hashmap. +/* Get tc id from ufid_to_tc hashmap. * * Returns 0 if successful and fills id. * Otherwise returns the error. @@ -226,7 +232,7 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tc_id *id) struct ufid_tc_data *data; ovs_mutex_lock(&ufid_lock); - HMAP_FOR_EACH_WITH_HASH(data, ufid_node, ufid_hash, &ufid_tc) { + HMAP_FOR_EACH_WITH_HASH(data, ufid_to_tc_node, ufid_hash, &ufid_to_tc) { if (ovs_u128_equals(*ufid, data->ufid)) { *id = data->id; ovs_mutex_unlock(&ufid_lock); @@ -238,7 +244,7 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tc_id *id) return ENOENT; } -/* Find ufid entry in ufid_tc hashmap using tc_id id. +/* Find ufid entry in ufid_to_tc hashmap using tc_id id. * The result is saved in ufid. * * Returns true on success. @@ -250,7 +256,7 @@ find_ufid(struct netdev *netdev, struct tc_id *id, ovs_u128 *ufid) struct ufid_tc_data *data; ovs_mutex_lock(&ufid_lock); - HMAP_FOR_EACH_WITH_HASH(data, tc_node, tc_hash, &ufid_tc) { + HMAP_FOR_EACH_WITH_HASH(data, tc_to_ufid_node, tc_hash, &tc_to_ufid) { if (netdev == data->netdev && data->id.prio == id->prio && data->id.handle == id->handle @@ -337,21 +343,23 @@ get_block_id_from_netdev(struct netdev *netdev) static int netdev_tc_flow_flush(struct netdev *netdev) { - enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev); - int ifindex = netdev_get_ifindex(netdev); - uint32_t block_id = 0; - struct tc_id id; - int prio = 0; + struct ufid_tc_data *data, *next; + int err; - if (ifindex < 0) { - VLOG_ERR_RL(&error_rl, "flow_flush: failed to get ifindex for %s: %s", - netdev_get_name(netdev), ovs_strerror(-ifindex)); - return -ifindex; + ovs_mutex_lock(&ufid_lock); + HMAP_FOR_EACH_SAFE(data, next, tc_to_ufid_node, &tc_to_ufid) { + if (data->netdev != netdev) { + continue; + } + + err = tc_del_filter(&data->id); + if (!err) { + del_ufid_tc_mapping_unlocked(&data->ufid); + } } + ovs_mutex_unlock(&ufid_lock); - block_id = get_block_id_from_netdev(netdev); - id = make_tc_id(ifindex, block_id, prio, hook); - return tc_del_filter(&id); + return 0; } static int From patchwork Thu Jul 4 14:28:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127588 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgX53VnDz9sPD for ; Fri, 5 Jul 2019 00:36:21 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 1C448130B; Thu, 4 Jul 2019 14:28:40 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id E938F1169 for ; Thu, 4 Jul 2019 14:28:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id B1A09894 for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWow010477; Thu, 4 Jul 2019 17:28:33 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:24 +0300 Message-Id: <1562250507-20335-6-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 5/8] netdev-offload-tc: Add recirculation support via tc chains X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Each recirculation id will create a tc chain, and we translate the recirculation action to a tc goto chain action. Signed-off-by: Paul Blakey --- lib/dpif-netlink.c | 1 + lib/netdev-offload-tc.c | 35 +++++++++++++++++++++++++---------- lib/tc.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ lib/tc.h | 6 ++++++ 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index ba80a00..3fd94bb 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1598,6 +1598,7 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match, .mask = &match->wc.masks, .support = { .max_vlan_headers = 2, + .recirc = true, }, }; size_t offset; diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index f4cc2db..7bcedc8 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -206,9 +206,12 @@ static void add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid, struct tc_id *id) { - size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); - size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); struct ufid_tc_data *new_data = xzalloc(sizeof *new_data); + size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); + size_t tc_hash; + + tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); + tc_hash = hash_int(id->chain, tc_hash); new_data->ufid = *ufid; new_data->id = *id; @@ -252,12 +255,16 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tc_id *id) static bool find_ufid(struct netdev *netdev, struct tc_id *id, ovs_u128 *ufid) { - size_t tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); struct ufid_tc_data *data; + size_t tc_hash; + + tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); + tc_hash = hash_int(id->chain, tc_hash); ovs_mutex_lock(&ufid_lock); HMAP_FOR_EACH_WITH_HASH(data, tc_to_ufid_node, tc_hash, &tc_to_ufid) { if (netdev == data->netdev + && data->id.chain == id->chain && data->id.prio == id->prio && data->id.handle == id->handle && data->id.hook == id->hook @@ -706,6 +713,10 @@ parse_tc_flower_to_match(struct tc_flower *flower, nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport)); } break; + case TC_ACT_GOTO: { + nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain); + } + break; } } } @@ -765,6 +776,7 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX); match->flow.in_port.odp_port = dump->port; + match_set_recirc_id(match, id.chain); return true; } @@ -929,12 +941,6 @@ test_key_and_mask(struct match *match) return EOPNOTSUPP; } - if (mask->recirc_id && key->recirc_id) { - VLOG_DBG_RL(&rl, "offloading attribute recirc_id isn't supported"); - return EOPNOTSUPP; - } - mask->recirc_id = 0; - if (mask->dp_hash) { VLOG_DBG_RL(&rl, "offloading attribute dp_hash isn't supported"); return EOPNOTSUPP; @@ -1102,6 +1108,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, uint32_t block_id = 0; struct nlattr *nla; struct tc_id id; + uint32_t chain; size_t left; int prio = 0; int ifindex; @@ -1116,6 +1123,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, memset(&flower, 0, sizeof flower); + chain = key->recirc_id; + mask->recirc_id = 0; + if (flow_tnl_dst_is_set(&key->tunnel)) { VLOG_DBG_RL(&rl, "tunnel: id %#" PRIx64 " src " IP_FMT @@ -1348,6 +1358,10 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, if (err) { return err; } + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) { + action->type = TC_ACT_GOTO; + action->chain = nl_attr_get_u32(nla); + flower.action_count++; } else { VLOG_DBG_RL(&rl, "unsupported put action type: %d", nl_attr_type(nla)); @@ -1370,7 +1384,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, flower.act_cookie.data = ufid; flower.act_cookie.len = sizeof *ufid; - id = make_tc_id(ifindex, block_id, prio, hook); + id = make_tc_id_chain(ifindex, block_id, chain, prio, hook); err = tc_replace_flower(&id, &flower); if (!err) { add_ufid_tc_mapping(netdev, ufid, &id); @@ -1414,6 +1428,7 @@ netdev_tc_flow_get(struct netdev *netdev, match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX); match->flow.in_port.odp_port = in_port; + match_set_recirc_id(match, id.chain); return 0; } diff --git a/lib/tc.c b/lib/tc.c index d7cb43e..fa23545 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -50,6 +50,7 @@ #endif #if TCA_MAX < 14 +#define TCA_CHAIN 11 #define TCA_INGRESS_BLOCK 13 #endif @@ -205,6 +206,10 @@ static void request_from_tc_id(struct tc_id *id, uint16_t eth_type, TC_EGRESS_PARENT : (id->block_id ? : TC_INGRESS_PARENT); tcmsg->tcm_info = tc_make_handle(id->prio, eth_type); tcmsg->tcm_handle = id->handle; + + if (id->chain) { + nl_msg_put_u32(request, TCA_CHAIN, id->chain); + } } @@ -285,6 +290,7 @@ tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id, static const struct nl_policy tca_policy[] = { [TCA_KIND] = { .type = NL_A_STRING, .optional = false, }, [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, }, + [TCA_CHAIN] = { .type = NL_A_U32, .optional = true, }, [TCA_STATS] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct tc_stats), .optional = true, }, [TCA_STATS2] = { .type = NL_A_NESTED, .optional = true, }, @@ -1128,12 +1134,13 @@ nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower) } static int -nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower) +nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower) { struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)]; const struct tc_gact *p; struct nlattr *gact_parms; const struct tcf_t *tm; + struct tc_action *action; if (!nl_parse_nested(options, gact_policy, gact_attrs, ARRAY_SIZE(gact_policy))) { @@ -1144,7 +1151,11 @@ nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower) gact_parms = gact_attrs[TCA_GACT_PARMS]; p = nl_attr_get_unspec(gact_parms, sizeof *p); - if (p->action != TC_ACT_SHOT) { + if (TC_ACT_EXT_CMP(p->action, TC_ACT_GOTO_CHAIN)) { + action = &flower->actions[flower->action_count++]; + action->chain = p->action & TC_ACT_EXT_VAL_MASK; + action->type = TC_ACT_GOTO; + } else if (p->action != TC_ACT_SHOT) { VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action); return EINVAL; } @@ -1329,7 +1340,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower) act_cookie = action_attrs[TCA_ACT_COOKIE]; if (!strcmp(act_kind, "gact")) { - err = nl_parse_act_drop(act_options, flower); + err = nl_parse_act_gact(act_options, flower); } else if (!strcmp(act_kind, "mirred")) { err = nl_parse_act_mirred(act_options, flower); } else if (!strcmp(act_kind, "vlan")) { @@ -1478,6 +1489,10 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_id *id, return EPROTO; } + if (ta[TCA_CHAIN]) { + id->chain = nl_attr_get_u32(ta[TCA_CHAIN]); + } + kind = nl_attr_get_string(ta[TCA_KIND]); if (strcmp(kind, "flower")) { VLOG_DBG_ONCE("Unsupported filter: %s", kind); @@ -1714,7 +1729,7 @@ nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, bool id_present, } static void -nl_msg_put_act_drop(struct ofpbuf *request) +nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain) { size_t offset; @@ -1723,6 +1738,10 @@ nl_msg_put_act_drop(struct ofpbuf *request) { struct tc_gact p = { .action = TC_ACT_SHOT }; + if (chain) { + p.action = TC_ACT_GOTO_CHAIN | chain; + } + nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p); } nl_msg_end_nested(request, offset); @@ -2032,12 +2051,20 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) nl_msg_end_nested(request, act_offset); } break; + case TC_ACT_GOTO: { + act_offset = nl_msg_start_nested(request, act_index++); + nl_msg_put_act_gact(request, action->chain); + nl_msg_put_act_cookie(request, &flower->act_cookie); + nl_msg_end_nested(request, act_offset); + } + break; } } } - if (!ifindex) { + + if (!flower->action_count) { act_offset = nl_msg_start_nested(request, act_index++); - nl_msg_put_act_drop(request); + nl_msg_put_act_gact(request, 0); nl_msg_put_act_cookie(request, &flower->act_cookie); nl_msg_end_nested(request, act_offset); } @@ -2280,6 +2307,16 @@ struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio, return id; } +struct tc_id make_tc_id_chain(int ifindex, uint32_t block_id, uint32_t chain, + uint16_t prio, enum tc_qdisc_hook hook) +{ + struct tc_id id = make_tc_id(ifindex, block_id, prio, hook); + + id.chain = chain; + + return id; +} + int tc_replace_flower(struct tc_id *id, struct tc_flower *flower) { diff --git a/lib/tc.h b/lib/tc.h index 00acc41..fe8da4a 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -153,10 +153,13 @@ enum tc_action_type { TC_ACT_PEDIT, TC_ACT_VLAN_POP, TC_ACT_VLAN_PUSH, + TC_ACT_GOTO, }; struct tc_action { union { + int chain; + struct { int ifindex_out; bool ingress; @@ -201,12 +204,15 @@ struct tc_id { enum tc_qdisc_hook hook; uint32_t block_id; int ifindex; + uint32_t chain; uint16_t prio; uint32_t handle; }; struct tc_id make_tc_id(int ifindex, uint32_t block_id, uint16_t prio, enum tc_qdisc_hook hook); +struct tc_id make_tc_id_chain(int ifindex, uint32_t block_id, uint32_t chain, + uint16_t prio, enum tc_qdisc_hook hook); struct tc_flower { struct tc_flower_key key; From patchwork Thu Jul 4 14:28:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127591 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgZg4lfzz9sPH for ; Fri, 5 Jul 2019 00:38:35 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 1739B12EB; Thu, 4 Jul 2019 14:28:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id B2D1612D8 for ; Thu, 4 Jul 2019 14:28:38 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 33C03897 for ; Thu, 4 Jul 2019 14:28:34 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWox010477; Thu, 4 Jul 2019 17:28:33 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:25 +0300 Message-Id: <1562250507-20335-7-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 6/8] netdev-offload-tc: Add conntrack support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Zone and ct_state first. Signed-off-by: Paul Blakey --- lib/dpif-netlink.c | 2 + lib/netdev-offload-tc.c | 133 ++++++++++++++++++++++++++++++++++++++++++++---- lib/tc.c | 108 +++++++++++++++++++++++++++++++++++++++ lib/tc.h | 11 ++++ 4 files changed, 244 insertions(+), 10 deletions(-) diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 3fd94bb..30378d3 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1599,6 +1599,8 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match, .support = { .max_vlan_headers = 2, .recirc = true, + .ct_state = true, + .ct_zone = true, }, }; size_t offset; diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 7bcedc8..1071b9a 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -600,6 +600,32 @@ parse_tc_flower_to_match(struct tc_flower *flower, match_set_tp_dst_masked(match, key->sctp_dst, mask->sctp_dst); match_set_tp_src_masked(match, key->sctp_src, mask->sctp_src); } + + if (mask->ct_state) { + uint8_t ct_statev = 0, ct_statem = 0; + + if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW) { + if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW) + ct_statev |= OVS_CS_F_NEW; + ct_statem |= OVS_CS_F_NEW; + } + + if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) { + if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) + ct_statev |= OVS_CS_F_ESTABLISHED; + ct_statem |= OVS_CS_F_ESTABLISHED; + } + + if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED) { + if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED) + ct_statev |= OVS_CS_F_TRACKED; + ct_statem |= OVS_CS_F_TRACKED; + } + + match_set_ct_state_masked(match, ct_statev, ct_statem); + } + + match_set_ct_zone_masked(match, key->ct_zone, mask->ct_zone); } if (flower->tunnel) { @@ -713,6 +739,27 @@ parse_tc_flower_to_match(struct tc_flower *flower, nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport)); } break; + case TC_ACT_CT: { + size_t ct_offset; + + if (action->ct.clear) { + nl_msg_put_flag(buf, OVS_ACTION_ATTR_CT_CLEAR); + break; + } + + ct_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_CT); + + if (action->ct.commit) { + nl_msg_put_flag(buf, OVS_CT_ATTR_COMMIT); + } + + if (action->ct.zone) { + nl_msg_put_u16(buf, OVS_CT_ATTR_ZONE, action->ct.zone); + } + + nl_msg_end_nested(buf, ct_offset); + } + break; case TC_ACT_GOTO: { nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain); } @@ -785,6 +832,34 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, } static int +parse_put_flow_ct_action(struct tc_flower *flower, + struct tc_action *action, + const struct nlattr *ct, + size_t ct_len) +{ + const struct nlattr *ct_attr; + size_t ct_left; + int err; + + NL_ATTR_FOR_EACH_UNSAFE(ct_attr, ct_left, ct, ct_len) { + switch (nl_attr_type(ct_attr)) { + case OVS_CT_ATTR_COMMIT: { + action->ct.commit = true; + } + break; + case OVS_CT_ATTR_ZONE: { + action->ct.zone = nl_attr_get_u16(ct_attr); + } + break; + } + } + + action->type = TC_ACT_CT; + flower->action_count++; + return 0; +} + +static int parse_put_flow_set_masked_action(struct tc_flower *flower, struct tc_action *action, const struct nlattr *set, @@ -962,16 +1037,6 @@ test_key_and_mask(struct match *match) return EOPNOTSUPP; } - if (mask->ct_state) { - VLOG_DBG_RL(&rl, "offloading attribute ct_state isn't supported"); - return EOPNOTSUPP; - } - - if (mask->ct_zone) { - VLOG_DBG_RL(&rl, "offloading attribute ct_zone isn't supported"); - return EOPNOTSUPP; - } - if (mask->ct_mark) { VLOG_DBG_RL(&rl, "offloading attribute ct_mark isn't supported"); return EOPNOTSUPP; @@ -1306,6 +1371,42 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, } } + if (mask->ct_state) { + if (mask->ct_state & OVS_CS_F_NEW) { + if (key->ct_state & OVS_CS_F_NEW) { + flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW; + } + flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW; + } + + if (mask->ct_state & OVS_CS_F_ESTABLISHED) { + if (key->ct_state & OVS_CS_F_ESTABLISHED) { + flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED; + } + flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED; + } + + if (mask->ct_state & OVS_CS_F_TRACKED) { + if (key->ct_state & OVS_CS_F_TRACKED) { + flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED; + } + flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED; + } + + if (flower.key.ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) { + flower.key.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW); + flower.mask.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW); + } + + mask->ct_state = 0; + } + + if (mask->ct_zone) { + flower.key.ct_zone = key->ct_zone; + flower.mask.ct_zone = mask->ct_zone; + mask->ct_zone = 0; + } + err = test_key_and_mask(match); if (err) { return err; @@ -1358,6 +1459,18 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, if (err) { return err; } + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) { + const struct nlattr *ct = nl_attr_get(nla); + const size_t ct_len = nl_attr_get_size(nla); + + err = parse_put_flow_ct_action(&flower, action, ct, ct_len); + if (err) { + return err; + } + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT_CLEAR) { + action->type = TC_ACT_CT; + action->ct.clear = true; + flower.action_count++; } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) { action->type = TC_ACT_GOTO; action->chain = nl_attr_get_u32(nla); diff --git a/lib/tc.c b/lib/tc.c index fa23545..99d65a0 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -398,6 +399,10 @@ static const struct nl_policy tca_flower_policy[] = { [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, }, [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED, .optional = true, }, + [TCA_FLOWER_KEY_CT_STATE] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, }, }; static void @@ -798,6 +803,16 @@ nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) { key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]); mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]); } + + if (attrs[TCA_FLOWER_KEY_CT_STATE_MASK]) { + key->ct_state = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_STATE]); + mask->ct_state = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_STATE_MASK]); + } + + if (attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]) { + key->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE]); + mask->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]); + } } static enum tc_offloaded_state @@ -1218,6 +1233,53 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower) return 0; } +static const struct nl_policy ct_policy[] = { + [TCA_CT_PARMS] = { .type = NL_A_UNSPEC, + .min_len = sizeof(struct tc_ct), + .optional = false, }, + [TCA_CT_ACTION] = { .type = NL_A_U16, + .optional = true, }, + [TCA_CT_ZONE] = { .type = NL_A_U16, + .optional = true, }, +}; + +static int +nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) +{ + struct nlattr *ct_attrs[ARRAY_SIZE(ct_policy)]; + const struct nlattr *ct_parms; + struct tc_action *action; + const struct tc_ct *ct; + uint16_t ct_action = 0; + + if (!nl_parse_nested(options, ct_policy, ct_attrs, + ARRAY_SIZE(ct_policy))) { + VLOG_ERR_RL(&error_rl, "failed to parse ct action options"); + return EPROTO; + } + + ct_parms = ct_attrs[TCA_CT_PARMS]; + ct = nl_attr_get_unspec(ct_parms, sizeof *ct); + + if (ct_attrs[TCA_CT_ACTION]) + ct_action = nl_attr_get_u16(ct_attrs[TCA_CT_ACTION]); + + action = &flower->actions[flower->action_count++]; + action->ct.clear = ct_action & TCA_CT_ACT_CLEAR; + if (!action->ct.clear) { + struct nlattr *zone = ct_attrs[TCA_CT_ZONE]; + + action->ct.commit = ct_action & TCA_CT_ACT_COMMIT; + action->ct.force = ct_action & TCA_CT_ACT_FORCE; + + action->ct.zone = zone ? nl_attr_get_u16(zone) : 0; + + } + action->type = TC_ACT_CT; + + return 0; +} + static const struct nl_policy vlan_policy[] = { [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct tc_vlan), @@ -1353,6 +1415,8 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower) nl_parse_act_csum(act_options, flower); } else if (!strcmp(act_kind, "skbedit")) { /* Added for TC rule only (not in OvS rule) so ignore. */ + } else if (!strcmp(act_kind, "ct")) { + nl_parse_act_ct(act_options, flower); } else { VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind); err = EINVAL; @@ -1748,6 +1812,40 @@ nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain) } static void +nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action) +{ + uint16_t ct_action = 0; + size_t offset; + + nl_msg_put_string(request, TCA_ACT_KIND, "ct"); + offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); + { + struct tc_ct ct = { + .action = TC_ACT_PIPE, + }; + + if (!action->ct.clear) { + if (action->ct.zone) { + nl_msg_put_u16(request, TCA_CT_ZONE, action->ct.zone); + } + + if (action->ct.commit) { + ct_action = TCA_CT_ACT_COMMIT; + if (action->ct.force) { + ct_action |= TCA_CT_ACT_FORCE; + } + } + } else { + ct_action = TCA_CT_ACT_CLEAR; + } + + nl_msg_put_u16(request, TCA_CT_ACTION, ct_action); + nl_msg_put_unspec(request, TCA_CT_PARMS, &ct, sizeof ct); + } + nl_msg_end_nested(request, offset); +} + +static void nl_msg_put_act_skbedit_to_host(struct ofpbuf *request) { size_t offset; @@ -2058,6 +2156,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) nl_msg_end_nested(request, act_offset); } break; + case TC_ACT_CT: { + act_offset = nl_msg_start_nested(request, act_index++); + nl_msg_put_act_ct(request, action); + nl_msg_put_act_cookie(request, &flower->act_cookie); + nl_msg_end_nested(request, act_offset); + } + break; } } } @@ -2224,6 +2329,9 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC); FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST); } + + FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE); + FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE); } if (host_eth_type == ETH_P_IP) { diff --git a/lib/tc.h b/lib/tc.h index fe8da4a..9ff9c0b 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -116,6 +116,9 @@ struct tc_flower_key { uint8_t ip_ttl; uint8_t ip_tos; + uint16_t ct_state; + uint16_t ct_zone; + struct { ovs_be32 ipv4_src; ovs_be32 ipv4_dst; @@ -154,6 +157,7 @@ enum tc_action_type { TC_ACT_VLAN_POP, TC_ACT_VLAN_PUSH, TC_ACT_GOTO, + TC_ACT_CT, }; struct tc_action { @@ -189,6 +193,13 @@ struct tc_action { } ipv6; struct tun_metadata data; } encap; + + struct { + uint16_t zone; + bool clear; + bool force; + bool commit; + } ct; }; enum tc_action_type type; From patchwork Thu Jul 4 14:28:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127589 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgYC2fw9z9sPB for ; Fri, 5 Jul 2019 00:37:19 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id C0316130E; Thu, 4 Jul 2019 14:28:40 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 5DB9E12B5 for ; Thu, 4 Jul 2019 14:28:37 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 555FB898 for ; Thu, 4 Jul 2019 14:28:35 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWp0010477; Thu, 4 Jul 2019 17:28:33 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:26 +0300 Message-Id: <1562250507-20335-8-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 7/8] netdev-offload-tc: Add conntrack label and mark support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Signed-off-by: Paul Blakey --- lib/dpif-netlink.c | 2 ++ lib/netdev-offload-tc.c | 63 +++++++++++++++++++++++++++++++++++++++++-------- lib/tc.c | 48 +++++++++++++++++++++++++++++++++++++ lib/tc.h | 6 +++++ 4 files changed, 109 insertions(+), 10 deletions(-) diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 30378d3..e8fdc5e 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1601,6 +1601,8 @@ dpif_netlink_netdev_match_to_dpif_flow(struct match *match, .recirc = true, .ct_state = true, .ct_zone = true, + .ct_mark = true, + .ct_label = true, }, }; size_t offset; diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 1071b9a..9c5c25e 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -626,6 +626,8 @@ parse_tc_flower_to_match(struct tc_flower *flower, } match_set_ct_zone_masked(match, key->ct_zone, mask->ct_zone); + match_set_ct_mark_masked(match, key->ct_mark, mask->ct_mark); + match_set_ct_label_masked(match, key->ct_label, mask->ct_label); } if (flower->tunnel) { @@ -757,6 +759,23 @@ parse_tc_flower_to_match(struct tc_flower *flower, nl_msg_put_u16(buf, OVS_CT_ATTR_ZONE, action->ct.zone); } + if (action->ct.mark_mask) { + uint32_t mark_and_mask[2] = { action->ct.mark, action->ct.mark_mask }; + nl_msg_put_unspec(buf, OVS_CT_ATTR_MARK, &mark_and_mask, sizeof mark_and_mask); + } + + if (!ovs_u128_is_zero(action->ct.label_mask)) { + struct { + ovs_u128 key; + ovs_u128 mask; + } *ct_label; + + ct_label = nl_msg_put_unspec_uninit(buf, OVS_CT_ATTR_LABELS, + sizeof *ct_label); + ct_label->key = action->ct.label; + ct_label->mask = action->ct.label_mask; + } + nl_msg_end_nested(buf, ct_offset); } break; @@ -851,6 +870,28 @@ parse_put_flow_ct_action(struct tc_flower *flower, action->ct.zone = nl_attr_get_u16(ct_attr); } break; + case OVS_CT_ATTR_MARK: { + const struct { + uint32_t key; + uint32_t mask; + } *ct_mark; + + ct_mark = nl_attr_get_unspec(ct_attr, sizeof *ct_mark); + action->ct.mark = ct_mark->key; + action->ct.mark_mask = ct_mark->mask; + } + break; + case OVS_CT_ATTR_LABELS: { + const struct { + ovs_u128 key; + ovs_u128 mask; + } *ct_label; + + ct_label = nl_attr_get_unspec(ct_attr, sizeof *ct_label); + action->ct.label = ct_label->key; + action->ct.label_mask = ct_label->mask; + } + break; } } @@ -1037,22 +1078,12 @@ test_key_and_mask(struct match *match) return EOPNOTSUPP; } - if (mask->ct_mark) { - VLOG_DBG_RL(&rl, "offloading attribute ct_mark isn't supported"); - return EOPNOTSUPP; - } - if (mask->packet_type && key->packet_type) { VLOG_DBG_RL(&rl, "offloading attribute packet_type isn't supported"); return EOPNOTSUPP; } mask->packet_type = 0; - if (!ovs_u128_is_zero(mask->ct_label)) { - VLOG_DBG_RL(&rl, "offloading attribute ct_label isn't supported"); - return EOPNOTSUPP; - } - for (int i = 0; i < FLOW_N_REGS; i++) { if (mask->regs[i]) { VLOG_DBG_RL(&rl, @@ -1407,6 +1438,18 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, mask->ct_zone = 0; } + if (mask->ct_mark) { + flower.key.ct_mark = key->ct_mark; + flower.mask.ct_mark = mask->ct_mark; + mask->ct_mark = 0; + } + + if (!ovs_u128_is_zero(mask->ct_label)) { + flower.key.ct_label = key->ct_label; + flower.mask.ct_label = mask->ct_label; + mask->ct_label = OVS_U128_ZERO; + } + err = test_key_and_mask(match); if (err) { return err; diff --git a/lib/tc.c b/lib/tc.c index 99d65a0..33a32e4 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -403,6 +403,11 @@ static const struct nl_policy tca_flower_policy[] = { [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, }, [TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, }, [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, }, + [TCA_FLOWER_KEY_CT_MARK] = { .type = NL_A_U32, .optional = true, }, + [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NL_A_U32, .optional = true, }, + [TCA_FLOWER_KEY_CT_LABELS] = { .type = NL_A_U128, .optional = true, }, + [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NL_A_U128, + .optional = true, }, }; static void @@ -813,6 +818,16 @@ nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) { key->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE]); mask->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]); } + + if (attrs[TCA_FLOWER_KEY_CT_MARK_MASK]) { + key->ct_mark = nl_attr_get_u32(attrs[TCA_FLOWER_KEY_CT_MARK]); + mask->ct_mark = nl_attr_get_u32(attrs[TCA_FLOWER_KEY_CT_MARK_MASK]); + } + + if (attrs[TCA_FLOWER_KEY_CT_LABELS_MASK]) { + key->ct_label = nl_attr_get_u128(attrs[TCA_FLOWER_KEY_CT_LABELS]); + mask->ct_label = nl_attr_get_u128(attrs[TCA_FLOWER_KEY_CT_LABELS_MASK]); + } } static enum tc_offloaded_state @@ -1241,6 +1256,14 @@ static const struct nl_policy ct_policy[] = { .optional = true, }, [TCA_CT_ZONE] = { .type = NL_A_U16, .optional = true, }, + [TCA_CT_MARK] = { .type = NL_A_U32, + .optional = true, }, + [TCA_CT_MARK_MASK] = { .type = NL_A_U32, + .optional = true, }, + [TCA_CT_LABELS] = { .type = NL_A_UNSPEC, + .optional = true, }, + [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC, + .optional = true, }, }; static int @@ -1268,11 +1291,20 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) action->ct.clear = ct_action & TCA_CT_ACT_CLEAR; if (!action->ct.clear) { struct nlattr *zone = ct_attrs[TCA_CT_ZONE]; + struct nlattr *mark = ct_attrs[TCA_CT_MARK]; + struct nlattr *mark_mask = ct_attrs[TCA_CT_MARK_MASK]; + struct nlattr *label = ct_attrs[TCA_CT_LABELS]; + struct nlattr *label_mask = ct_attrs[TCA_CT_LABELS_MASK]; action->ct.commit = ct_action & TCA_CT_ACT_COMMIT; action->ct.force = ct_action & TCA_CT_ACT_FORCE; action->ct.zone = zone ? nl_attr_get_u16(zone) : 0; + action->ct.mark = mark ? nl_attr_get_u32(mark) : 0; + action->ct.mark_mask = mark_mask ? nl_attr_get_u32(mark_mask) : 0; + action->ct.label = label? nl_attr_get_u128(label) : OVS_U128_ZERO; + action->ct.label_mask = label_mask ? + nl_attr_get_u128(label_mask) : OVS_U128_ZERO; } action->type = TC_ACT_CT; @@ -1829,6 +1861,21 @@ nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action) nl_msg_put_u16(request, TCA_CT_ZONE, action->ct.zone); } + if (!is_all_zeros(&action->ct.label_mask, + sizeof action->ct.label_mask)) { + nl_msg_put_u128(request, TCA_CT_LABELS, + action->ct.label); + nl_msg_put_u128(request, TCA_CT_LABELS_MASK, + action->ct.label_mask); + } + + if (action->ct.mark_mask) { + nl_msg_put_u32(request, TCA_CT_MARK, + action->ct.mark); + nl_msg_put_u32(request, TCA_CT_MARK_MASK, + action->ct.mark_mask); + } + if (action->ct.commit) { ct_action = TCA_CT_ACT_COMMIT; if (action->ct.force) { @@ -2332,6 +2379,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE); FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE); + FLOWER_PUT_MASKED_VALUE(ct_label, TCA_FLOWER_KEY_CT_LABELS); } if (host_eth_type == ETH_P_IP) { diff --git a/lib/tc.h b/lib/tc.h index 9ff9c0b..ebe6073 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -118,6 +118,8 @@ struct tc_flower_key { uint16_t ct_state; uint16_t ct_zone; + uint32_t ct_mark; + ovs_u128 ct_label; struct { ovs_be32 ipv4_src; @@ -196,6 +198,10 @@ struct tc_action { struct { uint16_t zone; + uint32_t mark; + uint32_t mark_mask; + ovs_u128 label; + ovs_u128 label_mask; bool clear; bool force; bool commit; From patchwork Thu Jul 4 14:28:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Blakey X-Patchwork-Id: 1127593 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45fgbQ3QySz9s4Y for ; Fri, 5 Jul 2019 00:39:14 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id D9FEB1359; Thu, 4 Jul 2019 14:28:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 5D0A91311 for ; Thu, 4 Jul 2019 14:28:41 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id E846088E for ; Thu, 4 Jul 2019 14:28:39 +0000 (UTC) Received: from Internal Mail-Server by MTLPINE2 (envelope-from paulb@mellanox.com) with ESMTPS (AES256-SHA encrypted); 4 Jul 2019 17:28:33 +0300 Received: from reg-r-vrt-019-180.mtr.labs.mlnx (reg-r-vrt-019-180.mtr.labs.mlnx [10.213.19.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x64ESWp1010477; Thu, 4 Jul 2019 17:28:33 +0300 From: Paul Blakey To: Paul Blakey , dev@openvswitch.org Date: Thu, 4 Jul 2019 17:28:27 +0300 Message-Id: <1562250507-20335-9-git-send-email-paulb@mellanox.com> X-Mailer: git-send-email 1.8.4.3 In-Reply-To: <1562250507-20335-1-git-send-email-paulb@mellanox.com> References: <1562250507-20335-1-git-send-email-paulb@mellanox.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Marcelo Ricardo Leitner , Oz Shlomo , Simon Horman , Rony Efraim , David Miller , Yossi Kuperman Subject: [ovs-dev] [PATCH RFC v2 8/8] netdev-offload-tc: Add conntrack nat support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Signed-off-by: Paul Blakey --- lib/netdev-offload-tc.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/tc.c | 84 +++++++++++++++++++++++++++++++++++++++ lib/tc.h | 24 +++++++++++ 3 files changed, 211 insertions(+) diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 9c5c25e..0a0ada8 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -776,6 +776,39 @@ parse_tc_flower_to_match(struct tc_flower *flower, ct_label->mask = action->ct.label_mask; } + if (action->ct.nat_type) { + size_t nat_offset = nl_msg_start_nested(buf, OVS_CT_ATTR_NAT); + + if (action->ct.nat_type == TC_NAT_SRC) { + nl_msg_put_flag(buf, OVS_NAT_ATTR_SRC); + } else if (action->ct.nat_type == TC_NAT_DST) { + nl_msg_put_flag(buf, OVS_NAT_ATTR_DST); + } + + if (action->ct.range.ip_family == AF_INET) { + nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MIN, + action->ct.range.min_addr.ipv4); + nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MAX, + action->ct.range.max_addr.ipv4); + } else if (action->ct.range.ip_family == AF_INET6) { + nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MIN, + &action->ct.range.min_addr.ipv6); + nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MAX, + &action->ct.range.max_addr.ipv6); + } + + if (action->ct.range.min_port) { + nl_msg_put_be16(buf, OVS_NAT_ATTR_PROTO_MIN, + action->ct.range.min_port); + if (action->ct.range.max_port) { + nl_msg_put_be16(buf, OVS_NAT_ATTR_PROTO_MAX, + action->ct.range.max_port); + } + } + + nl_msg_end_nested(buf, nat_offset); + } + nl_msg_end_nested(buf, ct_offset); } break; @@ -851,6 +884,66 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, } static int +parse_put_flow_nat_action(struct tc_action *action, + const struct nlattr *nat, + size_t nat_len) +{ + const struct nlattr *nat_attr; + size_t nat_left; + + action->ct.nat_type = TC_NAT_RESTORE; + NL_ATTR_FOR_EACH_UNSAFE(nat_attr, nat_left, nat, nat_len) { + switch (nl_attr_type(nat_attr)) { + case OVS_NAT_ATTR_SRC: { + action->ct.nat_type = TC_NAT_SRC; + }; + break; + case OVS_NAT_ATTR_DST: { + action->ct.nat_type = TC_NAT_DST; + }; + break; + case OVS_NAT_ATTR_IP_MIN: { + if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) { + ovs_be32 addr = nl_attr_get_be32(nat_attr); + + action->ct.range.min_addr.ipv4 = addr; + action->ct.range.ip_family = AF_INET; + } else { + struct in6_addr addr = nl_attr_get_in6_addr(nat_attr); + + action->ct.range.min_addr.ipv6 = addr; + action->ct.range.ip_family = AF_INET6; + } + }; + break; + case OVS_NAT_ATTR_IP_MAX: { + if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) { + ovs_be32 addr = nl_attr_get_be32(nat_attr); + + action->ct.range.max_addr.ipv4 = addr; + action->ct.range.ip_family = AF_INET; + } else { + struct in6_addr addr = nl_attr_get_in6_addr(nat_attr); + + action->ct.range.max_addr.ipv6 = addr; + action->ct.range.ip_family = AF_INET6; + } + }; + break; + case OVS_NAT_ATTR_PROTO_MIN: { + action->ct.range.min_port = nl_attr_get_be16(nat_attr); + }; + break; + case OVS_NAT_ATTR_PROTO_MAX: { + action->ct.range.max_port = nl_attr_get_be16(nat_attr); + }; + break; + } + } + return 0; +} + +static int parse_put_flow_ct_action(struct tc_flower *flower, struct tc_action *action, const struct nlattr *ct, @@ -870,6 +963,16 @@ parse_put_flow_ct_action(struct tc_flower *flower, action->ct.zone = nl_attr_get_u16(ct_attr); } break; + case OVS_CT_ATTR_NAT: { + const struct nlattr *nat = nl_attr_get(ct_attr); + const size_t nat_len = nl_attr_get_size(ct_attr); + + err = parse_put_flow_nat_action(action, nat, nat_len); + if (err) { + return err; + } + } + break; case OVS_CT_ATTR_MARK: { const struct { uint32_t key; diff --git a/lib/tc.c b/lib/tc.c index 33a32e4..2aa3523 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -1264,6 +1264,18 @@ static const struct nl_policy ct_policy[] = { .optional = true, }, [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC, .optional = true, }, + [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32, + .optional = true, }, + [TCA_CT_NAT_IPV4_MAX] = { .type = NL_A_U32, + .optional = true, }, + [TCA_CT_NAT_IPV6_MIN] = { .min_len = sizeof(struct in6_addr), + .optional = true, }, + [TCA_CT_NAT_IPV6_MAX] = { .min_len = sizeof(struct in6_addr), + .optional = true, }, + [TCA_CT_NAT_PORT_MIN] = { .type = NL_A_U16, + .optional = true, }, + [TCA_CT_NAT_PORT_MAX] = { .type = NL_A_U16, + .optional = true, }, }; static int @@ -1306,6 +1318,47 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) action->ct.label_mask = label_mask ? nl_attr_get_u128(label_mask) : OVS_U128_ZERO; + if (ct_action & TCA_CT_ACT_NAT) { + struct nlattr *ipv4_min = ct_attrs[TCA_CT_NAT_IPV4_MIN]; + struct nlattr *ipv4_max = ct_attrs[TCA_CT_NAT_IPV4_MAX]; + struct nlattr *ipv6_min = ct_attrs[TCA_CT_NAT_IPV6_MIN]; + struct nlattr *ipv6_max = ct_attrs[TCA_CT_NAT_IPV6_MAX]; + struct nlattr *min_port = ct_attrs[TCA_CT_NAT_PORT_MIN]; + struct nlattr *max_port = ct_attrs[TCA_CT_NAT_PORT_MAX]; + + action->ct.nat_type = TC_NAT_RESTORE; + if (ct_action & TCA_CT_ACT_NAT_SRC) { + action->ct.nat_type = TC_NAT_SRC; + } else if (ct_action & TCA_CT_ACT_NAT_DST) { + action->ct.nat_type = TC_NAT_DST; + } + + if (ipv4_min) { + action->ct.range.ip_family = AF_INET; + action->ct.range.min_addr.ipv4 = nl_attr_get_be32(ipv4_min); + if (ipv4_max) { + ovs_be32 port = nl_attr_get_be32(ipv4_max); + + action->ct.range.max_addr.ipv4 = port; + } + } else if (ipv6_min) { + action->ct.range.ip_family = AF_INET6; + action->ct.range.min_addr.ipv6 + = nl_attr_get_in6_addr(ipv6_min); + if (ipv6_max) { + struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max); + + action->ct.range.max_addr.ipv6 = addr; + } + } + + if (min_port) { + action->ct.range.min_port = nl_attr_get_be16(min_port); + if (max_port) { + action->ct.range.max_port = nl_attr_get_be16(max_port); + } + } + } } action->type = TC_ACT_CT; @@ -1882,6 +1935,37 @@ nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action) ct_action |= TCA_CT_ACT_FORCE; } } + + if (action->ct.nat_type) { + ct_action |= TCA_CT_ACT_NAT; + + if (action->ct.nat_type == TC_NAT_SRC) { + ct_action |= TCA_CT_ACT_NAT_SRC; + } else if (action->ct.nat_type == TC_NAT_DST) { + ct_action |= TCA_CT_ACT_NAT_DST; + } + + if (action->ct.range.ip_family == AF_INET) { + nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MIN, + action->ct.range.min_addr.ipv4); + nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MAX, + action->ct.range.max_addr.ipv4); + } else if (action->ct.range.ip_family == AF_INET6) { + nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MIN, + &action->ct.range.min_addr.ipv6); + nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MAX, + &action->ct.range.max_addr.ipv6); + } + + if (action->ct.range.min_port) { + nl_msg_put_be16(request, TCA_CT_NAT_PORT_MIN, + action->ct.range.min_port); + if (action->ct.range.max_port) { + nl_msg_put_be16(request, TCA_CT_NAT_PORT_MAX, + action->ct.range.max_port); + } + } + } } else { ct_action = TCA_CT_ACT_CLEAR; } diff --git a/lib/tc.h b/lib/tc.h index ebe6073..1068f3e 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -162,6 +162,13 @@ enum tc_action_type { TC_ACT_CT, }; +enum nat_type { + TC_NO_NAT = 0, + TC_NAT_SRC, + TC_NAT_DST, + TC_NAT_RESTORE, +}; + struct tc_action { union { int chain; @@ -202,6 +209,23 @@ struct tc_action { uint32_t mark_mask; ovs_u128 label; ovs_u128 label_mask; + uint8_t nat_type; + struct { + uint8_t ip_family; + + union { + struct in6_addr ipv6; + ovs_be32 ipv4; + } min_addr; + + union { + struct in6_addr ipv6; + ovs_be32 ipv4; + } max_addr; + + ovs_be16 min_port; + ovs_be16 max_port; + } range; bool clear; bool force; bool commit;