diff --git a/SOURCES/openvswitch-3.2.0.patch b/SOURCES/openvswitch-3.2.0.patch new file mode 100644 index 0000000..0dd7ce6 --- /dev/null +++ b/SOURCES/openvswitch-3.2.0.patch @@ -0,0 +1,1336 @@ +diff --git a/.ci/linux-build.sh b/.ci/linux-build.sh +index 99850a943..8227a5748 100755 +--- a/.ci/linux-build.sh ++++ b/.ci/linux-build.sh +@@ -82,6 +82,10 @@ if [ "$DPDK" ] || [ "$DPDK_SHARED" ]; then + install_dpdk + fi + ++if [ "$STD" ]; then ++ CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} -std=$STD" ++fi ++ + if [ "$CC" = "clang" ]; then + CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} -Wno-error=unused-command-line-argument" + elif [ "$M32" ]; then +diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml +index 47d239f10..bc5494e86 100644 +--- a/.github/workflows/build-and-test.yml ++++ b/.github/workflows/build-and-test.yml +@@ -85,6 +85,7 @@ jobs: + LIBS: ${{ matrix.libs }} + M32: ${{ matrix.m32 }} + OPTS: ${{ matrix.opts }} ++ STD: ${{ matrix.std }} + TESTSUITE: ${{ matrix.testsuite }} + + name: linux ${{ join(matrix.*, ' ') }} +@@ -100,6 +101,11 @@ jobs: + - compiler: clang + opts: --disable-ssl + ++ - compiler: gcc ++ std: c99 ++ - compiler: clang ++ std: c99 ++ + - compiler: gcc + testsuite: test + - compiler: clang +diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst +index d13895655..36adcc5db 100644 +--- a/Documentation/ref/ovs-actions.7.rst ++++ b/Documentation/ref/ovs-actions.7.rst +@@ -694,7 +694,8 @@ encapsulated in an OpenFlow ``packet-in`` message. The supported options are: + Limit to *max_len* the number of bytes of the packet to send in the + ``packet-in.`` A *max_len* of 0 prevents any of the packet from being + sent (thus, only metadata is included). By default, the entire packet is +- sent, equivalent to a *max_len* of 65535. ++ sent, equivalent to a *max_len* of 65535. This option has no effect in ++ Open vSwith 2.7 and later: the entire packet will always be sent. + + ``reason=``\ *reason* + Specify *reason* as the reason for sending the message in the +@@ -733,6 +734,12 @@ encapsulated in an OpenFlow ``packet-in`` message. The supported options are: + options require the Open vSwitch ``NXAST_CONTROLLER`` extension action added + in Open vSwitch 1.6. + ++ Open vSwitch 2.7 and later is configured to not buffer packets for the ++ packet-in event. As a result, the full packet is always sent to ++ controllers. This means that the ``max_len`` option has no effect on the ++ ``controller`` action, and all values (even 0) are equivalent to the default ++ value of 65535. ++ + + The ``enqueue`` action + ---------------------- +diff --git a/Makefile.am b/Makefile.am +index db341504d..439e2bf6d 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -368,7 +368,7 @@ ALL_LOCAL += manpage-check + manpage-check: $(man_MANS) $(dist_man_MANS) $(noinst_man_MANS) + @error=false; \ + for manpage in $?; do \ +- LANG=en_US.UTF-8 groff -w mac -w delim -w escape -w input -w missing -w tab -T utf8 -man -p -z $$manpage >$@.tmp 2>&1; \ ++ LANG=en_US.UTF-8 groff -t -w mac -w delim -w escape -w input -w missing -w tab -T utf8 -man -p -z $$manpage >$@.tmp 2>&1; \ + if grep warning: $@.tmp; then error=:; fi; \ + rm -f $@.tmp; \ + done; \ +@@ -415,7 +415,7 @@ endif + CLEANFILES += flake8-check + + -include manpages.mk +-manpages.mk: $(MAN_ROOTS) build-aux/sodepends.py python/build/soutil.py ++manpages.mk: $(MAN_ROOTS) build-aux/sodepends.py python/ovs_build_helpers/soutil.py + @PYTHONPATH=$$PYTHONPATH$(psep)$(srcdir)/python $(PYTHON3) $(srcdir)/build-aux/sodepends.py -I. -I$(srcdir) $(MAN_ROOTS) >$(@F).tmp + @if cmp -s $(@F).tmp $@; then \ + touch $@; \ +diff --git a/NEWS b/NEWS +index a3a5c2e4a..790cab495 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,3 +1,6 @@ ++v3.2.1 - xx xxx xxxx ++-------------------- ++ + v3.2.0 - 17 Aug 2023 + -------------------- + - OVSDB: +diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields +index efec59c25..05d3e1df3 100755 +--- a/build-aux/extract-ofp-fields ++++ b/build-aux/extract-ofp-fields +@@ -4,9 +4,9 @@ import getopt + import sys + import os.path + import xml.dom.minidom +-import build.nroff + +-from build.extract_ofp_fields import ( ++from ovs_build_helpers import nroff ++from ovs_build_helpers.extract_ofp_fields import ( + extract_ofp_fields, + PREREQS, + OXM_CLASSES, +@@ -216,7 +216,7 @@ def field_to_xml(field_node, f, body, summary): + """.PP + \\fB%s Field\\fR + .TS +-tab(;); ++tab(;),nowarn; + l lx. + """ + % title +@@ -297,7 +297,7 @@ l lx. + body += [".TE\n"] + + body += [".PP\n"] +- body += [build.nroff.block_xml_to_nroff(field_node.childNodes)] ++ body += [nroff.block_xml_to_nroff(field_node.childNodes)] + + + def group_xml_to_nroff(group_node, fields): +@@ -310,14 +310,14 @@ def group_xml_to_nroff(group_node, fields): + id_ = node.attributes["id"].nodeValue + field_to_xml(node, fields[id_], body, summary) + else: +- body += [build.nroff.block_xml_to_nroff([node])] ++ body += [nroff.block_xml_to_nroff([node])] + + content = [ + ".bp\n", +- '.SH "%s"\n' % build.nroff.text_to_nroff(title.upper() + " FIELDS"), ++ '.SH "%s"\n' % nroff.text_to_nroff(title.upper() + " FIELDS"), + '.SS "Summary:"\n', + ".TS\n", +- "tab(;);\n", ++ "tab(;),nowarn;\n", + "l l l l l l l.\n", + "Name;Bytes;Mask;RW?;Prereqs;NXM/OXM Support\n", + "\_;\_;\_;\_;\_;\_\n", +@@ -329,7 +329,7 @@ def group_xml_to_nroff(group_node, fields): + + + def make_oxm_classes_xml(document): +- s = """tab(;); ++ s = """tab(;),nowarn; + l l l. + Prefix;Vendor;Class + \_;\_;\_ +@@ -422,7 +422,7 @@ ovs\-fields \- protocol header fields in OpenFlow and Open vSwitch + elif node.nodeType == node.COMMENT_NODE: + pass + else: +- s += build.nroff.block_xml_to_nroff([node]) ++ s += nroff.block_xml_to_nroff([node]) + + for f in fields: + if "used" not in f: +diff --git a/build-aux/gen_ofp_field_decoders b/build-aux/gen_ofp_field_decoders +index 0b797ee8c..0cb6108c2 100755 +--- a/build-aux/gen_ofp_field_decoders ++++ b/build-aux/gen_ofp_field_decoders +@@ -2,7 +2,7 @@ + + import argparse + +-import build.extract_ofp_fields as extract_fields ++from ovs_build_helpers.extract_ofp_fields import extract_ofp_fields + + + def main(): +@@ -19,7 +19,7 @@ def main(): + + args = parser.parse_args() + +- fields = extract_fields.extract_ofp_fields(args.metaflow) ++ fields = extract_ofp_fields(args.metaflow) + + field_decoders = {} + aliases = {} +diff --git a/build-aux/sodepends.py b/build-aux/sodepends.py +index 45812bcbd..ac8dd61a4 100755 +--- a/build-aux/sodepends.py ++++ b/build-aux/sodepends.py +@@ -14,9 +14,10 @@ + # See the License for the specific language governing permissions and + # limitations under the License. + +-from build import soutil + import sys + ++from ovs_build_helpers import soutil ++ + + def sodepends(include_dirs, filenames, dst): + ok = True +diff --git a/build-aux/soexpand.py b/build-aux/soexpand.py +index 00adcf47a..7d4dc0486 100755 +--- a/build-aux/soexpand.py ++++ b/build-aux/soexpand.py +@@ -14,9 +14,10 @@ + # See the License for the specific language governing permissions and + # limitations under the License. + +-from build import soutil + import sys + ++from ovs_build_helpers import soutil ++ + + def soexpand(include_dirs, src, dst): + ok = True +diff --git a/build-aux/xml2nroff b/build-aux/xml2nroff +index ee5553f45..3e937910b 100755 +--- a/build-aux/xml2nroff ++++ b/build-aux/xml2nroff +@@ -18,7 +18,7 @@ import getopt + import sys + import xml.dom.minidom + +-import build.nroff ++from ovs_build_helpers import nroff + + argv0 = sys.argv[0] + +@@ -90,10 +90,10 @@ def manpage_to_nroff(xml_file, subst, include_path, version=None): + . I "\\$1" + . RE + .. +-''' % (build.nroff.text_to_nroff(program), build.nroff.text_to_nroff(section), +- build.nroff.text_to_nroff(title), build.nroff.text_to_nroff(version)) ++''' % (nroff.text_to_nroff(program), nroff.text_to_nroff(section), ++ nroff.text_to_nroff(title), nroff.text_to_nroff(version)) + +- s += build.nroff.block_xml_to_nroff(doc.childNodes) + "\n" ++ s += nroff.block_xml_to_nroff(doc.childNodes) + "\n" + + return s + +@@ -139,7 +139,7 @@ if __name__ == "__main__": + + try: + s = manpage_to_nroff(args[0], subst, include_path, version) +- except build.nroff.error.Error as e: ++ except nroff.error.Error as e: + sys.stderr.write("%s: %s\n" % (argv0, e.msg)) + sys.exit(1) + for line in s.splitlines(): +diff --git a/configure.ac b/configure.ac +index 320509c5f..5857ac8e6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -13,7 +13,7 @@ + # limitations under the License. + + AC_PREREQ(2.63) +-AC_INIT(openvswitch, 3.2.0, bugs@openvswitch.org) ++AC_INIT(openvswitch, 3.2.1, bugs@openvswitch.org) + AC_CONFIG_SRCDIR([vswitchd/ovs-vswitchd.c]) + AC_CONFIG_MACRO_DIR([m4]) + AC_CONFIG_AUX_DIR([build-aux]) +@@ -21,7 +21,11 @@ AC_CONFIG_HEADERS([config.h]) + AC_CONFIG_TESTDIR([tests]) + AM_INIT_AUTOMAKE([tar-pax]) + +-AC_PROG_CC_C99 ++# AC_PROG_CC doesn't try enabling C99 in autoconf 2.69 and below, but ++# AC_PROG_CC_C99 is deprecated in newer ones. In autoconf 2.70+ both ++# will try enabling features up to C11. ++m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_C99]) ++ + AM_PROG_CC_C_O + AC_PROG_CXX + AC_PROG_CPP +diff --git a/debian/changelog b/debian/changelog +index 8757e5cb2..385fe9716 100644 +--- a/debian/changelog ++++ b/debian/changelog +@@ -1,3 +1,9 @@ ++openvswitch (3.2.1-1) unstable; urgency=low ++ [ Open vSwitch team ] ++ * New upstream version ++ ++ -- Open vSwitch team Thu, 17 Aug 2023 15:20:36 +0200 ++ + openvswitch (3.2.0-1) unstable; urgency=low + + * New upstream version +diff --git a/include/openvswitch/compiler.h b/include/openvswitch/compiler.h +index cf009f826..52614a5ac 100644 +--- a/include/openvswitch/compiler.h ++++ b/include/openvswitch/compiler.h +@@ -37,6 +37,16 @@ + #define OVS_NO_RETURN + #endif + ++#ifndef typeof ++#define typeof __typeof__ ++#endif ++ ++#ifndef __cplusplus ++#ifndef asm ++#define asm __asm__ ++#endif ++#endif ++ + #if __GNUC__ && !__CHECKER__ + #define OVS_UNUSED __attribute__((__unused__)) + #define OVS_PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1))) +diff --git a/lib/automake.mk b/lib/automake.mk +index e64ee76ce..24b0ffefe 100644 +--- a/lib/automake.mk ++++ b/lib/automake.mk +@@ -451,7 +451,7 @@ lib_libsflow_la_SOURCES = \ + lib/sflow_poller.c \ + lib/sflow_receiver.c + lib_libsflow_la_CPPFLAGS = $(AM_CPPFLAGS) +-lib_libsflow_la_CFLAGS = $(AM_CFLAGS) ++lib_libsflow_la_CFLAGS = $(AM_CFLAGS) -D_BSD_SOURCE -D_DEFAULT_SOURCE + if HAVE_WNO_UNUSED + lib_libsflow_la_CFLAGS += -Wno-unused + endif +diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c +index 9730e0eec..157694bcf 100644 +--- a/lib/dpif-netdev.c ++++ b/lib/dpif-netdev.c +@@ -3380,14 +3380,13 @@ static inline void + netdev_flow_key_init(struct netdev_flow_key *key, + const struct flow *flow) + { +- uint64_t *dst = miniflow_values(&key->mf); + uint32_t hash = 0; + uint64_t value; + + miniflow_map_init(&key->mf, flow); + miniflow_init(&key->mf, flow); + +- size_t n = dst - miniflow_get_values(&key->mf); ++ size_t n = miniflow_n_values(&key->mf); + + FLOW_FOR_EACH_IN_MAPS (value, flow, key->mf.map) { + hash = hash_add64(hash, value); +diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c +index 77f0c87dd..953150074 100644 +--- a/lib/fatal-signal.c ++++ b/lib/fatal-signal.c +@@ -138,10 +138,6 @@ fatal_signal_init(void) + + backtrace_capture(&dummy_bt); + +- if (!dummy_bt.n_frames) { +- VLOG_DBG("Capturing of dummy backtrace has failed."); +- } +- + fatal_signal_create_wakeup_events(); + + #ifdef _WIN32 +diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml +index bdd12f6a7..ac72a44bc 100644 +--- a/lib/meta-flow.xml ++++ b/lib/meta-flow.xml +@@ -3517,23 +3517,24 @@ actions=clone(load:0->NXM_OF_IN_PORT[],output:123) +

+ + ++tab(;); + r r r r r. +-Criteria OpenFlow 1.0 OpenFlow 1.1 OpenFlow 1.2+ NXM +-\_ \_ \_ \_ \_ +-[1] \fL????\fR/\fL1\fR,\fL??\fR/\fL?\fR \fL????\fR/\fL1\fR,\fL??\fR/\fL?\fR \fL0000\fR/\fL0000\fR,\fL--\fR \fL0000\fR/\fL0000\fR +-[2] \fLffff\fR/\fL0\fR,\fL??\fR/\fL?\fR \fLffff\fR/\fL0\fR,\fL??\fR/\fL?\fR \fL0000\fR/\fLffff\fR,\fL--\fR \fL0000\fR/\fLffff\fR +-[3] \fL0xxx\fR/\fL0\fR,\fL??\fR/\fL1\fR \fL0xxx\fR/\fL0\fR,\fL??\fR/\fL1\fR \fL1xxx\fR/\fLffff\fR,\fL--\fR \fL1xxx\fR/\fL1fff\fR +-[4] \fL????\fR/\fL1\fR,\fL0y\fR/\fL0\fR \fLfffe\fR/\fL0\fR,\fL0y\fR/\fL0\fR \fL1000\fR/\fL1000\fR,\fL0y\fR \fLz000\fR/\fLf000\fR +-[5] \fL0xxx\fR/\fL0\fR,\fL0y\fR/\fL0\fR \fL0xxx\fR/\fL0\fR,\fL0y\fR/\fL0\fR \fL1xxx\fR/\fLffff\fR,\fL0y\fR \fLzxxx\fR/\fLffff\fR ++Criteria;OpenFlow 1.0;OpenFlow 1.1;OpenFlow 1.2+;NXM ++\_;\_;\_;\_;\_ ++[1];\fL????\fR/\fL1\fR,\fL??\fR/\fL?\fR;\fL????\fR/\fL1\fR,\fL??\fR/\fL?\fR;\fL0000\fR/\fL0000\fR,\fL--\fR;\fL0000\fR/\fL0000\fR ++[2];\fLffff\fR/\fL0\fR,\fL??\fR/\fL?\fR;\fLffff\fR/\fL0\fR,\fL??\fR/\fL?\fR;\fL0000\fR/\fLffff\fR,\fL--\fR;\fL0000\fR/\fLffff\fR ++[3];\fL0xxx\fR/\fL0\fR,\fL??\fR/\fL1\fR;\fL0xxx\fR/\fL0\fR,\fL??\fR/\fL1\fR;\fL1xxx\fR/\fLffff\fR,\fL--\fR;\fL1xxx\fR/\fL1fff\fR ++[4];\fL????\fR/\fL1\fR,\fL0y\fR/\fL0\fR;\fLfffe\fR/\fL0\fR,\fL0y\fR/\fL0\fR;\fL1000\fR/\fL1000\fR,\fL0y\fR;\fLz000\fR/\fLf000\fR ++[5];\fL0xxx\fR/\fL0\fR,\fL0y\fR/\fL0\fR;\fL0xxx\fR/\fL0\fR,\fL0y\fR/\fL0\fR;\fL1xxx\fR/\fLffff\fR,\fL0y\fR;\fLzxxx\fR/\fLffff\fR + .T& +-r r c c r. +-[6] (none) (none) \fL1001\fR/\fL1001\fR,\fL--\fR \fL1001\fR/\fL1001\fR ++r c c r r. ++[6];(none);(none);\fL1001\fR/\fL1001\fR,\fL--\fR;\fL1001\fR/\fL1001\fR + .T& +-r r c c c. +-[7] (none) (none) (none) \fL3000\fR/\fL3000\fR +-[8] (none) (none) (none) \fL0000\fR/\fL0fff\fR +-[9] (none) (none) (none) \fL0000\fR/\fLf000\fR +-[10] (none) (none) (none) \fL0000\fR/\fLefff\fR ++r c c c r. ++[7];(none);(none);(none);\fL3000\fR/\fL3000\fR ++[8];(none);(none);(none);\fL0000\fR/\fL0fff\fR ++[9];(none);(none);(none);\fL0000\fR/\fLf000\fR ++[10];(none);(none);(none);\fL0000\fR/\fLefff\fR + + +

+diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c +index 8f1361e21..55700250d 100644 +--- a/lib/netdev-dpdk.c ++++ b/lib/netdev-dpdk.c +@@ -1312,6 +1312,16 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev) + dev->hw_ol_features &= ~NETDEV_RX_HW_SCATTER; + } + ++ if (!strcmp(info.driver_name, "net_tap")) { ++ /* FIXME: L4 checksum offloading is broken in DPDK net/tap driver. ++ * This workaround can be removed once the fix makes it to a DPDK ++ * LTS release used by OVS. */ ++ VLOG_INFO("%s: disabled Tx L4 checksum offloads for a net/tap port.", ++ netdev_get_name(&dev->up)); ++ info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_UDP_CKSUM; ++ info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_TCP_CKSUM; ++ } ++ + if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) { + dev->hw_ol_features |= NETDEV_TX_IPV4_CKSUM_OFFLOAD; + } else { +@@ -2431,6 +2441,7 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) + + if (!(mbuf->ol_flags & (RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_L4_MASK + | RTE_MBUF_F_TX_TCP_SEG))) { ++ mbuf->ol_flags &= ~(RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IPV6); + return true; + } + +diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c +index 14bc87771..992627fa2 100644 +--- a/lib/netdev-offload-dpdk.c ++++ b/lib/netdev-offload-dpdk.c +@@ -2537,15 +2537,15 @@ out: + return ret; + } + +-static int +-netdev_offload_dpdk_flow_flush(struct netdev *netdev) ++static void ++flush_netdev_flows_in_related(struct netdev *netdev, struct netdev *related) + { +- struct cmap *map = offload_data_map(netdev); +- struct ufid_to_rte_flow_data *data; + unsigned int tid = netdev_offload_thread_id(); ++ struct cmap *map = offload_data_map(related); ++ struct ufid_to_rte_flow_data *data; + + if (!map) { +- return -1; ++ return; + } + + CMAP_FOR_EACH (data, node, map) { +@@ -2556,6 +2556,31 @@ netdev_offload_dpdk_flow_flush(struct netdev *netdev) + netdev_offload_dpdk_flow_destroy(data); + } + } ++} ++ ++static bool ++flush_in_vport_cb(struct netdev *vport, ++ odp_port_t odp_port OVS_UNUSED, ++ void *aux) ++{ ++ struct netdev *netdev = aux; ++ ++ /* Only vports are related to physical devices. */ ++ if (netdev_vport_is_vport_class(vport->netdev_class)) { ++ flush_netdev_flows_in_related(netdev, vport); ++ } ++ ++ return false; ++} ++ ++static int ++netdev_offload_dpdk_flow_flush(struct netdev *netdev) ++{ ++ flush_netdev_flows_in_related(netdev, netdev); ++ ++ if (!netdev_vport_is_vport_class(netdev->netdev_class)) { ++ netdev_ports_traverse(netdev->dpif_type, flush_in_vport_cb, netdev); ++ } + + return 0; + } +diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c +index 4fcde9ba1..492bfcffb 100644 +--- a/lib/netlink-conntrack.c ++++ b/lib/netlink-conntrack.c +@@ -579,7 +579,8 @@ nl_ct_put_tuple_proto(struct ofpbuf *buf, const struct ct_dpif_tuple *tuple) + nl_msg_put_u8(buf, CTA_PROTO_ICMPV6_TYPE, tuple->icmp_type); + nl_msg_put_u8(buf, CTA_PROTO_ICMPV6_CODE, tuple->icmp_code); + } else if (tuple->ip_proto == IPPROTO_TCP || +- tuple->ip_proto == IPPROTO_UDP) { ++ tuple->ip_proto == IPPROTO_UDP || ++ tuple->ip_proto == IPPROTO_SCTP) { + nl_msg_put_be16(buf, CTA_PROTO_SRC_PORT, tuple->src_port); + nl_msg_put_be16(buf, CTA_PROTO_DST_PORT, tuple->dst_port); + } else { +diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c +index b092e9e04..f7f7b1279 100644 +--- a/ofproto/connmgr.c ++++ b/ofproto/connmgr.c +@@ -1209,7 +1209,7 @@ ofconn_create(struct ofservice *ofservice, struct rconn *rconn, + hmap_init(&ofconn->bundles); + ofconn->next_bundle_expiry_check = time_msec() + BUNDLE_EXPIRY_INTERVAL; + +- ofconn_set_rate_limit(ofconn, settings->rate_limit, settings->burst_limit); ++ ofservice_reconfigure(ofservice, settings); + + ovs_mutex_unlock(&ofproto_mutex); + } +@@ -1915,10 +1915,7 @@ connmgr_count_hidden_rules(const struct connmgr *mgr) + } + + /* Creates a new ofservice for 'target' in 'mgr'. Returns 0 if successful, +- * otherwise a positive errno value. +- * +- * ofservice_reconfigure() must be called to fully configure the new +- * ofservice. */ ++ * otherwise a positive errno value. */ + static void + ofservice_create(struct connmgr *mgr, const char *target, + const struct ofproto_controller *c) +@@ -1928,7 +1925,8 @@ ofservice_create(struct connmgr *mgr, const char *target, + struct rconn *rconn = NULL; + if (!vconn_verify_name(target)) { + char *name = ofconn_make_name(mgr, target); +- rconn = rconn_create(5, 8, c->dscp, c->allowed_versions); ++ rconn = rconn_create(c->probe_interval, c->max_backoff, ++ c->dscp, c->allowed_versions); + rconn_connect(rconn, target, name); + free(name); + } else if (!pvconn_verify_name(target)) { +@@ -1951,7 +1949,6 @@ ofservice_create(struct connmgr *mgr, const char *target, + ofservice->rconn = rconn; + ofservice->pvconn = pvconn; + ofservice->s = *c; +- ofservice_reconfigure(ofservice, c); + + VLOG_INFO("%s: added %s controller \"%s\"", + mgr->name, ofconn_type_to_string(ofservice->type), target); +diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c +index 9224ee2e6..2e1fcb3a6 100644 +--- a/ofproto/ofproto-dpif-xlate-cache.c ++++ b/ofproto/ofproto-dpif-xlate-cache.c +@@ -125,7 +125,7 @@ xlate_push_stats_entry(struct xc_entry *entry, + case XC_LEARN: { + enum ofperr error; + error = ofproto_flow_mod_learn(entry->learn.ofm, true, +- entry->learn.limit, NULL); ++ entry->learn.limit, NULL, stats->used); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "xcache LEARN action execution failed."); +diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c +index 47ea0f47e..be4bd6657 100644 +--- a/ofproto/ofproto-dpif-xlate.c ++++ b/ofproto/ofproto-dpif-xlate.c +@@ -1615,7 +1615,8 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + } + + ofp_port_t in_port = recirc_id_node->state.metadata.in_port; +- if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) { ++ if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER && ++ !uuid_is_zero(&recirc_id_node->state.xport_uuid)) { + struct uuid xport_uuid = recirc_id_node->state.xport_uuid; + xport = xport_lookup_by_uuid(xcfg, &xport_uuid); + if (xport && xport->xbridge && xport->xbridge->ofproto) { +@@ -1626,11 +1627,19 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + * that the packet originated from the controller via an OpenFlow + * "packet-out". The right thing to do is to find just the + * ofproto. There is no xport, which is OK. ++ * Also a zeroed xport_uuid with a valid in_port, means that ++ * the packet originated from OFPP_CONTROLLER passed ++ * through a patch port. + * + * OFPP_NONE can also indicate that a bond caused recirculation. */ + struct uuid uuid = recirc_id_node->state.ofproto_uuid; + const struct xbridge *bridge = xbridge_lookup_by_uuid(xcfg, &uuid); ++ + if (bridge && bridge->ofproto) { ++ if (in_port != OFPP_CONTROLLER && in_port != OFPP_NONE && ++ !get_ofp_port(bridge, in_port)) { ++ goto xport_lookup; ++ } + if (errorp) { + *errorp = NULL; + } +@@ -1643,6 +1652,7 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + } + } + ++xport_lookup: + xport = xport_lookup(xcfg, tnl_port_should_receive(flow) + ? tnl_port_receive(flow) + : odp_port_to_ofport(backer, flow->in_port.odp_port)); +@@ -5700,8 +5710,16 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn) + if (!error) { + bool success = true; + if (ctx->xin->allow_side_effects) { ++ long long int last_used; ++ ++ if (ctx->xin->resubmit_stats) { ++ last_used = ctx->xin->resubmit_stats->used; ++ } else { ++ last_used = time_msec(); ++ } + error = ofproto_flow_mod_learn(ofm, ctx->xin->xcache != NULL, +- learn->limit, &success); ++ learn->limit, &success, ++ last_used); + } else if (learn->limit) { + if (!ofm->temp_rule + || ofm->temp_rule->state != RULE_INSERTED) { +diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c +index e22ca757a..ba5706f6a 100644 +--- a/ofproto/ofproto-dpif.c ++++ b/ofproto/ofproto-dpif.c +@@ -4880,7 +4880,7 @@ packet_xlate(struct ofproto *ofproto_, struct ofproto_packet_out *opo) + if (entry->type == XC_LEARN) { + struct ofproto_flow_mod *ofm = entry->learn.ofm; + +- error = ofproto_flow_mod_learn_refresh(ofm); ++ error = ofproto_flow_mod_learn_refresh(ofm, time_msec()); + if (error) { + goto error_out; + } +diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h +index 143ded690..9f7b8b6e8 100644 +--- a/ofproto/ofproto-provider.h ++++ b/ofproto/ofproto-provider.h +@@ -2027,9 +2027,11 @@ enum ofperr ofproto_flow_mod_init_for_learn(struct ofproto *, + struct ofproto_flow_mod *) + OVS_EXCLUDED(ofproto_mutex); + enum ofperr ofproto_flow_mod_learn(struct ofproto_flow_mod *, bool keep_ref, +- unsigned limit, bool *below_limit) ++ unsigned limit, bool *below_limit, ++ long long int last_used) + OVS_EXCLUDED(ofproto_mutex); +-enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm); ++enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm, ++ long long int last_used); + enum ofperr ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm) + OVS_REQUIRES(ofproto_mutex); + void ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm) +diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c +index dbf4958bc..e78c80d11 100644 +--- a/ofproto/ofproto.c ++++ b/ofproto/ofproto.c +@@ -5472,7 +5472,8 @@ ofproto_flow_mod_init_for_learn(struct ofproto *ofproto, + } + + enum ofperr +-ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) ++ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm, ++ long long int last_used) + { + enum ofperr error = 0; + +@@ -5493,9 +5494,37 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + * this function is executed the rule will be reinstated. */ + if (rule->state == RULE_REMOVED) { + struct cls_rule cr; ++ struct oftable *table = &rule->ofproto->tables[rule->table_id]; ++ ovs_version_t tables_version = rule->ofproto->tables_version; ++ ++ if (!cls_rule_visible_in_version(&rule->cr, tables_version)) { ++ const struct cls_rule *curr_cls_rule; ++ ++ /* Only check for matching classifier rules and their modified ++ * time, instead of also checking all rule metadata, with the goal ++ * of suppressing a learn action update that would replace a more ++ * recent rule in the classifier. */ ++ curr_cls_rule = classifier_find_rule_exactly(&table->cls, ++ &rule->cr, ++ tables_version); ++ if (curr_cls_rule) { ++ struct rule *curr_rule = rule_from_cls_rule(curr_cls_rule); ++ long long int curr_last_used; ++ ++ ovs_mutex_lock(&curr_rule->mutex); ++ curr_last_used = curr_rule->modified; ++ ovs_mutex_unlock(&curr_rule->mutex); ++ ++ if (curr_last_used > last_used) { ++ /* In the case of a newer visible rule, don't recreate the ++ * current rule. */ ++ return 0; ++ } ++ } ++ } + +- cls_rule_clone(&cr, &rule->cr); + ovs_mutex_lock(&rule->mutex); ++ cls_rule_clone(&cr, &rule->cr); + error = ofproto_rule_create(rule->ofproto, &cr, rule->table_id, + rule->flow_cookie, + rule->idle_timeout, +@@ -5506,6 +5535,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + rule->match_tlv_bitmap, + rule->ofpacts_tlv_bitmap, + &ofm->temp_rule); ++ ofm->temp_rule->modified = last_used; + ovs_mutex_unlock(&rule->mutex); + if (!error) { + ofproto_rule_unref(rule); /* Release old reference. */ +@@ -5513,7 +5543,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + } else { + /* Refresh the existing rule. */ + ovs_mutex_lock(&rule->mutex); +- rule->modified = time_msec(); ++ rule->modified = last_used; + ovs_mutex_unlock(&rule->mutex); + } + return error; +@@ -5565,10 +5595,16 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm, + + /* Refresh 'ofm->temp_rule', for which the caller holds a reference, if already + * in the classifier, insert it otherwise. If the rule has already been +- * removed from the classifier, a new rule is created using 'ofm->temp_rule' as +- * a template and the reference to the old 'ofm->temp_rule' is freed. If +- * 'keep_ref' is true, then a reference to the current rule is held, otherwise +- * it is released and 'ofm->temp_rule' is set to NULL. ++ * removed from the classifier and replaced by another rule, the 'last_used' ++ * parameter is used to determine whether the newer rule is replaced or kept. ++ * If 'last_used' is greater than the last modified time of an identical rule ++ * in the classifier, then a new rule is created using 'ofm->temp_rule' as a ++ * template and the reference to the old 'ofm->temp_rule' is freed. If the ++ * rule has been removed but another identical rule doesn't exist in the ++ * classifier, then it will be recreated. If the rule hasn't been removed ++ * from the classifier, then 'last_used' is used to update the rules modified ++ * time. If 'keep_ref' is true, then a reference to the current rule is held, ++ * otherwise it is released and 'ofm->temp_rule' is set to NULL. + * + * If 'limit' != 0, insertion will fail if there are more than 'limit' rules + * in the same table with the same cookie. If insertion succeeds, +@@ -5579,10 +5615,11 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm, + * during the call. */ + enum ofperr + ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref, +- unsigned limit, bool *below_limitp) ++ unsigned limit, bool *below_limitp, ++ long long int last_used) + OVS_EXCLUDED(ofproto_mutex) + { +- enum ofperr error = ofproto_flow_mod_learn_refresh(ofm); ++ enum ofperr error = ofproto_flow_mod_learn_refresh(ofm, last_used); + struct rule *rule = ofm->temp_rule; + bool below_limit = true; + +@@ -5615,6 +5652,11 @@ ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref, + + error = ofproto_flow_mod_learn_start(ofm); + if (!error) { ++ /* ofproto_flow_mod_learn_start may have overwritten ++ * modified with current time. */ ++ ovs_mutex_lock(&ofm->temp_rule->mutex); ++ ofm->temp_rule->modified = last_used; ++ ovs_mutex_unlock(&ofm->temp_rule->mutex); + error = ofproto_flow_mod_learn_finish(ofm, NULL); + } + } else { +diff --git a/ovsdb/condition.c b/ovsdb/condition.c +index 5a3eb4e8a..4911fbf59 100644 +--- a/ovsdb/condition.c ++++ b/ovsdb/condition.c +@@ -550,9 +550,14 @@ ovsdb_condition_diff(struct ovsdb_condition *diff, + &b->clauses[j]); + } + +- diff->optimized = a->optimized && b->optimized; +- if (diff->optimized) { +- ovsdb_condition_optimize(diff); ++ if (diff->n_clauses) { ++ diff->optimized = a->optimized && b->optimized; ++ if (diff->optimized) { ++ ovsdb_condition_optimize(diff); ++ } ++ } else { ++ free(diff->clauses); ++ diff->clauses = NULL; + } + } + +diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c +index 01091fabe..f4a9cf8fe 100644 +--- a/ovsdb/monitor.c ++++ b/ovsdb/monitor.c +@@ -821,6 +821,7 @@ ovsdb_monitor_table_condition_update( + ovsdb_condition_destroy(&mtc->new_condition); + ovsdb_condition_clone(&mtc->new_condition, &cond); + ovsdb_condition_destroy(&cond); ++ ovsdb_condition_destroy(&mtc->diff_condition); + ovsdb_condition_diff(&mtc->diff_condition, + &mtc->old_condition, &mtc->new_condition); + ovsdb_monitor_condition_add_columns(dbmon, +diff --git a/ovsdb/ovsdb-doc b/ovsdb/ovsdb-doc +index 10d0c0c13..099770d25 100755 +--- a/ovsdb/ovsdb-doc ++++ b/ovsdb/ovsdb-doc +@@ -24,7 +24,7 @@ import ovs.json + from ovs.db import error + import ovs.db.schema + +-from build.nroff import * ++from ovs_build_helpers.nroff import * + + argv0 = sys.argv[0] + +diff --git a/python/automake.mk b/python/automake.mk +index 82a508787..84cf2eab5 100644 +--- a/python/automake.mk ++++ b/python/automake.mk +@@ -66,10 +66,10 @@ ovs_pytests = \ + # These python files are used at build time but not runtime, + # so they are not installed. + EXTRA_DIST += \ +- python/build/__init__.py \ +- python/build/extract_ofp_fields.py \ +- python/build/nroff.py \ +- python/build/soutil.py ++ python/ovs_build_helpers/__init__.py \ ++ python/ovs_build_helpers/extract_ofp_fields.py \ ++ python/ovs_build_helpers/nroff.py \ ++ python/ovs_build_helpers/soutil.py + + # PyPI support. + EXTRA_DIST += \ +@@ -88,10 +88,10 @@ PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover) + + FLAKE8_PYFILES += \ + $(filter-out python/ovs/compat/% python/ovs/dirs.py,$(PYFILES)) \ +- python/build/__init__.py \ +- python/build/extract_ofp_fields.py \ +- python/build/nroff.py \ +- python/build/soutil.py \ ++ python/ovs_build_helpers/__init__.py \ ++ python/ovs_build_helpers/extract_ofp_fields.py \ ++ python/ovs_build_helpers/nroff.py \ ++ python/ovs_build_helpers/soutil.py \ + python/ovs/dirs.py.template \ + python/setup.py + +@@ -112,11 +112,14 @@ ovs-install-data-local: + $(INSTALL_DATA) python/ovs/dirs.py.tmp $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py + rm python/ovs/dirs.py.tmp + ++.PHONY: python-sdist + python-sdist: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py +- (cd python/ && $(PYTHON3) setup.py sdist) ++ cd python/ && $(PYTHON3) -m build --sdist ++ ++.PHONY: pypi-upload ++pypi-upload: python-sdist ++ twine upload python/dist/ovs-$(VERSION).tar.gz + +-pypi-upload: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py +- (cd python/ && $(PYTHON3) setup.py sdist upload) + install-data-local: ovs-install-data-local + + UNINSTALL_LOCAL += ovs-uninstall-local +diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py +index 9fc2159b0..16ece0334 100644 +--- a/python/ovs/db/idl.py ++++ b/python/ovs/db/idl.py +@@ -494,6 +494,7 @@ class Idl(object): + if not msg.result[0]: + self.__clear() + self.__parse_update(msg.result[2], OVSDB_UPDATE3) ++ self.last_id = msg.result[1] + elif self.state == self.IDL_S_DATA_MONITOR_COND_REQUESTED: + self.__clear() + self.__parse_update(msg.result, OVSDB_UPDATE2) +diff --git a/python/build/__init__.py b/python/ovs_build_helpers/__init__.py +similarity index 100% +rename from python/build/__init__.py +rename to python/ovs_build_helpers/__init__.py +diff --git a/python/build/extract_ofp_fields.py b/python/ovs_build_helpers/extract_ofp_fields.py +similarity index 100% +rename from python/build/extract_ofp_fields.py +rename to python/ovs_build_helpers/extract_ofp_fields.py +diff --git a/python/build/nroff.py b/python/ovs_build_helpers/nroff.py +similarity index 100% +rename from python/build/nroff.py +rename to python/ovs_build_helpers/nroff.py +diff --git a/python/build/soutil.py b/python/ovs_build_helpers/soutil.py +similarity index 100% +rename from python/build/soutil.py +rename to python/ovs_build_helpers/soutil.py +diff --git a/tests/.gitignore b/tests/.gitignore +index 83b1cb3b4..3a8c45975 100644 +--- a/tests/.gitignore ++++ b/tests/.gitignore +@@ -3,6 +3,7 @@ + /Makefile.in + /atconfig + /atlocal ++/clang-analyzer-results/ + /idltest.c + /idltest.h + /idltest.ovsidl +diff --git a/tests/learn.at b/tests/learn.at +index d127fed34..d0bcc8363 100644 +--- a/tests/learn.at ++++ b/tests/learn.at +@@ -836,3 +836,63 @@ AT_CHECK([ovs-vsctl add-br br1 -- set b br1 datapath_type=dummy]) + + OVS_VSWITCHD_STOP + AT_CLEANUP ++ ++AT_SETUP([learning action - flapping learn rule]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 3 ++ ++AT_CHECK([ovs-appctl time/stop], [0], [ignore]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=1,actions=resubmit(,2)']]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=2,actions=resubmit(,2)']]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=2,actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:OXM_OF_IN_PORT[]),output:3']]) ++ ++packet="eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)" ++ ++dnl Run this test a few times in a loop to reduce the likelyhood that it passes by chance. ++for i in 1 2 3; do ++ AT_CHECK([ovs-appctl revalidator/pause], [0]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ ++ AT_CHECK([ovs-appctl revalidator/resume], [0]) ++ AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++ AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl ++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:1 ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++ ++ AT_CHECK([ovs-appctl revalidator/pause], [0]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ ++ AT_CHECK([ovs-appctl revalidator/resume], [0]) ++ AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++ AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl ++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:2 ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++done ++ ++dnl Wait and check for learned rule eviction due to hard timeout. ++AT_CHECK([ovs-appctl time/warp 3200], [0], [ignore]) ++ ++AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | grep 0x123], [0], [dnl ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP +diff --git a/tests/mfex_fuzzy.py b/tests/mfex_fuzzy.py +index 30028ba7a..50b987064 100755 +--- a/tests/mfex_fuzzy.py ++++ b/tests/mfex_fuzzy.py +@@ -3,12 +3,15 @@ + import sys + import warnings + +-from cryptography.utils import CryptographyDeprecationWarning +-warnings.filterwarnings( +- "ignore", +- category=CryptographyDeprecationWarning, +- message=r"(blowfish|cast5)", +-) ++try: ++ from cryptography.utils import CryptographyDeprecationWarning ++ warnings.filterwarnings( ++ "ignore", ++ category=CryptographyDeprecationWarning, ++ message=r"(blowfish|cast5)", ++ ) ++except ModuleNotFoundError: ++ pass + + # flake8: noqa: E402 + from scapy.all import RandMAC, RandIP, PcapWriter, RandIP6, RandShort, fuzz +diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at +index f242f77f3..a39d0d3ae 100644 +--- a/tests/ofproto-dpif.at ++++ b/tests/ofproto-dpif.at +@@ -5854,6 +5854,40 @@ OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0]) + OVS_VSWITCHD_STOP + AT_CLEANUP + ++dnl Checks for regression against a bug in which OVS dropped packets ++dnl originating from a controller passing through a patch port. ++AT_SETUP([ofproto-dpif - packet-out recirculation OFPP_CONTROLLER and patch port]) ++OVS_VSWITCHD_START( ++ [add-port br0 patch-br1 -- \ ++ set interface patch-br1 type=patch options:peer=patch-br0 -- \ ++ add-br br1 -- set bridge br1 datapath-type=dummy fail-mode=secure -- \ ++ add-port br1 patch-br0 -- set interface patch-br0 type=patch options:peer=patch-br1 ++]) ++ ++add_of_ports --pcap br1 1 ++ ++AT_DATA([flows-br0.txt], [dnl ++table=0 icmp actions=output:patch-br1 ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows-br0.txt]) ++ ++AT_DATA([flows-br1.txt], [dnl ++table=0, icmp actions=ct(table=1,zone=1) ++table=1, ct_state=+trk, icmp actions=p1 ++]) ++AT_CHECK([ovs-ofctl add-flows br1 flows-br1.txt]) ++ ++packet=50540000000750540000000508004500005c000000008001b94dc0a80001c0a80002080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=CONTROLLER packet=$packet actions=table"]) ++ ++OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows -m br1 | grep "ct_state" | ofctl_strip], [dnl ++ table=1, n_packets=1, n_bytes=106, ct_state=+trk,icmp actions=output:2]) ++ ++OVS_WAIT_UNTIL([ovs-pcap p1-tx.pcap | grep -q "$packet"]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_SETUP([ofproto-dpif - debug_slow action]) + OVS_VSWITCHD_START + add_of_ports br0 1 2 3 +diff --git a/tests/ofproto.at b/tests/ofproto.at +index 2fa8486a8..2889f81fb 100644 +--- a/tests/ofproto.at ++++ b/tests/ofproto.at +@@ -6720,3 +6720,31 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(tip=172.31.1 + + OVS_VSWITCHD_STOP + AT_CLEANUP ++ ++AT_SETUP([ofproto - configure inactivity probe interval]) ++ ++# Set 6 second inactivity probe interval (default is 5 seconds). ++OVS_VSWITCHD_START([set-controller br0 unix:testcontroller \ ++ -- set Controller br0 inactivity_probe=6000], [], [], ++ [-vfile:rconn:dbg]) ++ ++# Start test openflow controller. ++AT_CHECK([ovs-testcontroller -vsyslog:off --detach --no-chdir --pidfile punix:testcontroller], ++ [0], [ignore]) ++on_exit 'kill `cat ovs-testcontroller.pid`' ++OVS_WAIT_UNTIL([test -e testcontroller]) ++ ++# After 6 seconds of inactivity there should be a log message. ++OVS_WAIT_UNTIL([grep "idle 6 seconds, sending inactivity probe" ovs-vswitchd.log]) ++ ++# Restart ovs-vswitchd with an empty ovs-vswitchd log file. ++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) ++mv ovs-vswitchd.log ovs-vswitchd_1.log ++AT_CHECK([ovs-vswitchd --enable-dummy --disable-system --disable-system-route --detach \ ++ --no-chdir --pidfile --log-file -vfile:rconn:dbg -vvconn -vofproto_dpif -vunixctl], ++ [0], [], [stderr]) ++ ++# After 6 seconds of inactivity there should be a log message. ++OVS_WAIT_UNTIL([grep "idle 6 seconds, sending inactivity probe" ovs-vswitchd.log]) ++OVS_VSWITCHD_STOP(["/br0<->unix:testcontroller: connection failed/d"]) ++AT_CLEANUP +diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at +index df5a9d2fd..1028b0237 100644 +--- a/tests/ovsdb-idl.at ++++ b/tests/ovsdb-idl.at +@@ -2332,6 +2332,23 @@ CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], + CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], + [ssl6], [[[::1]]]) + ++dnl OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS(LOG) ++dnl ++dnl Looks up transaction IDs in the log of OVSDB client application. ++dnl All-zero UUID should not be sent within a monitor request more than once, ++dnl unless some database requests were lost (not replied). ++m4_define([OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS], ++[ ++ requests=$(grep -c 'send request' $1) ++ replies=$(grep -c 'received reply' $1) ++ ++ if test "$requests" -eq "$replies"; then ++ AT_CHECK([grep 'monitor_cond_since' $1 \ ++ | grep -c "00000000-0000-0000-0000-000000000000" | tr -d '\n'], ++ [0], [1]) ++ fi ++]) ++ + # same as OVSDB_CHECK_IDL but uses Python IDL implementation with tcp + # with multiple remotes to assert the idl connects to the leader of the Raft cluster + m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY], +@@ -2347,10 +2364,11 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY], + pids=$(cat s2.pid s3.pid s1.pid | tr '\n' ',') + echo $pids + AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t30 idl-cluster $srcdir/idltest.ovsschema $remotes $pids $3], +- [0], [stdout], [ignore]) ++ [0], [stdout], [stderr]) + remote=$(ovsdb_cluster_leader $remotes "idltest") + leader=$(echo $remote | cut -d'|' -f 1) + AT_CHECK([grep -F -- "${leader}" stdout], [0], [ignore]) ++ OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) + AT_CLEANUP]) + + OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote']) +@@ -2393,6 +2411,7 @@ m4_define([OVSDB_CHECK_CLUSTER_IDL_C], + AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), + [0], [$5]) + m4_ifval([$8], [AT_CHECK([grep '$8' stderr], [1])], [], []) ++ OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) + AT_CLEANUP]) + + # Same as OVSDB_CHECK_CLUSTER_IDL_C but uses the Python IDL implementation. +@@ -2413,6 +2432,7 @@ m4_define([OVSDB_CHECK_CLUSTER_IDL_PY], + AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), + [0], [$5]) + m4_if([$8], [AT_CHECK([grep '$8' stderr], [1])], [], []) ++ OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) + AT_CLEANUP]) + + m4_define([OVSDB_CHECK_CLUSTER_IDL], +diff --git a/tests/ovsdb-monitor.at b/tests/ovsdb-monitor.at +index 12cd2bc31..3e1df18a1 100644 +--- a/tests/ovsdb-monitor.at ++++ b/tests/ovsdb-monitor.at +@@ -586,6 +586,7 @@ row,action,name,number,_version + [[]], + [], + [[[[["name","==","one"],["name","==","two"]]]], ++ [[[["name","==","two"],["name","==","one"]]]], + [[[["name","==","one"]]]], + [[[false]]], + [[[true]]]]) +diff --git a/tests/pmd.at b/tests/pmd.at +index 7c333a901..7bdaca9e7 100644 +--- a/tests/pmd.at ++++ b/tests/pmd.at +@@ -1355,18 +1355,22 @@ AT_CHECK([echo 'table=0,in_port=p1,ip,nw_dst=10.1.0.0/16 actions=ct(commit)' | d + ovs-ofctl --bundle replace-flows br0 -]) + AT_CHECK([ovs-appctl revalidator/wait]) + ++dnl Prevent flows from expiring. ++AT_CHECK([ovs-appctl time/stop]) ++ + AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.0.2,proto=6),tcp(src=1,dst=2)']) + OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-flows | sed 's/.*core: [[0-9]]*//' | strip_xout_keep_actions], [ + recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.0.2/255.255.0.0,frag=no), packets:0, bytes:0, used:never, actions:ct(commit) + recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.2.2/255.255.255.0,frag=no), packets:0, bytes:0, used:0.0s, actions:ct(commit)]) + +-dnl Hold the prefix 10.1.2.2/24 by another 10s. +-AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.2.2,proto=6),tcp(src=1,dst=2)']) + dnl Send more 10.1.0.2 to make 10.1.0.0/16 tuple prepend 10.1.2.0/24 tuple in the pvector of subtables. + for i in $(seq 0 256); do + AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.0.2,proto=6),tcp(src=1,dst=2)']) + done + ++dnl Warp time enough to trigger subtable optimization. ++AT_CHECK([ovs-appctl time/warp 500 2000], [0], [ignore]) ++ + AT_CHECK([echo 'table=0,in_port=p1,ip,nw_dst=10.1.0.0/16 actions=p2' | dnl + ovs-ofctl --bundle replace-flows br0 -]) + +diff --git a/tests/system-traffic.at b/tests/system-traffic.at +index 808c492a2..418cd32fe 100644 +--- a/tests/system-traffic.at ++++ b/tests/system-traffic.at +@@ -2516,6 +2516,7 @@ AT_CLEANUP + + AT_SETUP([conntrack - ct flush]) + CHECK_CONNTRACK() ++CHECK_CONNTRACK_SCTP() + OVS_TRAFFIC_VSWITCHD_START() + + ADD_NAMESPACES(at_ns0, at_ns1) +@@ -2526,10 +2527,8 @@ ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") + AT_DATA([flows.txt], [dnl + priority=1,action=drop + priority=10,arp,action=normal +-priority=100,in_port=1,udp,action=ct(commit),2 +-priority=100,in_port=2,udp,action=ct(zone=5,commit),1 +-priority=100,in_port=1,icmp,action=ct(commit),2 +-priority=100,in_port=2,icmp,action=ct(zone=5,commit),1 ++priority=100,in_port=1,ip,action=ct(commit),2 ++priority=100,in_port=2,ip,action=ct(zone=5,commit),1 + ]) + + AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) +@@ -2692,6 +2691,25 @@ udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10. + + AT_CHECK([FLUSH_CMD]) + ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) ++ ++dnl Test SCTP flush based on port. ++AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500003400010000408464410a0101010a01010200010002000000009178f7d30100001470e18ccc00000000000a000a00000000 actions=resubmit(,0)"]) ++AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000950540000000a08004500003400010000408464410a0101020a010101000200010000000098f29e470100001470e18ccc00000000000a000a00000000 actions=resubmit(,0)"]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sed "s/,protoinfo=.*$//" | sort], [0], [dnl ++sctp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) ++sctp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 ++]) ++ ++AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.1,ct_nw_proto=132,ct_tp_src=1,ct_tp_dst=2']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sed "s/,protoinfo=.*$//" | sort], [0], [dnl ++sctp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 ++]) ++ ++AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.2,ct_nw_proto=132,ct_tp_src=2,ct_tp_dst=1']) ++ + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) + ]) + +diff --git a/tests/test-barrier.c b/tests/test-barrier.c +index 3bc5291cc..fb0ab0e69 100644 +--- a/tests/test-barrier.c ++++ b/tests/test-barrier.c +@@ -14,13 +14,13 @@ + * limitations under the License. + */ + +-#include +- + #include ++#undef NDEBUG ++#include + +-#include "ovs-thread.h" +-#include "ovs-rcu.h" + #include "ovstest.h" ++#include "ovs-rcu.h" ++#include "ovs-thread.h" + #include "random.h" + #include "util.h" + +diff --git a/tests/test-id-fpool.c b/tests/test-id-fpool.c +index 25275d9ae..27800aa9b 100644 +--- a/tests/test-id-fpool.c ++++ b/tests/test-id-fpool.c +@@ -14,12 +14,12 @@ + * limitations under the License. + */ + ++#include + #undef NDEBUG + #include + #include + #include +- +-#include ++#include + + #include "command-line.h" + #include "id-fpool.h" +diff --git a/tests/test-mpsc-queue.c b/tests/test-mpsc-queue.c +index a38bf9e6d..16aa804a0 100644 +--- a/tests/test-mpsc-queue.c ++++ b/tests/test-mpsc-queue.c +@@ -14,12 +14,12 @@ + * limitations under the License. + */ + ++#include + #undef NDEBUG + #include + #include + #include +- +-#include ++#include + + #include "command-line.h" + #include "guarded-list.h" +diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml +index cfcde34ff..1e2a1267d 100644 +--- a/vswitchd/vswitch.xml ++++ b/vswitchd/vswitch.xml +@@ -3797,14 +3797,29 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ + Interface description string. + + +- +- Vendor ID of PCI device. ++ ++ Bus name and bus info such as Vendor ID and Device ID of PCI ++ device. + + +- +- Device ID of PCI device. ++ ++ Ethernet address set for this VF interface. Only reported for dpdk ++ VF representors. + + ++ ++ Hardware Rx queue steering policy in use. ++ ++ ++ ++ ID of rx steering queue. Only reported if rx-steering ++ is supported by hardware. ++ ++ ++ ++ IDs of rss queues. Only reported if rx-steering is ++ supported by hardware. ++ + + + diff --git a/SPECS/openvswitch3.2.spec b/SPECS/openvswitch3.2.spec index 34063a4..6c58882 100644 --- a/SPECS/openvswitch3.2.spec +++ b/SPECS/openvswitch3.2.spec @@ -57,7 +57,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 3.2.0 -Release: 10%{?dist} +Release: 17%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -93,7 +93,7 @@ Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.ta Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz Source103: https://pypi.io/packages/source/p/pyelftools/pyelftools-%{pyelftoolsver}.tar.gz -%define apply_patch %(test -s openvswitch-%{version}.patch && echo 1 || echo 0) +%define apply_patch %(test -s %{_sourcedir}/openvswitch-%{version}.patch && echo 1 || echo 0) %if %{apply_patch} Patch0: openvswitch-%{version}.patch @@ -761,6 +761,46 @@ exit 0 %endif %changelog +* Wed Oct 11 2023 Timothy Redaelli - 3.2.0-17 +- redhat: use rhpkg push instead of git push [RH git: 5bcbc4c7bc] + + +* Wed Oct 11 2023 Timothy Redaelli - 3.2.0-16 +- redhat: fix applying patches [RH git: 78c86345d1] + + +* Tue Oct 10 2023 Open vSwitch CI - 3.2.0-15 +- Merging upstream branch-3.2 [RH git: a8e307cc01] + Commit list: + 0f7907a761 netdev-dpdk: Document rx-steering status options. + 99fa50391e netdev-dpdk: Update docs for interface info. + af98a39f0b netdev-dpdk: Document status options for VF MAC address. + + +* Mon Oct 09 2023 Open vSwitch CI - 3.2.0-14 +- Merging upstream branch-3.2 [RH git: c3521203af] + Commit list: + 6cab4c6f92 netdev-offload-dpdk: Fix flushing of a physdev. + + +* Thu Oct 05 2023 Open vSwitch CI - 3.2.0-13 +- Merging upstream branch-3.2 [RH git: da3d5bd50b] + Commit list: + a2c01c797b connmgr: Fix ofconn configuration on vswitchd startup. + + +* Mon Sep 25 2023 Open vSwitch CI - 3.2.0-12 +- Merging upstream branch-3.2 [RH git: 0798b67699] + Commit list: + 0d0e95cd2a ovsdb: Fix potential leak when making diff of conditions. + + +* Mon Sep 18 2023 Open vSwitch CI - 3.2.0-11 +- Merging upstream branch-3.2 [RH git: c2edb93a92] + Commit list: + 25f11d058a python: idl: Fix last-id update from a monitor reply. + + * Sat Sep 09 2023 Open vSwitch CI - 3.2.0-10 - Merging upstream branch-3.2 [RH git: b964fa5bf0] Commit list: