On Wed, Nov 5, 2025 at 6:49 PM Han Zhou <[email protected]> wrote: > > > > On Mon, Nov 3, 2025 at 6:18 PM <[email protected]> wrote: > > > > From: Numan Siddique <[email protected]> > > > > 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 > > Thanks Numan for implementing this! > For the DB access, is there a consideration if this is used by different CMS, > each managing different OVS bridges, how to avoid stepping over the flow > records belonging to different components? Is it reasonable to add RBAC > support? This may be added as enhancement in the future, if possible at all, > but probably you already have thoughts on this.
Hi Han, Thanks for the review comments. I agree with you. I think it's reasonable to add RBAC support. I haven't thought of it in detail as there have been requirements for us to have multiple CMS/agents manage different OVS bridges. I think we should definitely consider it as a future support. > > > - 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: > > nit: s/Rational/Rationale Ack. > > > 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." > > > > This commit also adds ovn-br-controller sub package for fedora/rhel. > > > > Signed-off-by: Numan Siddique <[email protected]> > > --- > > Makefile.am | 5 +- > > automake.mk | 36 ++ > > br-controller/automake.mk | 8 + > > br-controller/ovn-br-controller.8.xml | 24 + > > br-controller/ovn-br-controller.c | 175 ++++++ > > lib/automake.mk | 17 +- > > lib/ovn-br-idl.ann | 9 + > > lib/ovn-util.c | 31 ++ > > lib/ovn-util.h | 16 + > > ovn-br.ovsschema | 94 ++++ > > ovn-br.xml | 452 +++++++++++++++ > > 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 ++ > > tutorial/ovn-sandbox | 24 +- > > utilities/automake.mk | 8 + > > utilities/ovn-brctl.c | 524 ++++++++++++++++++ > > utilities/ovn-sbctl.c | 30 - > > 19 files changed, 1511 insertions(+), 35 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 rhel/usr_lib_systemd_system_ovn-br-controller.service > > create mode 100644 rhel/usr_lib_systemd_system_ovn-br-db.service > > create mode 100644 utilities/ovn-brctl.c > > > > diff --git a/Makefile.am b/Makefile.am > > index 4ea4ae6c52..3ad2077b37 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 = > > @@ -512,4 +514,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 > [email protected] && \ > > mv [email protected] $@ > > > > +# 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) > [email protected] && \ > > + mv [email protected] $@ > > +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 > [email protected] && \ > > + mv [email protected] $@ > > + > > # 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..be81bd9188 > > --- /dev/null > > +++ b/br-controller/ovn-br-controller.8.xml > > @@ -0,0 +1,24 @@ > > +<?xml version="1.0" encoding="utf-8"?> > > +<manpage program="ovn-br-controller" section="8" title="ovn-br-controller"> > > + <h1>Name</h1> > > + <p>ovn-br-controller -- Open Virtual Network local OVS bridge > > controller</p> > > + > > + <h1>Synopsis</h1> > > + <p> > > + <code>ovn-br-controller</code> [<var>options</var>] > > + [<var>ovs-database</var>] > > + </p> > > + > > + <h1>Description</h1> > > + <p> > > + <code>ovn-br-controller</code> is OVN logical flow based > > + local controller daemon to control and program the Open vSwitch > > + bridges. It connects up to the OVN bridge database (see > > <code>ovn-br</code>(5)) > > + over the OVSDB protocol, and down to the Open vSwitch database (see > > + <code>ovs-vswitchd.conf.db</code>(5)) over the OVSDB protocol and > > + to <code>ovs-vswitchd</code>(8) via OpenFlow. > > <code>ovn-br-controller</code> > > + processes and converts the OVN Logical flows configured in the OVN > > bridge database > > + for the Open vSwitch bridges to OpenFlow rules and programs these > > flow rules to the > > + Open vSwitch bridges. > > + </p> > > +</manpage> > > 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 <config.h> > > + > > +#include <errno.h> > > +#include <getopt.h> > > +#include <signal.h> > > +#include <stdlib.h> > > +#include <string.h> > > + > > +/* 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) > [email protected] > > && \ > > mv [email protected] $@ > > + > > +# 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) > [email protected] && \ > > + mv [email protected] $@ > > 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 d27983d1eb..2afa0972c7 100644 > > --- a/lib/ovn-util.c > > +++ b/lib/ovn-util.c > > @@ -566,6 +566,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) > > { > > @@ -1648,3 +1661,21 @@ normalize_addr_str(const char *orig_addr) > > > > return ret; > > } > > + > > +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)); > > +} > > diff --git a/lib/ovn-util.h b/lib/ovn-util.h > > index 7a5bb9559e..9fc171b71c 100644 > > --- a/lib/ovn-util.h > > +++ b/lib/ovn-util.h > > @@ -134,6 +134,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; > > @@ -594,6 +595,21 @@ dynamic_bitmap_scan(struct dynamic_bitmap *dp, bool > > target, size_t start) > > #define DYNAMIC_BITMAP_FOR_EACH_1(IDX, MAP) \ > > BITMAP_FOR_EACH_1(IDX, (MAP)->capacity, (MAP)->map) > > > > + > > +static inline const char * > > +strip_leading_zero(const char *s) > > +{ > > + return s + strspn(s, "0"); > > +} > > + > > +static inline bool > > +is_uuid_with_prefix(const char *uuid) > > +{ > > + return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X'); > > +} > > + > > +bool is_partial_uuid_match(const struct uuid *uuid, const char *match); > > + > > /* Utilities around properly handling exit command. */ > > struct ovn_exit_args { > > struct unixctl_conn **conns; > > diff --git a/ovn-br.ovsschema b/ovn-br.ovsschema > > new file mode 100644 > > index 0000000000..1fc66ec99e > > --- /dev/null > > +++ b/ovn-br.ovsschema > > @@ -0,0 +1,94 @@ > > +{ > > + "name": "OVN_Bridge_Controller", > > + "version": "0.0.1", > > + "cksum": "1701378742 4390", > > + "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": 100}}}, > > + "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} > > Shall we define an index for the table: > <bridge, table_id, priority, match> ?? > Makes sense to me. I'll address this in v4. Thanks Numan > Best regards, > Han > > > + } > > +} > > 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 @@ > > +<?xml version="1.0" encoding="utf-8"?> > > +<database name="ovn-bridge-controller" title="OVN Bridge Controller > > Database"> > > + <p> > > + 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 <code>ovn-bridge-controller</code> > > program > > + monitors the database contents and programs the OVS bridges with the > > + OpenFlow rules. > > + </p> > > + > > + <h2>External IDs</h2> > > + > > + <p> > > + Each of the tables in this database contains a special column, named > > + <code>external_ids</code>. This column has the same form and purpose > > each > > + place it appears. > > + </p> > > + > > + <dl> > > + <dt><code>external_ids</code>: map of string-string pairs</dt> > > + <dd> > > + 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. > > + </dd> > > + </dl> > > + > > + <table name="BR_Global" title="Bridge Controller configuration"> > > + <column name="br_cfg"> > > + Sequence number for client to increment. When a client modifies any > > + part of the bridge Controller database configuration and wishes to > > wait > > + for <code>ovn-br-controller</code> to finish applying the changes, > > it may > > + increment this sequence number. > > + </column> > > + > > + <group title="Common Columns"> > > + <column name="external_ids"> > > + See <em>External IDs</em> at the beginning of this document. > > + </column> > > + </group> > > + > > + <group title="Common options"> > > + <column name="options"> > > + This column provides general key/value settings. The supported > > + options are described individually below. > > + </column> > > + </group> > > + > > + <group title="Connection Options"> > > + <column name="connections"> > > + 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 <ref > > table="Connection"/> > > + table for more information. > > + </column> > > + <column name="ssl"> > > + Global SSL/TLS configuration. > > + </column> > > + </group> > > + </table> > > + > > + <table name="Connection" title="OVSDB client connections."> > > + <p> > > + Configuration for a database connection to an Open vSwitch database > > + (OVSDB) client. > > + </p> > > + > > + <p> > > + This table primarily configures the Open vSwitch database server > > + (<code>ovsdb-server</code>). > > + </p> > > + > > + <p> > > + The Open vSwitch database server can initiate and maintain active > > + connections to remote clients. It can also listen for database > > + connections. > > + </p> > > + > > + <group title="Core Features"> > > + <column name="target"> > > + <p>Connection methods for clients.</p> > > + <p> > > + The following connection methods are currently supported: > > + </p> > > + <dl> > > + > > <dt><code>ssl:<var>host</var></code>[<code>:<var>port</var></code>]</dt> > > + <dd> > > + <p> > > + The specified SSL/TLS <var>port</var> on the host at the > > given > > + <var>host</var>, 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 <ref > > table="SSL"/> > > + table. > > + </p> > > + <p> > > + If <var>port</var> is not specified, it defaults to 6640. > > + </p> > > + <p> > > + SSL/TLS support is an optional feature that is not always > > + built as part of OVN or Open vSwitch. > > + </p> > > + </dd> > > + > > + > > <dt><code>tcp:<var>host</var></code>[<code>:<var>port</var></code>]</dt> > > + <dd> > > + <p> > > + The specified TCP <var>port</var> on the host at the given > > + <var>host</var>, which can either be a DNS name (if built > > with > > + unbound library) or an IP address. If <var>host</var> is an > > IPv6 > > + address, wrap it in square brackets, e.g. > > <code>tcp:[::1]:6640</code>. > > + </p> > > + <p> > > + If <var>port</var> is not specified, it defaults to 6640. > > + </p> > > + </dd> > > + > > <dt><code>pssl:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> > > + <dd> > > + <p> > > + Listens for SSL/TLS connections on the specified TCP > > + <var>port</var>. > > + Specify 0 for <var>port</var> to have the kernel > > automatically > > + choose an available port. If <var>host</var>, 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 > > + <var>host</var> is an IPv6 address, wrap in square brackets, > > + e.g. <code>pssl:6640:[::1]</code>. If <var>host</var> 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 <ref table="SSL"/> table. > > + </p> > > + <p> > > + If <var>port</var> is not specified, it defaults to 6640. > > + </p> > > + <p> > > + SSL/TLS support is an optional feature that is not always > > built > > + as part of OVN or Open vSwitch. > > + </p> > > + </dd> > > + > > <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> > > + <dd> > > + <p> > > + Listens for connections on the specified TCP <var>port</var>. > > + Specify 0 for <var>port</var> to have the kernel > > automatically > > + choose an available port. If <var>host</var>, 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 > > + <var>host</var> is an IPv6 address, wrap it in square > > brackets, > > + e.g. <code>ptcp:6640:[::1]</code>. If <var>host</var> is not > > + specified then it listens only on IPv4 addresses. > > + </p> > > + <p> > > + If <var>port</var> is not specified, it defaults to 6640. > > + </p> > > + </dd> > > + </dl> > > + <p>When multiple clients are configured, the <ref column="target"/> > > + values must be unique. Duplicate <ref column="target"/> values > > yield > > + unspecified results.</p> > > + </column> > > + </group> > > + > > + <group title="Client Failure Detection and Handling"> > > + <column name="max_backoff"> > > + Maximum number of milliseconds to wait between connection attempts. > > + Default is implementation-specific. > > + </column> > > + > > + <column name="inactivity_probe"> > > + 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. > > + </column> > > + </group> > > + > > + <group title="Status"> > > + <p> > > + Key-value pair of <ref column="is_connected"/> is always updated. > > + Other key-value pairs in the status columns may be updated depends > > + on the <ref column="target"/> type. > > + </p> > > + > > + <p> > > + When <ref column="target"/> specifies a connection method that > > + listens for inbound connections (e.g. <code>ptcp:</code> or > > + <code>punix:</code>), both <ref column="n_connections"/> and > > + <ref column="is_connected"/> may also be updated while the > > + remaining key-value pairs are omitted. > > + </p> > > + > > + <p> > > + On the other hand, when <ref column="target"/> 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. > > + </p> > > + > > + <column name="is_connected"> > > + <code>true</code> if currently connected to this client, > > + <code>false</code> otherwise. > > + </column> > > + > > + <column name="status" key="last_error"> > > + A human-readable description of the last error on the connection > > + to the manager; i.e. <code>strerror(errno)</code>. This key > > + will exist only if an error has occurred. > > + </column> > > + > > + <column name="status" key="state" > > + type='{"type": "string", "enum": ["set", ["VOID", "BACKOFF", > > "CONNECTING", "ACTIVE", "IDLE"]]}'> > > + <p> > > + The state of the connection to the manager: > > + </p> > > + <dl> > > + <dt><code>VOID</code></dt> > > + <dd>Connection is disabled.</dd> > > + > > + <dt><code>BACKOFF</code></dt> > > + <dd>Attempting to reconnect at an increasing period.</dd> > > + > > + <dt><code>CONNECTING</code></dt> > > + <dd>Attempting to connect.</dd> > > + > > + <dt><code>ACTIVE</code></dt> > > + <dd>Connected, remote host responsive.</dd> > > + > > + <dt><code>IDLE</code></dt> > > + <dd>Connection is idle. Waiting for response to keep-alive.</dd> > > + </dl> > > + <p> > > + These values may change in the future. They are provided only > > for > > + human consumption. > > + </p> > > + </column> > > + > > + <column name="status" key="sec_since_connect" > > + type='{"type": "integer", "minInteger": 0}'> > > + 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. > > + </column> > > + > > + <column name="status" key="sec_since_disconnect" > > + type='{"type": "integer", "minInteger": 0}'> > > + The amount of time since this client last disconnected from the > > + database (in seconds). Value is empty if client has never > > + disconnected. > > + </column> > > + > > + <column name="status" key="locks_held"> > > + Space-separated list of the names of OVSDB locks that the > > connection > > + holds. Omitted if the connection does not hold any locks. > > + </column> > > + > > + <column name="status" key="locks_waiting"> > > + 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. > > + </column> > > + > > + <column name="status" key="locks_lost"> > > + 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. > > + </column> > > + > > + <column name="status" key="n_connections" > > + type='{"type": "integer", "minInteger": 2}'> > > + When <ref column="target"/> specifies a connection method that > > + listens for inbound connections (e.g. <code>ptcp:</code> or > > + <code>pssl:</code>) and more than one connection is actually > > active, > > + the value is the number of active connections. Otherwise, this > > + key-value pair is omitted. > > + </column> > > + > > + <column name="status" key="bound_port" type='{"type": "integer"}'> > > + When <ref column="target"/> is <code>ptcp:</code> or > > + <code>pssl:</code>, this is the TCP port on which the OVSDB server > > is > > + listening. (This is particularly useful when <ref > > + column="target"/> specifies a port of 0, allowing the kernel to > > + choose any available port.) > > + </column> > > + </group> > > + > > + <group title="Common Columns"> > > + The overall purpose of these columns is described under <code>Common > > + Columns</code> at the beginning of this document. > > + > > + <column name="external_ids"/> > > + <column name="other_config"/> > > + </group> > > + </table> > > + > > + <table name="SSL"> > > + SSL/TLS configuration for ovn-nb database access. > > + > > + <column name="private_key"> > > + Name of a PEM file containing the private key used as the switch's > > + identity for SSL/TLS connections to the controller. > > + </column> > > + > > + <column name="certificate"> > > + 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. > > + </column> > > + > > + <column name="ca_cert"> > > + Name of a PEM file containing the CA certificate used to verify > > + that the switch is connected to a trustworthy controller. > > + </column> > > + > > + <column name="bootstrap_ca_cert"> > > + If set to <code>true</code>, 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. <em>This option exposes the > > + SSL/TLS connection to a man-in-the-middle attack obtaining the > > initial > > + CA certificate.</em> It may still be useful for bootstrapping. > > + </column> > > + > > + <column name="ssl_protocols"> > > + <p> > > + Range or a comma- or space-delimited list of the SSL/TLS protocols > > to > > + enable for SSL/TLS connections. > > + </p> > > + <p> > > + Supported protocols include <code>TLSv1.2</code> and > > + <code>TLSv1.3</code>. Ranges can be provided in a form of two > > protocol > > + names separated with a dash (<code>TLSv1.2-TLSv1.3</code>), or as a > > + single protocol name with a plus sign (<code>TLSv1.2+</code>). 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 > > + <code>TLSv1.X,TLSv1.(X+2)</code>, the <code>TLSv1.(X+1)</code> 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. > > + </p> > > + <p> > > + The default when this option is omitted is <code>TLSv1.2+</code>. > > + </p> > > + </column> > > + > > + <column name="ssl_ciphers"> > > + 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 <code>DEFAULT:@SECLEVEL=2</code>. > > + </column> > > + > > + <column name="ssl_ciphersuites"> > > + 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. > > + </column> > > + > > + <group title="Common Columns"> > > + The overall purpose of these columns is described under <code>Common > > + Columns</code> at the beginning of this document. > > + > > + <column name="external_ids"/> > > + </group> > > + </table> > > + > > + <table name="Bridge" title="OVS Bridge to control"> > > + <column name="name"> > > + Name of the OVS bridge. This bridge should exist in the local OVS > > + database. > > + </column> > > + > > + <column name="options"> > > + Reserved for future use. > > + </column> > > + > > + <column name="external_ids"> > > + See <em>External IDs</em> at the beginning of this document. > > + </column> > > + </table> > > + > > + <table name="Logical_Flow" title="Logical flow"> > > + <column name="bridge"> > > + The bridge to which the logical flow belongs to. > > + </column> > > + > > + <column name="table_id"> > > + The stage in the logical pipeline, analogous to an OpenFlow table > > number. > > + </column> > > + > > + <column name="priority"> > > + 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. > > + </column> > > + > > + <column name="match"> > > + <p> > > + A matching expression. OVN provides a superset of OpenFlow > > matching > > + capabilities, using a syntax similar to Boolean expressions in a > > + programming language. > > + </p> > > + > > + <p> > > + Please see the documentation of the > > + <ref > > + column="match" table="Logical_Flow" db="OVN_Southbound"/> > > + column of the <ref table="Logical_Flow" > > + db="OVN_Southbound"/> table in the <ref db="OVN_Southbound"/> > > database. > > + </p> > > + </column> > > + > > + <column name="actions"> > > + <p> > > + Logical datapath actions, to be executed when the logical flow > > + represented by this row is the highest-priority match. > > + </p> > > + > > + <p> > > + Actions share lexical syntax with the <ref column="match"/> > > column. An > > + empty set of actions (or one that contains just white space or > > + comments), or a set of actions that consists of just > > + <code>drop;</code>, causes the matched packets to be dropped. > > + Otherwise, the column should contain a sequence of actions, each > > + terminated by a semicolon. > > + </p> > > + > > + <p> > > + Please see the documentation of the > > + <ref > > + column="actions" table="Logical_Flow" db="OVN_Southbound"/> > > + column of the <ref table="Logical_Flow" > > + db="OVN_Southbound"/> table in the <ref db="OVN_Southbound"/> > > database. > > + </p> > > + </column> > > + > > + <column name="external_ids"> > > + See <em>External IDs</em> at the beginning of this document. > > + </column> > > + </table> > > +</database> > > 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..a1ab46ebef 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 > > +* Wed Sep 24 2025 Numan Siddique <[email protected]> > > +- Added ovn-br-controller systemd-units. > > + > > * Wed Jan 11 2023 Vladislav Odintsov <[email protected]> > > - Added [email protected] 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..9f1e3f4b81 > > --- /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 > > 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 > > diff --git a/tutorial/ovn-sandbox b/tutorial/ovn-sandbox > > index a689eb30f9..970afde5cd 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 <<EOF > > > > > > diff --git a/utilities/automake.mk b/utilities/automake.mk > > index 79cf780ab9..b620038d01 100644 > > --- a/utilities/automake.mk > > +++ b/utilities/automake.mk > > @@ -118,4 +118,12 @@ bin_PROGRAMS += utilities/ovn-debug > > utilities_ovn_debug_SOURCES = utilities/ovn-debug.c > > utilities_ovn_debug_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la > > $(OVS_LIBDIR)/libopenvswitch.la > > > > +# ovn-brctl > > +bin_PROGRAMS += utilities/ovn-brctl > > +utilities_ovn_brctl_SOURCES = \ > > + utilities/ovn-dbctl.c \ > > + utilities/ovn-dbctl.h \ > > + utilities/ovn-brctl.c > > +utilities_ovn_brctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la > > $(OVS_LIBDIR)/libopenvswitch.la > > + > > include utilities/bugtool/automake.mk > > diff --git a/utilities/ovn-brctl.c b/utilities/ovn-brctl.c > > new file mode 100644 > > index 0000000000..60b62420a7 > > --- /dev/null > > +++ b/utilities/ovn-brctl.c > > @@ -0,0 +1,524 @@ > > +/* > > + * 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 <config.h> > > + > > +#include <getopt.h> > > +#include <inttypes.h> > > +#include <stdlib.h> > > +#include <stdio.h> > > + > > +/* 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; > > +}; > > + > > +static struct ctl_context * > > +brctl_ctx_create(void) > > +{ > > + struct brctl_context *prctx = xmalloc(sizeof *prctx); > > + > > + return &prctx->base; > > +} > > + > > +static void > > +brctl_ctx_destroy(struct ctl_context *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); > > +} > > + > > +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); > > +} > > diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c > > index 0bba8f6889..c06bc435c4 100644 > > --- a/utilities/ovn-sbctl.c > > +++ b/utilities/ovn-sbctl.c > > @@ -624,12 +624,6 @@ sbctl_lflow_cmp(const void *a_, const void *b_) > > return cmp ? cmp : strcmp(a->actions, b->actions); > > } > > > > -static bool > > -is_uuid_with_prefix(const char *uuid) > > -{ > > - return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X'); > > -} > > - > > static bool > > parse_partial_uuid(char *s) > > { > > @@ -648,30 +642,6 @@ parse_partial_uuid(char *s) > > return false; > > } > > > > -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)); > > -} > > - > > static char * > > default_ovs(void) > > { > > -- > > 2.51.0 > > > > _______________________________________________ > > dev mailing list > > [email protected] > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
