From patchwork Mon Aug 11 10:09:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121388 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rRJ6zYtz1xx2 for ; Mon, 11 Aug 2025 20:27:08 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 5223A412B2; Mon, 11 Aug 2025 10:27:16 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id czjwtwGYLJ1T; Mon, 11 Aug 2025 10:27:09 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 4902541263 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 4902541263; Mon, 11 Aug 2025 10:27:09 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 217C0C0889; Mon, 11 Aug 2025 10:27:09 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id A3369C02A4 for ; Mon, 11 Aug 2025 10:27:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 8411D83DDD for ; Mon, 11 Aug 2025 10:27:07 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 6p21nGzcl9n7 for ; Mon, 11 Aug 2025 10:27:03 +0000 (UTC) X-Greylist: delayed 1014 seconds by postgrey-1.37 at util1.osuosl.org; Mon, 11 Aug 2025 10:27:01 UTC DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 053AC83DD9 Authentication-Results: smtp1.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 053AC83DD9 Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp1.osuosl.org (Postfix) with ESMTPS id 053AC83DD9 for ; Mon, 11 Aug 2025 10:27:01 +0000 (UTC) Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::226]) by mslow3.mail.gandi.net (Postfix) with ESMTP id B523E58173E for ; Mon, 11 Aug 2025 10:09:40 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id BB2584320E; Mon, 11 Aug 2025 10:09:33 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:39:25 +0530 Message-ID: <20250811100925.917826-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudekucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpefgteetudffjeehhfffkeetteelheelheekhffhudejuefhveffudelkeehteektdenucffohhmrghinheprghprggthhgvrdhorhhgnecukfhppedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieeknecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikedphhgvlhhopehfvgguohhrrgdrughomhgrihhnrdhnrghmvgdpmhgrihhlfhhrohhmpehnuhhmrghnshesohhvnhdrohhrghdpnhgspghrtghpthhtohepvddprhgtphhtthhopeguvghvsehophgvnhhvshifihhttghhrdhorhhgpdhrtghpthhtohepnhhumhgrnhhssehovhhnrdhorhhg X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 01/12] Add initial schema and skeleton for OVN bridge controller. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique This commit is the first of the series to add a new service to OVN called ovn-br-controller (OVN bridge controller). This commit adds - a schema file - a basic skeleton of ovn-br-controller service (which does nothing) - ovn-brctl utility similar to ovn-nbctl/ovn-sbctl. Rational for adding this service: There's a need to configure the provider bridges with specific OpenFlow rules after packets leave the OVN pipeline and enters these provider bridges via the patch ports. Since OVN has a very good abstraction of the OpenFlow rules using OVN logical flows, we can leverage the same here so that CMS doesn't have to deal with the specifics of OpenFlow protocol. Also if the CMS is written in golang or other languages, CMS has to mostly rely on ovs-vsctl/ovs-ofctl to program the flows. Adding this support in OVN avoids the CMS to exec these utils to add/delete and dump the existing OpenFlow rules. Any user can also use this new service to program and manage any OVS bridge without using OVN and hence this service is named as "ovn-br-controller." Signed-off-by: Numan Siddique --- Makefile.am | 5 +- automake.mk | 36 ++ br-controller/automake.mk | 8 + br-controller/ovn-br-controller.8.xml | 5 + br-controller/ovn-br-controller.c | 175 ++++++++ lib/automake.mk | 17 +- lib/ovn-br-idl.ann | 9 + lib/ovn-util.c | 13 + lib/ovn-util.h | 1 + ovn-br.ovsschema | 94 +++++ ovn-br.xml | 452 ++++++++++++++++++++ tutorial/ovn-sandbox | 24 +- utilities/automake.mk | 8 + utilities/ovn-brctl.c | 585 ++++++++++++++++++++++++++ 14 files changed, 1429 insertions(+), 3 deletions(-) create mode 100644 br-controller/automake.mk create mode 100644 br-controller/ovn-br-controller.8.xml create mode 100644 br-controller/ovn-br-controller.c create mode 100644 lib/ovn-br-idl.ann create mode 100644 ovn-br.ovsschema create mode 100644 ovn-br.xml create mode 100644 utilities/ovn-brctl.c diff --git a/Makefile.am b/Makefile.am index ea98fb5fb5..1936e9697a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,7 +115,9 @@ EXTRA_DIST = \ ovn-ic-nb.ovsschema \ ovn-ic-nb.xml \ ovn-ic-sb.ovsschema \ - ovn-ic-sb.xml + ovn-ic-sb.xml \ + ovn-br.ovsschema \ + ovn-br.xml bin_PROGRAMS = sbin_PROGRAMS = bin_SCRIPTS = @@ -507,4 +509,5 @@ include controller/automake.mk include controller-vtep/automake.mk include northd/automake.mk include ic/automake.mk +include br-controller/automake.mk include build-aux/automake.mk diff --git a/automake.mk b/automake.mk index a7947a3f54..53860ad89b 100644 --- a/automake.mk +++ b/automake.mk @@ -120,6 +120,35 @@ ovn-ic-sb.5: \ $(srcdir)/ovn-ic-sb.xml > $@.tmp && \ mv $@.tmp $@ +# OVN bridge controller E-R diagram +# +# If "python" or "dot" is not available, then we do not add graphical diagram +# to the documentation. +if HAVE_DOT +ovn-br.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-br.ovsschema + $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-br.ovsschema > $@ +ovn-br.pic: ovn-br.gv ${OVSDIR}/ovsdb/dot2pic + $(AM_V_GEN)(dot -T plain < ovn-br.gv | $(PYTHON3) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \ + mv $@.tmp $@ +OVN_BR_PIC = ovn-br.pic +OVN_BR_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_BR_PIC) +CLEANFILES += ovn-br.gv ovn-br.pic +endif + +# OVN bridge controller schema documentation +EXTRA_DIST += ovn-br.xml +CLEANFILES += ovn-br.5 +man_MANS += ovn-br.5 + +ovn-br.5: \ + ${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-br.xml $(srcdir)/ovn-br.ovsschema $(OVN_BR_PIC) + $(AM_V_GEN)$(OVSDB_DOC) \ + $(OVN_BR_DOT_DIAGRAM_ARG) \ + --version=$(VERSION) \ + $(srcdir)/ovn-br.ovsschema \ + $(srcdir)/ovn-br.xml > $@.tmp && \ + mv $@.tmp $@ + # Version checking for ovn-nb.ovsschema. ALL_LOCAL += ovn-nb.ovsschema.stamp ovn-nb.ovsschema.stamp: ovn-nb.ovsschema @@ -143,9 +172,16 @@ ovn-ic-sb.ovsschema.stamp: ovn-ic-sb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += ovn-ic-sb.ovsschema.stamp +# Version checking for ovn-br.ovsschema. +ALL_LOCAL += ovn-br.ovsschema.stamp +ovn-br.ovsschema.stamp: ovn-br.ovsschema + $(srcdir)/build-aux/cksum-schema-check $? $@ +CLEANFILES += ovn-br.ovsschema.stamp + pkgdata_DATA += ovn-nb.ovsschema pkgdata_DATA += ovn-sb.ovsschema pkgdata_DATA += ovn-ic-nb.ovsschema pkgdata_DATA += ovn-ic-sb.ovsschema +pkgdata_DATA += ovn-br.ovsschema CLEANFILES += ovn-sb.ovsschema.stamp diff --git a/br-controller/automake.mk b/br-controller/automake.mk new file mode 100644 index 0000000000..012e9a5ed8 --- /dev/null +++ b/br-controller/automake.mk @@ -0,0 +1,8 @@ +bin_PROGRAMS += br-controller/ovn-br-controller +br_controller_ovn_br_controller_SOURCES = \ + br-controller/ovn-br-controller.c + +br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la +man_MANS += br-controller/ovn-br-controller.8 +EXTRA_DIST += br-controller/ovn-br-controller.8.xml +CLEANFILES += br-controller/ovn-br-controller.8 diff --git a/br-controller/ovn-br-controller.8.xml b/br-controller/ovn-br-controller.8.xml new file mode 100644 index 0000000000..fa6eb2e2e2 --- /dev/null +++ b/br-controller/ovn-br-controller.8.xml @@ -0,0 +1,5 @@ + + +

Name

+

ovn-br-controller -- Open Virtual Network local OVS bridge controller

+
diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c new file mode 100644 index 0000000000..0fef0e5fee --- /dev/null +++ b/br-controller/ovn-br-controller.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2025 Crusoe Energy Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +/* OVS includes. */ +#include "openvswitch/vlog.h" +#include "lib/command-line.h" +#include "lib/daemon.h" +#include "lib/dirs.h" +#include "lib/fatal-signal.h" +#include "lib/stream.h" +#include "lib/stream-ssl.h" +#include "lib/unixctl.h" + +/* OVN includes. */ +#include "lib/ovn-br-idl.h" +#include "lib/ovn-util.h" + +VLOG_DEFINE_THIS_MODULE(main); + +static void parse_options(int argc, char *argv[]); +OVS_NO_RETURN static void usage(void); + + +/* SSL/TLS options. */ +static const char *ssl_private_key_file; +static const char *ssl_certificate_file; +static const char *ssl_ca_cert_file; + +/* --unixctl-path: Path to use for unixctl server socket. */ +static char *unixctl_path; + +int +main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + ovs_cmdl_proctitle_init(argc, argv); + ovn_set_program_name(argv[0]); + service_start(&argc, &argv); + parse_options(argc, argv); + fatal_ignore_sigpipe(); + + return 0; +} + +/* static functions. */ +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_PEER_CA_CERT = UCHAR_MAX + 1, + OPT_BOOTSTRAP_CA_CERT, + VLOG_OPTION_ENUMS, + OVN_DAEMON_OPTION_ENUMS, + SSL_OPTION_ENUMS, + }; + + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {"unixctl", required_argument, NULL, 'u'}, + VLOG_LONG_OPTIONS, + OVN_DAEMON_LONG_OPTIONS, + STREAM_SSL_LONG_OPTIONS, + {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, + {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, + {NULL, 0, NULL, 0} + }; + char *short_options = ovs_cmdl_long_options_to_short_options(long_options); + + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + usage(); + + case 'V': + ovs_print_version(OFP15_VERSION, OFP15_VERSION); + printf("OVN BR DB Schema %s\n", ovnbrrec_get_db_version()); + exit(EXIT_SUCCESS); + + case 'u': + unixctl_path = optarg; + break; + + VLOG_OPTION_HANDLERS + OVN_DAEMON_OPTION_HANDLERS + + case 'p': + ssl_private_key_file = optarg; + break; + + case 'c': + ssl_certificate_file = optarg; + break; + + case 'C': + ssl_ca_cert_file = optarg; + break; + + case OPT_SSL_PROTOCOLS: + stream_ssl_set_protocols(optarg); + break; + + case OPT_SSL_CIPHERS: + stream_ssl_set_ciphers(optarg); + break; + + case OPT_SSL_CIPHERSUITES: + stream_ssl_set_ciphersuites(optarg); + break; + + case OPT_PEER_CA_CERT: + stream_ssl_set_peer_ca_cert_file(optarg); + break; + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(optarg, true); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + ovs_abort(0, "Invalid option."); + } + } + free(short_options); + + argc -= optind; + argv += optind; +} + +static void +usage(void) +{ + printf("%s: OVN bridge controller\n" + "usage %s [OPTIONS] [OVS-DATABASE]\n" + "where OVS-DATABASE is a socket on which the OVS OVSDB server " + "is listening.\n", + program_name, program_name); + stream_usage("OVS-DATABASE", true, false, true); + daemon_usage(); + vlog_usage(); + printf("\nOther options:\n" + " -u, --unixctl=SOCKET set control socket name\n" + " -n custom chassis name\n" + " -h, --help display this help message\n" + " -V, --version display version information\n"); + exit(EXIT_SUCCESS); +} diff --git a/lib/automake.mk b/lib/automake.mk index a59c722d62..46c24e99d1 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -59,7 +59,9 @@ nodist_lib_libovn_la_SOURCES = \ lib/ovn-ic-nb-idl.c \ lib/ovn-ic-nb-idl.h \ lib/ovn-ic-sb-idl.c \ - lib/ovn-ic-sb-idl.h + lib/ovn-ic-sb-idl.h \ + lib/ovn-br-idl.c \ + lib/ovn-br-idl.h CLEANFILES += $(nodist_lib_libovn_la_SOURCES) @@ -129,3 +131,16 @@ OVN_IC_SB_IDL_FILES = \ lib/ovn-ic-sb-idl.ovsidl: $(OVN_IC_SB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_IC_SB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ + +# ovn-br IDL +OVSIDL_BUILT += \ + lib/ovn-br-idl.c \ + lib/ovn-br-idl.h \ + lib/ovn-br-idl.ovsidl +EXTRA_DIST += lib/ovn-br-idl.ann +OVN_PR_IDL_FILES = \ + $(srcdir)/ovn-br.ovsschema \ + $(srcdir)/lib/ovn-br-idl.ann +lib/ovn-br-idl.ovsidl: $(OVN_PR_IDL_FILES) + $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_PR_IDL_FILES) > $@.tmp && \ + mv $@.tmp $@ diff --git a/lib/ovn-br-idl.ann b/lib/ovn-br-idl.ann new file mode 100644 index 0000000000..80993338a6 --- /dev/null +++ b/lib/ovn-br-idl.ann @@ -0,0 +1,9 @@ +# -*- python -*- + +# This code, when invoked by "ovsdb-idlc annotate" (by the build +# process), annotates ovn-br.ovsschema with additional data that give +# the ovsdb-idl engine information about the types involved, so that +# it can generate more programmer-friendly data structures. + +s["idlPrefix"] = "ovnbrrec_" +s["idlHeader"] = "\"lib/ovn-br-idl.h\"" diff --git a/lib/ovn-util.c b/lib/ovn-util.c index ab5c4fc477..1cfeed6044 100644 --- a/lib/ovn-util.c +++ b/lib/ovn-util.c @@ -560,6 +560,19 @@ default_ic_sb_db(void) return def; } +const char * +default_br_db(void) +{ + static char *def; + if (!def) { + def = getenv("OVN_BR_DB"); + if (!def) { + def = xasprintf("unix:%s/ovnbr_db.sock", ovn_rundir()); + } + } + return def; +} + char * get_abs_unix_ctl_path(const char *path) { diff --git a/lib/ovn-util.h b/lib/ovn-util.h index bde40666a1..c66d7c31ff 100644 --- a/lib/ovn-util.h +++ b/lib/ovn-util.h @@ -131,6 +131,7 @@ const char *default_nb_db(void); const char *default_sb_db(void); const char *default_ic_nb_db(void); const char *default_ic_sb_db(void); +const char *default_br_db(void); char *get_abs_unix_ctl_path(const char *path); struct ovsdb_idl_table_class; diff --git a/ovn-br.ovsschema b/ovn-br.ovsschema new file mode 100644 index 0000000000..5635ee5bbd --- /dev/null +++ b/ovn-br.ovsschema @@ -0,0 +1,94 @@ +{ + "name": "OVN_Bridge_Controller", + "version": "0.0.1", + "cksum": "124113656 4389", + "tables": { + "BR_Global": { + "columns": { + "br_cfg": {"type": {"key": "integer"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "connections": { + "type": {"key": {"type": "uuid", + "refTable": "Connection"}, + "min": 0, + "max": "unlimited"}}, + "ssl": { + "type": {"key": {"type": "uuid", + "refTable": "SSL"}, + "min": 0, "max": 1}}, + "options": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "maxRows": 1, + "isRoot": true}, + "Connection": { + "columns": { + "target": {"type": "string"}, + "max_backoff": {"type": {"key": {"type": "integer", + "minInteger": 1000}, + "min": 0, + "max": 1}}, + "inactivity_probe": {"type": {"key": "integer", + "min": 0, + "max": 1}}, + "other_config": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}, + "external_ids": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}, + "is_connected": {"type": "boolean", "ephemeral": true}, + "status": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}, + "ephemeral": true}}, + "indexes": [["target"]]}, + "SSL": { + "columns": { + "private_key": {"type": "string"}, + "certificate": {"type": "string"}, + "ca_cert": {"type": "string"}, + "bootstrap_ca_cert": {"type": "boolean"}, + "ssl_protocols": {"type": "string"}, + "ssl_ciphers": {"type": "string"}, + "ssl_ciphersuites": {"type": "string"}, + "external_ids": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}}, + "maxRows": 1}, + "Bridge": { + "columns": { + "name": {"type": "string"}, + "options": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "indexes": [["name"]], + "isRoot": true}, + "Logical_Flow": { + "columns": { + "bridge": {"type": {"key": {"type": "uuid", + "refTable": "Bridge"}, + "min": 0, "max": 1}}, + "table_id": {"type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 32}}}, + "priority": {"type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 65535}}}, + "match": {"type": "string"}, + "actions": {"type": "string"}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "isRoot": true} + } +} diff --git a/ovn-br.xml b/ovn-br.xml new file mode 100644 index 0000000000..be6232094b --- /dev/null +++ b/ovn-br.xml @@ -0,0 +1,452 @@ + + +

+ This database is the interface between OVN Bridge Controller and the + cloud management system (CMS) to program and control the OVS bridges + using OVN logical flows. The CMS produces almost all of + the contents of the database. The ovn-bridge-controller program + monitors the database contents and programs the OVS bridges with the + OpenFlow rules. +

+ +

External IDs

+ +

+ Each of the tables in this database contains a special column, named + external_ids. This column has the same form and purpose each + place it appears. +

+ +
+
external_ids: map of string-string pairs
+
+ Key-value pairs for use by the CMS. The CMS might use certain pairs, for + example, to identify entities in its own configuration that correspond to + those in this database. +
+
+ + + + Sequence number for client to increment. When a client modifies any + part of the bridge Controller database configuration and wishes to wait + for ovn-br-controller to finish applying the changes, it may + increment this sequence number. + + + + + See External IDs at the beginning of this document. + + + + + + This column provides general key/value settings. The supported + options are described individually below. + + + + + + Database clients to which the Open vSwitch database server should + connect or on which it should listen, along with options for how these + connections should be configured. See the + table for more information. + + + Global SSL/TLS configuration. + + +
+ + +

+ Configuration for a database connection to an Open vSwitch database + (OVSDB) client. +

+ +

+ This table primarily configures the Open vSwitch database server + (ovsdb-server). +

+ +

+ The Open vSwitch database server can initiate and maintain active + connections to remote clients. It can also listen for database + connections. +

+ + + +

Connection methods for clients.

+

+ The following connection methods are currently supported: +

+
+
ssl:host[:port]
+
+

+ The specified SSL/TLS port on the host at the given + host, which can either be a DNS name (if built with + unbound library) or an IP address. A valid SSL/TLS configuration + must be provided when this form is used, this configuration can + be specified via command-line options or the + table. +

+

+ If port is not specified, it defaults to 6640. +

+

+ SSL/TLS support is an optional feature that is not always + built as part of OVN or Open vSwitch. +

+
+ +
tcp:host[:port]
+
+

+ The specified TCP port on the host at the given + host, which can either be a DNS name (if built with + unbound library) or an IP address. If host is an IPv6 + address, wrap it in square brackets, e.g. tcp:[::1]:6640. +

+

+ If port is not specified, it defaults to 6640. +

+
+
pssl:[port][:host]
+
+

+ Listens for SSL/TLS connections on the specified TCP + port. + Specify 0 for port to have the kernel automatically + choose an available port. If host, which can either + be a DNS name (if built with unbound library) or an IP address, + is specified, then connections are restricted to the resolved or + specified local IPaddress (either IPv4 or IPv6 address). If + host is an IPv6 address, wrap in square brackets, + e.g. pssl:6640:[::1]. If host is not + specified then it listens only on IPv4 (but not IPv6) addresses. + A valid SSL/TLS configuration must be provided when this form is + used, this can be specified either via command-line options or + the table. +

+

+ If port is not specified, it defaults to 6640. +

+

+ SSL/TLS support is an optional feature that is not always built + as part of OVN or Open vSwitch. +

+
+
ptcp:[port][:host]
+
+

+ Listens for connections on the specified TCP port. + Specify 0 for port to have the kernel automatically + choose an available port. If host, which can either + be a DNS name (if built with unbound library) or an IP address, + is specified, then connections are restricted to the resolved or + specified local IP address (either IPv4 or IPv6 address). If + host is an IPv6 address, wrap it in square brackets, + e.g. ptcp:6640:[::1]. If host is not + specified then it listens only on IPv4 addresses. +

+

+ If port is not specified, it defaults to 6640. +

+
+
+

When multiple clients are configured, the + values must be unique. Duplicate values yield + unspecified results.

+
+
+ + + + Maximum number of milliseconds to wait between connection attempts. + Default is implementation-specific. + + + + Maximum number of milliseconds of idle time on connection to the client + before sending an inactivity probe message. If Open vSwitch does not + communicate with the client for the specified number of seconds, it + will send a probe. If a response is not received for the same + additional amount of time, Open vSwitch assumes the connection has been + broken and attempts to reconnect. Default is implementation-specific. + A value of 0 disables inactivity probes. + + + + +

+ Key-value pair of is always updated. + Other key-value pairs in the status columns may be updated depends + on the type. +

+ +

+ When specifies a connection method that + listens for inbound connections (e.g. ptcp: or + punix:), both and + may also be updated while the + remaining key-value pairs are omitted. +

+ +

+ On the other hand, when specifies an + outbound connection, all key-value pairs may be updated, except + the above-mentioned two key-value pairs associated with inbound + connection targets. They are omitted. +

+ + + true if currently connected to this client, + false otherwise. + + + + A human-readable description of the last error on the connection + to the manager; i.e. strerror(errno). This key + will exist only if an error has occurred. + + + +

+ The state of the connection to the manager: +

+
+
VOID
+
Connection is disabled.
+ +
BACKOFF
+
Attempting to reconnect at an increasing period.
+ +
CONNECTING
+
Attempting to connect.
+ +
ACTIVE
+
Connected, remote host responsive.
+ +
IDLE
+
Connection is idle. Waiting for response to keep-alive.
+
+

+ These values may change in the future. They are provided only for + human consumption. +

+
+ + + The amount of time since this client last successfully connected + to the database (in seconds). Value is empty if client has never + successfully been connected. + + + + The amount of time since this client last disconnected from the + database (in seconds). Value is empty if client has never + disconnected. + + + + Space-separated list of the names of OVSDB locks that the connection + holds. Omitted if the connection does not hold any locks. + + + + Space-separated list of the names of OVSDB locks that the connection is + currently waiting to acquire. Omitted if the connection is not waiting + for any locks. + + + + Space-separated list of the names of OVSDB locks that the connection + has had stolen by another OVSDB client. Omitted if no locks have been + stolen from this connection. + + + + When specifies a connection method that + listens for inbound connections (e.g. ptcp: or + pssl:) and more than one connection is actually active, + the value is the number of active connections. Otherwise, this + key-value pair is omitted. + + + + When is ptcp: or + pssl:, this is the TCP port on which the OVSDB server is + listening. (This is particularly useful when specifies a port of 0, allowing the kernel to + choose any available port.) + +
+ + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + + +
+ + + SSL/TLS configuration for ovn-nb database access. + + + Name of a PEM file containing the private key used as the switch's + identity for SSL/TLS connections to the controller. + + + + Name of a PEM file containing a certificate, signed by the + certificate authority (CA) used by the controller and manager, + that certifies the switch's private key, identifying a trustworthy + switch. + + + + Name of a PEM file containing the CA certificate used to verify + that the switch is connected to a trustworthy controller. + + + + If set to true, then Open vSwitch will attempt to + obtain the CA certificate from the controller on its first SSL/TLS + connection and save it to the named PEM file. If it is successful, + it will immediately drop the connection and reconnect, and from then + on all SSL/TLS connections must be authenticated by a certificate signed + by the CA certificate thus obtained. This option exposes the + SSL/TLS connection to a man-in-the-middle attack obtaining the initial + CA certificate. It may still be useful for bootstrapping. + + + +

+ Range or a comma- or space-delimited list of the SSL/TLS protocols to + enable for SSL/TLS connections. +

+

+ Supported protocols include TLSv1.2 and + TLSv1.3. Ranges can be provided in a form of two protocol + names separated with a dash (TLSv1.2-TLSv1.3), or as a + single protocol name with a plus sign (TLSv1.2+). The + value can be a list of protocols or exactly one range. The range is a + preferred way of specifying protocols and the configuration always + behaves as if the range between the minimum and the maximum specified + version is provided, i.e., if the value is set to + TLSv1.X,TLSv1.(X+2), the TLSv1.(X+1) will + also be enabled as if it was a range. + Regardless of order, the highest protocol supported by both sides will + be chosen when making the connection. +

+

+ The default when this option is omitted is TLSv1.2+. +

+
+ + + List of ciphers (in OpenSSL cipher string format) to be supported + for SSL/TLS connections with TLSv1.2. The default when this option + is omitted is DEFAULT:@SECLEVEL=2. + + + + List of ciphersuites (in OpenSSL ciphersuites string format) to be + supported for SSL/TLS connections with TLSv1.3 and later. Default value + from OpenSSL will be used when this option is omitted. + + + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + +
+ + + + Name of the OVS bridge. This bridge should exist in the local OVS + database. + + + + Reserved for future use. + + + + See External IDs at the beginning of this document. + +
+ + + + The bridge to which the logical flow belongs to. + + + + The stage in the logical pipeline, analogous to an OpenFlow table number. + + + + The flow's priority. Flows with numerically higher priority take + precedence over those with lower. If two logical flows with the + same priority both match, then the one actually applied to the packet is + undefined. + + + +

+ A matching expression. OVN provides a superset of OpenFlow matching + capabilities, using a syntax similar to Boolean expressions in a + programming language. +

+ +

+ Please see the documentation of the + + column of the table in the database. +

+
+ + +

+ Logical datapath actions, to be executed when the logical flow + represented by this row is the highest-priority match. +

+ +

+ Actions share lexical syntax with the column. An + empty set of actions (or one that contains just white space or + comments), or a set of actions that consists of just + drop;, causes the matched packets to be dropped. + Otherwise, the column should contain a sequence of actions, each + terminated by a semicolon. +

+ +

+ Please see the documentation of the + + column of the table in the database. +

+
+ + + See External IDs at the beginning of this document. + +
+
diff --git a/tutorial/ovn-sandbox b/tutorial/ovn-sandbox index ed334d1c31..239db58f35 100755 --- a/tutorial/ovn-sandbox +++ b/tutorial/ovn-sandbox @@ -64,6 +64,8 @@ gdb_ovn_controller=false gdb_ovn_controller_ex=false gdb_ovn_controller_vtep=false gdb_ovn_controller_vtep_ex=false +gdb_ovn_br_controller=false +gdb_ovn_br_controller_ex=false builddir= ovsbuilddir= srcdir= @@ -90,6 +92,7 @@ ic_nb_servers=3 ic_sb_model=clustered ic_sb_servers=3 dummy=override +ovn_br=false for option; do # This option-parsing mechanism borrowed from a Autoconf-generated @@ -339,6 +342,9 @@ EOF --ic-sb-m*) prev=ic_sb_model ;; + --ovn-br) + ovn_br=true + ;; -R|--gdb-run) gdb_vswitchd_ex=true gdb_ovsdb_ex=true @@ -422,6 +428,11 @@ if $built; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi + br_schema=$srcdir/ovn-br.ovsschema + if test ! -e "$br_schema"; then + echo >&2 'source directory not found, please use --srcdir' + exit 1 + fi # Put built tools early in $PATH. if test ! -e $ovsbuilddir/vswitchd/ovs-vswitchd; then @@ -429,7 +440,7 @@ if $built; then exit 1 fi PATH=$ovsbuilddir/ovsdb:$ovsbuilddir/vswitchd:$ovsbuilddir/utilities:$ovsbuilddir/vtep:$PATH - PATH=$builddir/controller:$builddir/controller-vtep:$builddir/northd:$builddir/ic:$builddir/utilities:$PATH + PATH=$builddir/controller:$builddir/controller-vtep:$builddir/northd:$builddir/ic:$builddir/utilities:$builddir/br-controller:$PATH export PATH else case $schema in @@ -616,6 +627,10 @@ ovn_start_db sb "$sbdb_model" "$sbdb_servers" "$ovnsb_source" ovn_start_db ic_nb "$ic_nb_model" "$ic_nb_servers" "$ic_nb_schema" ovn_start_db ic_sb "$ic_sb_model" "$ic_sb_servers" "$ic_sb_schema" +if $ovn_br; then + ovn_start_db br standalone 1 "$br_schema" +fi + #Add a small delay to allow ovsdb-server to launch. sleep 0.1 @@ -692,6 +707,13 @@ rungdb $gdb_ovn_controller_vtep $gdb_ovn_controller_vtep_ex \ $OVN_CTRLR_PKI --log-file -vsyslog:off \ --ovnsb-db="$OVN_SB_DB" +if $ovn_br; then + run ovs-vsctl set open . external-ids:ovn-br-remote=$OVN_BR_DB + rungdb $gdb_ovn_br_controller $gdb_ovn_br_controller_ex ovn-br-controller \ + --detach --no-chdir -vsyslog:off \ + --log-file=ovn-br-controller.log \ + --pidfile=ovn-br-controller.pid -vconsole:off +fi cat < + +#include +#include +#include +#include + +/* OVS includes. */ +#include "command-line.h" +#include "daemon.h" +#include "db-ctl-base.h" +#include "dirs.h" +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "lib/ovn-br-idl.h" +#include "lib/ovn-dirs.h" +#include "lib/ovn-util.h" +#include "lib/vec.h" +#include "ovn-dbctl.h" + +VLOG_DEFINE_THIS_MODULE(brctl); + +static char * OVS_WARN_UNUSED_RESULT br_by_name_or_uuid( + struct ctl_context *ctx, const char *id, bool must_exist, + const struct ovnbrrec_bridge **br_p); + +static void +brctl_add_base_prerequisites(struct ovsdb_idl *idl, + enum nbctl_wait_type wait_type OVS_UNUSED) +{ + ovsdb_idl_add_table(idl, &ovnbrrec_table_br_global); +} + +static void +brctl_pre_execute(struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, + enum nbctl_wait_type wait_type OVS_UNUSED) +{ + const struct ovnbrrec_br_global *br = ovnbrrec_br_global_first(idl); + if (!br) { + br = ovnbrrec_br_global_insert(txn); + } +} + +static int +get_inactivity_probe(struct ovsdb_idl *idl) +{ + const struct ovnbrrec_br_global *pr = ovnbrrec_br_global_first(idl); + int interval = DEFAULT_UTILS_PROBE_INTERVAL_MSEC; + + if (pr) { + interval = smap_get_int(&pr->options, "brctl_probe_interval", + interval); + } + + return interval; +} + +/* ovn-brctl specific context. Inherits the 'struct ctl_context' as base. */ +struct brctl_context { + struct ctl_context base; + + /* A cache of the contents of the database. + * + * A command that needs to use any of this information must first call + * sbctl_context_populate_cache(). A command that changes anything that + * could invalidate the cache must either call + * sbctl_context_invalidate_cache() or manually update the cache to + * maintain its correctness. */ + bool cache_valid; +}; + +/* Casts 'base' into 'struct sbctl_context'. */ +static struct brctl_context * +brctl_context_cast(struct ctl_context *base) +{ + return CONTAINER_OF(base, struct brctl_context, base); +} + +static struct ctl_context * +brctl_ctx_create(void) +{ + struct brctl_context *prctx = xmalloc(sizeof *prctx); + *prctx = (struct brctl_context) { + .cache_valid = false, + }; + return &prctx->base; +} + +static void +brctl_context_invalidate_cache(struct ctl_context *ctx) +{ + struct brctl_context *brctl_ctx = brctl_context_cast(ctx); + + if (!brctl_ctx->cache_valid) { + return; + } + brctl_ctx->cache_valid = false; +} + +static void +brctl_ctx_destroy(struct ctl_context *ctx) +{ + brctl_context_invalidate_cache(ctx); + free(ctx); +} + +static void +print_br(const struct ovnbrrec_bridge *br, struct ds *s) +{ + ds_put_format(s, "bridge "UUID_FMT" (%s)\n", + UUID_ARGS(&br->header_.uuid), br->name); +} + +static void +brctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void +brctl_pre_show(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_name); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_options); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_external_ids); +} + +static const struct ctl_table_class tables[OVNBRREC_N_TABLES] = { + [OVNBRREC_TABLE_BRIDGE].row_ids[0] + = {&ovnbrrec_bridge_col_name, NULL, NULL}, + + [OVNBRREC_TABLE_CONNECTION].row_ids[0] + = {&ovnbrrec_connection_col_target, NULL, NULL}, +}; + +static void +brctl_show(struct ctl_context *ctx) +{ + const struct ovnbrrec_bridge *br; + + if (ctx->argc == 2) { + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &br); + if (error) { + ctx->error = error; + return; + } + + print_br(br, &ctx->output); + } else { + OVNBRREC_BRIDGE_FOR_EACH (br, ctx->idl) { + print_br(br, &ctx->output); + } + } +} + +static void +pre_get_info(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_name); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_options); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_external_ids); + + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_actions); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_bridge); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_match); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_priority); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_table_id); + ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_external_ids); +} + +static bool +is_uuid_with_prefix(const char *uuid) +{ + return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X'); +} + +static const char * +strip_leading_zero(const char *s) +{ + return s + strspn(s, "0"); +} + +static bool +is_partial_uuid_match(const struct uuid *uuid, const char *match) +{ + char uuid_s[UUID_LEN + 1]; + snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid)); + + /* We strip leading zeros because we want to accept cookie values derived + * from UUIDs, and cookie values are printed without leading zeros because + * they're just numbers. */ + const char *s1 = strip_leading_zero(uuid_s); + const char *s2 = match; + if (is_uuid_with_prefix(s2)) { + s2 = s2 + 2; + } + s2 = strip_leading_zero(s2); + return !strncmp(s1, s2, strlen(s2)); +} + + +struct brctl_lflow { + const struct ovnbrrec_logical_flow *lflow; + const struct ovnbrrec_bridge *br; +}; + +static int +brctl_lflow_cmp(const void *a_, const void *b_) +{ + const struct brctl_lflow *a_ctl_lflow = a_; + const struct brctl_lflow *b_ctl_lflow = b_; + + const struct ovnbrrec_logical_flow *a = a_ctl_lflow->lflow; + const struct ovnbrrec_logical_flow *b = b_ctl_lflow->lflow; + + const struct ovnbrrec_bridge *abr = a_ctl_lflow->br; + const struct ovnbrrec_bridge *bbr = b_ctl_lflow->br; + const char *a_name = abr->name; + const char *b_name = bbr->name; + int cmp = strcmp(a_name, b_name); + if (cmp) { + return cmp; + } + + cmp = uuid_compare_3way(&abr->header_.uuid, &bbr->header_.uuid); + if (cmp) { + return cmp; + } + + cmp = (a->table_id > b->table_id ? 1 + : a->table_id < b->table_id ? -1 + : a->priority > b->priority ? -1 + : a->priority < b->priority ? 1 + : strcmp(a->match, b->match)); + return cmp ? cmp : strcmp(a->actions, b->actions); +} + +static void +brctl_lflow_add(struct vector *lflows, + const struct ovnbrrec_logical_flow *lflow, + const struct ovnbrrec_bridge *br) +{ + struct brctl_lflow brctl_lflow = (struct brctl_lflow) { + .lflow = lflow, + .br = br, + }; + vector_push(lflows, &brctl_lflow); +} + +static void +print_uuid_part(const struct uuid *uuid, bool do_print, struct ds *s) +{ + if (!do_print) { + return; + } + ds_put_format(s, "uuid=0x%08"PRIx32", ", uuid->parts[0]); +} + +static void +print_bridge_prompt(const struct ovnbrrec_bridge *br, + const struct uuid *uuid, struct ds *s) { + ds_put_format(s, "Bridge: %s", br->name); + ds_put_format(s, " ("UUID_FMT")\n", + UUID_ARGS(uuid)); +} + +static char * OVS_WARN_UNUSED_RESULT +parse_priority(const char *arg, int64_t *priority_p) +{ + /* Validate priority. */ + int64_t priority; + if (!ovs_scan(arg, "%"SCNd64, &priority) + || priority < 0 || priority > 32767) { + /* Priority_p could be uninitialized as no valid priority was + * input, initialize it to a valid value of 0 before returning */ + *priority_p = 0; + return xasprintf("%s: priority must in range 0...32767", arg); + } + *priority_p = priority; + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +parse_table_id(const char *arg, int64_t *table_p) +{ + /* Validate table id. */ + int64_t table; + if (!ovs_scan(arg, "%"SCNd64, &table) + || table < 0 || table > 55) { + /* table_p could be uninitialized as no valid table id was + * input, initialize it to a valid value of 0 before returning */ + *table_p = 0; + return xasprintf("%s: table must in range 0...55", arg); + } + *table_p = table; + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +br_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist, + const struct ovnbrrec_bridge **br_p) +{ + const struct ovsdb_idl_row *row = NULL; + + char *error = ctl_get_row(ctx, &ovnbrrec_table_bridge, + id, must_exist, &row); + *br_p = (const struct ovnbrrec_bridge *) row; + return error; +} + +static void +cmd_br_add(struct ctl_context *ctx) +{ + const struct ovnbrrec_bridge *bridge = NULL; + + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], false, &bridge); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + + if (bridge) { + ctl_error(ctx, "Bridge %s already exists", ctx->argv[1]); + } + + bridge = ovnbrrec_bridge_insert(ctx->txn); + ovnbrrec_bridge_set_name(bridge, ctx->argv[1]); +} + +static void +cmd_br_del(struct ctl_context *ctx) +{ + const struct ovnbrrec_bridge *bridge = NULL; + + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + + ovnbrrec_bridge_delete(bridge); +} + +static void +cmd_lflow_list(struct ctl_context *ctx) +{ + struct vector lflows = VECTOR_EMPTY_INITIALIZER(struct brctl_lflow); + const struct ovnbrrec_bridge *bridge = NULL; + + if (ctx->argc > 1) { + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], false, &bridge); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + + if (bridge) { + ctx->argc--; + ctx->argv++; + } + } + + const struct ovnbrrec_logical_flow *lflow; + OVNBRREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) { + if (bridge && lflow->bridge != bridge) { + continue; + } + + brctl_lflow_add(&lflows, lflow, lflow->bridge); + } + + vector_qsort(&lflows, brctl_lflow_cmp); + + bool print_uuid = shash_find(&ctx->options, "--uuid") != NULL; + + const struct brctl_lflow *curr, *prev = NULL; + VECTOR_FOR_EACH_PTR (&lflows, curr) { + /* Figure out whether to print this particular flow. By default, we + * print all flows, but if any UUIDs were listed on the command line + * then we only print the matching ones. */ + bool include; + if (ctx->argc > 1) { + include = false; + for (size_t j = 1; j < ctx->argc; j++) { + if (is_partial_uuid_match(&curr->lflow->header_.uuid, + ctx->argv[j])) { + include = true; + break; + } + } + } else { + include = true; + } + if (!include) { + continue; + } + + /* Print a header line for this datapath or pipeline, if we haven't + * already done so. */ + if (!prev || prev->br != curr->br) { + print_bridge_prompt(curr->br, &curr->br->header_.uuid, + &ctx->output); + } + + /* Print the flow. */ + ds_put_cstr(&ctx->output, " "); + print_uuid_part(&curr->lflow->header_.uuid, print_uuid, &ctx->output); + ds_put_format(&ctx->output, + "table=%-2"PRId64", priority=%-5"PRId64 + ", match=(%s), action=(%s)\n", + curr->lflow->table_id, + curr->lflow->priority, curr->lflow->match, + curr->lflow->actions); + prev = curr; + } + + vector_destroy(&lflows); +} + +static void +cmd_lflow_add(struct ctl_context *ctx) +{ + const struct ovnbrrec_bridge *bridge = NULL; + + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + + int64_t table_id; + error = parse_table_id(ctx->argv[2], &table_id); + if (error) { + ctx->error = error; + return; + } + + int64_t priority; + error = parse_priority(ctx->argv[3], &priority); + if (error) { + ctx->error = error; + return; + } + + struct ovnbrrec_logical_flow *lflow = + ovnbrrec_logical_flow_insert(ctx->txn); + ovnbrrec_logical_flow_set_bridge(lflow, bridge); + ovnbrrec_logical_flow_set_table_id(lflow, table_id); + ovnbrrec_logical_flow_set_priority(lflow, priority); + ovnbrrec_logical_flow_set_match(lflow, ctx->argv[4]); + ovnbrrec_logical_flow_set_actions(lflow, ctx->argv[5]); +} + +static void +cmd_lflow_del(struct ctl_context *ctx) +{ + const struct ovnbrrec_logical_flow *lflow; + const struct ovsdb_idl_row *row; + + char *error = ctl_get_row(ctx, &ovnbrrec_table_logical_flow, + ctx->argv[1], true, &row); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + + lflow = (const struct ovnbrrec_logical_flow *) row; + ovnbrrec_logical_flow_delete(lflow); +} + +static void +cmd_lflows_del(struct ctl_context *ctx) +{ + const struct ovnbrrec_bridge *bridge = NULL; + + if (ctx->argc > 1) { + char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge); + if (error) { + ctl_error(ctx, "%s", error); + free(error); + return; + } + } + + const struct ovnbrrec_logical_flow *lflow; + OVNBRREC_LOGICAL_FLOW_FOR_EACH_SAFE (lflow, ctx->idl) { + if (!bridge || lflow->bridge == bridge) { + ovnbrrec_logical_flow_delete(lflow); + } + } +} + +static const struct ctl_command_syntax brctl_commands[] = { + { "init", 0, 0, "", NULL, brctl_init, NULL, "", RW }, + { "show", 0, 1, "[BRIDGE]", brctl_pre_show, brctl_show, NULL, "", RO }, + + /* Bridge commands. */ + {"add-br", 1, 1, "BRIDGE", pre_get_info, cmd_br_add, NULL, + "", RW}, + {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_br_del, NULL, + "", RW}, + + /* Logical flow commands */ + {"lflow-list", 0, INT_MAX, "[BRIDGE] [LFLOW...]", + pre_get_info, cmd_lflow_list, NULL, + "--uuid,--ovs?,--stats,--vflows?", RO}, + {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]", + pre_get_info, cmd_lflow_list, NULL, + "--uuid,--ovs?,--stats,--vflows?", + RO}, /* Friendly alias for lflow-list */ + {"add-flow", 5, 5, "BRIDGE TABLE PRIORITY MATCH ACTION", + pre_get_info, cmd_lflow_add, NULL, + "", RW}, + {"del-flow", 1, 1, "UUID", pre_get_info, cmd_lflow_del, NULL, + "", RW}, + {"del-flows", 0, 1, "[BRIDGE]", pre_get_info, cmd_lflows_del, NULL, + "", RW}, + {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, +}; + +static void +brctl_usage(void) +{ + printf("\ +%s: OVN Provider DB management utility\n\ +usage: %s [OPTIONS] COMMAND [ARG...]\n\ +\n\ +General commands:\n\ + init initialize the database\n\ + show print overview of database contents\n\ +\n\ +Logical flow commands:\n\ + lflow-list [BRIDGE] [LFLOW...] list logical flows for BRIDGE\n\ + dump-flows [BRIDGE] [LFLOW...] alias for lflow-list\n\ +\n\ +\n\n", program_name, program_name); +} + +int +main(int argc, char *argv[]) +{ + struct ovn_dbctl_options dbctl_options = { + .db_version = ovnbrrec_get_db_version(), + .default_db = default_br_db(), + .allow_wait = false, + + .options_env_var_name = "OVN_brctl_OPTIONS", + .daemon_env_var_name = "OVN_PR_DAEMON", + + .idl_class = &ovnbrrec_idl_class, + .tables = tables, + .cmd_show_table = NULL, + .commands = brctl_commands, + + .usage = brctl_usage, + .add_base_prerequisites = brctl_add_base_prerequisites, + .pre_execute = brctl_pre_execute, + .post_execute = NULL, + .get_inactivity_probe = get_inactivity_probe, + + .ctx_create = brctl_ctx_create, + .ctx_destroy = brctl_ctx_destroy, + }; + + return ovn_dbctl_main(argc, argv, &dbctl_options); +} From patchwork Mon Aug 11 10:09:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121387 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rRF5P6Kz1xx2 for ; Mon, 11 Aug 2025 20:27:05 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 9447461316; Mon, 11 Aug 2025 10:27:12 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id HPzFL9SNIipf; Mon, 11 Aug 2025 10:27:10 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 2DBC0612E2 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 2DBC0612E2; Mon, 11 Aug 2025 10:27:10 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DFB77C08B4; Mon, 11 Aug 2025 10:27:09 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 155D4C02A4 for ; Mon, 11 Aug 2025 10:27:08 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 0865F612E7 for ; Mon, 11 Aug 2025 10:27:08 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id rqCMM_W00axL for ; Mon, 11 Aug 2025 10:27:03 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 8DDA960F75 Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 8DDA960F75 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp3.osuosl.org (Postfix) with ESMTPS id 8DDA960F75 for ; Mon, 11 Aug 2025 10:27:02 +0000 (UTC) Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by mslow3.mail.gandi.net (Postfix) with ESMTP id B2CFE581BCF for ; Mon, 11 Aug 2025 10:09:45 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 03E2543212; Mon, 11 Aug 2025 10:09:40 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:39:35 +0530 Message-ID: <20250811100935.917848-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudekucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpefgteetudffjeehhfffkeetteelheelheekhffhudejuefhveffudelkeehteektdenucffohhmrghinheprghprggthhgvrdhorhhgnecukfhppedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieeknecuvehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehinhgvthepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikedphhgvlhhopehfvgguohhrrgdrughomhgrihhnrdhnrghmvgdpmhgrihhlfhhrohhmpehnuhhmrghnshesohhvnhdrohhrghdpnhgspghrtghpthhtohepvddprhgtphhtthhopeguvghvsehophgvnhhvshifihhttghhrdhorhhgpdhrtghpthhtohepnhhumhgrnhhssehovhhnrdhorhhg X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 02/12] ovn-br-controller: Connect to OVS database and define engine nodes. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- br-controller/automake.mk | 6 +- br-controller/en-bridge-data.c | 45 +++++ br-controller/en-bridge-data.h | 16 ++ br-controller/en-lflow.c | 43 +++++ br-controller/en-lflow.h | 16 ++ br-controller/ovn-br-controller.c | 279 +++++++++++++++++++++++++++++- lib/inc-proc-eng.h | 11 ++ 7 files changed, 409 insertions(+), 7 deletions(-) create mode 100644 br-controller/en-bridge-data.c create mode 100644 br-controller/en-bridge-data.h create mode 100644 br-controller/en-lflow.c create mode 100644 br-controller/en-lflow.h diff --git a/br-controller/automake.mk b/br-controller/automake.mk index 012e9a5ed8..497f440551 100644 --- a/br-controller/automake.mk +++ b/br-controller/automake.mk @@ -1,6 +1,10 @@ bin_PROGRAMS += br-controller/ovn-br-controller br_controller_ovn_br_controller_SOURCES = \ - br-controller/ovn-br-controller.c + br-controller/ovn-br-controller.c \ + br-controller/en-lflow.c \ + br-controller/en-lflow.h \ + br-controller/en-bridge-data.c \ + br-controller/en-bridge-data.h br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += br-controller/ovn-br-controller.8 diff --git a/br-controller/en-bridge-data.c b/br-controller/en-bridge-data.c new file mode 100644 index 0000000000..293ae895c0 --- /dev/null +++ b/br-controller/en-bridge-data.c @@ -0,0 +1,45 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +/* OVS includes. */ +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "en-bridge-data.h" + +VLOG_DEFINE_THIS_MODULE(en_bridge_data); + +void * +en_bridge_data_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return NULL; +} + +void +en_bridge_data_cleanup(void *data OVS_UNUSED) +{ +} + +enum engine_node_state +en_bridge_data_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +{ + return EN_UNCHANGED; +} diff --git a/br-controller/en-bridge-data.h b/br-controller/en-bridge-data.h new file mode 100644 index 0000000000..4a280b5cd1 --- /dev/null +++ b/br-controller/en-bridge-data.h @@ -0,0 +1,16 @@ +#ifndef EN_RUNTIME_DATA_H +#define EN_RUNTIME_DATA_H 1 + +#include + +#include +#include +#include + +#include "lib/inc-proc-eng.h" + +enum engine_node_state en_bridge_data_run(struct engine_node *, void *data); +void *en_bridge_data_init(struct engine_node *node, struct engine_arg *arg); +void en_bridge_data_cleanup(void *data); + +#endif /* EN_RUNTIME_DATA_H */ diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c new file mode 100644 index 0000000000..b1a6efbf5f --- /dev/null +++ b/br-controller/en-lflow.c @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +/* OVS includes. */ +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "en-lflow.h" + +VLOG_DEFINE_THIS_MODULE(en_lflow); + +void *en_lflow_output_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return NULL; +} + +void en_lflow_output_cleanup(void *data_ OVS_UNUSED) +{ +} + +enum engine_node_state +en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +{ + return EN_UNCHANGED; +} diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h new file mode 100644 index 0000000000..c3889cbdc9 --- /dev/null +++ b/br-controller/en-lflow.h @@ -0,0 +1,16 @@ +#ifndef EN_LFLOW_H +#define EN_LFLOW_H 1 + +#include + +#include +#include +#include + +#include "lib/inc-proc-eng.h" + +enum engine_node_state en_lflow_output_run(struct engine_node *, void *data); +void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg); +void en_lflow_output_cleanup(void *data); + +#endif /* EN_LFLOW_H */ diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c index 0fef0e5fee..27c5e28d0c 100644 --- a/br-controller/ovn-br-controller.c +++ b/br-controller/ovn-br-controller.c @@ -22,25 +22,30 @@ #include /* OVS includes. */ -#include "openvswitch/vlog.h" #include "lib/command-line.h" #include "lib/daemon.h" #include "lib/dirs.h" #include "lib/fatal-signal.h" #include "lib/stream.h" #include "lib/stream-ssl.h" +#include "lib/vswitch-idl.h" #include "lib/unixctl.h" +#include "openvswitch/poll-loop.h" +#include "openvswitch/vlog.h" + /* OVN includes. */ +#include "en-bridge-data.h" +#include "en-lflow.h" #include "lib/ovn-br-idl.h" +#include "lib/inc-proc-eng.h" #include "lib/ovn-util.h" VLOG_DEFINE_THIS_MODULE(main); -static void parse_options(int argc, char *argv[]); +static char *parse_options(int argc, char *argv[]); OVS_NO_RETURN static void usage(void); - /* SSL/TLS options. */ static const char *ssl_private_key_file; static const char *ssl_certificate_file; @@ -49,20 +54,225 @@ static const char *ssl_ca_cert_file; /* --unixctl-path: Path to use for unixctl server socket. */ static char *unixctl_path; +#define BRCTL_NODES \ + BRCTL_NODE(br_global) \ + BRCTL_NODE(bridge) \ + BRCTL_NODE(logical_flow) + +enum brctl_engine_node { +#define BRCTL_NODE(NAME) BRCTL_##NAME, + BRCTL_NODES +#undef BRCTL_NODE +}; + +#define BRCTL_NODE(NAME) ENGINE_FUNC_BR(NAME); + BRCTL_NODES +#undef BRCTL_NODE + +#define OVS_NODES \ + OVS_NODE(open_vswitch) \ + OVS_NODE(bridge) \ + OVS_NODE(port) \ + OVS_NODE(interface) + +enum ovs_engine_node { +#define OVS_NODE(NAME) OVS_##NAME, + OVS_NODES +#undef OVS_NODE +}; + +#define OVS_NODE(NAME) ENGINE_FUNC_OVS(NAME); + OVS_NODES +#undef OVS_NODE + +/* Engine static functions. */ +static void * +en_br_controller_output_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return NULL; +} + +static void +en_br_controller_output_cleanup(void *data OVS_UNUSED) +{ + +} + +static enum engine_node_state +en_br_controller_output_run(struct engine_node *node OVS_UNUSED, + void *data OVS_UNUSED) +{ + return EN_UPDATED; +} + +/* Static function declarations. */ +static void ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl); +static void update_br_db(struct ovsdb_idl *ovs_idl, + struct ovsdb_idl *ovn_br_idl); + int main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { + struct unixctl_server *unixctl; + struct ovn_exit_args exit_args = {0}; + int retval; + ovs_cmdl_proctitle_init(argc, argv); ovn_set_program_name(argv[0]); service_start(&argc, &argv); - parse_options(argc, argv); + char *ovs_remote = parse_options(argc, argv); fatal_ignore_sigpipe(); - return 0; + daemonize_start(true, false); + + char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path); + retval = unixctl_server_create(abs_unixctl_path, &unixctl); + free(abs_unixctl_path); + if (retval) { + exit(EXIT_FAILURE); + } + unixctl_command_register("exit", "", 0, 1, ovn_exit_command_callback, + &exit_args); + + daemonize_complete(); + + /* Connect to OVS OVSDB instance. */ + struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( + ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); + ctrl_register_ovs_idl(ovs_idl_loop.idl); + + ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl); + + /* Configure OVN bridge database. */ + struct ovsdb_idl_loop ovnbr_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( + ovsdb_idl_create_unconnected(&ovnbrrec_idl_class, true)); + ovsdb_idl_set_leader_only(ovnbr_idl_loop.idl, false); + + ovsdb_idl_track_add_all(ovnbr_idl_loop.idl); + + unixctl_command_register("connection-status", "", 0, 0, + ovn_conn_show, ovnbr_idl_loop.idl); + + /* We don't want to monitor Connection table at all. So omit all the + * columns. */ + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_external_ids); + ovsdb_idl_omit(ovnbr_idl_loop.idl, + &ovnbrrec_connection_col_inactivity_probe); + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_is_connected); + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_max_backoff); + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_other_config); + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_status); + ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_target); + + /* Define inc-proc-engine nodes. */ + ENGINE_NODE(bridge_data); + ENGINE_NODE(lflow_output); + ENGINE_NODE(br_controller_output); + +#define BRCTL_NODE(NAME) ENGINE_NODE_BR(NAME); + BRCTL_NODES +#undef BRCTL_NODE + +#define OVS_NODE(NAME) ENGINE_NODE_OVS(NAME); + OVS_NODES +#undef OVS_NODE + + engine_add_input(&en_bridge_data, &en_ovs_open_vswitch, NULL); + engine_add_input(&en_bridge_data, &en_ovs_bridge, NULL); + engine_add_input(&en_bridge_data, &en_ovs_port, NULL); + engine_add_input(&en_bridge_data, &en_ovs_interface, NULL); + + engine_add_input(&en_bridge_data, &en_ovnbr_br_global, NULL); + engine_add_input(&en_bridge_data, &en_ovnbr_bridge, NULL); + + engine_add_input(&en_lflow_output, &en_bridge_data, NULL); + engine_add_input(&en_lflow_output, &en_ovnbr_logical_flow, NULL); + engine_add_input(&en_br_controller_output, &en_lflow_output, NULL); + + struct engine_arg engine_arg = { + .ovs_idl = ovs_idl_loop.idl, + .ovnbr_idl = ovnbr_idl_loop.idl, + }; + engine_init(&en_br_controller_output, &engine_arg); + + unsigned int ovs_cond_seqno = UINT_MAX; + unsigned int ovnbr_cond_seqno = UINT_MAX; + + /* Main loop. */ + while (!exit_args.exiting) { + engine_init_run(); + + struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop); + unsigned int new_ovs_cond_seqno + = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl); + if (new_ovs_cond_seqno != ovs_cond_seqno) { + if (!new_ovs_cond_seqno) { + VLOG_INFO("OVS IDL reconnected, force recompute."); + engine_set_force_recompute(); + } + ovs_cond_seqno = new_ovs_cond_seqno; + } + + update_br_db(ovs_idl_loop.idl, ovnbr_idl_loop.idl); + struct ovsdb_idl_txn *ovnbr_idl_txn + = ovsdb_idl_loop_run(&ovnbr_idl_loop); + unsigned int new_ovnbr_cond_seqno + = ovsdb_idl_get_condition_seqno(ovnbr_idl_loop.idl); + if (new_ovnbr_cond_seqno != ovnbr_cond_seqno) { + if (!new_ovnbr_cond_seqno) { + VLOG_INFO("OVNBR IDL reconnected, force recompute."); + engine_set_force_recompute(); + } + ovnbr_cond_seqno = new_ovnbr_cond_seqno; + } + + struct engine_context eng_ctx = { + .ovs_idl_txn = ovs_idl_txn, + .ovnbr_idl_txn = ovnbr_idl_txn, + }; + + engine_set_context(&eng_ctx); + + const struct ovsrec_open_vswitch_table *ovs_table = + ovsrec_open_vswitch_table_get(ovs_idl_loop.idl); + const struct ovsrec_open_vswitch *cfg = + ovsrec_open_vswitch_table_first(ovs_table); + + if (ovsdb_idl_has_ever_connected(ovnbr_idl_loop.idl) && cfg) { + engine_run(true); + } + + unixctl_server_run(unixctl); + + unixctl_server_wait(unixctl); + if (exit_args.exiting) { + poll_immediate_wake(); + } + + ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop); + ovsdb_idl_loop_commit_and_wait(&ovnbr_idl_loop); + ovsdb_idl_track_clear(ovnbr_idl_loop.idl); + ovsdb_idl_track_clear(ovs_idl_loop.idl); + + poll_block(); + if (should_service_stop()) { + exit_args.exiting = true; + } + } + + engine_set_context(NULL); + engine_cleanup(); + + free(ovs_remote); + ovn_exit_args_finish(&exit_args); + unixctl_server_destroy(unixctl); + service_stop(); + exit(0); } /* static functions. */ -static void +static char * parse_options(int argc, char *argv[]) { enum { @@ -153,6 +363,17 @@ parse_options(int argc, char *argv[]) argc -= optind; argv += optind; + + char *ovs_remote = NULL; + if (argc == 0) { + ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir()); + } else if (argc == 1) { + ovs_remote = xstrdup(argv[0]); + } else { + VLOG_FATAL("exactly zero or one non-option argument required; " + "use --help for usage"); + } + return ovs_remote; } static void @@ -173,3 +394,49 @@ usage(void) " -V, --version display version information\n"); exit(EXIT_SUCCESS); } + +static void +ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) +{ + /* We do not monitor all tables by default, so modules must register + * their interest explicitly. + * When the same column is monitored in different modes by different + * modules, there is a chance that "track" flag added by + * ovsdb_idl_track_add_column by one module being overwritten by a + * following ovsdb_idl_add_column by another module. Before this is fixed + * in OVSDB IDL, we need to be careful about the order so that the "track" + * calls are after the "non-track" calls. */ + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch); + ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_other_config); + ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges); + ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_datapaths); + ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids); + + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_bridge_col_name); + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids); + + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); + + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids); +} + +/* Retrieves the pointer to the OVN Bridge Controller database from 'ovs_idl' + * and updates 'brdb_idl' with that pointer. */ +static void +update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl) +{ + const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); + if (!cfg) { + return; + } + + const char *remote = smap_get(&cfg->external_ids, "ovn-br-remote"); + ovsdb_idl_set_remote(ovnbr_idl, remote, true); +} diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h index d9174effa7..5a486f86f8 100644 --- a/lib/inc-proc-eng.h +++ b/lib/inc-proc-eng.h @@ -157,6 +157,7 @@ struct engine_context { struct ovsdb_idl_txn *ovs_idl_txn; struct ovsdb_idl_txn *ovnsb_idl_txn; struct ovsdb_idl_txn *ovnnb_idl_txn; + struct ovsdb_idl_txn *ovnbr_idl_txn; void *client_ctx; }; @@ -165,6 +166,7 @@ struct engine_context { struct engine_arg { struct ovsdb_idl *sb_idl; struct ovsdb_idl *nb_idl; + struct ovsdb_idl *ovnbr_idl; struct ovsdb_idl *ovs_idl; }; @@ -521,6 +523,11 @@ en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node) \ #define ENGINE_FUNC_NB(TBL_NAME) \ ENGINE_FUNC_OVSDB(nb, TBL_NAME) +/* Macro to define member functions of an engine node which represents + * a table of OVN BR DB */ +#define ENGINE_FUNC_BR(TBL_NAME) \ + ENGINE_FUNC_OVSDB(ovnbr, TBL_NAME) + /* Macro to define member functions of an engine node which represents * a table of open_vswitch DB */ #define ENGINE_FUNC_OVS(TBL_NAME) \ @@ -540,6 +547,10 @@ en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node) \ #define ENGINE_NODE_NB(TBL_NAME) \ ENGINE_NODE_OVSDB(nb, "NB", TBL_NAME, #TBL_NAME); +/* Macro to define an engine node which represents a table of OVN BR DB */ +#define ENGINE_NODE_BR(TBL_NAME) \ + ENGINE_NODE_OVSDB(ovnbr, "BR", TBL_NAME, #TBL_NAME); + /* Macro to define an engine node which represents a table of open_vswitch * DB */ #define ENGINE_NODE_OVS(TBL_NAME) \ From patchwork Mon Aug 11 10:09:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121389 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rTx55Zlz1xx2 for ; Mon, 11 Aug 2025 20:29:25 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 2F13E61300; Mon, 11 Aug 2025 10:29:33 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 4a02DZMeYz8X; Mon, 11 Aug 2025 10:29:31 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 8BF9F612F4 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 8BF9F612F4; Mon, 11 Aug 2025 10:29:31 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 6FFC7C0889; Mon, 11 Aug 2025 10:29:31 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7DCA7C02A4 for ; Mon, 11 Aug 2025 10:29:30 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 642D94125D for ; Mon, 11 Aug 2025 10:29:30 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id SJQq1meIFiSf for ; Mon, 11 Aug 2025 10:29:29 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org 5F39241092 Authentication-Results: smtp4.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 5F39241092 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp4.osuosl.org (Postfix) with ESMTPS id 5F39241092 for ; Mon, 11 Aug 2025 10:29:29 +0000 (UTC) Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 5F974581BEE for ; Mon, 11 Aug 2025 10:09:55 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id C20B6443AC; Mon, 11 Aug 2025 10:09:47 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:39:42 +0530 Message-ID: <20250811100942.917873-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeekiefgjedukeffheethfduvdehveegffethedtudetgeeuhfekgfelledutefhjeenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 03/12] ovn-br-controller: Build en_bridge_data engine node. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- br-controller/automake.mk | 6 +- br-controller/en-bridge-data.c | 122 +++++++++++++++++++++++++++++- br-controller/en-bridge-data.h | 23 ++++++ br-controller/ovn-br-controller.c | 4 + 4 files changed, 148 insertions(+), 7 deletions(-) diff --git a/br-controller/automake.mk b/br-controller/automake.mk index 497f440551..b77e2abcff 100644 --- a/br-controller/automake.mk +++ b/br-controller/automake.mk @@ -1,10 +1,10 @@ bin_PROGRAMS += br-controller/ovn-br-controller br_controller_ovn_br_controller_SOURCES = \ - br-controller/ovn-br-controller.c \ + br-controller/en-bridge-data.c \ + br-controller/en-bridge-data.h \ br-controller/en-lflow.c \ br-controller/en-lflow.h \ - br-controller/en-bridge-data.c \ - br-controller/en-bridge-data.h + br-controller/ovn-br-controller.c br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += br-controller/ovn-br-controller.8 diff --git a/br-controller/en-bridge-data.c b/br-controller/en-bridge-data.c index 293ae895c0..483c784a37 100644 --- a/br-controller/en-bridge-data.c +++ b/br-controller/en-bridge-data.c @@ -19,27 +19,141 @@ #include /* OVS includes. */ +#include "include/openvswitch/hmap.h" +#include "include/openvswitch/shash.h" +#include "lib/vswitch-idl.h" #include "openvswitch/vlog.h" /* OVN includes. */ #include "en-bridge-data.h" +#include "lib/ovn-br-idl.h" VLOG_DEFINE_THIS_MODULE(en_bridge_data); +static void ovn_bridges_init(struct shash *); +static void ovn_bridges_cleanup(struct shash *); +static void ovn_bridges_run(const struct ovnbrrec_bridge_table *, + struct shash *bridges, + struct ovsdb_idl_index *); +static void ovn_bridge_destroy(struct ovn_bridge *); +static const struct ovsrec_bridge *ovsbridge_lookup_by_name( + struct ovsdb_idl_index *ovsrec_bridge_by_name, + const char *name); +static void build_ovn_bridge_iface_simap(struct ovn_bridge *); + void * en_bridge_data_init(struct engine_node *node OVS_UNUSED, struct engine_arg *arg OVS_UNUSED) { - return NULL; + struct ed_type_bridge_data *data = xzalloc(sizeof *data); + ovn_bridges_init(&data->bridges); + + return data; } void -en_bridge_data_cleanup(void *data OVS_UNUSED) +en_bridge_data_cleanup(void *data_) { + struct ed_type_bridge_data *data = data_; + ovn_bridges_cleanup(&data->bridges); } enum engine_node_state -en_bridge_data_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_bridge_data_run(struct engine_node *node, void *data_) +{ + const struct ovnbrrec_bridge_table *ovnbrrec_br_table = + EN_OVSDB_GET(engine_get_input("BR_bridge", node)); + struct ovsdb_idl_index *ovsrec_bridge_by_name = + engine_ovsdb_node_get_index(engine_get_input("OVS_bridge", node), + "name"); + struct ed_type_bridge_data *data = data_; + + ovn_bridges_cleanup(&data->bridges); + ovn_bridges_init(&data->bridges); + ovn_bridges_run(ovnbrrec_br_table, &data->bridges, ovsrec_bridge_by_name); + + return EN_UPDATED; +} + +/* Static functions. */ +static void +ovn_bridges_init(struct shash *bridges) { - return EN_UNCHANGED; + shash_init(bridges); +} + +static void +ovn_bridges_cleanup(struct shash *bridges) +{ + struct shash_node *shash_node; + SHASH_FOR_EACH_SAFE (shash_node, bridges) { + ovn_bridge_destroy(shash_node->data); + } + shash_destroy(bridges); +} + +static void +ovn_bridges_run(const struct ovnbrrec_bridge_table *br_table, + struct shash *bridges, + struct ovsdb_idl_index *ovsrec_bridge_by_name) +{ + const struct ovnbrrec_bridge *db_br; + OVNBRREC_BRIDGE_TABLE_FOR_EACH (db_br, br_table) { + struct ovn_bridge *br = xzalloc(sizeof *br); + br->db_br = db_br; + br->key = db_br->header_.uuid; + simap_init(&br->ovs_ifaces); + shash_add(bridges, db_br->name, br); + + const struct ovsrec_bridge *ovs_br = + ovsbridge_lookup_by_name(ovsrec_bridge_by_name, db_br->name); + + if (!ovs_br) { + continue; + } + + br->ovs_br = ovs_br; + build_ovn_bridge_iface_simap(br); + } +} + +static void +ovn_bridge_destroy(struct ovn_bridge *br) +{ + simap_destroy(&br->ovs_ifaces); + free(br); +} + +static const struct ovsrec_bridge * +ovsbridge_lookup_by_name(struct ovsdb_idl_index *ovsrec_bridge_by_name, + const char *name) +{ + struct ovsrec_bridge *target = + ovsrec_bridge_index_init_row(ovsrec_bridge_by_name); + ovsrec_bridge_index_set_name(target, name); + + const struct ovsrec_bridge *retval = + ovsrec_bridge_index_find(ovsrec_bridge_by_name, target); + ovsrec_bridge_index_destroy_row(target); + + return retval; +} + +static void +build_ovn_bridge_iface_simap(struct ovn_bridge *br) +{ + ovs_assert(br->ovs_br); + for (size_t i = 0; i < br->ovs_br->n_ports; i++) { + const struct ovsrec_port *port_rec = br->ovs_br->ports[i]; + + for (size_t j = 0; j < port_rec->n_interfaces; j++) { + const struct ovsrec_interface *iface_rec; + + iface_rec = port_rec->interfaces[j]; + int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0; + if (ofport) { + simap_put(&br->ovs_ifaces, iface_rec->name, ofport); + } + } + } } diff --git a/br-controller/en-bridge-data.h b/br-controller/en-bridge-data.h index 4a280b5cd1..b374798649 100644 --- a/br-controller/en-bridge-data.h +++ b/br-controller/en-bridge-data.h @@ -7,8 +7,31 @@ #include #include +/* OVS includes. */ +#include "lib/simap.h" +#include "include/openvswitch/shash.h" +#include "lib/uuid.h" + +/* OVN includes. */ #include "lib/inc-proc-eng.h" +struct ovnbrrec_bridge; +struct ovsrec_bridge; + +struct ovn_bridge { + struct uuid key; /* ovnbrrec_bridge->header_.uuid */ + + const struct ovnbrrec_bridge *db_br; + const struct ovsrec_bridge *ovs_br; + + /* simap of ovs interface names to ofport numbers. */ + struct simap ovs_ifaces; +}; + +struct ed_type_bridge_data { + struct shash bridges; +}; + enum engine_node_state en_bridge_data_run(struct engine_node *, void *data); void *en_bridge_data_init(struct engine_node *node, struct engine_arg *arg); void en_bridge_data_cleanup(void *data); diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c index 27c5e28d0c..7cfe6ac23d 100644 --- a/br-controller/ovn-br-controller.c +++ b/br-controller/ovn-br-controller.c @@ -141,6 +141,9 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); ctrl_register_ovs_idl(ovs_idl_loop.idl); + struct ovsdb_idl_index *ovsrec_bridge_by_name + = ovsdb_idl_index_create1(ovs_idl_loop.idl, + &ovsrec_bridge_col_name); ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl); @@ -195,6 +198,7 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) .ovnbr_idl = ovnbr_idl_loop.idl, }; engine_init(&en_br_controller_output, &engine_arg); + engine_ovsdb_node_add_index(&en_ovs_bridge, "name", ovsrec_bridge_by_name); unsigned int ovs_cond_seqno = UINT_MAX; unsigned int ovnbr_cond_seqno = UINT_MAX; From patchwork Mon Aug 11 10:09:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121393 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rXb5FdMz1xvJ for ; Mon, 11 Aug 2025 20:31:43 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 6B7EE40CA0; Mon, 11 Aug 2025 10:31:49 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id kHowAxOmpDWS; Mon, 11 Aug 2025 10:31:47 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 1927B4012D Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTPS id 1927B4012D; Mon, 11 Aug 2025 10:31:47 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id EA02AC0889; Mon, 11 Aug 2025 10:31:46 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 32A4CC02A4 for ; Mon, 11 Aug 2025 10:31:45 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 0FE2361261 for ; Mon, 11 Aug 2025 10:31:45 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 5TiyiDosRc90 for ; Mon, 11 Aug 2025 10:31:42 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org E607461233 Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org E607461233 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp3.osuosl.org (Postfix) with ESMTPS id E607461233 for ; Mon, 11 Aug 2025 10:31:41 +0000 (UTC) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::228]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 29649581C05 for ; Mon, 11 Aug 2025 10:10:04 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id ECBCD43397; Mon, 11 Aug 2025 10:09:54 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:39:49 +0530 Message-ID: <20250811100949.917893-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpefgteetudffjeehhfffkeetteelheelheekhffhudejuefhveffudelkeehteektdenucffohhmrghinheprghprggthhgvrdhorhhgnecukfhppedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieeknecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikedphhgvlhhopehfvgguohhrrgdrughomhgrihhnrdhnrghmvgdpmhgrihhlfhhrohhmpehnuhhmrghnshesohhvnhdrohhrghdpnhgspghrtghpthhtohepvddprhgtphhtthhopeguvghvsehophgvnhhvshifihhttghhrdhorhhgpdhrtghpthhtohepnhhumhgrnhhssehovhhnrdhorhhg X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 04/12] ovn-br-controller: Add bridge flow table manager. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique --- br-controller/automake.mk | 2 + br-controller/br-flow-mgr.c | 1302 +++++++++++++++++++++++++++++++++++ br-controller/br-flow-mgr.h | 65 ++ 3 files changed, 1369 insertions(+) create mode 100644 br-controller/br-flow-mgr.c create mode 100644 br-controller/br-flow-mgr.h diff --git a/br-controller/automake.mk b/br-controller/automake.mk index b77e2abcff..ea515a85e4 100644 --- a/br-controller/automake.mk +++ b/br-controller/automake.mk @@ -1,5 +1,7 @@ bin_PROGRAMS += br-controller/ovn-br-controller br_controller_ovn_br_controller_SOURCES = \ + br-controller/br-flow-mgr.c \ + br-controller/br-flow-mgr.h \ br-controller/en-bridge-data.c \ br-controller/en-bridge-data.h \ br-controller/en-lflow.c \ diff --git a/br-controller/br-flow-mgr.c b/br-controller/br-flow-mgr.c new file mode 100644 index 0000000000..2c8c6a4e92 --- /dev/null +++ b/br-controller/br-flow-mgr.c @@ -0,0 +1,1302 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* OVS includes. */ +#include "lib/byte-order.h" +#include "lib/uuid.h" +#include "lib/hash.h" +#include "lib/hmapx.h" +#include "lib/ovs-atomic.h" +#include "openvswitch/ofp-actions.h" +#include "openvswitch/ofp-flow.h" +#include "openvswitch/ofp-group.h" +#include "openvswitch/ofp-match.h" +#include "openvswitch/ofp-msgs.h" +#include "openvswitch/ofp-meter.h" +#include "openvswitch/ofp-packet.h" +#include "openvswitch/ofp-print.h" +#include "openvswitch/ofp-util.h" +#include "openvswitch/ofpbuf.h" +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "br-flow-mgr.h" + +VLOG_DEFINE_THIS_MODULE(brflowmgr); + +static struct hmap br_flow_tables = HMAP_INITIALIZER(&br_flow_tables); + +#define CONJ_ACT_COOKIE UINT64_MAX + +/* Flow handling. */ +struct ovn_flow; + +/* An ovn_flow action. */ +struct ovn_flow_action { + struct ovs_list flow_uuid_action_node; + + struct ofpact *ofpacts; + size_t ofpacts_len; + uint64_t cookie; + struct uuid flow_uuid; + bool stale; + + struct ovs_list list_node; + struct ovn_flow *flow; +}; + +/* An OpenFlow flow. */ +struct ovn_flow { + struct hmap_node hmap_node; + struct hmap *match_flow_table; /* Points to the match flow table hmap for + * easy access. */ + struct hmap *uuid_action_flow_table; /* Points to the uuid action flow + * table hmap for easy access. */ + struct ovs_list flow_list_node; /* List node in + * br_flow_table.flows_list[]. */ + + /* Flow list in which this 'ovn_flow.flow_list_node' is present + * (for easy access). */ + struct ovs_list *flow_list; + + /* Key. */ + uint8_t table_id; + uint16_t priority; + struct minimatch match; + + /* Hash. */ + uint32_t hash; + + /* Actions associated with the flow. + * An ovn_flow can have + * - A normal action - has precendence over all the other below actions + * if set. + * - a list of allow actions - has precedence over drop and conjuctive + * actions. + * - a list of drop actions - has precedence over conjuctive actions. + * - a list of conjuctive actions. + */ + struct ovn_flow_action *normal_act; + struct ovs_list allow_act_list; + struct ovs_list drop_act_list; + struct ovs_list conj_act_list; + + /* Presently installed ofpacts. */ + struct ofpact *installed_ofpacts; + size_t installed_ofpacts_len; + + /* Cookie of the ovn_action which is presently active. */ + uint64_t active_cookie; +}; + +/* Represents a list of 'ovn flow actions' associated with + * a flow uuid. */ +struct flow_uuid_to_acts { + struct hmap_node hmap_node; /* Node in + * ovn_flow_table.uuid_flow_table. */ + struct uuid flow_uuid; + struct ovs_list acts; /* A list of struct ovn_flow_action nodes that + * are referenced by the uuid. */ +}; + +/* Represents a flow table. */ +struct ovn_flow_table { + /* Hash map of flow table using flow match conditions as hash key.*/ + struct hmap match_flow_table; + + /* Hash map of ovn_flow_action list table using uuid as hash key.*/ + struct hmap uuid_action_flow_table; +}; + +/* Represents a bridge flow table. + * + * We maintain 2 lists for openflows list. One represents an active flow + * list and the other represents an old flow list. When a flow is added to + * the br flow table, it is always added to the active flow list. If that flow + * is present in the old flow list, it is removed from the old one. + * + * The function br_flow_populate_oflow_msgs() clears all the + * flows present in the old flow list. + * + * User can swap the lists by calling br_flow_switch_oflow_tables(). + * + */ +struct br_flow_table { + struct hmap_node hmap_node; + char *bridge; /* key. */ + + struct ovn_flow_table lflow_table; /* For logical flows. */ + struct ovn_flow_table pflow_table; /* For physical flows. */ + + struct ovs_list lflows_list[2]; + struct ovs_list pflows_list[2]; + + struct ovs_list *active_lflows; /* Points to one of the element in + * lflows_list. */ + struct ovs_list *old_lflows; /* Points to other element in + * lflows_list. */ + + struct ovs_list *active_pflows; + struct ovs_list *old_pflows; + + struct hmapx modified_lflows; + struct hmapx modified_pflows; +}; + +/* Static functions. */ +static void br_flow_switch_lflow_table__(struct br_flow_table *); +static void br_flow_switch_pflow_table__(struct br_flow_table *); +static void br_flow_table_destroy__(struct br_flow_table *); + +static void br_flow_add_openflow__(struct br_flow_table *, + bool lflow_table, + uint8_t table_id, uint16_t priority, + uint64_t cookie, const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid); +static void br_flows_remove(struct br_flow_table *, bool lflow_table, + const struct uuid *flow_uuid); + +static void ovn_flow_table_init(struct ovn_flow_table *); +static void ovn_flow_table_clear(struct ovn_flow_table *); +static void ovn_flow_table_destroy(struct ovn_flow_table *); + +static uint32_t ovn_flow_match_hash__(uint8_t table_id, uint16_t priority, + const struct minimatch *); +static uint32_t ovn_flow_match_hash(const struct ovn_flow *); + +static void ovn_flow_init(struct ovn_flow *, uint8_t table_id, + uint16_t priority, const struct match *); +static void ovn_flow_uninit(struct ovn_flow *); +static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t priority, + const struct match *); +static void ovn_flow_destroy(struct ovn_flow *); + +static struct ovn_flow_action *ovn_flow_action_alloc(const struct ofpbuf *, + uint64_t cookie, + const struct uuid *); +static void ovn_flow_action_init(struct ovn_flow_action *, + const struct ofpbuf *, uint64_t cookie, + const struct uuid *); +static void ovn_flow_action_destroy(struct ovn_flow_action *); +static struct ovn_flow *ovn_flow_lookup(struct ovn_flow_table *, + uint8_t table_id, uint16_t priority, + const struct minimatch *); +static void ovn_flow_invalidate_all_actions(struct ovn_flow *f); +static void ovn_flow_clear_actions_from_list(struct ovs_list *act_list); +static void ovn_flow_invalidate_actions_from_list(struct ovs_list *act_list); +static void ovn_flow_clear_actions(struct ovn_flow *); +static bool ovn_flow_has_active_actions(struct ovn_flow *); + +static bool ovn_flow_action_is_normal(const struct ovn_flow_action *); +static bool ovn_flow_action_is_allow(const struct ovn_flow_action *); +static bool ovn_flow_action_is_drop(const struct ovn_flow_action *); +static bool ovn_flow_action_is_conj(const struct ovn_flow_action *); + +static bool ovn_flow_has_action(struct ovn_flow *, struct ovn_flow_action *); +static struct flow_uuid_to_acts *flow_uuid_to_acts_lookup( + struct hmap *uuid_action_table, const struct uuid *flow_uuid); +static void ovn_flow_action_link_to_flow_uuid( + struct hmap *uuid_action_table, struct ovn_flow_action *); +static void ovn_flow_unref_action__(struct ovn_flow *, + struct ovn_flow_action *); +static struct ovn_flow_action * ovn_flow_action_get_matching_in_list( + struct ovs_list *act_list, struct ovn_flow_action *a); +static void ovn_flow_unref_action(struct ovn_flow *, struct ovn_flow_action *); +static struct ovn_flow_action *ovn_flow_get_matching_action( + struct ovn_flow *, struct ovn_flow_action *); + +static bool ovn_flow_update_actions(struct ovn_flow_table *, + struct ovn_flow *, + struct ovn_flow_action *); + +static void ovn_flow_prepare_ofmsg(struct ovn_flow *, struct ovs_list *msgs); +static void br_flow_populate_oflow_msgs__(struct br_flow_table *, + struct ovs_list *msgs); + +static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *); +static void add_flow_mod(struct ofputil_flow_mod *, struct ovs_list *msgs); +static void ovn_flow_add_oflow(struct ovn_flow *, struct ovn_flow_action *, + struct ovs_list *msgs); +static void ovn_flow_mod_oflow(struct ovn_flow *, struct ovn_flow_action *, + struct ovs_list *msgs); +static void ovn_flow_del_oflow(struct ovn_flow *, struct ovs_list *msgs); +static void ovn_flow_log(const struct ovn_flow *); +static char * ovn_flow_to_string(const struct ovn_flow *); + +void +br_flow_tables_init(void) +{ + +} + +void +br_flow_tables_destroy(void) +{ + struct br_flow_table *br_table; + HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) { + br_flow_table_destroy__(br_table); + } + + hmap_destroy(&br_flow_tables); +} + +struct br_flow_table * +br_flow_table_get(const char *bridge) +{ + struct br_flow_table *br_table; + uint32_t hash = hash_string(bridge, 0); + HMAP_FOR_EACH_WITH_HASH (br_table, hmap_node, hash, &br_flow_tables) { + if (!strcmp(br_table->bridge, bridge)) { + return br_table; + } + } + + return NULL; +} + +struct br_flow_table * +br_flow_table_alloc(const char *bridge) +{ + struct br_flow_table *br_table = xzalloc(sizeof *br_table); + ovn_flow_table_init(&br_table->lflow_table); + ovn_flow_table_init(&br_table->pflow_table); + + ovs_list_init(&br_table->lflows_list[0]); + ovs_list_init(&br_table->lflows_list[1]); + + ovs_list_init(&br_table->pflows_list[0]); + ovs_list_init(&br_table->pflows_list[1]); + + hmapx_init(&br_table->modified_lflows); + hmapx_init(&br_table->modified_pflows); + + br_table->active_lflows = &br_table->lflows_list[0]; + br_table->old_lflows = &br_table->lflows_list[1]; + + br_table->active_pflows = &br_table->pflows_list[0]; + br_table->old_pflows = &br_table->pflows_list[1]; + + br_table->bridge = xstrdup(bridge); + hmap_insert(&br_flow_tables, &br_table->hmap_node, hash_string(bridge, 0)); + return br_table; +} + +void br_flow_table_destroy(const char *bridge) +{ + struct br_flow_table *br_table = br_flow_table_get(bridge); + if (br_table) { + hmap_remove(&br_flow_tables, &br_table->hmap_node); + br_flow_table_destroy__(br_table); + } +} + +void +br_flow_switch_logical_oflow_tables(void) +{ + struct br_flow_table *br_ftable; + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) { + br_flow_switch_lflow_table__(br_ftable); + } +} + +void +br_flow_switch_logical_oflow_table(const char *bridge) +{ + struct br_flow_table *br_ftable; + br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + br_flow_switch_lflow_table__(br_ftable); + } +} + +void +br_flow_switch_physical_oflow_tables(void) +{ + struct br_flow_table *br_ftable; + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) { + br_flow_switch_pflow_table__(br_ftable); + } +} + +void +br_flow_switch_physical_oflow_table(const char *bridge) +{ + struct br_flow_table *br_ftable; + br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + br_flow_switch_pflow_table__(br_ftable); + } +} + +void +br_flow_add_logical_oflow(const char *bridge, uint8_t table_id, + uint16_t priority, uint64_t cookie, + const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (!br_ftable) { + br_ftable = br_flow_table_alloc(bridge); + } + + br_flow_add_openflow__(br_ftable, true, table_id, priority, cookie, match, + actions, flow_uuid); +} + +void +br_flow_add_physical_oflow(const char *bridge, uint8_t table_id, + uint16_t priority, uint64_t cookie, + const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (!br_ftable) { + br_ftable = br_flow_table_alloc(bridge); + } + + br_flow_add_openflow__(br_ftable, false, table_id, priority, cookie, match, + actions, flow_uuid); +} + +void +br_flow_remove_logical_oflows_all(const struct uuid *flow_uuid) +{ + struct br_flow_table *br_ftable; + HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) { + br_flows_remove(br_ftable, true, flow_uuid); + } +} + +void +br_flow_remove_logical_oflows(const char *bridge, const struct uuid *flow_uuid) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + br_flows_remove(br_ftable, true, flow_uuid); + } +} + +void +br_flow_remove_physical_oflows(const char *bridge, + const struct uuid *flow_uuid) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + br_flows_remove(br_ftable, false, flow_uuid); + } +} + +void +br_flow_flush_all_oflows(void) +{ + struct br_flow_table *br_table; + HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) { + br_flow_table_destroy__(br_table); + } + + hmap_destroy(&br_flow_tables); + hmap_init(&br_flow_tables); +} + +void +br_flow_flush_oflows(const char *bridge) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + hmap_remove(&br_flow_tables, &br_ftable->hmap_node); + br_flow_table_destroy__(br_ftable); + } +} + +void +br_flow_populate_oflow_msgs(const char *bridge, struct ovs_list *msgs) +{ + struct br_flow_table *br_ftable = br_flow_table_get(bridge); + if (br_ftable) { + br_flow_populate_oflow_msgs__(br_ftable, msgs); + } +} + +/* Static functions. */ + +static void +br_flow_switch_lflow_table__(struct br_flow_table *br_ftable) +{ + struct ovs_list *t = br_ftable->active_lflows; + br_ftable->active_lflows = br_ftable->old_lflows; + br_ftable->old_lflows = t; + + hmapx_clear(&br_ftable->modified_lflows); +} + +static void +br_flow_switch_pflow_table__(struct br_flow_table *br_ftable) +{ + struct ovs_list *t = br_ftable->active_pflows; + br_ftable->active_pflows = br_ftable->old_pflows; + br_ftable->old_pflows = t; + + hmapx_clear(&br_ftable->modified_pflows); +} + +static void +br_flow_table_destroy__(struct br_flow_table *br_ftable) +{ + hmapx_clear(&br_ftable->modified_lflows); + hmapx_destroy(&br_ftable->modified_lflows); + ovn_flow_table_destroy(&br_ftable->lflow_table); + + hmapx_clear(&br_ftable->modified_pflows); + hmapx_destroy(&br_ftable->modified_pflows); + ovn_flow_table_destroy(&br_ftable->pflow_table); + + free(br_ftable->bridge); + free(br_ftable); +} + +/* This function + * - Looks up in the bridge flow table if the flow F + * with the provided (match, table_id, priority) is already + * present or not. + * + * - If not present it creates an ovn_flow 'F' with the provided + * (match, table_id, priority) and inserts into the flow table. + * + * - Allocates ovn_flow_action 'A' with the given - + * (actions, cookie, flow_uuid). + * + * - If 'F' already has 'A' it does nothing and returns. Otherwise + * it associates 'A' to the 'F' actions. + * + * - Adds 'F' to the active flow list. + */ +static void +br_flow_add_openflow__(struct br_flow_table *br_ftable, bool lflow_table, + uint8_t table_id, uint16_t priority, + uint64_t cookie, const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid) +{ + struct ovn_flow_table *ftable; + struct hmapx *modified_flows; + struct ovs_list *active_flows; + + if (lflow_table) { + ftable = &br_ftable->lflow_table; + modified_flows = &br_ftable->modified_lflows; + active_flows = br_ftable->active_lflows; + } else { + ftable = &br_ftable->pflow_table; + modified_flows = &br_ftable->modified_pflows; + active_flows = br_ftable->active_pflows; + } + + struct minimatch minimatch; + minimatch_init(&minimatch, match); + + struct ovn_flow *f = ovn_flow_lookup(ftable, table_id, priority, + &minimatch); + + minimatch_destroy(&minimatch); + + bool active_flow_list_changed = false; + bool flow_exists = true; + + if (!f) { + f = ovn_flow_alloc(table_id, priority, match); + f->match_flow_table = &ftable->match_flow_table; + f->uuid_action_flow_table = &ftable->uuid_action_flow_table; + ovs_list_init(&f->flow_list_node); + flow_exists = false; + hmap_insert(&ftable->match_flow_table, &f->hmap_node, + f->hash); + } else { + ovs_list_remove(&f->flow_list_node); + active_flow_list_changed = (active_flows != f->flow_list); + } + + f->flow_list = active_flows; + ovs_list_push_back(active_flows, &f->flow_list_node); + + struct ovn_flow_action *a = ovn_flow_action_alloc(actions, cookie, + flow_uuid); + + if (active_flow_list_changed) { + ovn_flow_invalidate_all_actions(f); + } + + bool push_flow_to_switch = true; + if (flow_exists && ovn_flow_has_action(f, a)) { + struct ovn_flow_action *existing_a = + ovn_flow_get_matching_action(f, a); + if (uuid_equals(&existing_a->flow_uuid, flow_uuid)) { + existing_a->stale = false; + } + if (!active_flow_list_changed) { + /* The flow-action pair already exists. Nothing to be done. */ + push_flow_to_switch = false; + } + ovn_flow_action_destroy(a); + } else { + ovn_flow_update_actions(ftable, f, a); + } + + ovn_flow_log(f); + if (push_flow_to_switch) { + hmapx_add(modified_flows, (void *) f); + } +} + +static void +br_flows_remove(struct br_flow_table *br_ftable, bool lflow_table, + const struct uuid *flow_uuid) +{ + struct ovn_flow_table *flow_table; + struct hmapx *modified_flows; + + if (lflow_table) { + flow_table = &br_ftable->lflow_table; + modified_flows = &br_ftable->modified_lflows; + } else { + flow_table = &br_ftable->pflow_table; + modified_flows = &br_ftable->modified_pflows; + } + + struct flow_uuid_to_acts *f_uuid_to_acts = + flow_uuid_to_acts_lookup(&flow_table->uuid_action_flow_table, + flow_uuid); + + if (!f_uuid_to_acts) { + return; + } + + struct ovn_flow_action *a; + LIST_FOR_EACH_POP (a, flow_uuid_action_node, &f_uuid_to_acts->acts) { + struct ovn_flow *f = a->flow; + ovn_flow_unref_action__(a->flow, a); + ovn_flow_action_destroy(a); + hmapx_add(modified_flows, (void *) f); + } + + hmap_remove(&flow_table->uuid_action_flow_table, + &f_uuid_to_acts->hmap_node); + free(f_uuid_to_acts); +} + +/* ovn flow table functions. */ +static void +ovn_flow_table_init(struct ovn_flow_table *flow_table) +{ + hmap_init(&flow_table->match_flow_table); + hmap_init(&flow_table->uuid_action_flow_table); +} + +static void +ovn_flow_table_clear(struct ovn_flow_table *flow_table) +{ + struct flow_uuid_to_acts *f_uuid_to_acts; + HMAP_FOR_EACH_POP (f_uuid_to_acts, hmap_node, + &flow_table->uuid_action_flow_table) { + struct ovn_flow_action *a; + LIST_FOR_EACH_POP (a, flow_uuid_action_node, + &f_uuid_to_acts->acts) { + ovn_flow_unref_action__(a->flow, a); + ovn_flow_action_destroy(a); + } + + free(f_uuid_to_acts); + } + + struct ovn_flow *f; + HMAP_FOR_EACH_POP (f, hmap_node, &flow_table->match_flow_table) { + ovn_flow_destroy(f); + } +} + +static void +ovn_flow_table_destroy(struct ovn_flow_table *flow_table) +{ + ovn_flow_table_clear(flow_table); + hmap_destroy(&flow_table->match_flow_table); + hmap_destroy(&flow_table->uuid_action_flow_table); +} + +static uint32_t +ovn_flow_match_hash__(uint8_t table_id, uint16_t priority, + const struct minimatch *match) +{ + return hash_2words((table_id << 16) | priority, + minimatch_hash(match, 0)); +} + +/* Returns a hash of the match key in 'f'. */ +static uint32_t +ovn_flow_match_hash(const struct ovn_flow *f) +{ + return ovn_flow_match_hash__(f->table_id, f->priority, &f->match); +} + +static void +ovn_flow_action_init(struct ovn_flow_action *a, const struct ofpbuf *actions, + uint64_t cookie, const struct uuid *flow_uuid) +{ + a->ofpacts = xmemdup(actions->data, actions->size); + a->ofpacts_len = actions->size; + a->cookie = cookie; + a->flow_uuid = *flow_uuid; + a->stale = false; + ovs_list_init(&a->list_node); +} + +static void +ovn_flow_action_destroy(struct ovn_flow_action *a) +{ + free(a->ofpacts); + free(a); +} + +static void +ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t priority, + const struct match *match) +{ + f->table_id = table_id; + f->priority = priority; + minimatch_init(&f->match, match); + + f->hash = ovn_flow_match_hash(f); + + ovs_list_init(&f->allow_act_list); + ovs_list_init(&f->drop_act_list); + ovs_list_init(&f->conj_act_list); + + f->installed_ofpacts = NULL; + f->installed_ofpacts_len = 0; + + f->active_cookie = 0; +} + +static struct ovn_flow * +ovn_flow_alloc(uint8_t table_id, uint16_t priority, + const struct match *match) +{ + struct ovn_flow *f = xzalloc(sizeof *f); + ovn_flow_init(f, table_id, priority, match); + + return f; +} + +static struct ovn_flow_action * +ovn_flow_action_alloc(const struct ofpbuf *actions, + uint64_t cookie, const struct uuid *flow_uuid) +{ + struct ovn_flow_action *a = xzalloc(sizeof *a); + ovn_flow_action_init(a, actions, cookie, flow_uuid); + + return a; +} + +/* Finds and returns a ovn_flow in 'flow_table' whose key is identical to + * 'target''s key, or NULL if there is none. + */ +static struct ovn_flow * +ovn_flow_lookup(struct ovn_flow_table *flow_table, + uint8_t table_id, uint16_t priority, + const struct minimatch *match) +{ + size_t hash = ovn_flow_match_hash__(table_id, priority, match); + + struct ovn_flow *f; + HMAP_FOR_EACH_WITH_HASH (f, hmap_node, hash, + &flow_table->match_flow_table) { + if (f->priority == priority && minimatch_equal(&f->match, match)) { + return f; + } + } + + return NULL; +} + +static void +ovn_flow_clear_actions_from_list(struct ovs_list *act_list) +{ + struct ovn_flow_action *a; + LIST_FOR_EACH_POP (a, list_node, act_list) { + ovs_list_remove(&a->flow_uuid_action_node); + ovn_flow_action_destroy(a); + } +} + +static void +ovn_flow_invalidate_actions_from_list(struct ovs_list *act_list) +{ + struct ovn_flow_action *a; + LIST_FOR_EACH (a, list_node, act_list) { + a->stale = true; + } +} + +static void +ovn_flow_clear_actions(struct ovn_flow *f) +{ + if (f->normal_act) { + ovs_list_remove(&f->normal_act->flow_uuid_action_node); + ovn_flow_action_destroy(f->normal_act); + f->normal_act = NULL; + } + + if (!ovs_list_is_empty(&f->allow_act_list)) { + ovn_flow_clear_actions_from_list(&f->allow_act_list); + } + + if (!ovs_list_is_empty(&f->drop_act_list)) { + ovn_flow_clear_actions_from_list(&f->drop_act_list); + } + + if (!ovs_list_is_empty(&f->conj_act_list)) { + ovn_flow_clear_actions_from_list(&f->conj_act_list); + } +} + +static void +ovn_flow_invalidate_all_actions(struct ovn_flow *f) +{ + if (f->normal_act) { + f->normal_act->stale = true; + } + + if (!ovs_list_is_empty(&f->allow_act_list)) { + ovn_flow_invalidate_actions_from_list(&f->allow_act_list); + } + + if (!ovs_list_is_empty(&f->drop_act_list)) { + ovn_flow_invalidate_actions_from_list(&f->drop_act_list); + } + + if (!ovs_list_is_empty(&f->conj_act_list)) { + ovn_flow_invalidate_actions_from_list(&f->conj_act_list); + } +} + +static void +ovn_flow_delete_stale_actions_from_list(struct ovs_list *act_list) +{ + struct ovn_flow_action *a, *next; + LIST_FOR_EACH_SAFE (a, next, list_node, act_list) { + if (a->stale) { + ovn_flow_unref_action(a->flow, a); + } + } +} + +static void +ovn_flow_delete_stale_actions(struct ovn_flow *f) +{ + if (f->normal_act && f->normal_act->stale) { + ovn_flow_unref_action(f, f->normal_act); + f->normal_act = NULL; + } + + if (!ovs_list_is_empty(&f->allow_act_list)) { + ovn_flow_delete_stale_actions_from_list(&f->allow_act_list); + } + + if (!ovs_list_is_empty(&f->drop_act_list)) { + ovn_flow_delete_stale_actions_from_list(&f->drop_act_list); + } + + if (!ovs_list_is_empty(&f->conj_act_list)) { + ovn_flow_delete_stale_actions_from_list(&f->conj_act_list); + } +} + +static void +ovn_flow_uninit(struct ovn_flow *f) +{ + minimatch_destroy(&f->match); + ovn_flow_clear_actions(f); +} + +static void +ovn_flow_destroy(struct ovn_flow *f) +{ + if (f) { + ovn_flow_uninit(f); + free(f->installed_ofpacts); + free(f); + } +} + +static bool +ovn_flow_action_is_drop(const struct ovn_flow_action *f) +{ + return f->ofpacts_len == 0; +} + +static bool +ovn_flow_action_is_conj(const struct ovn_flow_action *f) +{ + const struct ofpact *a = NULL; + + OFPACT_FOR_EACH (a, f->ofpacts, f->ofpacts_len) { + if (a->type == OFPACT_CONJUNCTION) { + return true; + } + } + return false; +} + +static bool +ovn_flow_action_is_allow(const struct ovn_flow_action *f) +{ + if (ovn_flow_action_is_drop(f)) { + return false; + } + + const struct ofpact *a = f->ofpacts; + return (a->type == OFPACT_RESUBMIT && + ofpact_last(a, f->ofpacts, f->ofpacts_len)); +} + +static bool +ovn_flow_action_is_normal(const struct ovn_flow_action *f) +{ + return (!ovn_flow_action_is_allow(f) && !ovn_flow_action_is_drop(f) && + !ovn_flow_action_is_conj(f)); +} + +static struct ovn_flow_action * +ovn_flow_action_get_matching_in_list(struct ovs_list *act_list, + struct ovn_flow_action *a) +{ + struct ovn_flow_action *act_in_list; + LIST_FOR_EACH (act_in_list, list_node, act_list) { + if (ofpacts_equal(act_in_list->ofpacts, + act_in_list->ofpacts_len, + a->ofpacts, + a->ofpacts_len)) { + return act_in_list; + } + } + + return NULL; +} + +static struct ovn_flow_action * +ovn_flow_get_matching_action(struct ovn_flow *f, struct ovn_flow_action *a) +{ + if (f->normal_act && ovn_flow_action_is_normal(a)) { + if (ofpacts_equal(f->normal_act->ofpacts, + f->normal_act->ofpacts_len, + a->ofpacts, + a->ofpacts_len)) { + return f->normal_act; + } + } + + if (ovn_flow_action_is_allow(a)) { + return ovn_flow_action_get_matching_in_list(&f->allow_act_list, a); + } + + if (ovn_flow_action_is_drop(a)) { + return ovn_flow_action_get_matching_in_list(&f->drop_act_list, a); + } + + if (ovn_flow_action_is_conj(a)) { + return ovn_flow_action_get_matching_in_list(&f->conj_act_list, a); + } + + return NULL; +} + +static bool +ovn_flow_has_action(struct ovn_flow *f, struct ovn_flow_action *a) +{ + struct ovn_flow_action *ma = ovn_flow_get_matching_action(f, a); + + if (ovn_flow_action_is_normal(a)) { + return ma ? true: false; + } + + return ma ? uuid_equals(&ma->flow_uuid, &a->flow_uuid) : false; +} + +static bool +ovn_flow_has_active_actions(struct ovn_flow *f) +{ + if (f->normal_act) { + return true; + } + + if (!ovs_list_is_empty(&f->allow_act_list)) { + return true; + } + + if (!ovs_list_is_empty(&f->drop_act_list)) { + return true; + } + + return !ovs_list_is_empty(&f->conj_act_list); +} + +static struct flow_uuid_to_acts * +flow_uuid_to_acts_lookup(struct hmap *uuid_action_table, + const struct uuid *flow_uuid) +{ + struct flow_uuid_to_acts *f_uuid_to_acts; + HMAP_FOR_EACH_WITH_HASH (f_uuid_to_acts, hmap_node, uuid_hash(flow_uuid), + uuid_action_table) { + if (uuid_equals(flow_uuid, &f_uuid_to_acts->flow_uuid)) { + return f_uuid_to_acts; + } + } + return NULL; +} + +static void +ovn_flow_action_link_to_flow_uuid(struct hmap *uuid_action_table, + struct ovn_flow_action *a) +{ + struct flow_uuid_to_acts *f_uuid_to_acts; + f_uuid_to_acts = flow_uuid_to_acts_lookup(uuid_action_table, + &a->flow_uuid); + if (!f_uuid_to_acts) { + f_uuid_to_acts = xzalloc(sizeof *f_uuid_to_acts); + f_uuid_to_acts->flow_uuid = a->flow_uuid; + ovs_list_init(&f_uuid_to_acts->acts); + hmap_insert(uuid_action_table, &f_uuid_to_acts->hmap_node, + uuid_hash(&a->flow_uuid)); + } + + ovs_list_push_back(&f_uuid_to_acts->acts, &a->flow_uuid_action_node); +} + +static void +ovn_flow_unref_action__(struct ovn_flow *f, struct ovn_flow_action *a) +{ + if (f->normal_act == a) { + f->normal_act = NULL; + } else if (!ovs_list_is_empty(&a->list_node)) { + ovs_list_remove(&a->list_node); + } +} + +static void +ovn_flow_unref_action(struct ovn_flow *f, struct ovn_flow_action *a) +{ + ovs_list_remove(&a->flow_uuid_action_node); + ovn_flow_unref_action__(f, a); + ovn_flow_action_destroy(a); +} + +static bool +ovn_flow_update_actions(struct ovn_flow_table *ftable, + struct ovn_flow *f, struct ovn_flow_action *a) +{ + bool flow_updated = true; + if (ovn_flow_action_is_normal(a)) { + if (f->normal_act) { + ovn_flow_unref_action(f, f->normal_act); + } + f->normal_act = a; + } else if (ovn_flow_action_is_allow(a)) { + flow_updated = ovs_list_is_empty(&f->allow_act_list); + ovs_list_push_back(&f->allow_act_list, &a->list_node); + } else if (ovn_flow_action_is_drop(a)) { + flow_updated = ovs_list_is_empty(&f->drop_act_list); + ovs_list_push_back(&f->drop_act_list, &a->list_node); + } else { + /* conj action. */ + ovs_list_push_back(&f->conj_act_list, &a->list_node); + } + + a->flow = f; + a->stale = false; + + ovn_flow_action_link_to_flow_uuid(&ftable->uuid_action_flow_table, a); + + return flow_updated; +} + +static bool +ovn_flow_needs_flow_mod(struct ovn_flow *f, struct ovn_flow_action *a) +{ + if (f->installed_ofpacts_len != a->ofpacts_len) { + return true; + } + + if (f->installed_ofpacts_len && a->ofpacts_len) { + return !ofpacts_equal(f->installed_ofpacts, + f->installed_ofpacts_len, + a->ofpacts, + a->ofpacts_len); + } + + return f->active_cookie ? false : true; +} + +static void +ovn_flow_prepare_ofmsg(struct ovn_flow *f, struct ovs_list *msgs) +{ + struct ovn_flow_action *a = NULL; + struct ovn_flow_action *conj_act = NULL; + + if (f->normal_act) { + a = f->normal_act; + } else if (!ovs_list_is_empty(&f->allow_act_list)) { + a = CONTAINER_OF(ovs_list_front(&f->allow_act_list), + struct ovn_flow_action, + list_node); + } else if (!ovs_list_is_empty(&f->drop_act_list)) { + a = CONTAINER_OF(ovs_list_front(&f->drop_act_list), + struct ovn_flow_action, + list_node); + } else if (!ovs_list_is_empty(&f->conj_act_list)) { + struct ovn_flow_action *c; + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + + LIST_FOR_EACH (c, list_node, &f->conj_act_list) { + ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len); + } + conj_act = xzalloc(sizeof *conj_act); + conj_act->cookie = CONJ_ACT_COOKIE; + conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size); + conj_act->ofpacts_len = ofpacts.size; + ofpbuf_uninit(&ofpacts); + a = conj_act; + } + + if (a) { + /* check if there is really a need to install or not. */ + if (ovn_flow_needs_flow_mod(f, a)) { + if (f->active_cookie == 0) { + ovn_flow_add_oflow(f, a, msgs); + } else { + ovn_flow_mod_oflow(f, a, msgs); + } + free(f->installed_ofpacts); + if (a->ofpacts_len) { + f->installed_ofpacts = xmemdup(a->ofpacts, a->ofpacts_len); + f->installed_ofpacts_len = a->ofpacts_len; + } else { + f->installed_ofpacts = NULL; + f->installed_ofpacts_len = 0; + } + } + } else { + ovn_flow_del_oflow(f, msgs); + free(f->installed_ofpacts); + f->installed_ofpacts = NULL; + f->installed_ofpacts_len = 0; + f->active_cookie = 0; + } + + if (conj_act) { + free(conj_act->ofpacts); + free(conj_act); + } +} + +static void +ovn_flow_handle_modified(struct ovn_flow *f, struct ovs_list *msgs) +{ + ovn_flow_delete_stale_actions(f); + ovn_flow_prepare_ofmsg(f, msgs); + if (!ovn_flow_has_active_actions(f)) { + hmap_remove(f->match_flow_table, &f->hmap_node); + ovs_list_remove(&f->flow_list_node); + ovn_flow_destroy(f); + } +} + +static void +br_flow_populate_oflow_msgs__(struct br_flow_table *br_ftable, + struct ovs_list *msgs) +{ + struct ovn_flow *f; + + LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_lflows) { + ovn_flow_clear_actions(f); + ovn_flow_prepare_ofmsg(f, msgs); + hmap_remove(f->match_flow_table, &f->hmap_node); + hmapx_find_and_delete(&br_ftable->modified_lflows, f); + ovn_flow_destroy(f); + } + + LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_pflows) { + ovn_flow_clear_actions(f); + ovn_flow_prepare_ofmsg(f, msgs); + hmap_remove(f->match_flow_table, &f->hmap_node); + hmapx_find_and_delete(&br_ftable->modified_pflows, f); + ovn_flow_destroy(f); + } + + struct hmapx_node *node; + HMAPX_FOR_EACH (node, &br_ftable->modified_lflows) { + f = node->data; + ovn_flow_handle_modified(f, msgs); + } + + hmapx_clear(&br_ftable->modified_lflows); + + HMAPX_FOR_EACH (node, &br_ftable->modified_pflows) { + f = node->data; + ovn_flow_handle_modified(f, msgs); + } + + hmapx_clear(&br_ftable->modified_pflows); +} + +/* Flow table update. */ + +static struct ofpbuf * +encode_flow_mod(struct ofputil_flow_mod *fm) +{ + fm->buffer_id = UINT32_MAX; + fm->out_port = OFPP_ANY; + fm->out_group = OFPG_ANY; + return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF15_OXM); +} + +static void +add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs) +{ + struct ofpbuf *msg = encode_flow_mod(fm); + ovs_list_push_back(msgs, &msg->list_node); +} + +static void +ovn_flow_add_oflow(struct ovn_flow *f, struct ovn_flow_action *a, + struct ovs_list *msgs) +{ + /* Send flow_mod to add flow. */ + struct ofputil_flow_mod fm = { + .match = f->match, + .priority = f->priority, + .table_id = f->table_id, + .ofpacts = a->ofpacts, + .ofpacts_len = a->ofpacts_len, + .new_cookie = htonll(a->cookie), + .command = OFPFC_ADD, + }; + add_flow_mod(&fm, msgs); + f->active_cookie = a->cookie; +} + +static void +ovn_flow_mod_oflow(struct ovn_flow *f, struct ovn_flow_action *a, + struct ovs_list *msgs) +{ + /* Update actions in installed flow. */ + struct ofputil_flow_mod fm = { + .match = f->match, + .priority = f->priority, + .table_id = f->table_id, + .ofpacts = a->ofpacts, + .ofpacts_len = a->ofpacts_len, + .command = OFPFC_MODIFY_STRICT, + }; + /* Update cookie if it is changed. */ + if (f->active_cookie != a->cookie) { + fm.modify_cookie = true; + fm.new_cookie = htonll(a->cookie); + /* Use OFPFC_ADD so that cookie can be updated. */ + fm.command = OFPFC_ADD; + } + add_flow_mod(&fm, msgs); + f->active_cookie = a->cookie; +} + +static void +ovn_flow_del_oflow(struct ovn_flow *f, struct ovs_list *msgs) +{ + struct ofputil_flow_mod fm = { + .match = f->match, + .priority = f->priority, + .table_id = f->table_id, + .command = OFPFC_DELETE_STRICT, + }; + add_flow_mod(&fm, msgs); + f->active_cookie = 0; +} + +static char * +ovn_flow_to_string(const struct ovn_flow *f) +{ + struct ds s = DS_EMPTY_INITIALIZER; + + struct ovn_flow_action *a = NULL; + struct ovn_flow_action *conj_act = NULL; + + if (f->normal_act) { + a = f->normal_act; + } else if (!ovs_list_is_empty(&f->allow_act_list)) { + a = CONTAINER_OF(ovs_list_front(&f->allow_act_list), + struct ovn_flow_action, + list_node); + } else if (!ovs_list_is_empty(&f->drop_act_list)) { + a = CONTAINER_OF(ovs_list_front(&f->drop_act_list), + struct ovn_flow_action, + list_node); + } else if (!ovs_list_is_empty(&f->conj_act_list)) { + struct ovn_flow_action *c; + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + + LIST_FOR_EACH (c, list_node, &f->conj_act_list) { + ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len); + } + conj_act = xzalloc(sizeof *conj_act); + conj_act->cookie = CONJ_ACT_COOKIE; + conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size); + conj_act->ofpacts_len = ofpacts.size; + ofpbuf_uninit(&ofpacts); + a = conj_act; + } + + if (a) { + ds_put_format(&s, "cookie=%"PRIx64", ", a->cookie); + } + ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id); + ds_put_format(&s, "priority=%"PRIu16", ", f->priority); + minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY); + ds_put_cstr(&s, ", actions="); + struct ofpact_format_params fp = { .s = &s }; + if (a) { + ofpacts_format(a->ofpacts, a->ofpacts_len, &fp); + } else { + ds_put_cstr(&s, ""); + } + + if (conj_act) { + free(conj_act->ofpacts); + free(conj_act); + } + return ds_steal_cstr(&s); +} + +static void +ovn_flow_log(const struct ovn_flow *f) +{ + if (VLOG_IS_DBG_ENABLED()) { + char *s = ovn_flow_to_string(f); + VLOG_DBG("flow: %s", s); + free(s); + } +} diff --git a/br-controller/br-flow-mgr.h b/br-controller/br-flow-mgr.h new file mode 100644 index 0000000000..68545941a9 --- /dev/null +++ b/br-controller/br-flow-mgr.h @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BR_FLOW_MGR_H +#define BR_FLOW_MGR_H 1 + +#include + +/* OVS includes. */ +#include "openvswitch/hmap.h" +#include "openvswitch/list.h" + +/* OVN includes. */ + +struct match; +struct ofpbuf; +struct uuid; + +#define DP_FLOW_TABLE_GLOBAL_KEY 0 + +void br_flow_tables_init(void); +void br_flow_tables_destroy(void); + +struct br_flow_table *br_flow_table_alloc(const char *bridge); +struct br_flow_table *br_flow_table_get(const char *bridge); +void br_flow_table_destroy(const char *bridge); + +void br_flow_switch_logical_oflow_tables(void); +void br_flow_switch_logical_oflow_table(const char *bridge); +void br_flow_switch_physical_oflow_tables(void); +void br_flow_switch_physical_oflow_table(const char *bridge); + +void br_flow_add_logical_oflow(const char *bridge, uint8_t table_id, + uint16_t priority, uint64_t cookie, + const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid); +void br_flow_add_physical_oflow(const char *bridge, uint8_t table_id, + uint16_t priority, uint64_t cookie, + const struct match *match, + const struct ofpbuf *actions, + const struct uuid *flow_uuid); + +void br_flow_remove_logical_oflows_all(const struct uuid *flow_uuid); +void br_flow_remove_logical_oflows(const char *bridge, + const struct uuid *flow_uuid); +void br_flow_remove_physical_oflows(const char *bridge, + const struct uuid *flow_uuid); +void br_flow_flush_oflows(const char *bridge); +void br_flow_flush_all_oflows(void); + +void br_flow_populate_oflow_msgs(const char *bridge, struct ovs_list *msgs); + +#endif /* BR_FLOW_MGR_H */ From patchwork Mon Aug 11 10:09:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121395 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rYt5sq4z1xvJ for ; Mon, 11 Aug 2025 20:32:50 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 556BB40CD6; Mon, 11 Aug 2025 10:32:58 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 5cPCq1BeYLYN; Mon, 11 Aug 2025 10:32:57 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 4E9E64012D Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTPS id 4E9E64012D; Mon, 11 Aug 2025 10:32:57 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 24D2FC0889; Mon, 11 Aug 2025 10:32:57 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 30919C02A4 for ; Mon, 11 Aug 2025 10:32:56 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 303D983E04 for ; Mon, 11 Aug 2025 10:32:50 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id LX1ryrdUvpYU for ; Mon, 11 Aug 2025 10:32:49 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org E764383E02 Authentication-Results: smtp1.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org E764383E02 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp1.osuosl.org (Postfix) with ESMTPS id E764383E02 for ; Mon, 11 Aug 2025 10:32:48 +0000 (UTC) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 89200581C1C for ; Mon, 11 Aug 2025 10:10:04 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id E4750433E7; Mon, 11 Aug 2025 10:09:59 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:39:56 +0530 Message-ID: <20250811100956.917908-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeekiefgjedukeffheethfduvdehveegffethedtudetgeeuhfekgfelledutefhjeenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 05/12] Move lflow-conj-ids module to lib. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique It will be used by ovn-br-controller. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- controller/automake.mk | 2 -- controller/lflow.h | 2 +- controller/ovn-controller.c | 2 +- lib/automake.mk | 2 ++ {controller => lib}/lflow-conj-ids.c | 0 {controller => lib}/lflow-conj-ids.h | 2 +- {controller => lib}/test-lflow-conj-ids.c | 0 tests/automake.mk | 3 +-- 8 files changed, 6 insertions(+), 7 deletions(-) rename {controller => lib}/lflow-conj-ids.c (100%) rename {controller => lib}/lflow-conj-ids.h (98%) rename {controller => lib}/test-lflow-conj-ids.c (100%) diff --git a/controller/automake.mk b/controller/automake.mk index f0638ea97e..cbbca28dbd 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -22,8 +22,6 @@ controller_ovn_controller_SOURCES = \ controller/lflow.h \ controller/lflow-cache.c \ controller/lflow-cache.h \ - controller/lflow-conj-ids.c \ - controller/lflow-conj-ids.h \ controller/lport.c \ controller/lport.h \ controller/ofctrl.c \ diff --git a/controller/lflow.h b/controller/lflow.h index fa9e795e02..7722bbb430 100644 --- a/controller/lflow.h +++ b/controller/lflow.h @@ -38,7 +38,7 @@ #include #include "lflow-cache.h" -#include "lflow-conj-ids.h" +#include "lib/lflow-conj-ids.h" #include "openvswitch/hmap.h" #include "openvswitch/uuid.h" #include "openvswitch/list.h" diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 945952c5c4..9cad2333ef 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -41,7 +41,7 @@ #include "lb.h" #include "lflow.h" #include "lflow-cache.h" -#include "lflow-conj-ids.h" +#include "lib/lflow-conj-ids.h" #include "lib/vswitch-idl.h" #include "lib/ovsdb-types.h" #include "local_data.h" diff --git a/lib/automake.mk b/lib/automake.mk index 46c24e99d1..5e7720051c 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -26,6 +26,8 @@ lib_libovn_la_SOURCES = \ lib/ovn-parallel-hmap.c \ lib/ip-mcast-index.c \ lib/ip-mcast-index.h \ + lib/lflow-conj-ids.c \ + lib/lflow-conj-ids.h \ lib/mac-binding-index.c \ lib/mac-binding-index.h \ lib/mcast-group-index.c \ diff --git a/controller/lflow-conj-ids.c b/lib/lflow-conj-ids.c similarity index 100% rename from controller/lflow-conj-ids.c rename to lib/lflow-conj-ids.c diff --git a/controller/lflow-conj-ids.h b/lib/lflow-conj-ids.h similarity index 98% rename from controller/lflow-conj-ids.h rename to lib/lflow-conj-ids.h index 1ee8f7c3ea..81b549dfcd 100644 --- a/controller/lflow-conj-ids.h +++ b/lib/lflow-conj-ids.h @@ -47,4 +47,4 @@ void lflow_conj_ids_clear(struct conj_ids *); void lflow_conj_ids_dump(struct conj_ids *, struct ds *out_data); void lflow_conj_ids_set_test_mode(bool); -#endif /* controller/lflow-conj-ids.h */ +#endif /* lib/lflow-conj-ids.h */ diff --git a/controller/test-lflow-conj-ids.c b/lib/test-lflow-conj-ids.c similarity index 100% rename from controller/test-lflow-conj-ids.c rename to lib/test-lflow-conj-ids.c diff --git a/tests/automake.mk b/tests/automake.mk index b96932dc21..06c768d2ee 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -286,9 +286,9 @@ tests_ovstest_SOURCES = \ tests/test-ovn.c \ tests/test-vector.c \ controller/test-lflow-cache.c \ - controller/test-lflow-conj-ids.c \ controller/test-ofctrl-seqno.c \ controller/test-vif-plug.c \ + lib/test-lflow-conj-ids.c \ lib/test-ovn-features.c \ northd/test-ipam.c @@ -300,7 +300,6 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ controller/ha-chassis.$(OBJEXT) \ controller/if-status.$(OBJEXT) \ controller/lflow-cache.$(OBJEXT) \ - controller/lflow-conj-ids.$(OBJEXT) \ controller/local_data.$(OBJEXT) \ controller/lport.$(OBJEXT) \ controller/ofctrl-seqno.$(OBJEXT) \ From patchwork Mon Aug 11 10:10:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121400 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rhm1mSnz1xxp for ; Mon, 11 Aug 2025 20:38:48 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id B22EF4127D; Mon, 11 Aug 2025 10:38:55 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id oTBFlkI_-MZc; Mon, 11 Aug 2025 10:38:53 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 29F1B41289 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 29F1B41289; Mon, 11 Aug 2025 10:38:53 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0E086C0889; Mon, 11 Aug 2025 10:38:53 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id B9CE1C02A4 for ; Mon, 11 Aug 2025 10:38:51 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id B842D40C25 for ; Mon, 11 Aug 2025 10:38:51 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id d0e3f5tcO9aw for ; Mon, 11 Aug 2025 10:38:51 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 5D8954012D Authentication-Results: smtp2.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 5D8954012D Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp2.osuosl.org (Postfix) with ESMTPS id 5D8954012D for ; Mon, 11 Aug 2025 10:38:50 +0000 (UTC) Received: from relay15.mail.gandi.net (relay15.mail.gandi.net [IPv6:2001:4b98:dc4:8::235]) by mslow3.mail.gandi.net (Postfix) with ESMTP id D0B0E5815D2 for ; Mon, 11 Aug 2025 10:10:09 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 585884313F; Mon, 11 Aug 2025 10:10:05 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:01 +0530 Message-ID: <20250811101001.917928-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: spam:medium X-GND-Score: 300 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecundevohgrshhtrghlucdlfedttddmnecujfgurhephffvvefufffkofgjfhgggfestdekredtredttdenucfhrhhomhepnhhumhgrnhhssehovhhnrdhorhhgnecuggftrfgrthhtvghrnhepteduudevheehhefggeeludfftdehuddthfegtdeggfeijeekgfeiuddvfedugeeinecuffhomhgrihhnpehuuhhiugdrphgrrhhtshenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh Subject: [ovs-dev] [PATCH ovn v1 06/12] ovn-br-controller: Process logical flows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique This commit processes the logical flows and converts them to OpenFlow rules. Much of the code is taken from controller/lflow.c. At some point we should consider moving controller/lflow.c to lib/lflow.c and reuse it in both ovn-controller and ovn-br-controller. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- br-controller/en-lflow.c | 312 ++++++++++++++++++++++++++++++++++++++- br-controller/en-lflow.h | 3 + 2 files changed, 310 insertions(+), 5 deletions(-) diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c index b1a6efbf5f..00e24de805 100644 --- a/br-controller/en-lflow.c +++ b/br-controller/en-lflow.c @@ -19,25 +19,327 @@ #include /* OVS includes. */ +#include "lib/coverage.h" #include "openvswitch/vlog.h" /* OVN includes. */ +#include "br-flow-mgr.h" #include "en-lflow.h" +#include "en-bridge-data.h" +#include "include/ovn/actions.h" +#include "lib/inc-proc-eng.h" +#include "lib/lflow-conj-ids.h" +#include "lib/ovn-br-idl.h" VLOG_DEFINE_THIS_MODULE(en_lflow); +COVERAGE_DEFINE(lflow_run); + +struct lflow_output_persistent_data { + struct shash symtab; +}; + +struct ed_type_lflow_output_data { + /* conjunciton ID usage information of lflows */ + struct conj_ids conj_ids; + + /* Data which is persistent and not cleared during + * full recompute. */ + struct lflow_output_persistent_data pd; +}; + +struct lflow_ctx_in { + struct shash *ovn_bridges; + struct shash *symtab; + struct ovnbrrec_logical_flow_table *brrec_lflow_table; +}; + +struct lflow_ctx_out { + struct conj_ids *conj_ids; +}; + +struct lookup_port_aux { + const struct ovnbrrec_logical_flow *lflow; + const struct ovn_bridge *br; +}; + +static struct expr *convert_match_to_expr( + const struct ovnbrrec_logical_flow *lflow, struct expr **prereqs, + struct shash *symtab); +static void init_lflow_ctx(struct engine_node *node, + struct ed_type_bridge_data *, + struct ed_type_lflow_output_data *, + struct lflow_ctx_in *, + struct lflow_ctx_out *); +static void lflow_run(struct lflow_ctx_in *, + struct lflow_ctx_out *); +static bool lookup_port_cb(const void *aux_, const char *port_name, + unsigned int *portp); +static void consider_logical_flow(const struct ovnbrrec_logical_flow *, + const struct ovn_bridge *, + struct lflow_ctx_in *, + struct lflow_ctx_out *); +static void add_matches_to_flow_table(const struct ovnbrrec_logical_flow *, + const struct ovn_bridge *, + struct hmap *matches, + uint8_t ptable, + uint8_t output_ptable, + struct ofpbuf *ovnacts); + void *en_lflow_output_init(struct engine_node *node OVS_UNUSED, - struct engine_arg *arg OVS_UNUSED) + struct engine_arg *arg OVS_UNUSED) { - return NULL; + struct ed_type_lflow_output_data *lflow_data = xzalloc(sizeof *lflow_data); + ovn_init_symtab(&lflow_data->pd.symtab); + lflow_conj_ids_init(&lflow_data->conj_ids); + + return lflow_data; } -void en_lflow_output_cleanup(void *data_ OVS_UNUSED) +void en_lflow_output_cleanup(void *data_) { + struct ed_type_lflow_output_data *lflow_data = data_; + lflow_conj_ids_destroy(&lflow_data->conj_ids); + expr_symtab_destroy(&lflow_data->pd.symtab); + shash_destroy(&lflow_data->pd.symtab); } enum engine_node_state -en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data_) +{ + struct ed_type_lflow_output_data *lflow_data = data_; + struct ed_type_bridge_data *bridge_data = + engine_get_input_data("bridge_data", node); + + lflow_conj_ids_clear(&lflow_data->conj_ids); + br_flow_switch_logical_oflow_tables(); + + struct lflow_ctx_in l_ctx_in; + struct lflow_ctx_out l_ctx_out; + + init_lflow_ctx(node, bridge_data, lflow_data, &l_ctx_in, &l_ctx_out); + lflow_run(&l_ctx_in, &l_ctx_out); + + return EN_UPDATED; +} + +/* Static functions. */ + +static void +init_lflow_ctx(struct engine_node *node, + struct ed_type_bridge_data *bridge_data, + struct ed_type_lflow_output_data *lflow_data, + struct lflow_ctx_in *l_ctx_in, + struct lflow_ctx_out *l_ctx_out) +{ + struct ovnbrrec_logical_flow_table *lflow_table = + (struct ovnbrrec_logical_flow_table *) EN_OVSDB_GET( + engine_get_input("BR_logical_flow", node)); + + l_ctx_in->ovn_bridges = &bridge_data->bridges; + l_ctx_in->brrec_lflow_table = lflow_table; + l_ctx_in->symtab = &lflow_data->pd.symtab; + l_ctx_out->conj_ids = &lflow_data->conj_ids; +} + +static void +lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out) +{ + COVERAGE_INC(lflow_run); + + const struct ovnbrrec_logical_flow *lflow; + OVNBRREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, l_ctx_in->brrec_lflow_table) { + struct ovn_bridge *br = shash_find_data(l_ctx_in->ovn_bridges, + lflow->bridge->name); + if (!br) { + continue; + } + + consider_logical_flow(lflow, br, l_ctx_in, l_ctx_out); + } +} + +static void +consider_logical_flow(const struct ovnbrrec_logical_flow *lflow, + const struct ovn_bridge *br, + struct lflow_ctx_in *l_ctx_in, + struct lflow_ctx_out *l_ctx_out) +{ + uint64_t ovnacts_stub[1024 / 8]; + struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub); + struct expr *prereqs = NULL; + + struct ovnact_parse_params pp = { + .symtab = l_ctx_in->symtab, + .pipeline = OVNACT_P_INGRESS, + .n_tables = 255, + .cur_ltable = lflow->table_id, + }; + + char *error = ovnacts_parse_string(lflow->actions, &pp, + &ovnacts, &prereqs); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", + lflow->actions, error); + free(error); + } + + struct hmap *matches = NULL; + struct expr *expr = NULL; + + expr = convert_match_to_expr(lflow, &prereqs, l_ctx_in->symtab); + if (!expr) { + goto done; + } + + expr = expr_normalize(expr); + + uint32_t start_conj_id = 0; + uint32_t n_conjs = 0; + + struct lookup_port_aux aux = { + .lflow = lflow, + .br = br, + }; + + matches = xmalloc(sizeof *matches); + n_conjs = expr_to_matches(expr, lookup_port_cb, &aux, matches); + if (n_conjs) { + start_conj_id = lflow_conj_ids_alloc(l_ctx_out->conj_ids, + &lflow->header_.uuid, + &br->key, n_conjs); + if (!start_conj_id) { + VLOG_ERR("32-bit conjunction ids exhausted!"); + goto done; + } + + expr_matches_prepare(matches, start_conj_id - 1); + } + + if (hmap_is_empty(matches)) { + VLOG_DBG("lflow "UUID_FMT" matches are empty, skip", + UUID_ARGS(&lflow->header_.uuid)); + goto done; + } + + uint8_t ptable = BR_OFTABLE_LOG_INGRESS_PIPELINE + lflow->table_id; + + add_matches_to_flow_table(lflow, br, matches, ptable, + BR_OFTABLE_SAVE_INPORT, + &ovnacts); + +done: + expr_destroy(expr); + expr_destroy(prereqs); + ovnacts_free(ovnacts.data, ovnacts.size); + ofpbuf_uninit(&ovnacts); + expr_matches_destroy(matches); + free(matches); +} + +/* Converts the match and returns the simplified expr tree. + * + * The caller should evaluate the conditions and normalize the expr tree. + * If parsing is successful, '*prereqs' is also consumed. + */ +static struct expr * +convert_match_to_expr(const struct ovnbrrec_logical_flow *lflow, + struct expr **prereqs, + struct shash *symtab) +{ + char *error = NULL; + + struct expr *e = expr_parse_string(lflow->match, symtab, NULL, NULL, NULL, + NULL, 0, &error); + if (!error) { + if (*prereqs) { + e = expr_combine(EXPR_T_AND, e, *prereqs); + *prereqs = NULL; + } + e = expr_annotate(e, symtab, &error); + } + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s", + lflow->match, error); + free(error); + return NULL; + } + + return expr_simplify(e); +} + +static bool +lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp) { - return EN_UNCHANGED; + if (!strcmp(port_name, "none")) { + *portp = 0; + return true; + } + + const struct lookup_port_aux *aux = aux_; + *portp = simap_get(&aux->br->ovs_ifaces, port_name); + + return *portp > 0; +} + +static void +add_matches_to_flow_table(const struct ovnbrrec_logical_flow *lflow, + const struct ovn_bridge *br, + struct hmap *matches, uint8_t ptable, + uint8_t output_ptable, struct ofpbuf *ovnacts) +{ + struct lookup_port_aux aux = { + .lflow = lflow, + .br = br, + }; + + /* Encode OVN logical actions into OpenFlow. */ + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + struct ovnact_encode_params ep = { + .lookup_port = lookup_port_cb, + .aux = &aux, + .is_switch = true, + .lflow_uuid = lflow->header_.uuid, + + .pipeline = OVNACT_P_INGRESS, + .ingress_ptable = BR_OFTABLE_LOG_INGRESS_PIPELINE, + .egress_ptable = 0, + .output_ptable = output_ptable, + }; + ovnacts_encode(ovnacts->data, ovnacts->size, &ep, &ofpacts); + + struct expr_match *m; + HMAP_FOR_EACH (m, hmap_node, matches) { + if (vector_is_empty(&m->conjunctions)) { + br_flow_add_logical_oflow(br->db_br->name, ptable, + lflow->priority, + lflow->header_.uuid.parts[0], + &m->match, &ofpacts, + &lflow->header_.uuid); + } else { + uint64_t conj_stubs[64 / 8]; + struct ofpbuf conj; + + ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs); + const struct cls_conjunction *src; + VECTOR_FOR_EACH_PTR (&m->conjunctions, src) { + struct ofpact_conjunction *dst = ofpact_put_CONJUNCTION(&conj); + dst->id = src->id; + dst->clause = src->clause; + dst->n_clauses = src->n_clauses; + } + + br_flow_add_logical_oflow(br->db_br->name, ptable, + lflow->priority, + lflow->header_.uuid.parts[0], + &m->match, &conj, + &lflow->header_.uuid); + ofpbuf_uninit(&conj); + } + } + + ofpbuf_uninit(&ofpacts); } diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h index c3889cbdc9..39daa30174 100644 --- a/br-controller/en-lflow.h +++ b/br-controller/en-lflow.h @@ -9,6 +9,9 @@ #include "lib/inc-proc-eng.h" +#define BR_OFTABLE_LOG_INGRESS_PIPELINE 8 +#define BR_OFTABLE_SAVE_INPORT 64 + enum engine_node_state en_lflow_output_run(struct engine_node *, void *data); void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg); void en_lflow_output_cleanup(void *data); From patchwork Mon Aug 11 10:10:06 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121399 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rfB0thHz1xxp for ; Mon, 11 Aug 2025 20:36:34 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 97B59612F7; Mon, 11 Aug 2025 10:36:41 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id eOy86aiK9LYM; Mon, 11 Aug 2025 10:36:40 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 4056F6131E Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id 4056F6131E; Mon, 11 Aug 2025 10:36:40 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 256BFC08B9; Mon, 11 Aug 2025 10:36:40 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 02970C02A4 for ; Mon, 11 Aug 2025 10:36:39 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 7B2E061325 for ; Mon, 11 Aug 2025 10:36:38 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id tmzuTaqodRyF for ; Mon, 11 Aug 2025 10:36:37 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org C5EFE61317 Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org C5EFE61317 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp3.osuosl.org (Postfix) with ESMTPS id C5EFE61317 for ; Mon, 11 Aug 2025 10:36:36 +0000 (UTC) Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 70053581C29 for ; Mon, 11 Aug 2025 10:10:19 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id D6C6F430D1; Mon, 11 Aug 2025 10:10:12 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:06 +0530 Message-ID: <20250811101006.917943-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeeugfethfdtleefudeuhffgvdfhgffhgffhkeefvdejvdfhgeehueetfeevheefheenucffohhmrghinheprghprggthhgvrdhorhhgpdhuuhhiugdrphgrrhhtshenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 07/12] ovn-br-controller: Add en_pflow_output engine. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique --- br-controller/automake.mk | 2 + br-controller/en-lflow.h | 6 + br-controller/en-pflow.c | 192 ++++++++++++++++++++++++++++++ br-controller/en-pflow.h | 16 +++ br-controller/ovn-br-controller.c | 6 + 5 files changed, 222 insertions(+) create mode 100644 br-controller/en-pflow.c create mode 100644 br-controller/en-pflow.h diff --git a/br-controller/automake.mk b/br-controller/automake.mk index ea515a85e4..4baea4f6fe 100644 --- a/br-controller/automake.mk +++ b/br-controller/automake.mk @@ -6,6 +6,8 @@ br_controller_ovn_br_controller_SOURCES = \ br-controller/en-bridge-data.h \ br-controller/en-lflow.c \ br-controller/en-lflow.h \ + br-controller/en-pflow.c \ + br-controller/en-pflow.h \ br-controller/ovn-br-controller.c br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h index 39daa30174..093c2849dd 100644 --- a/br-controller/en-lflow.h +++ b/br-controller/en-lflow.h @@ -12,6 +12,12 @@ #define BR_OFTABLE_LOG_INGRESS_PIPELINE 8 #define BR_OFTABLE_SAVE_INPORT 64 +/* OpenFlow table numbers. */ +#define BR_OFTABLE_PHY_TO_LOG 0 +#define BR_OFTABLE_LOG_INGRESS_PIPELINE 8 +#define BR_OFTABLE_SAVE_INPORT 64 +#define BR_OFTABLE_LOG_TO_PHY 65 + enum engine_node_state en_lflow_output_run(struct engine_node *, void *data); void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg); void en_lflow_output_cleanup(void *data); diff --git a/br-controller/en-pflow.c b/br-controller/en-pflow.c new file mode 100644 index 0000000000..5f4c5b914f --- /dev/null +++ b/br-controller/en-pflow.c @@ -0,0 +1,192 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +/* OVS includes. */ +#include "lib/coverage.h" +#include "lib/flow.h" +#include "lib/simap.h" +#include "lib/vswitch-idl.h" +#include "openvswitch/match.h" +#include "openvswitch/ofp-actions.h" +#include "openvswitch/ofpbuf.h" +#include "openvswitch/ofp-parse.h" + +#include "openvswitch/vlog.h" +#include "openvswitch/uuid.h" + +/* OVN includes. */ +#include "br-flow-mgr.h" +#include "en-lflow.h" +#include "en-pflow.h" +#include "en-bridge-data.h" +#include "include/ovn/logical-fields.h" +#include "lib/ovn-br-idl.h" + +VLOG_DEFINE_THIS_MODULE(en_pflow); + + +static void pflow_run(const struct shash *ovn_bridges); +static void physical_flows_add(const struct ovn_bridge *); + +static void put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, + struct ofpbuf *); +static void put_resubmit(uint8_t table_id, struct ofpbuf *); + +static void add_interface_flows(const char *bridge, + const struct ovsrec_interface *iface_rec, + int64_t, struct ofpbuf *); + +/* Public functions. */ +void *en_pflow_output_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return NULL; +} + +void en_pflow_output_cleanup(void *data_ OVS_UNUSED) +{ +} + +enum engine_node_state +en_pflow_output_run(struct engine_node *node, void *data_ OVS_UNUSED) +{ + struct ed_type_bridge_data *br_data = + engine_get_input_data("bridge_data", node); + + pflow_run(&br_data->bridges); + + return EN_UPDATED; +} + +/* Static functions. */ +static void +pflow_run(const struct shash *ovn_bridges) +{ + br_flow_switch_physical_oflow_tables(); + struct shash_node *shash_node; + SHASH_FOR_EACH (shash_node, ovn_bridges) { + physical_flows_add(shash_node->data); + } +} + +static void +physical_flows_add(const struct ovn_bridge *br) +{ + if (!br->ovs_br) { + return; + } + + struct ofpbuf ofpacts; + ofpbuf_init(&ofpacts, 0); + + struct match match = MATCH_CATCHALL_INITIALIZER; + + /* Table 0 and 65, priority 0, actions=NORMAL + * =================================== + * + */ + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; + br_flow_add_physical_oflow(br->db_br->name, BR_OFTABLE_PHY_TO_LOG, 0, + br->ovs_br->header_.uuid.parts[0], + &match, &ofpacts, &br->ovs_br->header_.uuid); + br_flow_add_physical_oflow(br->db_br->name, BR_OFTABLE_LOG_TO_PHY, 0, + br->ovs_br->header_.uuid.parts[0], + &match, &ofpacts, &br->ovs_br->header_.uuid); + + ofpbuf_clear(&ofpacts); + put_resubmit(BR_OFTABLE_LOG_TO_PHY, &ofpacts); + br_flow_add_physical_oflow(br->db_br->name, BR_OFTABLE_SAVE_INPORT, 0, + br->ovs_br->header_.uuid.parts[0], + &match, &ofpacts, &br->ovs_br->header_.uuid); + + for (size_t i = 0; i < br->ovs_br->n_ports; i++) { + const struct ovsrec_port *port_rec = br->ovs_br->ports[i]; + + for (size_t j = 0; j < port_rec->n_interfaces; j++) { + const struct ovsrec_interface *iface_rec; + + iface_rec = port_rec->interfaces[j]; + int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0; + if (ofport > 0 && ofport != ofp_to_u16(OFPP_LOCAL) && + smap_get_bool(&iface_rec->external_ids, + "ovnbr-managed", true)) { + add_interface_flows(br->db_br->name, iface_rec, + ofport, &ofpacts); + } + } + } + + ofpbuf_uninit(&ofpacts); +} + +static void +put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, + struct ofpbuf *ofpacts) +{ + struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, + mf_from_id(dst), NULL, + NULL); + ovs_be64 n_value = htonll(value); + bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits); + bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits); +} + +static void +put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts) +{ + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); + resubmit->in_port = OFPP_IN_PORT; + resubmit->table_id = table_id; +} + +static void +add_interface_flows(const char *bridge, + const struct ovsrec_interface *iface_rec, + int64_t ofport, struct ofpbuf *ofpacts_p) +{ + struct match match = MATCH_CATCHALL_INITIALIZER; + match_set_in_port(&match, u16_to_ofp(ofport)); + + ofpbuf_clear(ofpacts_p); + put_load(ofport, MFF_LOG_INPORT, 0, 32, ofpacts_p); + + uint32_t iface_metadata = + smap_get_uint(&iface_rec->external_ids, "ovn-iface-metadata", 0); + if (iface_metadata) { + put_load(iface_metadata, MFF_METADATA, 0, 32, ofpacts_p); + } + + put_resubmit(BR_OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); + br_flow_add_physical_oflow(bridge, BR_OFTABLE_PHY_TO_LOG, 100, + iface_rec->header_.uuid.parts[0], + &match, ofpacts_p, + &iface_rec->header_.uuid); + + + match_init_catchall(&match); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, ofport); + + ofpbuf_clear(ofpacts_p); + ofpact_put_OUTPUT(ofpacts_p)->port = u16_to_ofp(ofport); + br_flow_add_physical_oflow(bridge, BR_OFTABLE_LOG_TO_PHY, 100, + iface_rec->header_.uuid.parts[0], + &match, ofpacts_p, + &iface_rec->header_.uuid); +} diff --git a/br-controller/en-pflow.h b/br-controller/en-pflow.h new file mode 100644 index 0000000000..85edf53c17 --- /dev/null +++ b/br-controller/en-pflow.h @@ -0,0 +1,16 @@ +#ifndef EN_PFLOW_H +#define EN_PFLOW_H 1 + +#include + +#include +#include +#include + +#include "lib/inc-proc-eng.h" + +enum engine_node_state en_pflow_output_run(struct engine_node *, void *data); +void *en_pflow_output_init(struct engine_node *node, struct engine_arg *arg); +void en_pflow_output_cleanup(void *data); + +#endif /* EN_PFLOW_H */ diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c index 7cfe6ac23d..ae0e192429 100644 --- a/br-controller/ovn-br-controller.c +++ b/br-controller/ovn-br-controller.c @@ -37,6 +37,7 @@ /* OVN includes. */ #include "en-bridge-data.h" #include "en-lflow.h" +#include "en-pflow.h" #include "lib/ovn-br-idl.h" #include "lib/inc-proc-eng.h" #include "lib/ovn-util.h" @@ -171,6 +172,7 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) /* Define inc-proc-engine nodes. */ ENGINE_NODE(bridge_data); ENGINE_NODE(lflow_output); + ENGINE_NODE(pflow_output); ENGINE_NODE(br_controller_output); #define BRCTL_NODE(NAME) ENGINE_NODE_BR(NAME); @@ -191,7 +193,11 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) engine_add_input(&en_lflow_output, &en_bridge_data, NULL); engine_add_input(&en_lflow_output, &en_ovnbr_logical_flow, NULL); + + engine_add_input(&en_pflow_output, &en_bridge_data, NULL); + engine_add_input(&en_br_controller_output, &en_lflow_output, NULL); + engine_add_input(&en_br_controller_output, &en_pflow_output, NULL); struct engine_arg engine_arg = { .ovs_idl = ovs_idl_loop.idl, From patchwork Mon Aug 11 10:10:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121391 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rWG50DXz1xvJ for ; Mon, 11 Aug 2025 20:30:34 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 3827361308; Mon, 11 Aug 2025 10:30:42 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id k8111Cd5RnuP; Mon, 11 Aug 2025 10:30:41 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 4378B612E2 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 4378B612E2; Mon, 11 Aug 2025 10:30:41 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 22AC3C0889; Mon, 11 Aug 2025 10:30:41 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 50CA2C02A4 for ; Mon, 11 Aug 2025 10:30:40 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 4326840C91 for ; Mon, 11 Aug 2025 10:30:40 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 1--61ckOTm0l for ; Mon, 11 Aug 2025 10:30:39 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 5D2D940C8C Authentication-Results: smtp2.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 5D2D940C8C Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp2.osuosl.org (Postfix) with ESMTPS id 5D2D940C8C for ; Mon, 11 Aug 2025 10:30:39 +0000 (UTC) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 3F30E581C45 for ; Mon, 11 Aug 2025 10:10:23 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 7A9FD4338F; Mon, 11 Aug 2025 10:10:18 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:13 +0530 Message-ID: <20250811101013.917962-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeekiefgjedukeffheethfduvdehveegffethedtudetgeeuhfekgfelledutefhjeenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 08/12] Move ofctrl-seqno module to lib. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- controller/automake.mk | 2 -- controller/if-status.c | 2 +- controller/ovn-controller.c | 2 +- lib/automake.mk | 2 ++ {controller => lib}/ofctrl-seqno.c | 0 {controller => lib}/ofctrl-seqno.h | 0 {controller => lib}/test-ofctrl-seqno.c | 0 tests/automake.mk | 3 +-- 8 files changed, 5 insertions(+), 6 deletions(-) rename {controller => lib}/ofctrl-seqno.c (100%) rename {controller => lib}/ofctrl-seqno.h (100%) rename {controller => lib}/test-ofctrl-seqno.c (100%) diff --git a/controller/automake.mk b/controller/automake.mk index cbbca28dbd..e7c4618ebc 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -26,8 +26,6 @@ controller_ovn_controller_SOURCES = \ controller/lport.h \ controller/ofctrl.c \ controller/ofctrl.h \ - controller/ofctrl-seqno.c \ - controller/ofctrl-seqno.h \ controller/pinctrl.c \ controller/pinctrl.h \ controller/patch.c \ diff --git a/controller/if-status.c b/controller/if-status.c index 5b176b86da..ee9337e636 100644 --- a/controller/if-status.c +++ b/controller/if-status.c @@ -17,7 +17,7 @@ #include "binding.h" #include "if-status.h" -#include "ofctrl-seqno.h" +#include "lib/ofctrl-seqno.h" #include "ovsport.h" #include "simap.h" diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 9cad2333ef..ffe546c63d 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -48,7 +48,7 @@ #include "lport.h" #include "memory.h" #include "ofctrl.h" -#include "ofctrl-seqno.h" +#include "lib/ofctrl-seqno.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovn/actions.h" diff --git a/lib/automake.mk b/lib/automake.mk index 5e7720051c..3924c631cb 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -37,6 +37,8 @@ lib_libovn_la_SOURCES = \ lib/lex.c \ lib/objdep.c \ lib/objdep.h \ + lib/ofctrl-seqno.c \ + lib/ofctrl-seqno.h \ lib/ovn-l7.h \ lib/ovn-l7.c \ lib/ovn-util.c \ diff --git a/controller/ofctrl-seqno.c b/lib/ofctrl-seqno.c similarity index 100% rename from controller/ofctrl-seqno.c rename to lib/ofctrl-seqno.c diff --git a/controller/ofctrl-seqno.h b/lib/ofctrl-seqno.h similarity index 100% rename from controller/ofctrl-seqno.h rename to lib/ofctrl-seqno.h diff --git a/controller/test-ofctrl-seqno.c b/lib/test-ofctrl-seqno.c similarity index 100% rename from controller/test-ofctrl-seqno.c rename to lib/test-ofctrl-seqno.c diff --git a/tests/automake.mk b/tests/automake.mk index 06c768d2ee..e6a31c6f69 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -286,10 +286,10 @@ tests_ovstest_SOURCES = \ tests/test-ovn.c \ tests/test-vector.c \ controller/test-lflow-cache.c \ - controller/test-ofctrl-seqno.c \ controller/test-vif-plug.c \ lib/test-lflow-conj-ids.c \ lib/test-ovn-features.c \ + lib/test-ofctrl-seqno.c \ northd/test-ipam.c tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ @@ -302,7 +302,6 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ controller/lflow-cache.$(OBJEXT) \ controller/local_data.$(OBJEXT) \ controller/lport.$(OBJEXT) \ - controller/ofctrl-seqno.$(OBJEXT) \ controller/ovsport.$(OBJEXT) \ controller/patch.$(OBJEXT) \ controller/route.$(OBJEXT) \ From patchwork Mon Aug 11 10:10:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121394 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rYQ2ZlVz1xvJ for ; Mon, 11 Aug 2025 20:32:26 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id A6B2640CEB; Mon, 11 Aug 2025 10:32:33 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Hd_Wg2YNx80g; Mon, 11 Aug 2025 10:32:30 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 49F6040CD1 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTPS id 49F6040CD1; Mon, 11 Aug 2025 10:32:30 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2886EC0889; Mon, 11 Aug 2025 10:32:30 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id B789CC02A4 for ; Mon, 11 Aug 2025 10:32:28 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id A9BAA410A2 for ; Mon, 11 Aug 2025 10:32:28 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id HWMU-H0JmjTl for ; Mon, 11 Aug 2025 10:32:26 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org 2B7C2410A6 Authentication-Results: smtp4.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 2B7C2410A6 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp4.osuosl.org (Postfix) with ESMTPS id 2B7C2410A6 for ; Mon, 11 Aug 2025 10:32:24 +0000 (UTC) Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::226]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 20ADF581C4E for ; Mon, 11 Aug 2025 10:10:31 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 9CD724320D; Mon, 11 Aug 2025 10:10:23 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:19 +0530 Message-ID: <20250811101019.917977-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudekucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpefgteetudffjeehhfffkeetteelheelheekhffhudejuefhveffudelkeehteektdenucffohhmrghinheprghprggthhgvrdhorhhgnecukfhppedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieeknecuvehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehinhgvthepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikedphhgvlhhopehfvgguohhrrgdrughomhgrihhnrdhnrghmvgdpmhgrihhlfhhrohhmpehnuhhmrghnshesohhvnhdrohhrghdpnhgspghrtghpthhtohepvddprhgtphhtthhopeguvghvsehophgvnhhvshifihhttghhrdhorhhgpdhrtghpthhtohepnhhumhgrnhhssehovhhnrdhorhhg X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 09/12] ovn-br-controller: Program the openflows to the bridges. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique --- br-controller/automake.mk | 2 + br-controller/br-ofctrl.c | 731 ++++++++++++++++++++++++++++++ br-controller/br-ofctrl.h | 33 ++ br-controller/en-bridge-data.c | 40 ++ br-controller/en-bridge-data.h | 4 + br-controller/ovn-br-controller.c | 116 ++++- tests/automake.mk | 5 +- tests/ovn-br-controller.at | 327 +++++++++++++ tests/testsuite.at | 1 + 9 files changed, 1253 insertions(+), 6 deletions(-) create mode 100644 br-controller/br-ofctrl.c create mode 100644 br-controller/br-ofctrl.h create mode 100644 tests/ovn-br-controller.at diff --git a/br-controller/automake.mk b/br-controller/automake.mk index 4baea4f6fe..f8cae3a098 100644 --- a/br-controller/automake.mk +++ b/br-controller/automake.mk @@ -2,6 +2,8 @@ bin_PROGRAMS += br-controller/ovn-br-controller br_controller_ovn_br_controller_SOURCES = \ br-controller/br-flow-mgr.c \ br-controller/br-flow-mgr.h \ + br-controller/br-ofctrl.c \ + br-controller/br-ofctrl.h \ br-controller/en-bridge-data.c \ br-controller/en-bridge-data.h \ br-controller/en-lflow.c \ diff --git a/br-controller/br-ofctrl.c b/br-controller/br-ofctrl.c new file mode 100644 index 0000000000..6c11c43530 --- /dev/null +++ b/br-controller/br-ofctrl.c @@ -0,0 +1,731 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* OVS includes. */ +#include "bitmap.h" +#include "byte-order.h" +#include "dirs.h" +#include "dp-packet.h" +#include "flow.h" +#include "hash.h" +#include "hindex.h" +#include "lib/socket-util.h" +#include "lib/util.h" +#include "lib/vswitch-idl.h" +#include "openflow/openflow.h" +#include "openvswitch/dynamic-string.h" +#include "openvswitch/hmap.h" +#include "openvswitch/list.h" +#include "openvswitch/match.h" +#include "openvswitch/ofp-actions.h" +#include "openvswitch/ofp-bundle.h" +#include "openvswitch/ofp-flow.h" +#include "openvswitch/ofp-group.h" +#include "openvswitch/ofp-match.h" +#include "openvswitch/ofp-msgs.h" +#include "openvswitch/ofp-meter.h" +#include "openvswitch/ofp-packet.h" +#include "openvswitch/ofp-print.h" +#include "openvswitch/ofp-util.h" +#include "openvswitch/ofpbuf.h" +#include "openvswitch/vlog.h" +#include "openvswitch/poll-loop.h" +#include "openvswitch/rconn.h" + +/* OVN includes. */ +#include "br-flow-mgr.h" +#include "en-bridge-data.h" +#include "br-ofctrl.h" +#include "lib/ovn-util.h" +#include "lib/ovn-br-idl.h" + +VLOG_DEFINE_THIS_MODULE(brofctrl); + +/* Connection state machine. */ +#define STATES \ + STATE(S_NEW) \ + STATE(S_WAIT_BEFORE_CLEAR) \ + STATE(S_CLEAR_FLOWS) \ + STATE(S_UPDATE_FLOWS) + +enum br_ofctrl_state { +#define STATE(NAME) NAME, + STATES +#undef STATE +}; + +/* An in-flight update to the switch's flow table. + * + * When we receive a barrier reply from the switch with the given 'xid', we + * know that the switch is caught up to the requested sequence number + * 'req_cfg' (and make that available to the client via ofctrl_get_cur_cfg(), + * so that it can store it into external state, e.g., our Chassis record's + * nb_cfg column). */ +struct br_ofctrl_flow_update { + struct ovs_list list_node; /* In 'flow_updates'. */ + ovs_be32 xid; /* OpenFlow transaction ID for barrier. */ + uint64_t req_cfg; /* Requested sequence number. */ +}; + +struct br_ofctrl { + struct hmap_node hmap_node; + char *bridge; /* key. */ + + /* OpenFlow connection to the switch. */ + struct rconn *swconn; + int probe_interval; + char *conn_target; + + unsigned int wait_before_clear_time; + /* The time when the state S_WAIT_BEFORE_CLEAR should complete. + * If the timer is not started yet, it is set to 0. */ + long long int wait_before_clear_expire; + + /* Currently in-flight updates. */ + struct ovs_list flow_updates; + + /* req_cfg of latest committed flow update. */ + uint64_t cur_cfg; + uint64_t old_req_cfg; + bool skipped_last_time; + + /* Indicates if we just went through the S_CLEAR_FLOWS state, which means + * we need to perform a one time deletion for all the existing flows, + * groups and meters. This can happen during initialization or OpenFlow + * reconnection (e.g. after OVS restart). */ + bool ofctrl_initial_clear; + + /* Last seen sequence number for 'swconn'. When this differs from + * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ + unsigned int seqno; + + /* Counter for in-flight OpenFlow messages on 'swconn'. We only send a new + * round of flow table modifications to the switch when the counter falls + * to zero, to avoid unbounded buffering. */ + struct rconn_packet_counter *tx_counter; + + /* Current state. */ + enum br_ofctrl_state state; +}; + +static struct hmap br_ofctrls = HMAP_INITIALIZER(&br_ofctrls); + +static struct br_ofctrl *br_ofctrl_get(const char *bridge); +static void br_ofctrl_put(struct br_ofctrl *br_ofctrl, uint64_t req_cfg, + bool lflows_changed, bool pflows_changed); +static void br_ofctrl_destroy(struct br_ofctrl *); +static ovs_be32 queue_msg(struct br_ofctrl *, struct ofpbuf *); +static struct br_ofctrl_flow_update *br_ofctrl_flow_update_from_list_node( + const struct ovs_list *); +static bool br_ofctrl_run__(struct br_ofctrl *); +static bool br_ofctrl_has_backlog(struct br_ofctrl *); +static bool br_ofctrl_can_put(struct br_ofctrl *); + +void +br_ofctrls_init(void) +{ + +} + +void +br_ofctrls_destroy(void) +{ + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH_POP (br_ofctrl, hmap_node, &br_ofctrls) { + br_ofctrl_destroy(br_ofctrl); + } + + hmap_destroy(&br_ofctrls); +} + +void +br_ofctrls_add_or_update_bridge(struct ovn_bridge *br) +{ + ovs_assert(br->ovs_br); + + struct br_ofctrl *br_ofctrl = br_ofctrl_get(br->db_br->name); + + if (!br_ofctrl) { + br_ofctrl = xzalloc(sizeof *br_ofctrl); + br_ofctrl->bridge = xstrdup(br->db_br->name); + br_ofctrl->swconn = rconn_create(0, 0, DSCP_DEFAULT, + 1 << OFP15_VERSION); + br_ofctrl->tx_counter = rconn_packet_counter_create(); + ovs_list_init(&br_ofctrl->flow_updates); + + hmap_insert(&br_ofctrls, &br_ofctrl->hmap_node, + hash_string(br_ofctrl->bridge, 0)); + } else { + free(br_ofctrl->conn_target); + } + + br_ofctrl->probe_interval = br->probe_interval; + br_ofctrl->conn_target = xstrdup(br->conn_target); + br_ofctrl->wait_before_clear_time = br->wait_before_clear_time; +} + +void +br_ofctrls_remove_bridge(const char *bridge) +{ + struct br_ofctrl *br_ofctrl = br_ofctrl_get(bridge); + if (br_ofctrl) { + hmap_remove(&br_ofctrls, &br_ofctrl->hmap_node); + br_ofctrl_destroy(br_ofctrl); + } +} + +void +br_ofctrls_get_bridges(struct sset *managed_bridges) +{ + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH (br_ofctrl, hmap_node, &br_ofctrls) { + sset_add(managed_bridges, br_ofctrl->bridge); + } +} + +/* Runs the OpenFlow state machine against each bridge in the br_ofctrls hmap, + * which is local to the hypervisor on which we are running. + * + * Returns 'true' if an OpenFlow reconnect happened for any of the bridge; + * 'false' otherwise. + */ +bool +br_ofctrls_run(void) +{ + bool reconnected = false; + + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH (br_ofctrl, hmap_node, &br_ofctrls) { + reconnected |= br_ofctrl_run__(br_ofctrl); + } + + return reconnected; +} + +/* Programs the flow table on the switch, if possible, by the flows + * added to the br-flow-mgr. + * + * This should be called after br_ofctrls_run() within the main loop. */ +void +br_ofctrls_put(uint64_t req_cfg, bool lflows_changed, bool pflows_changed) +{ + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH (br_ofctrl, hmap_node, &br_ofctrls) { + br_ofctrl_put(br_ofctrl, req_cfg, lflows_changed, pflows_changed); + } +} + +void +br_ofctrls_wait(void) +{ + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH (br_ofctrl, hmap_node, &br_ofctrls) { + rconn_run_wait(br_ofctrl->swconn); + rconn_recv_wait(br_ofctrl->swconn); + } +} + +uint64_t +br_ofctrl_get_cur_cfg(void) +{ + uint64_t of_cur_cfg = UINT64_MAX; + struct br_ofctrl *br_ofctrl; + HMAP_FOR_EACH (br_ofctrl, hmap_node, &br_ofctrls) { + of_cur_cfg = MIN(of_cur_cfg, br_ofctrl->cur_cfg); + } + + return of_cur_cfg; +} + +/* Static functions. */ + +static void +br_ofctrl_destroy(struct br_ofctrl *br_ofctrl) +{ + rconn_destroy(br_ofctrl->swconn); + rconn_packet_counter_destroy(br_ofctrl->tx_counter); + free(br_ofctrl->bridge); + free(br_ofctrl); +} + +static struct br_ofctrl * +br_ofctrl_get(const char *bridge) +{ + struct br_ofctrl *br_ofctrl; + uint32_t hash = hash_string(bridge, 0); + HMAP_FOR_EACH_WITH_HASH (br_ofctrl, hmap_node, hash, &br_ofctrls) { + if (!strcmp(br_ofctrl->bridge, bridge)) { + return br_ofctrl; + } + } + + return NULL; +} + +static ovs_be32 +queue_msg(struct br_ofctrl *br_ofctrl, struct ofpbuf *msg) +{ + const struct ofp_header *oh = msg->data; + ovs_be32 xid_ = oh->xid; + rconn_send(br_ofctrl->swconn, msg, br_ofctrl->tx_counter); + return xid_; +} + +static void +log_openflow_rl(struct vlog_rate_limit *rl, enum vlog_level level, + const struct ofp_header *oh, const char *title) +{ + if (!vlog_should_drop(&this_module, level, rl)) { + char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2); + vlog(&this_module, level, "%s: %s", title, s); + free(s); + } +} + +static struct br_ofctrl_flow_update * +br_ofctrl_flow_update_from_list_node(const struct ovs_list *list_node) +{ + return CONTAINER_OF(list_node, struct br_ofctrl_flow_update, list_node); +} + +/* ofctrl state machine functions. */ + +static void +ofctrl_recv(struct br_ofctrl *br_ofctrl, const struct ofp_header *oh, + enum ofptype type) +{ + if (type == OFPTYPE_ECHO_REQUEST) { + queue_msg(br_ofctrl, ofputil_encode_echo_reply(oh)); + } else if (type == OFPTYPE_ERROR) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); + log_openflow_rl(&rl, VLL_INFO, oh, "OpenFlow error"); + rconn_reconnect(br_ofctrl->swconn); + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); + log_openflow_rl(&rl, VLL_DBG, oh, "OpenFlow packet ignored"); + } +} + + +/* S_NEW, for a new connection. + * + */ + +static void +run_S_NEW(struct br_ofctrl *br_ofctrl) +{ + br_ofctrl->state = S_WAIT_BEFORE_CLEAR; +} + +static void +recv_S_NEW(struct br_ofctrl *br_ofctrl OVS_UNUSED, + const struct ofp_header *oh OVS_UNUSED, + enum ofptype type OVS_UNUSED) +{ + OVS_NOT_REACHED(); +} + +/* S_WAIT_BEFORE_CLEAR, we are almost ready to set up flows, but just wait for + * a while until the initial flow compute to complete before we clear the + * existing flows in OVS, so that we won't end up with an empty flow table, + * which may cause data plane down time. */ +static void +run_S_WAIT_BEFORE_CLEAR(struct br_ofctrl *br_ofctrl) +{ + if (!br_ofctrl->wait_before_clear_time || + (br_ofctrl->wait_before_clear_expire && + time_msec() >= br_ofctrl->wait_before_clear_expire)) { + br_ofctrl->state = S_CLEAR_FLOWS; + return; + } + + if (!br_ofctrl->wait_before_clear_expire) { + /* Start the timer. */ + br_ofctrl->wait_before_clear_expire = + time_msec() + br_ofctrl->wait_before_clear_time; + } + poll_timer_wait_until(br_ofctrl->wait_before_clear_expire); +} + +static void +recv_S_WAIT_BEFORE_CLEAR(struct br_ofctrl *br_ofctrl, + const struct ofp_header *oh, enum ofptype type) +{ + ofctrl_recv(br_ofctrl, oh, type); +} + +/* Sends an OFPT_TABLE_MOD to clear all flows, then transitions to + * S_UPDATE_FLOWS. */ + +static void +run_S_CLEAR_FLOWS(struct br_ofctrl *br_ofctrl) +{ + VLOG_DBG("clearing all flows for bridge %s", br_ofctrl->bridge); + + /* Set the flag so that the ofctrl_run() can clear the existing flows, + * groups and meters. We clear them in ofctrl_run() right before the new + * ones are installed to avoid data plane downtime. */ + br_ofctrl->ofctrl_initial_clear = true; + + /* Clear installed_flows, to match the state of the switch. */ + br_flow_flush_oflows(br_ofctrl->bridge); + + /* All flow updates are irrelevant now. */ + struct br_ofctrl_flow_update *fup; + LIST_FOR_EACH_SAFE (fup, list_node, &br_ofctrl->flow_updates) { + ovs_list_remove(&fup->list_node); + free(fup); + } + + br_ofctrl->state = S_UPDATE_FLOWS; + + /* Give a chance for the main loop to call ofctrl_put() in case there were + * pending flows waiting ofctrl state change to S_UPDATE_FLOWS. */ + poll_immediate_wake(); +} + +static void +recv_S_CLEAR_FLOWS(struct br_ofctrl *br_ofctrl, + const struct ofp_header *oh, enum ofptype type) +{ + ofctrl_recv(br_ofctrl, oh, type); +} + +/* S_UPDATE_FLOWS, for maintaining the flow table over time. + * + * Compare the installed flows to the ones we want. Send OFPT_FLOW_MOD as + * necessary. + * + * This is a terminal state. We only transition out of it if the connection + * drops. */ + +static void +run_S_UPDATE_FLOWS(struct br_ofctrl *br_ofctrl OVS_UNUSED) +{ + /* Nothing to do here. + * + * Being in this state enables ofctrl_put() to work, however. */ +} + +static void +br_flow_updates_handle_barrier_reply(struct br_ofctrl *br_ofctrl, + const struct ofp_header *oh) +{ + if (ovs_list_is_empty(&br_ofctrl->flow_updates)) { + return; + } + + struct br_ofctrl_flow_update *fup = br_ofctrl_flow_update_from_list_node( + ovs_list_front(&br_ofctrl->flow_updates)); + if (fup->xid == oh->xid) { + if (fup->req_cfg >= br_ofctrl->cur_cfg) { + br_ofctrl->cur_cfg = fup->req_cfg; + } + ovs_list_remove(&fup->list_node); + free(fup); + } +} + +static void +recv_S_UPDATE_FLOWS(struct br_ofctrl *br_ofctrl, + const struct ofp_header *oh, enum ofptype type) +{ + if (type == OFPTYPE_BARRIER_REPLY) { + br_flow_updates_handle_barrier_reply(br_ofctrl, oh); + } else { + ofctrl_recv(br_ofctrl, oh, type); + } +} + +static bool +br_ofctrl_run__(struct br_ofctrl *br_ofctrl) +{ + struct rconn *swconn = br_ofctrl->swconn; + + ovn_update_swconn_at(swconn, br_ofctrl->conn_target, + br_ofctrl->probe_interval, "br_ofctrl"); + rconn_run(swconn); + + if (!rconn_is_connected(swconn)) { + return false; + } + + bool reconnected = false; + + if (br_ofctrl->seqno != rconn_get_connection_seqno(swconn)) { + br_ofctrl->seqno = rconn_get_connection_seqno(swconn); + reconnected = true; + br_ofctrl->state = S_NEW; + } + + bool progress = true; + for (int i = 0; progress && i < 50; i++) { + /* Allow the state machine to run. */ + enum br_ofctrl_state old_state = br_ofctrl->state; + switch (br_ofctrl->state) { +#define STATE(NAME) case NAME: run_##NAME(br_ofctrl); break; + STATES +#undef STATE + default: + OVS_NOT_REACHED(); + } + + /* Try to process a received packet. */ + struct ofpbuf *msg = rconn_recv(swconn); + if (msg) { + const struct ofp_header *oh = msg->data; + enum ofptype type; + enum ofperr error; + + error = ofptype_decode(&type, oh); + if (!error) { + switch (br_ofctrl->state) { +#define STATE(NAME) case NAME: recv_##NAME(br_ofctrl, oh, type); break; + STATES +#undef STATE + default: + OVS_NOT_REACHED(); + } + } else { + char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1); + VLOG_WARN("could not decode OpenFlow message (%s): %s", + ofperr_to_string(error), s); + free(s); + } + + ofpbuf_delete(msg); + } + + /* If we did some work, plan to go around again. */ + progress = old_state != br_ofctrl->state || msg; + } + if (progress) { + /* We bailed out to limit the amount of work we do in one go, to allow + * other code a chance to run. We were still making progress at that + * point, so ensure that we come back again without waiting. */ + poll_immediate_wake(); + } + + return reconnected; +} + +static bool +br_ofctrl_has_backlog(struct br_ofctrl *br_ofctrl) +{ + if (rconn_packet_counter_n_packets(br_ofctrl->tx_counter) + || rconn_get_version(br_ofctrl->swconn) < 0) { + return true; + } + return false; +} + +/* The flow table can be updated if the connection to the switch is up and + * in the correct state and not backlogged with existing flow_mods. (Our + * criteria for being backlogged appear very conservative, but the socket + * between ovn-controller and OVS provides some buffering.) */ +static bool +br_ofctrl_can_put(struct br_ofctrl *br_ofctrl) +{ + if (br_ofctrl->state != S_UPDATE_FLOWS + || br_ofctrl_has_backlog(br_ofctrl)) { + return false; + } + return true; +} + +static struct ofpbuf * +encode_flow_mod(struct ofputil_flow_mod *fm) +{ + fm->buffer_id = UINT32_MAX; + fm->out_port = OFPP_ANY; + fm->out_group = OFPG_ANY; + return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF15_OXM); +} + +static struct ofpbuf * +encode_bundle_add(struct ofpbuf *msg, struct ofputil_bundle_ctrl_msg *bc) +{ + struct ofputil_bundle_add_msg bam = { + .bundle_id = bc->bundle_id, + .flags = bc->flags, + .msg = msg->data, + }; + return ofputil_encode_bundle_add(OFP15_VERSION, &bam); +} + +static bool +add_flow_mod(struct ofputil_flow_mod *fm, + struct ofputil_bundle_ctrl_msg *bc, + struct ovs_list *msgs) +{ + struct ofpbuf *msg = encode_flow_mod(fm); + struct ofpbuf *bundle_msg = encode_bundle_add(msg, bc); + + uint32_t flow_mod_len = msg->size; + uint32_t bundle_len = bundle_msg->size; + + ofpbuf_delete(msg); + + if (flow_mod_len > UINT16_MAX || bundle_len > UINT16_MAX) { + ofpbuf_delete(bundle_msg); + + return false; + } + + ovs_list_push_back(msgs, &bundle_msg->list_node); + return true; +} + +static void +br_ofctrl_put(struct br_ofctrl *br_ofctrl, uint64_t req_cfg, + bool lflows_changed, bool pflows_changed) +{ + bool need_put = false; + + if (lflows_changed || pflows_changed || br_ofctrl->skipped_last_time || + br_ofctrl->ofctrl_initial_clear) { + need_put = true; + br_ofctrl->old_req_cfg = req_cfg; + } else if (req_cfg != br_ofctrl->old_req_cfg) { + /* req_cfg changed since last ofctrl_put() call */ + if (br_ofctrl->cur_cfg == br_ofctrl->old_req_cfg) { + /* If there are no updates pending, we were up-to-date already, + * update with the new req_cfg. + */ + if (ovs_list_is_empty(&br_ofctrl->flow_updates)) { + br_ofctrl->cur_cfg = req_cfg; + br_ofctrl->old_req_cfg = req_cfg; + } + } else { + need_put = true; + br_ofctrl->old_req_cfg = req_cfg; + } + } + + if (!need_put) { + VLOG_DBG("ofctrl_put not needed for bridge %s", br_ofctrl->bridge); + return; + } + + /* OpenFlow messages to send to the switch to bring it up-to-date. */ + struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs); + + if (!br_ofctrl_can_put(br_ofctrl)) { + VLOG_DBG("ofctrl_put can't be performed for bridge %s", + br_ofctrl->bridge); + + br_ofctrl->skipped_last_time = true; + return; + } + + /* Add all flow updates into a bundle. */ + static int bundle_id = 0; + struct ofputil_bundle_ctrl_msg bc = { + .bundle_id = bundle_id++, + .flags = OFPBF_ORDERED | OFPBF_ATOMIC, + }; + struct ofpbuf *bundle_open, *bundle_commit; + + /* Open a new bundle. */ + bc.type = OFPBCT_OPEN_REQUEST; + bundle_open = ofputil_encode_bundle_ctrl_request(OFP15_VERSION, &bc); + ovs_list_push_back(&msgs, &bundle_open->list_node); + + if (br_ofctrl->ofctrl_initial_clear) { + /* Send a flow_mod to delete all flows. */ + struct ofputil_flow_mod fm = { + .table_id = OFPTT_ALL, + .command = OFPFC_DELETE, + }; + minimatch_init_catchall(&fm.match); + add_flow_mod(&fm, &bc, &msgs); + minimatch_destroy(&fm.match); + + br_ofctrl->ofctrl_initial_clear = false; + } + + br_flow_populate_oflow_msgs(br_ofctrl->bridge, &msgs); + + if (ovs_list_back(&msgs) == &bundle_open->list_node) { + /* No flow updates. Removing the bundle open request. */ + ovs_list_pop_back(&msgs); + ofpbuf_delete(bundle_open); + } else { + /* Committing the bundle. */ + bc.type = OFPBCT_COMMIT_REQUEST; + bundle_commit = ofputil_encode_bundle_ctrl_request(OFP15_VERSION, &bc); + ovs_list_push_back(&msgs, &bundle_commit->list_node); + } + + if (!ovs_list_is_empty(&msgs)) { + /* Add a barrier to the list of messages. */ + struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP15_VERSION); + const struct ofp_header *oh = barrier->data; + ovs_be32 xid_ = oh->xid; + ovs_list_push_back(&msgs, &barrier->list_node); + + /* Queue the messages. */ + struct ofpbuf *msg; + LIST_FOR_EACH_POP (msg, list_node, &msgs) { + queue_msg(br_ofctrl, msg); + } + + /* Track the flow update. */ + struct br_ofctrl_flow_update *fup; + LIST_FOR_EACH_REVERSE_SAFE (fup, list_node, &br_ofctrl->flow_updates) { + if (req_cfg < fup->req_cfg) { + /* This ofctrl_flow_update is for a configuration later than + * 'req_cfg'. This should not normally happen, because it + * means that the local seqno decreased and it should normally + * be monotonically increasing. */ + VLOG_WARN("req_cfg regressed from %"PRId64" to %"PRId64, + fup->req_cfg, req_cfg); + ovs_list_remove(&fup->list_node); + free(fup); + } else if (req_cfg == fup->req_cfg) { + /* This ofctrl_flow_update is for the same configuration as + * 'req_cfg'. Probably, some change to the physical topology + * means that we had to revise the OpenFlow flow table even + * though the logical topology did not change. Update fp->xid, + * so that we don't send a notification that we're up-to-date + * until we're really caught up. */ + VLOG_DBG("advanced xid target for req_cfg=%"PRId64, req_cfg); + fup->xid = xid_; + goto done; + } else { + break; + } + } + + /* Add a flow update. */ + fup = xmalloc(sizeof *fup); + ovs_list_push_back(&br_ofctrl->flow_updates, &fup->list_node); + fup->xid = xid_; + fup->req_cfg = req_cfg; + done:; + } else if (!ovs_list_is_empty(&br_ofctrl->flow_updates)) { + /* Getting up-to-date with 'req_cfg' didn't require any extra flow + * table changes, so whenever we get up-to-date with the most recent + * flow table update, we're also up-to-date with 'req_cfg'. */ + struct br_ofctrl_flow_update *fup = + br_ofctrl_flow_update_from_list_node( + ovs_list_back(&br_ofctrl->flow_updates)); + fup->req_cfg = req_cfg; + } else { + /* We were completely up-to-date before and still are. */ + br_ofctrl->cur_cfg = req_cfg; + } +} diff --git a/br-controller/br-ofctrl.h b/br-controller/br-ofctrl.h new file mode 100644 index 0000000000..9b629e2123 --- /dev/null +++ b/br-controller/br-ofctrl.h @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BR_OFCTRL_H +#define BR_OFCTRL_H 1 + +struct ovn_bridge; +struct shash; + +void br_ofctrls_init(void); +bool br_ofctrls_run(void); +void br_ofctrls_put(uint64_t req_cfg, bool lflows_changed, + bool pflows_changed); +void br_ofctrls_destroy(void); +void br_ofctrls_wait(void); + +void br_ofctrls_add_or_update_bridge(struct ovn_bridge *); +void br_ofctrls_remove_bridge(const char *); +uint64_t br_ofctrl_get_cur_cfg(void); +void br_ofctrls_get_bridges(struct sset *); + +#endif /* BR_OFCTRL_H */ \ No newline at end of file diff --git a/br-controller/en-bridge-data.c b/br-controller/en-bridge-data.c index 483c784a37..5c10a1f1f1 100644 --- a/br-controller/en-bridge-data.c +++ b/br-controller/en-bridge-data.c @@ -26,6 +26,7 @@ /* OVN includes. */ #include "en-bridge-data.h" +#include "lib/dirs.h" #include "lib/ovn-br-idl.h" VLOG_DEFINE_THIS_MODULE(en_bridge_data); @@ -40,6 +41,7 @@ static const struct ovsrec_bridge *ovsbridge_lookup_by_name( struct ovsdb_idl_index *ovsrec_bridge_by_name, const char *name); static void build_ovn_bridge_iface_simap(struct ovn_bridge *); +static void update_ovn_br_remote(struct ovn_bridge *); void * en_bridge_data_init(struct engine_node *node OVS_UNUSED, @@ -114,6 +116,7 @@ ovn_bridges_run(const struct ovnbrrec_bridge_table *br_table, br->ovs_br = ovs_br; build_ovn_bridge_iface_simap(br); + update_ovn_br_remote(br); } } @@ -121,6 +124,7 @@ static void ovn_bridge_destroy(struct ovn_bridge *br) { simap_destroy(&br->ovs_ifaces); + free(br->conn_target); free(br); } @@ -157,3 +161,39 @@ build_ovn_bridge_iface_simap(struct ovn_bridge *br) } } } + +static void +update_ovn_br_remote(struct ovn_bridge *br) +{ + ovs_assert(br->ovs_br); + + const char *ext_target = smap_get(&br->ovs_br->external_ids, + "ovn-bridge-remote"); + char *target = ext_target + ? xstrdup(ext_target) + : xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br->ovs_br->name); + + if (!br->conn_target || strcmp(br->conn_target, target)) { + free(br->conn_target); + br->conn_target = target; + } else { + free(target); + } + + unsigned long long probe_interval = + smap_get_ullong(&br->ovs_br->external_ids, + "ovn-openflow-remote-probe-interval", 0); + br->probe_interval = MIN(probe_interval / 1000, INT_MAX); + + unsigned int _wait_before_clear_time = + smap_get_uint(&br->ovs_br->external_ids, + "ovn-ofctrl-wait-before-clear", 0); + + if (_wait_before_clear_time != br->wait_before_clear_time) { + VLOG_INFO("ofctrl-wait-before-clear is now %u ms (was %u ms) " + "for bridge %s", + _wait_before_clear_time, br->wait_before_clear_time, + br->ovs_br->name); + br->wait_before_clear_time = _wait_before_clear_time; + } +} diff --git a/br-controller/en-bridge-data.h b/br-controller/en-bridge-data.h index b374798649..05ab556637 100644 --- a/br-controller/en-bridge-data.h +++ b/br-controller/en-bridge-data.h @@ -26,6 +26,10 @@ struct ovn_bridge { /* simap of ovs interface names to ofport numbers. */ struct simap ovs_ifaces; + + int probe_interval; + char *conn_target; + unsigned int wait_before_clear_time; }; struct ed_type_bridge_data { diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c index ae0e192429..74f2b7a2d2 100644 --- a/br-controller/ovn-br-controller.c +++ b/br-controller/ovn-br-controller.c @@ -35,11 +35,13 @@ /* OVN includes. */ +#include "br-ofctrl.h" #include "en-bridge-data.h" #include "en-lflow.h" #include "en-pflow.h" #include "lib/ovn-br-idl.h" #include "lib/inc-proc-eng.h" +#include "lib/ofctrl-seqno.h" #include "lib/ovn-util.h" VLOG_DEFINE_THIS_MODULE(main); @@ -55,6 +57,9 @@ static const char *ssl_ca_cert_file; /* --unixctl-path: Path to use for unixctl server socket. */ static char *unixctl_path; +/* Registered ofctrl seqno type for br_cfg propagation. */ +static size_t ofctrl_seq_type_br_cfg; + #define BRCTL_NODES \ BRCTL_NODE(br_global) \ BRCTL_NODE(bridge) \ @@ -110,7 +115,12 @@ en_br_controller_output_run(struct engine_node *node OVS_UNUSED, /* Static function declarations. */ static void ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl); static void update_br_db(struct ovsdb_idl *ovs_idl, - struct ovsdb_idl *ovn_br_idl); + struct ovsdb_idl *ovnbr_idl, + unsigned int *ovnbr_cond_seqno); +static unsigned int update_ovnbr_monitors(struct ovsdb_idl *); +static uint64_t get_ovnbr_cfg(const struct ovnbrrec_br_global_table *, + unsigned int cond_seqno, + unsigned int expected_cond_seqno); int main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) @@ -138,6 +148,9 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) daemonize_complete(); + /* Register ofctrl seqno types. */ + ofctrl_seq_type_br_cfg = ofctrl_seqno_add_type(); + /* Connect to OVS OVSDB instance. */ struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); @@ -206,8 +219,12 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) engine_init(&en_br_controller_output, &engine_arg); engine_ovsdb_node_add_index(&en_ovs_bridge, "name", ovsrec_bridge_by_name); - unsigned int ovs_cond_seqno = UINT_MAX; + unsigned int ovnbr_expected_cond_seqno = UINT_MAX; unsigned int ovnbr_cond_seqno = UINT_MAX; + unsigned int ovs_cond_seqno = UINT_MAX; + + struct ed_type_bridge_data *br_data = + engine_get_internal_data(&en_bridge_data); /* Main loop. */ while (!exit_args.exiting) { @@ -224,7 +241,8 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ovs_cond_seqno = new_ovs_cond_seqno; } - update_br_db(ovs_idl_loop.idl, ovnbr_idl_loop.idl); + update_br_db(ovs_idl_loop.idl, ovnbr_idl_loop.idl, + &ovnbr_expected_cond_seqno); struct ovsdb_idl_txn *ovnbr_idl_txn = ovsdb_idl_loop_run(&ovnbr_idl_loop); unsigned int new_ovnbr_cond_seqno @@ -251,10 +269,48 @@ main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) if (ovsdb_idl_has_ever_connected(ovnbr_idl_loop.idl) && cfg) { engine_run(true); + + br_data = engine_get_data(&en_bridge_data); + if (br_data) { + struct sset bridges_in_br_ofctrl = + SSET_INITIALIZER(&bridges_in_br_ofctrl); + br_ofctrls_get_bridges(&bridges_in_br_ofctrl); + struct shash_node *node; + SHASH_FOR_EACH (node, &br_data->bridges) { + struct ovn_bridge *br = node->data; + + if (br->ovs_br) { + sset_find_and_delete(&bridges_in_br_ofctrl, + br->db_br->name); + br_ofctrls_add_or_update_bridge(br); + } + } + + const char *bridge; + SSET_FOR_EACH (bridge, &bridges_in_br_ofctrl) { + br_ofctrls_remove_bridge(bridge); + } + + sset_destroy(&bridges_in_br_ofctrl); + } + + br_ofctrls_run(); + + ofctrl_seqno_update_create( + ofctrl_seq_type_br_cfg, + get_ovnbr_cfg(ovnbrrec_br_global_table_get(ovnbr_idl_loop.idl), + ovnbr_cond_seqno, ovnbr_expected_cond_seqno)); + + br_ofctrls_put(ofctrl_seqno_get_req_cfg(), + engine_node_changed(&en_lflow_output), + engine_node_changed(&en_pflow_output)); + + ofctrl_seqno_run(br_ofctrl_get_cur_cfg()); } unixctl_server_run(unixctl); + br_ofctrls_wait(); unixctl_server_wait(unixctl); if (exit_args.exiting) { poll_immediate_wake(); @@ -440,7 +496,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) /* Retrieves the pointer to the OVN Bridge Controller database from 'ovs_idl' * and updates 'brdb_idl' with that pointer. */ static void -update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl) +update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl, + unsigned int *ovnbr_cond_seqno) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); if (!cfg) { @@ -449,4 +506,55 @@ update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl) const char *remote = smap_get(&cfg->external_ids, "ovn-br-remote"); ovsdb_idl_set_remote(ovnbr_idl, remote, true); + + unsigned int next_cond_seqno = update_ovnbr_monitors(ovnbr_idl); + if (ovnbr_cond_seqno) { + *ovnbr_cond_seqno = next_cond_seqno; + } +} + +/* Assume the table exists in the server schema and set its condition. */ +#define ovnbr_table_set_req_mon_condition(idl, table, cond) \ + ovnbrrec_##table##_set_condition(idl, cond) + +static unsigned int +update_ovnbr_monitors(struct ovsdb_idl *ovnbr_idl) +{ + struct ovsdb_idl_condition br = OVSDB_IDL_CONDITION_INIT(&br); + struct ovsdb_idl_condition lf = OVSDB_IDL_CONDITION_INIT(&lf); + + ovsdb_idl_condition_add_clause_true(&br); + ovsdb_idl_condition_add_clause_true(&lf); + + unsigned int cond_seqnos[] = { + ovnbr_table_set_req_mon_condition(ovnbr_idl, bridge, &br), + ovnbr_table_set_req_mon_condition(ovnbr_idl, logical_flow, &lf), + }; + + unsigned int expected_cond_seqno = 0; + for (size_t i = 0; i < ARRAY_SIZE(cond_seqnos); i++) { + expected_cond_seqno = MAX(expected_cond_seqno, cond_seqnos[i]); + } + + return expected_cond_seqno; +} + +static uint64_t +get_ovnbr_cfg(const struct ovnbrrec_br_global_table *br_global_table, + unsigned int cond_seqno, unsigned int expected_cond_seqno) +{ + static uint64_t br_cfg = 0; + + /* Delay getting br_cfg if there are monitor condition changes + * in flight. It might be that those changes would instruct the + * server to send updates that happened before PR_Global.pr_cfg. + */ + if (cond_seqno != expected_cond_seqno) { + return br_cfg; + } + + const struct ovnbrrec_br_global *br_global + = ovnbrrec_br_global_table_first(br_global_table); + br_cfg = br_global ? br_global->br_cfg : 0; + return br_cfg; } diff --git a/tests/automake.mk b/tests/automake.mk index e6a31c6f69..84c0ea2219 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -46,7 +46,8 @@ TESTSUITE_AT = \ tests/ovn-lflow-conj-ids.at \ tests/ovn-ipsec.at \ tests/ovn-vif-plug.at \ - tests/ovn-util.at + tests/ovn-util.at \ + tests/ovn-br-controller.at SYSTEM_DPDK_TESTSUITE_AT = \ tests/system-dpdk-testsuite.at \ @@ -89,7 +90,7 @@ DISTCLEANFILES += tests/atconfig tests/atlocal MULTINODE_TESTSUITE = $(srcdir)/tests/multinode-testsuite MULTINODE_TESTSUITE_DIR = $(abs_top_builddir)/tests/multinode-testsuite.dir MULTINODE_TESTSUITE_RESULTS = $(MULTINODE_TESTSUITE_DIR)/results -AUTOTEST_PATH = $(ovs_builddir)/utilities:$(ovs_builddir)/vswitchd:$(ovs_builddir)/ovsdb:$(ovs_builddir)/vtep:tests:$(PTHREAD_WIN32_DIR_DLL):$(SSL_DIR):controller-vtep:northd:utilities:controller:ic +AUTOTEST_PATH = $(ovs_builddir)/utilities:$(ovs_builddir)/vswitchd:$(ovs_builddir)/ovsdb:$(ovs_builddir)/vtep:tests:$(PTHREAD_WIN32_DIR_DLL):$(SSL_DIR):controller-vtep:northd:utilities:controller:ic:br-controller export ovs_srcdir export ovs_builddir diff --git a/tests/ovn-br-controller.at b/tests/ovn-br-controller.at new file mode 100644 index 0000000000..bb10e5bd10 --- /dev/null +++ b/tests/ovn-br-controller.at @@ -0,0 +1,327 @@ +AT_BANNER([ovn_br_controller]) + +# OVN_BR_CONTROLLER_START(SIM_NAME) +# +# $1 - optional simulator name. If none is given, runs ovn-br-controller +# in $ovs_dir. +# Starts the test with a setup with ovn bridge controller. Each test case must first +# call this macro and ovn_start. +# +m4_define([OVN_BR_CONTROLLER_START], [ + AT_KEYWORDS([ovn-br-controller]) + mkdir -p "$ovs_dir" || return 1 + mkdir "$ovs_base"/ovn-br || return 1 + + dnl Create databases (vswitch). + check ovsdb-tool create "$ovs_dir"/vswitchd.db $ovs_srcdir/vswitchd/vswitch.ovsschema + check ovsdb-tool create "$ovs_base"/ovn-br/ovn-br.db "$abs_top_srcdir"/ovn-br.ovsschema + + dnl Start ovsdb-server. + start_daemon ovsdb-server --remote=punix:"$ovs_dir"/db.sock \ + "$ovs_dir"/vswitchd.db + + ovn_br_remote=unix:"$ovs_base"/ovn-br/ovnbr_db.sock + dnl Start ovs-vswitchd. + start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif + + ovs-vsctl \ + -- set Open_vSwitch . external-ids:ovn-br-remote=$ovn_br_remote + dnl Start ovsdb-server for ovn-br. + as ovn-br start_daemon ovsdb-server --remote=punix:"$ovs_base"/ovn-br/ovnbr_db.sock \ + "$ovs_base"/ovn-br/ovn-br.db + + which ovn-br-controller + dnl Start ovn-br-controller. + start_daemon ovn-br-controller +]) + +m4_define([OVN_BR_CONTROLLER_STOP],[ + echo + echo "Clean up ovn-br-controller related processes in $2" + test -n "$2" && as "$2" + OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) + OVS_APP_EXIT_AND_WAIT([ovn-br-controller]) + + as ovn-br + OVS_APP_EXIT_AND_WAIT([ovsdb-server]) +]) + +AT_SETUP([ovn-br-controller - brctl test]) +OVN_BR_CONTROLLER_START + +check as ovn-br ovn-brctl show +check as ovn-br ovn-brctl add-br br0 + +AT_CHECK([as ovn-br ovn-brctl show | uuidfilt], [0], + [bridge <0> (br0) +]) + +AT_CHECK([as ovn-br ovn-brctl show br0 | uuidfilt], [0], + [bridge <0> (br0) +]) + +AT_CHECK([as ovn-br ovn-brctl show br1 | uuidfilt], [0], [], + [ovn-brctl: no row "br1" in table Bridge +]) + +check as ovn-br ovn-brctl del-br br0 +check as ovn-br ovn-brctl show + +check as ovn-br ovn-brctl add-br br0 + +check as ovn-br ovn-brctl add-flow br0 0 1000 "ip4 && tcp" "drop;" +check as ovn-br ovn-brctl add-flow br0 0 1000 "ip4 && udp" "next;" +check as ovn-br ovn-brctl add-flow br0 1 0 "ip4 && udp" "output;" + +check as ovn-br ovn-brctl add-br br1 + +check as ovn-br ovn-brctl add-flow br1 0 1000 "ip4 && tcp.dst == 1000 && ip4.dst == 10.0.0.10" "drop;" +check as ovn-br ovn-brctl add-flow br1 0 0 "1" "output;" + +AT_CHECK([as ovn-br ovn-brctl dump-flows | uuidfilt], [0], + [dnl +Bridge: br0 (<0>) + table=0 , priority=1000 , match=(ip4 && tcp), action=(drop;) + table=0 , priority=1000 , match=(ip4 && udp), action=(next;) + table=1 , priority=0 , match=(ip4 && udp), action=(output;) +Bridge: br1 (<1>) + table=0 , priority=1000 , match=(ip4 && tcp.dst == 1000 && ip4.dst == 10.0.0.10), action=(drop;) + table=0 , priority=0 , match=(1), action=(output;) +]) + +as ovn-br ovn-brctl del-flows br1 + +AT_CHECK([as ovn-br ovn-brctl dump-flows | uuidfilt], [0], + [dnl +Bridge: br0 (<0>) + table=0 , priority=1000 , match=(ip4 && tcp), action=(drop;) + table=0 , priority=1000 , match=(ip4 && udp), action=(next;) + table=1 , priority=0 , match=(ip4 && udp), action=(output;) +]) + +lflow_uuid=$(as ovn-br ovn-brctl --bare --columns _uuid find logical_flow table_id=1) +check as ovn-br ovn-brctl del-flow $lflow_uuid + +AT_CHECK([as ovn-br ovn-brctl dump-flows | uuidfilt], [0], + [dnl +Bridge: br0 (<0>) + table=0 , priority=1000 , match=(ip4 && tcp), action=(drop;) + table=0 , priority=1000 , match=(ip4 && udp), action=(next;) +]) + +OVN_BR_CONTROLLER_STOP +AT_CLEANUP + +AT_SETUP([ovn-br-controller - logical flows]) +OVN_BR_CONTROLLER_START + +check as ovn-br ovn-brctl add-br br0 + +check ovs-vsctl add-br br0 +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br0 | grep -v NXST_FLOW | wc -l` -eq 3]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL +NXST_FLOW reply: +]) + +check ovs-vsctl add-port br0 p1 -- set interface p1 ofport-request=2 +check ovs-vsctl add-port br0 p2 -- set interface p2 ofport-request=3 +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br0 | grep -v NXST_FLOW | wc -l` -eq 7]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=3 actions=load:0x3->NXM_NX_REG14[[]],resubmit(,8) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x2 actions=output:2 + table=65, priority=100,reg15=0x3 actions=output:3 +NXST_FLOW reply: +]) + +check as ovn-br ovn-brctl add-flow br0 0 1000 'inport == "p1"' "next;" +check as ovn-br ovn-brctl add-flow br0 0 1000 'inport == "p2"' "drop;" +check as ovn-br ovn-brctl add-flow br0 1 1000 'ip4 && tcp' "ip4.src <-> ip4.dst; tcp.dst = 8080; next;" +check as ovn-br ovn-brctl add-flow br0 1 1000 'ip4' "next;" +check as ovn-br ovn-brctl add-flow br0 2 1000 '1' "output;" + +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br0 | grep -v NXST_FLOW | wc -l` -eq 12]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=3 actions=load:0x3->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000 actions=resubmit(,64) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x2 actions=output:2 + table=65, priority=100,reg15=0x3 actions=output:3 + table=8, priority=1000,reg14=0x2 actions=resubmit(,9) + table=8, priority=1000,reg14=0x3 actions=drop + table=9, priority=1000,ip actions=resubmit(,10) + table=9, priority=1000,tcp actions=push:NXM_OF_IP_DST[[]],push:NXM_OF_IP_SRC[[]],pop:NXM_OF_IP_DST[[]],pop:NXM_OF_IP_SRC[[]],mod_tp_dst:8080,resubmit(,10) +NXST_FLOW reply: +]) + +check ovs-vsctl del-port p2 +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br0 | grep -v NXST_FLOW | wc -l` -eq 9]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000 actions=resubmit(,64) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x2 actions=output:2 + table=8, priority=1000,reg14=0x2 actions=resubmit(,9) + table=9, priority=1000,ip actions=resubmit(,10) + table=9, priority=1000,tcp actions=push:NXM_OF_IP_DST[[]],push:NXM_OF_IP_SRC[[]],pop:NXM_OF_IP_DST[[]],pop:NXM_OF_IP_SRC[[]],mod_tp_dst:8080,resubmit(,10) +NXST_FLOW reply: +]) + +check ovs-vsctl add-br br1 +check ovs-vsctl add-port br1 br1-p1 -- set interface br1-p1 ofport-request=1 +check ovs-vsctl add-port br1 br1-p2 -- set interface br1-p2 ofport-request=2 + +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br1 | grep -v NXST_FLOW | wc -l` -eq 1]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br1 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL +NXST_FLOW reply: +]) + +as ovn-br ovn-brctl --id=4830e8c3-9b6b-48db-ba52-e030d9db7256 create bridge name=br1 +as ovn-br ovn-brctl list bridge + +# check as ovn-br ovn-brctl add-br br1 +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br1 | grep -v NXST_FLOW | wc -l` -eq 7]) +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br1 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=1 actions=load:0x1->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x1 actions=output:1 + table=65, priority=100,reg15=0x2 actions=output:2 +NXST_FLOW reply: +]) + +check as ovn-br ovn-brctl add-flow br1 1 1000 "ip4" "ct_snat;" +check as ovn-br ovn-brctl add-flow br1 2 1000 "ip4 && ct.new && ct.trk && ip4.src == 10.0.0.11" "ct_snat(100.64.0.11); next;" +check as ovn-br ovn-brctl add-flow br1 3 1000 "inport == \"br1-p1\"" "outport = \"br1-p2\"; output;" +check as ovn-br ovn-brctl add-flow br1 3 1000 "inport == \"br1-p2\"" "outport = \"br1-p1\"; output;" + +as ovn-br ovn-brctl --id=75bf46aa-4204-4e36-af23-6114f59e3fe8 create logical_flow \ +match='"ip4 && tcp.src > 0 && tcp.src < 1000 && tcp.dst > 1000 && tcp.dst < 2000"' \ +actions="next;" bridge=4830e8c3-9b6b-48db-ba52-e030d9db7256 table_id=10 priority=1000 + +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br1 | grep -v NXST_FLOW | wc -l` -eq 35]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br1 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=1 actions=load:0x1->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000,ct_state=+new+trk,ip,nw_src=10.0.0.11 actions=ct(commit,table=11,zone=NXM_NX_REG12[[0..15]],nat(src=100.64.0.11)),resubmit(,11) + table=11, priority=1000,reg14=0x1 actions=load:0x2->NXM_NX_REG15[[]],resubmit(,64) + table=11, priority=1000,reg14=0x2 actions=load:0x1->NXM_NX_REG15[[]],resubmit(,64) + table=18, priority=1000,conj_id=1644032429,tcp actions=resubmit(,19) + table=18, priority=1000,tcp,tp_dst=0x3ea/0xfffe actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x3ec/0xfffc actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x3f0/0xfff0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x400/0xfe00 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x600/0xff00 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x700/0xff80 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x780/0xffc0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x7c0/0xfff0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=1001 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_src=0x1/0xfe01 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x10/0xfe10 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x100/0xff00 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x2/0xfe02 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x20/0xfe20 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x200/0xff00 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x300/0xff80 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x380/0xffc0 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x3c0/0xffe0 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x3e0/0xfff8 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x4/0xfe04 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x40/0xfe40 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x8/0xfe08 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x80/0xfe80 actions=conjunction(1644032429,2/2) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x1 actions=output:1 + table=65, priority=100,reg15=0x2 actions=output:2 + table=9, priority=1000,ip actions=ct(table=10,zone=NXM_NX_REG12[[0..15]],nat) +NXST_FLOW reply: +]) + +as ovn-br ovn-brctl set logical_flow 75bf46aa-4204-4e36-af23-6114f59e3fe8 match='"ip4 && sctp"' +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br1 | grep -v NXST_FLOW | wc -l` -eq 12]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br1 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=1 actions=load:0x1->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000,ct_state=+new+trk,ip,nw_src=10.0.0.11 actions=ct(commit,table=11,zone=NXM_NX_REG12[[0..15]],nat(src=100.64.0.11)),resubmit(,11) + table=11, priority=1000,reg14=0x1 actions=load:0x2->NXM_NX_REG15[[]],resubmit(,64) + table=11, priority=1000,reg14=0x2 actions=load:0x1->NXM_NX_REG15[[]],resubmit(,64) + table=18, priority=1000,sctp actions=resubmit(,19) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x1 actions=output:1 + table=65, priority=100,reg15=0x2 actions=output:2 + table=9, priority=1000,ip actions=ct(table=10,zone=NXM_NX_REG12[[0..15]],nat) +NXST_FLOW reply: +]) + +# Make sure that the same conj_id is used when the lflow is updated with the conj match. +as ovn-br ovn-brctl set logical_flow 75bf46aa-4204-4e36-af23-6114f59e3fe8 \ +match='"ip4 && tcp.src > 0 && tcp.src < 1000 && tcp.dst > 1000 && tcp.dst < 2000"' +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br1 | grep -v NXST_FLOW | wc -l` -eq 35]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br1 | sort | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=1 actions=load:0x1->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000,ct_state=+new+trk,ip,nw_src=10.0.0.11 actions=ct(commit,table=11,zone=NXM_NX_REG12[[0..15]],nat(src=100.64.0.11)),resubmit(,11) + table=11, priority=1000,reg14=0x1 actions=load:0x2->NXM_NX_REG15[[]],resubmit(,64) + table=11, priority=1000,reg14=0x2 actions=load:0x1->NXM_NX_REG15[[]],resubmit(,64) + table=18, priority=1000,conj_id=1644032429,tcp actions=resubmit(,19) + table=18, priority=1000,tcp,tp_dst=0x3ea/0xfffe actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x3ec/0xfffc actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x3f0/0xfff0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x400/0xfe00 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x600/0xff00 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x700/0xff80 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x780/0xffc0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=0x7c0/0xfff0 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_dst=1001 actions=conjunction(1644032429,1/2) + table=18, priority=1000,tcp,tp_src=0x1/0xfe01 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x10/0xfe10 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x100/0xff00 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x2/0xfe02 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x20/0xfe20 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x200/0xff00 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x300/0xff80 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x380/0xffc0 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x3c0/0xffe0 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x3e0/0xfff8 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x4/0xfe04 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x40/0xfe40 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x8/0xfe08 actions=conjunction(1644032429,2/2) + table=18, priority=1000,tcp,tp_src=0x80/0xfe80 actions=conjunction(1644032429,2/2) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x1 actions=output:1 + table=65, priority=100,reg15=0x2 actions=output:2 + table=9, priority=1000,ip actions=ct(table=10,zone=NXM_NX_REG12[[0..15]],nat) +NXST_FLOW reply: +]) + +OVN_BR_CONTROLLER_STOP +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 8e60bf82e1..5f5eabb42a 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -41,3 +41,4 @@ m4_include([tests/checkpatch.at]) m4_include([tests/ovn-ipsec.at]) m4_include([tests/ovn-vif-plug.at]) m4_include([tests/ovn-util.at]) +m4_include([tests/ovn-br-controller.at]) From patchwork Mon Aug 11 10:10:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121397 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rf66KTNz1xxG for ; Mon, 11 Aug 2025 20:36:30 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 5AFB183E32; Mon, 11 Aug 2025 10:36:37 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id z7FEh47E_Ypo; Mon, 11 Aug 2025 10:36:35 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 9CEC083E0F Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 9CEC083E0F; Mon, 11 Aug 2025 10:36:34 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 677E4C08B4; Mon, 11 Aug 2025 10:36:34 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id D2D6EC02A4 for ; Mon, 11 Aug 2025 10:36:32 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id B9C9540C91 for ; Mon, 11 Aug 2025 10:36:32 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 6_0_viw2wBis for ; Mon, 11 Aug 2025 10:36:32 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org CA9FD4012D Authentication-Results: smtp2.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org CA9FD4012D Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp2.osuosl.org (Postfix) with ESMTPS id CA9FD4012D for ; Mon, 11 Aug 2025 10:36:31 +0000 (UTC) Received: from relay15.mail.gandi.net (relay15.mail.gandi.net [IPv6:2001:4b98:dc4:8::235]) by mslow3.mail.gandi.net (Postfix) with ESMTP id E9F885816B5 for ; Mon, 11 Aug 2025 10:10:34 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 7BDF643140; Mon, 11 Aug 2025 10:10:30 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:26 +0530 Message-ID: <20250811101026.918019-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeekiefgjedukeffheethfduvdehveegffethedtudetgeeuhfekgfelledutefhjeenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh Subject: [ovs-dev] [PATCH ovn v1 10/12] ovn-br-controller: Extend the symbol table to add new fields. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- br-controller/en-lflow.c | 19 +++++++++++++++++++ tests/ovn-br-controller.at | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c index 00e24de805..147554c3c6 100644 --- a/br-controller/en-lflow.c +++ b/br-controller/en-lflow.c @@ -63,6 +63,7 @@ struct lookup_port_aux { const struct ovn_bridge *br; }; +static void br_controller_extend_symtab(struct shash *symtab); static struct expr *convert_match_to_expr( const struct ovnbrrec_logical_flow *lflow, struct expr **prereqs, struct shash *symtab); @@ -92,6 +93,7 @@ void *en_lflow_output_init(struct engine_node *node OVS_UNUSED, struct ed_type_lflow_output_data *lflow_data = xzalloc(sizeof *lflow_data); ovn_init_symtab(&lflow_data->pd.symtab); lflow_conj_ids_init(&lflow_data->conj_ids); + br_controller_extend_symtab(&lflow_data->pd.symtab); return lflow_data; } @@ -125,6 +127,23 @@ en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data_) /* Static functions. */ +static void +br_controller_extend_symtab(struct shash *symtab) +{ + expr_symtab_add_field(symtab, "ct_snat_zone", MFF_LOG_SNAT_ZONE, + NULL, false); + expr_symtab_add_field(symtab, "ct_dnat_zone", MFF_LOG_DNAT_ZONE, + NULL, false); + expr_symtab_add_field(symtab, "metadata", MFF_METADATA, NULL, false); + expr_symtab_add_field(symtab, "tun.id", MFF_TUN_ID, NULL, false); + expr_symtab_add_field(symtab, "tun_ip4.src", MFF_TUN_SRC, "ip4", false); + expr_symtab_add_field(symtab, "tun_ip4.dst", MFF_TUN_DST, "ip4", false); + expr_symtab_add_field(symtab, "tun_ip6.src", MFF_TUN_IPV6_SRC, + "ip6", false); + expr_symtab_add_field(symtab, "tun_ip6.dst", MFF_TUN_IPV6_DST, + "ip6", false); +} + static void init_lflow_ctx(struct engine_node *node, struct ed_type_bridge_data *bridge_data, diff --git a/tests/ovn-br-controller.at b/tests/ovn-br-controller.at index bb10e5bd10..7a71b7c24b 100644 --- a/tests/ovn-br-controller.at +++ b/tests/ovn-br-controller.at @@ -183,6 +183,34 @@ AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | ofctl_strip_all], [0], [dnl NXST_FLOW reply: ]) +check ovs-vsctl add-port br0 eth1 -- set interface eth1 ofport-request=4 +check as ovn-br ovn-brctl add-flow br0 3 100 "ip4 && metadata == 1000" "ct_snat_zone = 100; ct_snat;" +check as ovn-br ovn-brctl add-flow br0 3 100 "ip4 && metadata == 1001" "ct_dnat_zone = 101; ct_dnat;" +check as ovn-br ovn-brctl add-flow br0 4 100 "ip4 && metadata == 1000 && ip4.dst == 52.92.128.0/17" "tun.id = 1000; tun_ip4.dst = 10.100.100.1; eth.dst = 4c:96:14:14:01:b0; outport = \"eth1\"; output;" +check as ovn-br ovn-brctl add-flow br0 4 101 "ip6 && metadata == 1000" "tun.id = 1000; tun_ip6.src = aef0::3; tun_ip6.dst = bef0::4; eth.dst = 4c:96:14:14:01:b0; outport = \"eth1\"; output;" + +OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br0 | grep -v NXST_FLOW | wc -l` -eq 15]) + +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0 | ofctl_strip_all], [0], [dnl + priority=0 actions=NORMAL + priority=100,in_port=2 actions=load:0x2->NXM_NX_REG14[[]],resubmit(,8) + priority=100,in_port=4 actions=load:0x4->NXM_NX_REG14[[]],resubmit(,8) + table=10, priority=1000 actions=resubmit(,64) + table=11, priority=100,ip,metadata=0x3e8 actions=load:0x64->NXM_NX_REG12[[]],ct(table=12,zone=NXM_NX_REG12[[0..15]],nat) + table=11, priority=100,ip,metadata=0x3e9 actions=load:0x65->NXM_NX_REG11[[]],ct(table=12,zone=NXM_NX_REG11[[0..15]],nat) + table=12, priority=100,ip,metadata=0x3e8,nw_dst=52.92.128.0/17 actions=load:0x3e8->NXM_NX_TUN_ID[[]],load:0xa646401->NXM_NX_TUN_IPV4_DST[[]],mod_dl_dst:4c:96:14:14:01:b0,load:0x4->NXM_NX_REG15[[]],resubmit(,64) + table=12, priority=101,ipv6,metadata=0x3e8 actions=load:0x3e8->NXM_NX_TUN_ID[[]],load:0x3->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0xaef0000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x4->NXM_NX_TUN_IPV6_DST[[0..63]],load:0xbef0000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],mod_dl_dst:4c:96:14:14:01:b0,load:0x4->NXM_NX_REG15[[]],resubmit(,64) + table=64, priority=0 actions=resubmit(,65) + table=65, priority=0 actions=NORMAL + table=65, priority=100,reg15=0x2 actions=output:2 + table=65, priority=100,reg15=0x4 actions=output:4 + table=8, priority=1000,reg14=0x2 actions=resubmit(,9) + table=9, priority=1000,ip actions=resubmit(,10) + table=9, priority=1000,tcp actions=push:NXM_OF_IP_DST[[]],push:NXM_OF_IP_SRC[[]],pop:NXM_OF_IP_DST[[]],pop:NXM_OF_IP_SRC[[]],mod_tp_dst:8080,resubmit(,10) +NXST_FLOW reply: +]) + + check ovs-vsctl add-br br1 check ovs-vsctl add-port br1 br1-p1 -- set interface br1-p1 ofport-request=1 check ovs-vsctl add-port br1 br1-p2 -- set interface br1-p2 ofport-request=2 From patchwork Mon Aug 11 10:10:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121390 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rTz4tDBz1xx2 for ; Mon, 11 Aug 2025 20:29:27 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 04F9881A2B; Mon, 11 Aug 2025 10:29:35 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 832EQ67z3w8L; Mon, 11 Aug 2025 10:29:33 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 84C9B8139F Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 84C9B8139F; Mon, 11 Aug 2025 10:29:33 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 6FB66C0889; Mon, 11 Aug 2025 10:29:33 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 34C6FC08BA for ; Mon, 11 Aug 2025 10:29:32 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 2414E61304 for ; Mon, 11 Aug 2025 10:29:32 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id gCZBtmnDB1Ci for ; Mon, 11 Aug 2025 10:29:31 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 6FF91612F3 Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 6FF91612F3 Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp3.osuosl.org (Postfix) with ESMTPS id 6FF91612F3 for ; Mon, 11 Aug 2025 10:29:30 +0000 (UTC) Received: from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::227]) by mslow3.mail.gandi.net (Postfix) with ESMTP id 5AB37581C8F for ; Mon, 11 Aug 2025 10:10:42 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 2F62C43219; Mon, 11 Aug 2025 10:10:35 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:31 +0530 Message-ID: <20250811101031.918034-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeekiefgjedukeffheethfduvdehveegffethedtudetgeeuhfekgfelledutefhjeenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 11/12] ovn-ctl: Add commands to start OVN bridge controller services. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique --- utilities/ovn-ctl | 163 ++++++++++++++++++++++++++++++++++++++++ utilities/ovn-ctl.8.xml | 36 +++++++++ 2 files changed, 199 insertions(+) diff --git a/utilities/ovn-ctl b/utilities/ovn-ctl index acbeacd099..5af2db17cb 100755 --- a/utilities/ovn-ctl +++ b/utilities/ovn-ctl @@ -444,6 +444,11 @@ start_ic_ovsdb () { start_ic_sb_ovsdb } + +start_ovnbr_ovsdb() { + start_ovsdb__ OVNBR br OVN_Bridge_Controller BR_Global +} + sync_status() { local ctl_file=$1 ovn-appctl -t $ctl_file ovsdb-server/sync-status | \ @@ -510,6 +515,14 @@ status_ic_ovsdb () { fi } +status_ovnbr_ovsdb() { + if ! pidfile_is_running $DB_OVNBR_PIDFILE; then + echo "not-running" + else + echo "running/$(sync_status $DB_OVNBR_CTRL_SOCK)" + fi +} + run_nb_ovsdb() { DB_NB_DETACH=no start_nb_ovsdb @@ -535,6 +548,11 @@ run_ic_sb_ovsdb() { start_ic_sb_ovsdb } +run_ovnbr_ovsdb() { + DB_NB_DETACH=no + start_ovnbr_ovsdb +} + start_northd () { if [ ! -e $ovn_northd_db_conf_file ]; then if test X"$OVN_MANAGE_OVSDB" = Xyes; then @@ -717,6 +735,40 @@ start_controller_vtep () { OVS_RUNDIR=${OVS_RUNDIR} start_ovn_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@" } +start_ovnbr_controller () { + set ovn-br-controller + set "$@" -vconsole:emer -vsyslog:err -vfile:info + if test X"$OVN_CONTROLLER_SSL_KEY" != X; then + set "$@" --private-key=$OVN_CONTROLLER_SSL_KEY + fi + if test X"$OVN_CONTROLLER_SSL_CERT" != X; then + set "$@" --certificate=$OVN_CONTROLLER_SSL_CERT + fi + if test X"$OVN_CONTROLLER_SSL_CA_CERT" != X; then + set "$@" --ca-cert=$OVN_CONTROLLER_SSL_CA_CERT + fi + if test X"$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT" != X; then + set "$@" --bootstrap-ca-cert=$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT + fi + if test X"$OVN_CONTROLLER_SSL_PROTOCOLS" != X; then + set "$@" --ssl-protocols=$OVN_CONTROLLER_SSL_PROTOCOLS + fi + if test X"$OVN_CONTROLLER_SSL_CIPHERS" != X; then + set "$@" --ssl-ciphers=$OVN_CONTROLLER_SSL_CIPHERS + fi + if test X"$OVN_CONTROLLER_SSL_CIPHERSUITES" != X; then + set "$@" --ssl-ciphersuites=$OVN_CONTROLLER_SSL_CIPHERSUITES + fi + + [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER" + + if test X"$extra_args" != X; then + set "$@" $extra_args + fi + + OVS_RUNDIR=${OVS_RUNDIR} start_ovn_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@" +} + ## ---- ## ## stop ## ## ---- ## @@ -747,6 +799,10 @@ stop_controller_vtep () { OVS_RUNDIR=${OVS_RUNDIR} stop_ovn_daemon ovn-controller-vtep } +stop_ovnbr_controller () { + OVS_RUNDIR=${OVS_RUNDIR} stop_ovn_daemon ovn-br-controller +} + ## ------- ## ## restart ## ## ------- ## @@ -807,6 +863,16 @@ restart_sb_relay_ovsdb() { start_sb_relay_ovsdb } +restart_ovnbr_ovsdb () { + stop_ovnbr_ovsdb + start_ovnbr_ovsdb +} + +restart_ovnbr_controller () { + stop_ovnbr_controller + start_ovnbr_controller +} + ## ---- ## ## main ## ## ---- ## @@ -870,6 +936,7 @@ set_defaults () { DB_SB_SCHEMA=$ovn_datadir/ovn-sb.ovsschema DB_IC_NB_SCHEMA=$ovn_datadir/ovn-ic-nb.ovsschema DB_IC_SB_SCHEMA=$ovn_datadir/ovn-ic-sb.ovsschema + DB_OVNBR_SCHEMA=$ovn_datadir/ovn-br.ovsschema DB_SOCK=$OVS_RUNDIR/db.sock DB_CONF_FILE=$dbdir/conf.db @@ -1020,6 +1087,39 @@ set_defaults () { DB_CLUSTER_SCHEMA_UPGRADE="yes" OVN_CONTROLLER_SYSTEM_ID="" + + DB_OVNBR_SOCK=$OVN_RUNDIR/ovnbr_db.sock + DB_OVNBR_PIDFILE=$OVN_RUNDIR/ovnbr_db.pid + DB_OVNBR_CTRL_SOCK=$OVN_RUNDIR/ovnbr_db.ctl + DB_OVNBR_FILE=$ovn_dbdir/ovnbr_db.db + DB_OVNBR_ADDR=0.0.0.0 + DB_OVNBR_PORT=6651 + + OVNBR_CONTROLLER_PRIORITY=-10 + OVNBR_CONTROLLER_WRAPPER= + + OVNBR_CONTROLLER_LOG="-vconsole:emer -vsyslog:err -vfile:info" + OVN_OVNBR_LOGFILE="$ovn_logdir/ovsdb-server-ovnbr.log" + + OVNBR_CONTROLLER_SSL_KEY="" + OVNBR_CONTROLLER_SSL_CERT="" + OVNBR_CONTROLLER_SSL_CA_CERT="" + OVNBR_CONTROLLER_SSL_BOOTSTRAP_CA_CERT="" + OVNBR_CONTROLLER_SSL_PROTOCOLS="" + OVNBR_CONTROLLER_SSL_CIPHERS="" + OVNBR_CONTROLLER_SSL_CIPHERSUITES="" + + DB_OVNBR_CREATE_INSECURE_REMOTE="no" + + DB_OVNBR_DETACH="yes" + DB_OVNBR_USE_REMOTE_IN_DB="yes" + + OVNBR_DB_SSL_KEY="" + OVNBR_DB_SSL_CERT="" + OVNBR_DB_SSL_CA_CERT="" + OVNBR_DB_SSL_PROTOCOLS="" + OVNBR_DB_SSL_CIPHERS="" + OVNBR_DB_SSL_CIPHERSUITES="" } set_option () { @@ -1096,6 +1196,15 @@ Commands: demote_ic_sb demote ovn ic-southbound db active server to backup run_ic_nb_ovsdb run ovn ic-northbound db ovsdb-server process run_ic_sb_ovsdb run ovn ic-southbound db ovsdb-server process + start_ovnbr_ovsdb start ovn bridge controller db ovsdb-server process + start_ovnbr_controller start ovn-br-controller + stop_ovnbr_ovsdb stop ovn bridge controller db ovsdb-server process + stop_ovnbr_controller stop ovn-br-controller + restart_ovnbr_ovsdb restart ovn bridge controller db ovsdb-server process + restart_ovnbr_controller restart ovn-br-controller + status_ovnbr_ovsdb status ovn bridge controller db ovsdb-server processes + status_ovnbr_controller status ovn-br-controller + run_ovnbr_ovsdb run bridge controller db ovsdb-server process Options: --ovn-northd-priority=NICE set ovn-northd's niceness (default: $OVN_NORTHD_PRIORITY) @@ -1173,6 +1282,24 @@ Options: --db-sb-relay-remote Specifies upstream cluster/server remote for ovsdb relay --db-sb-relay-use-remote-in-db=no|yes OVN_Sorthbound db listen on target connection table (default: $DB_SB_RELAY_USE_REMOTE_IN_DB) + --ovn-br-controller-priority=NICE set ovn-br-controller's niceness (default: $OVN_CONTROLLER_PRIORITY) + --ovn-br-controller-wrapper=WRAPPER run with a wrapper like valgrind for debugging + --ovn-br-controller-ssl-key=KEY OVN Bridge Controller SSL/TLS private key file + --ovn-br-controller-ssl-cert=CERT OVN Bridge Controller SSL/TLS certificate file + --ovn-br-controller-ssl-ca-cert=CERT OVN Bridge Controller SSL/TLS CA certificate file + --ovn-br-controller-ssl-bootstrap-ca-cert=CERT Bootstrapped OVN Bridge Controller SSL/TLS CA certificate file + --ovn-br-controller-ssl-protocols=PROTOCOLS OVN Bridge Controller SSL/TLS protocols + --ovn-br-controller-ssl-ciphers=CIPHERS OVN Bridge Controller SSL/TLS cipher list + --ovn-br-controller-ssl-ciphersuites=CIPHERSUITES OVN Bridge Controller TLSv1.3+ ciphersuite list + --ovn-br-db-ssl-key=KEY OVN Bridge Controller DB SSL/TLS private key file + --ovn-br-db-ssl-cert=CERT OVN Bridge Controller DB SSL/TLS certificate file + --ovn-br-db-ssl-ca-cert=CERT OVN Bridge Controller DB SSL/TLS CA certificate file + --ovn-br-db-ssl-protocols=PROTOCOLS OVN Bridge Controller DB SSL/TLS protocols + --ovn-br-db-ssl-ciphers=CIPHERS OVN Bridge Controller DB SSL/TLS cipher list + --ovn-br-db-ssl-ciphersuites=CIPHERSUITES OVN Bridge Controller DB TLSv1.3+ ciphersuite list + --ovn-br-controller-log=STRING ovn controller process logging params (default: $OVN_CONTROLLER_LOG) + --ovn-br-db-log=STRING ovn brdb ovsdb-server processes logging params (default: $OVN_BR_DB_LOG) + --ovsdb-br-wrapper=WRAPPER run with a wrapper like valgrind for debugging -h, --help display this help message File location options: @@ -1305,12 +1432,22 @@ File location options: --ovn-sb-relay-db-ssl-cert=CERT OVN_Southbound DB relay SSL/TLS certificate file --ovn-sb-relay-db-ssl-ca-cert=CERT OVN OVN_Southbound DB relay SSL/TLS CA certificate file --db-cluster-schema-upgrade=yes|no (default: $DB_CLUSTER_SCHEMA_UPGRADE) + --db-ovnbr-sock=SOCKET OVN_Bridge_Controller db socket (default: $DB_OVNBR_SOCK) + --db-ovnbr-file=FILE OVN_Bridge_Controller db file (default: $DB_OVNBR_FILE) + --db-ovnbr-pidfile=FILE OVN_Bridge_Controller db pidfile (default: $DB_OVNBR_PIDFILE) + --db-ovnbr-schema=FILE OVN_Bridge_Controller db file (default: $DB_OVNBR_SCHEMA) + --db-ovnbr-addr=ADDR OVN_Bridge_Controller db ptcp address (default: $DB_OVNBR_ADDR) + --db-ovnbr-port=PORT OVN_Bridge_Controller db ptcp port (default: $DB_OVNBR_PORT) + --db-ovnbr-ctrl-sock=SOCKET OVN_Bridge_Controller db control socket (default: $DB_OVNBR_CTRL_SOCK) + --ovn-ovnbr-logfile=FILE OVN_Bridge_Controller log file (default: $OVN_OVNBR_LOGFILE) + --db-ovnbr-create-insecure-remote=yes|no Create ptcp OVN_Bridge_Controller remote (default: $DB_OVNBR_CREATE_INSECURE_REMOTE) Default directories with "configure" option and environment variable override: logs: /usr/local/var/log/ovn (--with-logdir, OVN_LOGDIR) pidfiles and sockets: /usr/local/var/run/ovn (--with-rundir, OVN_RUNDIR) ovn-nb.db: /usr/local/etc/ovn (--with-dbdir, OVN_DBDIR) ovn-sb.db: /usr/local/etc/ovn (--with-dbdir, OVN_DBDIR) + ovn-ovnbr.db: /usr/local/etc/ovn (--with-dbdir, OVN_DBDIR) system configuration: /usr/local/etc (--sysconfdir, OVN_SYSCONFDIR) data files: /usr/local/share/ovn (--pkgdatadir, OVN_PKGDATADIR) user binaries: /usr/local/bin (--bindir, OVN_BINDIR) @@ -1536,6 +1673,32 @@ case $command in run_ic_sb_ovsdb) run_ic_sb_ovsdb ;; + start_ovnbr_ovsdb) + start_ovnbr_ovsdb + ;; + start_ovnbr_controller) + start_ovnbr_controller + ;; + stop_ovnbr_ovsdb) + stop_ovnbr_ovsdb + ;; + stop_ovnbr_controller) + stop_ovnbr_controller + ;; + restart_ovnbr_ovsdb) + restart_ovnbr_ovsdb + ;; + restart_ovnbr_controller) + restart_ovnbr_controller + ;; + status_ovnbr_ovsdb) + status_ovnbr_ovsdb + ;; + status_ovnbr_controller) + daemon_status ovn-br-controller || exit 1 + ;; + run_ovnbr_ovsdb) + run_ovnbr_ovsdb;; help) usage ;; diff --git a/utilities/ovn-ctl.8.xml b/utilities/ovn-ctl.8.xml index 99f512043e..0e03247469 100644 --- a/utilities/ovn-ctl.8.xml +++ b/utilities/ovn-ctl.8.xml @@ -58,6 +58,15 @@
restart_ic_ovsdb
run_ic_nb_ovsdb
run_ic_sb_ovsdb
+
start_ovnbr_ovsdb
+
start_br_controller
+
stop_ovnbr_ovsdb
+
stop_br_controller
+
restart_ovnbr_ovsdb
+
restart_br_controller
+
status_ovnbr_ovsdb
+
status_br_controller
+
run_ovnbr_ovsdb

Options

@@ -69,6 +78,8 @@

--ovn-ic-wrapper=WRAPPER

--ovsdb-nb-wrapper=WRAPPER

--ovsdb-sb-wrapper=WRAPPER

+

--ovn-br-controller-priority=NICE

+

--ovn-br-controller-wrapper=WRAPPER

--ovn-user=USER:GROUP

-h | --help

@@ -95,6 +106,15 @@

--ovn-controller-ssl-cert=CERT

--ovn-controller-ssl-ca-cert=CERT

--ovn-controller-ssl-bootstrap-ca-cert=CERT

+

--db-ovnbr-sock=SOCKET

+

--db-ovnbr-file=FILE

+

--db-ovnbr-schema=FILE

+

--db-ovnbr-create-insecure-remote=yes|no

+

--db-ovnbr-config-file=FILE

+

--ovn-br-controller-ssl-key=KEY

+

--ovn-br-controller-ssl-cert=CERT

+

--ovn-br-controller-ssl-ca-cert=CERT

+

--ovn-br-controller-ssl-bootstrap-ca-cert=CERT

Protocol, Cipher and Ciphersuite options

--ovn-controller-ssl-protocols=PROTOCOLS

@@ -118,6 +138,11 @@

--ovn-sb-db-ssl-ciphersuites=CIPHERSUITES

--ovn-ic-nb-db-ssl-ciphersuites=CIPHERSUITES

--ovn-ic-sb-db-ssl-ciphersuites=CIPHERSUITES

+

--ovn-br-controller-ssl-protocols=PROTOCOLS

+

--ovn-br-db-ssl-protocols=PROTOCOLS

+

--ovn-br-controller-ssl-ciphers=CIPHERS

+

--ovn-br-db-ssl-ciphers=CIPHERS

+

--ovn-br-db-ssl-ciphersuites=CIPHERSUITES

Address and port options

--db-nb-sync-from-addr=IP ADDRESS

@@ -273,6 +298,13 @@ This command will be useful for starting the OVN IC-SB ovsdb-server in a container.

+

# ovn-ctl run_ovnbr_ovsdb

+

+ This command runs the OVN bridge db ovsdb-server without passing the + detach option, making it to block until ovsdb-server exits. + This command will be useful for starting the OVN br db ovsdb-server in a + container. +

Example Usage

Run ovn-controller on a host already running OVS

@@ -372,4 +404,8 @@ # ovsdb-client convert unix:/var/run/ovn/ovnsb_db.sock /usr/local/share/ovn/ovn-sb.ovsschema

+ +

Run OVN bridge controller services on a host already running OVS

+

# ovn-ctl start_ovnbr_ovsdb

+

# ovn-ctl start_br_controller

From patchwork Mon Aug 11 10:10:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 2121396 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4c0rf34BFLz1xxG for ; Mon, 11 Aug 2025 20:36:27 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id C945961301; Mon, 11 Aug 2025 10:36:34 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id m-4YzIo3obdF; Mon, 11 Aug 2025 10:36:33 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org BD85B61256 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id BD85B61256; Mon, 11 Aug 2025 10:36:33 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 863A5C0889; Mon, 11 Aug 2025 10:36:33 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 74B61C02A4 for ; Mon, 11 Aug 2025 10:36:32 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 6751261256 for ; Mon, 11 Aug 2025 10:36:32 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id VwNYWzvNCx9z for ; Mon, 11 Aug 2025 10:36:31 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.178.249; helo=mslow3.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 3D26560F9B Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 3D26560F9B Received: from mslow3.mail.gandi.net (mslow3.mail.gandi.net [217.70.178.249]) by smtp3.osuosl.org (Postfix) with ESMTPS id 3D26560F9B for ; Mon, 11 Aug 2025 10:36:30 +0000 (UTC) Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::226]) by mslow3.mail.gandi.net (Postfix) with ESMTP id E7B30581CD7 for ; Mon, 11 Aug 2025 10:10:46 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 7AFC44320D; Mon, 11 Aug 2025 10:10:42 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Mon, 11 Aug 2025 15:40:37 +0530 Message-ID: <20250811101037.918053-1-numans@ovn.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250811100900.917769-1-numans@ovn.org> References: <20250811100900.917769-1-numans@ovn.org> MIME-Version: 1.0 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgddufedvudekucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpehnuhhmrghnshesohhvnhdrohhrghenucggtffrrghtthgvrhhnpeevudfhtdekleetkeeivdfgheefieevtdduieeikeekvdeutdeliedtgfdugfefleenucffohhmrghinhepshhpvggtrdhinhenucfkphepvdegtddumeegledttdemudgtsgelmeejtgegvgemgeekugefmegvheefvgemtggsjeegmeeltdeikeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvgedtudemgeeltddtmedutggsleemjegtgegvmeegkegufeemvgehfegvmegtsgejgeemledtieekpdhhvghlohepfhgvughorhgrrdguohhmrghinhdrnhgrmhgvpdhmrghilhhfrhhomhepnhhumhgrnhhssehovhhnrdhorhhgpdhnsggprhgtphhtthhopedvpdhrtghpthhtohepuggvvhesohhpvghnvhhsfihithgthhdrohhrghdprhgtphhtthhopehnuhhmrghnshesohhvnhdrohhrgh X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v1 12/12] rhel: Add ovn-br-controller sub package. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- rhel/automake.mk | 4 ++- rhel/ovn-fedora.spec.in | 22 +++++++++++- ...b_systemd_system_ovn-br-controller.service | 35 +++++++++++++++++++ rhel/usr_lib_systemd_system_ovn-br-db.service | 32 +++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 rhel/usr_lib_systemd_system_ovn-br-controller.service create mode 100644 rhel/usr_lib_systemd_system_ovn-br-db.service diff --git a/rhel/automake.mk b/rhel/automake.mk index 445dcd2fd4..c4ebf89d8a 100644 --- a/rhel/automake.mk +++ b/rhel/automake.mk @@ -18,7 +18,9 @@ EXTRA_DIST += \ rhel/usr_lib_systemd_system_ovn-northd.service \ rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \ rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \ - rhel/usr_share_ovn_scripts_systemd_sysconfig.template + rhel/usr_share_ovn_scripts_systemd_sysconfig.template \ + rhel/usr_lib_systemd_system_ovn-br-controller.service \ + rhel/usr_lib_systemd_system_ovn-br-db.service update_rhel_spec = \ $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in index 670f1ca9eb..c9c67c1d63 100644 --- a/rhel/ovn-fedora.spec.in +++ b/rhel/ovn-fedora.spec.in @@ -119,6 +119,14 @@ Provides: openvswitch-ovn-docker = %{?epoch:%{epoch}:}%{version}-%{release} %description docker Docker network plugins for OVN. +%package br-controller +Summary: Open Virtual Network support +License: ASL 2.0 +Requires: ovn + +%description br-controller +OVN bridge controller + %prep %autosetup -n ovn-%{version} -a 10 -p 1 @@ -165,7 +173,7 @@ install -p -D -m 0644 \ rhel/usr_share_ovn_scripts_systemd_sysconfig.template \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ovn -for service in ovn-controller ovn-controller-vtep ovn-northd ovn-ic ovn-ic-db ovn-db@; do +for service in ovn-controller ovn-controller-vtep ovn-northd ovn-ic ovn-ic-db ovn-db@ ovn-br-controller ovn-br-db; do install -p -D -m 0644 \ rhel/usr_lib_systemd_system_${service}.service \ $RPM_BUILD_ROOT%{_unitdir}/${service}.service @@ -549,7 +557,19 @@ fi %{_mandir}/man8/ovn-controller-vtep.8* %{_unitdir}/ovn-controller-vtep.service +%files br-controller +%{_bindir}/ovn-br-controller +%{_bindir}/ovn-brctl +%{_mandir}/man8/ovn-br-controller.8* +%{_mandir}/man5/ovn-br.5* +%config %{_datadir}/ovn/ovn-br.ovsschema +%{_unitdir}/ovn-br-controller.service +%{_unitdir}/ovn-br-db.service + %changelog +* Thu Jul 24 2025 Numan Siddique +- Added ovn-br-controller systemd-units. + * Wed Jan 11 2023 Vladislav Odintsov - Added ovn-db@.service systemd-unit. diff --git a/rhel/usr_lib_systemd_system_ovn-br-controller.service b/rhel/usr_lib_systemd_system_ovn-br-controller.service new file mode 100644 index 0000000000..ce59ebec02 --- /dev/null +++ b/rhel/usr_lib_systemd_system_ovn-br-controller.service @@ -0,0 +1,35 @@ +# See ovn-br-controller(8) for details about ovn-br-controller. +# +# To customize the ovn-br-controller service, you may create a configuration file +# in the /etc/systemd/system/ovn-br-controller.d/ directory. For example, to specify +# additional options to be passed to the "ovn-ctl start_ovnbr_controller" command, you +# could place the following contents in +# /etc/systemd/system/ovn-br-controller.d/local.conf: +# +# [System] +# Environment="OVNBR_CONTROLLER_OPTS=--ovnbr-controller-log=-vconsole:emer -vsyslog:err -vfile:info" +# +# Alternatively, you may specify environment variables in the file /etc/sysconfig/ovn-controller: +# +# OVNBR_CONTROLLER_OPTS="--ovnbr-controller-log=-vconsole:emer -vsyslog:err -vfile:info" + +[Unit] +Description=OVN Bridge controller daemon +After=syslog.target +Requires=openvswitch.service +After=openvswitch.service + +[Service] +Type=forking +PIDFile=/var/run/ovn/ovn-br-controller.pid +Restart=on-failure +Environment=OVN_RUNDIR=%t/ovn OVS_RUNDIR=%t/openvswitch +EnvironmentFile=-/etc/sysconfig/ovn +EnvironmentFile=-/etc/sysconfig/ovn-br-controller +ExecStart=/usr/share/ovn/scripts/ovn-ctl --no-monitor \ + --ovn-user=${OVN_USER_ID} \ + start_ovnbr_controller $OVNBR_CONTROLLER_OPTS +ExecStop=/usr/share/ovn/scripts/ovn-ctl stop_ovnbr_controller + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/rhel/usr_lib_systemd_system_ovn-br-db.service b/rhel/usr_lib_systemd_system_ovn-br-db.service new file mode 100644 index 0000000000..6de2a22f1e --- /dev/null +++ b/rhel/usr_lib_systemd_system_ovn-br-db.service @@ -0,0 +1,32 @@ +# See ovn-br(8) for details about ovn-br. +# +# To customize the ovn-br-db service, you may create a configuration file +# in the /etc/systemd/system/ovn-br-db.d/ directory. For example, to specify +# additional options to be passed to the "ovn-ctl start_ovnbr_ovsdb" command, you +# could place the following contents in +# /etc/systemd/system/ovn-br-db.d/local.conf: +# +# [System] +# Environment="OVN_BR_DB_OPTS=--db-br-create-insecure-remote=yes" +# +# Alternatively, you may specify environment variables in the file /etc/sysconfig/ovn-br-db: +# +# OVN_BR_DB_OPTS="--db-br-create-insecure-remote=yes" + +[Unit] +Description=OVN Bridge Controller OVSDB server +After=syslog.target + +[Service] +Type=oneshot +RemainAfterExit=yes +Environment=OVN_RUNDIR=%t/ovn OVN_DBDIR=/var/lib/ovn +EnvironmentFile=-/etc/sysconfig/ovn +EnvironmentFile=-/etc/sysconfig/ovn-br-db +ExecStartPre=-/usr/bin/chown -R ${OVN_USER_ID} ${OVN_DBDIR} +ExecStart=/usr/share/ovn/scripts/ovn-ctl \ + --ovn-user=${OVN_USER_ID} start_ovnbr_ovsdb $OVN_BR_DB_OPTS +ExecStop=/usr/share/ovn/scripts/ovn-ctl stop_ovnbr_ovsdb + +[Install] +WantedBy=multi-user.target