diff --git a/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch b/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch
deleted file mode 100644
index d73b70f..0000000
--- a/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From e1cd90a8ac7ede76dabc3714358c32076f9557e7 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Wed, 26 Aug 2020 16:48:26 +0530
-Subject: [PATCH] Fix ovn-controller crash when a lport of type 'virtual' is
- deleted.
-
-The below bt is seen when a lport of type 'virtual' is deleted.
-
-(gdb) bt
-0x00001470c0708655 in __strlen_avx2 () from /lib64/libc.so.6
-0x0000563340037449 in hash_string (basis=0, s=s@entry=0x0) at lib/hash.h:342
-hash_name (name=name@entry=0x0) at lib/shash.c:28
-0x0000563340037a76 in shash_find (sh=0x5633407bb260, name=0x0) at lib/shash.c:231
-0x0000563340037b7d in shash_find_data (sh=<optimized out>, name=<optimized out>) at lib/shash.c:245
-0x000056333ff71151 in local_binding_find (name=<optimized out>, local_bindings=<optimized out>) at controller/binding.h:108
-get_lbinding_for_lport (b_ctx_out=0x7fff616745b0, lport_type=<optimized out>, pb=0x56334314d630) at controller/binding.c:1960
-handle_deleted_vif_lport (b_ctx_in=0x7fff61674600, b_ctx_in=0x7fff61674600, b_ctx_out=0x7fff616745b0, lport_type=<optimized out>, pb=0x56334314d630) at controller/binding.c:1979
-binding_handle_port_binding_changes (b_ctx_in=b_ctx_in@entry=0x7fff61674600, b_ctx_out=b_ctx_out@entry=0x7fff616745b0) at controller/binding.c:2087
-0x000056333ff8e208 in runtime_data_sb_port_binding_handler (node=0x7fff616759f0, data=0x5633407bb240) at controller/ovn-controller.c:1325
-0x000056333ffa6de3 in engine_compute (recompute_allowed=<optimized out>, node=<optimized out>) at lib/inc-proc-eng.c:306
-...
-...
-
-Fixes: 354bdba51ab("ovn-controller: I-P for SB port binding and OVS interface in runtime_data.")
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from master commit 51fea73f0e09a1e670b8b7ca52819963bfa29c7e)
-
-(cherry-picked from upstream branch-20.06 commit edc8b8ffdfd4d0250ea8650b1501e49fb4a82b6f)
-
-Change-Id: I926ee596800a5cc105e481845bd328acda578dd2
----
- controller/binding.c | 12 ++++++++----
- tests/ovn.at         | 11 +++++++++++
- 2 files changed, 19 insertions(+), 4 deletions(-)
-
-diff --git a/controller/binding.c b/controller/binding.c
-index 880fbb13b..3c102dc7f 100644
---- a/controller/binding.c
-+++ b/controller/binding.c
-@@ -1957,11 +1957,15 @@ get_lbinding_for_lport(const struct sbrec_port_binding *pb,
-     struct local_binding *parent_lbinding = NULL;
- 
-     if (lport_type == LP_VIRTUAL) {
--        parent_lbinding = local_binding_find(b_ctx_out->local_bindings,
--                                             pb->virtual_parent);
-+        if (pb->virtual_parent) {
-+            parent_lbinding = local_binding_find(b_ctx_out->local_bindings,
-+                                                 pb->virtual_parent);
-+        }
-     } else {
--        parent_lbinding = local_binding_find(b_ctx_out->local_bindings,
--                                             pb->parent_port);
-+        if (pb->parent_port) {
-+            parent_lbinding = local_binding_find(b_ctx_out->local_bindings,
-+                                                 pb->parent_port);
-+        }
-     }
- 
-     return parent_lbinding
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 0d99adf3f..1216bc50f 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -16115,6 +16115,17 @@ ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02
- ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
- 
- OVN_POPULATE_ARP
-+
-+# Delete sw0-vir and add again.
-+ovn-nbctl lsp-del sw0-vir
-+
-+ovn-nbctl lsp-add sw0 sw0-vir
-+ovn-nbctl lsp-set-addresses sw0-vir "50:54:00:00:00:10 10.0.0.10"
-+ovn-nbctl lsp-set-port-security sw0-vir "50:54:00:00:00:10 10.0.0.10"
-+ovn-nbctl lsp-set-type sw0-vir virtual
-+ovn-nbctl set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10
-+ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2,sw0-p3
-+
- ovn-nbctl --wait=hv sync
- 
- # Check that logical flows are added for sw0-vir in lsp_in_arp_rsp pipeline
--- 
-2.26.2
-
diff --git a/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch b/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch
deleted file mode 100644
index 36246f3..0000000
--- a/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From fe0be84c42c1b304bfbe49e59aa11eea100e16b1 Mon Sep 17 00:00:00 2001
-From: Dhathri Purohith <dhathri.purohith@nutanix.com>
-Date: Thu, 11 Jun 2020 13:44:40 -0700
-Subject: [PATCH 01/22] Fix the data type for DHCP option tftp_server (66)
-
-Currently, DHCP option is of type ipv4. But, according to RFC 2132,
-option 66 can be a hostname i.e, we should also accept string type.
-In order to be backward compatible, a new type called "host_id" is
-introduced, which accepts both ipv4 address and string. Type for DHCP
-option 66 is changed to "host_id" instead of ipv4.
-OVN northd code that updates the OVN southbound database is enhanced to
-consider the change in the type and code for DHCP option, so that the
-change in datatype is reflected.
-
-Signed-off-by: Dhathri Purohith <dhathri.purohith@nutanix.com>
-Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit b06319993debbeb6d116901afa511d627543c10d)
-
-Change-Id: I45b4d783bfc8849a69d7c8c8584429c2740e668c
----
- lib/actions.c       | 12 ++++++++
- lib/ovn-l7.h        |  2 +-
- northd/ovn-northd.c |  7 ++++-
- ovn-nb.xml          | 18 ++++++++----
- ovn-sb.ovsschema    |  7 +++--
- ovn-sb.xml          | 13 +++++++++
- tests/ovn.at        | 68 +++++++++++++++++++++++++++++++++++++++++++++
- tests/test-ovn.c    |  2 +-
- 8 files changed, 117 insertions(+), 12 deletions(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index 6d0d687b3..616c93e8a 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2083,6 +2083,10 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
-         return;
-     }
- 
-+    if (!strcmp(o->option->type, "host_id")) {
-+        return;
-+    }
-+
-     if (!strcmp(o->option->type, "str")) {
-         if (o->value.type != EXPR_C_STRING) {
-             lexer_error(ctx->lexer, "%s option %s requires string value.",
-@@ -2410,6 +2414,14 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
-     } else if (!strcmp(o->option->type, "str")) {
-         opt_header[1] = strlen(c->string);
-         ofpbuf_put(ofpacts, c->string, opt_header[1]);
-+    } else if (!strcmp(o->option->type, "host_id")) {
-+        if (o->value.type == EXPR_C_STRING) {
-+            opt_header[1] = strlen(c->string);
-+            ofpbuf_put(ofpacts, c->string, opt_header[1]);
-+        } else {
-+           opt_header[1] = sizeof(ovs_be32);
-+           ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
-+        }
-     }
- }
- 
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index d5c6feaeb..22a2153de 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -57,7 +57,7 @@ struct gen_opts_map {
- #define DHCP_OPT_NIS_SERVER  DHCP_OPTION("nis_server", 41, "ipv4")
- #define DHCP_OPT_NTP_SERVER  DHCP_OPTION("ntp_server", 42, "ipv4")
- #define DHCP_OPT_SERVER_ID   DHCP_OPTION("server_id", 54, "ipv4")
--#define DHCP_OPT_TFTP_SERVER DHCP_OPTION("tftp_server", 66, "ipv4")
-+#define DHCP_OPT_TFTP_SERVER DHCP_OPTION("tftp_server", 66, "host_id")
- 
- #define DHCP_OPT_CLASSLESS_STATIC_ROUTE \
-     DHCP_OPTION("classless_static_route", 121, "static_routes")
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 6858bf8fd..14be87435 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -11785,7 +11785,12 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
-         struct gen_opts_map *dhcp_opt =
-             dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
-         if (dhcp_opt) {
--            hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
-+            if (!strcmp(dhcp_opt->type, opt_row->type) &&
-+                 dhcp_opt->code == opt_row->code) {
-+                hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
-+            } else {
-+                sbrec_dhcp_options_delete(opt_row);
-+            }
-         } else {
-             sbrec_dhcp_options_delete(opt_row);
-         }
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index e947c440d..8d04d3d3b 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -2830,12 +2830,6 @@
-           </p>
-         </column>
- 
--        <column name="options" key="tftp_server">
--          <p>
--            The DHCPv4 option code for this option is 66.
--          </p>
--        </column>
--
-         <column name="options" key="classless_static_route">
-           <p>
-             The DHCPv4 option code for this option is 121.
-@@ -2984,6 +2978,18 @@
-           </p>
-         </column>
-       </group>
-+
-+      <group title="DHCP Options of type host_id">
-+        <p>
-+          These options accept either an IPv4 address or a string value.
-+        </p>
-+
-+        <column name="options" key="tftp_server">
-+          <p>
-+            The DHCPv4 option code for this option is 66.
-+          </p>
-+        </column>
-+      </group>
-     </group>
- 
-     <group title="DHCPv6 options">
-diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
-index c196ddaf3..2ec729b77 100644
---- a/ovn-sb.ovsschema
-+++ b/ovn-sb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Southbound",
--    "version": "2.8.0",
--    "cksum": "1643994484 21853",
-+    "version": "2.8.1",
-+    "cksum": "236203406 21905",
-     "tables": {
-         "SB_Global": {
-             "columns": {
-@@ -217,7 +217,8 @@
-                     "type": {"key": {
-                         "type": "string",
-                         "enum": ["set", ["bool", "uint8", "uint16", "uint32",
--                                         "ipv4", "static_routes", "str"]]}}}},
-+                                         "ipv4", "static_routes", "str",
-+                                         "host_id"]]}}}},
-             "isRoot": true},
-         "DHCPv6_Options": {
-             "columns": {
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 0641e4942..2edafd48f 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -3274,6 +3274,19 @@ tcp.flags = RST;
-             Example. "name=host_name", "code=12", "type=str".
-           </p>
-         </dd>
-+
-+        <dt><code>value: host_id</code></dt>
-+        <dd>
-+          <p>
-+            This indicates that the value of the DHCP option is a host_id.
-+            It can either be a host_name or an IP address.
-+          </p>
-+
-+          <p>
-+            Example. "name=tftp_server", "code=66", "type=host_id".
-+          </p>
-+        </dd>
-+
-       </dl>
-     </column>
-   </table>
-diff --git a/tests/ovn.at b/tests/ovn.at
-index f6adbb7a3..4e98790af 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1249,6 +1249,12 @@ reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,m
- reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5},arp_cache_timeout=10,tcp_keepalive_interval=10);
-     formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5}, arp_cache_timeout = 10, tcp_keepalive_interval = 10);
-     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05.23.04.00.00.00.0a.26.04.00.00.00.0a,pause)
-+reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server=10.0.0.10);
-+    formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = 10.0.0.10);
-+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.04.0a.00.00.0a,pause)
-+reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server="tftp_server_test");
-+    formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = "tftp_server_test");
-+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause)
- 
- reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
-     Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
-@@ -5624,6 +5630,68 @@ AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
- cat 1.expected | cut -c 53- > expout
- AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
- 
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Set tftp server option (IPv4 address) for ls1
-+echo "------ Set tftp server (IPv4 address) --------"
-+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \
-+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \
-+tftp_server=10.10.10.10
-+echo "----------------------------------------------"
-+
-+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP
-+# address in the Requested IP Address option.
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=`ip_to_hex 0 0 0 0`
-+request_ip=$offer_ip
-+expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 10.
-+OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Set tftp server option (Hostname) for ls1
-+echo "------ Set tftp server (hostname) --------"
-+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \
-+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \
-+tftp_server=\"test_tftp_server\"
-+echo "------------------------------------------"
-+
-+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP
-+# address in the Requested IP Address option.
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=`ip_to_hex 0 0 0 0`
-+request_ip=$offer_ip
-+expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 11.
-+OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
- OVN_CLEANUP([hv1])
- 
- AT_CLEANUP
-diff --git a/tests/test-ovn.c b/tests/test-ovn.c
-index 8f1bb7e01..29d343b60 100644
---- a/tests/test-ovn.c
-+++ b/tests/test-ovn.c
-@@ -175,7 +175,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
-     dhcp_opt_add(dhcp_opts, "nis_server", 41, "ipv4");
-     dhcp_opt_add(dhcp_opts, "ntp_server", 42, "ipv4");
-     dhcp_opt_add(dhcp_opts, "server_id",  54, "ipv4");
--    dhcp_opt_add(dhcp_opts, "tftp_server", 66, "ipv4");
-+    dhcp_opt_add(dhcp_opts, "tftp_server", 66, "host_id");
-     dhcp_opt_add(dhcp_opts, "classless_static_route", 121, "static_routes");
-     dhcp_opt_add(dhcp_opts, "ip_forward_enable",  19, "bool");
-     dhcp_opt_add(dhcp_opts, "router_discovery", 31, "bool");
--- 
-2.26.2
-
diff --git a/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch b/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch
deleted file mode 100644
index a4614fa..0000000
--- a/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 22ae217041eee8b7d655cc489797ac88432495d6 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Mon, 24 Aug 2020 16:42:48 +0530
-Subject: [PATCH 1/2] I-P engine: Provide the option to store client data in
- engine ctx.
-
-There can be some client specific data which could change from one engine run
-to another. Adding a 'void *' data in 'struct engine_ctx' will be useful.
-One such usecase is to provide a config option in ovn-controller to turn on or
-off logical flow expr caching. And this config knob can be stored in the engine_ctx.
-An upcoming patch will make use of this.
-
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- lib/inc-proc-eng.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
-index e25bcb29c..80de75029 100644
---- a/lib/inc-proc-eng.h
-+++ b/lib/inc-proc-eng.h
-@@ -66,6 +66,7 @@
- struct engine_context {
-     struct ovsdb_idl_txn *ovs_idl_txn;
-     struct ovsdb_idl_txn *ovnsb_idl_txn;
-+    void *client_ctx;
- };
- 
- /* Arguments to be passed to the engine at engine_init(). */
--- 
-2.26.2
-
diff --git a/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch b/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch
deleted file mode 100644
index 8607655..0000000
--- a/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch
+++ /dev/null
@@ -1,74 +0,0 @@
-From 9ad199184959deac21b3dbf0efbbb3d23f6baed1 Mon Sep 17 00:00:00 2001
-Message-Id: <9ad199184959deac21b3dbf0efbbb3d23f6baed1.1599568829.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 26 Aug 2020 12:15:46 +0200
-Subject: [PATCH] Introduce DHCPDECLINE msg support to OVN DHCP server
-
-According to the RFC2131 (https://tools.ietf.org/html/rfc2131), if the
-server server receives a DHCPDECLINE message, the client has discovered
-through some other means that the suggested network address is already
-in use. The server SHOULD notify the local system administrator of a
-possible configuration problem.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c |  6 ++++++
- lib/ovn-l7.h         |  1 +
- tests/ovn.at         | 13 +++++++++++++
- 3 files changed, 20 insertions(+)
-
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -1889,6 +1889,12 @@ pinctrl_handle_put_dhcp_opts(
- 
-         break;
-     }
-+    case OVN_DHCP_MSG_DECLINE:
-+        if (request_ip == *offer_ip) {
-+            VLOG_INFO("DHCPDECLINE from "ETH_ADDR_FMT ", "IP_FMT" duplicated",
-+                      ETH_ADDR_ARGS(in_flow->dl_src), IP_ARGS(*offer_ip));
-+        }
-+        goto exit;
-     default: {
-         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-         VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -180,6 +180,7 @@ struct dhcp_opt6_header {
- };
- 
- /* These are not defined in ovs/lib/dhcp.h, hence defining here. */
-+#define OVN_DHCP_MSG_DECLINE        4
- #define OVN_DHCP_MSG_RELEASE        7
- #define OVN_DHCP_MSG_INFORM         8
- 
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -5332,6 +5332,10 @@ test_dhcp() {
-         reply_dst_ip=${offer_ip}
-     fi
- 
-+    if test "$dhcp_type" == "04"; then
-+        ciaddr=$offer_ip
-+    fi
-+
-     local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip}
-     # udp header and dhcp header
-     request=${request}00440043${udp_len}0000
-@@ -5897,6 +5901,15 @@ AT_CHECK([cat 2.packets | cut -c -48], [
- cat 2.expected | cut -c 53- > expout
- AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
- 
-+# test DHCPDECLINE
-+offer_ip=`ip_to_hex 10 0 0 4`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=`ip_to_hex 0 0 0 0`
-+request_ip=0
-+expected_dhcp_opts=""
-+test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
-+AT_CHECK([fgrep -iq 'DHCPDECLINE from f0:00:00:00:00:01, 10.0.0.4 duplicated' hv1/ovn-controller.log], [0], [])
-+
- OVN_CLEANUP([hv1])
- 
- AT_CLEANUP
diff --git a/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch b/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch
index 8cb059a..9f9f93d 100644
--- a/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch
+++ b/SOURCES/0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch
@@ -1,7 +1,7 @@
-From 7d3fe4b24896304bc1d832f95a425fa62c48f7f8 Mon Sep 17 00:00:00 2001
+From e8922fefa62184cdd0ff808f4e52de74e8d9bbe5 Mon Sep 17 00:00:00 2001
 From: Dumitru Ceara <dceara@redhat.com>
 Date: Wed, 25 Mar 2020 21:15:23 +0100
-Subject: [PATCH] Revert "ovsdb-idl: Avoid sending redundant conditional
+Subject: [PATCH 1/4] Revert "ovsdb-idl: Avoid sending redundant conditional
  monitoring updates"
 
 This reverts commit 5351980b047f4dd40be7a59a1e4b910df21eca0a.
@@ -43,7 +43,7 @@ Change-Id: Iaa24bc949d648e8fa29abea1fe8fb5878ba45864
  1 file changed, 2 deletions(-)
 
 diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
-index 190143f..1535ad7 100644
+index 190143f36..1535ad7b5 100644
 --- a/openvswitch-2.13.0/lib/ovsdb-idl.c
 +++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
 @@ -610,7 +610,6 @@ ovsdb_idl_db_clear(struct ovsdb_idl_db *db)
@@ -63,5 +63,5 @@ index 190143f..1535ad7 100644
      ovsdb_idl_db_track_clear(db);
  
 -- 
-1.8.3.1
+2.26.2
 
diff --git a/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch b/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch
deleted file mode 100644
index ee4a429..0000000
--- a/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From 536d6aa32497ab17e12767446f294fc8467cfc7c Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 3 Sep 2020 17:03:45 +0200
-Subject: [PATCH 1/2] chassis: Fix the way encaps are updated for a chassis
- record.
-
-ovn-controller always stores the last configured system-id/chassis-id in
-memory regardless if the connection to the SB is up or down. This is OK
-as long as the change can be committed successfully when the SB DB
-connection comes back up.
-
-Without this change, if the chassis-id changes while the SB connection is
-down, ovn-controller will fail to create the new record but nevertheless
-update its in-memory chassis-id. When the SB connection is restored
-ovn-controller tries to find the record corresponding to the chassis-id
-it stored in memory. This fails causing ovn-controller to try to insert
-a new record. But at this point a constraint violation is hit in the SB
-because the Encap records of the "stale" chassis still exist in the DB,
-along with the old chassis record.
-
-This commit changes the way we search for a "stale" chassis record in the
-SB to cover the above mentioned case. Also, in such cases there's no need
-to recreate the Encaps, it's sufficient to update the chassis_name field.
-
-Fixes: 5344f24ecb1a ("ovn-controller: Refactor chassis.c to abstract the string parsing")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from master commit 94a32fca2d2b825fece0ef5b1873459bd9857dd3)
-
-(cherry picked from upstream commit 0716c4f4cf2d97f03c1f2e5099fece92f3183d43)
-
-Change-Id: I20f0c61fddffba599b16f43d15b110414a6a6e6b
----
- controller/chassis.c    | 60 +++++++++++++++++++++++++++++++------------------
- tests/ovn-controller.at | 17 ++++++++++++++
- 2 files changed, 55 insertions(+), 22 deletions(-)
-
-diff --git a/controller/chassis.c b/controller/chassis.c
-index 6ac591e..773d966 100644
---- a/controller/chassis.c
-+++ b/controller/chassis.c
-@@ -397,10 +397,7 @@ chassis_tunnels_changed(const struct sset *encap_type_set,
- {
-     size_t encap_type_count = 0;
- 
--    for (int i = 0; i < chassis_rec->n_encaps; i++) {
--        if (strcmp(chassis_rec->name, chassis_rec->encaps[i]->chassis_name)) {
--            return true;
--        }
-+    for (size_t i = 0; i < chassis_rec->n_encaps; i++) {
- 
-         if (!sset_contains(encap_type_set, chassis_rec->encaps[i]->type)) {
-             return true;
-@@ -473,6 +470,19 @@ chassis_build_encaps(struct ovsdb_idl_txn *ovnsb_idl_txn,
- }
- 
- /*
-+ * Updates encaps for a given chassis. This can happen when the chassis
-+ * name has changed. Also, the only thing we support updating is the
-+ * chassis_name. For other changes the encaps will be recreated.
-+ */
-+static void
-+chassis_update_encaps(const struct sbrec_chassis *chassis)
-+{
-+    for (size_t i = 0; i < chassis->n_encaps; i++) {
-+        sbrec_encap_set_chassis_name(chassis->encaps[i], chassis->name);
-+    }
-+}
-+
-+/*
-  * Returns a pointer to a chassis record from 'chassis_table' that
-  * matches at least one tunnel config.
-  */
-@@ -503,9 +513,10 @@ chassis_get_stale_record(const struct sbrec_chassis_table *chassis_table,
- /* If this is a chassis config update after we initialized the record once
-  * then we should always be able to find it with the ID we saved in
-  * chassis_state.
-- * Otherwise (i.e., first time we create the record) then we check if there's
-- * a stale record from a previous controller run that didn't end gracefully
-- * and reuse it. If not then we create a new record.
-+ * Otherwise (i.e., first time we create the record or if the system-id
-+ * changed) then we check if there's a stale record from a previous
-+ * controller run that didn't end gracefully and reuse it. If not then we
-+ * create a new record.
-  *
-  * Sets '*chassis_rec' to point to the local chassis record.
-  * Returns true if this record was already in the database, false if it was
-@@ -519,28 +530,32 @@ chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                    const char *chassis_id,
-                    const struct sbrec_chassis **chassis_rec)
- {
-+    const struct sbrec_chassis *chassis = NULL;
-+
-     if (chassis_info_id_inited(&chassis_state)) {
--        *chassis_rec = chassis_lookup_by_name(sbrec_chassis_by_name,
--                                              chassis_info_id(&chassis_state));
--        if (!(*chassis_rec)) {
--            VLOG_DBG("Could not find Chassis, will create it"
--                     ": stored (%s) ovs (%s)",
-+        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
-+                                         chassis_info_id(&chassis_state));
-+        if (!chassis) {
-+            VLOG_DBG("Could not find Chassis, will check if the id changed: "
-+                     "stored (%s) ovs (%s)",
-                      chassis_info_id(&chassis_state), chassis_id);
--            if (ovnsb_idl_txn) {
--                /* Recreate the chassis record.  */
--                *chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
--                return false;
--            }
-         }
--    } else {
--        *chassis_rec =
--            chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id);
-+    }
- 
--        if (!(*chassis_rec) && ovnsb_idl_txn) {
-+    if (!chassis) {
-+        chassis = chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id);
-+    }
-+
-+    if (!chassis) {
-+        /* Recreate the chassis record. */
-+        VLOG_DBG("Could not find Chassis, will create it: %s", chassis_id);
-+        if (ovnsb_idl_txn) {
-             *chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
--            return false;
-         }
-+        return false;
-     }
-+
-+    *chassis_rec = chassis;
-     return true;
- }
- 
-@@ -602,6 +617,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
-                                 &ovs_cfg->encap_ip_set, ovs_cfg->encap_csum,
-                                 chassis_rec);
-     if (!tunnels_changed) {
-+        chassis_update_encaps(chassis_rec);
-         return updated;
-     }
- 
-diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
-index 1b96934..f2faf1f 100644
---- a/tests/ovn-controller.at
-+++ b/tests/ovn-controller.at
-@@ -196,6 +196,23 @@ OVS_WAIT_UNTIL([
-     test "${sysid}" = "${chassis_id}"
- ])
- 
-+# Simulate system-id changing while ovn-controller is disconnected from the
-+# SB.
-+valid_remote=$(ovs-vsctl get Open_vSwitch . external_ids:ovn-remote)
-+invalid_remote=tcp:0.0.0.0:4242
-+ovs-vsctl set Open_vSwitch . external_ids:ovn-remote=${invalid_remote}
-+expected_state="not connected"
-+OVS_WAIT_UNTIL([
-+    test "${expected_state}" = "$(ovn-appctl -t ovn-controller connection-status)"
-+])
-+sysid=${sysid}-bar
-+ovs-vsctl set Open_vSwitch . external-ids:system-id="${sysid}"
-+ovs-vsctl set Open_vSwitch . external_ids:ovn-remote=${valid_remote}
-+OVS_WAIT_UNTIL([
-+    chassis_id=$(ovn-sbctl get Chassis "${sysid}" name)
-+    test "${sysid}" = "${chassis_id}"
-+])
-+
- # Gracefully terminate daemons
- OVN_CLEANUP_SBOX([hv])
- OVN_CLEANUP_VSWITCH([main])
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch b/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch
deleted file mode 100644
index e1d5078..0000000
--- a/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From 714a097ba82ad53b90cfff921ea3749cd1130f3e Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 23 Jun 2020 10:17:50 +0200
-Subject: [PATCH] lex: Allow unmasked bits in value/mask tokens.
-
-It's quite restrictive to not accept ACLs/policies that match on a CIDR
-that has non-zero host bits. Right now this generates a lexer error that
-can only be detected in the logs.
-
-There's no real harm in automatically zero-ing the unmasked bits.
-
-Reported-at: https://bugzilla.redhat.com/1812820
-Reported-by: Ying Xu <yinxu@redhat.com>
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 2104f67aacd62f62a31f4e23a6720aeeaa751154)
-
-Change-Id: I90c57fe51170d63fcd08d1a57d6d9555755a43be
----
- lib/lex.c    | 10 ++--------
- tests/ovn.at | 11 +++++++----
- 2 files changed, 9 insertions(+), 12 deletions(-)
-
-diff --git a/lib/lex.c b/lib/lex.c
-index 94f6c77..4d92199 100644
---- a/lib/lex.c
-+++ b/lib/lex.c
-@@ -485,16 +485,10 @@ lex_parse_mask(const char *p, struct lex_token *token)
-         return p;
-     }
- 
--    /* Check invariant that a 1-bit in the value corresponds to a 1-bit in the
-+    /* Apply invariant that a 1-bit in the value corresponds to a 1-bit in the
-      * mask. */
-     for (int i = 0; i < ARRAY_SIZE(token->mask.be32); i++) {
--        ovs_be32 v = token->value.be32[i];
--        ovs_be32 m = token->mask.be32[i];
--
--        if (v & ~m) {
--            lex_error(token, "Value contains unmasked 1-bits.");
--            break;
--        }
-+        token->value.be32[i] &= token->mask.be32[i];
-     }
- 
-     /* Done! */
-diff --git a/tests/ovn.at b/tests/ovn.at
-index cf521af..e7e0439 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -79,7 +79,7 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b
- 
- 0/0
- 0/1
--1/0 => error("Value contains unmasked 1-bits.")
-+1/0 => 0/0
- 1/1
- 128/384
- 1/3
-@@ -99,7 +99,7 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b
- 0X => error("Hex digits expected following 0X.")
- 0x0/0x0 => 0/0
- 0x0/0x1 => 0/0x1
--0x1/0x0 => error("Value contains unmasked 1-bits.")
-+0x1/0x0 => 0/0
- 0xffff/0x1ffff
- 0x. => error("Invalid syntax in hexadecimal constant.")
- 
-@@ -109,9 +109,12 @@ a/b => a error("`/' is only valid as part of `//' or `/*'.") b
- 192.168.0.0/255.255.0.0 => 192.168.0.0/16
- 192.168.0.0/255.255.255.0 => 192.168.0.0/24
- 192.168.0.0/255.255.0.255
--192.168.0.0/255.0.0.0 => error("Value contains unmasked 1-bits.")
-+192.168.0.0/255.0.0.0 => 192.0.0.0/8
- 192.168.0.0/32
- 192.168.0.0/255.255.255.255 => 192.168.0.0/32
-+192.168.0.2/32
-+192.168.0.2/30 => 192.168.0.0/30
-+192.168.0.2/24 => 192.168.0.0/24
- 1.2.3.4:5 => 1.2.3.4 : 5
- 
- ::
-@@ -135,7 +138,7 @@ FE:DC:ba:98:76:54 => fe:dc:ba:98:76:54
- 01:00:00:00:00:00/01:00:00:00:00:00
- ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff
- fe:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff
--ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => error("Value contains unmasked 1-bits.")
-+ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => fe:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff
- fe:x => error("Invalid numeric constant.")
- 00:01:02:03:04:x => error("Invalid numeric constant.")
- 
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch b/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch
deleted file mode 100644
index 51d2497..0000000
--- a/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From 940fff5c75ccc1ef0d66f37ce167f89f8ae6d098 Mon Sep 17 00:00:00 2001
-Message-Id: <940fff5c75ccc1ef0d66f37ce167f89f8ae6d098.1599499391.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Fri, 4 Sep 2020 15:44:49 +0200
-Subject: [PATCH] northd: fix empty_lb_backends controller_event for IPv6
-
-Introduce missing square brackets defining IPv6 empty_lb_backends
-controller_event logical flows in order to properly extract vip ip
-and port from the related string
-
-Fixes: bb9f2b9ce56c ("ovn-northd: Consider load balancer active backends in router pipeline")
-Fixes: 821e1e54abcb ("OVN: use trigger_event action to report 'empty_lb_rule' events")
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.c |  7 ++++---
- tests/ovn.at        | 30 +++++++++++++++++++++++++++---
- 2 files changed, 31 insertions(+), 6 deletions(-)
-
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4995,6 +4995,7 @@ build_empty_lb_event_flow(struct ovn_dat
-         return;
-     }
- 
-+    bool ipv4 = (lb_vip->addr_family == AF_INET);
-     struct ds match = DS_EMPTY_INITIALIZER;
-     char *meter = "", *action;
- 
-@@ -5003,14 +5004,14 @@ build_empty_lb_event_flow(struct ovn_dat
-     }
- 
-     ds_put_format(&match, "ip%s.dst == %s && %s",
--                  lb_vip->addr_family == AF_INET ? "4": "6",
--                  lb_vip->vip, lb->protocol);
-+                  ipv4 ? "4": "6", lb_vip->vip, lb->protocol);
- 
-     char *vip = lb_vip->vip;
-     if (lb_vip->vip_port) {
-         ds_put_format(&match, " && %s.dst == %u", lb->protocol,
-                       lb_vip->vip_port);
--        vip = xasprintf("%s:%u", lb_vip->vip, lb_vip->vip_port);
-+        vip = xasprintf("%s%s%s:%u", ipv4 ? "" : "[", lb_vip->vip,
-+                        ipv4 ? "" : "]", lb_vip->vip_port);
-     }
- 
-     action = xasprintf("trigger_event(event = \"%s\", "
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -16404,7 +16404,7 @@ ovn-nbctl create Logical_Router name=lr0
- for i in 0 1; do
-     idx=$((i+1))
-     ovn-nbctl ls-add sw$i
--    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24
-+    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24 200$idx::a/64
-     ovn-nbctl \
-         -- lsp-add sw$i lrp$i-attachment \
-         -- set Logical_Switch_Port lrp$i-attachment type=router \
-@@ -16420,7 +16420,7 @@ for i in 1 2; do
- 
-     for j in 1 2; do
-         ovn-nbctl lsp-add sw0 sw0-p$i$j -- \
--                lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j"
-+                lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j 2001::$i$j"
- 
-         ovs-vsctl -- add-port br-int vif$i$j -- \
-                 set interface vif$i$j \
-@@ -16433,7 +16433,7 @@ done
- 
- as hv1
- ovn-nbctl lsp-add sw1 sw1-p0 \
--    -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11"
-+    -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11 2002::1"
- ovs-vsctl -- add-port br-int vif33 -- \
-         set interface vif33 \
-         external-ids:iface-id=sw1-p0 \
-@@ -16449,6 +16449,11 @@ uuid_lb0=$(ovn-nbctl --bare --columns=_u
- ovn-nbctl lb-add lb1 192.168.2.100:80 ""
- ovn-nbctl lr-lb-add lr0 lb1
- uuid_lb1=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb1)
-+
-+ovn-nbctl lb-add lb2 [[2001::10]]:50051 ""
-+ovn-nbctl ls-lb-add sw0 lb2
-+uuid_lb2=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb2)
-+
- ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10
- 
- OVN_POPULATE_ARP
-@@ -16493,6 +16498,25 @@ empty_lb_backends
- AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
- "192.168.2.100:80"
- ])
-+ovn-sbctl destroy controller_event $uuid
-+
-+packet2="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 &&
-+         ip6 && ip.ttl==64 && ip6.src==2001::11 && ip6.dst==2001::10 &&
-+         tcp && tcp.src==10000 && tcp.dst==50051"
-+
-+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet2"
-+ovn-sbctl list controller_event
-+uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}')
-+
-+AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl
-+empty_lb_backends
-+])
-+AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
-+"[[2001::10]]:50051"
-+])
-+AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl
-+"$uuid_lb2"
-+])
- 
- OVN_CLEANUP([hv1], [hv2])
- AT_CLEANUP
diff --git a/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch b/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch
deleted file mode 100644
index e525ef5..0000000
--- a/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch
+++ /dev/null
@@ -1,207 +0,0 @@
-From 499546979fcf98c8423fb18263261005f747b228 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Mon, 31 Aug 2020 14:14:52 +0200
-Subject: [PATCH] ovn-controller: Fix incremental processing of Port_Binding
- deletes.
-
-If a Port_Binding is deleted from the Southbound DB and the
-corresponding OVS interface is also deleted from the OVS DB, and
-if both updates are received and processed by ovn-controller in
-the same iteration, ovn-controller should process port_binding
-delete operations first.
-
-This commit also adds three new unixctl debug commands for
-ovn-controller:
-- debug/pause: pause ovn-controller processing, except unixctl commands.
-- debug/resume: resume ovn-controller processing.
-- debug/status: return the status of the ovn-controller processing.
-
-These new commands are needed by the test for this scenario as without
-them we have no way of ensuring predictable results. Users should not
-use these commands in production. This is also why the commands are not
-documented.
-
-CC: Numan Siddique <numans@ovn.org>
-Fixes: 6b0f01116bab ("ovn-controller: Handle runtime data changes in flow output engine")
-Reported-by: Tim Rozet <trozet@redhat.com>
-Reported-at: https://bugzilla.redhat.com/1871961
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit d2de07a627721ef443d8929fe04eb888c5086e98)
-
-Change-Id: I889235d31cf9b7fd6a9dde6e0903038c87106b13
----
- controller/ovn-controller.c | 71 ++++++++++++++++++++++++++++++++++++++++++---
- tests/ovn.at                | 38 ++++++++++++++++++++++++
- 2 files changed, 105 insertions(+), 4 deletions(-)
-
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 933acf6..28ca7a8 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -73,6 +73,9 @@ static unixctl_cb_func extend_table_list;
- static unixctl_cb_func inject_pkt;
- static unixctl_cb_func engine_recompute_cmd;
- static unixctl_cb_func cluster_state_reset_cmd;
-+static unixctl_cb_func debug_pause_execution;
-+static unixctl_cb_func debug_resume_execution;
-+static unixctl_cb_func debug_status_execution;
- 
- #define DEFAULT_BRIDGE_NAME "br-int"
- #define DEFAULT_PROBE_INTERVAL_MSEC 5000
-@@ -2253,10 +2256,6 @@ main(int argc, char *argv[])
- 
-     engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
-     engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL);
--    engine_add_input(&en_runtime_data, &en_ovs_port,
--                     engine_noop_handler);
--    engine_add_input(&en_runtime_data, &en_ovs_interface,
--                     runtime_data_ovs_interface_handler);
-     engine_add_input(&en_runtime_data, &en_ovs_qos, NULL);
- 
-     engine_add_input(&en_runtime_data, &en_sb_chassis, NULL);
-@@ -2265,6 +2264,15 @@ main(int argc, char *argv[])
-     engine_add_input(&en_runtime_data, &en_sb_port_binding,
-                      runtime_data_sb_port_binding_handler);
- 
-+    /* The OVS interface handler for runtime_data changes MUST be executed
-+     * after the sb_port_binding_handler as port_binding deletes must be
-+     * processed first.
-+     */
-+    engine_add_input(&en_runtime_data, &en_ovs_port,
-+                     engine_noop_handler);
-+    engine_add_input(&en_runtime_data, &en_ovs_interface,
-+                     runtime_data_ovs_interface_handler);
-+
-     struct engine_arg engine_arg = {
-         .sb_idl = ovnsb_idl_loop.idl,
-         .ovs_idl = ovs_idl_loop.idl,
-@@ -2319,6 +2327,14 @@ main(int argc, char *argv[])
-                              cluster_state_reset_cmd,
-                              &reset_ovnsb_idl_min_index);
- 
-+    bool paused = false;
-+    unixctl_command_register("debug/pause", "", 0, 0, debug_pause_execution,
-+                             &paused);
-+    unixctl_command_register("debug/resume", "", 0, 0, debug_resume_execution,
-+                             &paused);
-+    unixctl_command_register("debug/status", "", 0, 0, debug_status_execution,
-+                             &paused);
-+
-     unsigned int ovs_cond_seqno = UINT_MAX;
-     unsigned int ovnsb_cond_seqno = UINT_MAX;
- 
-@@ -2327,6 +2343,15 @@ main(int argc, char *argv[])
-     restart = false;
-     bool sb_monitor_all = false;
-     while (!exiting) {
-+        /* If we're paused just run the unixctl server and skip most of the
-+         * processing loop.
-+         */
-+        if (paused) {
-+            unixctl_server_run(unixctl);
-+            unixctl_server_wait(unixctl);
-+            goto loop_done;
-+        }
-+
-         engine_init_run();
- 
-         struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop);
-@@ -2581,6 +2606,8 @@ main(int argc, char *argv[])
- 
-         ovsdb_idl_track_clear(ovnsb_idl_loop.idl);
-         ovsdb_idl_track_clear(ovs_idl_loop.idl);
-+
-+loop_done:
-         poll_block();
-         if (should_service_stop()) {
-             exiting = true;
-@@ -2834,3 +2861,39 @@ cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
-     poll_immediate_wake();
-     unixctl_command_reply(conn, NULL);
- }
-+
-+static void
-+debug_pause_execution(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+                      const char *argv[] OVS_UNUSED, void *paused_)
-+{
-+    bool *paused = paused_;
-+
-+    VLOG_INFO("User triggered execution pause.");
-+    *paused = true;
-+    unixctl_command_reply(conn, NULL);
-+}
-+
-+static void
-+debug_resume_execution(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+                       const char *argv[] OVS_UNUSED, void *paused_)
-+{
-+    bool *paused = paused_;
-+
-+    VLOG_INFO("User triggered execution resume.");
-+    *paused = false;
-+    poll_immediate_wake();
-+    unixctl_command_reply(conn, NULL);
-+}
-+
-+static void
-+debug_status_execution(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+                       const char *argv[] OVS_UNUSED, void *paused_)
-+{
-+    bool *paused = paused_;
-+
-+    if (*paused) {
-+        unixctl_command_reply(conn, "paused");
-+    } else {
-+        unixctl_command_reply(conn, "running");
-+    }
-+}
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 1216bc5..cf521af 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -21361,3 +21361,41 @@ AT_CHECK([ovn-sbctl find mac ip=10.0.0.2 mac='"00:00:00:00:03:02"' logical_port=
- OVN_CLEANUP([hv1],[hv2])
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- Delete Port_Binding and OVS port Incremental Processing])
-+ovn_start
-+
-+net_add n1
-+sim_add hv1
-+as hv1
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.10
-+
-+ovn-nbctl ls-add ls
-+ovn-nbctl lsp-add ls lsp
-+
-+as hv1 ovs-vsctl \
-+    -- add-port br-int vif1 \
-+    -- set Interface vif1 external_ids:iface-id=lsp
-+
-+# Wait for port to be bound.
-+OVS_WAIT_UNTIL([test $(ovn-sbctl --columns _uuid --bare list chassis hv1 | wc -l) -eq 1])
-+ch=$(ovn-sbctl --columns _uuid --bare list chassis hv1)
-+OVS_WAIT_UNTIL([test $(ovn-sbctl --columns chassis --bare list port_binding lsp | grep $ch -c) -eq 1])
-+
-+# Pause ovn-controller.
-+as hv1 ovn-appctl -t ovn-controller debug/pause
-+
-+# Delete port binding and OVS port. The updates will be processed in the same
-+# loop in ovn-controller when it resumes.
-+ovn-nbctl --wait=sb lsp-del lsp
-+as hv1 ovs-vsctl del-port vif1
-+
-+# Resume ovn-controller.
-+as hv1 ovn-appctl -t ovn-controller debug/resume
-+
-+# Make sure ovn-controller runs fine.
-+OVS_WAIT_UNTIL([test x$(as hv1 ovn-appctl -t ovn-controller debug/status) = "xrunning"])
-+
-+OVN_CLEANUP([hv1])
-+AT_CLEANUP
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch b/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch
deleted file mode 100644
index c2fe3b1..0000000
--- a/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From 1060d922a0fee3c9795eb58d5799b235ae406bc6 Mon Sep 17 00:00:00 2001
-Message-Id: <1060d922a0fee3c9795eb58d5799b235ae406bc6.1599568836.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Fri, 21 Aug 2020 15:40:13 +0200
-Subject: [PATCH] ovn-ctl: introduce ovsdb-{n, s}b-wrapper options
-
-ovn-ctl has the following options to run ovn-northd, ovn-controller or
-ovn-ic under strace or valgrind wrappers.
-
-  --ovn-northd-wrapper
-  --ovn-controller-wrapper
-  --ovn-ic-wrapper
-
-Introduce --ovsdb-nb-wrapper and --ovsdb-sb-wrapper to do the same for
-ovsdb processes for ovn-{nb,sb} dbs
-
-Tested-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- utilities/ovn-ctl       | 18 ++++++++++-----
- utilities/ovn-ctl.8.xml |  2 ++
- utilities/ovn-lib.in    | 49 ++++++++++++++++++++++++-----------------
- 3 files changed, 43 insertions(+), 26 deletions(-)
-
---- a/utilities/ovn-ctl
-+++ b/utilities/ovn-ctl
-@@ -145,7 +145,7 @@ promote_ic_sb() {
- }
- 
- start_ovsdb__() {
--    local DB=$1 db=$2 schema_name=$3 table_name=$4
-+    local DB=$1 db=$2 schema_name=$3 table_name=$4 wrapper=$5
-     local db_pid_file
-     local cluster_local_addr
-     local cluster_local_port
-@@ -288,7 +288,7 @@ $cluster_remote_port
-         set "$@" --sync-from=`cat $active_conf_file`
-     fi
- 
--    "$@" "$file"
-+    start_wrapped_daemon "$wrapper" ovsdb-$db "" "$@" "$file"
- 
-     # Initialize the database if it's NOT joining a cluster.
-     if test -z "$cluster_remote_addr"; then
-@@ -301,7 +301,7 @@ $cluster_remote_port
- }
- 
- start_nb_ovsdb() {
--    start_ovsdb__ NB nb OVN_Northbound NB_Global
-+    start_ovsdb__ NB nb OVN_Northbound NB_Global "$OVSDB_NB_WRAPPER"
- }
- 
- start_sb_ovsdb() {
-@@ -313,7 +313,7 @@ start_sb_ovsdb() {
-         ulimit -n $MAXFD
-     fi
- 
--    start_ovsdb__ SB sb OVN_Southbound SB_Global
-+    start_ovsdb__ SB sb OVN_Southbound SB_Global "$OVSDB_SB_WRAPPER"
- }
- 
- start_ovsdb () {
-@@ -322,11 +322,13 @@ start_ovsdb () {
- }
- 
- start_ic_nb_ovsdb() {
--    start_ovsdb__ IC_NB ic_nb OVN_IC_Northbound IC_NB_Global
-+    start_ovsdb__ IC_NB ic_nb OVN_IC_Northbound IC_NB_Global \
-+                  "$OVSDB_NB_WRAPPER"
- }
- 
- start_ic_sb_ovsdb() {
--    start_ovsdb__ IC_SB ic_sb OVN_IC_Southbound IC_SB_Global
-+    start_ovsdb__ IC_SB ic_sb OVN_IC_Southbound IC_SB_Global \
-+                  "$OVSDB_SB_WRAPPER"
- }
- 
- start_ic_ovsdb () {
-@@ -692,6 +694,8 @@ set_defaults () {
-     OVN_IC_WRAPPER=
-     OVN_CONTROLLER_PRIORITY=-10
-     OVN_CONTROLLER_WRAPPER=
-+    OVSDB_NB_WRAPPER=
-+    OVSDB_SB_WRAPPER=
- 
-     OVN_USER=
- 
-@@ -908,6 +912,8 @@ Options:
-   --ovn-ic-sb-db-ssl-ca-cert=CERT OVN IC Southbound DB SSL CA certificate file
-   --ovn-user="user[:group]"      pass the --user flag to the ovn daemons
-   --ovs-user="user[:group]"      pass the --user flag to ovs daemons
-+  --ovsdb-nb-wrapper=WRAPPER     run with a wrapper like valgrind for debugging
-+  --ovsdb-sb-wrapper=WRAPPER     run with a wrapper like valgrind for debugging
-   -h, --help                     display this help message
- 
- File location options:
---- a/utilities/ovn-ctl.8.xml
-+++ b/utilities/ovn-ctl.8.xml
-@@ -64,6 +64,8 @@
-     <p><code>--ovn-controller-wrapper=<var>WRAPPER</var></code></p>
-     <p><code>--ovn-ic-priority=<var>NICE</var></code></p>
-     <p><code>--ovn-ic-wrapper=<var>WRAPPER</var></code></p>
-+    <p><code>--ovsdb-nb-wrapper=<var>WRAPPER</var></code></p>
-+    <p><code>--ovsdb-sb-wrapper=<var>WRAPPER</var></code></p>
-     <p><code>--ovn-user=<var>USER:GROUP</var></code></p>
-     <p><code>--ovs-user=<var>USER:GROUP</var></code></p>
-     <p><code>-h</code> | <code>--help</code></p>
---- a/utilities/ovn-lib.in
-+++ b/utilities/ovn-lib.in
-@@ -59,27 +59,12 @@ ovn_install_dir () {
-     fi
- }
- 
--start_ovn_daemon () {
--    priority=$1
--    wrapper=$2
--    shift; shift
--    daemon=$1
-+start_wrapped_daemon() {
-+    wrapper=$1
-+    daemon=$2
-+    priority=$3
-     strace=""
--
--    # drop core files in a sensible place
--    ovn_install_dir "$DAEMON_CWD"
--    set "$@" --no-chdir
--    cd "$DAEMON_CWD"
--
--    # log file
--    ovn_install_dir "$ovn_logdir" "750"
--    set "$@" --log-file="$ovn_logdir/$daemon.log"
--
--    # pidfile and monitoring
--    ovn_install_dir "$ovn_rundir"
--    set "$@" --pidfile="$ovn_rundir/$daemon.pid"
--    set "$@" --detach
--    test X"$MONITOR" = Xno || set "$@" --monitor
-+    shift ; shift ; shift ;
- 
-     # wrapper
-     case $wrapper in
-@@ -127,6 +112,30 @@ start_ovn_daemon () {
-     fi
- }
- 
-+start_ovn_daemon () {
-+    priority=$1
-+    wrapper=$2
-+    shift; shift
-+    daemon=$1
-+
-+    # drop core files in a sensible place
-+    ovn_install_dir "$DAEMON_CWD"
-+    set "$@" --no-chdir
-+    cd "$DAEMON_CWD"
-+
-+    # log file
-+    ovn_install_dir "$ovn_logdir" "750"
-+    set "$@" --log-file="$ovn_logdir/$daemon.log"
-+
-+    # pidfile and monitoring
-+    ovn_install_dir "$ovn_rundir"
-+    set "$@" --pidfile="$ovn_rundir/$daemon.pid"
-+    set "$@" --detach
-+    test X"$MONITOR" = Xno || set "$@" --monitor
-+
-+    start_wrapped_daemon "$wrapper" $daemon "$priority" "$@"
-+}
-+
- stop_ovn_daemon () {
-     if test -e "$ovn_rundir/$1.pid"; then
-         if pid=`cat "$ovn_rundir/$1.pid"`; then
diff --git a/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch b/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch
deleted file mode 100644
index c1a1dae..0000000
--- a/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 9cb2f23877fec13693f27986e4d075152f318d0f Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Fri, 28 Aug 2020 14:10:54 +0200
-Subject: [PATCH] ovn-nbctl: Deal with nb_cfg overflows.
-
-Reported-at: https://bugzilla.redhat.com/id=1873455
-Reported-by: Ying Xu <yinxu@redhat.com>
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from master commit be3a60f8e6a3a01e08ae6fe760279dc4f274562a)
-
-(cherry picked from upstream commit 49f322806785cf1195dfa8cfb63531cd1c119e5b)
-
-Change-Id: Iba7df83c1d8b88a70fff3c69b985ef46db8d18c4
----
- utilities/ovn-nbctl.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index e6d8dbe..bd66ee6 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -6080,6 +6080,11 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
-         nb = nbrec_nb_global_insert(txn);
-     }
- 
-+    /* Deal with potential overflows. */
-+    if (nb->nb_cfg == LLONG_MAX) {
-+        nbrec_nb_global_set_nb_cfg(nb, 0);
-+    }
-+
-     if (wait_type != NBCTL_WAIT_NONE) {
-         ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg,
-                                 force_wait);
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch b/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch
new file mode 100644
index 0000000..76d59a0
--- /dev/null
+++ b/SOURCES/0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch
@@ -0,0 +1,157 @@
+From d811e1027f74de0f1eee1af9af8dd3338eadb61d Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Fri, 25 Sep 2020 13:21:58 +0200
+Subject: [PATCH] ovn-nbctl: add --may-exist/--if-exists options for policy
+ routing
+
+Introduce the following options to avoid error reporting for policy
+routing:
+1) --may-exist: the lr-policy-add does not result in an error if a policy
+   with the same priority and match string is already present
+2) --if-exists: the lr-policy-del does not result in an error if a policy
+   with the specified uuid is not present in the db
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+---
+ tests/ovn-nbctl.at        |  7 ++++++-
+ utilities/ovn-nbctl.8.xml | 20 +++++++++++++++-----
+ utilities/ovn-nbctl.c     | 16 ++++++++++------
+ 3 files changed, 31 insertions(+), 12 deletions(-)
+
+diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
+index baf7a87f5..3dbedc843 100644
+--- a/tests/ovn-nbctl.at
++++ b/tests/ovn-nbctl.at
+@@ -1651,6 +1651,8 @@ AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], [1], []
+   [ovn-nbctl: Same routing policy already existed on the logical router lr0.
+ ])
+ 
++AT_CHECK([ovn-nbctl --may-exist lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop])
++
+ dnl Add duplicated policy
+ AT_CHECK([ovn-nbctl lr-policy-add lr0 103 "ip4.src == 1.1.1.0/24" deny], [1], [],
+   [ovn-nbctl: deny: action must be one of "allow", "drop", and "reroute"
+@@ -1675,10 +1677,13 @@ Routing Policies
+ 
+ 
+ dnl Delete policy by specified uuid
+-AT_CHECK([ovn-nbctl lr-policy-del lr0 $(ovn-nbctl --bare --column _uuid list logical_router_policy)])
++uuid=$(ovn-nbctl --bare --column _uuid list logical_router_policy)
++AT_CHECK([ovn-nbctl lr-policy-del lr0 $uuid])
+ AT_CHECK([ovn-nbctl list logical-router-policy], [0], [dnl
+ ])
+ 
++AT_CHECK([ovn-nbctl --if-exists lr-policy-del lr0 $uuid])
++
+ dnl Add policy with reroute action
+ AT_CHECK([ovn-nbctl lr-policy-add lr0 102 "ip4.src == 3.1.2.0/24" reroute 3.3.3.3])
+ 
+diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
+index fcc4312dd..59302296b 100644
+--- a/utilities/ovn-nbctl.8.xml
++++ b/utilities/ovn-nbctl.8.xml
+@@ -737,8 +737,9 @@
+     <h1>Logical Router Policy Commands</h1>
+ 
+     <dl>
+-      <dt><code>lr-policy-add</code> <var>router</var> <var>priority</var>
+-          <var>match</var> <var>action</var> [<var>nexthop</var>]
++      <dt>[<code>--may-exist</code>]<code>lr-policy-add</code>
++          <var>router</var> <var>priority</var> <var>match</var>
++          <var>action</var> [<var>nexthop</var>]
+           [<var>options key=value]</var>] </dt>
+       <dd>
+         <p>
+@@ -754,6 +755,13 @@
+           The supported option is : <code>pkt_mark</code>.
+         </p>
+ 
++        <p>
++          If <code>--may-exist</code> is specified, adding a duplicated
++          routing policy with the same priority and match string is not
++          really created. Without <code>--may-exist</code>, adding a
++          duplicated routing policy results in error.
++        </p>
++
+           <p>
+           The following example shows a policy to lr1, which will drop packets
+           from<code>192.168.100.0/24</code>.
+@@ -771,8 +779,8 @@
+           </p>
+       </dd>
+ 
+-      <dt><code>lr-policy-del</code> <var>router</var> [<var>{priority | uuid}
+-          [match]</var>]</dt>
++      <dt>[<code>--if-exists</code>] <code>lr-policy-del</code>
++          <var>router</var> [<var>{priority | uuid} [match]</var>]</dt>
+       <dd>
+         <p>
+           Deletes polices from <var>router</var>. If only <var>router</var>
+@@ -784,7 +792,9 @@
+ 
+         <p>
+           If <var>router</var> and <var>uuid</var> are supplied, then the
+-          policy with sepcified uuid is deleted.
++          policy with sepcified uuid is deleted. It is an error if
++          <var>uuid</var> does not exist, unless <code>--if-exists</code>
++          is specified.
+         </p>
+       </dd>
+ 
+diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
+index c54e63937..caf99dfeb 100644
+--- a/utilities/ovn-nbctl.c
++++ b/utilities/ovn-nbctl.c
+@@ -3648,12 +3648,15 @@ nbctl_lr_policy_add(struct ctl_context *ctx)
+ 
+     /* Check if same routing policy already exists.
+      * A policy is uniquely identified by priority and match */
++    bool may_exist = !!shash_find(&ctx->options, "--may-exist");
+     for (int i = 0; i < lr->n_policies; i++) {
+         const struct nbrec_logical_router_policy *policy = lr->policies[i];
+         if (policy->priority == priority &&
+             !strcmp(policy->match, ctx->argv[3])) {
+-            ctl_error(ctx, "Same routing policy already existed on the "
+-                      "logical router %s.", ctx->argv[1]);
++            if (!may_exist) {
++                ctl_error(ctx, "Same routing policy already existed on the "
++                          "logical router %s.", ctx->argv[1]);
++            }
+             return;
+         }
+     }
+@@ -3733,7 +3736,6 @@ nbctl_lr_policy_del(struct ctl_context *ctx)
+             ctx->error = error;
+             return;
+         }
+-
+     }
+     /* If uuid was specified, delete routing policy with the
+      * specified uuid. */
+@@ -3751,7 +3753,9 @@ nbctl_lr_policy_del(struct ctl_context *ctx)
+                 }
+             }
+             if (n_policies == lr->n_policies) {
+-                ctl_error(ctx, "Logical router policy uuid is not found.");
++                if (!shash_find(&ctx->options, "--if-exists")) {
++                    ctl_error(ctx, "Logical router policy uuid is not found.");
++                }
+                 return;
+             }
+ 
+@@ -6529,9 +6533,9 @@ static const struct ctl_command_syntax nbctl_commands[] = {
+     /* Policy commands */
+     { "lr-policy-add", 4, INT_MAX,
+      "ROUTER PRIORITY MATCH ACTION [NEXTHOP] [OPTIONS - KEY=VALUE ...]",
+-     NULL, nbctl_lr_policy_add, NULL, "", RW },
++     NULL, nbctl_lr_policy_add, NULL, "--may-exist", RW },
+     { "lr-policy-del", 1, 3, "ROUTER [{PRIORITY | UUID} [MATCH]]", NULL,
+-        nbctl_lr_policy_del, NULL, "", RW },
++        nbctl_lr_policy_del, NULL, "--if-exists", RW },
+     { "lr-policy-list", 1, 1, "ROUTER", NULL, nbctl_lr_policy_list, NULL,
+        "", RO },
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch b/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch
new file mode 100644
index 0000000..28fe93c
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch
@@ -0,0 +1,183 @@
+From 89502a3b33b1bf407debe5edc6578b2a72afdd5b Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 8 Oct 2020 15:48:07 +0200
+Subject: [PATCH] ovn-northd: Add localnet ports to Multicast_Groups created by
+ IGMP_Group.
+
+In case a logical switch is a "bridged logical switch", i.e., it has a
+localnet port, add the localnet port to all multicast groups generated
+from IGMP_Groups.  This is needed because multicast packets are never
+tunneled between hypervisors.
+
+Note: There still exists an issue regarding IP Multicast Relay (routing)
+when traffic is received on bridged logical switches connected to
+logical routers with mcast_relay=true.  That issue is not addressed
+yet but an item is added to TODO.rst to track it.
+
+Reported-at: https://bugzilla.redhat.com/1886103
+Fixes: 5d1527b11e94 ("ovn-northd: Add IGMP Relay support")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from master commit 97778ab3e422ac071faa67f9f477fd54977e9c04)
+
+(cherry picked from upstream commit a2563f623f2c88f78134928a4f40bdd06cc89b02)
+
+Change-Id: I3f251a19317bfb8a3334357604605ccbb054a0f9
+---
+ TODO.rst            |  8 ++++++++
+ northd/ovn-northd.c |  7 +++++++
+ tests/ovn.at        | 40 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 55 insertions(+)
+
+diff --git a/TODO.rst b/TODO.rst
+index 4321630..c158155 100644
+--- a/TODO.rst
++++ b/TODO.rst
+@@ -152,3 +152,11 @@ OVN To-do List
+   <Logical_Datapath.tunnel_key_NB-Port_Group.name>. This causes an additional
+   hashtable lookup in parse_port_group() which can be avoided when we are sure
+   that the Southbound DB uses the new format.
++
++* IP Multicast Relay
++
++  * When connecting bridged logical switches (localnet) to logical routers
++    with IP Multicast Relay enabled packets might get duplicated. We need
++    to find a way of determining if routing has already been executed (on a
++    different hypervisor) for the IP multicast packet being processed locally
++    in the router pipeline.
+diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
+index 3a00c8a..c7ca0f8 100644
+--- a/northd/ovn-northd.c
++++ b/northd/ovn-northd.c
+@@ -4095,6 +4095,13 @@ ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group,
+         ovn_igmp_group_destroy_entry(entry);
+         free(entry);
+     }
++
++    if (igmp_group->datapath->n_localnet_ports) {
++        ovn_multicast_add_ports(mcast_groups, igmp_group->datapath,
++                                &igmp_group->mcgroup,
++                                igmp_group->datapath->localnet_ports,
++                                igmp_group->datapath->n_localnet_ports);
++    }
+ }
+ 
+ static void
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 7769b69..4c6adf2 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16650,6 +16650,7 @@ ovn_start
+ #   - subnet 30.0.0.0/8
+ #   - 1 port bound on hv1 (sw3-p1)
+ #   - 1 port bound on hv2 (sw3-p2)
++#   - 1 localnet port (sw3-ln)
+ 
+ reset_pcap_file() {
+     local iface=$1
+@@ -16776,6 +16777,9 @@ ovn-nbctl lsp-add sw2 sw2-p1
+ ovn-nbctl lsp-add sw2 sw2-p2
+ ovn-nbctl lsp-add sw3 sw3-p1
+ ovn-nbctl lsp-add sw3 sw3-p2
++ovn-nbctl lsp-add sw3 sw3-ln                    \
++    -- lsp-set-type sw3-ln localnet             \
++    -- lsp-set-options sw3-ln network_name=phys
+ 
+ ovn-nbctl lr-add rtr
+ ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10.0.0.254/24
+@@ -16820,6 +16824,7 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \
+     options:tx_pcap=hv1/vif4-tx.pcap \
+     options:rxq_pcap=hv1/vif4-rx.pcap \
+     ofport-request=1
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ 
+ sim_add hv2
+ as hv2
+@@ -16845,6 +16850,7 @@ ovs-vsctl -- add-port br-int hv2-vif4 -- \
+     options:tx_pcap=hv2/vif4-tx.pcap \
+     options:rxq_pcap=hv2/vif4-rx.pcap \
+     ofport-request=1
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ 
+ OVN_POPULATE_ARP
+ 
+@@ -17128,6 +17134,18 @@ store_ip_multicast_pkt \
+     $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
+     e518e518000a3b3a0000 expected_switched
+ 
++# TODO: IGMP Relay duplicates IP multicast packets in some conditions, for
++# more details see TODO.rst, section "IP Multicast Relay". Once that issue is
++# fixed the duplicated packets should not appear anymore.
++store_ip_multicast_pkt \
++    000000000100 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw1
++store_ip_multicast_pkt \
++    000000000200 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw2
++
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1])
+ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2])
+ OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched])
+@@ -17275,6 +17293,7 @@ ovn_start
+ #   - subnet 30::/64
+ #   - 1 port bound on hv1 (sw3-p1)
+ #   - 1 port bound on hv2 (sw3-p2)
++#   - 1 localnet port (sw3-ln)
+ 
+ reset_pcap_file() {
+     local iface=$1
+@@ -17400,6 +17419,9 @@ ovn-nbctl lsp-add sw2 sw2-p1
+ ovn-nbctl lsp-add sw2 sw2-p2
+ ovn-nbctl lsp-add sw3 sw3-p1
+ ovn-nbctl lsp-add sw3 sw3-p2
++ovn-nbctl lsp-add sw3 sw3-ln                    \
++    -- lsp-set-type sw3-ln localnet             \
++    -- lsp-set-options sw3-ln network_name=phys
+ 
+ ovn-nbctl lr-add rtr
+ ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10::fe/64
+@@ -17459,6 +17481,7 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \
+     options:tx_pcap=hv1/vif4-tx.pcap \
+     options:rxq_pcap=hv1/vif4-rx.pcap \
+     ofport-request=1
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ 
+ sim_add hv2
+ as hv2
+@@ -17484,6 +17507,7 @@ ovs-vsctl -- add-port br-int hv2-vif4 -- \
+     options:tx_pcap=hv2/vif4-tx.pcap \
+     options:rxq_pcap=hv2/vif4-rx.pcap \
+     ofport-request=1
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ 
+ OVN_POPULATE_ARP
+ 
+@@ -17794,6 +17818,22 @@ store_ip_multicast_pkt \
+     93407a69000e1b5e61736461640a \
+     expected_switched
+ 
++# TODO: MLD Relay duplicates IP multicast packets in some conditions, for
++# more details see TODO.rst, section "IP Multicast Relay". Once that issue is
++# fixed the duplicated packets should not appear anymore.
++store_ip_multicast_pkt \
++    000000000100 333300000001 \
++    10000000000000000000000000000042 ff0adeadbeef00000000000000000001 \
++    000e 01 11 \
++    93407a69000e1b5e61736461640a \
++    expected_routed_sw1
++store_ip_multicast_pkt \
++    000000000200 333300000001 \
++    10000000000000000000000000000042 ff0adeadbeef00000000000000000001 \
++    000e 01 11 \
++    93407a69000e1b5e61736461640a \
++    expected_routed_sw2
++
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1])
+ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2])
+ OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched])
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch b/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch
deleted file mode 100644
index f370ad9..0000000
--- a/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch
+++ /dev/null
@@ -1,393 +0,0 @@
-From eec06481841c053a00f04849a305b66b2d274f2a Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Sat, 12 Sep 2020 18:25:53 +0530
-Subject: [PATCH] ovn-northd: Fix multiple ARP replies for SNAT entries
- configured on a distributed router.
-
-The commit in the Fixes tag, while addressing the issue to send ARP replies for
-the SNAT entries, didn't take into account the gateway router port scenario.
-Because of this, all the chassis which have bridge mappings configured reply
-to ARP request for SNAT entries. This patch fixes the issue by adding
-"is_chassis_resident()" condition for such flows so that only the gateway chassis
-which claims the gateway router port responds to the ARP request.
-
-Note: This patch doesn't require any changes to ovn-northd.8.xml as it was already
-documented with the desired flows for SNAT entries.
-
-Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-September/050679.html
-Reported-by: Chris <kklimonda@syntaxhighlighted.com>
-Fixes: e2aa124ff7c2("ovn-northd: Add ARP responder flows for SNAT entries.")
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- TODO.rst            |   4 +
- northd/ovn-northd.c |  14 ++-
- tests/ovn-northd.at |   5 +
- tests/ovn.at        | 260 ++++++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 279 insertions(+), 4 deletions(-)
-
-diff --git a/TODO.rst b/TODO.rst
-index 98b86cef8..53ca2057e 100644
---- a/TODO.rst
-+++ b/TODO.rst
-@@ -25,6 +25,10 @@
- OVN To-do List
- ==============
- 
-+* Refactor ovn-northd code to have separate functions to add logical flows
-+  for gateway logical routers and logical routers with distributed gateway
-+  port.
-+
- * Get incremental updates in ovn-controller and ovn-northd in some
-   sensible way.
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 63c58ca6a..e5e524ec3 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -8678,10 +8678,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                              ext_addrs->ipv4_addrs[0].addr_s;
- 
-             if (!strcmp(nat->type, "snat")) {
--                if (sset_contains(&snat_ips, ext_addr)) {
-+                if (!sset_add(&snat_ips, ext_addr)) {
-                     continue;
-                 }
--                sset_add(&snat_ips, ext_addr);
-             }
- 
-             /* Priority 91 and 92 flows are added for each gateway router
-@@ -9063,6 +9062,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             continue;
-         }
- 
-+        struct sset sset_snat_ips = SSET_INITIALIZER(&sset_snat_ips);
-         for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
-             struct ovn_nat *nat_entry = &op->od->nat_entries[i];
-             const struct nbrec_nat *nat = nat_entry->nb;
-@@ -9072,8 +9072,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                 continue;
-             }
- 
-+            struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
-+            char *ext_addr = (nat_entry_is_v6(nat_entry)
-+                              ? ext_addrs->ipv6_addrs[0].addr_s
-+                              : ext_addrs->ipv4_addrs[0].addr_s);
-             if (!strcmp(nat->type, "snat")) {
--                continue;
-+                if (!sset_add(&sset_snat_ips, ext_addr)) {
-+                    continue;
-+                }
-             }
- 
-             /* Mac address to use when replying to ARP/NS. */
-@@ -9115,7 +9121,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             /* Respond to ARP/NS requests on the chassis that binds the gw
-              * port. Drop the ARP/NS requests on other chassis.
-              */
--            struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
-             if (nat_entry_is_v6(nat_entry)) {
-                 build_lrouter_nd_flow(op->od, op, "nd_na",
-                                       ext_addrs->ipv6_addrs[0].addr_s,
-@@ -9138,6 +9143,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                                        &nat->header_, lflows);
-             }
-         }
-+        sset_destroy(&sset_snat_ips);
-     }
- 
-     /* DHCPv6 reply handling */
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 86eee3542..70ae8b92b 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1754,6 +1754,8 @@ action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100
- # Priority 91 drop flows (per distributed gw port), if port is not resident.
- AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=91" | grep "arp\|nd" | sort], [0], [dnl
-   table=3 (lr_in_ip_input     ), priority=91   , dnl
-+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.150), action=(drop;)
-+  table=3 (lr_in_ip_input     ), priority=91   , dnl
- match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl
- action=(drop;)
-   table=3 (lr_in_ip_input     ), priority=91   , dnl
-@@ -1767,6 +1769,9 @@ action=(drop;)
- # Priority 92 ARP/NS responders (per distributed gw port), if port is resident.
- AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=92" | grep "arp\|nd" | sort], [0], [dnl
-   table=3 (lr_in_ip_input     ), priority=92   , dnl
-+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.150 && is_chassis_resident("cr-lrp-public")), dnl
-+action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.150; outport = inport; flags.loopback = 1; output;)
-+  table=3 (lr_in_ip_input     ), priority=92   , dnl
- match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl
- action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;)
-   table=3 (lr_in_ip_input     ), priority=92   , dnl
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 2bef87b40..7cc1756e1 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -21439,3 +21439,263 @@ OVS_WAIT_UNTIL([test x$(as hv1 ovn-appctl -t ovn-controller debug/status) = "xru
- 
- OVN_CLEANUP([hv1])
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- ARP replies for SNAT external ips])
-+ovn_start
-+
-+net_add n1
-+
-+sim_add hv1
-+as hv1
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+ovs-vsctl -- add-port br-int hv1-vif1 -- \
-+    set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
-+    options:tx_pcap=hv1/vif1-tx.pcap \
-+    options:rxq_pcap=hv1/vif1-rx.pcap \
-+    ofport-request=1
-+ovs-vsctl -- add-port br-int hv1-vif2 -- \
-+    set interface hv1-vif2 external-ids:iface-id=sw3-port1 \
-+    options:tx_pcap=hv1/vif2-tx.pcap \
-+    options:rxq_pcap=hv1/vif2-rx.pcap \
-+    ofport-request=2
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
-+
-+sim_add hv2
-+as hv2
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.2
-+ovs-vsctl -- add-port br-int hv2-vif1 -- \
-+    set interface hv2-vif1 external-ids:iface-id=sw0-port2 \
-+    options:tx_pcap=hv2/vif1-tx.pcap \
-+    options:rxq_pcap=hv2/vif1-rx.pcap \
-+    ofport-request=1
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
-+
-+sim_add hv3
-+as hv3
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.3
-+ovs-vsctl -- add-port br-int hv3-vif1 -- \
-+    set interface hv3-vif1 external-ids:iface-id=sw1-port1 \
-+    options:tx_pcap=hv3/vif1-tx.pcap \
-+    options:rxq_pcap=hv3/vif1-rx.pcap \
-+    ofport-request=1
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-+ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
-+
-+ovn-nbctl ls-add sw0
-+ovn-nbctl lsp-add sw0 sw0-port1
-+ovn-nbctl lsp-set-addresses sw0-port1 "10:54:00:00:00:03 10.0.0.3 1000::3"
-+ovn-nbctl lsp-add sw0 sw0-port2
-+ovn-nbctl lsp-set-addresses sw0-port2 "10:54:00:00:00:04 10.0.0.4 1000::4"
-+
-+ovn-nbctl ls-add sw1
-+ovn-nbctl lsp-add sw1 sw1-port1
-+ovn-nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
-+
-+ovn-nbctl lr-add lr0
-+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
-+ovn-nbctl lsp-add sw0 sw0-lr0
-+ovn-nbctl lsp-set-type sw0-lr0 router
-+ovn-nbctl lsp-set-addresses sw0-lr0 router
-+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
-+
-+ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
-+ovn-nbctl lsp-add sw1 sw1-lr0
-+ovn-nbctl lsp-set-type sw1-lr0 router
-+ovn-nbctl lsp-set-addresses sw1-lr0 router
-+ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
-+
-+ovn-nbctl ls-add public
-+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.16.0.100/24 3000::a/64
-+ovn-nbctl lsp-add public public-lr0
-+ovn-nbctl lsp-set-type public-lr0 router
-+ovn-nbctl lsp-set-addresses public-lr0 router
-+ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
-+
-+# localnet port
-+ovn-nbctl lsp-add public ln-public
-+ovn-nbctl lsp-set-type ln-public localnet
-+ovn-nbctl lsp-set-addresses ln-public unknown
-+ovn-nbctl lsp-set-options ln-public network_name=physnet1
-+
-+# schedule the gw router port to a chassis.
-+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
-+
-+# Create NAT entries for the ports
-+
-+# sw0-port1
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.0.110 10.0.0.3 sw0-port1 30:54:00:00:00:03
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 3000::c 1000::3 sw0-port1 40:54:00:00:00:03
-+# sw1-port1
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.0.120 20.0.0.3 sw1-port1 30:54:00:00:00:04
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 3000::d 2000::3 sw1-port1 40:54:00:00:00:04
-+
-+# Add snat entriess
-+ovn-nbctl lr-nat-add lr0 snat 172.16.0.100 10.0.0.0/24
-+ovn-nbctl lr-nat-add lr0 snat 172.16.0.101 10.0.0.10
-+ovn-nbctl lr-nat-add lr0 snat 172.16.0.102 10.0.0.11
-+ovn-nbctl lr-nat-add lr0 snat 172.16.0.100 20.0.0.0/24
-+
-+ovn-nbctl ls-add sw3
-+ovn-nbctl lsp-add sw3 sw3-port1
-+ovn-nbctl lsp-set-addresses sw3-port1 "20:14:00:00:00:03 30.0.0.3 3000::3"
-+
-+ovn-nbctl lr-add lr1
-+ovn-nbctl lrp-add lr1 lr1-sw3 00:00:00:10:ff:03 30.0.0.1/24 3000::a/64
-+ovn-nbctl lsp-add sw3 sw3-lr1
-+ovn-nbctl lsp-set-type sw3-lr1 router
-+ovn-nbctl lsp-set-addresses sw3-lr1 router
-+ovn-nbctl lsp-set-options sw3-lr1 router-port=lr1-sw3
-+
-+ovn-nbctl ls-add join
-+
-+# Connect lr1 to join
-+ovn-nbctl lrp-add lr1 lr1-join 00:00:04:01:02:03 170.0.0.1/24
-+ovn-nbctl lsp-add join join-lr1
-+ovn-nbctl lsp-set-type join-lr1 router
-+ovn-nbctl lsp-set-addresses join-lr1 router
-+ovn-nbctl lsp-set-options join-lr1 router-port=lr1-join
-+
-+# Create GW router
-+ovn-nbctl lr-add gw_router
-+# connect gw_router to join
-+ovn-nbctl lrp-add gw_router gw_router-join 00:00:03:11:12:13 170.0.0.2/24
-+ovn-nbctl lsp-add join join-gw_router
-+ovn-nbctl lsp-set-type join-gw_router router
-+ovn-nbctl lsp-set-addresses join-gw_router router
-+ovn-nbctl lsp-set-options join-gw_router router-port=gw_router-join
-+
-+# Connect gw_router to public
-+ovn-nbctl lrp-add gw_router gw_router-public 00:00:30:30:32:33 172.16.0.200/24 3000::b/64
-+ovn-nbctl lsp-add public public-gw_router
-+ovn-nbctl lsp-set-type public-gw_router router
-+ovn-nbctl lsp-set-addresses public-gw_router router
-+ovn-nbctl lsp-set-options public-gw_router router-port=gw_router-public
-+
-+# Pin gw_router to hv3
-+ovn-nbctl set logical_router gw_router options:chassis=hv3
-+
-+# Add snat entries on gw_router
-+ovn-nbctl lr-nat-add gw_router snat 172.16.0.200 30.0.0.0/24
-+ovn-nbctl lr-nat-add gw_router snat 172.16.0.201 30.0.0.3
-+
-+ovn-nbctl --wait=hv sync
-+
-+# Create an interface in br-phys in hv2 and send ARP request for 172.16.0.100
-+as hv2
-+ovs-vsctl -- add-port br-phys hv2-phys1 -- \
-+    set interface hv2-phys1 options:tx_pcap=hv2/phys1-tx.pcap \
-+    options:rxq_pcap=hv2/phys1-rx.pcap \
-+    ofport-request=1
-+
-+reset_pcap_file() {
-+    local iface=$1
-+    local pcap_file=$2
-+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-+options:rxq_pcap=dummy-rx.pcap
-+    rm -f ${pcap_file}*.pcap
-+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-+options:rxq_pcap=${pcap_file}-rx.pcap
-+}
-+
-+ip_to_hex() {
-+    printf "%02x%02x%02x%02x" "$@"
-+}
-+
-+send_arp_request() {
-+    local eth_src=$1 spa=$2 tpa=$3
-+    local eth_dst=ffffffffffff
-+    local eth_type=0806
-+    local eth=${eth_dst}${eth_src}${eth_type}
-+
-+    local arp=0001080006040001${eth_src}${spa}${eth_dst}${tpa}
-+
-+    local request=${eth}${arp}
-+    as hv2 ovs-appctl netdev-dummy/receive hv2-phys1 $request
-+}
-+
-+test_arp_response () {
-+    local router_mac=$1 router_ip=$2 gw=$3 nongw1=$4 nongw2=$5
-+
-+    echo "Checking arp reply for IP - $router_ip"
-+    as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
-+    as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
-+    as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
-+    as hv2 reset_pcap_file hv2-phys1 hv2/phys1
-+
-+    local src_mac=000200100011
-+    src_ip=$(ip_to_hex 172 16 0 40)
-+    send_arp_request ${src_mac} ${src_ip} ${router_ip}
-+    arp_reply=${src_mac}${router_mac}08060001080006040002${router_mac}
-+    arp_reply=${arp_reply}${router_ip}${src_mac}${src_ip}
-+
-+    OVS_WAIT_UNTIL([
-+        test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/phys1-tx.pcap | wc -l) -ge 1
-+    ])
-+
-+    AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/phys1-tx.pcap | \
-+    grep -c $arp_reply], [0], [1
-+])
-+
-+    # $gw phys1-n1 should see the response because $gw ovn-controller responds
-+    # to arp request.
-+    AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $gw/br-phys_n1-tx.pcap | \
-+    grep -c $arp_reply], [0], [1
-+])
-+
-+    # $nongw1 and $nongw1 phys1-n1 should not see the response.
-+    $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw1/br-phys_n1-tx.pcap
-+    AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw1/br-phys_n1-tx.pcap | \
-+    grep -c $arp_reply], [1], [0
-+])
-+
-+    AT_CHECK([$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $nongw2/br-phys_n1-tx.pcap | \
-+    grep -c $arp_reply], [1], [0
-+])
-+}
-+
-+# Send ARP request for the IPs which belongs to lr0 having
-+# distributed gw router port - lr0-public.
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 100) hv1 hv2 hv3
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 101) hv1 hv2 hv3
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 102) hv1 hv2 hv3
-+
-+# Send ARP request for the IP which belongs to gw_router
-+test_arp_response 000030303233 $(ip_to_hex 172 16 0 200) hv3 hv1 hv2
-+test_arp_response 000030303233 $(ip_to_hex 172 16 0 201) hv3 hv1 hv2
-+
-+# Make hv3 claim the cr-lr0-public
-+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
-+ovn-nbctl lrp-set-gateway-chassis lr0-public hv2 30
-+ovn-nbctl lrp-set-gateway-chassis lr0-public hv3 40
-+
-+hv3_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv3)
-+
-+OVS_WAIT_UNTIL([
-+    cr_lr0_public_ch=$(ovn-sbctl --bare --columns chassis list port_binding cr-lr0-public)
-+    test $cr_lr0_public_ch = $hv3_uuid
-+])
-+
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 100) hv3 hv1 hv2
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 101) hv3 hv1 hv2
-+test_arp_response 000020201213 $(ip_to_hex 172 16 0 102) hv3 hv1 hv2
-+
-+# Schedule gw_router on hv1.
-+ovn-nbctl set logical_router gw_router options:chassis=hv1
-+hv1_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv1)
-+
-+OVS_WAIT_UNTIL([
-+    gw_router_ch=$(ovn-sbctl --bare --columns chassis list port_binding gw_router-public)
-+    test $gw_router_ch = $hv1_uuid
-+])
-+
-+# Send ARP request for the IP which belongs to gw_router
-+test_arp_response 000030303233 $(ip_to_hex 172 16 0 200) hv1 hv2 hv3
-+test_arp_response 000030303233 $(ip_to_hex 172 16 0 201) hv1 hv2 hv3
-+
-+OVN_CLEANUP([hv1],[hv2],[hv3])
-+AT_CLEANUP
--- 
-2.26.2
-
diff --git a/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch b/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch
deleted file mode 100644
index 5eb3010..0000000
--- a/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 4fe045290891859c14e19d6bb00b2c857a0d4021 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 1 Sep 2020 21:20:04 +0200
-Subject: [PATCH] ovn-northd: Rate limit missing chassis log.
-
-This log does indicate a problem so it's useful to log it at WARN level
-but there's not much use to spam at every iteration of ovn-northd.
-
-CC: Han Zhou <hzhou@ovn.org>
-Fixes: 4adc10f58127 ("Avoid nb_cfg update notification flooding")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-(cherry picked from upstream commit 81e88122985caf35b6c60b8d04e16cd0b53aa2b7)
-
-Change-Id: I946b74b44149cefec09cd1a19de3ffb8d7c01647
----
- northd/ovn-northd.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index c83f9d5..8f2cd43 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -12254,8 +12254,10 @@ update_northbound_cfg(struct northd_context *ctx,
-                     continue;
-                 }
-             } else {
--                VLOG_WARN("Chassis not exist for Chassis_Private record, "
--                          "name: %s", chassis_priv->name);
-+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-+                VLOG_WARN_RL(&rl, "Chassis does not exist for "
-+                             "Chassis_Private record, name: %s",
-+                             chassis_priv->name);
-             }
- 
-             if (chassis_priv->nb_cfg < hv_cfg) {
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch b/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
deleted file mode 100644
index 5df4118..0000000
--- a/SOURCES/0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
+++ /dev/null
@@ -1,460 +0,0 @@
-From 966dd7bc7fb7931d7616ca886dc24ecb1f34cced Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 28 May 2020 14:32:31 +0200
-Subject: [PATCH] ovsdb-idl: Avoid inconsistent IDL state with
- OVSDB_MONITOR_V3.
-
-Assuming an ovsdb client connected to a database using OVSDB_MONITOR_V3
-(i.e., "monitor_cond_since" method) with the initial monitor condition
-MC1.
-
-Assuming the following two transactions are executed on the
-ovsdb-server:
-TXN1: "insert record R1 in table T1"
-TXN2: "insert record R2 in table T2"
-
-If the client's monitor condition MC1 for table T2 matches R2 then the
-client will receive the following update3 message:
-method="update3", "insert record R2 in table T2", last-txn-id=TXN2
-
-At this point, if the presence of the new record R2 in the IDL triggers
-the client to update its monitor condition to MC2 and add a clause for
-table T1 which matches R1, a monitor_cond_change message is sent to the
-server:
-method="monitor_cond_change", "clauses from MC2"
-
-In normal operation the ovsdb-server will reply with a new update3
-message of the form:
-method="update3", "insert record R1 in table T1", last-txn-id=TXN2
-
-However, if the connection drops in the meantime, this last update might
-get lost.
-
-It might happen that during the reconnect a new transaction happens
-that modifies the original record R1:
-TXN3: "modify record R1 in table T1"
-
-When the client reconnects, it will try to perform a fast resync by
-sending:
-method="monitor_cond_since", "clauses from MC2", last-txn-id=TXN2
-
-Because TXN2 is still in the ovsdb-server transaction history, the
-server replies with the changes from the most recent transactions only,
-i.e., TXN3:
-result="true", last-txbb-id=TXN3, "modify record R1 in table T1"
-
-This causes the IDL on the client in to end up in an inconsistent
-state because it has never seen the update that created R1.
-
-Such a scenario is described in:
-https://bugzilla.redhat.com/show_bug.cgi?id=1808580#c22
-
-To avoid this issue, the IDL will now maintain (up to) 3 different
-types of conditions for each DB table:
-- new_cond: condition that has been set by the IDL client but has
-  not yet been sent to the server through monitor_cond_change.
-- req_cond: condition that has been sent to the server but the reply
-  acknowledging the change hasn't been received yet.
-- ack_cond: condition that has been acknowledged by the server.
-
-Whenever the IDL FSM is restarted (e.g., voluntary or involuntary
-disconnect):
-- if there is a known last_id txn-id the code ensures that new_cond
-  will contain the most recent condition set by the IDL client
-  (either req_cond if there was a request in flight, or new_cond
-  if the IDL client set a condition while the IDL was disconnected)
-- if there is no known last_id txn-id the code ensures that ack_cond will
-  contain the most recent conditions set by the IDL client regardless
-  whether they were acked by the server or not.
-
-When monitor_cond_since/monitor_cond requests are sent they will
-always include ack_cond and if new_cond is not NULL a follow up
-monitor_cond_change will be generated afterwards.
-
-On the other hand ovsdb_idl_db_set_condition() will always modify new_cond.
-
-This ensures that updates of type "insert" that happened before the last
-transaction known by the IDL but didn't match old monitor conditions are
-sent upon reconnect if the monitor condition has changed to include them
-in the meantime.
-
-Fixes: 403a6a0cb003 ("ovsdb-idl: Fast resync from server when connection reset.")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-(cherry picked from upstream OVS commit ae25f8c8fff80a58cd0a15e2d3ae7ab1b4994e48)
-
-Change-Id: I4f3cd43cf69dfe76eb65c9709b759e5062c29e89
----
- openvswitch-2.13.0/lib/ovsdb-idl-provider.h |   8 +-
- openvswitch-2.13.0/lib/ovsdb-idl.c          | 167 ++++++++++++++++++++++++----
- openvswitch-2.13.0/tests/ovsdb-idl.at       |  56 ++++++++++
- 3 files changed, 206 insertions(+), 25 deletions(-)
-
-diff --git a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
-index 30d1d08..00497d9 100644
---- a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
-+++ b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
-@@ -122,8 +122,12 @@ struct ovsdb_idl_table {
-     unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
-     struct ovs_list indexes;    /* Contains "struct ovsdb_idl_index"s */
-     struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */
--    struct ovsdb_idl_condition condition;
--    bool cond_changed;
-+    struct ovsdb_idl_condition *ack_cond; /* Last condition acked by the
-+                                           * server. */
-+    struct ovsdb_idl_condition *req_cond; /* Last condition requested to the
-+                                           * server. */
-+    struct ovsdb_idl_condition *new_cond; /* Latest condition set by the IDL
-+                                           * client. */
- };
- 
- struct ovsdb_idl_class {
-diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
-index 2d35179..8eb4213 100644
---- a/openvswitch-2.13.0/lib/ovsdb-idl.c
-+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
-@@ -240,6 +240,10 @@ static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *,
-                                            struct ovsdb_idl_db *,
-                                            enum ovsdb_idl_monitor_method);
- static void ovsdb_idl_db_clear(struct ovsdb_idl_db *db);
-+static void ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db);
-+static void ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db);
-+static void ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst,
-+                                     struct ovsdb_idl_condition **src);
- 
- struct ovsdb_idl {
-     struct ovsdb_idl_db server;
-@@ -424,9 +428,11 @@ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
-             = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]
-             = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
-         table->db = db;
--        ovsdb_idl_condition_init(&table->condition);
--        ovsdb_idl_condition_add_clause_true(&table->condition);
--        table->cond_changed = false;
-+        table->ack_cond = NULL;
-+        table->req_cond = NULL;
-+        table->new_cond = xmalloc(sizeof *table->new_cond);
-+        ovsdb_idl_condition_init(table->new_cond);
-+        ovsdb_idl_condition_add_clause_true(table->new_cond);
-     }
-     db->monitor_id = json_array_create_2(json_string_create("monid"),
-                                          json_string_create(class->database));
-@@ -558,12 +564,15 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle)
- static void
- ovsdb_idl_db_destroy(struct ovsdb_idl_db *db)
- {
-+    struct ovsdb_idl_condition *null_cond = NULL;
-     ovs_assert(!db->txn);
-     ovsdb_idl_db_txn_abort_all(db);
-     ovsdb_idl_db_clear(db);
-     for (size_t i = 0; i < db->class_->n_tables; i++) {
-         struct ovsdb_idl_table *table = &db->tables[i];
--        ovsdb_idl_condition_destroy(&table->condition);
-+        ovsdb_idl_condition_move(&table->ack_cond, &null_cond);
-+        ovsdb_idl_condition_move(&table->req_cond, &null_cond);
-+        ovsdb_idl_condition_move(&table->new_cond, &null_cond);
-         ovsdb_idl_destroy_indexes(table);
-         shash_destroy(&table->columns);
-         hmap_destroy(&table->rows);
-@@ -692,6 +701,12 @@ ovsdb_idl_send_request(struct ovsdb_idl *idl, struct jsonrpc_msg *request)
- static void
- ovsdb_idl_restart_fsm(struct ovsdb_idl *idl)
- {
-+    /* Resync data DB table conditions to avoid missing updates due to
-+     * conditions that were in flight or changed locally while the connection
-+     * was down.
-+     */
-+    ovsdb_idl_db_sync_condition(&idl->data);
-+
-     ovsdb_idl_send_schema_request(idl, &idl->server);
-     ovsdb_idl_transition(idl, IDL_S_SERVER_SCHEMA_REQUESTED);
-     idl->data.monitoring = OVSDB_IDL_NOT_MONITORING;
-@@ -799,7 +814,9 @@ ovsdb_idl_process_response(struct ovsdb_idl *idl, struct jsonrpc_msg *msg)
-          * do, it's a "monitor_cond_change", which means that the conditional
-          * monitor clauses were updated.
-          *
--         * If further condition changes were pending, send them now. */
-+         * Mark the last requested conditions as acked and if further
-+         * condition changes were pending, send them now. */
-+        ovsdb_idl_db_ack_condition(&idl->data);
-         ovsdb_idl_send_cond_change(idl);
-         idl->data.cond_seqno++;
-         break;
-@@ -1495,30 +1512,60 @@ ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a,
- }
- 
- static void
--ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst,
-+ovsdb_idl_condition_clone(struct ovsdb_idl_condition **dst,
-                           const struct ovsdb_idl_condition *src)
- {
--    ovsdb_idl_condition_init(dst);
-+    if (*dst) {
-+        ovsdb_idl_condition_destroy(*dst);
-+    } else {
-+        *dst = xmalloc(sizeof **dst);
-+    }
-+    ovsdb_idl_condition_init(*dst);
- 
--    dst->is_true = src->is_true;
-+    (*dst)->is_true = src->is_true;
- 
-     const struct ovsdb_idl_clause *clause;
-     HMAP_FOR_EACH (clause, hmap_node, &src->clauses) {
--        ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash);
-+        ovsdb_idl_condition_add_clause__(*dst, clause, clause->hmap_node.hash);
-     }
- }
- 
-+static void
-+ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst,
-+                         struct ovsdb_idl_condition **src)
-+{
-+    if (*dst) {
-+        ovsdb_idl_condition_destroy(*dst);
-+        free(*dst);
-+    }
-+    *dst = *src;
-+    *src = NULL;
-+}
-+
- static unsigned int
- ovsdb_idl_db_set_condition(struct ovsdb_idl_db *db,
-                            const struct ovsdb_idl_table_class *tc,
-                            const struct ovsdb_idl_condition *condition)
- {
-+    struct ovsdb_idl_condition *table_cond;
-     struct ovsdb_idl_table *table = ovsdb_idl_db_table_from_class(db, tc);
-     unsigned int seqno = db->cond_seqno;
--    if (!ovsdb_idl_condition_equals(condition, &table->condition)) {
--        ovsdb_idl_condition_destroy(&table->condition);
--        ovsdb_idl_condition_clone(&table->condition, condition);
--        db->cond_changed = table->cond_changed = true;
-+
-+    /* Compare the new condition to the last known condition which can be
-+     * either "new" (not sent yet), "requested" or "acked", in this order.
-+     */
-+    if (table->new_cond) {
-+        table_cond = table->new_cond;
-+    } else if (table->req_cond) {
-+        table_cond = table->req_cond;
-+    } else {
-+        table_cond = table->ack_cond;
-+    }
-+    ovs_assert(table_cond);
-+
-+    if (!ovsdb_idl_condition_equals(condition, table_cond)) {
-+        ovsdb_idl_condition_clone(&table->new_cond, condition);
-+        db->cond_changed = true;
-         poll_immediate_wake();
-         return seqno + 1;
-     }
-@@ -1563,9 +1610,8 @@ ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd)
- }
- 
- static struct json *
--ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table)
-+ovsdb_idl_create_cond_change_req(const struct ovsdb_idl_condition *cond)
- {
--    const struct ovsdb_idl_condition *cond = &table->condition;
-     struct json *monitor_cond_change_request = json_object_create();
-     struct json *cond_json = ovsdb_idl_condition_to_json(cond);
- 
-@@ -1585,8 +1631,12 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
-     for (size_t i = 0; i < db->class_->n_tables; i++) {
-         struct ovsdb_idl_table *table = &db->tables[i];
- 
--        if (table->cond_changed) {
--            struct json *req = ovsdb_idl_create_cond_change_req(table);
-+        /* Always use the most recent conditions set by the IDL client when
-+         * requesting monitor_cond_change, i.e., table->new_cond.
-+         */
-+        if (table->new_cond) {
-+            struct json *req =
-+                ovsdb_idl_create_cond_change_req(table->new_cond);
-             if (req) {
-                 if (!monitor_cond_change_requests) {
-                     monitor_cond_change_requests = json_object_create();
-@@ -1595,7 +1645,11 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
-                              table->class_->name,
-                              json_array_create_1(req));
-             }
--            table->cond_changed = false;
-+            /* Mark the new condition as requested by moving it to req_cond.
-+             * If there's already requested condition that's a bug.
-+             */
-+            ovs_assert(table->req_cond == NULL);
-+            ovsdb_idl_condition_move(&table->req_cond, &table->new_cond);
-         }
-     }
- 
-@@ -1610,6 +1664,73 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
-     return jsonrpc_create_request("monitor_cond_change", params, NULL);
- }
- 
-+/* Marks all requested table conditions in 'db' as acked by the server.
-+ * It should be called when the server replies to monitor_cond_change
-+ * requests.
-+ */
-+static void
-+ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db)
-+{
-+    for (size_t i = 0; i < db->class_->n_tables; i++) {
-+        struct ovsdb_idl_table *table = &db->tables[i];
-+
-+        if (table->req_cond) {
-+            ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond);
-+        }
-+    }
-+}
-+
-+/* Should be called when the IDL fsm is restarted and resyncs table conditions
-+ * based on the state the DB is in:
-+ * - if a non-zero last_id is available for the DB then upon reconnect
-+ *   the IDL should first request acked conditions to avoid missing updates
-+ *   about records that were added before the transaction with
-+ *   txn-id == last_id. If there were requested condition changes in flight
-+ *   (i.e., req_cond not NULL) and the IDL client didn't set new conditions
-+ *   (i.e., new_cond is NULL) then move req_cond to new_cond to trigger a
-+ *   follow up monitor_cond_change request.
-+ * - if there's no last_id available for the DB then it's safe to use the
-+ *   latest conditions set by the IDL client even if they weren't acked yet.
-+ */
-+static void
-+ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db)
-+{
-+    bool ack_all = uuid_is_zero(&db->last_id);
-+
-+    db->cond_changed = false;
-+    for (size_t i = 0; i < db->class_->n_tables; i++) {
-+        struct ovsdb_idl_table *table = &db->tables[i];
-+
-+        /* When monitor_cond_since requests will be issued, the
-+         * table->ack_cond condition will be added to the "where" clause".
-+         * Follow up monitor_cond_change requests will use table->new_cond.
-+         */
-+        if (ack_all) {
-+            if (table->new_cond) {
-+                ovsdb_idl_condition_move(&table->req_cond, &table->new_cond);
-+            }
-+
-+            if (table->req_cond) {
-+                ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond);
-+            }
-+        } else {
-+            /* If there was no "unsent" condition but instead a
-+             * monitor_cond_change request was in flight, move table->req_cond
-+             * to table->new_cond and set db->cond_changed to trigger a new
-+             * monitor_cond_change request.
-+             *
-+             * However, if a new condition has been set by the IDL client,
-+             * monitor_cond_change will be sent anyway and will use the most
-+             * recent table->new_cond so there's no need to update it here.
-+             */
-+            if (table->req_cond && !table->new_cond) {
-+                ovsdb_idl_condition_move(&table->new_cond, &table->req_cond);
-+                db->cond_changed = true;
-+            }
-+        }
-+    }
-+}
-+
- static void
- ovsdb_idl_send_cond_change(struct ovsdb_idl *idl)
- {
-@@ -2064,13 +2185,15 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db,
-             monitor_request = json_object_create();
-             json_object_put(monitor_request, "columns", columns);
- 
--            const struct ovsdb_idl_condition *cond = &table->condition;
-+            /* Always use acked conditions when requesting
-+             * monitor_cond/monitor_cond_since.
-+             */
-+            const struct ovsdb_idl_condition *cond = table->ack_cond;
-             if ((monitor_method == OVSDB_IDL_MM_MONITOR_COND ||
-                  monitor_method == OVSDB_IDL_MM_MONITOR_COND_SINCE) &&
--                !ovsdb_idl_condition_is_true(cond)) {
-+                cond && !ovsdb_idl_condition_is_true(cond)) {
-                 json_object_put(monitor_request, "where",
-                                 ovsdb_idl_condition_to_json(cond));
--                table->cond_changed = false;
-             }
-             json_object_put(monitor_requests, tc->name,
-                             json_array_create_1(monitor_request));
-@@ -2078,8 +2201,6 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db,
-     }
-     free_schema(schema);
- 
--    db->cond_changed = false;
--
-     struct json *params = json_array_create_3(
-                               json_string_create(db->class_->database),
-                               json_clone(db->monitor_id),
-diff --git a/openvswitch-2.13.0/tests/ovsdb-idl.at b/openvswitch-2.13.0/tests/ovsdb-idl.at
-index cc38d69..a5ca966 100644
---- a/openvswitch-2.13.0/tests/ovsdb-idl.at
-+++ b/openvswitch-2.13.0/tests/ovsdb-idl.at
-@@ -1814,3 +1814,59 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY],
- 
- OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote'])
- OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote'])
-+
-+# same as OVSDB_CHECK_IDL but uses C IDL implementation with tcp
-+# with multiple remotes.
-+m4_define([OVSDB_CHECK_CLUSTER_IDL_C],
-+  [AT_SETUP([$1 - C - tcp])
-+   AT_KEYWORDS([ovsdb server idl positive tcp socket $5])
-+   m4_define([LPBK],[127.0.0.1])
-+   AT_CHECK([ovsdb_cluster_start_idltest $2 "ptcp:0:"LPBK])
-+   PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1])
-+   PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2])
-+   PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3])
-+   remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3
-+
-+   m4_if([$3], [], [],
-+     [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])])
-+   AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4],
-+            [0], [stdout], [ignore])
-+   AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]),
-+            [0], [$5])
-+   AT_CLEANUP])
-+
-+# Checks that monitor_cond_since works fine when disconnects happen
-+# with cond_change requests in flight (i.e., IDL is properly updated).
-+OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect],
-+  3,
-+  [['["idltest",
-+       {"op": "insert",
-+       "table": "simple",
-+       "row": {"i": 1,
-+               "r": 1.0,
-+               "b": true}},
-+       {"op": "insert",
-+       "table": "simple",
-+       "row": {"i": 2,
-+               "r": 1.0,
-+               "b": true}}]']],
-+  [['condition simple []' \
-+    'condition simple [["i","==",2]]' \
-+    'condition simple [["i","==",1]]' \
-+    '+reconnect' \
-+    '["idltest",
-+      {"op": "update",
-+       "table": "simple",
-+       "where": [["i", "==", 1]],
-+       "row": {"r": 2.0 }}]']],
-+  [[000: change conditions
-+001: empty
-+002: change conditions
-+003: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
-+004: change conditions
-+005: reconnect
-+006: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
-+007: {"error":null,"result":[{"count":1}]}
-+008: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2>
-+009: done
-+]])
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch b/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch
deleted file mode 100644
index 522da2d..0000000
--- a/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 6a3006116945234a5ad8e88f9cf86a4c4212b1f2 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 27 Aug 2020 16:47:33 +0200
-Subject: [PATCH] pinctrl: Fix incorrect warning message for multicast querier.
-
-It's quite common that IP Multicast is enabled only for IPv4 or only for
-IPv6. In such cases ovn-controller should not generate warnings.
-
-Fixes: 677a3ba4d66b ("ovn: Add MLD support.")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from master commit d6d59911412edc20c40c026a1bb8c48ef01800fb)
-
-(cherry picked from upstream commit be614f0743abe605c4305003c9de6859dd224fe9)
-
-Change-Id: I9fbd75723fdd163e4129106b378f7fb71a0e6877
----
- controller/pinctrl.c | 38 +++++++++++++++++++++++---------------
- 1 file changed, 23 insertions(+), 15 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index f72ab70..c8fbd37 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -4183,21 +4183,11 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg,
-     cfg->seq_no = ip_mcast->seq_no;
- 
-     if (querier_enabled) {
--        /* Try to parse the source ETH address. */
--        if (!ip_mcast->eth_src ||
--                !eth_addr_from_string(ip_mcast->eth_src,
--                                      &cfg->query_eth_src)) {
--            VLOG_WARN_RL(&rl,
--                         "IGMP Querier enabled with invalid ETH src address");
--            /* Failed to parse the ETH source address. Disable the querier. */
--            cfg->querier_v4_enabled = false;
--            cfg->querier_v6_enabled = false;
--        }
--
-         /* Try to parse the source IPv4 address. */
-         if (cfg->querier_v4_enabled) {
--            if (!ip_mcast->ip4_src ||
--                    !ip_parse(ip_mcast->ip4_src, &cfg->query_ipv4_src)) {
-+            if (!ip_mcast->ip4_src || !ip_mcast->ip4_src[0]) {
-+                cfg->querier_v4_enabled = false;
-+            } else if (!ip_parse(ip_mcast->ip4_src, &cfg->query_ipv4_src)) {
-                 VLOG_WARN_RL(&rl,
-                             "IGMP Querier enabled with invalid IPv4 "
-                             "src address");
-@@ -4215,8 +4205,9 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg,
- 
-         /* Try to parse the source IPv6 address. */
-         if (cfg->querier_v6_enabled) {
--            if (!ip_mcast->ip6_src ||
--                    !ipv6_parse(ip_mcast->ip6_src, &cfg->query_ipv6_src)) {
-+            if (!ip_mcast->ip6_src || !ip_mcast->ip6_src[0]) {
-+                cfg->querier_v6_enabled = false;
-+            } else if (!ipv6_parse(ip_mcast->ip6_src, &cfg->query_ipv6_src)) {
-                 VLOG_WARN_RL(&rl,
-                             "MLD Querier enabled with invalid IPv6 "
-                             "src address");
-@@ -4232,6 +4223,23 @@ ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg,
-             cfg->query_ipv6_dst =
-                 (struct in6_addr)IN6ADDR_ALL_HOSTS_INIT;
-         }
-+
-+        if (!cfg->querier_v4_enabled && !cfg->querier_v6_enabled) {
-+            VLOG_WARN_RL(&rl,
-+                         "IGMP Querier enabled without a valid IPv4 or IPv6 "
-+                         "address");
-+        }
-+
-+        /* Try to parse the source ETH address. */
-+        if (!ip_mcast->eth_src ||
-+                !eth_addr_from_string(ip_mcast->eth_src,
-+                                      &cfg->query_eth_src)) {
-+            VLOG_WARN_RL(&rl,
-+                         "IGMP Querier enabled with invalid ETH src address");
-+            /* Failed to parse the ETH source address. Disable the querier. */
-+            cfg->querier_v4_enabled = false;
-+            cfg->querier_v6_enabled = false;
-+        }
-     }
- }
- 
--- 
-1.8.3.1
-
diff --git a/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch b/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch
deleted file mode 100644
index 594f6f7..0000000
--- a/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch
+++ /dev/null
@@ -1,309 +0,0 @@
-From a0f09fcde4c1ef4f2971cc4c504d939298aa7ec5 Mon Sep 17 00:00:00 2001
-From: Dhathri Purohith <dhathri.purohith@nutanix.com>
-Date: Wed, 24 Jun 2020 19:16:24 -0700
-Subject: [PATCH 02/22] Add support for DHCP domain search option (119)
-
-Domain search list is encoded according to the specifications in RFC 1035,
-section 4.1.4.
-
-Change-Id: I43d8e10f90fc64c5d8463f3d41fbaac5a2e1a929
-Signed-off-by: Dhathri Purohith <dhathri.purohith@nutanix.com>
-Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upmstream master commit d79bb92c4b4938da89aa16a6795c7bcb4e128374)
----
- lib/actions.c       | 85 ++++++++++++++++++++++++++++++++++++++++++++-
- lib/ovn-l7.h        |  3 ++
- northd/ovn-northd.c |  1 +
- ovn-nb.xml          | 13 +++++++
- ovn-sb.ovsschema    |  6 ++--
- ovn-sb.xml          | 11 ++++++
- tests/ovn.at        | 39 +++++++++++++++++++++
- tests/test-ovn.c    |  1 +
- 8 files changed, 155 insertions(+), 4 deletions(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index 616c93e8a..e4de97c23 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2087,7 +2087,8 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
-         return;
-     }
- 
--    if (!strcmp(o->option->type, "str")) {
-+    if (!strcmp(o->option->type, "str") ||
-+        !strcmp(o->option->type, "domains")) {
-         if (o->value.type != EXPR_C_STRING) {
-             lexer_error(ctx->lexer, "%s option %s requires string value.",
-                         opts_type, o->option->name);
-@@ -2422,6 +2423,88 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
-            opt_header[1] = sizeof(ovs_be32);
-            ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
-         }
-+    } else if (!strcmp(o->option->type, "domains")) {
-+        /* Please refer to RFC 1035, section 4.1.4 for the format of encoding
-+         * domain names. Below is an example for encoding a search list
-+         * consisting of the "abc.com" and "xyz.abc.com".
-+         *
-+         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
-+         * |119|14 | 3 |'a'|'b'|'c'| 3 |'c'|'o'|'m'| 0 |'x'|'y'|'z'|xC0|x00|
-+         * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
-+         *
-+         * The encoding of "abc.com" ends with 0 to mark the end of the
-+         * domain name as required by RFC 1035.
-+         *
-+         * The encoding of "xyz" (for "xyz.abc.com") ends with the two-octet
-+         * compression pointer C000 (hex), which points to offset 0 where
-+         * another validly encoded domain name can be found to complete
-+         * the name ("abc.com").
-+         *
-+         * Encoding adds 2 bytes (one for length and one for delimiter) for
-+         * every domain name that is unique. If all the domain names are unique
-+         * (which probably never happens in real world), then encoded string
-+         * could be longer than the original string. Just to be on the safer
-+         * side, allocate the (approx.) worst case length here.
-+         */
-+        uint8_t *dns_encoded = xzalloc(2 * strlen(c->string));
-+        uint16_t encode_offset = 0;
-+        struct shash label_offset_map = SHASH_INITIALIZER(&label_offset_map);
-+        char *domain_list = xstrdup(c->string), *dom_ptr = NULL;
-+        char *suffix = xzalloc(strlen(domain_list));
-+        for (char *domain = strtok_r(domain_list, ",", &dom_ptr);
-+             domain != NULL;
-+             domain = strtok_r(NULL, ",", &dom_ptr)) {
-+            if (strlen(domain) > DOMAIN_NAME_MAX_LEN) {
-+                VLOG_WARN("Domain names longer than 255 characters are not"
-+                          "supported");
-+                goto out;
-+            }
-+            ovs_strlcpy(suffix, domain, strlen(domain));
-+            char *label;
-+            for (label = strtok_r(domain, ".", &domain);
-+                 label != NULL;
-+                 label = strtok_r(NULL, ".", &domain)) {
-+                /* Check if we have already encoded this suffix.
-+                 * If yes, fill in the reference and break. */
-+                uint16_t *get_offset;
-+                get_offset  = shash_find_data(&label_offset_map, suffix);
-+                if (get_offset != NULL) {
-+                    ovs_be16 temp = htons(0xc000) | htons(*get_offset);
-+                    memcpy(dns_encoded + encode_offset, &temp,
-+                        sizeof(temp));
-+                    encode_offset += sizeof(temp);
-+                    break;
-+                } else {
-+                    /* The suffix was not encoded before, encode it now
-+                     * and add the offset to the label_offset_map. */
-+                    uint16_t *set_offset = xzalloc(sizeof(uint16_t));
-+                    *set_offset = encode_offset;
-+                    shash_add_once(&label_offset_map, suffix, set_offset);
-+
-+                    uint8_t len = strlen(label);
-+                    memcpy(dns_encoded + encode_offset, &len, sizeof(uint8_t));
-+                    encode_offset += sizeof(uint8_t);
-+                    memcpy(dns_encoded + encode_offset, label, len);
-+                    encode_offset += len;
-+                }
-+               ovs_strlcpy(suffix, domain, strlen(domain));
-+            }
-+            /* Add the end marker (0 byte) to determine the end of the
-+             * domain. */
-+            if (label == NULL) {
-+                uint8_t end = 0;
-+                memcpy(dns_encoded + encode_offset, &end, sizeof(uint8_t));
-+                encode_offset += sizeof(uint8_t);
-+            }
-+        }
-+        opt_header[1] = encode_offset;
-+        ofpbuf_put(ofpacts, dns_encoded, encode_offset);
-+
-+        out:
-+            free(suffix);
-+            free(domain_list);
-+            free(dns_encoded);
-+            shash_destroy_free_data(&label_offset_map);
-     }
- }
- 
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index 22a2153de..cea97b9ce 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -34,6 +34,7 @@ struct gen_opts_map {
-     size_t code;
- };
- 
-+#define DOMAIN_NAME_MAX_LEN 255
- #define DHCP_BROADCAST_FLAG 0x8000
- 
- #define DHCP_OPTION(NAME, CODE, TYPE) \
-@@ -81,6 +82,8 @@ struct gen_opts_map {
- #define DHCP_OPT_PATH_PREFIX DHCP_OPTION("path_prefix", 210, "str")
- #define DHCP_OPT_TFTP_SERVER_ADDRESS \
-     DHCP_OPTION("tftp_server_address", 150, "ipv4")
-+#define DHCP_OPT_DOMAIN_SEARCH_LIST \
-+    DHCP_OPTION("domain_search_list", 119, "domains")
- 
- #define DHCP_OPT_ARP_CACHE_TIMEOUT \
-     DHCP_OPTION("arp_cache_timeout", 35, "uint32")
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 14be87435..a665d52e9 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -11761,6 +11761,7 @@ static struct gen_opts_map supported_dhcp_opts[] = {
-     DHCP_OPT_DOMAIN_NAME,
-     DHCP_OPT_ARP_CACHE_TIMEOUT,
-     DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
-+    DHCP_OPT_DOMAIN_SEARCH_LIST,
- };
- 
- static struct gen_opts_map supported_dhcpv6_opts[] = {
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index 8d04d3d3b..0fdc1592b 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -2990,6 +2990,19 @@
-           </p>
-         </column>
-       </group>
-+
-+      <group title=" DHCP Options of type domains">
-+        <p>
-+          These options accept string value which is a comma separated
-+          list of domain names. The domain names are encoded based on RFC 1035.
-+        </p>
-+
-+        <column name="options" key="domain_search_list">
-+          <p>
-+            The DHCPv4 option code for this option is 119.
-+          </p>
-+        </column>
-+      </group>
-     </group>
- 
-     <group title="DHCPv6 options">
-diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
-index 2ec729b77..99c5de822 100644
---- a/ovn-sb.ovsschema
-+++ b/ovn-sb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Southbound",
--    "version": "2.8.1",
--    "cksum": "236203406 21905",
-+    "version": "2.8.2",
-+    "cksum": "464326363 21916",
-     "tables": {
-         "SB_Global": {
-             "columns": {
-@@ -218,7 +218,7 @@
-                         "type": "string",
-                         "enum": ["set", ["bool", "uint8", "uint16", "uint32",
-                                          "ipv4", "static_routes", "str",
--                                         "host_id"]]}}}},
-+                                         "host_id", "domains"]]}}}},
-             "isRoot": true},
-         "DHCPv6_Options": {
-             "columns": {
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 2edafd48f..293b0920c 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -3287,6 +3287,17 @@ tcp.flags = RST;
-           </p>
-         </dd>
- 
-+        <dt><code>value: domains</code></dt>
-+        <dd>
-+          <p>
-+            This indicates that the value of the DHCP option is a domain name
-+            or a comma separated list of domain names.
-+          </p>
-+
-+          <p>
-+            Example. "name=domain_search_list", "code=119", "type=domains".
-+          </p>
-+        </dd>
-       </dl>
-     </column>
-   </table>
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 4e98790af..be677b663 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1255,6 +1255,12 @@ reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,
- reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server="tftp_server_test");
-     formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server = "tftp_server_test");
-     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause)
-+reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org");
-+    formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org");
-+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
-+reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
-+    formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
-+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
- 
- reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
-     Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
-@@ -1272,6 +1278,8 @@ reg1[0] = put_dhcp_opts(offerip="xyzzy");
-     DHCPv4 option offerip requires numeric value.
- reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_name=1.2.3.4);
-     DHCPv4 option domain_name requires string value.
-+reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_search_list=1.2.3.4);
-+    DHCPv4 option domain_search_list requires string value.
- 
- # nd_ns
- nd_ns { nd.target = xxreg0; output; };
-@@ -5692,6 +5700,37 @@ AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
- cat 2.expected | cut -c 53- > expout
- AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
- 
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Set domain search list option for ls1
-+echo "------ Set domain search list --------"
-+ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \
-+server_mac=ff:10:00:00:00:01 lease_time=3600 router=10.0.0.1 \
-+domain_search_list=\"test1.com,test2.com\"
-+echo "------------------------------------------"
-+
-+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP
-+# address in the Requested IP Address option.
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=`ip_to_hex 0 0 0 0`
-+request_ip=$offer_ip
-+expected_dhcp_opts=771305746573743103636f6d00057465737432c006330400000e100104ffffff0003040a00000136040a000001
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 16.
-+OVS_WAIT_UNTIL([test 16 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
- OVN_CLEANUP([hv1])
- 
- AT_CLEANUP
-diff --git a/tests/test-ovn.c b/tests/test-ovn.c
-index 29d343b60..ba628288b 100644
---- a/tests/test-ovn.c
-+++ b/tests/test-ovn.c
-@@ -190,6 +190,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
-     dhcp_opt_add(dhcp_opts, "tftp_server_address", 150, "ipv4");
-     dhcp_opt_add(dhcp_opts, "arp_cache_timeout", 35, "uint32");
-     dhcp_opt_add(dhcp_opts, "tcp_keepalive_interval", 38, "uint32");
-+    dhcp_opt_add(dhcp_opts, "domain_search_list", 119, "domains");
- 
-     /* DHCPv6 options. */
-     hmap_init(dhcpv6_opts);
--- 
-2.26.2
-
diff --git a/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch b/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch
deleted file mode 100644
index b9e4333..0000000
--- a/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch
+++ /dev/null
@@ -1,274 +0,0 @@
-From 59103ff841797ad399e1679bfecfb6256bb6a0c4 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 3 Sep 2020 17:04:01 +0200
-Subject: [PATCH 2/2] chassis: Fix chassis_private record updates when the
- system-id changes.
-
-Also:
-- Change conditional monitoring for Chassis_Private to use the chassis uuid
-  instead of chassis name. Using the chassis->name field does not work
-  because this is the old value of the field and would cause ovsdb-server
-  to inform ovn-controller that the updated Chassis_Private record was
-  "deleted" because it doesn't match the monitor condition anymore.
-- Allow ovn-sbctl to access Chassis_Private records by name.
-
-Reported-at: https://bugzilla.redhat.com/1873032
-Reported-by: Ying Xu <yinxu@redhat.com>
-CC: Han Zhou <hzhou@ovn.org>
-Fixes: 4adc10f58127 ("Avoid nb_cfg update notification flooding")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit dce1af31b550a9fb57b01cbe0b4139b6768f2521)
-
-Change-Id: Ic5f7f0b820b43715e1f1cf68b215374daf237fd5
----
- controller/chassis.c        | 92 +++++++++++++++++++++++++++++++++++++++------
- controller/chassis.h        |  2 +
- controller/ovn-controller.c | 13 ++++---
- tests/ovn-controller.at     | 18 +++++++++
- utilities/ovn-sbctl.c       |  3 ++
- 5 files changed, 112 insertions(+), 16 deletions(-)
-
-diff --git a/controller/chassis.c b/controller/chassis.c
-index 773d966..8231169 100644
---- a/controller/chassis.c
-+++ b/controller/chassis.c
-@@ -633,6 +633,77 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
-     return true;
- }
- 
-+/*
-+ * Returns a pointer to a chassis_private record from 'chassis_pvt_table' that
-+ * matches the chassis record.
-+ */
-+static const struct sbrec_chassis_private *
-+chassis_private_get_stale_record(
-+    const struct sbrec_chassis_private_table *chassis_pvt_table,
-+    const struct sbrec_chassis *chassis)
-+{
-+    const struct sbrec_chassis_private *chassis_pvt_rec;
-+
-+    SBREC_CHASSIS_PRIVATE_TABLE_FOR_EACH (chassis_pvt_rec, chassis_pvt_table) {
-+        if (chassis_pvt_rec->chassis == chassis) {
-+            return chassis_pvt_rec;
-+        }
-+    }
-+
-+    return NULL;
-+}
-+
-+/* If this is a chassis_private config update after we initialized the record
-+ * once then we should always be able to find it with the ID we saved in
-+ * chassis_state.
-+ * Otherwise (i.e., first time we created the chassis record or if the
-+ * system-id changed) then we check if there's a stale record from a previous
-+ * controller run that didn't end gracefully and reuse it. If not then we
-+ * create a new record.
-+ *
-+ * Returns the local chassis record.
-+ */
-+static const struct sbrec_chassis_private *
-+chassis_private_get_record(
-+    struct ovsdb_idl_txn *ovnsb_idl_txn,
-+    struct ovsdb_idl_index *sbrec_chassis_pvt_by_name,
-+    const struct sbrec_chassis_private_table *chassis_pvt_table,
-+    const struct sbrec_chassis *chassis)
-+{
-+    const struct sbrec_chassis_private *chassis_p = NULL;
-+
-+    if (chassis_info_id_inited(&chassis_state)) {
-+        chassis_p =
-+            chassis_private_lookup_by_name(sbrec_chassis_pvt_by_name,
-+                                           chassis_info_id(&chassis_state));
-+    }
-+
-+    if (!chassis_p) {
-+        chassis_p = chassis_private_get_stale_record(chassis_pvt_table,
-+                                                     chassis);
-+    }
-+
-+    if (!chassis_p && ovnsb_idl_txn) {
-+        return sbrec_chassis_private_insert(ovnsb_idl_txn);
-+    }
-+
-+    return chassis_p;
-+}
-+
-+static void
-+chassis_private_update(const struct sbrec_chassis_private *chassis_pvt,
-+                       const struct sbrec_chassis *chassis,
-+                       const char *chassis_id)
-+{
-+    if (!chassis_pvt->name || strcmp(chassis_pvt->name, chassis_id)) {
-+        sbrec_chassis_private_set_name(chassis_pvt, chassis_id);
-+    }
-+
-+    if (chassis_pvt->chassis != chassis) {
-+        sbrec_chassis_private_set_chassis(chassis_pvt, chassis);
-+    }
-+}
-+
- /* Returns this chassis's Chassis record, if it is available. */
- const struct sbrec_chassis *
- chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-@@ -640,6 +711,7 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-             struct ovsdb_idl_index *sbrec_chassis_private_by_name,
-             const struct ovsrec_open_vswitch_table *ovs_table,
-             const struct sbrec_chassis_table *chassis_table,
-+            const struct sbrec_chassis_private_table *chassis_pvt_table,
-             const char *chassis_id,
-             const struct ovsrec_bridge *br_int,
-             const struct sset *transport_zones,
-@@ -668,7 +740,6 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-         bool updated = chassis_update(chassis_rec, ovnsb_idl_txn, &ovs_cfg,
-                                       chassis_id, transport_zones);
- 
--        chassis_info_set_id(&chassis_state, chassis_id);
-         if (!existed || updated) {
-             ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
-                                       "ovn-controller: %s chassis '%s'",
-@@ -676,17 +747,16 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                                       chassis_id);
-         }
- 
--        const struct sbrec_chassis_private *chassis_private_rec =
--            chassis_private_lookup_by_name(sbrec_chassis_private_by_name,
--                                           chassis_id);
--        if (!chassis_private_rec && ovnsb_idl_txn) {
--            chassis_private_rec = sbrec_chassis_private_insert(ovnsb_idl_txn);
--            sbrec_chassis_private_set_name(chassis_private_rec,
--                                           chassis_id);
--            sbrec_chassis_private_set_chassis(chassis_private_rec,
--                                              chassis_rec);
-+        *chassis_private =
-+            chassis_private_get_record(ovnsb_idl_txn,
-+                                       sbrec_chassis_private_by_name,
-+                                       chassis_pvt_table, chassis_rec);
-+
-+        if (*chassis_private) {
-+            chassis_private_update(*chassis_private, chassis_rec, chassis_id);
-         }
--        *chassis_private = chassis_private_rec;
-+
-+        chassis_info_set_id(&chassis_state, chassis_id);
-     }
- 
-     ovs_chassis_cfg_destroy(&ovs_cfg);
-diff --git a/controller/chassis.h b/controller/chassis.h
-index 81055b4..220f726 100644
---- a/controller/chassis.h
-+++ b/controller/chassis.h
-@@ -26,6 +26,7 @@ struct ovsrec_bridge;
- struct ovsrec_open_vswitch_table;
- struct sbrec_chassis;
- struct sbrec_chassis_table;
-+struct sbrec_chassis_private_table;
- struct sset;
- struct eth_addr;
- struct smap;
-@@ -37,6 +38,7 @@ const struct sbrec_chassis *chassis_run(
-     struct ovsdb_idl_index *sbrec_chassis_private_by_name,
-     const struct ovsrec_open_vswitch_table *,
-     const struct sbrec_chassis_table *,
-+    const struct sbrec_chassis_private_table *,
-     const char *chassis_id, const struct ovsrec_bridge *br_int,
-     const struct sset *transport_zones,
-     const struct sbrec_chassis_private **chassis_private);
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 28ca7a8..6aeeb15 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -179,7 +179,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
-      * chassis */
-     sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "chassisredirect");
-     sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external");
--    if (chassis) {
-+    if (chassis && !sbrec_chassis_is_new(chassis)) {
-         /* This should be mostly redundant with the other clauses for port
-          * bindings, but it allows us to catch any ports that are assigned to
-          * us but should not be.  That way, we can clear their chassis
-@@ -202,9 +202,9 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
-         sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
-                                             &chassis->header_.uuid);
- 
--        /* Monitors Chassis_Private record for current chassis only */
--        sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ,
--                                              chassis->name);
-+        /* Monitors Chassis_Private record for current chassis only. */
-+        sbrec_chassis_private_add_clause_chassis(&chprv, OVSDB_F_EQ,
-+                                                 &chassis->header_.uuid);
-     } else {
-         /* During initialization, we monitor all records in Chassis_Private so
-          * that we don't try to recreate existing ones. */
-@@ -2402,6 +2402,8 @@ main(int argc, char *argv[])
-                 ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
-             const struct sbrec_chassis_table *chassis_table =
-                 sbrec_chassis_table_get(ovnsb_idl_loop.idl);
-+            const struct sbrec_chassis_private_table *chassis_pvt_table =
-+                sbrec_chassis_private_table_get(ovnsb_idl_loop.idl);
-             const struct ovsrec_bridge *br_int =
-                 process_br_int(ovs_idl_txn, bridge_table, ovs_table);
-             const char *chassis_id = get_ovs_chassis_id(ovs_table);
-@@ -2410,7 +2412,8 @@ main(int argc, char *argv[])
-             if (chassis_id) {
-                 chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
-                                       sbrec_chassis_private_by_name,
--                                      ovs_table, chassis_table, chassis_id,
-+                                      ovs_table, chassis_table,
-+                                      chassis_pvt_table, chassis_id,
-                                       br_int, &transport_zones,
-                                       &chassis_private);
-             }
-diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
-index f2faf1f..812946b 100644
---- a/tests/ovn-controller.at
-+++ b/tests/ovn-controller.at
-@@ -195,6 +195,15 @@ OVS_WAIT_UNTIL([
-     chassis_id=$(ovn-sbctl get Chassis "${sysid}" name)
-     test "${sysid}" = "${chassis_id}"
- ])
-+OVS_WAIT_UNTIL([
-+    chassis_id=$(ovn-sbctl get Chassis_Private "${sysid}" name)
-+    test "${sysid}" = "${chassis_id}"
-+])
-+
-+# Only one Chassis_Private record should exist.
-+OVS_WAIT_UNTIL([
-+    test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1
-+])
- 
- # Simulate system-id changing while ovn-controller is disconnected from the
- # SB.
-@@ -212,6 +221,15 @@ OVS_WAIT_UNTIL([
-     chassis_id=$(ovn-sbctl get Chassis "${sysid}" name)
-     test "${sysid}" = "${chassis_id}"
- ])
-+OVS_WAIT_UNTIL([
-+    chassis_id=$(ovn-sbctl get Chassis_Private "${sysid}" name)
-+    test "${sysid}" = "${chassis_id}"
-+])
-+
-+# Only one Chassis_Private record should exist.
-+OVS_WAIT_UNTIL([
-+    test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1
-+])
- 
- # Gracefully terminate daemons
- OVN_CLEANUP_SBOX([hv])
-diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
-index 04e082c..d3eec91 100644
---- a/utilities/ovn-sbctl.c
-+++ b/utilities/ovn-sbctl.c
-@@ -1391,6 +1391,9 @@ cmd_set_ssl(struct ctl_context *ctx)
- static const struct ctl_table_class tables[SBREC_N_TABLES] = {
-     [SBREC_TABLE_CHASSIS].row_ids[0] = {&sbrec_chassis_col_name, NULL, NULL},
- 
-+    [SBREC_TABLE_CHASSIS_PRIVATE].row_ids[0]
-+    = {&sbrec_chassis_private_col_name, NULL, NULL},
-+
-     [SBREC_TABLE_DATAPATH_BINDING].row_ids
-      = {{&sbrec_datapath_binding_col_external_ids, "name", NULL},
-         {&sbrec_datapath_binding_col_external_ids, "name2", NULL},
--- 
-1.8.3.1
-
diff --git a/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch b/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch
deleted file mode 100644
index 6410d70..0000000
--- a/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch
+++ /dev/null
@@ -1,745 +0,0 @@
-From dc36d5ee7b510e9b258037be454460dea53c00a5 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 18 Aug 2020 20:16:07 +0530
-Subject: [PATCH 2/2] ovn-controller: Persist the conjunction ids allocated for
- conjuctive matches.
-
-For a logical flow which results in conjuctive OF matches, we are not persisting
-the allocated conjunction ids for it. There are few side effects because of this.
-  - When a port group or address set gets modified, the logical flows which references
-    these port groups/address sets gets reprocessed again and the resulting OpenvSwitch flows
-    with conjunctive matches gets modified in the vswitchd if the conjunction id changes.
-
-  - And because of this there is small probability of a packet getting dropped when the
-    OF flows gets updated with different conjunction ids.
-
-This patch fixes this issue by persisting the conjunction ids. Earlier, logical flow caching
-support was added [1] to ovn-controller and then reverted [2] later due to some issues.
-
-This patch takes the lflow caching approach to persist the conjunction ids. But it only
-creates the cache for logical flows which result in conjunctive matches. And it doesn't
-cache the expr tree.
-
-The lflow caching is made configurable and enabled by default. Any user can disable caching
-by setting 'ovn-enable-lflow-cache' to 'false' in the OVS db.
- - ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
-
-Note: Changing the option 'ovn-enable-lflow-cache' doesn't result in full recompute of
-I-P engine. But ovn-controller updates the chassis.other_config column. And when it
-receives the update2/update3 message, this results in full recompute (as any chassis
-changes results in full recompute). This is the case with all the config options in OVS db.
-
-An upcoming patch series, will attempt to add back the expr caching (addressing all the issues.)
-
-[1] - 8795bec737b("ovn-controller: Cache logical flow expr tree for each lflow.)
-[2] - 065bcf46218("ovn-controller: Revert lflow expr caching")
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1858878
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit 2662498bfd13ccb13defd1c800f449be7e271abe)
-Conflicts:
-	controller/lflow.c
-
-Change-Id: I8a8de80e6bc3f7d9935266fbf466c2aab3e30c8c
----
- controller/chassis.c        |  22 +++++-
- controller/lflow.c          | 118 ++++++++++++++++++++++++++++++-
- controller/lflow.h          |   8 ++-
- controller/ovn-controller.c |  97 +++++++++++++++++++++++---
- tests/ovn.at                | 135 ++++++++++++++++++++++++++++++++++++
- 5 files changed, 363 insertions(+), 17 deletions(-)
-
-diff --git a/controller/chassis.c b/controller/chassis.c
-index 823116926..e77fb0849 100644
---- a/controller/chassis.c
-+++ b/controller/chassis.c
-@@ -86,6 +86,7 @@ struct ovs_chassis_cfg {
-     const char *cms_options;
-     const char *monitor_all;
-     const char *chassis_macs;
-+    const char *enable_lflow_cache;
- 
-     /* Set of encap types parsed from the 'ovn-encap-type' external-id. */
-     struct sset encap_type_set;
-@@ -165,6 +166,12 @@ get_monitor_all(const struct smap *ext_ids)
-     return smap_get_def(ext_ids, "ovn-monitor-all", "false");
- }
- 
-+static const char *
-+get_enable_lflow_cache(const struct smap *ext_ids)
-+{
-+    return smap_get_def(ext_ids, "ovn-enable-lflow-cache", "true");
-+}
-+
- static const char *
- get_encap_csum(const struct smap *ext_ids)
- {
-@@ -284,6 +291,7 @@ chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table,
-     ovs_cfg->cms_options = get_cms_options(&cfg->external_ids);
-     ovs_cfg->monitor_all = get_monitor_all(&cfg->external_ids);
-     ovs_cfg->chassis_macs = get_chassis_mac_mappings(&cfg->external_ids);
-+    ovs_cfg->enable_lflow_cache = get_enable_lflow_cache(&cfg->external_ids);
- 
-     if (!chassis_parse_ovs_encap_type(encap_type, &ovs_cfg->encap_type_set)) {
-         return false;
-@@ -310,12 +318,14 @@ static void
- chassis_build_other_config(struct smap *config, const char *bridge_mappings,
-                            const char *datapath_type, const char *cms_options,
-                            const char *monitor_all, const char *chassis_macs,
--                           const char *iface_types, bool is_interconn)
-+                           const char *iface_types,
-+                           const char *enable_lflow_cache, bool is_interconn)
- {
-     smap_replace(config, "ovn-bridge-mappings", bridge_mappings);
-     smap_replace(config, "datapath-type", datapath_type);
-     smap_replace(config, "ovn-cms-options", cms_options);
-     smap_replace(config, "ovn-monitor-all", monitor_all);
-+    smap_replace(config, "ovn-enable-lflow-cache", enable_lflow_cache);
-     smap_replace(config, "iface-types", iface_types);
-     smap_replace(config, "ovn-chassis-mac-mappings", chassis_macs);
-     smap_replace(config, "is-interconn", is_interconn ? "true" : "false");
-@@ -330,6 +340,7 @@ chassis_other_config_changed(const char *bridge_mappings,
-                              const char *cms_options,
-                              const char *monitor_all,
-                              const char *chassis_macs,
-+                             const char *enable_lflow_cache,
-                              const struct ds *iface_types,
-                              bool is_interconn,
-                              const struct sbrec_chassis *chassis_rec)
-@@ -362,6 +373,13 @@ chassis_other_config_changed(const char *bridge_mappings,
-         return true;
-     }
- 
-+    const char *chassis_enable_lflow_cache =
-+        get_enable_lflow_cache(&chassis_rec->other_config);
-+
-+    if (strcmp(enable_lflow_cache, chassis_enable_lflow_cache)) {
-+        return true;
-+    }
-+
-     const char *chassis_mac_mappings =
-         get_chassis_mac_mappings(&chassis_rec->other_config);
-     if (strcmp(chassis_macs, chassis_mac_mappings)) {
-@@ -586,6 +604,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
-                                      ovs_cfg->cms_options,
-                                      ovs_cfg->monitor_all,
-                                      ovs_cfg->chassis_macs,
-+                                     ovs_cfg->enable_lflow_cache,
-                                      &ovs_cfg->iface_types,
-                                      ovs_cfg->is_interconn,
-                                      chassis_rec)) {
-@@ -598,6 +617,7 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
-                                    ovs_cfg->monitor_all,
-                                    ovs_cfg->chassis_macs,
-                                    ds_cstr_ro(&ovs_cfg->iface_types),
-+                                   ovs_cfg->enable_lflow_cache,
-                                    ovs_cfg->is_interconn);
-         sbrec_chassis_verify_other_config(chassis_rec);
-         sbrec_chassis_set_other_config(chassis_rec, &other_config);
-diff --git a/controller/lflow.c b/controller/lflow.c
-index 151561210..c2f939f90 100644
---- a/controller/lflow.c
-+++ b/controller/lflow.c
-@@ -269,6 +269,70 @@ lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
-     free(lfrn);
- }
- 
-+/* Represents an lflow cache which
-+ *  - stores the conjunction id offset if the lflow matches
-+ *    results in conjunctive OpenvSwitch flows.
-+ */
-+struct lflow_cache {
-+    struct hmap_node node;
-+    struct uuid lflow_uuid; /* key */
-+    uint32_t conj_id_ofs;
-+};
-+
-+static struct lflow_cache *
-+lflow_cache_add(struct hmap *lflow_cache_map,
-+               const struct sbrec_logical_flow *lflow)
-+{
-+    struct lflow_cache *lc = xmalloc(sizeof *lc);
-+    lc->lflow_uuid = lflow->header_.uuid;
-+    lc->conj_id_ofs = 0;
-+    hmap_insert(lflow_cache_map, &lc->node, uuid_hash(&lc->lflow_uuid));
-+    return lc;
-+}
-+
-+static struct lflow_cache *
-+lflow_cache_get(struct hmap *lflow_cache_map,
-+                const struct sbrec_logical_flow *lflow)
-+{
-+    struct lflow_cache *lc;
-+    size_t hash = uuid_hash(&lflow->header_.uuid);
-+    HMAP_FOR_EACH_WITH_HASH (lc, node, hash, lflow_cache_map) {
-+        if (uuid_equals(&lc->lflow_uuid, &lflow->header_.uuid)) {
-+            return lc;
-+        }
-+    }
-+
-+    return NULL;
-+}
-+
-+static void
-+lflow_cache_delete(struct hmap *lflow_cache_map,
-+                   const struct sbrec_logical_flow *lflow)
-+{
-+    struct lflow_cache *lc = lflow_cache_get(lflow_cache_map, lflow);
-+    if (lc) {
-+        hmap_remove(lflow_cache_map, &lc->node);
-+        free(lc);
-+    }
-+}
-+
-+void
-+lflow_cache_init(struct hmap *lflow_cache_map)
-+{
-+    hmap_init(lflow_cache_map);
-+}
-+
-+void
-+lflow_cache_destroy(struct hmap *lflow_cache_map)
-+{
-+    struct lflow_cache *lc;
-+    HMAP_FOR_EACH_POP (lc, node, lflow_cache_map) {
-+        free(lc);
-+    }
-+
-+    hmap_destroy(lflow_cache_map);
-+}
-+
- /* Adds the logical flows from the Logical_Flow table to flow tables. */
- static void
- add_logical_flows(struct lflow_ctx_in *l_ctx_in,
-@@ -306,6 +370,7 @@ add_logical_flows(struct lflow_ctx_in *l_ctx_in,
-             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
-             VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow "
-                         UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
-+            l_ctx_out->conj_id_overflow = true;
-         }
-     }
- 
-@@ -355,6 +420,9 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
-             /* Delete entries from lflow resource reference. */
-             lflow_resource_destroy_lflow(l_ctx_out->lfrr,
-                                          &lflow->header_.uuid);
-+            if (l_ctx_out->lflow_cache_map) {
-+                lflow_cache_delete(l_ctx_out->lflow_cache_map, lflow);
-+            }
-         }
-     }
- 
-@@ -382,6 +450,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
-                                        &nd_ra_opts, &controller_event_opts,
-                                        l_ctx_in, l_ctx_out)) {
-                 ret = false;
-+                l_ctx_out->conj_id_overflow = true;
-                 break;
-             }
-         }
-@@ -467,6 +536,7 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
-                                    &nd_ra_opts, &controller_event_opts,
-                                    l_ctx_in, l_ctx_out)) {
-             ret = false;
-+            l_ctx_out->conj_id_overflow = true;
-             break;
-         }
-         *changed = true;
-@@ -652,13 +722,41 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
-     ovnacts_free(ovnacts.data, ovnacts.size);
-     ofpbuf_uninit(&ovnacts);
- 
-+    uint32_t conj_id_ofs = *l_ctx_out->conj_id_ofs;
-+    if (n_conjs) {
-+        if (l_ctx_out->lflow_cache_map) {
-+            struct lflow_cache *lc =
-+                lflow_cache_get(l_ctx_out->lflow_cache_map, lflow);
-+            if (!lc) {
-+                lc = lflow_cache_add(l_ctx_out->lflow_cache_map, lflow);
-+            }
-+
-+            if (!lc->conj_id_ofs) {
-+                lc->conj_id_ofs = *l_ctx_out->conj_id_ofs;
-+                if (!update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs)) {
-+                    lc->conj_id_ofs = 0;
-+                    expr_matches_destroy(&matches);
-+                    return false;
-+                }
-+            }
-+
-+            conj_id_ofs = lc->conj_id_ofs;
-+        } else {
-+            /* lflow caching is disabled. */
-+            if (!update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs)) {
-+                expr_matches_destroy(&matches);
-+                return false;
-+            }
-+        }
-+    }
-+
-     /* Prepare the OpenFlow matches for adding to the flow table. */
-     struct expr_match *m;
-     HMAP_FOR_EACH (m, hmap_node, &matches) {
-         match_set_metadata(&m->match,
-                            htonll(lflow->logical_datapath->tunnel_key));
-         if (m->match.wc.masks.conj_id) {
--            m->match.flow.conj_id += *l_ctx_out->conj_id_ofs;
-+            m->match.flow.conj_id += conj_id_ofs;
-         }
-         if (datapath_is_switch(ldp)) {
-             unsigned int reg_index
-@@ -693,7 +791,7 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
-                 struct ofpact_conjunction *dst;
- 
-                 dst = ofpact_put_CONJUNCTION(&conj);
--                dst->id = src->id + *l_ctx_out->conj_id_ofs;
-+                dst->id = src->id + conj_id_ofs;
-                 dst->clause = src->clause;
-                 dst->n_clauses = src->n_clauses;
-             }
-@@ -708,7 +806,7 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
-     /* Clean up. */
-     expr_matches_destroy(&matches);
-     ofpbuf_uninit(&ofpacts);
--    return update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs);
-+    return true;
- }
- 
- static void
-@@ -858,6 +956,19 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out)
- {
-     COVERAGE_INC(lflow_run);
- 
-+    /* when lflow_run is called, it's possible that some of the logical flows
-+     * are deleted. We need to delete the lflow cache for these
-+     * lflows (if present), otherwise, they will not be deleted at all. */
-+    if (l_ctx_out->lflow_cache_map) {
-+        const struct sbrec_logical_flow *lflow;
-+        SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow,
-+                                                l_ctx_in->logical_flow_table) {
-+            if (sbrec_logical_flow_is_deleted(lflow)) {
-+                lflow_cache_delete(l_ctx_out->lflow_cache_map, lflow);
-+            }
-+        }
-+    }
-+
-     add_logical_flows(l_ctx_in, l_ctx_out);
-     add_neighbor_flows(l_ctx_in->sbrec_port_binding_by_name,
-                        l_ctx_in->mac_binding_table, l_ctx_in->local_datapaths,
-@@ -914,6 +1025,7 @@ lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
-                                    &nd_ra_opts, &controller_event_opts,
-                                    l_ctx_in, l_ctx_out)) {
-             handled = false;
-+            l_ctx_out->conj_id_overflow = true;
-             break;
-         }
-     }
-diff --git a/controller/lflow.h b/controller/lflow.h
-index ae02eaf5e..c66b318e9 100644
---- a/controller/lflow.h
-+++ b/controller/lflow.h
-@@ -141,12 +141,13 @@ struct lflow_ctx_out {
-     struct ovn_extend_table *group_table;
-     struct ovn_extend_table *meter_table;
-     struct lflow_resource_ref *lfrr;
--    struct hmap *lflow_expr_cache;
-+    struct hmap *lflow_cache_map;
-     uint32_t *conj_id_ofs;
-+    bool conj_id_overflow;
- };
- 
- void lflow_init(void);
--void lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *);
-+void  lflow_run(struct lflow_ctx_in *, struct lflow_ctx_out *);
- bool lflow_handle_changed_flows(struct lflow_ctx_in *, struct lflow_ctx_out *);
- bool lflow_handle_changed_ref(enum ref_type, const char *ref_name,
-                               struct lflow_ctx_in *, struct lflow_ctx_out *,
-@@ -159,7 +160,8 @@ void lflow_handle_changed_neighbors(
- 
- void lflow_destroy(void);
- 
--void lflow_expr_destroy(struct hmap *lflow_expr_cache);
-+void lflow_cache_init(struct hmap *);
-+void lflow_cache_destroy(struct hmap *);
- 
- bool lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *,
-                                   struct lflow_ctx_in *,
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 6aeeb15f4..3531b187f 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -76,6 +76,7 @@ static unixctl_cb_func cluster_state_reset_cmd;
- static unixctl_cb_func debug_pause_execution;
- static unixctl_cb_func debug_resume_execution;
- static unixctl_cb_func debug_status_execution;
-+static unixctl_cb_func flush_lflow_cache;
- 
- #define DEFAULT_BRIDGE_NAME "br-int"
- #define DEFAULT_PROBE_INTERVAL_MSEC 5000
-@@ -462,7 +463,8 @@ get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl)
-  * updates 'sbdb_idl' with that pointer. */
- static void
- update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
--             bool *monitor_all_p, bool *reset_ovnsb_idl_min_index)
-+             bool *monitor_all_p, bool *reset_ovnsb_idl_min_index,
-+             bool *enable_lflow_cache)
- {
-     const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
-     if (!cfg) {
-@@ -498,6 +500,11 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
-         ovsdb_idl_reset_min_index(ovnsb_idl);
-         *reset_ovnsb_idl_min_index = false;
-     }
-+
-+    if (enable_lflow_cache != NULL) {
-+        *enable_lflow_cache =
-+            smap_get_bool(&cfg->external_ids, "ovn-enable-lflow-cache", true);
-+    }
- }
- 
- static void
-@@ -788,6 +795,10 @@ enum ovs_engine_node {
-     OVS_NODES
- #undef OVS_NODE
- 
-+struct controller_engine_ctx {
-+    bool enable_lflow_cache;
-+};
-+
- struct ed_type_ofctrl_is_connected {
-     bool connected;
- };
-@@ -1519,6 +1530,12 @@ physical_flow_changes_ovs_iface_handler(struct engine_node *node, void *data)
-     return true;
- }
- 
-+struct flow_output_persistent_data {
-+    uint32_t conj_id_ofs;
-+    struct hmap lflow_cache_map;
-+    bool lflow_cache_enabled;
-+};
-+
- struct ed_type_flow_output {
-     /* desired flows */
-     struct ovn_desired_flow_table flow_table;
-@@ -1526,10 +1543,12 @@ struct ed_type_flow_output {
-     struct ovn_extend_table group_table;
-     /* meter ids for QoS */
-     struct ovn_extend_table meter_table;
--    /* conjunction id offset */
--    uint32_t conj_id_ofs;
-     /* lflow resource cross reference */
-     struct lflow_resource_ref lflow_resource_ref;
-+
-+    /* Data which is persistent and not cleared during
-+     * full recompute. */
-+    struct flow_output_persistent_data pd;
- };
- 
- static void init_physical_ctx(struct engine_node *node,
-@@ -1680,7 +1699,13 @@ static void init_lflow_ctx(struct engine_node *node,
-     l_ctx_out->group_table = &fo->group_table;
-     l_ctx_out->meter_table = &fo->meter_table;
-     l_ctx_out->lfrr = &fo->lflow_resource_ref;
--    l_ctx_out->conj_id_ofs = &fo->conj_id_ofs;
-+    l_ctx_out->conj_id_ofs = &fo->pd.conj_id_ofs;
-+    if (fo->pd.lflow_cache_enabled) {
-+        l_ctx_out->lflow_cache_map = &fo->pd.lflow_cache_map;
-+    } else {
-+        l_ctx_out->lflow_cache_map = NULL;
-+    }
-+    l_ctx_out->conj_id_overflow = false;
- }
- 
- static void *
-@@ -1692,8 +1717,10 @@ en_flow_output_init(struct engine_node *node OVS_UNUSED,
-     ovn_desired_flow_table_init(&data->flow_table);
-     ovn_extend_table_init(&data->group_table);
-     ovn_extend_table_init(&data->meter_table);
--    data->conj_id_ofs = 1;
-+    data->pd.conj_id_ofs = 1;
-     lflow_resource_init(&data->lflow_resource_ref);
-+    lflow_cache_init(&data->pd.lflow_cache_map);
-+    data->pd.lflow_cache_enabled = true;
-     return data;
- }
- 
-@@ -1705,6 +1732,7 @@ en_flow_output_cleanup(void *data)
-     ovn_extend_table_destroy(&flow_output_data->group_table);
-     ovn_extend_table_destroy(&flow_output_data->meter_table);
-     lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
-+    lflow_cache_destroy(&flow_output_data->pd.lflow_cache_map);
- }
- 
- static void
-@@ -1738,7 +1766,6 @@ en_flow_output_run(struct engine_node *node, void *data)
-     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
-     struct ovn_extend_table *group_table = &fo->group_table;
-     struct ovn_extend_table *meter_table = &fo->meter_table;
--    uint32_t *conj_id_ofs = &fo->conj_id_ofs;
-     struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
- 
-     static bool first_run = true;
-@@ -1751,12 +1778,39 @@ en_flow_output_run(struct engine_node *node, void *data)
-         lflow_resource_clear(lfrr);
-     }
- 
--    *conj_id_ofs = 1;
-+    struct controller_engine_ctx *ctrl_ctx = engine_get_context()->client_ctx;
-+    if (fo->pd.lflow_cache_enabled && !ctrl_ctx->enable_lflow_cache) {
-+        lflow_cache_destroy(&fo->pd.lflow_cache_map);
-+        lflow_cache_init(&fo->pd.lflow_cache_map);
-+    }
-+    fo->pd.lflow_cache_enabled = ctrl_ctx->enable_lflow_cache;
-+
-+    if (!fo->pd.lflow_cache_enabled) {
-+        fo->pd.conj_id_ofs = 1;
-+    }
-+
-     struct lflow_ctx_in l_ctx_in;
-     struct lflow_ctx_out l_ctx_out;
-     init_lflow_ctx(node, rt_data, fo, &l_ctx_in, &l_ctx_out);
-     lflow_run(&l_ctx_in, &l_ctx_out);
- 
-+    if (l_ctx_out.conj_id_overflow) {
-+        /* Conjunction ids overflow. There can be many holes in between.
-+         * Destroy lflow cache and call lflow_run() again. */
-+        ovn_desired_flow_table_clear(flow_table);
-+        ovn_extend_table_clear(group_table, false /* desired */);
-+        ovn_extend_table_clear(meter_table, false /* desired */);
-+        lflow_resource_clear(lfrr);
-+        fo->pd.conj_id_ofs = 1;
-+        lflow_cache_destroy(&fo->pd.lflow_cache_map);
-+        lflow_cache_init(&fo->pd.lflow_cache_map);
-+        l_ctx_out.conj_id_overflow = false;
-+        lflow_run(&l_ctx_in, &l_ctx_out);
-+        if (l_ctx_out.conj_id_overflow) {
-+            VLOG_WARN("Conjunction id overflow.");
-+        }
-+    }
-+
-     struct physical_ctx p_ctx;
-     init_physical_ctx(node, rt_data, &p_ctx);
- 
-@@ -2321,6 +2375,8 @@ main(int argc, char *argv[])
- 
-     unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd,
-                              NULL);
-+    unixctl_command_register("flush-lflow-cache", "", 0, 0, flush_lflow_cache,
-+                             &flow_output_data->pd);
- 
-     bool reset_ovnsb_idl_min_index = false;
-     unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
-@@ -2338,6 +2394,10 @@ main(int argc, char *argv[])
-     unsigned int ovs_cond_seqno = UINT_MAX;
-     unsigned int ovnsb_cond_seqno = UINT_MAX;
- 
-+    struct controller_engine_ctx ctrl_engine_ctx = {
-+        .enable_lflow_cache = true
-+    };
-+
-     /* Main loop. */
-     exiting = false;
-     restart = false;
-@@ -2366,7 +2426,8 @@ main(int argc, char *argv[])
-         }
- 
-         update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all,
--                     &reset_ovnsb_idl_min_index);
-+                     &reset_ovnsb_idl_min_index,
-+                     &ctrl_engine_ctx.enable_lflow_cache);
-         update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
-         ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl));
- 
-@@ -2384,7 +2445,8 @@ main(int argc, char *argv[])
- 
-         struct engine_context eng_ctx = {
-             .ovs_idl_txn = ovs_idl_txn,
--            .ovnsb_idl_txn = ovnsb_idl_txn
-+            .ovnsb_idl_txn = ovnsb_idl_txn,
-+            .client_ctx = &ctrl_engine_ctx
-         };
- 
-         engine_set_context(&eng_ctx);
-@@ -2624,7 +2686,8 @@ loop_done:
-     if (!restart) {
-         bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
-         while (!done) {
--            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, NULL);
-+            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl,
-+                         NULL, NULL, NULL);
-             update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
- 
-             struct ovsdb_idl_txn *ovs_idl_txn
-@@ -2854,6 +2917,20 @@ engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
-     unixctl_command_reply(conn, NULL);
- }
- 
-+static void
-+flush_lflow_cache(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
-+                     const char *argv[] OVS_UNUSED, void *arg_)
-+{
-+    VLOG_INFO("User triggered lflow cache flush.");
-+    struct flow_output_persistent_data *fo_pd = arg_;
-+    lflow_cache_destroy(&fo_pd->lflow_cache_map);
-+    lflow_cache_init(&fo_pd->lflow_cache_map);
-+    fo_pd->conj_id_ofs = 1;
-+    engine_set_force_recompute(true);
-+    poll_immediate_wake();
-+    unixctl_command_reply(conn, NULL);
-+}
-+
- static void
- cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                const char *argv[] OVS_UNUSED, void *idl_reset_)
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 7cc1756e1..5a1e59df5 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -21402,6 +21402,141 @@ OVN_CLEANUP([hv1],[hv2])
- 
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- lflow cache for conjunctions])
-+ovn_start
-+net_add n1
-+sim_add hv1
-+
-+as hv1
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+
-+ovn-nbctl ls-add sw0
-+ovn-nbctl lsp-add sw0 sw0-p1
-+ovn-nbctl lsp-set-addresses sw0-p1 "10:14:00:00:00:03 10.0.0.3"
-+ovn-nbctl lsp-set-port-security sw0-p1 "10:14:00:00:00:03 10.0.0.3"
-+
-+ovn-nbctl lsp-add sw0 sw0-p2
-+ovn-nbctl lsp-set-addresses sw0-p2 "10:14:00:00:00:04 10.0.0.4"
-+ovn-nbctl lsp-set-port-security sw0-p2 "10:14:00:00:00:04 10.0.0.4"
-+
-+ovn-nbctl lsp-add sw0 sw0-p3
-+ovn-nbctl lsp-set-addresses sw0-p3 "10:14:00:00:00:05 10.0.0.5"
-+ovn-nbctl lsp-set-port-security sw0-p3 "10:14:00:00:00:05 10.0.0.5"
-+
-+ovn-nbctl lsp-add sw0 sw0-p4
-+ovn-nbctl lsp-set-addresses sw0-p4 "10:14:00:00:00:06 10.0.0.6"
-+ovn-nbctl lsp-set-port-security sw0-p4 "10:14:00:00:00:06 10.0.0.6"
-+
-+as hv1
-+ovs-vsctl -- add-port br-int hv1-vif1 -- \
-+    set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
-+    options:tx_pcap=hv1/vif1-tx.pcap \
-+    options:rxq_pcap=hv1/vif1-rx.pcap \
-+    ofport-request=1
-+ovs-vsctl -- add-port br-int hv1-vif2 -- \
-+    set interface hv1-vif2 external-ids:iface-id=sw0-p2 \
-+    options:tx_pcap=hv1/vif2-tx.pcap \
-+    options:rxq_pcap=hv1/vif2-rx.pcap \
-+    ofport-request=2
-+ovs-vsctl -- add-port br-int hv1-vif3 -- \
-+    set interface hv1-vif3 external-ids:iface-id=sw0-p3 \
-+    options:tx_pcap=hv1/vif3-tx.pcap \
-+    options:rxq_pcap=hv1/vif3-rx.pcap \
-+    ofport-request=3
-+ovs-vsctl -- add-port br-int hv1-vif4 -- \
-+    set interface hv1-vif4 external-ids:iface-id=sw0-p4 \
-+    options:tx_pcap=hv1/vif4-tx.pcap \
-+    options:rxq_pcap=hv1/vif4-rx.pcap \
-+    ofport-request=4
-+
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p1) = xup])
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p2) = xup])
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p3) = xup])
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw0-p4) = xup])
-+
-+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-+ovn-nbctl --wait=hv sync
-+
-+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+
-+# Add sw0-p3 to the port group pg0. The conj_id should be 2.
-+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
-+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+
-+# Add sw0p4 to the port group pg0. The conj_id should be 2.
-+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 sw0-p4
-+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+
-+# Add another ACL with conjunction.
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=2")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")])
-+
-+# Delete tcp ACL.
-+ovn-nbctl acl-del pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82"
-+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")])
-+
-+# Add back the tcp ACL.
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=4")])
-+
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && inport == @pg0 && ip4 && tcp.dst >= 84 && tcp.dst <= 86" allow
-+OVS_WAIT_UNTIL([test 3 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=4")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=5")])
-+
-+ovn-nbctl clear port_group pg0 acls
-+OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+
-+ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-+ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=6")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=7")])
-+
-+# Flush the lflow cache.
-+as hv1 ovn-appctl -t ovn-controller flush-lflow-cache
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")])
-+
-+# Disable lflow caching.
-+
-+as hv1 ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
-+
-+# Wait until ovn-enble-lflow-cache is processed by ovn-controller.
-+OVS_WAIT_UNTIL([
-+    test $(ovn-sbctl get chassis hv1 other_config:ovn-enable-lflow-cache) = '"false"'
-+])
-+
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")])
-+
-+# Remove port sw0-p4 from port group.
-+ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=4")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=5")])
-+
-+as hv1 ovn-appctl -t ovn-controller recompute
-+
-+OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id")])
-+OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=2")])
-+AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep -c "conj_id=3")])
-+
-+OVN_CLEANUP([hv1])
-+
-+AT_CLEANUP
-+
- AT_SETUP([ovn -- Delete Port_Binding and OVS port Incremental Processing])
- ovn_start
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
new file mode 100644
index 0000000..7f4c1c4
--- /dev/null
+++ b/SOURCES/0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
@@ -0,0 +1,217 @@
+From a2271039d49a12390ca3118fb3a90a057577d360 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Fri, 5 Jun 2020 14:00:29 +0530
+Subject: [PATCH 2/4] ovsdb idl: Try committing the pending txn in
+ ovsdb_idl_loop_run.
+
+The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(),
+returns a transaction object (of type 'struct ovsdb_idl_txn').
+The returned transaction object can be NULL if there is a pending
+transaction (loop->committing_txn) in the idl loop object.
+
+Normally the clients of idl library, first call ovsdb_idl_loop_run(),
+then do their own processing and create any idl transactions during
+this processing and then finally call ovsdb_idl_loop_commit_and_wait().
+
+If ovsdb_idl_loop_run() returns NULL transaction object, then much
+of the processing done by the client gets wasted as in the case
+of ovn-controller.
+
+The client (in this case ovn-controller), can skip the processing
+and instead call ovsdb_idl_loop_commit_and_wait() if the transaction
+oject is NULL. But ovn-controller uses IDL tracking and it may
+loose the tracked changes in that run.
+
+This patch tries to improve this scenario, by checking if the
+pending transaction can be committed in the ovsdb_idl_loop_run()
+itself and if the pending transaction is cleared (because of the
+response messages from ovsdb-server due to a transaction message
+in the previous run), ovsdb_idl_loop_run() can return a valid
+transaction object.
+
+CC: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Ben Pfaff <blp@ovn.org>
+---
+ openvswitch-2.13.0/lib/ovsdb-idl.c | 143 +++++++++++++++++++----------
+ 1 file changed, 93 insertions(+), 50 deletions(-)
+
+diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
+index 1535ad7b5..2d351791f 100644
+--- a/openvswitch-2.13.0/lib/ovsdb-idl.c
++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
+@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
+ static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *);
+ static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *);
+ static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *);
++static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
++                                         bool *may_need_wakeup);
+ 
+ static void
+ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
+@@ -5329,6 +5331,12 @@ struct ovsdb_idl_txn *
+ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
+ {
+     ovsdb_idl_run(loop->idl);
++
++    /* See if we can commit the loop->committing_txn. */
++    if (loop->committing_txn) {
++        ovsdb_idl_try_commit_loop_txn(loop, NULL);
++    }
++
+     loop->open_txn = (loop->committing_txn
+                       || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
+                       ? NULL
+@@ -5336,6 +5344,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
+     return loop->open_txn;
+ }
+ 
++/* Attempts to commit the current transaction, if one is open.
++ *
++ * If a transaction was open, in this or a previous iteration of the main loop,
++ * and had not before finished committing (successfully or unsuccessfully), the
++ * return value is one of:
++ *
++ *  1: The transaction committed successfully (or it did not change anything in
++ *     the database).
++ *  0: The transaction failed.
++ * -1: The commit is still in progress.
++ *
++ * Thus, the return value is -1 if the transaction is in progress and otherwise
++ * true for success, false for failure.
++ *
++ * (In the corner case where the IDL sends a transaction to the database and
++ * the database commits it, and the connection between the IDL and the database
++ * drops before the IDL receives the message confirming the commit, this
++ * function can return 0 even though the transaction succeeded.)
++ */
++static int
++ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
++                              bool *may_need_wakeup)
++{
++    if (!loop->committing_txn) {
++        /* Not a meaningful return value: no transaction was in progress. */
++        return 1;
++    }
++
++    int retval;
++    struct ovsdb_idl_txn *txn = loop->committing_txn;
++
++    enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
++    if (status != TXN_INCOMPLETE) {
++        switch (status) {
++        case TXN_TRY_AGAIN:
++            /* We want to re-evaluate the database when it's changed from
++             * the contents that it had when we started the commit.  (That
++             * might have already happened.) */
++            loop->skip_seqno = loop->precommit_seqno;
++            if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno
++                && may_need_wakeup) {
++                *may_need_wakeup = true;
++            }
++            retval = 0;
++            break;
++
++        case TXN_SUCCESS:
++            /* Possibly some work on the database was deferred because no
++             * further transaction could proceed.  Wake up again. */
++            retval = 1;
++            loop->cur_cfg = loop->next_cfg;
++            if (may_need_wakeup) {
++                *may_need_wakeup =  true;
++            }
++            break;
++
++        case TXN_UNCHANGED:
++            retval = 1;
++            loop->cur_cfg = loop->next_cfg;
++            break;
++
++        case TXN_ABORTED:
++        case TXN_NOT_LOCKED:
++        case TXN_ERROR:
++            retval = 0;
++            break;
++
++        case TXN_UNCOMMITTED:
++        case TXN_INCOMPLETE:
++        default:
++            OVS_NOT_REACHED();
++        }
++        ovsdb_idl_txn_destroy(txn);
++        loop->committing_txn = NULL;
++    } else {
++        retval = -1;
++    }
++
++    return retval;
++}
++
+ /* Attempts to commit the current transaction, if one is open, and sets up the
+  * poll loop to wake up when some more work might be needed.
+  *
+@@ -5366,57 +5455,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop)
+         loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
+     }
+ 
+-    struct ovsdb_idl_txn *txn = loop->committing_txn;
+-    int retval;
+-    if (txn) {
+-        enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
+-        if (status != TXN_INCOMPLETE) {
+-            switch (status) {
+-            case TXN_TRY_AGAIN:
+-                /* We want to re-evaluate the database when it's changed from
+-                 * the contents that it had when we started the commit.  (That
+-                 * might have already happened.) */
+-                loop->skip_seqno = loop->precommit_seqno;
+-                if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
+-                    poll_immediate_wake();
+-                }
+-                retval = 0;
+-                break;
+-
+-            case TXN_SUCCESS:
+-                /* Possibly some work on the database was deferred because no
+-                 * further transaction could proceed.  Wake up again. */
+-                retval = 1;
+-                loop->cur_cfg = loop->next_cfg;
+-                poll_immediate_wake();
+-                break;
+-
+-            case TXN_UNCHANGED:
+-                retval = 1;
+-                loop->cur_cfg = loop->next_cfg;
+-                break;
+-
+-            case TXN_ABORTED:
+-            case TXN_NOT_LOCKED:
+-            case TXN_ERROR:
+-                retval = 0;
+-                break;
+-
+-            case TXN_UNCOMMITTED:
+-            case TXN_INCOMPLETE:
+-            default:
+-                OVS_NOT_REACHED();
+-            }
+-            ovsdb_idl_txn_destroy(txn);
+-            loop->committing_txn = NULL;
+-        } else {
+-            retval = -1;
+-        }
+-    } else {
+-        /* Not a meaningful return value: no transaction was in progress. */
+-        retval = 1;
++    bool may_need_wakeup = false;
++    int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup);
++    if (may_need_wakeup) {
++        poll_immediate_wake();
+     }
+-
+     ovsdb_idl_wait(loop->idl);
+ 
+     return retval;
+-- 
+2.26.2
+
diff --git a/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch b/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch
deleted file mode 100644
index 8cfd40b..0000000
--- a/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch
+++ /dev/null
@@ -1,455 +0,0 @@
-From 74b88cd58dcfdb5e80fa2d64a909170b5a24f76a Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Wed, 17 Jun 2020 23:39:29 +0530
-Subject: [PATCH 03/22] Support packet metadata marking for logical router
- policies.
-
-This patch adds a new column 'options' of type smap in the
-Logical_Router_Policy table in the NB DB and supports the key 'pkt_mark'.
-CMS can set a desired value for this key in the 'options' column. When this
-router policy is applied, the packet metadata is marked with the specified
-value (to the NXM_NX_PKT_MARK OVS field).
-
-In the case of Linux, this corresponds to struct sk_buff's "skb_mark"
-member and this mark can be seen by the linux networking subsystem.
-CMS can inspect this value (as an iptables rule or adding an OF flow
-in another ovs bridge) and take appropriate action when the marked packet
-leaves the integration bridge via the patch port.
-
-Change-Id: I05efa5e1d77bb02bf59d28b174151ba33779b8c3
-Requested-at: https://bugzilla.redhat.com/show_bug.cgi?id=1828933
-Requested-by: Alexander Constantinescu <aconstan@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit a123ef0fb8fd03cc586f924cb511fb24a661743a)
----
- NEWS                 |   2 +
- lib/logical-fields.c |   2 +
- northd/ovn-northd.c  |   8 ++
- ovn-nb.ovsschema     |   7 +-
- ovn-nb.xml           |   9 ++
- ovn-sb.xml           |   1 +
- tests/ovn.at         | 291 +++++++++++++++++++++++++++++++++++++++++++
- 7 files changed, 318 insertions(+), 2 deletions(-)
-
-diff --git a/NEWS b/NEWS
-index 30b24e5ad..8abdc95b9 100644
---- a/NEWS
-+++ b/NEWS
-@@ -7,6 +7,8 @@ OVN v20.06.2 - 21 Aug 2020
- 
- OVN v20.06.1 - 08 Jul 2020
- --------------------------
-+   - Added packet marking support for traffic routed with
-+     a routing policy.
- 
- OVN v20.06.0 - 08 Jun 2020
- --------------------------
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index a007085b3..8ad56aa53 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -254,6 +254,8 @@ ovn_init_symtab(struct shash *symtab)
-     expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
-     expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
- 
-+    expr_symtab_add_field(symtab, "pkt.mark", MFF_PKT_MARK, NULL, false);
-+
-     expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU);
- }
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index a665d52e9..2b1257114 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -7386,6 +7386,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
-                          rule->priority, rule->nexthop);
-             return;
-         }
-+        uint32_t pkt_mark = smap_get_int(&rule->options, "pkt_mark", 0);
-+        if (pkt_mark) {
-+            ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
-+        }
-         bool is_ipv4 = strchr(rule->nexthop, '.') ? true : false;
-         ds_put_format(&actions, "%s = %s; "
-                       "%s = %s; "
-@@ -7403,6 +7407,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
-     } else if (!strcmp(rule->action, "drop")) {
-         ds_put_cstr(&actions, "drop;");
-     } else if (!strcmp(rule->action, "allow")) {
-+        uint32_t pkt_mark = smap_get_int(&rule->options, "pkt_mark", 0);
-+        if (pkt_mark) {
-+            ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark);
-+        }
-         ds_put_cstr(&actions, "next;");
-     }
-     ds_put_format(&match, "%s", rule->match);
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index a06972aa0..da9af7157 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
--    "version": "5.23.0",
--    "cksum": "111023208 25806",
-+    "version": "5.24.0",
-+    "cksum": "1092394564 25961",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -379,6 +379,9 @@
-                     "key": {"type": "string",
-                             "enum": ["set", ["allow", "drop", "reroute"]]}}},
-                 "nexthop": {"type": {"key": "string", "min": 0, "max": 1}},
-+                "options": {
-+                    "type": {"key": "string", "value": "string",
-+                             "min": 0, "max": "unlimited"}},
-                 "external_ids": {
-                     "type": {"key": "string", "value": "string",
-                              "min": 0, "max": "unlimited"}}},
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index 0fdc1592b..02161372a 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -2581,6 +2581,15 @@
-       </p>
-     </column>
- 
-+    <column name="options" key="pkt_mark">
-+      <p>
-+        Marks the packet with the value specified when the router policy
-+        is applied. CMS can inspect this packet marker and take some decisions
-+        if desired. This value is not preserved when the packet goes out on the
-+        wire.
-+      </p>
-+    </column>
-+
-     <group title="Common Columns">
-       <column name="external_ids">
-         See <em>External IDs</em> at the beginning of this document.
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 293b0920c..709cb4c48 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -975,6 +975,7 @@
-         <li><code>xxreg0</code> <code>xxreg1</code></li>
-         <li><code>inport</code> <code>outport</code></li>
-         <li><code>flags.loopback</code></li>
-+        <li><code>pkt.mark</code></li>
-         <li><code>eth.src</code> <code>eth.dst</code> <code>eth.type</code></li>
-         <li><code>vlan.tci</code> <code>vlan.vid</code> <code>vlan.pcp</code> <code>vlan.present</code></li>
-         <li><code>ip.proto</code> <code>ip.dscp</code> <code>ip.ecn</code> <code>ip.ttl</code> <code>ip.frag</code></li>
-diff --git a/tests/ovn.at b/tests/ovn.at
-index be677b663..8ce45823f 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -965,6 +965,23 @@ ip.ttl--;
- ip.ttl
-     Syntax error at end of input expecting `--'.
- 
-+# Packet mark.
-+pkt.mark=1;
-+    formats as pkt.mark = 1;
-+    encodes as set_field:0x1->pkt_mark
-+
-+pkt.mark = 1000;
-+    encodes as set_field:0x3e8->pkt_mark
-+
-+pkt.mark;
-+    Syntax error at `pkt.mark' expecting action.
-+
-+pkt.mark = foo;
-+    Syntax error at `foo' expecting field name.
-+
-+pkt.mark = "foo";
-+    Integer field pkt.mark is not compatible with string constant.
-+
- # load balancing.
- ct_lb;
-     encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat)
-@@ -20199,6 +20216,280 @@ AT_CHECK([ovn-nbctl --if-exists lr-nat-del r1 snat beef:0000::0/ffff:ffff:ffff:f
- AT_CHECK([ovn-nbctl --if-exists lr-nat-del r1 dnat aef0:0000:00::1])
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- Logical router policy packet marking])
-+ovn_start
-+
-+net_add n1
-+sim_add hv1
-+as hv1
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+ovs-vsctl -- add-port br-int hv1-vif1 -- \
-+    set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
-+    options:tx_pcap=hv1/vif1-tx.pcap \
-+    options:rxq_pcap=hv1/vif1-rx.pcap \
-+    ofport-request=1
-+ovs-vsctl -- add-port br-int hv1-vif2 -- \
-+    set interface hv1-vif2 external-ids:iface-id=sw0-port2 \
-+    options:tx_pcap=hv1/vif2-tx.pcap \
-+    options:rxq_pcap=hv1/vif2-rx.pcap \
-+    ofport-request=2
-+
-+as hv1 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=public:br-phys
-+
-+ovn-nbctl ls-add sw0
-+ovn-nbctl lsp-add sw0 sw0-port1
-+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 10.0.0.5"
-+ovn-nbctl lsp-set-port-security sw0-port1 "50:54:00:00:00:03 10.0.0.3 10.0.0.5"
-+
-+ovn-nbctl lsp-add sw0 sw0-port2
-+ovn-nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 aef0::4"
-+
-+ovn-nbctl lr-add lr0
-+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 aef0::1/64
-+ovn-nbctl lsp-add sw0 sw0-lr0
-+ovn-nbctl lsp-set-type sw0-lr0 router
-+ovn-nbctl lsp-set-addresses sw0-lr0 router
-+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
-+
-+ovn-nbctl ls-add public
-+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 bef0::1/64
-+ovn-nbctl lsp-add public public-lr0
-+ovn-nbctl lsp-set-type public-lr0 router
-+ovn-nbctl lsp-set-addresses public-lr0 router
-+ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
-+
-+# localnet port
-+ovn-nbctl lsp-add public ln-public
-+ovn-nbctl lsp-set-type ln-public localnet
-+ovn-nbctl lsp-set-addresses ln-public unknown
-+ovn-nbctl lsp-set-options ln-public network_name=public
-+
-+ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
-+ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24
-+lr0_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0)
-+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip=172.168.0.120 \
-+logical_port=lr0-public mac="10\:54\:00\:00\:00\:03"
-+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip=172.168.0.200 \
-+logical_port=lr0-public mac="10\:54\:00\:00\:00\:04"
-+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:4" \
-+logical_port=lr0-public mac="10\:54\:00\:00\:00\:05"
-+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:5" \
-+logical_port=lr0-public mac="10\:54\:00\:00\:00\:06"
-+ovn-sbctl create mac_binding datapath=$lr0_dp_uuid ip="bef0\:\:6" \
-+logical_port=lr0-public mac="10\:54\:00\:00\:00\:07"
-+
-+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-+ip_prefix="\:\:/64" nexthop="bef0\:\:4" -- add Logical_Router lr0 \
-+static_routes @lrt
-+
-+ovn-nbctl --wait=hv sync
-+
-+# Add logical router policy and set pkt_mark on it.
-+ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow
-+ovn-nbctl lr-policy-add lr0 1000 "ip4.src == 10.0.0.4" allow
-+ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200
-+ovn-nbctl lr-policy-add lr0 2001 "ip6.dst == bef0::5" reroute bef0::6
-+ovn-nbctl lr-policy-add lr0 1001 "ip6" allow
-+
-+
-+pol1=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2000)
-+pol3=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=900)
-+pol4=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2001)
-+pol5=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=1001)
-+
-+ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=100
-+ovn-nbctl set logical_router_policy $pol3 options:pkt_mark=3
-+ovn-nbctl set logical_router_policy $pol4 options:pkt_mark=4
-+ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5
-+ovn-nbctl --wait=hv sync
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x64->NXM_NX_PKT_MARK" -c)
-+])
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x3->NXM_NX_PKT_MARK" -c)
-+])
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x4->NXM_NX_PKT_MARK" -c)
-+])
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x5->NXM_NX_PKT_MARK" -c)
-+])
-+
-+AT_CHECK([as hv1 ovs-ofctl del-flows br-phys])
-+AT_DATA([flows.txt], [dnl
-+table=0, priority=0 actions=NORMAL
-+table=0, priority=200 arp,actions=drop
-+table=0, priority=100, pkt_mark=0x64 actions=drop
-+table=0, priority=100, pkt_mark=0x2 actions=drop
-+table=0, priority=100, pkt_mark=0x3 actions=drop
-+table=0, priority=100, pkt_mark=0x4 actions=drop
-+table=0, priority=100, pkt_mark=0x5 actions=drop
-+])
-+
-+AT_CHECK([as hv1 ovs-ofctl --protocols=OpenFlow13 add-flows br-phys flows.txt])
-+
-+ip_to_hex() {
-+     printf "%02x%02x%02x%02x" "$@"
-+}
-+
-+send_ipv4_pkt() {
-+    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4
-+    local ip_src=$5 ip_dst=$6
-+    packet=${eth_dst}${eth_src}08004500001c0000000040110000${ip_src}${ip_dst}0035111100080000
-+    as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet}
-+}
-+
-+send_icmp6_packet() {
-+    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6
-+
-+    local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst}
-+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000dcb662f00001
-+
-+    as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet}
-+}
-+
-+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \
-+    $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120)
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x64" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep priority=0 | \
-+    grep "n_packets=0" -c)
-+])
-+
-+# Send the pkt from sw0-port2. Packet should not be marked.
-+send_ipv4_pkt hv1 hv1-vif2 505400000004 00000000ff01 \
-+    $(ip_to_hex 10 0 0 4) $(ip_to_hex 172 168 0 120)
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep priority=0 | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x64" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x3" | \
-+    grep "n_packets=0" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x4" | \
-+    grep "n_packets=0" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x5" | \
-+    grep "n_packets=0" -c)
-+])
-+
-+ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=2
-+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \
-+    $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120)
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x2->NXM_NX_PKT_MARK" -c)
-+])
-+
-+AT_CHECK([
-+    test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    grep "load:0x64->NXM_NX_PKT_MARK" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x2" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep priority=0 | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x3" | \
-+    grep "n_packets=0" -c)
-+])
-+
-+# Send with src ip 10.0.0.5. The reroute policy should be hit
-+# and the packet should be marked with 5.
-+send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \
-+    $(ip_to_hex 10 0 0 5) $(ip_to_hex 172 168 0 120)
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x3" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+# Send IPv6 traffic.
-+src_ip6=aef00000000000000000000000000004
-+dst_ip6=bef00000000000000000000000000004
-+
-+send_icmp6_packet hv1 hv1-vif2 505400000004 00000000ff01 ${src_ip6} ${dst_ip6}
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x5" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x4" | \
-+    grep "n_packets=0" -c)
-+])
-+
-+# Send IPv6 packet which hits the reroute policy. Packet should be marked
-+# with 4.
-+
-+src_ip6=aef00000000000000000000000000004
-+dst_ip6=bef00000000000000000000000000005
-+
-+send_icmp6_packet hv1 hv1-vif2 505400000004 00000000ff01 ${src_ip6} ${dst_ip6}
-+
-+OVS_WAIT_UNTIL([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x4" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-phys table=0 | \
-+    grep "priority=100,pkt_mark=0x5" | \
-+    grep "n_packets=1" -c)
-+])
-+
-+OVN_CLEANUP([hv1])
-+AT_CLEANUP
-+
- AT_SETUP([ovn -- Load balancer selection fields])
- AT_KEYWORDS([lb])
- ovn_start
--- 
-2.26.2
-
diff --git a/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch b/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
new file mode 100644
index 0000000..c2856ef
--- /dev/null
+++ b/SOURCES/0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
@@ -0,0 +1,460 @@
+From 2b29d852dc13843c72c3820fb82c1b123f3f77d7 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 28 May 2020 14:32:31 +0200
+Subject: [PATCH 3/4] ovsdb-idl: Avoid inconsistent IDL state with
+ OVSDB_MONITOR_V3.
+
+Assuming an ovsdb client connected to a database using OVSDB_MONITOR_V3
+(i.e., "monitor_cond_since" method) with the initial monitor condition
+MC1.
+
+Assuming the following two transactions are executed on the
+ovsdb-server:
+TXN1: "insert record R1 in table T1"
+TXN2: "insert record R2 in table T2"
+
+If the client's monitor condition MC1 for table T2 matches R2 then the
+client will receive the following update3 message:
+method="update3", "insert record R2 in table T2", last-txn-id=TXN2
+
+At this point, if the presence of the new record R2 in the IDL triggers
+the client to update its monitor condition to MC2 and add a clause for
+table T1 which matches R1, a monitor_cond_change message is sent to the
+server:
+method="monitor_cond_change", "clauses from MC2"
+
+In normal operation the ovsdb-server will reply with a new update3
+message of the form:
+method="update3", "insert record R1 in table T1", last-txn-id=TXN2
+
+However, if the connection drops in the meantime, this last update might
+get lost.
+
+It might happen that during the reconnect a new transaction happens
+that modifies the original record R1:
+TXN3: "modify record R1 in table T1"
+
+When the client reconnects, it will try to perform a fast resync by
+sending:
+method="monitor_cond_since", "clauses from MC2", last-txn-id=TXN2
+
+Because TXN2 is still in the ovsdb-server transaction history, the
+server replies with the changes from the most recent transactions only,
+i.e., TXN3:
+result="true", last-txbb-id=TXN3, "modify record R1 in table T1"
+
+This causes the IDL on the client in to end up in an inconsistent
+state because it has never seen the update that created R1.
+
+Such a scenario is described in:
+https://bugzilla.redhat.com/show_bug.cgi?id=1808580#c22
+
+To avoid this issue, the IDL will now maintain (up to) 3 different
+types of conditions for each DB table:
+- new_cond: condition that has been set by the IDL client but has
+  not yet been sent to the server through monitor_cond_change.
+- req_cond: condition that has been sent to the server but the reply
+  acknowledging the change hasn't been received yet.
+- ack_cond: condition that has been acknowledged by the server.
+
+Whenever the IDL FSM is restarted (e.g., voluntary or involuntary
+disconnect):
+- if there is a known last_id txn-id the code ensures that new_cond
+  will contain the most recent condition set by the IDL client
+  (either req_cond if there was a request in flight, or new_cond
+  if the IDL client set a condition while the IDL was disconnected)
+- if there is no known last_id txn-id the code ensures that ack_cond will
+  contain the most recent conditions set by the IDL client regardless
+  whether they were acked by the server or not.
+
+When monitor_cond_since/monitor_cond requests are sent they will
+always include ack_cond and if new_cond is not NULL a follow up
+monitor_cond_change will be generated afterwards.
+
+On the other hand ovsdb_idl_db_set_condition() will always modify new_cond.
+
+This ensures that updates of type "insert" that happened before the last
+transaction known by the IDL but didn't match old monitor conditions are
+sent upon reconnect if the monitor condition has changed to include them
+in the meantime.
+
+Fixes: 403a6a0cb003 ("ovsdb-idl: Fast resync from server when connection reset.")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
+(cherry picked from upstream OVS commit ae25f8c8fff80a58cd0a15e2d3ae7ab1b4994e48)
+
+Change-Id: I4f3cd43cf69dfe76eb65c9709b759e5062c29e89
+---
+ openvswitch-2.13.0/lib/ovsdb-idl-provider.h |   8 +-
+ openvswitch-2.13.0/lib/ovsdb-idl.c          | 167 +++++++++++++++++---
+ openvswitch-2.13.0/tests/ovsdb-idl.at       |  56 +++++++
+ 3 files changed, 206 insertions(+), 25 deletions(-)
+
+diff --git a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
+index 30d1d08eb..00497d940 100644
+--- a/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
++++ b/openvswitch-2.13.0/lib/ovsdb-idl-provider.h
+@@ -122,8 +122,12 @@ struct ovsdb_idl_table {
+     unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
+     struct ovs_list indexes;    /* Contains "struct ovsdb_idl_index"s */
+     struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */
+-    struct ovsdb_idl_condition condition;
+-    bool cond_changed;
++    struct ovsdb_idl_condition *ack_cond; /* Last condition acked by the
++                                           * server. */
++    struct ovsdb_idl_condition *req_cond; /* Last condition requested to the
++                                           * server. */
++    struct ovsdb_idl_condition *new_cond; /* Latest condition set by the IDL
++                                           * client. */
+ };
+ 
+ struct ovsdb_idl_class {
+diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
+index 2d351791f..8eb421366 100644
+--- a/openvswitch-2.13.0/lib/ovsdb-idl.c
++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
+@@ -240,6 +240,10 @@ static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *,
+                                            struct ovsdb_idl_db *,
+                                            enum ovsdb_idl_monitor_method);
+ static void ovsdb_idl_db_clear(struct ovsdb_idl_db *db);
++static void ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db);
++static void ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db);
++static void ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst,
++                                     struct ovsdb_idl_condition **src);
+ 
+ struct ovsdb_idl {
+     struct ovsdb_idl_db server;
+@@ -424,9 +428,11 @@ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
+             = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]
+             = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
+         table->db = db;
+-        ovsdb_idl_condition_init(&table->condition);
+-        ovsdb_idl_condition_add_clause_true(&table->condition);
+-        table->cond_changed = false;
++        table->ack_cond = NULL;
++        table->req_cond = NULL;
++        table->new_cond = xmalloc(sizeof *table->new_cond);
++        ovsdb_idl_condition_init(table->new_cond);
++        ovsdb_idl_condition_add_clause_true(table->new_cond);
+     }
+     db->monitor_id = json_array_create_2(json_string_create("monid"),
+                                          json_string_create(class->database));
+@@ -558,12 +564,15 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle)
+ static void
+ ovsdb_idl_db_destroy(struct ovsdb_idl_db *db)
+ {
++    struct ovsdb_idl_condition *null_cond = NULL;
+     ovs_assert(!db->txn);
+     ovsdb_idl_db_txn_abort_all(db);
+     ovsdb_idl_db_clear(db);
+     for (size_t i = 0; i < db->class_->n_tables; i++) {
+         struct ovsdb_idl_table *table = &db->tables[i];
+-        ovsdb_idl_condition_destroy(&table->condition);
++        ovsdb_idl_condition_move(&table->ack_cond, &null_cond);
++        ovsdb_idl_condition_move(&table->req_cond, &null_cond);
++        ovsdb_idl_condition_move(&table->new_cond, &null_cond);
+         ovsdb_idl_destroy_indexes(table);
+         shash_destroy(&table->columns);
+         hmap_destroy(&table->rows);
+@@ -692,6 +701,12 @@ ovsdb_idl_send_request(struct ovsdb_idl *idl, struct jsonrpc_msg *request)
+ static void
+ ovsdb_idl_restart_fsm(struct ovsdb_idl *idl)
+ {
++    /* Resync data DB table conditions to avoid missing updates due to
++     * conditions that were in flight or changed locally while the connection
++     * was down.
++     */
++    ovsdb_idl_db_sync_condition(&idl->data);
++
+     ovsdb_idl_send_schema_request(idl, &idl->server);
+     ovsdb_idl_transition(idl, IDL_S_SERVER_SCHEMA_REQUESTED);
+     idl->data.monitoring = OVSDB_IDL_NOT_MONITORING;
+@@ -799,7 +814,9 @@ ovsdb_idl_process_response(struct ovsdb_idl *idl, struct jsonrpc_msg *msg)
+          * do, it's a "monitor_cond_change", which means that the conditional
+          * monitor clauses were updated.
+          *
+-         * If further condition changes were pending, send them now. */
++         * Mark the last requested conditions as acked and if further
++         * condition changes were pending, send them now. */
++        ovsdb_idl_db_ack_condition(&idl->data);
+         ovsdb_idl_send_cond_change(idl);
+         idl->data.cond_seqno++;
+         break;
+@@ -1495,30 +1512,60 @@ ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a,
+ }
+ 
+ static void
+-ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst,
++ovsdb_idl_condition_clone(struct ovsdb_idl_condition **dst,
+                           const struct ovsdb_idl_condition *src)
+ {
+-    ovsdb_idl_condition_init(dst);
++    if (*dst) {
++        ovsdb_idl_condition_destroy(*dst);
++    } else {
++        *dst = xmalloc(sizeof **dst);
++    }
++    ovsdb_idl_condition_init(*dst);
+ 
+-    dst->is_true = src->is_true;
++    (*dst)->is_true = src->is_true;
+ 
+     const struct ovsdb_idl_clause *clause;
+     HMAP_FOR_EACH (clause, hmap_node, &src->clauses) {
+-        ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash);
++        ovsdb_idl_condition_add_clause__(*dst, clause, clause->hmap_node.hash);
+     }
+ }
+ 
++static void
++ovsdb_idl_condition_move(struct ovsdb_idl_condition **dst,
++                         struct ovsdb_idl_condition **src)
++{
++    if (*dst) {
++        ovsdb_idl_condition_destroy(*dst);
++        free(*dst);
++    }
++    *dst = *src;
++    *src = NULL;
++}
++
+ static unsigned int
+ ovsdb_idl_db_set_condition(struct ovsdb_idl_db *db,
+                            const struct ovsdb_idl_table_class *tc,
+                            const struct ovsdb_idl_condition *condition)
+ {
++    struct ovsdb_idl_condition *table_cond;
+     struct ovsdb_idl_table *table = ovsdb_idl_db_table_from_class(db, tc);
+     unsigned int seqno = db->cond_seqno;
+-    if (!ovsdb_idl_condition_equals(condition, &table->condition)) {
+-        ovsdb_idl_condition_destroy(&table->condition);
+-        ovsdb_idl_condition_clone(&table->condition, condition);
+-        db->cond_changed = table->cond_changed = true;
++
++    /* Compare the new condition to the last known condition which can be
++     * either "new" (not sent yet), "requested" or "acked", in this order.
++     */
++    if (table->new_cond) {
++        table_cond = table->new_cond;
++    } else if (table->req_cond) {
++        table_cond = table->req_cond;
++    } else {
++        table_cond = table->ack_cond;
++    }
++    ovs_assert(table_cond);
++
++    if (!ovsdb_idl_condition_equals(condition, table_cond)) {
++        ovsdb_idl_condition_clone(&table->new_cond, condition);
++        db->cond_changed = true;
+         poll_immediate_wake();
+         return seqno + 1;
+     }
+@@ -1563,9 +1610,8 @@ ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd)
+ }
+ 
+ static struct json *
+-ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table)
++ovsdb_idl_create_cond_change_req(const struct ovsdb_idl_condition *cond)
+ {
+-    const struct ovsdb_idl_condition *cond = &table->condition;
+     struct json *monitor_cond_change_request = json_object_create();
+     struct json *cond_json = ovsdb_idl_condition_to_json(cond);
+ 
+@@ -1585,8 +1631,12 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
+     for (size_t i = 0; i < db->class_->n_tables; i++) {
+         struct ovsdb_idl_table *table = &db->tables[i];
+ 
+-        if (table->cond_changed) {
+-            struct json *req = ovsdb_idl_create_cond_change_req(table);
++        /* Always use the most recent conditions set by the IDL client when
++         * requesting monitor_cond_change, i.e., table->new_cond.
++         */
++        if (table->new_cond) {
++            struct json *req =
++                ovsdb_idl_create_cond_change_req(table->new_cond);
+             if (req) {
+                 if (!monitor_cond_change_requests) {
+                     monitor_cond_change_requests = json_object_create();
+@@ -1595,7 +1645,11 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
+                              table->class_->name,
+                              json_array_create_1(req));
+             }
+-            table->cond_changed = false;
++            /* Mark the new condition as requested by moving it to req_cond.
++             * If there's already requested condition that's a bug.
++             */
++            ovs_assert(table->req_cond == NULL);
++            ovsdb_idl_condition_move(&table->req_cond, &table->new_cond);
+         }
+     }
+ 
+@@ -1610,6 +1664,73 @@ ovsdb_idl_db_compose_cond_change(struct ovsdb_idl_db *db)
+     return jsonrpc_create_request("monitor_cond_change", params, NULL);
+ }
+ 
++/* Marks all requested table conditions in 'db' as acked by the server.
++ * It should be called when the server replies to monitor_cond_change
++ * requests.
++ */
++static void
++ovsdb_idl_db_ack_condition(struct ovsdb_idl_db *db)
++{
++    for (size_t i = 0; i < db->class_->n_tables; i++) {
++        struct ovsdb_idl_table *table = &db->tables[i];
++
++        if (table->req_cond) {
++            ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond);
++        }
++    }
++}
++
++/* Should be called when the IDL fsm is restarted and resyncs table conditions
++ * based on the state the DB is in:
++ * - if a non-zero last_id is available for the DB then upon reconnect
++ *   the IDL should first request acked conditions to avoid missing updates
++ *   about records that were added before the transaction with
++ *   txn-id == last_id. If there were requested condition changes in flight
++ *   (i.e., req_cond not NULL) and the IDL client didn't set new conditions
++ *   (i.e., new_cond is NULL) then move req_cond to new_cond to trigger a
++ *   follow up monitor_cond_change request.
++ * - if there's no last_id available for the DB then it's safe to use the
++ *   latest conditions set by the IDL client even if they weren't acked yet.
++ */
++static void
++ovsdb_idl_db_sync_condition(struct ovsdb_idl_db *db)
++{
++    bool ack_all = uuid_is_zero(&db->last_id);
++
++    db->cond_changed = false;
++    for (size_t i = 0; i < db->class_->n_tables; i++) {
++        struct ovsdb_idl_table *table = &db->tables[i];
++
++        /* When monitor_cond_since requests will be issued, the
++         * table->ack_cond condition will be added to the "where" clause".
++         * Follow up monitor_cond_change requests will use table->new_cond.
++         */
++        if (ack_all) {
++            if (table->new_cond) {
++                ovsdb_idl_condition_move(&table->req_cond, &table->new_cond);
++            }
++
++            if (table->req_cond) {
++                ovsdb_idl_condition_move(&table->ack_cond, &table->req_cond);
++            }
++        } else {
++            /* If there was no "unsent" condition but instead a
++             * monitor_cond_change request was in flight, move table->req_cond
++             * to table->new_cond and set db->cond_changed to trigger a new
++             * monitor_cond_change request.
++             *
++             * However, if a new condition has been set by the IDL client,
++             * monitor_cond_change will be sent anyway and will use the most
++             * recent table->new_cond so there's no need to update it here.
++             */
++            if (table->req_cond && !table->new_cond) {
++                ovsdb_idl_condition_move(&table->new_cond, &table->req_cond);
++                db->cond_changed = true;
++            }
++        }
++    }
++}
++
+ static void
+ ovsdb_idl_send_cond_change(struct ovsdb_idl *idl)
+ {
+@@ -2064,13 +2185,15 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db,
+             monitor_request = json_object_create();
+             json_object_put(monitor_request, "columns", columns);
+ 
+-            const struct ovsdb_idl_condition *cond = &table->condition;
++            /* Always use acked conditions when requesting
++             * monitor_cond/monitor_cond_since.
++             */
++            const struct ovsdb_idl_condition *cond = table->ack_cond;
+             if ((monitor_method == OVSDB_IDL_MM_MONITOR_COND ||
+                  monitor_method == OVSDB_IDL_MM_MONITOR_COND_SINCE) &&
+-                !ovsdb_idl_condition_is_true(cond)) {
++                cond && !ovsdb_idl_condition_is_true(cond)) {
+                 json_object_put(monitor_request, "where",
+                                 ovsdb_idl_condition_to_json(cond));
+-                table->cond_changed = false;
+             }
+             json_object_put(monitor_requests, tc->name,
+                             json_array_create_1(monitor_request));
+@@ -2078,8 +2201,6 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, struct ovsdb_idl_db *db,
+     }
+     free_schema(schema);
+ 
+-    db->cond_changed = false;
+-
+     struct json *params = json_array_create_3(
+                               json_string_create(db->class_->database),
+                               json_clone(db->monitor_id),
+diff --git a/openvswitch-2.13.0/tests/ovsdb-idl.at b/openvswitch-2.13.0/tests/ovsdb-idl.at
+index cc38d69c1..a5ca96646 100644
+--- a/openvswitch-2.13.0/tests/ovsdb-idl.at
++++ b/openvswitch-2.13.0/tests/ovsdb-idl.at
+@@ -1814,3 +1814,59 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY],
+ 
+ OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote'])
+ OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote'])
++
++# same as OVSDB_CHECK_IDL but uses C IDL implementation with tcp
++# with multiple remotes.
++m4_define([OVSDB_CHECK_CLUSTER_IDL_C],
++  [AT_SETUP([$1 - C - tcp])
++   AT_KEYWORDS([ovsdb server idl positive tcp socket $5])
++   m4_define([LPBK],[127.0.0.1])
++   AT_CHECK([ovsdb_cluster_start_idltest $2 "ptcp:0:"LPBK])
++   PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1])
++   PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2])
++   PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3])
++   remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3
++
++   m4_if([$3], [], [],
++     [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])])
++   AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4],
++            [0], [stdout], [ignore])
++   AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]),
++            [0], [$5])
++   AT_CLEANUP])
++
++# Checks that monitor_cond_since works fine when disconnects happen
++# with cond_change requests in flight (i.e., IDL is properly updated).
++OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect],
++  3,
++  [['["idltest",
++       {"op": "insert",
++       "table": "simple",
++       "row": {"i": 1,
++               "r": 1.0,
++               "b": true}},
++       {"op": "insert",
++       "table": "simple",
++       "row": {"i": 2,
++               "r": 1.0,
++               "b": true}}]']],
++  [['condition simple []' \
++    'condition simple [["i","==",2]]' \
++    'condition simple [["i","==",1]]' \
++    '+reconnect' \
++    '["idltest",
++      {"op": "update",
++       "table": "simple",
++       "where": [["i", "==", 1]],
++       "row": {"r": 2.0 }}]']],
++  [[000: change conditions
++001: empty
++002: change conditions
++003: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
++004: change conditions
++005: reconnect
++006: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
++007: {"error":null,"result":[{"count":1}]}
++008: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2>
++009: done
++]])
+-- 
+2.26.2
+
diff --git a/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
deleted file mode 100644
index 64e808e..0000000
--- a/SOURCES/0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
+++ /dev/null
@@ -1,217 +0,0 @@
-From 22a9d1b20218d2467f30c9a87205344ff787bcf8 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 5 Jun 2020 14:00:29 +0530
-Subject: [PATCH 3/3] ovsdb idl: Try committing the pending txn in
- ovsdb_idl_loop_run.
-
-The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(),
-returns a transaction object (of type 'struct ovsdb_idl_txn').
-The returned transaction object can be NULL if there is a pending
-transaction (loop->committing_txn) in the idl loop object.
-
-Normally the clients of idl library, first call ovsdb_idl_loop_run(),
-then do their own processing and create any idl transactions during
-this processing and then finally call ovsdb_idl_loop_commit_and_wait().
-
-If ovsdb_idl_loop_run() returns NULL transaction object, then much
-of the processing done by the client gets wasted as in the case
-of ovn-controller.
-
-The client (in this case ovn-controller), can skip the processing
-and instead call ovsdb_idl_loop_commit_and_wait() if the transaction
-oject is NULL. But ovn-controller uses IDL tracking and it may
-loose the tracked changes in that run.
-
-This patch tries to improve this scenario, by checking if the
-pending transaction can be committed in the ovsdb_idl_loop_run()
-itself and if the pending transaction is cleared (because of the
-response messages from ovsdb-server due to a transaction message
-in the previous run), ovsdb_idl_loop_run() can return a valid
-transaction object.
-
-CC: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Ben Pfaff <blp@ovn.org>
----
- openvswitch-2.13.0/lib/ovsdb-idl.c | 143 +++++++++++++++++++----------
- 1 file changed, 93 insertions(+), 50 deletions(-)
-
-diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
-index 1535ad7b5..2d351791f 100644
---- a/openvswitch-2.13.0/lib/ovsdb-idl.c
-+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
-@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
- static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *);
- static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *);
- static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *);
-+static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
-+                                         bool *may_need_wakeup);
- 
- static void
- ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
-@@ -5329,6 +5331,12 @@ struct ovsdb_idl_txn *
- ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
- {
-     ovsdb_idl_run(loop->idl);
-+
-+    /* See if we can commit the loop->committing_txn. */
-+    if (loop->committing_txn) {
-+        ovsdb_idl_try_commit_loop_txn(loop, NULL);
-+    }
-+
-     loop->open_txn = (loop->committing_txn
-                       || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
-                       ? NULL
-@@ -5336,6 +5344,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
-     return loop->open_txn;
- }
- 
-+/* Attempts to commit the current transaction, if one is open.
-+ *
-+ * If a transaction was open, in this or a previous iteration of the main loop,
-+ * and had not before finished committing (successfully or unsuccessfully), the
-+ * return value is one of:
-+ *
-+ *  1: The transaction committed successfully (or it did not change anything in
-+ *     the database).
-+ *  0: The transaction failed.
-+ * -1: The commit is still in progress.
-+ *
-+ * Thus, the return value is -1 if the transaction is in progress and otherwise
-+ * true for success, false for failure.
-+ *
-+ * (In the corner case where the IDL sends a transaction to the database and
-+ * the database commits it, and the connection between the IDL and the database
-+ * drops before the IDL receives the message confirming the commit, this
-+ * function can return 0 even though the transaction succeeded.)
-+ */
-+static int
-+ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
-+                              bool *may_need_wakeup)
-+{
-+    if (!loop->committing_txn) {
-+        /* Not a meaningful return value: no transaction was in progress. */
-+        return 1;
-+    }
-+
-+    int retval;
-+    struct ovsdb_idl_txn *txn = loop->committing_txn;
-+
-+    enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
-+    if (status != TXN_INCOMPLETE) {
-+        switch (status) {
-+        case TXN_TRY_AGAIN:
-+            /* We want to re-evaluate the database when it's changed from
-+             * the contents that it had when we started the commit.  (That
-+             * might have already happened.) */
-+            loop->skip_seqno = loop->precommit_seqno;
-+            if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno
-+                && may_need_wakeup) {
-+                *may_need_wakeup = true;
-+            }
-+            retval = 0;
-+            break;
-+
-+        case TXN_SUCCESS:
-+            /* Possibly some work on the database was deferred because no
-+             * further transaction could proceed.  Wake up again. */
-+            retval = 1;
-+            loop->cur_cfg = loop->next_cfg;
-+            if (may_need_wakeup) {
-+                *may_need_wakeup =  true;
-+            }
-+            break;
-+
-+        case TXN_UNCHANGED:
-+            retval = 1;
-+            loop->cur_cfg = loop->next_cfg;
-+            break;
-+
-+        case TXN_ABORTED:
-+        case TXN_NOT_LOCKED:
-+        case TXN_ERROR:
-+            retval = 0;
-+            break;
-+
-+        case TXN_UNCOMMITTED:
-+        case TXN_INCOMPLETE:
-+        default:
-+            OVS_NOT_REACHED();
-+        }
-+        ovsdb_idl_txn_destroy(txn);
-+        loop->committing_txn = NULL;
-+    } else {
-+        retval = -1;
-+    }
-+
-+    return retval;
-+}
-+
- /* Attempts to commit the current transaction, if one is open, and sets up the
-  * poll loop to wake up when some more work might be needed.
-  *
-@@ -5366,57 +5455,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop)
-         loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
-     }
- 
--    struct ovsdb_idl_txn *txn = loop->committing_txn;
--    int retval;
--    if (txn) {
--        enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
--        if (status != TXN_INCOMPLETE) {
--            switch (status) {
--            case TXN_TRY_AGAIN:
--                /* We want to re-evaluate the database when it's changed from
--                 * the contents that it had when we started the commit.  (That
--                 * might have already happened.) */
--                loop->skip_seqno = loop->precommit_seqno;
--                if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
--                    poll_immediate_wake();
--                }
--                retval = 0;
--                break;
--
--            case TXN_SUCCESS:
--                /* Possibly some work on the database was deferred because no
--                 * further transaction could proceed.  Wake up again. */
--                retval = 1;
--                loop->cur_cfg = loop->next_cfg;
--                poll_immediate_wake();
--                break;
--
--            case TXN_UNCHANGED:
--                retval = 1;
--                loop->cur_cfg = loop->next_cfg;
--                break;
--
--            case TXN_ABORTED:
--            case TXN_NOT_LOCKED:
--            case TXN_ERROR:
--                retval = 0;
--                break;
--
--            case TXN_UNCOMMITTED:
--            case TXN_INCOMPLETE:
--            default:
--                OVS_NOT_REACHED();
--            }
--            ovsdb_idl_txn_destroy(txn);
--            loop->committing_txn = NULL;
--        } else {
--            retval = -1;
--        }
--    } else {
--        /* Not a meaningful return value: no transaction was in progress. */
--        retval = 1;
-+    bool may_need_wakeup = false;
-+    int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup);
-+    if (may_need_wakeup) {
-+        poll_immediate_wake();
-     }
--
-     ovsdb_idl_wait(loop->idl);
- 
-     return retval;
--- 
-2.26.2
-
diff --git a/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch b/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch
deleted file mode 100644
index 379c3df..0000000
--- a/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch
+++ /dev/null
@@ -1,254 +0,0 @@
-From 025fec42cebe9efc7c6a2d94816b173748e7e4f6 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 23 Jun 2020 17:07:04 +0530
-Subject: [PATCH 04/22] ovn-nbctl: Enhance lr-policy-add to set the options.
-
-The commit [1] added a new column - 'options' to Logical_Router_Policy NB DB
-table. This patch enhances the lr-policy-add command to set the options
-as key=value pairs.
-
-For nbctl_lr_policy_add(), this patch now returns after ctl_error() as there is no
-point continuing further and the comments in the ctl_error() implementation says so.
-
-[1] - a123ef0fb8fd("Support packet metadata marking for logical router policies.")
-
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit 742474bad730fbdc9705b4c2784a2b4acca327cf)
-
-Change-Id: I64c786ff4c5244b643a57bff270a14d85d5204f1
----
- tests/ovn-nbctl.at        | 15 +++++++++---
- tests/ovn.at              |  8 ++-----
- utilities/ovn-nbctl.8.xml | 11 ++++++++-
- utilities/ovn-nbctl.c     | 48 ++++++++++++++++++++++++++++++++++-----
- 4 files changed, 66 insertions(+), 16 deletions(-)
-
-diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
-index dc9d9d76a..6d6608729 100644
---- a/tests/ovn-nbctl.at
-+++ b/tests/ovn-nbctl.at
-@@ -1590,11 +1590,20 @@ AT_CHECK([ovn-nbctl lr-add lr0])
- 
- dnl Add policies with allow and drop actions
- AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop])
--AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.2.0/24" allow])
-+AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.2.0/24" allow pkt_mark=100,foo=bar])
- AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.1.0/24" allow])
- AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.2.0/24" drop])
- AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip6.src == 2002::/64" drop])
- 
-+dnl Incomplete option set.
-+AT_CHECK([ovn-nbctl lr-policy-add lr0 200 "ip4.src == 1.1.4.0/24" reroute 192.168.0.10 foo], [1], [],
-+  [ovn-nbctl: No value specified for the option : foo
-+])
-+
-+AT_CHECK([ovn-nbctl lr-policy-add lr0 200 "ip4.src == 1.1.4.0/24" allow bar=], [1], [],
-+  [ovn-nbctl: No value specified for the option : bar
-+])
-+
- dnl Add duplicated policy
- AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], [1], [],
-   [ovn-nbctl: Same routing policy already existed on the logical router lr0.
-@@ -1612,14 +1621,14 @@ Routing Policies
-        101                              ip4.src == 2.1.1.0/24           allow
-        101                              ip4.src == 2.1.2.0/24            drop
-        101                               ip6.src == 2002::/64            drop
--       100                              ip4.src == 1.1.2.0/24           allow
-+       100                              ip4.src == 1.1.2.0/24           allow               pkt_mark=100,foo=bar
- ])
- 
- dnl Delete all policies for given priority
- AT_CHECK([ovn-nbctl lr-policy-del lr0 101])
- AT_CHECK([ovn-nbctl lr-policy-list lr0], [0], [dnl
- Routing Policies
--       100                              ip4.src == 1.1.2.0/24           allow
-+       100                              ip4.src == 1.1.2.0/24           allow               pkt_mark=100,foo=bar
- ])
- 
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 8ce45823f..b84cf75fd 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -20286,20 +20286,16 @@ static_routes @lrt
- ovn-nbctl --wait=hv sync
- 
- # Add logical router policy and set pkt_mark on it.
--ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow
-+ovn-nbctl lr-policy-add lr0 2000 "ip4.src == 10.0.0.3" allow pkt_mark=100
- ovn-nbctl lr-policy-add lr0 1000 "ip4.src == 10.0.0.4" allow
--ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200
-+ovn-nbctl lr-policy-add lr0 900 "ip4.src == 10.0.0.5" reroute 172.168.0.200 pkt_mark=3
- ovn-nbctl lr-policy-add lr0 2001 "ip6.dst == bef0::5" reroute bef0::6
- ovn-nbctl lr-policy-add lr0 1001 "ip6" allow
- 
--
- pol1=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2000)
--pol3=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=900)
- pol4=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=2001)
- pol5=$(ovn-nbctl --bare --columns _uuid find logical_router_policy priority=1001)
- 
--ovn-nbctl set logical_router_policy $pol1 options:pkt_mark=100
--ovn-nbctl set logical_router_policy $pol3 options:pkt_mark=3
- ovn-nbctl set logical_router_policy $pol4 options:pkt_mark=4
- ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5
- ovn-nbctl --wait=hv sync
-diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
-index d265c7fcc..de86b70e6 100644
---- a/utilities/ovn-nbctl.8.xml
-+++ b/utilities/ovn-nbctl.8.xml
-@@ -721,7 +721,8 @@
- 
-     <dl>
-       <dt><code>lr-policy-add</code> <var>router</var> <var>priority</var>
--          <var>match</var> <var>action</var> [<var>nexthop</var>]</dt>
-+          <var>match</var> <var>action</var> [<var>nexthop</var>]
-+          [<var>options key=value]</var>] </dt>
-       <dd>
-         <p>
-           Add Policy to <var>router</var> which provides a way to configure
-@@ -732,6 +733,8 @@
-           only when <var>action</var> is <var>reroute</var>. A policy is
-           uniquely identified by <var>priority</var> and <var>match</var>.
-           Multiple policies can have the same <var>priority</var>.
-+          <var>options</var> sets the router policy options as key-value pair.
-+          The supported option is : <code>pkt_mark</code>.
-         </p>
- 
-           <p>
-@@ -743,6 +746,12 @@
-           <code>lr-policy-add lr1 100 ip4.src == 192.168.100.0/24 drop</code>.
-           </p>
- 
-+          <p>
-+          <code>
-+            lr-policy-add lr1 100 ip4.src == 192.168.100.0/24 allow
-+            pkt_mark=100
-+          </code>.
-+          </p>
-       </dd>
- 
-       <dt><code>lr-policy-del</code> <var>router</var> [<var>{priority | uuid}
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index 159a44960..7578b9928 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -694,7 +694,8 @@ Route commands:\n\
-   lr-route-list ROUTER      print routes for ROUTER\n\
- \n\
- Policy commands:\n\
--  lr-policy-add ROUTER PRIORITY MATCH ACTION [NEXTHOP]\n\
-+  lr-policy-add ROUTER PRIORITY MATCH ACTION [NEXTHOP] \
-+[OPTIONS KEY=VALUE ...] \n\
-                             add a policy to router\n\
-   lr-policy-del ROUTER [{PRIORITY | UUID} [MATCH]]\n\
-                             remove policies from ROUTER\n\
-@@ -3609,16 +3610,19 @@ nbctl_lr_policy_add(struct ctl_context *ctx)
-     const char *action = ctx->argv[4];
-     char *next_hop = NULL;
- 
-+    bool reroute = false;
-     /* Validate action. */
-     if (strcmp(action, "allow") && strcmp(action, "drop")
-         && strcmp(action, "reroute")) {
-         ctl_error(ctx, "%s: action must be one of \"allow\", \"drop\", "
-                   "and \"reroute\"", action);
-+        return;
-     }
-     if (!strcmp(action, "reroute")) {
-         if (ctx->argc < 6) {
-             ctl_error(ctx, "Nexthop is required when action is reroute.");
-         }
-+        reroute = true;
-     }
- 
-     /* Check if same routing policy already exists.
-@@ -3629,12 +3633,14 @@ nbctl_lr_policy_add(struct ctl_context *ctx)
-             !strcmp(policy->match, ctx->argv[3])) {
-             ctl_error(ctx, "Same routing policy already existed on the "
-                       "logical router %s.", ctx->argv[1]);
-+            return;
-         }
-     }
--    if (ctx->argc == 6) {
-+    if (reroute) {
-         next_hop = normalize_prefix_str(ctx->argv[5]);
-         if (!next_hop) {
-             ctl_error(ctx, "bad next hop argument: %s", ctx->argv[5]);
-+            return;
-         }
-     }
- 
-@@ -3643,9 +3649,28 @@ nbctl_lr_policy_add(struct ctl_context *ctx)
-     nbrec_logical_router_policy_set_priority(policy, priority);
-     nbrec_logical_router_policy_set_match(policy, ctx->argv[3]);
-     nbrec_logical_router_policy_set_action(policy, action);
--    if (ctx->argc == 6) {
-+    if (reroute) {
-         nbrec_logical_router_policy_set_nexthop(policy, next_hop);
-     }
-+
-+    /* Parse the options. */
-+    struct smap options = SMAP_INITIALIZER(&options);
-+    for (size_t i = reroute ? 6 : 5; i < ctx->argc; i++) {
-+        char *key, *value;
-+        value = xstrdup(ctx->argv[i]);
-+        key = strsep(&value, "=");
-+        if (value && value[0]) {
-+            smap_add(&options, key, value);
-+        } else {
-+            ctl_error(ctx, "No value specified for the option : %s", key);
-+            free(key);
-+            return;
-+        }
-+        free(key);
-+    }
-+    nbrec_logical_router_policy_set_options(policy, &options);
-+    smap_destroy(&options);
-+
-     nbrec_logical_router_verify_policies(lr);
-     struct nbrec_logical_router_policy **new_policies
-         = xmalloc(sizeof *new_policies * (lr->n_policies + 1));
-@@ -3773,6 +3798,16 @@ print_routing_policy(const struct nbrec_logical_router_policy *policy,
-         ds_put_format(s, "%10"PRId64" %50s %15s", policy->priority,
-                       policy->match, policy->action);
-     }
-+
-+    if (!smap_is_empty(&policy->options)) {
-+        ds_put_format(s, "%15s", "");
-+        struct smap_node *node;
-+        SMAP_FOR_EACH (node, &policy->options) {
-+            ds_put_format(s, "%s=%s,", node->key, node->value);
-+        }
-+        ds_chomp(s, ',');
-+    }
-+
-     ds_put_char(s, '\n');
- }
- 
-@@ -3788,7 +3823,7 @@ nbctl_lr_policy_list(struct ctl_context *ctx)
-         return;
-     }
-     policies = xmalloc(sizeof *policies * lr->n_policies);
--     for (int i = 0; i < lr->n_policies; i++) {
-+    for (int i = 0; i < lr->n_policies; i++) {
-         const struct nbrec_logical_router_policy *policy
-             = lr->policies[i];
-         policies[n_policies].priority = policy->priority;
-@@ -6362,8 +6397,9 @@ static const struct ctl_command_syntax nbctl_commands[] = {
-       "", RO },
- 
-     /* Policy commands */
--    { "lr-policy-add", 4, 5, "ROUTER PRIORITY MATCH ACTION [NEXTHOP]", NULL,
--        nbctl_lr_policy_add, NULL, "", RW },
-+    { "lr-policy-add", 4, INT_MAX,
-+     "ROUTER PRIORITY MATCH ACTION [NEXTHOP] [OPTIONS - KEY=VALUE ...]",
-+     NULL, nbctl_lr_policy_add, NULL, "", RW },
-     { "lr-policy-del", 1, 3, "ROUTER [{PRIORITY | UUID} [MATCH]]", NULL,
-         nbctl_lr_policy_del, NULL, "", RW },
-     { "lr-policy-list", 1, 1, "ROUTER", NULL, nbctl_lr_policy_list, NULL,
--- 
-2.26.2
-
diff --git a/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch b/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch
new file mode 100644
index 0000000..656edca
--- /dev/null
+++ b/SOURCES/0004-ovsdb-idl-Add-function-to-reset-min_index.patch
@@ -0,0 +1,68 @@
+From 1de31c3a531f5db6793819fa18f6e69304db929c Mon Sep 17 00:00:00 2001
+From: Mark Michelson <mmichels@redhat.com>
+Date: Fri, 1 May 2020 15:13:08 -0400
+Subject: [PATCH 4/4] ovsdb-idl: Add function to reset min_index.
+
+If an administrator removes all of the databases in a cluster from
+disk, then ovsdb IDL clients will have a problem. The databases will all
+reset their stored indexes to 0, so The IDL client's min_index will be
+higher than the indexes of all databases in the cluster. This results in
+the client constantly connecting to databases, detecting the data as
+"stale", and then attempting to connect to another.
+
+This function provides a way to reset the IDL to an initial state with
+min_index of 0. This way, the client will not wrongly detect the
+database data as stale and will recover properly.
+
+Notice that this function is not actually used anywhere in this patch.
+This will be used by OVN, though, since OVN is the primary user of
+clustered OVSDB.
+
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
+
+(cherry-picked from upstream ovs commit 89b522aee379f7ebd21ec67ffb622118af7e9db1)
+
+Change-Id: I943ece9a07566a34b11248455cc1abbe7892d4e8
+---
+ openvswitch-2.13.0/lib/ovsdb-idl.c | 10 ++++++++++
+ openvswitch-2.13.0/lib/ovsdb-idl.h |  1 +
+ 2 files changed, 11 insertions(+)
+
+diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
+index 8eb421366..648c227d6 100644
+--- a/openvswitch-2.13.0/lib/ovsdb-idl.c
++++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
+@@ -561,6 +561,16 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle)
+     idl->shuffle_remotes = shuffle;
+ }
+ 
++/* Reset min_index to 0. This prevents a situation where the client
++ * thinks all databases have stale data, when they actually have all
++ * been destroyed and rebuilt from scratch.
++ */
++void
++ovsdb_idl_reset_min_index(struct ovsdb_idl *idl)
++{
++    idl->min_index = 0;
++}
++
+ static void
+ ovsdb_idl_db_destroy(struct ovsdb_idl_db *db)
+ {
+diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.h b/openvswitch-2.13.0/lib/ovsdb-idl.h
+index 9f12ce320..c56cd19b1 100644
+--- a/openvswitch-2.13.0/lib/ovsdb-idl.h
++++ b/openvswitch-2.13.0/lib/ovsdb-idl.h
+@@ -64,6 +64,7 @@ struct ovsdb_idl *ovsdb_idl_create_unconnected(
+     const struct ovsdb_idl_class *, bool monitor_everything_by_default);
+ void ovsdb_idl_set_remote(struct ovsdb_idl *, const char *, bool);
+ void ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *, bool);
++void ovsdb_idl_reset_min_index(struct ovsdb_idl *);
+ void ovsdb_idl_destroy(struct ovsdb_idl *);
+ 
+ void ovsdb_idl_set_leader_only(struct ovsdb_idl *, bool leader_only);
+-- 
+2.26.2
+
diff --git a/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch b/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
deleted file mode 100644
index b4d6b18..0000000
--- a/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
+++ /dev/null
@@ -1,438 +0,0 @@
-From d41a77411d154b9bdfab1a7c78deb63c7c5b990b Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 29 May 2020 23:29:37 +0530
-Subject: [PATCH 05/22] pinctrl: Support DHCPRELEASE and DHCPINFORM in native
- OVN dhcp responder.
-
-Right now we ignore these dhcp packets. This patch adds the support
-as per RFC 2131.
-
-Acked-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit e008a4d46020a778b8f1f85b9dfd7c9e9b6fde21)
-
-Change-Id: I1363f99d241f661957147a7c124ac5b1c093ce00
----
- NEWS                 |   2 +
- controller/pinctrl.c | 125 ++++++++++++++++++++++++++++++++++---------
- lib/ovn-l7.h         |  12 +++++
- tests/ovn.at         | 125 ++++++++++++++++++++++++++++++++++++++++---
- 4 files changed, 233 insertions(+), 31 deletions(-)
-
-diff --git a/NEWS b/NEWS
-index 8abdc95b9..2ed3d480a 100644
---- a/NEWS
-+++ b/NEWS
-@@ -9,6 +9,8 @@ OVN v20.06.1 - 08 Jul 2020
- --------------------------
-    - Added packet marking support for traffic routed with
-      a routing policy.
-+   - Added DHCPINFORM and DHCPRELEASE support in native
-+     OVN DHCPv4 responder.
- 
- OVN v20.06.0 - 08 Jun 2020
- --------------------------
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index dab1782f6..9231efbe3 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -1682,11 +1682,13 @@ static void
- pinctrl_handle_put_dhcp_opts(
-     struct rconn *swconn,
-     struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
--    struct ofpbuf *userdata, struct ofpbuf *continuation)
-+    struct flow *in_flow, struct ofpbuf *userdata,
-+    struct ofpbuf *continuation)
- {
-     enum ofp_version version = rconn_get_version(swconn);
-     enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-     struct dp_packet *pkt_out_ptr = NULL;
-+    struct ofpbuf *dhcp_inform_reply_buf = NULL;
-     uint32_t success = 0;
- 
-     /* Parse result field. */
-@@ -1810,22 +1812,15 @@ pinctrl_handle_put_dhcp_opts(
-         VLOG_WARN_RL(&rl, "Missing DHCP message type");
-         goto exit;
-     }
--    if (*in_dhcp_msg_type != DHCP_MSG_DISCOVER &&
--        *in_dhcp_msg_type != DHCP_MSG_REQUEST) {
--        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
--        VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
--        goto exit;
--    }
- 
--    uint8_t msg_type;
--    if (*in_dhcp_msg_type == DHCP_MSG_DISCOVER) {
-+    struct ofpbuf *reply_dhcp_opts_ptr = userdata;
-+    uint8_t msg_type = 0;
-+
-+    switch (*in_dhcp_msg_type) {
-+    case DHCP_MSG_DISCOVER:
-         msg_type = DHCP_MSG_OFFER;
--    } else {
--        /* This is a DHCPREQUEST. If the client has requested an IP that
--         * does not match the offered IP address, reply with a NAK. The
--         * requested IP address may be supplied either via Requested IP Address
--         * (opt 50) or via ciaddr, depending on the client's state.
--         */
-+        break;
-+    case DHCP_MSG_REQUEST: {
-         msg_type = DHCP_MSG_ACK;
-         if (request_ip != *offer_ip) {
-             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-@@ -1834,12 +1829,81 @@ pinctrl_handle_put_dhcp_opts(
-                          IP_ARGS(*offer_ip));
-             msg_type = DHCP_MSG_NAK;
-         }
-+        break;
-+    }
-+    case OVN_DHCP_MSG_RELEASE: {
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
-+        const struct eth_header *l2 = dp_packet_eth(pkt_in);
-+        VLOG_INFO_RL(&rl, "DHCPRELEASE "ETH_ADDR_FMT " "IP_FMT"",
-+                     ETH_ADDR_ARGS(l2->eth_src),
-+                     IP_ARGS(in_dhcp_data->ciaddr));
-+        break;
-+    }
-+    case OVN_DHCP_MSG_INFORM: {
-+        /* RFC 2131 section 3.4.
-+         * Remove all the offer ip related dhcp options and
-+         * all the time related dhcp options.
-+         * Loop through the dhcp option defined in the userdata buffer
-+         * and copy all the options into dhcp_inform_reply_buf skipping
-+         * the not required ones.
-+         * */
-+        msg_type = DHCP_MSG_ACK;
-+        in_dhcp_ptr = userdata->data;
-+        end = (const char *)userdata->data + userdata->size;
-+
-+        /* The buf size cannot be greater > userdata->size. */
-+        dhcp_inform_reply_buf = ofpbuf_new(userdata->size);
-+
-+        reply_dhcp_opts_ptr = dhcp_inform_reply_buf;
-+        while (in_dhcp_ptr < end) {
-+            const struct dhcp_opt_header *in_dhcp_opt =
-+                (const struct dhcp_opt_header *)in_dhcp_ptr;
-+
-+            switch (in_dhcp_opt->code) {
-+            case OVN_DHCP_OPT_CODE_NETMASK:
-+            case OVN_DHCP_OPT_CODE_LEASE_TIME:
-+            case OVN_DHCP_OPT_CODE_T1:
-+            case OVN_DHCP_OPT_CODE_T2:
-+                break;
-+            default:
-+                /* Copy the dhcp option to reply_dhcp_opts_ptr. */
-+                ofpbuf_put(reply_dhcp_opts_ptr, in_dhcp_opt,
-+                           in_dhcp_opt->len + sizeof *in_dhcp_opt);
-+                break;
-+            }
-+
-+            in_dhcp_ptr += sizeof *in_dhcp_opt;
-+            if (in_dhcp_ptr > end) {
-+                break;
-+            }
-+            in_dhcp_ptr += in_dhcp_opt->len;
-+            if (in_dhcp_ptr > end) {
-+                break;
-+            }
-+        }
-+
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
-+        VLOG_INFO_RL(&rl, "DHCPINFORM from "ETH_ADDR_FMT " "IP_FMT"",
-+                     ETH_ADDR_ARGS(in_flow->dl_src),
-+                     IP_ARGS(in_flow->nw_src));
-+
-+        break;
-+    }
-+    default: {
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-+        VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
-+        goto exit;
-+    }
-+    }
-+
-+    if (!msg_type) {
-+        goto exit;
-     }
- 
-     /* Frame the DHCP reply packet
--     * Total DHCP options length will be options stored in the userdata +
--     * 16 bytes. Note that the DHCP options stored in userdata are not included
--     * in DHCPNAK messages.
-+     * Total DHCP options length will be options stored in the
-+     * reply_dhcp_opts_ptr + 16 bytes. Note that the DHCP options stored in
-+     * reply_dhcp_opts_ptr are not included in DHCPNAK messages.
-      *
-      * --------------------------------------------------------------
-      *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options |
-@@ -1849,7 +1913,7 @@ pinctrl_handle_put_dhcp_opts(
-      */
-     uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
-     if (msg_type != DHCP_MSG_NAK) {
--        new_l4_size += userdata->size;
-+        new_l4_size += reply_dhcp_opts_ptr->size;
-     }
-     size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
- 
-@@ -1874,12 +1938,18 @@ pinctrl_handle_put_dhcp_opts(
-     struct dhcp_header *dhcp_data = dp_packet_put(
-         &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN);
-     dhcp_data->op = DHCP_OP_REPLY;
--    dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip;
-+
-+    if (*in_dhcp_msg_type != OVN_DHCP_MSG_INFORM) {
-+        dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip;
-+    } else {
-+        dhcp_data->yiaddr = 0;
-+    }
-+
-     dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32));
- 
-     uint16_t out_dhcp_opts_size = 12;
-     if (msg_type != DHCP_MSG_NAK) {
--      out_dhcp_opts_size += userdata->size;
-+      out_dhcp_opts_size += reply_dhcp_opts_ptr->size;
-     }
-     uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out,
-                                                  out_dhcp_opts_size);
-@@ -1890,8 +1960,9 @@ pinctrl_handle_put_dhcp_opts(
-     out_dhcp_opts += 3;
- 
-     if (msg_type != DHCP_MSG_NAK) {
--      memcpy(out_dhcp_opts, userdata->data, userdata->size);
--      out_dhcp_opts += userdata->size;
-+        memcpy(out_dhcp_opts, reply_dhcp_opts_ptr->data,
-+               reply_dhcp_opts_ptr->size);
-+        out_dhcp_opts += reply_dhcp_opts_ptr->size;
-     }
- 
-     /* Padding */
-@@ -1939,6 +2010,10 @@ exit:
-     if (pkt_out_ptr) {
-         dp_packet_uninit(pkt_out_ptr);
-     }
-+
-+    if (dhcp_inform_reply_buf) {
-+        ofpbuf_delete(dhcp_inform_reply_buf);
-+    }
- }
- 
- static bool
-@@ -2644,8 +2719,8 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
-         break;
- 
-     case ACTION_OPCODE_PUT_DHCP_OPTS:
--        pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &userdata,
--                                     &continuation);
-+        pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers,
-+                                     &userdata, &continuation);
-         break;
- 
-     case ACTION_OPCODE_ND_NA:
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index cea97b9ce..9acfbe075 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -37,6 +37,14 @@ struct gen_opts_map {
- #define DOMAIN_NAME_MAX_LEN 255
- #define DHCP_BROADCAST_FLAG 0x8000
- 
-+/* These are not defined in ovs/lib/dhcp.h and hence defined here with
-+ * OVN_DHCP_OPT_CODE_<opt_name>.
-+ */
-+#define OVN_DHCP_OPT_CODE_NETMASK      1
-+#define OVN_DHCP_OPT_CODE_LEASE_TIME   51
-+#define OVN_DHCP_OPT_CODE_T1           58
-+#define OVN_DHCP_OPT_CODE_T2           59
-+
- #define DHCP_OPTION(NAME, CODE, TYPE) \
-     {.name = NAME, .code = CODE, .type = TYPE}
- 
-@@ -171,6 +179,10 @@ struct dhcp_opt6_header {
-     ovs_be16 size;
- };
- 
-+/* These are not defined in ovs/lib/dhcp.h, hence defining here. */
-+#define OVN_DHCP_MSG_RELEASE        7
-+#define OVN_DHCP_MSG_INFORM         8
-+
- /* Supported DHCPv6 Message Types */
- #define DHCPV6_MSG_TYPE_SOLICIT     1
- #define DHCPV6_MSG_TYPE_ADVT        2
-diff --git a/tests/ovn.at b/tests/ovn.at
-index b84cf75fd..6866a58da 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -5330,6 +5330,12 @@ test_dhcp() {
-     done
-     if test $offer_ip != 0; then
-         local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4
-+        local offered_ip=$offer_ip
-+        if [[ "$dhcp_type" == "08" ]]; then
-+            # DHCP ACK for DHCP INFORM should not have any offer ip.
-+            offered_ip=00000000
-+        fi
-+
-         # total IP length will be the IP length of the request packet
-         # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2)
-         ip_len=`expr 280 + ${#expected_dhcp_opts} / 2`
-@@ -5345,7 +5351,7 @@ test_dhcp() {
-         if test $dhcp_reply_type = 06; then
-             reply=${reply}00000000
-         else
--            reply=${reply}${offer_ip}
-+            reply=${reply}${offered_ip}
-         fi
-         # next server ip address, relay agent ip address, client mac address
-         reply=${reply}0000000000000000${src_mac}
-@@ -5485,7 +5491,7 @@ rm -f 2.expected
- ciaddr=`ip_to_hex 0 0 0 0`
- offer_ip=0
- request_ip=0
--test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1
-+test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1
- 
- # NXT_RESUMEs should be 4.
- OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -5660,6 +5666,113 @@ reset_pcap_file hv1-vif2 hv1/vif2
- rm -f 1.expected
- rm -f 2.expected
- 
-+# Send DHCPRELEASE.
-+offer_ip=0
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=`ip_to_hex 10 0 0 6`
-+request_ip=0
-+expected_dhcp_opts=0
-+test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001
-+
-+# NXT_RESUMEs should be 10.
-+OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
-+
-+# There is no reply for this. Check for the INFO log in ovn-controller.log
-+AT_CHECK([test 1 = $(cat hv1/ovn-controller.log | \
-+grep "DHCPRELEASE f0:00:00:00:00:02 10.0.0.6" -c)])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+AT_CHECK([cat 2.packets], [0], [])
-+
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Send DHCPINFORM
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=$offer_ip
-+request_ip=0
-+src_ip=$offer_ip
-+dst_ip=$server_ip
-+# In the expected_dhcp_opts we should not see 330400000e10 which is
-+# dhcp lease time option and 0104ffffff00 which is subnet mask option.
-+expected_dhcp_opts=03040a00000136040a000001
-+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 11.
-+OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
-+# Now add the dhcp option T1 to the dhcp options.
-+ovn-nbctl set dhcp_options ${d1} options:T1=4000
-+
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Send DHCPREQUEST to make sure that T1 is in the reply dhcp options.
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=$offer_ip
-+request_ip=0
-+src_ip=$offer_ip
-+dst_ip=$server_ip
-+# In the expected_dhcp_opts we should not see 330400000e10 which is
-+# dhcp lease time option.
-+expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 12.
-+OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Now send DHCPINFORM again.
-+offer_ip=`ip_to_hex 10 0 0 6`
-+server_ip=`ip_to_hex 10 0 0 1`
-+ciaddr=00000000
-+request_ip=0
-+src_ip=$offer_ip
-+dst_ip=$server_ip
-+# In the expected_dhcp_opts we should not see 330400000e10 which is
-+# dhcp lease time option and 0104ffffff00 which is subnet mask option.
-+expected_dhcp_opts=03040a00000136040a000001
-+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 13.
-+OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-+cat 2.expected | cut -c -48 > expout
-+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 2.expected | cut -c 53- > expout
-+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-+
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
- # Set tftp server option (IPv4 address) for ls1
- echo "------ Set tftp server (IPv4 address) --------"
- ovn-nbctl dhcp-options-set-options $d1 server_id=10.0.0.1 \
-@@ -5676,8 +5789,8 @@ request_ip=$offer_ip
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a
- test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
- 
--# NXT_RESUMEs should be 10.
--OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 14.
-+OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
- cat 2.expected | cut -c -48 > expout
-@@ -5707,8 +5820,8 @@ request_ip=$offer_ip
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572
- test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
- 
--# NXT_RESUMEs should be 11.
--OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 15.
-+OVS_WAIT_UNTIL([test 15 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
- cat 2.expected | cut -c -48 > expout
--- 
-2.26.2
-
diff --git a/SOURCES/0006-Introduce-icmp6_error-action.patch b/SOURCES/0006-Introduce-icmp6_error-action.patch
deleted file mode 100644
index e03f1d6..0000000
--- a/SOURCES/0006-Introduce-icmp6_error-action.patch
+++ /dev/null
@@ -1,178 +0,0 @@
-From 13e1aade24b29ce4a6425a355f482b149848928a Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Tue, 7 Jul 2020 17:18:26 +0200
-Subject: [PATCH 06/22] Introduce icmp6_error action
-
-Introduce icmp6_error action in order to generate an ICMPv6 packet in
-response to an error in original IPv6 packet
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c  |  1 +
- include/ovn/actions.h |  9 ++++++++-
- lib/actions.c         | 22 ++++++++++++++++++++++
- ovn-sb.xml            |  8 ++++++++
- tests/ovn.at          | 10 ++++++++++
- utilities/ovn-trace.c |  5 +++++
- 6 files changed, 54 insertions(+), 1 deletion(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 9231efbe3..f8be22db0 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -2772,6 +2772,7 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
-         break;
- 
-     case ACTION_OPCODE_ICMP4_ERROR:
-+    case ACTION_OPCODE_ICMP6_ERROR:
-         pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata,
-                             &userdata, false);
-         break;
-diff --git a/include/ovn/actions.h b/include/ovn/actions.h
-index 2b5a63a74..5abf61a40 100644
---- a/include/ovn/actions.h
-+++ b/include/ovn/actions.h
-@@ -94,7 +94,8 @@ struct ovn_extend_table;
-     OVNACT(BIND_VPORT,        ovnact_bind_vport)       \
-     OVNACT(HANDLE_SVC_CHECK,  ovnact_handle_svc_check) \
-     OVNACT(FWD_GROUP,         ovnact_fwd_group)       \
--    OVNACT(DHCP6_REPLY,       ovnact_null)
-+    OVNACT(DHCP6_REPLY,       ovnact_null)            \
-+    OVNACT(ICMP6_ERROR,       ovnact_nest)
- 
- /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
- enum OVS_PACKED_ENUM ovnact_type {
-@@ -601,6 +602,12 @@ enum action_opcode {
-      *  The actions, in OpenFlow 1.3 format, follow the action_header.
-      */
-     ACTION_OPCODE_DHCP6_SERVER,
-+
-+    /* "icmp6_error { ...actions... }".
-+     *
-+     * The actions, in OpenFlow 1.3 format, follow the action_header.
-+     */
-+    ACTION_OPCODE_ICMP6_ERROR,
- };
- 
- /* Header. */
-diff --git a/lib/actions.c b/lib/actions.c
-index e4de97c23..515dbd2db 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -1408,6 +1408,12 @@ parse_ICMP6(struct action_context *ctx)
-     parse_nested_action(ctx, OVNACT_ICMP6, "ip6");
- }
- 
-+static void
-+parse_ICMP6_ERROR(struct action_context *ctx)
-+{
-+    parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6");
-+}
-+
- static void
- parse_TCP_RESET(struct action_context *ctx)
- {
-@@ -1471,6 +1477,12 @@ format_ICMP6(const struct ovnact_nest *nest, struct ds *s)
-     format_nested_action(nest, "icmp6", s);
- }
- 
-+static void
-+format_ICMP6_ERROR(const struct ovnact_nest *nest, struct ds *s)
-+{
-+    format_nested_action(nest, "icmp6_error", s);
-+}
-+
- static void
- format_IGMP(const struct ovnact_null *a OVS_UNUSED, struct ds *s)
- {
-@@ -1582,6 +1594,14 @@ encode_ICMP6(const struct ovnact_nest *on,
-     encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts);
- }
- 
-+static void
-+encode_ICMP6_ERROR(const struct ovnact_nest *on,
-+                   const struct ovnact_encode_params *ep,
-+                   struct ofpbuf *ofpacts)
-+{
-+    encode_nested_actions(on, ep, ACTION_OPCODE_ICMP6_ERROR, ofpacts);
-+}
-+
- static void
- encode_IGMP(const struct ovnact_null *a OVS_UNUSED,
-             const struct ovnact_encode_params *ep OVS_UNUSED,
-@@ -3558,6 +3578,8 @@ parse_action(struct action_context *ctx)
-         parse_ICMP4_ERROR(ctx);
-     } else if (lexer_match_id(ctx->lexer, "icmp6")) {
-         parse_ICMP6(ctx);
-+    } else if (lexer_match_id(ctx->lexer, "icmp6_error")) {
-+        parse_ICMP6_ERROR(ctx);
-     } else if (lexer_match_id(ctx->lexer, "igmp")) {
-         ovnact_put_IGMP(ctx->ovnacts);
-     } else if (lexer_match_id(ctx->lexer, "tcp_reset")) {
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 709cb4c48..6d6775a45 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -2090,6 +2090,9 @@
-         </dd>
- 
-         <dt><code>icmp6 { <var>action</var>; </code>...<code> };</code></dt>
-+        <dt>
-+          <code>icmp6_error { <var>action</var>; </code>...<code> };</code>
-+        </dt>
-         <dd>
-           <p>
-             Temporarily replaces the IPv6 packet being processed by an ICMPv6
-@@ -2112,6 +2115,11 @@
-             <li><code>icmp6.code = 1</code> (administratively prohibited)</li>
-           </ul>
- 
-+          <p>
-+              <code>icmp6_error</code> action is expected to be used to
-+              generate an ICMPv6 packet in response to an error in original
-+              IPv6 packet.
-+          </p>
-           <p><b>Prerequisite:</b> <code>ip6</code></p>
-         </dd>
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 6866a58da..cfcfa0915 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1547,6 +1547,16 @@ icmp6 { };
-     encodes as controller(userdata=00.00.00.0a.00.00.00.00)
-     has prereqs ip6
- 
-+# icmp6_error
-+icmp6_error { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
-+    encodes as controller(userdata=00.00.00.14.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
-+    has prereqs ip6
-+
-+icmp6_error { };
-+    formats as icmp6_error { drop; };
-+    encodes as controller(userdata=00.00.00.14.00.00.00.00)
-+    has prereqs ip6
-+
- # tcp_reset
- tcp_reset { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
-     encodes as controller(userdata=00.00.00.0b.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index 647cf3075..c34517aaa 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -2319,6 +2319,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
-                           super);
-             break;
- 
-+        case OVNACT_ICMP6_ERROR:
-+            execute_icmp6(ovnact_get_ICMP6_ERROR(a), dp, uflow, table_id,
-+                          pipeline, super);
-+            break;
-+
-         case OVNACT_IGMP:
-             /* Nothing to do for tracing. */
-             break;
--- 
-2.26.2
-
diff --git a/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch b/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch
deleted file mode 100644
index 7f39cba..0000000
--- a/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch
+++ /dev/null
@@ -1,287 +0,0 @@
-From c659c6ae95e5ffa94e795a0f104c27c3f5523eae Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Tue, 7 Jul 2020 17:18:27 +0200
-Subject: [PATCH 07/22] Introduce icmp6.frag_mtu action
-
-Similar to what have been already done for IPv4, introduce
-icmp6.frag_mtu action in order to set correct mtu in ICMPv6 "packet too
-big" error message
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c         | 77 ++++++++++++++++++++++--------------
- include/ovn/actions.h        |  4 ++
- include/ovn/logical-fields.h |  7 ++++
- lib/actions.c                | 13 +++++-
- lib/logical-fields.c         |  5 +++
- ovn-sb.xml                   |  7 ++--
- tests/ovn.at                 |  8 ++++
- utilities/ovn-trace.c        |  7 +++-
- 8 files changed, 93 insertions(+), 35 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index f8be22db0..f72ab70e1 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -228,12 +228,12 @@ static void pinctrl_handle_nd_ns(struct rconn *swconn,
-                                  struct dp_packet *pkt_in,
-                                  const struct match *md,
-                                  struct ofpbuf *userdata);
--static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
--                                              const struct flow *in_flow,
--                                              struct dp_packet *pkt_in,
--                                              struct ofputil_packet_in *pin,
--                                              struct ofpbuf *userdata,
--                                              struct ofpbuf *continuation);
-+static void pinctrl_handle_put_icmp_frag_mtu(struct rconn *swconn,
-+                                             const struct flow *in_flow,
-+                                             struct dp_packet *pkt_in,
-+                                             struct ofputil_packet_in *pin,
-+                                             struct ofpbuf *userdata,
-+                                             struct ofpbuf *continuation);
- static void
- pinctrl_handle_event(struct ofpbuf *userdata)
-     OVS_REQUIRES(pinctrl_mutex);
-@@ -2783,8 +2783,9 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
-         break;
- 
-     case ACTION_OPCODE_PUT_ICMP4_FRAG_MTU:
--        pinctrl_handle_put_icmp4_frag_mtu(swconn, &headers, &packet,
--                                          &pin, &userdata, &continuation);
-+    case ACTION_OPCODE_PUT_ICMP6_FRAG_MTU:
-+        pinctrl_handle_put_icmp_frag_mtu(swconn, &headers, &packet, &pin,
-+                                         &userdata, &continuation);
-         break;
- 
-     case ACTION_OPCODE_EVENT:
-@@ -5475,26 +5476,22 @@ exit:
- 
- /* Called with in the pinctrl_handler thread context. */
- static void
--pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
--                                  const struct flow *in_flow,
--                                  struct dp_packet *pkt_in,
--                                  struct ofputil_packet_in *pin,
--                                  struct ofpbuf *userdata,
--                                  struct ofpbuf *continuation)
-+pinctrl_handle_put_icmp_frag_mtu(struct rconn *swconn,
-+                                 const struct flow *in_flow,
-+                                 struct dp_packet *pkt_in,
-+                                 struct ofputil_packet_in *pin,
-+                                 struct ofpbuf *userdata,
-+                                 struct ofpbuf *continuation)
- {
-     enum ofp_version version = rconn_get_version(swconn);
-     enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-     struct dp_packet *pkt_out = NULL;
- 
--    /* This action only works for ICMPv4 packets. */
--    if (!is_icmpv4(in_flow, NULL)) {
-+    /* This action only works for ICMPv4/v6 packets. */
-+    if (!is_icmpv4(in_flow, NULL) && !is_icmpv6(in_flow, NULL)) {
-         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
--        VLOG_WARN_RL(&rl, "put_icmp4_frag_mtu action on non-ICMPv4 packet");
--        goto exit;
--    }
--
--    ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
--    if (!mtu) {
-+        VLOG_WARN_RL(&rl,
-+                     "put_icmp(4/6)_frag_mtu action on non-ICMPv4/v6 packet");
-         goto exit;
-     }
- 
-@@ -5504,13 +5501,35 @@ pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
-     pkt_out->l3_ofs = pkt_in->l3_ofs;
-     pkt_out->l4_ofs = pkt_in->l4_ofs;
- 
--    struct ip_header *nh = dp_packet_l3(pkt_out);
--    struct icmp_header *ih = dp_packet_l4(pkt_out);
--    ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu;
--    ih->icmp_fields.frag.mtu = *mtu;
--    ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu);
--    nh->ip_csum = 0;
--    nh->ip_csum = csum(nh, sizeof *nh);
-+    if (is_icmpv4(in_flow, NULL)) {
-+        ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
-+        if (!mtu) {
-+            goto exit;
-+        }
-+
-+        struct ip_header *nh = dp_packet_l3(pkt_out);
-+        struct icmp_header *ih = dp_packet_l4(pkt_out);
-+        ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu;
-+        ih->icmp_fields.frag.mtu = *mtu;
-+        ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu);
-+        nh->ip_csum = 0;
-+        nh->ip_csum = csum(nh, sizeof *nh);
-+    } else {
-+        ovs_be32 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
-+        if (!mtu) {
-+            goto exit;
-+        }
-+
-+        struct icmp6_data_header *ih = dp_packet_l4(pkt_out);
-+        put_16aligned_be32(ih->icmp6_data.be32, *mtu);
-+
-+        /* compute checksum and set correct mtu */
-+        ih->icmp6_base.icmp6_cksum = 0;
-+        uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(pkt_out));
-+        uint32_t size = (uint8_t *)dp_packet_tail(pkt_out) - (uint8_t *)ih;
-+        ih->icmp6_base.icmp6_cksum = csum_finish(
-+                csum_continue(csum, ih, size));
-+    }
- 
-     pin->packet = dp_packet_data(pkt_out);
-     pin->packet_len = dp_packet_size(pkt_out);
-diff --git a/include/ovn/actions.h b/include/ovn/actions.h
-index 5abf61a40..34ba0d880 100644
---- a/include/ovn/actions.h
-+++ b/include/ovn/actions.h
-@@ -608,6 +608,10 @@ enum action_opcode {
-      * The actions, in OpenFlow 1.3 format, follow the action_header.
-      */
-     ACTION_OPCODE_ICMP6_ERROR,
-+
-+    /* MTU value (to put in the icmp6 header field - frag_mtu) follow the
-+     * action header. */
-+    ACTION_OPCODE_PUT_ICMP6_FRAG_MTU,
- };
- 
- /* Header. */
-diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
-index c7bd2dba9..61d17d14f 100644
---- a/include/ovn/logical-fields.h
-+++ b/include/ovn/logical-fields.h
-@@ -108,6 +108,13 @@ enum ovn_field_id {
-      * packet as per the RFC 1191.
-      */
-     OVN_ICMP4_FRAG_MTU,
-+    /*
-+     * Name: "icmp6.frag_mtu" -
-+     * Type: be32
-+     * Description: Sets the first 32 bits of the ICMPv6 body to the MTU of
-+     * next-hop link (RFC 4443)
-+     */
-+    OVN_ICMP6_FRAG_MTU,
- 
-     OVN_FIELD_N_IDS
- };
-diff --git a/lib/actions.c b/lib/actions.c
-index 515dbd2db..1deeef799 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -3159,6 +3159,7 @@ format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s)
-     const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
-     switch (f->id) {
-     case OVN_ICMP4_FRAG_MTU:
-+    case OVN_ICMP6_FRAG_MTU:
-         ds_put_format(s, "%s = %u;", f->name,
-                       ntohs(load->imm.value.be16_int));
-         break;
-@@ -3178,12 +3179,20 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load,
-     switch (f->id) {
-     case OVN_ICMP4_FRAG_MTU: {
-         size_t oc_offset = encode_start_controller_op(
--            ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, NX_CTLR_NO_METER,
--            ofpacts);
-+            ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true,
-+            NX_CTLR_NO_METER, ofpacts);
-         ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16));
-         encode_finish_controller_op(oc_offset, ofpacts);
-         break;
-     }
-+    case OVN_ICMP6_FRAG_MTU: {
-+        size_t oc_offset = encode_start_controller_op(
-+            ACTION_OPCODE_PUT_ICMP6_FRAG_MTU, true,
-+            NX_CTLR_NO_METER, ofpacts);
-+        ofpbuf_put(ofpacts, &load->imm.value.be32_int, sizeof(ovs_be32));
-+        encode_finish_controller_op(oc_offset, ofpacts);
-+        break;
-+    }
-     case OVN_FIELD_N_IDS:
-     default:
-         OVS_NOT_REACHED();
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index 8ad56aa53..8639523ea 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -29,6 +29,10 @@ const struct ovn_field ovn_fields[OVN_FIELD_N_IDS] = {
-         OVN_ICMP4_FRAG_MTU,
-         "icmp4.frag_mtu",
-         2, 16,
-+    }, {
-+        OVN_ICMP6_FRAG_MTU,
-+        "icmp6.frag_mtu",
-+        4, 32,
-     },
- };
- 
-@@ -257,6 +261,7 @@ ovn_init_symtab(struct shash *symtab)
-     expr_symtab_add_field(symtab, "pkt.mark", MFF_PKT_MARK, NULL, false);
- 
-     expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU);
-+    expr_symtab_add_ovn_field(symtab, "icmp6.frag_mtu", OVN_ICMP6_FRAG_MTU);
- }
- 
- const char *
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 6d6775a45..fc39b2d03 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -1170,10 +1170,11 @@
-           <ul>
-             <li>
-               <code>icmp4.frag_mtu</code>
-+              <code>icmp6.frag_mtu</code>
-               <p>
--                This field sets the low-order 16 bits of the ICMP4 header field
--                that is labelled "unused" in the ICMP specification as defined
--                in the RFC 1191 with the value specified in
-+                This field sets the low-order 16 bits of the ICMP{4,6} header
-+                field that is labelled "unused" in the ICMP specification as
-+                defined in the RFC 1191 with the value specified in
-                 <var>constant</var>.
-               </p>
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index cfcfa0915..80cd62c49 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1557,6 +1557,14 @@ icmp6_error { };
-     encodes as controller(userdata=00.00.00.14.00.00.00.00)
-     has prereqs ip6
- 
-+# icmp6_error with icmp6.frag_mtu
-+icmp6_error { eth.dst = ff:ff:ff:ff:ff:ff; icmp6.frag_mtu = 1500; output; }; output;
-+    encodes as controller(userdata=00.00.00.14.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.10.00.00.00.15.00.00.00.00.00.00.05.dc.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
-+    has prereqs ip6
-+
-+icmp6.frag_mtu = 1500;
-+    encodes as controller(userdata=00.00.00.15.00.00.00.00.00.00.05.dc,pause)
-+
- # tcp_reset
- tcp_reset { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
-     encodes as controller(userdata=00.00.00.0b.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index c34517aaa..50a32b714 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -2119,7 +2119,12 @@ execute_ovnfield_load(const struct ovnact_load *load,
-                              ntohs(load->imm.value.be16_int));
-         break;
-     }
--
-+    case OVN_ICMP6_FRAG_MTU: {
-+        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
-+                             "icmp6.frag_mtu = %u",
-+                             ntohs(load->imm.value.be16_int));
-+        break;
-+    }
-     case OVN_FIELD_N_IDS:
-     default:
-         OVS_NOT_REACHED();
--- 
-2.26.2
-
diff --git a/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch b/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch
deleted file mode 100644
index 246780e..0000000
--- a/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch
+++ /dev/null
@@ -1,263 +0,0 @@
-From a1a0c7061850d78edb74a7977d0241121575be0e Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Tue, 7 Jul 2020 17:18:28 +0200
-Subject: [PATCH 08/22] northd: introduce icmp6_error logical flows in router
- pipeline
-
-Introduce icmp6_error logical flows in router pipeline if gateway_mtu
-has been added to logical router port option column in order to perform
-IPv6 PMTU discovery
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.8.xml | 24 +++++++++---
- northd/ovn-northd.c     | 84 ++++++++++++++++++++++++++++-------------
- tests/ovn.at            | 48 +++++++++++++++++++++--
- 3 files changed, 120 insertions(+), 36 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 67481f458..623768988 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2922,11 +2922,11 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
-       For distributed logical routers with distributed gateway port configured
-       with <code>options:gateway_mtu</code> to a valid integer value, this
-       table adds the following priority-50 logical flow for each
--      logical router port with the match <code>ip4 &amp;&amp;
--      inport == <var>LRP</var> &amp;&amp; outport == <var>GW_PORT</var>
--      &amp;&amp; REGBIT_PKT_LARGER</code>, where <var>LRP</var> is the logical
--      router port and <var>GW_PORT</var> is the distributed gateway router port
--      and applies the following action
-+      logical router port with the match <code>inport == <var>LRP</var>
-+      &amp;&amp; outport == <var>GW_PORT</var> &amp;&amp;
-+      REGBIT_PKT_LARGER</code>, where <var>LRP</var> is the logical
-+      router port and <var>GW_PORT</var> is the distributed gateway router
-+      port and applies the following action for ipv4 and ipv6 respectively:
-     </p>
- 
-     <pre>
-@@ -2941,6 +2941,18 @@ icmp4 {
-     REGBIT_EGRESS_LOOPBACK = 1;
-     next(pipeline=ingress, table=0);
- };
-+
-+icmp6 {
-+    icmp6.type = 2;
-+    icmp6.code = 0;
-+    icmp6.frag_mtu = <var>M</var>;
-+    eth.dst = <var>E</var>;
-+    ip6.dst = ip6.src;
-+    ip6.src = <var>I</var>;
-+    ip.ttl = 255;
-+    REGBIT_EGRESS_LOOPBACK = 1;
-+    next(pipeline=ingress, table=0);
-+};
-     </pre>
- 
-     <ul>
-@@ -2956,7 +2968,7 @@ icmp4 {
-       </li>
- 
-       <li>
--        <var>I</var> is the IPv4 address of the logical router port.
-+        <var>I</var> is the IPv4/IPv6 address of the logical router port.
-       </li>
-     </ul>
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 2b1257114..6375aee8d 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -10495,8 +10495,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             }
- 
-             ds_clear(&match);
--            ds_put_format(&match, "outport == %s && ip4",
--                          od->l3dgw_port->json_key);
-+            ds_put_format(&match, "outport == %s", od->l3dgw_port->json_key);
- 
-             ds_clear(&actions);
-             ds_put_format(&actions,
-@@ -10509,34 +10508,65 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             for (size_t i = 0; i < od->nbr->n_ports; i++) {
-                 struct ovn_port *rp = ovn_port_find(ports,
-                                                     od->nbr->ports[i]->name);
--                if (!rp || rp == od->l3dgw_port ||
--                    !rp->lrp_networks.ipv4_addrs) {
-+                if (!rp || rp == od->l3dgw_port) {
-                     continue;
-                 }
--                ds_clear(&match);
--                ds_put_format(&match, "inport == %s && outport == %s && ip4 "
--                              "&& "REGBIT_PKT_LARGER,
--                              rp->json_key, od->l3dgw_port->json_key);
- 
--                ds_clear(&actions);
--                /* Set icmp4.frag_mtu to gw_mtu */
--                ds_put_format(&actions,
--                    "icmp4_error {"
--                    REGBIT_EGRESS_LOOPBACK" = 1; "
--                    "eth.dst = %s; "
--                    "ip4.dst = ip4.src; "
--                    "ip4.src = %s; "
--                    "ip.ttl = 255; "
--                    "icmp4.type = 3; /* Destination Unreachable. */ "
--                    "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
--                    "icmp4.frag_mtu = %d; "
--                    "next(pipeline=ingress, table=0); };",
--                    rp->lrp_networks.ea_s,
--                    rp->lrp_networks.ipv4_addrs[0].addr_s,
--                    gw_mtu);
--                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_LARGER_PKTS,
--                                        50, ds_cstr(&match), ds_cstr(&actions),
--                                        &rp->nbrp->header_);
-+                if (rp->lrp_networks.ipv4_addrs) {
-+                    ds_clear(&match);
-+                    ds_put_format(&match, "inport == %s && outport == %s"
-+                                  " && ip4 && "REGBIT_PKT_LARGER,
-+                                  rp->json_key, od->l3dgw_port->json_key);
-+
-+                    ds_clear(&actions);
-+                    /* Set icmp4.frag_mtu to gw_mtu */
-+                    ds_put_format(&actions,
-+                        "icmp4_error {"
-+                        REGBIT_EGRESS_LOOPBACK" = 1; "
-+                        "eth.dst = %s; "
-+                        "ip4.dst = ip4.src; "
-+                        "ip4.src = %s; "
-+                        "ip.ttl = 255; "
-+                        "icmp4.type = 3; /* Destination Unreachable. */ "
-+                        "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
-+                        "icmp4.frag_mtu = %d; "
-+                        "next(pipeline=ingress, table=0); };",
-+                        rp->lrp_networks.ea_s,
-+                        rp->lrp_networks.ipv4_addrs[0].addr_s,
-+                        gw_mtu);
-+                    ovn_lflow_add_with_hint(lflows, od,
-+                                            S_ROUTER_IN_LARGER_PKTS, 50,
-+                                            ds_cstr(&match), ds_cstr(&actions),
-+                                            &rp->nbrp->header_);
-+                }
-+
-+                if (rp->lrp_networks.ipv6_addrs) {
-+                    ds_clear(&match);
-+                    ds_put_format(&match, "inport == %s && outport == %s"
-+                                  " && ip6 && "REGBIT_PKT_LARGER,
-+                                  rp->json_key, od->l3dgw_port->json_key);
-+
-+                    ds_clear(&actions);
-+                    /* Set icmp6.frag_mtu to gw_mtu */
-+                    ds_put_format(&actions,
-+                        "icmp6_error {"
-+                        REGBIT_EGRESS_LOOPBACK" = 1; "
-+                        "eth.dst = %s; "
-+                        "ip6.dst = ip6.src; "
-+                        "ip6.src = %s; "
-+                        "ip.ttl = 255; "
-+                        "icmp6.type = 2; /* Packet Too Big. */ "
-+                        "icmp6.code = 0; "
-+                        "icmp6.frag_mtu = %d; "
-+                        "next(pipeline=ingress, table=0); };",
-+                        rp->lrp_networks.ea_s,
-+                        rp->lrp_networks.ipv6_addrs[0].addr_s,
-+                        gw_mtu);
-+                    ovn_lflow_add_with_hint(lflows, od,
-+                                            S_ROUTER_IN_LARGER_PKTS, 50,
-+                                            ds_cstr(&match), ds_cstr(&actions),
-+                                            &rp->nbrp->header_);
-+                }
-             }
-         }
-     }
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 80cd62c49..905112a8d 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -15115,17 +15115,17 @@ ovn_start
- 
- ovn-nbctl ls-add sw0
- ovn-nbctl lsp-add sw0 sw0-port1
--ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3"
-+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3 1000::3"
- 
- ovn-nbctl lr-add lr0
--ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
-+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::1/64
- ovn-nbctl lsp-add sw0 sw0-lr0
- ovn-nbctl lsp-set-type sw0-lr0 router
- ovn-nbctl lsp-set-addresses sw0-lr0 router
- ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
- 
- ovn-nbctl ls-add public
--ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
-+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 2000::1/64
- ovn-nbctl lsp-add public public-lr0
- ovn-nbctl lsp-set-type public-lr0 router
- ovn-nbctl lsp-set-addresses public-lr0 router
-@@ -15139,6 +15139,7 @@ ovn-nbctl lsp-set-options ln-public network_name=phys
- 
- ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
- ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24
-+ovn-nbctl lr-nat-add lr0 snat 2000::1 1000::/64
- 
- net_add n1
- 
-@@ -15249,6 +15250,41 @@ test_ip_packet_larger() {
-     fi
- }
- 
-+test_ip6_packet_larger() {
-+    local icmp_pmtu_reply_expected=$1
-+
-+    local eth_src=505400000001
-+    local eth_dst=00000000ff01
-+
-+    local ipv6_src=10000000000000000000000000000003
-+    local ipv6_dst=20000000000000000000000000000002
-+    local ipv6_rt=10000000000000000000000000000001
-+
-+    local payload=0000000000000000000000000000000000000000
-+    local payload=${payload}0000000000000000000000000000000000000000
-+    local payload=${payload}0000000000000000000000000000000000000000
-+    local payload=${payload}0000000000000000000000000000000000000000
-+
-+    local ip6_hdr=6000000000583aff${ipv6_src}${ipv6_dst}
-+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000ec7662f00001${payload}
-+
-+    as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
-+    as hv1 reset_pcap_file hv1-vif1 hv1/vif1
-+
-+    # Send packet from sw0-port1 to outside
-+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-+
-+    if test $icmp_pmtu_reply_expected = 1; then
-+        icmp6_reply=${eth_src}${eth_dst}86dd6000000000883afe
-+        icmp6_reply=${icmp6_reply}${ipv6_rt}${ipv6_src}020041ff00000076
-+        icmp6_reply=${icmp6_reply}6000000000583afe${ipv6_src}${ipv6_dst}
-+        icmp6_reply=${icmp6_reply}8000ec7662f00001${payload}
-+        echo $icmp6_reply > hv1-vif1.expected
-+
-+        OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected])
-+    fi
-+}
-+
- ovn-nbctl show
- ovn-sbctl show
- 
-@@ -15283,6 +15319,12 @@ OVS_WAIT_UNTIL([
- # Now the packet should be sent via the localnet port to br-phys.
- icmp_reply_expected=0
- test_ip_packet_larger $icmp_reply_expected
-+
-+# Set the gateway mtu to 118
-+ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=118
-+icmp_reply_expected=1
-+test_ip6_packet_larger $icmp_reply_expected
-+
- OVN_CLEANUP([hv1])
- AT_CLEANUP
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch b/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch
deleted file mode 100644
index e7a69ce..0000000
--- a/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch
+++ /dev/null
@@ -1,126 +0,0 @@
-From 6d0628f34e7462cbe7c580774b19644609757b85 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 20 Jul 2020 14:53:15 -0400
-Subject: [PATCH 09/22] Add IP address normalization to ovn-utils.
-
-This moves a couple of existing IP address normalization routines from
-ovn-nbctl.c to ovn-util.c. It also adds a new normalization function for
-a v46_address. This new function is not used in this commit but will be
-used in a future commit in this series.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
----
- lib/ovn-util.c        | 39 +++++++++++++++++++++++++++++++++++++++
- lib/ovn-util.h        |  4 ++++
- utilities/ovn-nbctl.c | 29 -----------------------------
- 3 files changed, 43 insertions(+), 29 deletions(-)
-
-diff --git a/lib/ovn-util.c b/lib/ovn-util.c
-index f09fdaffe..cdb5e18fb 100644
---- a/lib/ovn-util.c
-+++ b/lib/ovn-util.c
-@@ -589,6 +589,45 @@ ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2)
-              IN6_ARE_ADDR_EQUAL(&addr1->ipv6, &addr2->ipv6)));
- }
- 
-+/* The caller must free the returned string. */
-+char *
-+normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen)
-+{
-+    ovs_be32 network = ipv4 & be32_prefix_mask(plen);
-+    if (plen == 32) {
-+        return xasprintf(IP_FMT, IP_ARGS(network));
-+    } else {
-+        return xasprintf(IP_FMT "/%d", IP_ARGS(network), plen);
-+    }
-+}
-+
-+/* The caller must free the returned string. */
-+char *
-+normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen)
-+{
-+    char network_s[INET6_ADDRSTRLEN];
-+
-+    struct in6_addr mask = ipv6_create_mask(plen);
-+    struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask);
-+
-+    inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN);
-+    if (plen == 128) {
-+        return xasprintf("%s", network_s);
-+    } else {
-+        return xasprintf("%s/%d", network_s, plen);
-+    }
-+}
-+
-+char *
-+normalize_v46_prefix(const struct v46_ip *prefix, unsigned int plen)
-+{
-+    if (prefix->family == AF_INET) {
-+        return normalize_ipv4_prefix(prefix->ipv4, plen);
-+    } else {
-+        return normalize_ipv6_prefix(prefix->ipv6, plen);
-+    }
-+}
-+
- char *
- str_tolower(const char *orig)
- {
-diff --git a/lib/ovn-util.h b/lib/ovn-util.h
-index 4e08ee01e..0f7b501f1 100644
---- a/lib/ovn-util.h
-+++ b/lib/ovn-util.h
-@@ -144,6 +144,10 @@ bool ip46_parse_cidr(const char *str, struct v46_ip *prefix,
-                      unsigned int *plen);
- bool ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2);
- 
-+char *normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen);
-+char *normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen);
-+char *normalize_v46_prefix(const struct v46_ip *prefix, unsigned int plen);
-+
- /* Returns a lowercase copy of orig.
-  * Caller must free the returned string.
-  */
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index 7578b9928..0079ad5a6 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -3482,35 +3482,6 @@ nbctl_dhcp_options_list(struct ctl_context *ctx)
-     free(nodes);
- }
- 
--/* The caller must free the returned string. */
--static char *
--normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen)
--{
--    ovs_be32 network = ipv4 & be32_prefix_mask(plen);
--    if (plen == 32) {
--        return xasprintf(IP_FMT, IP_ARGS(network));
--    } else {
--        return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen);
--    }
--}
--
--/* The caller must free the returned string. */
--static char *
--normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen)
--{
--    char network_s[INET6_ADDRSTRLEN];
--
--    struct in6_addr mask = ipv6_create_mask(plen);
--    struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask);
--
--    inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN);
--    if (plen == 128) {
--        return xasprintf("%s", network_s);
--    } else {
--        return xasprintf("%s/%d", network_s, plen);
--    }
--}
--
- static char *
- normalize_ipv4_prefix_str(const char *orig_prefix)
- {
--- 
-2.26.2
-
diff --git a/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch b/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch
deleted file mode 100644
index 90d5f06..0000000
--- a/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 5b5b130408eebf999edacfe298c2fedc40d6e603 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 27 Jul 2020 16:04:06 -0400
-Subject: [PATCH 10/22] Don't check for writeability of rhs during assignment.
-
-The only condition under which the right-hand side of an assignment
-needs to be checked for writeability is if it is an exchange (<->)
-operation.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
----
- lib/actions.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index 1deeef799..23e334404 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -515,7 +515,7 @@ parse_assignment_action(struct action_context *ctx, bool exchange,
- 
-     char *error = expr_type_check(lhs, lhs->n_bits, true);
-     if (!error) {
--        error = expr_type_check(&rhs, rhs.n_bits, true);
-+        error = expr_type_check(&rhs, rhs.n_bits, exchange);
-     }
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
--- 
-2.26.2
-
diff --git a/SOURCES/0011-Add-expression-writeability-scopes.patch b/SOURCES/0011-Add-expression-writeability-scopes.patch
deleted file mode 100644
index c5d0f1c..0000000
--- a/SOURCES/0011-Add-expression-writeability-scopes.patch
+++ /dev/null
@@ -1,423 +0,0 @@
-From 2bbba1279ae7b197d1b21dfb2ef7e45d0bcd8000 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 27 Jul 2020 16:08:21 -0400
-Subject: [PATCH 11/22] Add expression writeability scopes.
-
-Logical fields are defined as either being writeable or read-only. There
-is no way to make fields writeable only in specific scenarios.
-
-This commit changes the boolean writeability field to a field of flags
-indicating contexts where a field is writeable. Any time that nested
-actions are used (i.e. actions enclosed in curly braces), a new scope
-may be set for the nested action. For this particular commit, no
-functionality is changed, and only a "default" scope is added
-that mirrors the current setup. A future commit will make use of this
-feature.
-
-Change-Id: Id7a8dbedb862e8274c70597251233eeb35f81af6
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
----
- include/ovn/expr.h | 52 ++++++++++++++++++++++++++++++++++------------
- lib/actions.c      | 46 +++++++++++++++++++++-------------------
- lib/expr.c         | 35 ++++++++++++++++++-------------
- 3 files changed, 84 insertions(+), 49 deletions(-)
-
-diff --git a/include/ovn/expr.h b/include/ovn/expr.h
-index 9838251c1..11bfdad5b 100644
---- a/include/ovn/expr.h
-+++ b/include/ovn/expr.h
-@@ -83,6 +83,10 @@ enum expr_level {
-     EXPR_L_ORDINAL
- };
- 
-+enum expr_write_scope {
-+    WR_DEFAULT   = (1 << 0), /* Writeable at "global" level */
-+};
-+
- const char *expr_level_to_string(enum expr_level);
- 
- /* A symbol.
-@@ -255,7 +259,8 @@ struct expr_symbol {
- 
-     char *prereqs;
-     bool must_crossproduct;
--    bool rw;
-+    enum expr_write_scope rw; /* Bit map indicating in which nested contexts
-+                               * the symbol is writeable */
- };
- 
- void expr_symbol_format(const struct expr_symbol *, struct ds *);
-@@ -273,20 +278,40 @@ bool expr_field_parse(struct lexer *, const struct shash *symtab,
-                       struct expr_field *, struct expr **prereqsp);
- void expr_field_format(const struct expr_field *, struct ds *);
- 
--struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
--                                          const char *name, enum mf_field_id,
--                                          const char *prereqs,
--                                          bool must_crossproduct);
--struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab,
--                                             const char *name,
--                                             const char *prereqs,
--                                             const char *subfield);
--struct expr_symbol *expr_symtab_add_string(struct shash *symtab,
--                                           const char *name, enum mf_field_id,
--                                           const char *prereqs);
-+struct expr_symbol *expr_symtab_add_field_scoped(struct shash *symtab,
-+                                                 const char *name,
-+                                                 enum mf_field_id,
-+                                                 const char *prereqs,
-+                                                 bool must_crossproduct,
-+                                                 enum expr_write_scope scope);
-+
-+#define expr_symtab_add_field(SYMTAB, NAME, MF_FIELD_ID, PREREQS, \
-+                              MUST_CROSSPRODUCT) \
-+    expr_symtab_add_field_scoped((SYMTAB), (NAME), (MF_FIELD_ID), (PREREQS), \
-+                                 (MUST_CROSSPRODUCT), WR_DEFAULT)
-+
-+struct expr_symbol *expr_symtab_add_subfield_scoped(struct shash *symtab,
-+   const char *name, const char *prereqs, const char *subfield,
-+   enum expr_write_scope scope);
-+
-+#define expr_symtab_add_subfield(SYMTAB, NAME, PREREQS, SUBFIELD) \
-+    expr_symtab_add_subfield_scoped((SYMTAB), (NAME), (PREREQS), \
-+                                    (SUBFIELD), WR_DEFAULT)
-+
-+struct expr_symbol *expr_symtab_add_string_scoped(struct shash *symtab,
-+                                                  const char *name,
-+                                                  enum mf_field_id,
-+                                                  const char *prereqs,
-+                                                  enum expr_write_scope scope);
-+
-+#define expr_symtab_add_string(SYMTAB, NAME, MF_FIELD_ID, PREREQS) \
-+    expr_symtab_add_string_scoped((SYMTAB), (NAME), (MF_FIELD_ID), (PREREQS), \
-+                                  WR_DEFAULT)
-+
- struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab,
-                                               const char *name,
-                                               const char *expansion);
-+
- struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab,
-                                               const char *name,
-                                               enum ovn_field_id id);
-@@ -452,7 +477,8 @@ void expr_matches_print(const struct hmap *matches, FILE *);
- 
- /* Action parsing helper. */
- 
--char *expr_type_check(const struct expr_field *, int n_bits, bool rw)
-+char *expr_type_check(const struct expr_field *, int n_bits, bool rw,
-+                      enum expr_write_scope scope)
-     OVS_WARN_UNUSED_RESULT;
- struct mf_subfield expr_resolve_field(const struct expr_field *);
- 
-diff --git a/lib/actions.c b/lib/actions.c
-index 23e334404..460ab0cf5 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -195,6 +195,7 @@ struct action_context {
-     struct ofpbuf *ovnacts;     /* Actions. */
-     struct expr *prereqs;       /* Prerequisites to apply to match. */
-     int depth;                  /* Current nested action depth. */
-+    enum expr_write_scope scope;  /* Current writeability scope */
- };
- 
- static void parse_actions(struct action_context *, enum lex_type sentinel);
-@@ -207,7 +208,7 @@ action_parse_field(struct action_context *ctx,
-         return false;
-     }
- 
--    char *error = expr_type_check(f, n_bits, rw);
-+    char *error = expr_type_check(f, n_bits, rw, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -374,7 +375,7 @@ parse_LOAD(struct action_context *ctx, const struct expr_field *lhs)
- 
-     load->dst = *lhs;
- 
--    char *error = expr_type_check(lhs, lhs->n_bits, true);
-+    char *error = expr_type_check(lhs, lhs->n_bits, true, ctx->scope);
-     if (error) {
-         ctx->ovnacts->size = ofs;
-         lexer_error(ctx->lexer, "%s", error);
-@@ -513,9 +514,9 @@ parse_assignment_action(struct action_context *ctx, bool exchange,
-         return;
-     }
- 
--    char *error = expr_type_check(lhs, lhs->n_bits, true);
-+    char *error = expr_type_check(lhs, lhs->n_bits, true, ctx->scope);
-     if (!error) {
--        error = expr_type_check(&rhs, rhs.n_bits, exchange);
-+        error = expr_type_check(&rhs, rhs.n_bits, exchange, ctx->scope);
-     }
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-@@ -1186,7 +1187,8 @@ static void
- parse_select_action(struct action_context *ctx, struct expr_field *res_field)
- {
-     /* Check if the result field is modifiable. */
--    char *error = expr_type_check(res_field, res_field->n_bits, true);
-+    char *error = expr_type_check(res_field, res_field->n_bits, true,
-+                                  ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -1337,7 +1339,7 @@ encode_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED,
-  * actions on a packet derived from the one being processed. */
- static void
- parse_nested_action(struct action_context *ctx, enum ovnact_type type,
--                    const char *prereq)
-+                    const char *prereq, enum expr_write_scope scope)
- {
-     if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) {
-         return;
-@@ -1357,6 +1359,7 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type,
-         .ovnacts = &nested,
-         .prereqs = NULL,
-         .depth = ctx->depth + 1,
-+        .scope = scope,
-     };
-     parse_actions(&inner_ctx, LEX_T_RCURLY);
- 
-@@ -1387,61 +1390,61 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type,
- static void
- parse_ARP(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ARP, "ip4");
-+    parse_nested_action(ctx, OVNACT_ARP, "ip4", ctx->scope);
- }
- 
- static void
- parse_ICMP4(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ICMP4, "ip4");
-+    parse_nested_action(ctx, OVNACT_ICMP4, "ip4", ctx->scope);
- }
- 
- static void
- parse_ICMP4_ERROR(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4");
-+    parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4", ctx->scope);
- }
- 
- static void
- parse_ICMP6(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ICMP6, "ip6");
-+    parse_nested_action(ctx, OVNACT_ICMP6, "ip6", ctx->scope);
- }
- 
- static void
- parse_ICMP6_ERROR(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6");
-+    parse_nested_action(ctx, OVNACT_ICMP6_ERROR, "ip6", ctx->scope);
- }
- 
- static void
- parse_TCP_RESET(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp");
-+    parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp", ctx->scope);
- }
- 
- static void
- parse_ND_NA(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns");
-+    parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns", ctx->scope);
- }
- 
- static void
- parse_ND_NA_ROUTER(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns");
-+    parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns", ctx->scope);
- }
- 
- static void
- parse_ND_NS(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_ND_NS, "ip6");
-+    parse_nested_action(ctx, OVNACT_ND_NS, "ip6", ctx->scope);
- }
- 
- static void
- parse_CLONE(struct action_context *ctx)
- {
--    parse_nested_action(ctx, OVNACT_CLONE, NULL);
-+    parse_nested_action(ctx, OVNACT_CLONE, NULL, WR_DEFAULT);
- }
- 
- static void
-@@ -1947,7 +1950,7 @@ parse_lookup_mac_bind(struct action_context *ctx,
-                       struct ovnact_lookup_mac_bind *lookup_mac)
- {
-     /* Validate that the destination is a 1-bit, modifiable field. */
--    char *error = expr_type_check(dst, 1, true);
-+    char *error = expr_type_check(dst, 1, true, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -2053,7 +2056,7 @@ parse_lookup_mac_bind_ip(struct action_context *ctx,
-                          struct ovnact_lookup_mac_bind_ip *lookup_mac)
- {
-     /* Validate that the destination is a 1-bit, modifiable field. */
--    char *error = expr_type_check(dst, 1, true);
-+    char *error = expr_type_check(dst, 1, true, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -2283,7 +2286,7 @@ parse_put_opts(struct action_context *ctx, const struct expr_field *dst,
-     lexer_get(ctx->lexer); /* Skip '('. */
- 
-     /* Validate that the destination is a 1-bit, modifiable field. */
--    char *error = expr_type_check(dst, 1, true);
-+    char *error = expr_type_check(dst, 1, true, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -2680,7 +2683,7 @@ parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst,
-         return;
-     }
-     /* Validate that the destination is a 1-bit, modifiable field. */
--    char *error = expr_type_check(dst, 1, true);
-+    char *error = expr_type_check(dst, 1, true, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -3205,7 +3208,7 @@ parse_check_pkt_larger(struct action_context *ctx,
-                        struct ovnact_check_pkt_larger *cipl)
- {
-      /* Validate that the destination is a 1-bit, modifiable field. */
--    char *error = expr_type_check(dst, 1, true);
-+    char *error = expr_type_check(dst, 1, true, ctx->scope);
-     if (error) {
-         lexer_error(ctx->lexer, "%s", error);
-         free(error);
-@@ -3677,6 +3680,7 @@ ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp,
-         .lexer = lexer,
-         .ovnacts = ovnacts,
-         .prereqs = NULL,
-+        .scope = WR_DEFAULT,
-     };
-     if (!lexer->error) {
-         parse_actions(&ctx, LEX_T_END);
-diff --git a/lib/expr.c b/lib/expr.c
-index 497b2accc..c07e7dd4d 100644
---- a/lib/expr.c
-+++ b/lib/expr.c
-@@ -1447,7 +1447,7 @@ expr_symbol_format(const struct expr_symbol *symbol, struct ds *s)
- static struct expr_symbol *
- add_symbol(struct shash *symtab, const char *name, int width,
-            const char *prereqs, enum expr_level level,
--           bool must_crossproduct, bool rw)
-+           bool must_crossproduct, enum expr_write_scope rw)
- {
-     struct expr_symbol *symbol = xzalloc(sizeof *symbol);
-     symbol->name = xstrdup(name);
-@@ -1471,9 +1471,10 @@ add_symbol(struct shash *symtab, const char *name, int width,
-  * Use subfields to duplicate or subset a field (you can even make a subfield
-  * include all the bits of the "parent" field if you like). */
- struct expr_symbol *
--expr_symtab_add_field(struct shash *symtab, const char *name,
--                      enum mf_field_id id, const char *prereqs,
--                      bool must_crossproduct)
-+expr_symtab_add_field_scoped(struct shash *symtab, const char *name,
-+                             enum mf_field_id id, const char *prereqs,
-+                             bool must_crossproduct,
-+                             enum expr_write_scope scope)
- {
-     const struct mf_field *field = mf_from_id(id);
-     struct expr_symbol *symbol;
-@@ -1482,7 +1483,8 @@ expr_symtab_add_field(struct shash *symtab, const char *name,
-                         (field->maskable == MFM_FULLY
-                          ? EXPR_L_ORDINAL
-                          : EXPR_L_NOMINAL),
--                        must_crossproduct, field->writable);
-+                        must_crossproduct,
-+                        field->writable ? scope : 0);
-     symbol->field = field;
-     return symbol;
- }
-@@ -1511,8 +1513,9 @@ parse_field_from_string(const char *s, const struct shash *symtab,
-  * 'subfield' must describe the subfield as a string, e.g. "vlan.tci[0..11]"
-  * for the low 12 bits of a larger field named "vlan.tci". */
- struct expr_symbol *
--expr_symtab_add_subfield(struct shash *symtab, const char *name,
--                         const char *prereqs, const char *subfield)
-+expr_symtab_add_subfield_scoped(struct shash *symtab, const char *name,
-+                                const char *prereqs, const char *subfield,
-+                                enum expr_write_scope scope)
- {
-     struct expr_symbol *symbol;
-     struct expr_field f;
-@@ -1531,7 +1534,7 @@ expr_symtab_add_subfield(struct shash *symtab, const char *name,
-     }
- 
-     symbol = add_symbol(symtab, name, f.n_bits, prereqs, level, false,
--                        f.symbol->rw);
-+                        f.symbol->rw ? scope : 0);
-     symbol->parent = f.symbol;
-     symbol->parent_ofs = f.ofs;
-     return symbol;
-@@ -1540,14 +1543,15 @@ expr_symtab_add_subfield(struct shash *symtab, const char *name,
- /* Adds a string-valued symbol named 'name' to 'symtab' with the specified
-  * 'prereqs'. */
- struct expr_symbol *
--expr_symtab_add_string(struct shash *symtab, const char *name,
--                       enum mf_field_id id, const char *prereqs)
-+expr_symtab_add_string_scoped(struct shash *symtab, const char *name,
-+                              enum mf_field_id id, const char *prereqs,
-+                              enum expr_write_scope scope)
- {
-     const struct mf_field *field = mf_from_id(id);
-     struct expr_symbol *symbol;
- 
-     symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false,
--                        field->writable);
-+                        field->writable ? scope : 0);
-     symbol->field = field;
-     return symbol;
- }
-@@ -1610,7 +1614,7 @@ expr_symtab_add_predicate(struct shash *symtab, const char *name,
-         return NULL;
-     }
- 
--    symbol = add_symbol(symtab, name, 1, NULL, level, false, false);
-+    symbol = add_symbol(symtab, name, 1, NULL, level, false, 0);
-     symbol->predicate = xstrdup(expansion);
-     return symbol;
- }
-@@ -1623,7 +1627,7 @@ expr_symtab_add_ovn_field(struct shash *symtab, const char *name,
-     struct expr_symbol *symbol;
- 
-     symbol = add_symbol(symtab, name, ovn_field->n_bits, NULL,
--                        EXPR_L_NOMINAL, false, true);
-+                        EXPR_L_NOMINAL, false, UINT32_MAX);
-     symbol->ovn_field = ovn_field;
-     return symbol;
- }
-@@ -3322,7 +3326,8 @@ expr_evaluate(const struct expr *e, const struct flow *uflow,
-  * if 'f' is acceptable, otherwise a malloc()'d error message that the caller
-  * must free(). */
- char * OVS_WARN_UNUSED_RESULT
--expr_type_check(const struct expr_field *f, int n_bits, bool rw)
-+expr_type_check(const struct expr_field *f, int n_bits, bool rw,
-+                uint32_t write_scope)
- {
-     if (n_bits != f->n_bits) {
-         if (n_bits && f->n_bits) {
-@@ -3340,7 +3345,7 @@ expr_type_check(const struct expr_field *f, int n_bits, bool rw)
-         }
-     }
- 
--    if (rw && !f->symbol->rw) {
-+    if (rw && !(f->symbol->rw & write_scope)) {
-         return xasprintf("Field %s is not modifiable.", f->symbol->name);
-     }
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0012-Used-nested-actions-in-ct_commit.patch b/SOURCES/0012-Used-nested-actions-in-ct_commit.patch
deleted file mode 100644
index 4c8db8e..0000000
--- a/SOURCES/0012-Used-nested-actions-in-ct_commit.patch
+++ /dev/null
@@ -1,401 +0,0 @@
-From b74a558d0a5e4c217f966d9615611e3dca1d6c23 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 27 Jul 2020 16:11:56 -0400
-Subject: [PATCH 12/22] Used nested actions in ct_commit
-
-ct_commit allows for ct_label and ct_mark to be set within. However,
-there are some restrictions with the current implementation:
-
-* It is not possible to address the indiviual bits within the ct_mark or
-   ct_label.
-* It is not possible to set these to the value of a register. Only
-   explicit integer setting can be used.
-
-With this change, ct_commit now can have arbitrary nested actions
-inside. This makes it similar to how the "exec" option works in OVS's
-ct() action.
-
-ct_commit now also sets a writeability scope so that ct_mark and
-ct_label are the only symbols that are writeable. The positive side
-effect is that ct_mark and ct_label are no longer writeable except for
-inside ct_commit.
-
-In this commit, the only noticeable effect is that it allows for
-slightly more expressive setting of ct_label.blocked. A future commit
-will take further advantage of this.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
----
- include/ovn/actions.h |   9 +---
- include/ovn/expr.h    |   1 +
- lib/actions.c         | 110 +++++++-----------------------------------
- lib/logical-fields.c  |   9 ++--
- northd/ovn-northd.c   |   8 +--
- ovn-sb.xml            |  11 +++--
- tests/ovn.at          |  59 ++++++++++++----------
- 7 files changed, 72 insertions(+), 135 deletions(-)
-
-diff --git a/include/ovn/actions.h b/include/ovn/actions.h
-index 34ba0d880..636cb4bc1 100644
---- a/include/ovn/actions.h
-+++ b/include/ovn/actions.h
-@@ -57,7 +57,7 @@ struct ovn_extend_table;
-     OVNACT(EXCHANGE,          ovnact_move)            \
-     OVNACT(DEC_TTL,           ovnact_null)            \
-     OVNACT(CT_NEXT,           ovnact_ct_next)         \
--    OVNACT(CT_COMMIT,         ovnact_ct_commit)       \
-+    OVNACT(CT_COMMIT,         ovnact_nest)            \
-     OVNACT(CT_DNAT,           ovnact_ct_nat)          \
-     OVNACT(CT_SNAT,           ovnact_ct_nat)          \
-     OVNACT(CT_LB,             ovnact_ct_lb)           \
-@@ -222,13 +222,6 @@ struct ovnact_ct_next {
-     uint8_t ltable;                /* Logical table ID of next table. */
- };
- 
--/* OVNACT_CT_COMMIT. */
--struct ovnact_ct_commit {
--    struct ovnact ovnact;
--    uint32_t ct_mark, ct_mark_mask;
--    ovs_be128 ct_label, ct_label_mask;
--};
--
- /* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */
- struct ovnact_ct_nat {
-     struct ovnact ovnact;
-diff --git a/include/ovn/expr.h b/include/ovn/expr.h
-index 11bfdad5b..b34fb0e81 100644
---- a/include/ovn/expr.h
-+++ b/include/ovn/expr.h
-@@ -85,6 +85,7 @@ enum expr_level {
- 
- enum expr_write_scope {
-     WR_DEFAULT   = (1 << 0), /* Writeable at "global" level */
-+    WR_CT_COMMIT = (1 << 1), /* Writeable in "ct_commit" action */
- };
- 
- const char *expr_level_to_string(enum expr_level);
-diff --git a/lib/actions.c b/lib/actions.c
-index 460ab0cf5..79ac79a95 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -200,6 +200,15 @@ struct action_context {
- 
- static void parse_actions(struct action_context *, enum lex_type sentinel);
- 
-+static void parse_nested_action(struct action_context *ctx,
-+                                enum ovnact_type type,
-+                                const char *prereq,
-+                                enum expr_write_scope scope);
-+
-+static void format_nested_action(const struct ovnact_nest *on,
-+                                 const char *name,
-+                                 struct ds *s);
-+
- static bool
- action_parse_field(struct action_context *ctx,
-                    int n_bits, bool rw, struct expr_field *f)
-@@ -618,125 +627,42 @@ ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED)
- {
- }
- 
--static void
--parse_ct_commit_arg(struct action_context *ctx,
--                    struct ovnact_ct_commit *cc)
--{
--    if (lexer_match_id(ctx->lexer, "ct_mark")) {
--        if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
--            return;
--        }
--        if (ctx->lexer->token.type == LEX_T_INTEGER) {
--            cc->ct_mark = ntohll(ctx->lexer->token.value.integer);
--            cc->ct_mark_mask = UINT32_MAX;
--        } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) {
--            cc->ct_mark = ntohll(ctx->lexer->token.value.integer);
--            cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer);
--        } else {
--            lexer_syntax_error(ctx->lexer, "expecting integer");
--            return;
--        }
--        lexer_get(ctx->lexer);
--    } else if (lexer_match_id(ctx->lexer, "ct_label")) {
--        if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
--            return;
--        }
--        if (ctx->lexer->token.type == LEX_T_INTEGER) {
--            cc->ct_label = ctx->lexer->token.value.be128_int;
--            cc->ct_label_mask = OVS_BE128_MAX;
--        } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) {
--            cc->ct_label = ctx->lexer->token.value.be128_int;
--            cc->ct_label_mask = ctx->lexer->token.mask.be128_int;
--        } else {
--            lexer_syntax_error(ctx->lexer, "expecting integer");
--            return;
--        }
--        lexer_get(ctx->lexer);
--    } else {
--        lexer_syntax_error(ctx->lexer, NULL);
--    }
--}
--
- static void
- parse_CT_COMMIT(struct action_context *ctx)
- {
--    add_prerequisite(ctx, "ip");
- 
--    struct ovnact_ct_commit *ct_commit = ovnact_put_CT_COMMIT(ctx->ovnacts);
--    if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
--        while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
--            parse_ct_commit_arg(ctx, ct_commit);
--            if (ctx->lexer->error) {
--                return;
--            }
--            lexer_match(ctx->lexer, LEX_T_COMMA);
--        }
--    }
-+    parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip",
-+                        WR_CT_COMMIT);
- }
- 
- static void
--format_CT_COMMIT(const struct ovnact_ct_commit *cc, struct ds *s)
-+format_CT_COMMIT(const struct ovnact_nest *on, struct ds *s)
- {
--    ds_put_cstr(s, "ct_commit(");
--    if (cc->ct_mark_mask) {
--        ds_put_format(s, "ct_mark=%#"PRIx32, cc->ct_mark);
--        if (cc->ct_mark_mask != UINT32_MAX) {
--            ds_put_format(s, "/%#"PRIx32, cc->ct_mark_mask);
--        }
--    }
--    if (!ovs_be128_is_zero(cc->ct_label_mask)) {
--        if (ds_last(s) != '(') {
--            ds_put_cstr(s, ", ");
--        }
--
--        ds_put_format(s, "ct_label=");
--        ds_put_hex(s, &cc->ct_label, sizeof cc->ct_label);
--        if (!ovs_be128_equals(cc->ct_label_mask, OVS_BE128_MAX)) {
--            ds_put_char(s, '/');
--            ds_put_hex(s, &cc->ct_label_mask, sizeof cc->ct_label_mask);
--        }
--    }
--    if (!ds_chomp(s, '(')) {
--        ds_put_char(s, ')');
--    }
--    ds_put_char(s, ';');
-+    format_nested_action(on, "ct_commit", s);
- }
- 
- static void
--encode_CT_COMMIT(const struct ovnact_ct_commit *cc,
-+encode_CT_COMMIT(const struct ovnact_nest *on,
-                  const struct ovnact_encode_params *ep OVS_UNUSED,
-                  struct ofpbuf *ofpacts)
- {
-     struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
-     ct->flags = NX_CT_F_COMMIT;
-     ct->recirc_table = NX_CT_RECIRC_NONE;
--    ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE);
-+    ct->zone_src.field = ep->is_switch
-+        ? mf_from_id(MFF_LOG_CT_ZONE)
-+        : mf_from_id(MFF_LOG_DNAT_ZONE);
-     ct->zone_src.ofs = 0;
-     ct->zone_src.n_bits = 16;
- 
-     size_t set_field_offset = ofpacts->size;
-     ofpbuf_pull(ofpacts, set_field_offset);
- 
--    if (cc->ct_mark_mask) {
--        const ovs_be32 value = htonl(cc->ct_mark);
--        const ovs_be32 mask = htonl(cc->ct_mark_mask);
--        ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_MARK), &value, &mask);
--    }
--
--    if (!ovs_be128_is_zero(cc->ct_label_mask)) {
--        ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_LABEL), &cc->ct_label,
--                             &cc->ct_label_mask);
--    }
--
-+    ovnacts_encode(on->nested, on->nested_len, ep, ofpacts);
-     ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset);
-     ct = ofpacts->header;
-     ofpact_finish(ofpacts, &ct->ofpact);
- }
--
--static void
--ovnact_ct_commit_free(struct ovnact_ct_commit *cc OVS_UNUSED)
--{
--}
- 
- static void
- parse_ct_nat(struct action_context *ctx, const char *name,
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index 8639523ea..fde53a47e 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -123,10 +123,13 @@ ovn_init_symtab(struct shash *symtab)
-                              flags_str);
- 
-     /* Connection tracking state. */
--    expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false);
-+    expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL, false,
-+                                 WR_CT_COMMIT);
- 
--    expr_symtab_add_field(symtab, "ct_label", MFF_CT_LABEL, NULL, false);
--    expr_symtab_add_subfield(symtab, "ct_label.blocked", NULL, "ct_label[0]");
-+    expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL, false,
-+                                 WR_CT_COMMIT);
-+    expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL,
-+                                    "ct_label[0]", WR_CT_COMMIT);
- 
-     expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 6375aee8d..44e7d9365 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -5356,7 +5356,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
-             ds_clear(&match);
-             ds_clear(&actions);
-             ds_put_cstr(&match, "ct.est && ct_label.blocked == 0");
--            ds_put_cstr(&actions, "ct_commit(ct_label=1/1); ");
-+            ds_put_cstr(&actions, "ct_commit { ct_label.blocked = 1; }; ");
-             if (!strcmp(acl->action, "reject")) {
-                 build_reject_acl_rules(od, lflows, stage, acl, &match,
-                                        &actions, &acl->header_);
-@@ -5880,9 +5880,11 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
-      * any packet that makes it this far is part of a connection we
-      * want to allow to continue. */
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
--                  REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
-+                  REGBIT_CONNTRACK_COMMIT" == 1",
-+                  "ct_commit { ct_label.blocked = 0; }; next;");
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
--                  REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
-+                  REGBIT_CONNTRACK_COMMIT" == 1",
-+                  "ct_commit { ct_label.blocked = 0; }; next;");
- 
-     /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
-      * through nat (without committing).
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index fc39b2d03..a74d9c3ea 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -1261,10 +1261,10 @@
-           </p>
-         </dd>
- 
--        <dt><code>ct_commit;</code></dt>
--        <dt><code>ct_commit(ct_mark=<var>value[/mask]</var>);</code></dt>
--        <dt><code>ct_commit(ct_label=<var>value[/mask]</var>);</code></dt>
--        <dt><code>ct_commit(ct_mark=<var>value[/mask]</var>, ct_label=<var>value[/mask]</var>);</code></dt>
-+        <dt><code>ct_commit { };</code></dt>
-+        <dt><code>ct_commit { ct_mark=<var>value[/mask]</var>; };</code></dt>
-+        <dt><code>ct_commit { ct_label=<var>value[/mask]</var>; };</code></dt>
-+        <dt><code>ct_commit { ct_mark=<var>value[/mask]</var>; ct_label=<var>value[/mask]</var>; };</code></dt>
-         <dd>
-           <p>
-             Commit the flow to the connection tracking entry associated with it
-@@ -1276,6 +1276,9 @@
-             tracking entry. <code>ct_mark</code> is a 32-bit field.
-             <code>ct_label</code> is a 128-bit field. The <var>value[/mask]</var>
-             should be specified in hex string if more than 64bits are to be used.
-+            Registers and other named fields can be used for <var>value</var>.
-+            <code>ct_mark</code> and <code>ct_label</code> may be sub-addressed
-+            in order to have specific bits set.
-           </p>
- 
-           <p>
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 905112a8d..4c68b77d8 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1045,51 +1045,60 @@ ct_next;
-     has prereqs ip
- 
- # ct_commit
--ct_commit;
-+ct_commit { };
-+    formats as ct_commit { drop; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15])
-     has prereqs ip
--ct_commit();
--    formats as ct_commit;
--    encodes as ct(commit,zone=NXM_NX_REG13[0..15])
--    has prereqs ip
--ct_commit(ct_mark=1);
--    formats as ct_commit(ct_mark=0x1);
-+ct_commit { ct_mark=1; };
-+    formats as ct_commit { ct_mark = 1; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark))
-     has prereqs ip
--ct_commit(ct_mark=1/1);
--    formats as ct_commit(ct_mark=0x1/0x1);
-+ct_commit { ct_mark=1/1; };
-+    formats as ct_commit { ct_mark = 1/1; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_mark))
-     has prereqs ip
--ct_commit(ct_label=1);
--    formats as ct_commit(ct_label=0x1);
-+ct_commit { ct_label=1; };
-+    formats as ct_commit { ct_label = 1; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_label))
-     has prereqs ip
--ct_commit(ct_label=1/1);
--    formats as ct_commit(ct_label=0x1/0x1);
-+ct_commit { ct_label=1/1; };
-+    formats as ct_commit { ct_label = 1/1; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_label))
-     has prereqs ip
--ct_commit(ct_mark=1, ct_label=2);
--    formats as ct_commit(ct_mark=0x1, ct_label=0x2);
-+ct_commit { ct_mark=1; ct_label=2; };
-+    formats as ct_commit { ct_mark = 1; ct_label = 2; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark,set_field:0x2->ct_label))
-     has prereqs ip
- 
--ct_commit(ct_label=0x01020304050607080910111213141516);
--    formats as ct_commit(ct_label=0x1020304050607080910111213141516);
-+ct_commit { ct_label=0x01020304050607080910111213141516; };
-+    formats as ct_commit { ct_label = 0x1020304050607080910111213141516; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1020304050607080910111213141516->ct_label))
-     has prereqs ip
--ct_commit(ct_label=0x181716151413121110090807060504030201);
--    formats as ct_commit(ct_label=0x16151413121110090807060504030201);
--    encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x16151413121110090807060504030201->ct_label))
--    has prereqs ip
--ct_commit(ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000);
-+ct_commit { ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000; };
-+    formats as ct_commit { ct_label = 0x1000000000000000000000000000000/0x1000000000000000000000000000000; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1000000000000000000000000000000/0x1000000000000000000000000000000->ct_label))
-     has prereqs ip
--ct_commit(ct_label=18446744073709551615);
--    formats as ct_commit(ct_label=0xffffffffffffffff);
-+ct_commit { ct_label=18446744073709551615; };
-+    formats as ct_commit { ct_label = 18446744073709551615; };
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xffffffffffffffff->ct_label))
-     has prereqs ip
--ct_commit(ct_label=18446744073709551616);
-+ct_commit { ct_label[0..47] = 0x00000f040201; ct_label[48..63] = 0x0002; };
-+    formats as ct_commit { ct_label[0..47] = 0xf040201; ct_label[48..63] = 0x2; };
-+    encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xf040201/0xffffffffffff->ct_label,set_field:0x2000000000000/0xffff000000000000->ct_label))
-+    has prereqs ip
-+ct_commit { ct_label=18446744073709551616; };
-     Decimal constants must be less than 2**64.
-+ct_commit { ct_label=0x181716151413121110090807060504030201; };
-+    141-bit constant is not compatible with 128-bit field ct_label.
-+ct_commit { ip4.dst = 192.168.0.1; };
-+    Field ip4.dst is not modifiable.
-+
-+ct_mark = 12345
-+    Field ct_mark is not modifiable.
-+ct_label = 0xcafe
-+    Field ct_label is not modifiable.
-+ct_label.blocked = 1/1
-+    Field ct_label.blocked is not modifiable.
- 
- # ct_dnat
- ct_dnat;
--- 
-2.26.2
-
diff --git a/SOURCES/0013-Add-ECMP-symmetric-replies.patch b/SOURCES/0013-Add-ECMP-symmetric-replies.patch
deleted file mode 100644
index 6a0a858..0000000
--- a/SOURCES/0013-Add-ECMP-symmetric-replies.patch
+++ /dev/null
@@ -1,922 +0,0 @@
-From 750e47ec508977af7bb37e9d0c98dd13984e9002 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 20 Jul 2020 15:01:32 -0400
-Subject: [PATCH 13/22] Add ECMP symmetric replies.
-
-When traffic arrives over an ECMP route, there is no guarantee that the
-reply traffic will egress over the same route. Sometimes, the nature of
-the traffic (or the intervening equipment) means that it is important
-for reply traffic to go out the same route it came in.
-
-This commit introduces optional ECMP symmetric reply behavior. If
-configured, then traffic to or from the ECMP route will be sent to
-conntrack. New incoming traffic over the route will have the source MAC
-address and incoming port saved in the ct_label. Reply traffic then uses
-this saved information to send the packet back out the same way it came
-in.
-
-To facilitate this, a new table was added to the ingress logical router
-pipeline. The ECMP_STATEFUL table is responsible for committing to
-conntrack and setting the ct_label when it detects new incoming traffic
-from the route.
-
-Since ingress pipeline logic on the logical router depends on ct state
-of a particular hypervisor, this feature is only usable on gateway
-routers.
-
-Change-Id: I6e5177a6de2258286869114ab6b4028667fee009
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1849683
-Acked-by: Numan Siddique <numans@ovn.org>
----
- lib/logical-fields.c      |   4 +
- northd/ovn-northd.8.xml   |  49 +++++++++---
- northd/ovn-northd.c       | 123 ++++++++++++++++++++++++++----
- ovn-architecture.7.xml    |   7 +-
- ovn-nb.ovsschema          |   7 +-
- ovn-nb.xml                |  16 ++++
- tests/ovn.at              | 152 ++++++++++++++++++++++++++++++++++----
- tests/system-ovn.at       | 143 +++++++++++++++++++++++++++++++++++
- utilities/ovn-nbctl.8.xml |  31 ++++++--
- utilities/ovn-nbctl.c     |  18 ++++-
- 10 files changed, 496 insertions(+), 54 deletions(-)
-
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index fde53a47e..15342dded 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -130,6 +130,10 @@ ovn_init_symtab(struct shash *symtab)
-                                  WR_CT_COMMIT);
-     expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL,
-                                     "ct_label[0]", WR_CT_COMMIT);
-+    expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_eth", NULL,
-+                                    "ct_label[32..79]", WR_CT_COMMIT);
-+    expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_port", NULL,
-+                                    "ct_label[80..95]", WR_CT_COMMIT);
- 
-     expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);
- 
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 623768988..f35a035fd 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2175,15 +2175,31 @@ icmp6 {
-     <p>
-       This is to send packets to connection tracker for tracking and
-       defragmentation.  It contains a priority-0 flow that simply moves traffic
--      to the next table.  If load balancing rules with virtual IP addresses
--      (and ports) are configured in <code>OVN_Northbound</code> database for a
--      Gateway router, a priority-100 flow is added for each configured virtual
--      IP address <var>VIP</var>. For IPv4 <var>VIPs</var> the flow matches
--      <code>ip &amp;&amp; ip4.dst == <var>VIP</var></code>.  For IPv6
--      <var>VIPs</var>, the flow matches <code>ip &amp;&amp; ip6.dst ==
--      <var>VIP</var></code>.  The flow uses the action <code>ct_next;</code>
--      to send IP packets to the connection tracker for packet de-fragmentation
--      and tracking before sending it to the next table.
-+      to the next table.
-+    </p>
-+
-+    <p>
-+      If load balancing rules with virtual IP addresses (and ports) are
-+      configured in <code>OVN_Northbound</code> database for a Gateway router,
-+      a priority-100 flow is added for each configured virtual IP address
-+      <var>VIP</var>. For IPv4 <var>VIPs</var> the flow matches <code>ip
-+      &amp;&amp; ip4.dst == <var>VIP</var></code>.  For IPv6 <var>VIPs</var>,
-+      the flow matches <code>ip &amp;&amp; ip6.dst == <var>VIP</var></code>.
-+      The flow uses the action <code>ct_next;</code> to send IP packets to the
-+      connection tracker for packet de-fragmentation and tracking before
-+      sending it to the next table.
-+    </p>
-+
-+    <p>
-+      If ECMP routes with symmetric reply are configured in the
-+      <code>OVN_Northbound</code> database for a gateway router, a priority-100
-+      flow is added for each router port on which symmetric replies are
-+      configured. The matching logic for these ports essentially reverses the
-+      configured logic of the ECMP route. So for instance, a route with a
-+      destination routing policy will instead match if the source IP address
-+      matches the static route's prefix. The flow uses the action
-+      <code>ct_next</code> to send IP packets to the connection tracker for
-+      packet de-fragmentation and tracking before sending it to the next table.
-     </p>
- 
-     <h3>Ingress Table 5: UNSNAT</h3>
-@@ -2544,7 +2560,15 @@ output;
-       table.  This table, instead, is responsible for determine the ECMP
-       group id and select a member id within the group based on 5-tuple
-       hashing.  It stores group id in <code>reg8[0..15]</code> and member id in
--      <code>reg8[16..31]</code>.
-+      <code>reg8[16..31]</code>. This step is skipped if the traffic going
-+      out the ECMP route is reply traffic, and the ECMP route was configured
-+      to use symmetric replies. Instead, the stored <code>ct_label</code> value
-+      is used to choose the destination. The least significant 48 bits of the
-+      <code>ct_label</code> tell the destination MAC address to which the
-+      packet should be sent. The next 16 bits tell the logical router port on
-+      which the packet should be sent. These values in the
-+      <code>ct_label</code> are set when the initial ingress traffic is
-+      received over the ECMP route.
-     </p>
- 
-     <p>
-@@ -2694,6 +2718,11 @@ select(reg8[16..31], <var>MID1</var>, <var>MID2</var>, ...);
-       address and <code>reg1</code> as the source protocol address).
-     </p>
- 
-+    <p>
-+      This processing is skipped for reply traffic being sent out of an ECMP
-+      route if the route was configured to use symmetric replies.
-+    </p>
-+
-     <p>
-       This table contains the following logical flows:
-     </p>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 44e7d9365..cb8e25bdf 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -171,16 +171,17 @@ enum ovn_stage {
-     PIPELINE_STAGE(ROUTER, IN,  DEFRAG,          4, "lr_in_defrag")       \
-     PIPELINE_STAGE(ROUTER, IN,  UNSNAT,          5, "lr_in_unsnat")       \
-     PIPELINE_STAGE(ROUTER, IN,  DNAT,            6, "lr_in_dnat")         \
--    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,   7, "lr_in_nd_ra_options") \
--    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE,  8, "lr_in_nd_ra_response") \
--    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,      9, "lr_in_ip_routing")   \
--    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_ECMP, 10, "lr_in_ip_routing_ecmp") \
--    PIPELINE_STAGE(ROUTER, IN,  POLICY,          11, "lr_in_policy")       \
--    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,     12, "lr_in_arp_resolve")  \
--    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN   ,  13, "lr_in_chk_pkt_len")   \
--    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,     14,"lr_in_larger_pkts")   \
--    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,     15, "lr_in_gw_redirect")  \
--    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,     16, "lr_in_arp_request")  \
-+    PIPELINE_STAGE(ROUTER, IN,  ECMP_STATEFUL,   7, "lr_in_ecmp_stateful") \
-+    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,   8, "lr_in_nd_ra_options") \
-+    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE,  9, "lr_in_nd_ra_response") \
-+    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,      10, "lr_in_ip_routing")   \
-+    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_ECMP, 11, "lr_in_ip_routing_ecmp") \
-+    PIPELINE_STAGE(ROUTER, IN,  POLICY,          12, "lr_in_policy")       \
-+    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,     13, "lr_in_arp_resolve")  \
-+    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN   ,  14, "lr_in_chk_pkt_len")   \
-+    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,     15,"lr_in_larger_pkts")   \
-+    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,     16, "lr_in_gw_redirect")  \
-+    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,     17, "lr_in_arp_request")  \
-                                                                       \
-     /* Logical router egress stages. */                               \
-     PIPELINE_STAGE(ROUTER, OUT, UNDNAT,    0, "lr_out_undnat")        \
-@@ -7430,6 +7431,7 @@ struct parsed_route {
-     bool is_src_route;
-     uint32_t hash;
-     const struct nbrec_logical_router_static_route *route;
-+    bool ecmp_symmetric_reply;
- };
- 
- static uint32_t
-@@ -7491,6 +7493,8 @@ parsed_routes_add(struct ovs_list *routes,
-                                                  "src-ip"));
-     pr->hash = route_hash(pr);
-     pr->route = route;
-+    pr->ecmp_symmetric_reply = smap_get_bool(&route->options,
-+                                             "ecmp_symmetric_reply", false);
-     ovs_list_insert(routes, &pr->list_node);
-     return pr;
- }
-@@ -7739,18 +7743,95 @@ find_static_route_outport(struct ovn_datapath *od, struct hmap *ports,
-     return true;
- }
- 
-+static void
-+add_ecmp_symmetric_reply_flows(struct hmap *lflows,
-+                               struct ovn_datapath *od,
-+                               const char *port_ip,
-+                               struct ovn_port *out_port,
-+                               const struct parsed_route *route,
-+                               struct ds *route_match)
-+{
-+    const struct nbrec_logical_router_static_route *st_route = route->route;
-+    struct ds match = DS_EMPTY_INITIALIZER;
-+    struct ds actions = DS_EMPTY_INITIALIZER;
-+    struct ds ecmp_reply = DS_EMPTY_INITIALIZER;
-+    char *cidr = normalize_v46_prefix(&route->prefix, route->plen);
-+
-+    /* If symmetric ECMP replies are enabled, then packets that arrive over
-+     * an ECMP route need to go through conntrack.
-+     */
-+    ds_put_format(&match, "inport == %s && ip%s.%s == %s",
-+                  out_port->json_key,
-+                  route->prefix.family == AF_INET ? "4" : "6",
-+                  route->is_src_route ? "dst" : "src",
-+                  cidr);
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100,
-+                            ds_cstr(&match), "ct_next;",
-+                            &st_route->header_);
-+
-+    /* And packets that go out over an ECMP route need conntrack */
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100,
-+                            ds_cstr(route_match), "ct_next;",
-+                            &st_route->header_);
-+
-+    /* Save src eth and inport in ct_label for packets that arrive over
-+     * an ECMP route.
-+     *
-+     * NOTE: we purposely are not clearing match before this
-+     * ds_put_cstr() call. The previous contents are needed.
-+     */
-+    ds_put_cstr(&match, " && (ct.new && !ct.est)");
-+
-+    ds_put_format(&actions, "ct_commit { ct_label.ecmp_reply_eth = eth.src;"
-+                  " ct_label.ecmp_reply_port = %" PRId64 ";}; next;",
-+                  out_port->sb->tunnel_key);
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
-+                            ds_cstr(&match), ds_cstr(&actions),
-+                            &st_route->header_);
-+
-+    /* Bypass ECMP selection if we already have ct_label information
-+     * for where to route the packet.
-+     */
-+    ds_put_format(&ecmp_reply, "ct.rpl && ct_label.ecmp_reply_port == %"
-+                  PRId64, out_port->sb->tunnel_key);
-+    ds_clear(&match);
-+    ds_put_format(&match, "%s && %s", ds_cstr(&ecmp_reply),
-+                  ds_cstr(route_match));
-+    ds_clear(&actions);
-+    ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; "
-+                  "eth.src = %s; %sreg1 = %s; outport = %s; next;",
-+                  out_port->lrp_networks.ea_s,
-+                  route->prefix.family == AF_INET ? "" : "xx",
-+                  port_ip, out_port->json_key);
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, 100,
-+                           ds_cstr(&match), ds_cstr(&actions),
-+                           &st_route->header_);
-+
-+    /* Egress reply traffic for symmetric ECMP routes skips router policies. */
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, 65535,
-+                            ds_cstr(&ecmp_reply), "next;",
-+                            &st_route->header_);
-+
-+    ds_clear(&actions);
-+    ds_put_cstr(&actions, "eth.dst = ct_label.ecmp_reply_eth; next;");
-+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_RESOLVE,
-+                            200, ds_cstr(&ecmp_reply),
-+                            ds_cstr(&actions), &st_route->header_);
-+}
-+
- static void
- build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-                       struct hmap *ports, struct ecmp_groups_node *eg)
- 
- {
-     bool is_ipv4 = (eg->prefix.family == AF_INET);
--    struct ds match = DS_EMPTY_INITIALIZER;
-     uint16_t priority;
-+    struct ecmp_route_list_node *er;
-+    struct ds route_match = DS_EMPTY_INITIALIZER;
- 
-     char *prefix_s = build_route_prefix_s(&eg->prefix, eg->plen);
-     build_route_match(NULL, prefix_s, eg->plen, eg->is_src_route, is_ipv4,
--                      &match, &priority);
-+                      &route_match, &priority);
-     free(prefix_s);
- 
-     struct ds actions = DS_EMPTY_INITIALIZER;
-@@ -7758,7 +7839,6 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-                   "; %s = select(", REG_ECMP_GROUP_ID, eg->id,
-                   REG_ECMP_MEMBER_ID);
- 
--    struct ecmp_route_list_node *er;
-     bool is_first = true;
-     LIST_FOR_EACH (er, list_node, &eg->route_list) {
-         if (is_first) {
-@@ -7772,11 +7852,12 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-     ds_put_cstr(&actions, ");");
- 
-     ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, priority,
--                  ds_cstr(&match), ds_cstr(&actions));
-+                  ds_cstr(&route_match), ds_cstr(&actions));
- 
-     /* Add per member flow */
-+    struct ds match = DS_EMPTY_INITIALIZER;
-+    struct sset visited_ports = SSET_INITIALIZER(&visited_ports);
-     LIST_FOR_EACH (er, list_node, &eg->route_list) {
--
-         const struct parsed_route *route_ = er->route;
-         const struct nbrec_logical_router_static_route *route = route_->route;
-         /* Find the outgoing port. */
-@@ -7786,6 +7867,15 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-                                        &out_port)) {
-             continue;
-         }
-+        /* Symmetric ECMP reply is only usable on gateway routers.
-+         * It is NOT usable on distributed routers with a gateway port.
-+         */
-+        if (smap_get(&od->nbr->options, "chassis") &&
-+            route_->ecmp_symmetric_reply && sset_add(&visited_ports,
-+                                                     out_port->key)) {
-+            add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port,
-+                                           route_, &route_match);
-+        }
-         ds_clear(&match);
-         ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
-                       REG_ECMP_MEMBER_ID" == %"PRIu16,
-@@ -7806,7 +7896,9 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-                                 ds_cstr(&match), ds_cstr(&actions),
-                                 &route->header_);
-     }
-+    sset_destroy(&visited_ports);
-     ds_destroy(&match);
-+    ds_destroy(&route_match);
-     ds_destroy(&actions);
- }
- 
-@@ -9161,6 +9253,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 0, "1", "next;");
-         ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 0, "1", "next;");
-         ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
-+        ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;");
- 
-         /* Send the IPv6 NS packets to next table. When ovn-controller
-          * generates IPv6 NS (for the action - nd_ns{}), the injected
-diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml
-index 246cebc19..b1a462933 100644
---- a/ovn-architecture.7.xml
-+++ b/ovn-architecture.7.xml
-@@ -1210,11 +1210,12 @@
-     <dd>
-       Fields that denote the connection tracking zones for routers.  These
-       values only have local significance and are not meaningful between
--      chassis.  OVN stores the zone information for DNATting in Open vSwitch
-+      chassis.  OVN stores the zone information for north to south traffic
-+      (for DNATting or ECMP symmetric replies) in Open vSwitch
-         <!-- Keep the following in sync with MFF_LOG_DNAT_ZONE and
-         MFF_LOG_SNAT_ZONE in ovn/lib/logical-fields.h. -->
--      extension register number 11 and zone information for SNATing in
--      Open vSwitch extension register number 12.
-+      extension register number 11 and zone information for south to north
-+      traffic (for SNATing) in Open vSwitch extension register number 12.
-     </dd>
- 
-     <dt>logical flow flags</dt>
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index da9af7157..0c939b715 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
--    "version": "5.24.0",
--    "cksum": "1092394564 25961",
-+    "version": "5.25.0",
-+    "cksum": "1354137211 26116",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -365,6 +365,9 @@
-                                     "min": 0, "max": 1}},
-                 "nexthop": {"type": "string"},
-                 "output_port": {"type": {"key": "string", "min": 0, "max": 1}},
-+                "options": {
-+                    "type": {"key": "string", "value": "string",
-+                             "min": 0, "max": "unlimited"}},
-                 "external_ids": {
-                     "type": {"key": "string", "value": "string",
-                              "min": 0, "max": "unlimited"}}},
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index 02161372a..98d36b270 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -2520,6 +2520,22 @@
-       </column>
-     </group>
- 
-+    <group title="Common options">
-+      <column name="options">
-+        This column provides general key/value settings. The supported
-+        options are described individually below.
-+      </column>
-+
-+      <column name="options" key="ecmp_symmetric_reply">
-+        It true, then new traffic that arrives over this route will have
-+        its reply traffic bypass ECMP route selection and will be sent out
-+        this route instead. Note that this option overrides any rules set
-+        in the <ref table="Logical_Router_policy" /> table. This option
-+        only works on gateway routers (routers that have
-+        <ref column="options" key="chassis" table="Logical_Router" /> set).
-+      </column>
-+    </group>
-+
-   </table>
- 
-   <table name="Logical_Router_Policy" title="Logical router policies">
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 4c68b77d8..b626bcfcc 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -192,6 +192,8 @@ ct.snat = ct_state[6]
- ct.trk = ct_state[5]
- ct_label = NXM_NX_CT_LABEL
- ct_label.blocked = ct_label[0]
-+ct_label.ecmp_reply_eth = ct_label[32..79]
-+ct_label.ecmp_reply_port = ct_label[80..95]
- ct_mark = NXM_NX_CT_MARK
- ct_state = NXM_NX_CT_STATE
- ]])
-@@ -16128,7 +16130,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- # Since the sw0-vir is not claimed by any chassis, eth.dst should be set to
- # zero if the ip4.dst is the virtual ip in the router pipeline.
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
- ])
- 
- ip_to_hex() {
-@@ -16179,7 +16181,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- # There should be an arp resolve flow to resolve the virtual_ip with the
- # sw0-p1's MAC.
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
- ])
- 
- # Forcibly clear virtual_parent. ovn-controller should release the binding
-@@ -16220,7 +16222,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- # There should be an arp resolve flow to resolve the virtual_ip with the
- # sw0-p2's MAC.
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;)
- ])
- 
- # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir
-@@ -16243,7 +16245,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- # There should be an arp resolve flow to resolve the virtual_ip with the
- # sw0-p3's MAC.
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
- ])
- 
- # Now send arp reply from sw0-p1. hv1 should claim sw0-vir
-@@ -16264,7 +16266,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- > lflows.txt
- 
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
- ])
- 
- # Delete hv1-vif1 port. hv1 should release sw0-vir
-@@ -16282,7 +16284,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- > lflows.txt
- 
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
- ])
- 
- # Now send arp reply from sw0-p2. hv2 should claim sw0-vir
-@@ -16303,7 +16305,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
- > lflows.txt
- 
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=12(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
-+  table=13(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
- ])
- 
- # Delete sw0-p2 logical port
-@@ -20483,22 +20485,22 @@ ovn-nbctl set logical_router_policy $pol5 options:pkt_mark=5
- ovn-nbctl --wait=hv sync
- 
- OVS_WAIT_UNTIL([
--    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x64->NXM_NX_PKT_MARK" -c)
- ])
- 
- OVS_WAIT_UNTIL([
--    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x3->NXM_NX_PKT_MARK" -c)
- ])
- 
- OVS_WAIT_UNTIL([
--    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x4->NXM_NX_PKT_MARK" -c)
- ])
- 
- OVS_WAIT_UNTIL([
--    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x5->NXM_NX_PKT_MARK" -c)
- ])
- 
-@@ -20589,12 +20591,12 @@ send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \
-     $(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120)
- 
- OVS_WAIT_UNTIL([
--    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x2->NXM_NX_PKT_MARK" -c)
- ])
- 
- AT_CHECK([
--    test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=19 | \
-+    test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
-     grep "load:0x64->NXM_NX_PKT_MARK" -c)
- ])
- 
-@@ -20927,7 +20929,6 @@ AT_CHECK([test "$hv2_offlows" = "$hv2_offlows_mon"])
- OVN_CLEANUP([hv1], [hv2])
- AT_CLEANUP
- 
--
- AT_SETUP([ovn -- controller I-P handling when lrp added last])
- 
- ovn_start
-@@ -21111,6 +21112,129 @@ AT_CHECK([test ! -z $p1_zoneid])
- OVN_CLEANUP([hv1])
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- Symmetric ECMP reply flows])
-+ovn_start
-+
-+net_add n1
-+sim_add hv1
-+as hv1
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+
-+sim_add hv2
-+as hv2
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.2
-+
-+# Logical network
-+#
-+#   ls1 \
-+#        \
-+#         DR -- join -- GW -- ext
-+#        /
-+#   ls2 /
-+#
-+#  ls1 and ls2 are internal switches connected to distributed router
-+#  DR. DR is then connected via a join switch to gateway router GW.
-+#  GW is then connected to external switch ext. In real life, this
-+#  would likely have a localnet port, but for the purposes of this test
-+#  it is unnecessary.
-+
-+ovn-nbctl create Logical_Router name=DR
-+gw_uuid=$(ovn-nbctl create Logical_Router name=GW)
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl ls-add ls2
-+ovn-nbctl ls-add join
-+ovn-nbctl ls-add ext
-+
-+# Connect ls1 to DR
-+ovn-nbctl lrp-add DR dr-ls1 00:00:01:01:02:03 10.0.0.1/24
-+ovn-nbctl lsp-add ls1 ls1-dr -- set Logical_Switch_Port ls1-dr \
-+    type=router options:router-port=dr-ls1 addresses='"00:00:01:01:02:03"'
-+
-+# Connect ls2 to DR
-+ovn-nbctl lrp-add DR dr-ls2 00:00:01:01:02:04 10.0.0.2/24
-+ovn-nbctl lsp-add ls2 ls2-dr -- set Logical_Switch_Port ls2-dr \
-+    type=router options:router-port=dr-ls2 addresses='"00:00:01:01:02:04"'
-+
-+# Connect join to DR
-+ovn-nbctl lrp-add DR dr-join 00:00:02:01:02:03 20.0.0.1/24
-+ovn-nbctl lsp-add join join-dr -- set Logical_Switch_Port join-dr \
-+    type=router options:router-port=dr-join addresses='"00:00:02:01:02:03"'
-+
-+# Connect join to GW
-+ovn-nbctl lrp-add GW gw-join 00:00:02:01:02:04 20.0.0.2/24
-+ovn-nbctl lsp-add join join-gw -- set Logical_Switch_Port join-gw \
-+    type=router options:router-port=gw-join addresses='"00:00:02:01:02:04"'
-+
-+# Connect ext to GW
-+ovn-nbctl lrp-add GW gw-ext 00:00:03:01:02:03 172.16.0.1/16
-+ovn-nbctl lsp-add ext ext-gw -- set Logical_Switch_Port ext-gw \
-+    type=router options:router-port=gw-ext addresses='"00:00:03:01:02:03"'
-+
-+ovn-nbctl lr-route-add GW 10.0.0.0/24 20.0.0.1
-+ovn-nbctl --policy="src-ip" lr-route-add DR 10.0.0.0/24 20.0.0.2
-+
-+# Now add some ECMP routes to the GW router.
-+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add GW 10.0.0.0/24 172.16.0.2
-+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add GW 10.0.0.0/24 172.16.0.3
-+
-+ovn-nbctl --wait=hv sync
-+
-+# Ensure ECMP symmetric reply flows are not present on any hypervisor.
-+AT_CHECK([
-+    test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=15 | \
-+    grep "priority=100" | \
-+    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-+])
-+AT_CHECK([
-+    test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \
-+    grep "priority=200" | \
-+    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
-+])
-+
-+AT_CHECK([
-+    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=15 | \
-+    grep "priority=100" | \
-+    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-+])
-+AT_CHECK([
-+    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=21 | \
-+    grep "priority=200" | \
-+    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
-+])
-+
-+# Now make GW a gateway router on hv1
-+ovn-nbctl set Logical_Router $gw_uuid options:chassis=hv1
-+ovn-nbctl --wait=hv sync
-+
-+# And ensure that ECMP symmetric reply flows are present only on hv1
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=15 | \
-+    grep "priority=100" | \
-+    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-+])
-+AT_CHECK([
-+    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \
-+    grep "priority=200" | \
-+    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
-+])
-+
-+AT_CHECK([
-+    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=15 | \
-+    grep "priority=100" | \
-+    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-+])
-+AT_CHECK([
-+    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=21 | \
-+    grep "priority=200" | \
-+    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
-+])
-+
-+OVN_CLEANUP([hv1], [hv2])
-+AT_CLEANUP
-+
- # Test option:dynamic_neigh_routers. No static neighbor flows when enabled, and
- # traffic should still work, with the help of dynamic mac_bindings.
- AT_SETUP([ovn -- Dynamic neighbor between LRs])
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 94e3964e5..bce097b17 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -4555,6 +4555,149 @@ NS_CHECK_EXEC([sw0-p1-f], [ping -q -c 3 -i 0.3 -w 2 10.0.0.5 | FORMAT_PING], \
- 3 packets transmitted, 3 received, 0% packet loss, time 0ms
- ])
- 
-+OVS_APP_EXIT_AND_WAIT([ovn-controller])
-+
-+as ovn-sb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as ovn-nb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as northd
-+OVS_APP_EXIT_AND_WAIT([ovn-northd])
-+
-+as
-+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-+/connection dropped.*/d"])
-+
-+AT_CLEANUP
-+
-+AT_SETUP([ovn -- ECMP symmetric reply])
-+AT_KEYWORDS([ecmp])
-+
-+CHECK_CONNTRACK()
-+ovn_start
-+
-+OVS_TRAFFIC_VSWITCHD_START()
-+ADD_BR([br-int])
-+
-+# Set external-ids in br-int needed for ovn-controller
-+ovs-vsctl \
-+        -- set Open_vSwitch . external-ids:system-id=hv1 \
-+        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
-+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
-+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
-+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-+
-+# Start ovn-controller
-+start_daemon ovn-controller
-+
-+# Logical network:
-+# Alice is connected to gateway router R1. R1 is connected to two "external"
-+# routers, R2 and R3 via an "ext" switch.
-+# Bob is connected to both R2 and R3. R1 contains two ECMP routes, one through R2
-+# and one through R3, to Bob.
-+#
-+#     alice -- R1 -- ext ---- R2
-+#                     |         \
-+#                     |           bob
-+#                     |         /
-+#                     + ----- R3
-+#
-+# For this test, Bob sends request traffic through R2 to Alice. We want to ensure that
-+# all response traffic from Alice is routed through R2 as well.
-+
-+ovn-nbctl create Logical_Router name=R1 options:chassis=hv1
-+ovn-nbctl create Logical_Router name=R2
-+ovn-nbctl create Logical_Router name=R3
-+
-+ovn-nbctl ls-add alice
-+ovn-nbctl ls-add bob
-+ovn-nbctl ls-add ext
-+
-+# connect alice to R1
-+ovn-nbctl lrp-add R1 alice 00:00:01:01:02:03 10.0.0.1/24
-+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
-+    type=router options:router-port=alice addresses='"00:00:01:01:02:03"'
-+
-+# connect bob to R2
-+ovn-nbctl lrp-add R2 R2_bob 00:00:02:01:02:03 172.16.0.2/16
-+ovn-nbctl lsp-add bob rp2-bob -- set Logical_Switch_Port rp2-bob \
-+    type=router options:router-port=R2_bob addresses='"00:00:02:01:02:03"'
-+
-+# connect bob to R3
-+ovn-nbctl lrp-add R3 R3_bob 00:00:02:01:02:04 172.16.0.3/16
-+ovn-nbctl lsp-add bob rp3-bob -- set Logical_Switch_Port rp3-bob \
-+    type=router options:router-port=R3_bob addresses='"00:00:02:01:02:04"'
-+
-+# Connect R1 to ext
-+ovn-nbctl lrp-add R1 R1_ext 00:00:04:01:02:03 20.0.0.1/24
-+ovn-nbctl lsp-add ext r1-ext -- set Logical_Switch_Port r1-ext \
-+    type=router options:router-port=R1_ext addresses='"00:00:04:01:02:03"'
-+
-+# Connect R2 to ext
-+ovn-nbctl lrp-add R2 R2_ext 00:00:04:01:02:04 20.0.0.2/24
-+ovn-nbctl lsp-add ext r2-ext -- set Logical_Switch_Port r2-ext \
-+    type=router options:router-port=R2_ext addresses='"00:00:04:01:02:04"'
-+
-+# Connect R3 to ext
-+ovn-nbctl lrp-add R3 R3_ext 00:00:04:01:02:05 20.0.0.3/24
-+ovn-nbctl lsp-add ext r3-ext -- set Logical_Switch_Port r3-ext \
-+    type=router options:router-port=R3_ext addresses='"00:00:04:01:02:05"'
-+
-+# Install ECMP routes for alice.
-+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add R1 10.0.0.0/24 20.0.0.2
-+ovn-nbctl --ecmp-symmetric-reply --policy="src-ip" lr-route-add R1 10.0.0.0/24 20.0.0.3
-+
-+# Static Routes
-+ovn-nbctl lr-route-add R2 10.0.0.0/24 20.0.0.1
-+ovn-nbctl lr-route-add R3 10.0.0.0/24 20.0.0.1
-+
-+# Logical port 'alice1' in switch 'alice'.
-+ADD_NAMESPACES(alice1)
-+ADD_VETH(alice1, alice1, br-int, "10.0.0.2/24", "f0:00:00:01:02:04", \
-+         "10.0.0.1")
-+ovn-nbctl lsp-add alice alice1 \
-+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 10.0.0.2"
-+
-+# Logical port 'bob1' in switch 'bob'.
-+ADD_NAMESPACES(bob1)
-+ADD_VETH(bob1, bob1, br-int, "172.16.0.1/16", "f0:00:00:01:02:06", \
-+         "172.16.0.2")
-+ovn-nbctl lsp-add bob bob1 \
-+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.0.1"
-+
-+# Ensure ovn-controller is caught up
-+ovn-nbctl --wait=hv sync
-+
-+on_exit 'ovs-ofctl dump-flows br-int'
-+
-+# 'bob1' should be able to ping 'alice1' directly.
-+NS_CHECK_EXEC([bob1], [ping -q -c 20 -i 0.3 -w 15 10.0.0.2 | FORMAT_PING], \
-+[0], [dnl
-+20 packets transmitted, 20 received, 0% packet loss, time 0ms
-+])
-+
-+# Ensure conntrack entry is present. We should not try to predict
-+# the tunnel key for the output port, so we strip it from the labels
-+# and just ensure that the known ethernet address is present.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
-+sed -e 's/labels=0x[[0-9a-f]]*00000401020400000000/labels=0x00000401020400000000/'], [0], [dnl
-+icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=<cleared>,type=0,code=0),zone=<cleared>,labels=0x00000401020400000000
-+])
-+
-+# Ensure datapaths show conntrack states as expected
-+# Like with conntrack entries, we shouldn't try to predict
-+# port binding tunnel keys. So omit them from expected labels.
-+AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x.*00000401020400000000/0xffffffffffffffff00000000)' -c], [0], [dnl
-+1
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x.*00000401020400000000/0xffffffffffffffff00000000)' -c], [0], [dnl
-+1
-+])
-+
-+ovs-ofctl dump-flows br-int
- 
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
-diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
-index de86b70e6..18bf90e08 100644
---- a/utilities/ovn-nbctl.8.xml
-+++ b/utilities/ovn-nbctl.8.xml
-@@ -658,7 +658,8 @@
- 
-     <dl>
-       <dt>[<code>--may-exist</code>] [<code>--policy</code>=<var>POLICY</var>]
--        [<code>--ecmp</code>] <code>lr-route-add</code> <var>router</var>
-+        [<code>--ecmp</code>] [<code>--ecmp-symmetric-reply</code>]
-+        <code>lr-route-add</code> <var>router</var>
-         <var>prefix</var> <var>nexthop</var> [<var>port</var>]</dt>
-       <dd>
-         <p>
-@@ -680,15 +681,31 @@
-           specified, the default is "dst-ip".
-         </p>
- 
-+        <p>
-+          The <code>--ecmp</code> option allows for multiple routes with the
-+          same <var>prefix</var> <var>POLICY</var> but different
-+          <var>nexthop</var> and <var>port</var> to be added.
-+        </p>
-+
-+        <p>
-+          The <code>--ecmp-symmetric-reply</code> option makes it so that
-+          traffic that arrives over an ECMP route will have its reply traffic
-+          sent out over that same route. Setting
-+          <code>--ecmp-symmetric-reply</code> implies <code>--ecmp</code> so
-+          it is not necessary to set both.
-+        </p>
-+
-         <p>
-           It is an error if a route with <var>prefix</var> and
--          <var>POLICY</var> already exists, unless <code>--may-exist</code> or
--          <code>--ecmp</code> is specified.  If <code>--may-exist</code> is
--          specified but not <code>--ecmp</code>, the existed route will be
--          updated with the new nexthop and port.  If <code>--ecmp</code> is
-+          <var>POLICY</var> already exists, unless <code>--may-exist</code>,
-+          <code>--ecmp</code>, or <code>--ecmp-symmetric-reply</code> is
-+          specified.  If <code>--may-exist</code> is specified but not
-+          <code>--ecmp</code> or <code>--ecmp-symmetric-reply</code>, the
-+          existed route will be updated with the new nexthop and port.  If
-+          <code>--ecmp</code> or <code>--ecmp-symmetric-reply</code> is
-           specified, a new route will be added, regardless of the existed
--          route, which is useful when adding ECMP routes, i.e. routes with same
--          <var>POLICY</var> and <var>prefix</var> but different
-+          route., which is useful when adding ECMP routes, i.e. routes with
-+          same <var>POLICY</var> and <var>prefix</var> but different
-           <var>nexthop</var> and <var>port</var>.
-         </p>
-       </dd>
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index 0079ad5a6..e6d8dbe63 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -687,7 +687,8 @@ Logical router port commands:\n\
-                             ('overlay' or 'bridged')\n\
- \n\
- Route commands:\n\
--  [--policy=POLICY] [--ecmp] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
-+  [--policy=POLICY] [--ecmp] [--ecmp-symmetric-reply] lr-route-add ROUTER \n\
-+                            PREFIX NEXTHOP [PORT]\n\
-                             add a route to ROUTER\n\
-   [--policy=POLICY] lr-route-del ROUTER [PREFIX [NEXTHOP [PORT]]]\n\
-                             remove routes from ROUTER\n\
-@@ -3855,7 +3856,10 @@ nbctl_lr_route_add(struct ctl_context *ctx)
-     }
- 
-     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
--    bool ecmp = shash_find(&ctx->options, "--ecmp") != NULL;
-+    bool ecmp_symmetric_reply = shash_find(&ctx->options,
-+                                           "--ecmp-symmetric-reply") != NULL;
-+    bool ecmp = shash_find(&ctx->options, "--ecmp") != NULL ||
-+                ecmp_symmetric_reply;
-     if (!ecmp) {
-         for (int i = 0; i < lr->n_static_routes; i++) {
-             const struct nbrec_logical_router_static_route *route
-@@ -3920,6 +3924,13 @@ nbctl_lr_route_add(struct ctl_context *ctx)
-         nbrec_logical_router_static_route_set_policy(route, policy);
-     }
- 
-+    if (ecmp_symmetric_reply) {
-+        const struct smap options = SMAP_CONST1(&options,
-+                                                "ecmp_symmetric_reply",
-+                                                "true");
-+        nbrec_logical_router_static_route_set_options(route, &options);
-+    }
-+
-     nbrec_logical_router_verify_static_routes(lr);
-     struct nbrec_logical_router_static_route **new_routes
-         = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1));
-@@ -6361,7 +6372,8 @@ static const struct ctl_command_syntax nbctl_commands[] = {
- 
-     /* logical router route commands. */
-     { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
--      nbctl_lr_route_add, NULL, "--may-exist,--ecmp,--policy=", RW },
-+      nbctl_lr_route_add, NULL, "--may-exist,--ecmp,--ecmp-symmetric-reply,"
-+      "--policy=", RW },
-     { "lr-route-del", 1, 4, "ROUTER [PREFIX [NEXTHOP [PORT]]]", NULL,
-       nbctl_lr_route_del, NULL, "--if-exists,--policy=", RW },
-     { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
--- 
-2.26.2
-
diff --git a/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch b/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch
deleted file mode 100644
index 5e9b04a..0000000
--- a/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 677e51935f9030a779956cf3c6fb0f4d2fb19c10 Mon Sep 17 00:00:00 2001
-From: Han Zhou <hzhou@ovn.org>
-Date: Wed, 29 Jul 2020 18:36:38 -0700
-Subject: [PATCH 14/22] expr.c: Fix argument type of expr_write_scope().
-
-There is compile error introduced by the commit 2054d01247.
-***
-../lib/expr.c:3328:6: error: symbol 'expr_type_check' redeclared with different
-type (originally declared at ../include/ovn/expr.h:481) - incompatible argument
-4 (different signedness)
-Makefile:1971: recipe for target 'lib/expr.lo' failed
-***
-
-Fixes: 2054d01247 ("Add expression writeability scopes.")
-Acked-by: Ankur Sharma <ankur.sharma@nutanix.com>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-(cherry picked from upstream commit c64ed2a9bb175539ed9493a08e31cc29d10aa8df)
-
-Change-Id: I1de528787319b21a50bdcebbe60894026b32ab06
----
- lib/expr.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/lib/expr.c b/lib/expr.c
-index c07e7dd4d..6fb96757a 100644
---- a/lib/expr.c
-+++ b/lib/expr.c
-@@ -3327,7 +3327,7 @@ expr_evaluate(const struct expr *e, const struct flow *uflow,
-  * must free(). */
- char * OVS_WARN_UNUSED_RESULT
- expr_type_check(const struct expr_field *f, int n_bits, bool rw,
--                uint32_t write_scope)
-+                enum expr_write_scope write_scope)
- {
-     if (n_bits != f->n_bits) {
-         if (n_bits && f->n_bits) {
--- 
-2.26.2
-
diff --git a/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch b/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch
deleted file mode 100644
index e0e34c9..0000000
--- a/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-From bc0e773fa3620fa3c4fef817d3b2256542be6b11 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Thu, 6 Aug 2020 10:48:12 -0400
-Subject: [PATCH 15/22] Allow bare ct_commits when no nested actions are
- required.
-
-In the fixes commit below, ct_commit was changed to use nested actions.
-This requires that curly braces be present for all ct_commits. When
-adjusting ovn-northd, some ct_commits were not updated to have them.
-This commit changes the behavior of the ct_commit action not to require
-curly braces if there are no nested actions required.
-
-Fixes: 6cfb44a76c61("Used nested actions in ct_commit")
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
----
- lib/actions.c | 20 ++++++++++++++++----
- tests/ovn.at  |  5 ++++-
- 2 files changed, 20 insertions(+), 5 deletions(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index 79ac79a95..245486b0a 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -630,15 +630,27 @@ ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED)
- static void
- parse_CT_COMMIT(struct action_context *ctx)
- {
--
--    parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip",
--                        WR_CT_COMMIT);
-+    if (ctx->lexer->token.type == LEX_T_LCURLY) {
-+        parse_nested_action(ctx, OVNACT_CT_COMMIT, "ip",
-+                            WR_CT_COMMIT);
-+    } else {
-+        /* Add an empty nested action to allow for "ct_commit;" syntax */
-+        add_prerequisite(ctx, "ip");
-+        struct ovnact_nest *on = ovnact_put(ctx->ovnacts, OVNACT_CT_COMMIT,
-+                                            OVNACT_ALIGN(sizeof *on));
-+        on->nested_len = 0;
-+        on->nested = NULL;
-+    }
- }
- 
- static void
- format_CT_COMMIT(const struct ovnact_nest *on, struct ds *s)
- {
--    format_nested_action(on, "ct_commit", s);
-+    if (on->nested_len) {
-+        format_nested_action(on, "ct_commit", s);
-+    } else {
-+        ds_put_cstr(s, "ct_commit;");
-+    }
- }
- 
- static void
-diff --git a/tests/ovn.at b/tests/ovn.at
-index b626bcfcc..2651b3eac 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1047,8 +1047,11 @@ ct_next;
-     has prereqs ip
- 
- # ct_commit
-+ct_commit;
-+    encodes as ct(commit,zone=NXM_NX_REG13[0..15])
-+    has prereqs ip
- ct_commit { };
--    formats as ct_commit { drop; };
-+    formats as ct_commit;
-     encodes as ct(commit,zone=NXM_NX_REG13[0..15])
-     has prereqs ip
- ct_commit { ct_mark=1; };
--- 
-2.26.2
-
diff --git a/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch b/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch
deleted file mode 100644
index f9cf520..0000000
--- a/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch
+++ /dev/null
@@ -1,887 +0,0 @@
-From 3aeb8b73e0947010945aea8bf8fa6df1b7a558a7 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Thu, 6 Aug 2020 15:04:15 -0400
-Subject: [PATCH 16/22] Allow force_snat options to work for dual-stack
- routers.
-
-The lb_force_snat and dnat_force_snat options could accept only a single
-IP address. For routers that only route traffic of a single IP address
-family, this is fine. However, if a router routes both IPv4 and IPv6
-traffic, then this limitation is a problem.
-
-This patch addresses this problem by allowing for these options to
-specify both an IPv4 and IPv6 address.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit 474821c55608cbad5bdb8deee468827ab489c02b)
-
-Change-Id: I42266af72622d1f15ec94d68f106954cc49979bd
----
- northd/ovn-northd.c | 187 ++++++++-------
- ovn-nb.xml          |  24 +-
- tests/system-ovn.at | 541 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 657 insertions(+), 95 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index cb8e25bdf..1f5433d9d 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -8013,44 +8013,37 @@ op_put_v6_networks(struct ds *ds, const struct ovn_port *op)
-     ds_put_cstr(ds, "}");
- }
- 
--static const char *
-+static bool
- get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
--                  struct v46_ip *ip)
-+                  struct lport_addresses *laddrs)
- {
-     char *key = xasprintf("%s_force_snat_ip", key_type);
--    const char *ip_address = smap_get(&od->nbr->options, key);
-+    const char *addresses = smap_get(&od->nbr->options, key);
-     free(key);
- 
--    if (ip_address) {
--        ovs_be32 mask;
--        ip->family = AF_INET;
--        char *error = ip_parse_masked(ip_address, &ip->ipv4, &mask);
--        if (error || mask != OVS_BE32_MAX) {
--            free(error);
--            struct in6_addr mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
--            ip->family = AF_INET6;
--            error = ipv6_parse_masked(ip_address, &ip->ipv6, &mask_v6);
--            if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
--                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
--                VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
--                             ip_address, UUID_ARGS(&od->key));
--                memset(ip, 0, sizeof *ip);
--                ip->family = AF_UNSPEC;
--                return NULL;
--            }
--        }
--        return ip_address;
-+    if (!addresses) {
-+        return false;
-     }
- 
--    memset(ip, 0, sizeof *ip);
--    ip->family = AF_UNSPEC;
--    return NULL;
-+    if (!extract_ip_addresses(addresses, laddrs) ||
-+        laddrs->n_ipv4_addrs > 1 ||
-+        laddrs->n_ipv6_addrs > 1 ||
-+        (laddrs->n_ipv4_addrs && laddrs->ipv4_addrs[0].plen != 32) ||
-+        (laddrs->n_ipv6_addrs && laddrs->ipv6_addrs[0].plen != 128)) {
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-+        VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
-+                     addresses, UUID_ARGS(&od->key));
-+        destroy_lport_addresses(laddrs);
-+        return false;
-+    }
-+
-+    return true;
- }
- 
- static void
- add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
-                    struct ds *match, struct ds *actions, int priority,
--                   const char *lb_force_snat_ip, struct lb_vip *lb_vip,
-+                   bool lb_force_snat_ip, struct lb_vip *lb_vip,
-                    const char *proto, struct nbrec_load_balancer *lb,
-                    struct shash *meter_groups, struct sset *nat_entries)
- {
-@@ -8371,6 +8364,32 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
-     ds_destroy(&actions);
- }
- 
-+static void
-+build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od,
-+                               const char *ip_version, const char *ip_addr,
-+                               const char *context)
-+{
-+    struct ds match = DS_EMPTY_INITIALIZER;
-+    struct ds actions = DS_EMPTY_INITIALIZER;
-+    ds_put_format(&match, "ip%s && ip%s.dst == %s",
-+                  ip_version, ip_version, ip_addr);
-+    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
-+                  ds_cstr(&match), "ct_snat;");
-+
-+    /* Higher priority rules to force SNAT with the IP addresses
-+     * configured in the Gateway router.  This only takes effect
-+     * when the packet has already been DNATed or load balanced once. */
-+    ds_clear(&match);
-+    ds_put_format(&match, "flags.force_snat_for_%s == 1 && ip%s",
-+                  context, ip_version);
-+    ds_put_format(&actions, "ct_snat(%s);", ip_addr);
-+    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
-+                  ds_cstr(&match), ds_cstr(&actions));
-+
-+    ds_destroy(&match);
-+    ds_destroy(&actions);
-+}
-+
- static void
- build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                     struct hmap *lflows, struct shash *meter_groups,
-@@ -8892,24 +8911,37 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             }
-         }
- 
--        /* A gateway router can have 2 SNAT IP addresses to force DNATed and
-+        /* A gateway router can have 4 SNAT IP addresses to force DNATed and
-          * LBed traffic respectively to be SNATed.  In addition, there can be
-          * a number of SNAT rules in the NAT table. */
-         struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips
--                                          * (op->od->nbr->n_nat + 2));
-+                                          * (op->od->nbr->n_nat + 4));
-         size_t n_snat_ips = 0;
-+        struct lport_addresses snat_addrs;
- 
--        struct v46_ip snat_ip;
--        const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
--                                                           &snat_ip);
--        if (dnat_force_snat_ip) {
--            snat_ips[n_snat_ips++] = snat_ip;
-+        if (get_force_snat_ip(op->od, "dnat", &snat_addrs)) {
-+            if (snat_addrs.n_ipv4_addrs) {
-+                snat_ips[n_snat_ips].family = AF_INET;
-+                snat_ips[n_snat_ips++].ipv4 = snat_addrs.ipv4_addrs[0].addr;
-+            }
-+            if (snat_addrs.n_ipv6_addrs) {
-+                snat_ips[n_snat_ips].family = AF_INET6;
-+                snat_ips[n_snat_ips++].ipv6 = snat_addrs.ipv6_addrs[0].addr;
-+            }
-+            destroy_lport_addresses(&snat_addrs);
-         }
- 
--        const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb",
--                                                         &snat_ip);
--        if (lb_force_snat_ip) {
--            snat_ips[n_snat_ips++] = snat_ip;
-+        memset(&snat_addrs, 0, sizeof(snat_addrs));
-+        if (get_force_snat_ip(op->od, "lb", &snat_addrs)) {
-+            if (snat_addrs.n_ipv4_addrs) {
-+                snat_ips[n_snat_ips].family = AF_INET;
-+                snat_ips[n_snat_ips++].ipv4 = snat_addrs.ipv4_addrs[0].addr;
-+            }
-+            if (snat_addrs.n_ipv6_addrs) {
-+                snat_ips[n_snat_ips].family = AF_INET6;
-+                snat_ips[n_snat_ips++].ipv6 = snat_addrs.ipv6_addrs[0].addr;
-+            }
-+            destroy_lport_addresses(&snat_addrs);
-         }
- 
-         for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
-@@ -9269,11 +9301,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
- 
-         struct sset nat_entries = SSET_INITIALIZER(&nat_entries);
- 
--        struct v46_ip snat_ip, lb_snat_ip;
--        const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat",
--                                                           &snat_ip);
--        const char *lb_force_snat_ip = get_force_snat_ip(od, "lb",
--                                                         &lb_snat_ip);
-+        struct lport_addresses dnat_force_snat_addrs;
-+        struct lport_addresses lb_force_snat_addrs;
-+        bool dnat_force_snat_ip = get_force_snat_ip(od, "dnat",
-+                                                    &dnat_force_snat_addrs);
-+        bool lb_force_snat_ip = get_force_snat_ip(od, "lb",
-+                                                  &lb_force_snat_addrs);
- 
-         for (int i = 0; i < od->nbr->n_nat; i++) {
-             const struct nbrec_nat *nat;
-@@ -9739,49 +9772,28 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         }
- 
-         /* Handle force SNAT options set in the gateway router. */
--        if (dnat_force_snat_ip && !od->l3dgw_port) {
--            /* If a packet with destination IP address as that of the
--             * gateway router (as set in options:dnat_force_snat_ip) is seen,
--             * UNSNAT it. */
--            ds_clear(&match);
--            ds_put_format(&match, "ip && ip%s.dst == %s",
--                          snat_ip.family == AF_INET ? "4" : "6",
--                          dnat_force_snat_ip);
--            ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
--                          ds_cstr(&match), "ct_snat;");
--
--            /* Higher priority rules to force SNAT with the IP addresses
--             * configured in the Gateway router.  This only takes effect
--             * when the packet has already been DNATed once. */
--            ds_clear(&match);
--            ds_put_format(&match, "flags.force_snat_for_dnat == 1 && ip");
--            ds_clear(&actions);
--            ds_put_format(&actions, "ct_snat(%s);", dnat_force_snat_ip);
--            ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
--                          ds_cstr(&match), ds_cstr(&actions));
--        }
--        if (lb_force_snat_ip && !od->l3dgw_port) {
--            /* If a packet with destination IP address as that of the
--             * gateway router (as set in options:lb_force_snat_ip) is seen,
--             * UNSNAT it. */
--            ds_clear(&match);
--            ds_put_format(&match, "ip && ip%s.dst == %s",
--                          lb_snat_ip.family == AF_INET ? "4" : "6",
--                          lb_force_snat_ip);
--            ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
--                          ds_cstr(&match), "ct_snat;");
--
--            /* Load balanced traffic will have flags.force_snat_for_lb set.
--             * Force SNAT it. */
--            ds_clear(&match);
--            ds_put_format(&match, "flags.force_snat_for_lb == 1 && ip");
--            ds_clear(&actions);
--            ds_put_format(&actions, "ct_snat(%s);", lb_force_snat_ip);
--            ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
--                          ds_cstr(&match), ds_cstr(&actions));
--        }
--
-         if (!od->l3dgw_port) {
-+            if (dnat_force_snat_ip) {
-+                if (dnat_force_snat_addrs.n_ipv4_addrs) {
-+                    build_lrouter_force_snat_flows(lflows, od, "4",
-+                        dnat_force_snat_addrs.ipv4_addrs[0].addr_s, "dnat");
-+                }
-+                if (dnat_force_snat_addrs.n_ipv6_addrs) {
-+                    build_lrouter_force_snat_flows(lflows, od, "6",
-+                        dnat_force_snat_addrs.ipv6_addrs[0].addr_s, "dnat");
-+                }
-+            }
-+            if (lb_force_snat_ip) {
-+                if (lb_force_snat_addrs.n_ipv4_addrs) {
-+                    build_lrouter_force_snat_flows(lflows, od, "4",
-+                        lb_force_snat_addrs.ipv4_addrs[0].addr_s, "lb");
-+                }
-+                if (lb_force_snat_addrs.n_ipv6_addrs) {
-+                    build_lrouter_force_snat_flows(lflows, od, "6",
-+                        lb_force_snat_addrs.ipv6_addrs[0].addr_s, "lb");
-+                }
-+            }
-+
-             /* For gateway router, re-circulate every packet through
-             * the DNAT zone.  This helps with the following.
-             *
-@@ -9795,6 +9807,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                           "ip", "flags.loopback = 1; ct_dnat;");
-         }
- 
-+        if (dnat_force_snat_ip) {
-+            destroy_lport_addresses(&dnat_force_snat_addrs);
-+        }
-+        if (lb_force_snat_ip) {
-+            destroy_lport_addresses(&lb_force_snat_addrs);
-+        }
-+
-         /* Load balancing and packet defrag are only valid on
-          * Gateway routers or router with gateway port. */
-         if (!smap_get(&od->nbr->options, "chassis") && !od->l3dgw_port) {
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index 98d36b270..5f4291559 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -1816,27 +1816,29 @@
-       </column>
-       <column name="options" key="dnat_force_snat_ip">
-         <p>
--          If set, indicates the IP address to use to force SNAT a packet
--          that has already been DNATed in the gateway router.  When multiple
--          gateway routers are configured, a packet can potentially enter any
--          of the gateway router, get DNATted and eventually reach the logical
--          switch port.  For the return traffic to go back to the same gateway
--          router (for unDNATing), the packet needs a SNAT in the first place.
--          This can be achieved by setting the above option with a gateway
--          specific IP address.
-+          If set, indicates a set of IP addresses to use to force SNAT a
-+          packet that has already been DNATed in the gateway router.  When
-+          multiple gateway routers are configured, a packet can potentially
-+          enter any of the gateway router, get DNATted and eventually reach the
-+          logical switch port.  For the return traffic to go back to the same
-+          gateway router (for unDNATing), the packet needs a SNAT in the first
-+          place. This can be achieved by setting the above option with a
-+          gateway specific set of IP addresses. This option may have exactly
-+          one IPv4 and/or one IPv6 address on it, separated by a a space.
-         </p>
-       </column>
-       <column name="options" key="lb_force_snat_ip">
-         <p>
--          If set, indicates the IP address to use to force SNAT a packet
-+          If set, indicates a set of IP addresses to use to force SNAT a packet
-           that has already been load-balanced in the gateway router.  When
-           multiple gateway routers are configured, a packet can potentially
-           enter any of the gateway routers, get DNATted as part of the load-
-           balancing and eventually reach the logical switch port.
-           For the return traffic to go back to the same gateway router (for
-           unDNATing), the packet needs a SNAT in the first place.  This can be
--          achieved by setting the above option with a gateway specific IP
--          address.
-+          achieved by setting the above option with a gateway specific set of
-+          IP addresses. This option may have exactly one IPv4 and/or one IPv6
-+          address on it, separated by a space character.
-         </p>
-       </column>
-       <column name="options" key="mcast_relay" type='{"type": "boolean"}'>
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index bce097b17..0d478b4aa 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -1026,6 +1026,323 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
- /connection dropped.*/d"])
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT - Dual Stack])
-+AT_KEYWORDS([ovnnat])
-+
-+CHECK_CONNTRACK()
-+CHECK_CONNTRACK_NAT()
-+ovn_start
-+OVS_TRAFFIC_VSWITCHD_START()
-+ADD_BR([br-int])
-+
-+# Set external-ids in br-int needed for ovn-controller
-+ovs-vsctl \
-+        -- set Open_vSwitch . external-ids:system-id=hv1 \
-+        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
-+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
-+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
-+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-+
-+# Start ovn-controller
-+start_daemon ovn-controller
-+
-+# Logical network:
-+# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-+# in 20.0.0.0/24 and fd20::/64 networks. R1 has switches foo (192.168.1.0/24
-+# and fd11::/64) and bar (192.168.2.0/24 and fd12::/64) connected to it. R2
-+# has alice (172.16.1.0/24 and fd30::/64) connected to it.  R3 has bob
-+# (172.16.1.0/24 andfd30::/64) connected to it. Note how both alice and bob
-+# have the same subnets behind them.  We are trying to simulate external network
-+# via those 2 switches. In real world the switch ports of these switches will
-+# have addresses set as "unknown" to make them learning switches. Or those
-+# switches will be "localnet" ones.
-+#
-+#    foo -- R1 -- join - R2 -- alice
-+#           |          |
-+#    bar ----          - R3 --- bob
-+
-+ovn-nbctl create Logical_Router name=R1
-+ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-+ovn-nbctl create Logical_Router name=R3 options:chassis=hv1
-+
-+ovn-nbctl ls-add foo
-+ovn-nbctl ls-add bar
-+ovn-nbctl ls-add alice
-+ovn-nbctl ls-add bob
-+ovn-nbctl ls-add join
-+
-+# Connect foo to R1
-+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64
-+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
-+    type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-+
-+# Connect bar to R1
-+ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64
-+ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
-+    type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-+
-+# Connect alice to R2
-+ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 fd30::1/64
-+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
-+    type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-+
-+# Connect bob to R3
-+ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 fd30::2/64
-+ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
-+    type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-+
-+# Connect R1 to join
-+ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 fd20::1/64
-+ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
-+    type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-+
-+# Connect R2 to join
-+ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 fd20::2/64
-+ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
-+    type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-+
-+# Connect R3 to join
-+ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 fd20::3/64
-+ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
-+    type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-+
-+# Install static routes with source ip address as the policy for routing.
-+# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
-+ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2
-+ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3
-+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2
-+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3
-+
-+# Static routes.
-+ovn-nbctl lr-route-add R2 fd11::/64 fd20::1
-+ovn-nbctl lr-route-add R2 fd12::/64 fd20::1
-+ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-+ovn-nbctl lr-route-add R3 fd11::/64 fd20::1
-+ovn-nbctl lr-route-add R3 fd12::/64 fd20::1
-+ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1
-+
-+# For gateway routers R2 and R3, set a force SNAT rule.
-+ovn-nbctl set logical_router R2 options:dnat_force_snat_ip="20.0.0.2 fd20::2"
-+ovn-nbctl set logical_router R3 options:dnat_force_snat_ip="20.0.0.3 fd20::3"
-+
-+# Logical port 'foo1' in switch 'foo'.
-+ADD_NAMESPACES(foo1)
-+ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
-+         "192.168.1.1")
-+ovn-nbctl lsp-add foo foo1 \
-+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-+
-+ADD_NAMESPACES(foo16)
-+ADD_VETH(foo16, foo16, br-int, "fd11::2/64", "f0:00:00:02:02:03", \
-+         "fd11::1")
-+OVS_WAIT_UNTIL([test "$(ip netns exec foo16 ip a | grep fd11::2 | grep tentative)" = ""])
-+ovn-nbctl lsp-add foo foo16 \
-+-- lsp-set-addresses foo16 "f0:00:00:02:02:03 fd11::2"
-+
-+# Logical port 'alice1' in switch 'alice'.
-+ADD_NAMESPACES(alice1)
-+ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \
-+         "172.16.1.1")
-+ovn-nbctl lsp-add alice alice1 \
-+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3"
-+
-+ADD_NAMESPACES(alice16)
-+ADD_VETH(alice16, alice16, br-int, "fd30::3/64", "f0:00:00:02:02:04", \
-+         "fd30::1")
-+OVS_WAIT_UNTIL([test "$(ip netns exec alice16 ip a | grep fd30::3 | grep tentative)" = ""])
-+ovn-nbctl lsp-add alice alice16 \
-+-- lsp-set-addresses alice16 "f0:00:00:02:02:04 fd30::3"
-+
-+# Logical port 'bar1' in switch 'bar'.
-+ADD_NAMESPACES(bar1)
-+ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-+"192.168.2.1")
-+ovn-nbctl lsp-add bar bar1 \
-+-- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-+
-+ADD_NAMESPACES(bar16)
-+ADD_VETH(bar16, bar16, br-int, "fd12::2/64", "f0:00:00:02:02:05", \
-+         "fd12::1")
-+OVS_WAIT_UNTIL([test "$(ip netns exec bar16 ip a | grep fd12::2 | grep tentative)" = ""])
-+ovn-nbctl lsp-add bar bar16 \
-+-- lsp-set-addresses bar16 "f0:00:00:02:02:05 fd12::2"
-+
-+# Logical port 'bob1' in switch 'bob'.
-+ADD_NAMESPACES(bob1)
-+ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \
-+         "172.16.1.2")
-+ovn-nbctl lsp-add bob bob1 \
-+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4"
-+
-+ADD_NAMESPACES(bob16)
-+ADD_VETH(bob16, bob16, br-int, "fd30::4/64", "f0:00:00:02:02:06", \
-+         "fd30::2")
-+OVS_WAIT_UNTIL([test "$(ip netns exec bob16 ip a | grep fd30::4 | grep tentative)" = ""])
-+ovn-nbctl lsp-add bob bob16 \
-+-- lsp-set-addresses bob16 "f0:00:00:02:02:06 fd30::4"
-+
-+# Router R2
-+# Add a DNAT rule.
-+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \
-+    external_ip=30.0.0.2 -- add logical_router R2 nat @nat
-+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \
-+    external_ip='"fd40::2"' -- add logical_router R2 nat @nat
-+
-+# Add a SNAT rule
-+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \
-+    external_ip=30.0.0.1 -- add logical_router R2 nat @nat
-+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd11::2"' \
-+    external_ip='"fd40::1"' -- add logical_router R2 nat @nat
-+
-+# Router R3
-+# Add a DNAT rule.
-+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \
-+    external_ip=30.0.0.3 -- add logical_router R3 nat @nat
-+ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \
-+    external_ip='"fd40::3"' -- add logical_router R3 nat @nat
-+
-+# Add a SNAT rule
-+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \
-+    external_ip=30.0.0.4 -- add logical_router R3 nat @nat
-+ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd12::2"' \
-+    external_ip='"fd40::4"' -- add logical_router R3 nat @nat
-+
-+# wait for ovn-controller to catch up.
-+ovn-nbctl --wait=hv sync
-+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd40::4)'])
-+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.4)'])
-+
-+# North-South DNAT: 'alice1' should be able to ping 'foo1' via 30.0.0.2
-+NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# North-South DNAT: 'alice16' should be able to ping 'foo16' via fd30::2
-+NS_CHECK_EXEC([alice16], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::2 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# Check conntrack entries.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=172.16.1.3,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::3) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd30::3,dst=fd40::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd30::3,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+# But foo1 should receive traffic from 20.0.0.2
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=172.16.1.3,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+# But foo16 should receive traffic from fd20::2
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd30::3,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+# North-South DNAT: 'bob1' should be able to ping 'foo1' via 30.0.0.3
-+NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.3 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# North-South DNAT: 'bob16' should be able to ping 'foo16' via fd40::3
-+NS_CHECK_EXEC([bob16], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::3 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# Check conntrack entries.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=172.16.1.4,dst=30.0.0.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::4) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd30::4,dst=fd40::3,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd30::4,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+# But foo1 should receive traffic from 20.0.0.3
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.3) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.3,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+
-+# But foo16 should receive traffic from fd20::3
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd30::4,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+# South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic
-+# from 30.0.0.4
-+NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+# South-North SNAT: 'bar16' pings 'bob16'. But 'bob16' receives traffic
-+# from fd40::4
-+NS_CHECK_EXEC([bar16], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::4 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# We verify that SNAT indeed happened via 'dump-conntrack' command.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.4) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=192.168.2.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=30.0.0.4,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::4) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd12::2,dst=fd30::4,id=<cleared>,type=128,code=0),reply=(src=fd30::4,dst=fd40::4,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic
-+# from 30.0.0.1
-+NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# South-North SNAT: 'foo16' pings 'alice16'. But 'alice16' receives traffic
-+# from fd40::1
-+NS_CHECK_EXEC([foo16], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::3 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+# We verify that SNAT indeed happened via 'dump-conntrack' command.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::1) | \
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmpv6,orig=(src=fd11::2,dst=fd30::3,id=<cleared>,type=128,code=0),reply=(src=fd30::3,dst=fd40::1,id=<cleared>,type=129,code=0),zone=<cleared>
-+])
-+
-+OVS_APP_EXIT_AND_WAIT([ovn-controller])
-+
-+as ovn-sb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as ovn-nb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as northd
-+OVS_APP_EXIT_AND_WAIT([ovn-northd])
-+
-+as
-+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-+/connection dropped.*/d"])
-+AT_CLEANUP
-+
-+
- AT_SETUP([ovn -- load-balancing])
- AT_KEYWORDS([ovnlb])
- 
-@@ -2405,6 +2722,230 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
- /connection dropped.*/d"])
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- multiple gateway routers, load-balancing - Dual Stack])
-+AT_KEYWORDS([ovnlb])
-+
-+CHECK_CONNTRACK()
-+CHECK_CONNTRACK_NAT()
-+ovn_start
-+OVS_TRAFFIC_VSWITCHD_START()
-+ADD_BR([br-int])
-+
-+# Set external-ids in br-int needed for ovn-controller
-+ovs-vsctl \
-+        -- set Open_vSwitch . external-ids:system-id=hv1 \
-+        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
-+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
-+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
-+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-+
-+# Start ovn-controller
-+start_daemon ovn-controller
-+
-+# Logical network:
-+# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-+# in 20.0.0.0/24 and fd20::/64 networks. R1 has switches foo (192.168.1.0/24
-+# and fd11::/64) and bar (192.168.2.0/24 and fd12::/64) connected to it. R2
-+# has alice (172.16.1.0/24 and fd72::/64) connected to it.  R3 has bob
-+# (172.16.1.0/24 and fd72::/64) connected to it. Note how both alice and
-+# bob have the same subnets behind them.  We are trying to simulate external
-+# network via those 2 switches. In real world the switch ports of these
-+# switches will have addresses set as "unknown" to make them learning switches.
-+# Or those switches will be "localnet" ones.
-+#
-+#    foo -- R1 -- join - R2 -- alice
-+#           |          |
-+#    bar ----          - R3 --- bob
-+
-+ovn-nbctl create Logical_Router name=R1
-+ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-+ovn-nbctl create Logical_Router name=R3 options:chassis=hv1
-+
-+ovn-nbctl ls-add foo
-+ovn-nbctl ls-add bar
-+ovn-nbctl ls-add alice
-+ovn-nbctl ls-add bob
-+ovn-nbctl ls-add join
-+
-+# Connect foo to R1
-+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64
-+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
-+    type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-+
-+# Connect bar to R1
-+ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64
-+ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
-+    type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-+
-+# Connect alice to R2
-+ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 fd72::1/64
-+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
-+    type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-+
-+# Connect bob to R3
-+ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24 fd72::2/64
-+ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
-+    type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-+
-+# Connect R1 to join
-+ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 fd20::1/64
-+ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
-+    type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-+
-+# Connect R2 to join
-+ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 fd20::2/64
-+ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
-+    type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-+
-+# Connect R3 to join
-+ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24 fd20::3/64
-+ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
-+    type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-+
-+# Install static routes with source ip address as the policy for routing.
-+# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
-+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2
-+ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3
-+ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2
-+ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3
-+
-+# Static routes.
-+ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-+ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1
-+ovn-nbctl lr-route-add R2 fd11::/64 fd20::1
-+ovn-nbctl lr-route-add R2 fd12::/64 fd20::1
-+ovn-nbctl lr-route-add R3 fd11::/64 fd20::1
-+ovn-nbctl lr-route-add R3 fd12::/64 fd20::1
-+
-+# For gateway routers R2 and R3, set a force SNAT rule.
-+ovn-nbctl set logical_router R2 options:lb_force_snat_ip="20.0.0.2 fd20::2"
-+ovn-nbctl set logical_router R3 options:lb_force_snat_ip="20.0.0.3 fd20::3"
-+
-+# Logical port 'foo1' in switch 'foo'.
-+ADD_NAMESPACES(foo1)
-+ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
-+         "192.168.1.1")
-+ovn-nbctl lsp-add foo foo1 \
-+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-+
-+# Logical port 'foo16' in switch 'foo'.
-+ADD_NAMESPACES(foo16)
-+ADD_VETH(foo16, foo16, br-int, "fd11::2/64", "f0:00:06:01:02:03", \
-+         "fd11::1")
-+ovn-nbctl lsp-add foo foo16 \
-+-- lsp-set-addresses foo16 "f0:00:06:01:02:03 fd11::2"
-+
-+# Logical port 'alice1' in switch 'alice'.
-+ADD_NAMESPACES(alice1)
-+ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \
-+         "172.16.1.1")
-+ovn-nbctl lsp-add alice alice1 \
-+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3"
-+
-+# Logical port 'alice16' in switch 'alice'.
-+ADD_NAMESPACES(alice16)
-+ADD_VETH(alice16, alice16, br-int, "fd72::3/64", "f0:00:06:01:02:04", \
-+         "fd72::1")
-+ovn-nbctl lsp-add alice alice16 \
-+-- lsp-set-addresses alice16 "f0:00:06:01:02:04 fd72::3"
-+
-+# Logical port 'bar1' in switch 'bar'.
-+ADD_NAMESPACES(bar1)
-+ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-+"192.168.2.1")
-+ovn-nbctl lsp-add bar bar1 \
-+-- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-+
-+# Logical port 'bar16' in switch 'bar'.
-+ADD_NAMESPACES(bar16)
-+ADD_VETH(bar16, bar16, br-int, "fd12::2/64", "f0:00:06:01:02:05", \
-+"fd12::1")
-+ovn-nbctl lsp-add bar bar16 \
-+-- lsp-set-addresses bar16 "f0:00:06:01:02:05 fd12::2"
-+
-+# Logical port 'bob1' in switch 'bob'.
-+ADD_NAMESPACES(bob1)
-+ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \
-+         "172.16.1.2")
-+ovn-nbctl lsp-add bob bob1 \
-+-- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4"
-+
-+# Logical port 'bob16' in switch 'bob'.
-+ADD_NAMESPACES(bob16)
-+ADD_VETH(bob16, bob16, br-int, "fd72::4/64", "f0:00:06:01:02:06", \
-+         "fd72::2")
-+ovn-nbctl lsp-add bob bob16 \
-+-- lsp-set-addresses bob16 "f0:00:06:01:02:06 fd72::4"
-+
-+# Config OVN load-balancer with a VIP.
-+uuid=`ovn-nbctl  create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2" \
-+vips:\"fd30::1\"=\"fd11::2,fd12::2\"`
-+ovn-nbctl set logical_router R2 load_balancer=$uuid
-+ovn-nbctl set logical_router R3 load_balancer=$uuid
-+
-+# Wait for ovn-controller to catch up.
-+ovn-nbctl --wait=hv sync
-+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-+grep 'nat(dst=192.168.2.2)'])
-+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-+grep 'nat(dst=fd12::2)'])
-+
-+# Start webservers in 'foo1', 'foo16, 'bar1', and 'bar16'.
-+OVS_START_L7([foo1], [http])
-+OVS_START_L7([bar1], [http])
-+OVS_START_L7([foo16], [http6])
-+OVS_START_L7([bar16], [http6])
-+
-+dnl Should work with the virtual IP address through NAT
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-+done
-+
-+for i in `seq 1 20`; do
-+    echo Request ${i}_6
-+    NS_CHECK_EXEC([alice16], [wget http://[[fd30::1]] -t 5 -T 1 --retry-connrefused -v -o wget${i}_6.log])
-+done
-+
-+dnl Each server should have at least one connection.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+
-+dnl Force SNAT should have worked.
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0) |
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+tcp,orig=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | grep -v fe80 |
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+tcp,orig=(src=fd72::3,dst=fd11::2,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd20::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd12::2,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd20::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+OVS_APP_EXIT_AND_WAIT([ovn-controller])
-+
-+as ovn-sb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as ovn-nb
-+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-+
-+as northd
-+OVS_APP_EXIT_AND_WAIT([ovn-northd])
-+
-+as
-+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-+/connection dropped.*/d"])
-+AT_CLEANUP
-+
- AT_SETUP([ovn -- load balancing in router with gateway router port])
- AT_KEYWORDS([ovnlb])
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch b/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch
deleted file mode 100644
index 8be01b7..0000000
--- a/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch
+++ /dev/null
@@ -1,187 +0,0 @@
-From ac218b6e00ca97ddb54362d71435c4ea2d47cfb7 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 4 Aug 2020 12:49:34 +0530
-Subject: [PATCH 17/22] ovn-northd: Don't send the pkt to conntrack if it is to
- be routed in egress stage.
-
-If there is a logical port 'P1' with the IP - 10.0.0.3 and a logical port 'P2' with
-the IP 20.0.0.3 and if the logical switch of 'P1' has atleast one load balancer
-associated with it and atleast one ACL with allow-related action associated with it.
-Then for every packet from 'P1' to 'P2' after the TCP connection
-is established we see a total of 4 recirculations in the datapath on the chassis
-claiming 'P1'. This is because,
-
-In the ingress logical switch pipeline, below logical flows are hit
-  - table=9 (ls_in_lb           ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;)
-  - table=10(ls_in_stateful     ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
-
-And in the egress logical switch pipeline, below logical flows are hit
- - table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[0] = 1; next;)
- - table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[0] == 1), action=(ct_next;)
- - table=3 (ls_out_lb          ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;)
- - table=7 (ls_out_stateful    ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
-
-In the above example, when the packet enters the egress pipeline and since it needs to
-enter the router pipeline, we can skip setting reg0[0] if outport is peer port of
-logical router port. There is no need to send the packet to conntrack in this case.
-
-This patch handles this case for router ports. Next patch in the series avoids sending to
-conntrack with the action - ct_lb if the packet is not destined to the LB VIP.
-
-With the present master for the above example, we see total of 4 recirculations on the
-chassis claiming the lport 'P1'. With this patch we see only 2 recirculations.
-
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.8.xml | 33 ++++++++++++++++++++++++++++++++-
- northd/ovn-northd.c     | 39 ++++++++++++++++++++++++++++++---------
- 2 files changed, 62 insertions(+), 10 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index f35a035fd..e45d494e8 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -338,6 +338,15 @@
-       db="OVN_Northbound"/> table.
-     </p>
- 
-+    <p>
-+      This table also has a priority-110 flow with the match
-+      <code>inport == <var>I</var></code> for all logical switch
-+      datapaths to move traffic to the next table. Where <var>I</var>
-+      is the peer of a logical router port. This flow is added to
-+      skip the connection tracking of packets which enter from
-+      logical router datapath to logical switch datapath.
-+    </p>
-+
-     <h3>Ingress Table 5: Pre-stateful</h3>
- 
-     <p>
-@@ -505,7 +514,20 @@
- 
-     <p>
-       It contains a priority-0 flow that simply moves traffic to the next
--      table.  For established connections a priority 100 flow matches on
-+      table.
-+    </p>
-+
-+    <p>
-+      A priority-65535 flow with the match
-+      <code>inport == <var>I</var></code> for all logical switch
-+      datapaths to move traffic to the next table. Where <var>I</var>
-+      is the peer of a logical router port. This flow is added to
-+      skip the connection tracking of packets which enter from
-+      logical router datapath to logical switch datapath.
-+    </p>
-+
-+    <p>
-+      For established connections a priority 65534 flow matches on
-       <code>ct.est &amp;&amp; !ct.rel &amp;&amp; !ct.new &amp;&amp;
-       !ct.inv</code> and sets an action <code>reg0[2] = 1; next;</code> to act
-       as a hint for table <code>Stateful</code> to send packets through
-@@ -1342,6 +1364,15 @@ output;
-       db="OVN_Northbound"/> table.
-     </p>
- 
-+    <p>
-+      This table also has a priority-110 flow with the match
-+      <code>outport == <var>I</var></code> for all logical switch
-+      datapaths to move traffic to the next table. Where <var>I</var>
-+      is the peer of a logical router port. This flow is added to
-+      skip the connection tracking of packets which will be entering
-+      logical router datapath from logical switch datapath for routing.
-+    </p>
-+
-     <h3>Egress Table 2: Pre-stateful</h3>
- 
-     <p>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 1f5433d9d..7b534ce3c 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4850,8 +4850,9 @@ build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths,
- }
- 
- static void
--build_pre_acl_flows(struct ovn_datapath *od, struct ovn_port *op,
--                    struct hmap *lflows)
-+skip_port_from_conntrack(struct ovn_datapath *od, struct ovn_port *op,
-+                         enum ovn_stage in_stage, enum ovn_stage out_stage,
-+                         uint16_t priority, struct hmap *lflows)
- {
-     /* Can't use ct() for router ports. Consider the following configuration:
-      * lp1(10.0.0.2) on hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
-@@ -4867,10 +4868,10 @@ build_pre_acl_flows(struct ovn_datapath *od, struct ovn_port *op,
- 
-     ds_put_format(&match_in, "ip && inport == %s", op->json_key);
-     ds_put_format(&match_out, "ip && outport == %s", op->json_key);
--    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
-+    ovn_lflow_add_with_hint(lflows, od, in_stage, priority,
-                             ds_cstr(&match_in), "next;",
-                             &op->nbsp->header_);
--    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
-+    ovn_lflow_add_with_hint(lflows, od, out_stage, priority,
-                             ds_cstr(&match_out), "next;",
-                             &op->nbsp->header_);
- 
-@@ -4903,10 +4904,14 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-      * defragmentation, in order to match L4 headers. */
-     if (has_stateful) {
-         for (size_t i = 0; i < od->n_router_ports; i++) {
--            build_pre_acl_flows(od, od->router_ports[i], lflows);
-+            skip_port_from_conntrack(od, od->router_ports[i],
-+                                     S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL,
-+                                     110, lflows);
-         }
-         for (size_t i = 0; i < od->n_localnet_ports; i++) {
--            build_pre_acl_flows(od, od->localnet_ports[i], lflows);
-+            skip_port_from_conntrack(od, od->localnet_ports[i],
-+                                     S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL,
-+                                     110, lflows);
-         }
- 
-         /* Ingress and Egress Pre-ACL Table (Priority 110).
-@@ -5050,6 +5055,17 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
- 
-+    for (size_t i = 0; i < od->n_router_ports; i++) {
-+        skip_port_from_conntrack(od, od->router_ports[i],
-+                                 S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB,
-+                                 110, lflows);
-+    }
-+    for (size_t i = 0; i < od->n_localnet_ports; i++) {
-+        skip_port_from_conntrack(od, od->localnet_ports[i],
-+                                 S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB,
-+                                 110, lflows);
-+    }
-+
-     struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
-     struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
-     bool vip_configured = false;
-@@ -5725,13 +5741,18 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows)
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 0, "1", "next;");
- 
-     if (od->nbs->load_balancer) {
--        /* Ingress and Egress LB Table (Priority 65535).
-+        for (size_t i = 0; i < od->n_router_ports; i++) {
-+            skip_port_from_conntrack(od, od->router_ports[i],
-+                                     S_SWITCH_IN_LB, S_SWITCH_OUT_LB,
-+                                     UINT16_MAX, lflows);
-+        }
-+        /* Ingress and Egress LB Table (Priority 65534).
-          *
-          * Send established traffic through conntrack for just NAT. */
--        ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX,
-+        ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX - 1,
-                       "ct.est && !ct.rel && !ct.new && !ct.inv",
-                       REGBIT_CONNTRACK_NAT" = 1; next;");
--        ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX,
-+        ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX - 1,
-                       "ct.est && !ct.rel && !ct.new && !ct.inv",
-                       REGBIT_CONNTRACK_NAT" = 1; next;");
-     }
--- 
-2.26.2
-
diff --git a/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch b/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch
deleted file mode 100644
index 17307a3..0000000
--- a/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch
+++ /dev/null
@@ -1,475 +0,0 @@
-From ad27f022b9bda5364f34c611ba116e0e18269f2c Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 4 Aug 2020 12:49:48 +0530
-Subject: [PATCH 18/22] ovn-northd: Don't send the pkt to conntrack for NAT if
- its not destined for LB VIP.
-
-Presently when a logical switch has load balancer(s) associated to it, then the
-packet is still sent to conntrack with the action ct_lb on both the ingress
-and egress logical switch pipeline even if the destination IP is not LB VIP.
-
-This is because below logical flows are hit:
-
-In the ingress logical switch pipeline:
-  - table=9 (ls_in_lb       ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;)
-  - table=10(ls_in_stateful ), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
-
-In the egress logical switch pipeline:
-  - table=3 (ls_out_lb      ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(reg0[2] = 1; next;)
-  - table=7 (ls_out_stateful), priority=100  , match=(reg0[2] == 1), action=(ct_lb;)
-
-This patch avoid unnecessary ct actions by setting the ct_label.natted to 1 when the ct_lb(backends=...) action
-is applied for NEW connections and updating the above logical flows to check for this mark:
-
- - table=9 (ls_in_lb), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1),
-   action=(reg0[2] = 1; next;)
-
- - table=3 (ls_out_lb), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1),
-   action=(reg0[2] = 1; next;)
-
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- lib/actions.c        |   3 +-
- lib/logical-fields.c |   6 +-
- northd/ovn-northd.c  |   6 +-
- tests/ovn.at         |  17 +++---
- tests/system-ovn.at  | 130 +++++++++++++++++++++----------------------
- 5 files changed, 84 insertions(+), 78 deletions(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index 245486b0a..8c11b7b3f 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -1098,7 +1098,8 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
-         if (dst->port) {
-             ds_put_format(&ds, ":%"PRIu16, dst->port);
-         }
--        ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15])",
-+        ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15],"
-+                      "exec(set_field:2/2->ct_label))",
-                       recirc_table, zone_reg);
-     }
- 
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index 15342dded..bf61df771 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -126,10 +126,12 @@ ovn_init_symtab(struct shash *symtab)
-     expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL, false,
-                                  WR_CT_COMMIT);
- 
--    expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL, false,
--                                 WR_CT_COMMIT);
-+    expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL,
-+                                 false, WR_CT_COMMIT);
-     expr_symtab_add_subfield_scoped(symtab, "ct_label.blocked", NULL,
-                                     "ct_label[0]", WR_CT_COMMIT);
-+    expr_symtab_add_subfield_scoped(symtab, "ct_label.natted", NULL,
-+                                    "ct_label[1]", WR_CT_COMMIT);
-     expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_eth", NULL,
-                                     "ct_label[32..79]", WR_CT_COMMIT);
-     expr_symtab_add_subfield_scoped(symtab, "ct_label.ecmp_reply_port", NULL,
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 7b534ce3c..5f0abeee1 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -5750,10 +5750,12 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows)
-          *
-          * Send established traffic through conntrack for just NAT. */
-         ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX - 1,
--                      "ct.est && !ct.rel && !ct.new && !ct.inv",
-+                      "ct.est && !ct.rel && !ct.new && !ct.inv && "
-+                      "ct_label.natted == 1",
-                       REGBIT_CONNTRACK_NAT" = 1; next;");
-         ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX - 1,
--                      "ct.est && !ct.rel && !ct.new && !ct.inv",
-+                      "ct.est && !ct.rel && !ct.new && !ct.inv && "
-+                      "ct_label.natted == 1",
-                       REGBIT_CONNTRACK_NAT" = 1; next;");
-     }
- }
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 2651b3eac..0d99adf3f 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -194,6 +194,7 @@ ct_label = NXM_NX_CT_LABEL
- ct_label.blocked = ct_label[0]
- ct_label.ecmp_reply_eth = ct_label[32..79]
- ct_label.ecmp_reply_port = ct_label[80..95]
-+ct_label.natted = ct_label[1]
- ct_mark = NXM_NX_CT_MARK
- ct_state = NXM_NX_CT_STATE
- ]])
-@@ -996,17 +997,17 @@ ct_lb(192.168.1.2:80, 192.168.1.3:80);
-     Syntax error at `192.168.1.2' expecting backends.
- ct_lb(backends=192.168.1.2:80,192.168.1.3:80);
-     encodes as group:1
--    uses group: id(1), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(1), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=192.168.1.2, 192.168.1.3, );
-     formats as ct_lb(backends=192.168.1.2,192.168.1.3);
-     encodes as group:2
--    uses group: id(2), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(2), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=fd0f::2, fd0f::3, );
-     formats as ct_lb(backends=fd0f::2,fd0f::3);
-     encodes as group:3
--    uses group: id(3), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(3), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- 
- ct_lb(backends=192.168.1.2:);
-@@ -1022,23 +1023,23 @@ ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields=eth_src,eth_dst,ip_src
-     Syntax error at `eth_src' invalid hash_fields.
- ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields="eth_src,eth_dst,ip_src");
-     encodes as group:4
--    uses group: id(4), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(4), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst");
-     encodes as group:5
--    uses group: id(5), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(5), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst");
-     encodes as group:6
--    uses group: id(6), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(6), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst");
-     encodes as group:7
--    uses group: id(7), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(7), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst");
-     encodes as group:8
--    uses group: id(8), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
-+    uses group: id(8), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
-     has prereqs ip
- 
- # ct_next
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 0d478b4aa..d0ffb5c1a 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -1441,9 +1441,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Should work with the virtual IP 30.0.0.3 address through NAT
-@@ -1455,9 +1455,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.3) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -1469,9 +1469,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- # Configure selection_fields.
-@@ -1492,9 +1492,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-@@ -1687,9 +1687,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::1) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Should work with the virtual IP fd03::3 address through NAT
-@@ -1701,9 +1701,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::3) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::3,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -1715,9 +1715,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- # Configure selection_fields.
-@@ -1738,9 +1738,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-@@ -1884,9 +1884,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -1898,9 +1898,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- 
-@@ -1993,9 +1993,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::1) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::5,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::1,sport=<cleared>,dport=<cleared>),reply=(src=fd01::5,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -2007,9 +2007,9 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::5,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::5,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- 
-@@ -2145,8 +2145,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -2158,8 +2158,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- check_est_flows () {
-@@ -2200,8 +2200,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) |
-@@ -2349,8 +2349,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -2362,8 +2362,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::2) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::2,dst=fd30::2,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::2,dst=fd30::2,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd30::2,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd30::2,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
-@@ -2525,8 +2525,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Force SNAT should have worked.
-@@ -2696,8 +2696,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Force SNAT should have worked.
-@@ -2910,13 +2910,13 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Force SNAT should have worked.
-@@ -3054,8 +3054,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.10) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -3067,8 +3067,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.11) |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
-@@ -3195,8 +3195,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd72::10) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::2,dst=fd72::10,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::2,dst=fd72::10,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd72::10,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd72::10,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- dnl Test load-balancing that includes L4 ports in NAT.
-@@ -3208,8 +3208,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd72::11) | grep -v fe80 |
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=fd72::2,dst=fd72::11,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=fd72::2,dst=fd72::11,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd72::11,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=fd72::2,dst=fd72::11,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd72::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
-@@ -4207,8 +4207,8 @@ done
- dnl Each server should have at least one connection.
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
--tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
-+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- # Stop webserer in sw0-p1
-@@ -4232,7 +4232,7 @@ done
- 
- AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
- sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
--tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,labels=0x2,protoinfo=(state=<cleared>)
- ])
- 
- # Create udp load balancer.
--- 
-2.26.2
-
diff --git a/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch b/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch
deleted file mode 100644
index e85d3ac..0000000
--- a/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 3ad242eef272a8cc31a18a1b047ba7d54e861b0d Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Fri, 1 May 2020 15:13:08 -0400
-Subject: [PATCH 19/22] ovsdb-idl: Add function to reset min_index.
-
-If an administrator removes all of the databases in a cluster from
-disk, then ovsdb IDL clients will have a problem. The databases will all
-reset their stored indexes to 0, so The IDL client's min_index will be
-higher than the indexes of all databases in the cluster. This results in
-the client constantly connecting to databases, detecting the data as
-"stale", and then attempting to connect to another.
-
-This function provides a way to reset the IDL to an initial state with
-min_index of 0. This way, the client will not wrongly detect the
-database data as stale and will recover properly.
-
-Notice that this function is not actually used anywhere in this patch.
-This will be used by OVN, though, since OVN is the primary user of
-clustered OVSDB.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-
-(cherry-picked from upstream ovs commit 89b522aee379f7ebd21ec67ffb622118af7e9db1)
-
-Change-Id: I943ece9a07566a34b11248455cc1abbe7892d4e8
----
- openvswitch-2.13.0/lib/ovsdb-idl.c | 10 ++++++++++
- openvswitch-2.13.0/lib/ovsdb-idl.h |  1 +
- 2 files changed, 11 insertions(+)
-
-diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.c b/openvswitch-2.13.0/lib/ovsdb-idl.c
-index 8eb421366..648c227d6 100644
---- a/openvswitch-2.13.0/lib/ovsdb-idl.c
-+++ b/openvswitch-2.13.0/lib/ovsdb-idl.c
-@@ -561,6 +561,16 @@ ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle)
-     idl->shuffle_remotes = shuffle;
- }
- 
-+/* Reset min_index to 0. This prevents a situation where the client
-+ * thinks all databases have stale data, when they actually have all
-+ * been destroyed and rebuilt from scratch.
-+ */
-+void
-+ovsdb_idl_reset_min_index(struct ovsdb_idl *idl)
-+{
-+    idl->min_index = 0;
-+}
-+
- static void
- ovsdb_idl_db_destroy(struct ovsdb_idl_db *db)
- {
-diff --git a/openvswitch-2.13.0/lib/ovsdb-idl.h b/openvswitch-2.13.0/lib/ovsdb-idl.h
-index 9f12ce320..c56cd19b1 100644
---- a/openvswitch-2.13.0/lib/ovsdb-idl.h
-+++ b/openvswitch-2.13.0/lib/ovsdb-idl.h
-@@ -64,6 +64,7 @@ struct ovsdb_idl *ovsdb_idl_create_unconnected(
-     const struct ovsdb_idl_class *, bool monitor_everything_by_default);
- void ovsdb_idl_set_remote(struct ovsdb_idl *, const char *, bool);
- void ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *, bool);
-+void ovsdb_idl_reset_min_index(struct ovsdb_idl *);
- void ovsdb_idl_destroy(struct ovsdb_idl *);
- 
- void ovsdb_idl_set_leader_only(struct ovsdb_idl *, bool leader_only);
--- 
-2.26.2
-
diff --git a/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch b/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch
deleted file mode 100644
index 73d6138..0000000
--- a/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch
+++ /dev/null
@@ -1,246 +0,0 @@
-From 8e629246fead7a5dfee78ca45627a0cfd7fef4b1 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Wed, 10 Jun 2020 14:50:06 -0400
-Subject: [PATCH 20/22] Add northd and ovn-controller cluster status reset
- commands.
-
-During the course of debugging a clustered DB environment, all members
-of the southbound database cluster were destroyed (i.e. the .db files
-were removed from disk) and then restarted. Once this happened,
-ovn-northd and ovn-controller could not interact with the southbound
-database because they both detected all members of the cluster as having
-"stale" data. The only course of action was to reset ovn-northd and all
-ovn-controllers. It is possible to have this happen with the northbound
-database as well if it is clustered.
-
-This patch offers new ovn-appctl commands for ovn-northd and
-ovn-controller that allows for it to reset its clustered status. This
-allows for it to interact with the database successfully after a cluster
-teardown and restart.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1829109
-
-(cherry-picked from upstream ovn master commit 512b884dea3f85791eca44fd1d92956e8282be6d)
-
-Change-Id: I3e7d844d6f79552fd53a018c74b80def6069edcb
----
- controller/ovn-controller.8.xml | 16 ++++++++++++++++
- controller/ovn-controller.c     | 30 ++++++++++++++++++++++++++---
- northd/ovn-northd.8.xml         | 28 +++++++++++++++++++++++++++
- northd/ovn-northd.c             | 34 +++++++++++++++++++++++++++++++++
- 4 files changed, 105 insertions(+), 3 deletions(-)
-
-diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
-index 92e0a6e43..66877314c 100644
---- a/controller/ovn-controller.8.xml
-+++ b/controller/ovn-controller.8.xml
-@@ -491,6 +491,22 @@
-         recomputes are cpu intensive.
-       </p>
-       </dd>
-+
-+      <dt><code>sb-cluster-state-reset</code></dt>
-+      <dd>
-+      <p>
-+        Reset southbound database cluster status when databases are destroyed
-+        and rebuilt.
-+      </p>
-+      <p>
-+        If all databases in a clustered southbound database are removed from
-+        disk, then the stored index of all databases will be reset to zero.
-+        This will cause ovn-controller to be unable to read or write to the
-+        southbound database, because it will always detect the data as stale.
-+        In such a case, run this command so that ovn-controller will reset its
-+        local index so that it can interact with the southbound database again.
-+      </p>
-+      </dd>
-       </dl>
-     </p>
- 
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 85e58d04f..fe6048153 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -72,6 +72,7 @@ static unixctl_cb_func ct_zone_list;
- static unixctl_cb_func extend_table_list;
- static unixctl_cb_func inject_pkt;
- static unixctl_cb_func engine_recompute_cmd;
-+static unixctl_cb_func cluster_state_reset_cmd;
- 
- #define DEFAULT_BRIDGE_NAME "br-int"
- #define DEFAULT_PROBE_INTERVAL_MSEC 5000
-@@ -445,7 +446,7 @@ get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl)
-  * updates 'sbdb_idl' with that pointer. */
- static void
- update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
--             bool *monitor_all_p)
-+             bool *monitor_all_p, bool *reset_ovnsb_idl_min_index)
- {
-     const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
-     if (!cfg) {
-@@ -475,6 +476,12 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
-     if (monitor_all_p) {
-         *monitor_all_p = monitor_all;
-     }
-+    if (*reset_ovnsb_idl_min_index) {
-+        VLOG_INFO("Resetting southbound database cluster state");
-+        engine_set_force_recompute(true);
-+        ovsdb_idl_reset_min_index(ovnsb_idl);
-+        *reset_ovnsb_idl_min_index = false;
-+    }
- }
- 
- static void
-@@ -2287,6 +2294,11 @@ main(int argc, char *argv[])
-     unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd,
-                              NULL);
- 
-+    bool reset_ovnsb_idl_min_index = false;
-+    unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
-+                             cluster_state_reset_cmd,
-+                             &reset_ovnsb_idl_min_index);
-+
-     unsigned int ovs_cond_seqno = UINT_MAX;
-     unsigned int ovnsb_cond_seqno = UINT_MAX;
- 
-@@ -2308,7 +2320,8 @@ main(int argc, char *argv[])
-             ovs_cond_seqno = new_ovs_cond_seqno;
-         }
- 
--        update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all);
-+        update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, &sb_monitor_all,
-+                     &reset_ovnsb_idl_min_index);
-         update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
-         ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl));
- 
-@@ -2558,7 +2571,7 @@ main(int argc, char *argv[])
-     if (!restart) {
-         bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
-         while (!done) {
--            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL);
-+            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, false);
-             update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
- 
-             struct ovsdb_idl_txn *ovs_idl_txn
-@@ -2780,3 +2793,14 @@ engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
-     poll_immediate_wake();
-     unixctl_command_reply(conn, NULL);
- }
-+
-+static void
-+cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+               const char *argv[] OVS_UNUSED, void *idl_reset_)
-+{
-+    bool *idl_reset = idl_reset_;
-+
-+    *idl_reset = true;
-+    poll_immediate_wake();
-+    unixctl_command_reply(conn, NULL);
-+}
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index e45d494e8..989e3643b 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -96,6 +96,34 @@
-         acquired OVSDB lock on SB DB, "standby" if it has not or "paused" if
-         this instance is paused.
-       </dd>
-+
-+      <dt><code>sb-cluster-state-reset</code></dt>
-+      <dd>
-+      <p>
-+        Reset southbound database cluster status when databases are destroyed
-+        and rebuilt.
-+      </p>
-+      <p>
-+        If all databases in a clustered southbound database are removed from
-+        disk, then the stored index of all databases will be reset to zero.
-+        This will cause ovn-northd to be unable to read or write to the
-+        southbound database, because it will always detect the data as stale.
-+        In such a case, run this command so that ovn-northd will reset its
-+        local index so that it can interact with the southbound database again.
-+      </p>
-+      </dd>
-+
-+      <dt><code>nb-cluster-state-reset</code></dt>
-+      <dd>
-+      <p>
-+        Reset northbound database cluster status when databases are destroyed
-+        and rebuilt.
-+      </p>
-+      <p>
-+        This performs the same task as <code>sb-cluster-state-reset</code>
-+        except for the northbound database client.
-+      </p>
-+      </dd>
-       </dl>
-     </p>
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 5f0abeee1..fc05accde 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -56,6 +56,7 @@ static unixctl_cb_func ovn_northd_pause;
- static unixctl_cb_func ovn_northd_resume;
- static unixctl_cb_func ovn_northd_is_paused;
- static unixctl_cb_func ovn_northd_status;
-+static unixctl_cb_func cluster_state_reset_cmd;
- 
- struct northd_context {
-     struct ovsdb_idl *ovnnb_idl;
-@@ -12393,6 +12394,16 @@ main(int argc, char *argv[])
-                              &state);
-     unixctl_command_register("status", "", 0, 0, ovn_northd_status, &state);
- 
-+    bool reset_ovnsb_idl_min_index = false;
-+    unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
-+                             cluster_state_reset_cmd,
-+                             &reset_ovnsb_idl_min_index);
-+
-+    bool reset_ovnnb_idl_min_index = false;
-+    unixctl_command_register("nb-cluster-state-reset", "", 0, 0,
-+                             cluster_state_reset_cmd,
-+                             &reset_ovnnb_idl_min_index);
-+
-     daemonize_complete();
- 
-     /* We want to detect (almost) all changes to the ovn-nb db. */
-@@ -12684,6 +12695,18 @@ main(int argc, char *argv[])
-         ovsdb_idl_set_probe_interval(ovnnb_idl_loop.idl, northd_probe_interval);
-         ovsdb_idl_set_probe_interval(ovnsb_idl_loop.idl, northd_probe_interval);
- 
-+        if (reset_ovnsb_idl_min_index) {
-+            VLOG_INFO("Resetting southbound database cluster state");
-+            ovsdb_idl_reset_min_index(ovnsb_idl_loop.idl);
-+            reset_ovnsb_idl_min_index = false;
-+        }
-+
-+        if (reset_ovnnb_idl_min_index) {
-+            VLOG_INFO("Resetting northbound database cluster state");
-+            ovsdb_idl_reset_min_index(ovnnb_idl_loop.idl);
-+            reset_ovnnb_idl_min_index = false;
-+        }
-+
-         poll_block();
-         if (should_service_stop()) {
-             exiting = true;
-@@ -12762,3 +12785,14 @@ ovn_northd_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
-     unixctl_command_reply(conn, ds_cstr(&s));
-     ds_destroy(&s);
- }
-+
-+static void
-+cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+               const char *argv[] OVS_UNUSED, void *idl_reset_)
-+{
-+    bool *idl_reset = idl_reset_;
-+
-+    *idl_reset = true;
-+    poll_immediate_wake();
-+    unixctl_command_reply(conn, NULL);
-+}
--- 
-2.26.2
-
diff --git a/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch b/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch
deleted file mode 100644
index 17b4266..0000000
--- a/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From a426e7292fc6a8c9d37e60abd0ff58eac983b18e Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 11 Jun 2020 12:47:08 +0530
-Subject: [PATCH 21/22] Fix compilation error when configured with
- --enable-sparse.
-
-The below error is seen.
-
-../controller/ovn-controller.c:2305:70: error: Using plain integer as NULL pointer
-make[1]: *** [Makefile:2000: controller/ovn-controller.o] Error 1
-make[1]: *** Waiting for unfinished jobs....
-
-Fixes: 512b884dea3f("Add northd and ovn-controller cluster status reset commands.")
-CC: Mark Michelson <mmichels@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream ovn master commit 10deb869fff9f0b6736523cf3bdf0d856035895b)
-
-Change-Id: Iaff476538e85a9d68a1269cd55963edb5275dbe3
----
- controller/ovn-controller.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index fe6048153..67b3cd989 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -476,7 +476,7 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
-     if (monitor_all_p) {
-         *monitor_all_p = monitor_all;
-     }
--    if (*reset_ovnsb_idl_min_index) {
-+    if (reset_ovnsb_idl_min_index && *reset_ovnsb_idl_min_index) {
-         VLOG_INFO("Resetting southbound database cluster state");
-         engine_set_force_recompute(true);
-         ovsdb_idl_reset_min_index(ovnsb_idl);
-@@ -2571,7 +2571,7 @@ main(int argc, char *argv[])
-     if (!restart) {
-         bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
-         while (!done) {
--            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, false);
-+            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl, NULL, NULL);
-             update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
- 
-             struct ovsdb_idl_txn *ovs_idl_txn
--- 
-2.26.2
-
diff --git a/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch b/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch
deleted file mode 100644
index 184e0b3..0000000
--- a/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch
+++ /dev/null
@@ -1,565 +0,0 @@
-From ddbdb15a4b6050d9c667ca2bc546a118e208a342 Mon Sep 17 00:00:00 2001
-From: Han Zhou <hzhou@ovn.org>
-Date: Thu, 30 Jul 2020 23:18:58 -0700
-Subject: [PATCH 22/22] Avoid nb_cfg update notification flooding
-
-nb_cfg as a mechanism to "ping" OVN control plane is very useful
-in many ways. However, the current implementation will trigger
-update notifications flooding in the whole control plane. Each
-HV updates to SB the nb_cfg number and all these updates are
-notified to all the other HVs, which is O(n^2). Although updates
-are batched in fewers notifications than n^2, it still generates
-significant load on SB DB and ovn-controllers.
-
-To solve this problem and make the mechanism more useful in large
-scale producation deployment, this patch separates the per HV
-*private* data (write only by the owning chassis and not
-interesting to any other HVs) from the Chassis table to a separate
-table, so that each HV can conditionally monitor and get updates
-only for its own record.
-
-Test result shows great improvement:
-In a test environment with 1200 sandbox HVs, and 12K ports created
-on 80 lswitches and 1 lrouter, do the sync test when the system
-is idle, with command:
-
-    time ovn-nbctl --wait=hv sync
-
-Original result:
-real    0m13.724s
-user    0m0.295s
-sys     0m0.012s
-
-With this patch:
-real    0m3.255s
-user    0m0.248s
-sys     0m0.020s
-
-Also, regarding backwards compatibility note that the nb_cfg from the
-Chassis table is no longer updated. If any system is relying on this
-mechanism they should start using the nb_cfg from the Chassis_Private
-table from now on.
-
-Change-Id: I9be2449f3317ff6b91d9afc8f53a9caa8e14c062
-Co-authored-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
-Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-
-(cherry-picked from upstream master commit 4adc10f58127e45b5883f2e7cb1c702720b95043)
----
- controller/chassis.c        | 30 ++++++++++++++++++++----
- controller/chassis.h        |  8 +++++--
- controller/ovn-controller.c | 42 ++++++++++++++++++++++++++++-----
- lib/chassis-index.c         | 26 +++++++++++++++++++++
- lib/chassis-index.h         |  6 +++++
- northd/ovn-northd.c         | 46 +++++++++++++++++++++++++++++++------
- ovn-sb.ovsschema            | 17 ++++++++++++--
- ovn-sb.xml                  | 42 +++++++++++++++++++++++++++++----
- tests/ovn-controller.at     | 26 +++++++++++++++++++++
- 9 files changed, 218 insertions(+), 25 deletions(-)
-
-diff --git a/controller/chassis.c b/controller/chassis.c
-index bdf3fb950..6ac591e02 100644
---- a/controller/chassis.c
-+++ b/controller/chassis.c
-@@ -621,14 +621,18 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
- const struct sbrec_chassis *
- chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-             struct ovsdb_idl_index *sbrec_chassis_by_name,
-+            struct ovsdb_idl_index *sbrec_chassis_private_by_name,
-             const struct ovsrec_open_vswitch_table *ovs_table,
-             const struct sbrec_chassis_table *chassis_table,
-             const char *chassis_id,
-             const struct ovsrec_bridge *br_int,
--            const struct sset *transport_zones)
-+            const struct sset *transport_zones,
-+            const struct sbrec_chassis_private **chassis_private)
- {
-     struct ovs_chassis_cfg ovs_cfg;
- 
-+    *chassis_private = NULL;
-+
-     /* Get the chassis config from the ovs table. */
-     ovs_chassis_cfg_init(&ovs_cfg);
-     if (!chassis_parse_ovs_config(ovs_table, br_int, &ovs_cfg)) {
-@@ -655,6 +659,18 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                                       !existed ? "registering" : "updating",
-                                       chassis_id);
-         }
-+
-+        const struct sbrec_chassis_private *chassis_private_rec =
-+            chassis_private_lookup_by_name(sbrec_chassis_private_by_name,
-+                                           chassis_id);
-+        if (!chassis_private_rec && ovnsb_idl_txn) {
-+            chassis_private_rec = sbrec_chassis_private_insert(ovnsb_idl_txn);
-+            sbrec_chassis_private_set_name(chassis_private_rec,
-+                                           chassis_id);
-+            sbrec_chassis_private_set_chassis(chassis_private_rec,
-+                                              chassis_rec);
-+        }
-+        *chassis_private = chassis_private_rec;
-     }
- 
-     ovs_chassis_cfg_destroy(&ovs_cfg);
-@@ -710,16 +726,22 @@ chassis_get_mac(const struct sbrec_chassis *chassis_rec,
-  * required. */
- bool
- chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
--                const struct sbrec_chassis *chassis_rec)
-+                const struct sbrec_chassis *chassis_rec,
-+                const struct sbrec_chassis_private *chassis_private_rec)
- {
--    if (!chassis_rec) {
-+    if (!chassis_rec && !chassis_private_rec) {
-         return true;
-     }
-     if (ovnsb_idl_txn) {
-         ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
-                                   "ovn-controller: unregistering chassis '%s'",
-                                   chassis_rec->name);
--        sbrec_chassis_delete(chassis_rec);
-+        if (chassis_rec) {
-+            sbrec_chassis_delete(chassis_rec);
-+        }
-+        if (chassis_private_rec) {
-+            sbrec_chassis_private_delete(chassis_private_rec);
-+        }
-     }
-     return false;
- }
-diff --git a/controller/chassis.h b/controller/chassis.h
-index 178d2957e..81055b403 100644
---- a/controller/chassis.h
-+++ b/controller/chassis.h
-@@ -17,6 +17,7 @@
- #define OVN_CHASSIS_H 1
- 
- #include <stdbool.h>
-+#include "lib/ovn-sb-idl.h"
- 
- struct ovsdb_idl;
- struct ovsdb_idl_index;
-@@ -33,12 +34,15 @@ void chassis_register_ovs_idl(struct ovsdb_idl *);
- const struct sbrec_chassis *chassis_run(
-     struct ovsdb_idl_txn *ovnsb_idl_txn,
-     struct ovsdb_idl_index *sbrec_chassis_by_name,
-+    struct ovsdb_idl_index *sbrec_chassis_private_by_name,
-     const struct ovsrec_open_vswitch_table *,
-     const struct sbrec_chassis_table *,
-     const char *chassis_id, const struct ovsrec_bridge *br_int,
--    const struct sset *transport_zones);
-+    const struct sset *transport_zones,
-+    const struct sbrec_chassis_private **chassis_private);
- bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
--                     const struct sbrec_chassis *);
-+                     const struct sbrec_chassis *,
-+                     const struct sbrec_chassis_private *);
- bool chassis_get_mac(const struct sbrec_chassis *chassis,
-                      const char *bridge_mapping,
-                      struct eth_addr *chassis_mac);
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 67b3cd989..933acf676 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -155,6 +155,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
-     struct ovsdb_idl_condition ce =  OVSDB_IDL_CONDITION_INIT(&ce);
-     struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast);
-     struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp);
-+    struct ovsdb_idl_condition chprv = OVSDB_IDL_CONDITION_INIT(&chprv);
- 
-     if (monitor_all) {
-         ovsdb_idl_condition_add_clause_true(&pb);
-@@ -165,6 +166,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
-         ovsdb_idl_condition_add_clause_true(&ce);
-         ovsdb_idl_condition_add_clause_true(&ip_mcast);
-         ovsdb_idl_condition_add_clause_true(&igmp);
-+        ovsdb_idl_condition_add_clause_true(&chprv);
-         goto out;
-     }
- 
-@@ -196,7 +198,16 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
-                                                   &chassis->header_.uuid);
-         sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
-                                             &chassis->header_.uuid);
-+
-+        /* Monitors Chassis_Private record for current chassis only */
-+        sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ,
-+                                              chassis->name);
-+    } else {
-+        /* During initialization, we monitor all records in Chassis_Private so
-+         * that we don't try to recreate existing ones. */
-+        ovsdb_idl_condition_add_clause_true(&chprv);
-     }
-+
-     if (local_ifaces) {
-         const char *name;
-         SSET_FOR_EACH (name, local_ifaces) {
-@@ -229,6 +240,7 @@ out:
-     sbrec_controller_event_set_condition(ovnsb_idl, &ce);
-     sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast);
-     sbrec_igmp_group_set_condition(ovnsb_idl, &igmp);
-+    sbrec_chassis_private_set_condition(ovnsb_idl, &chprv);
-     ovsdb_idl_condition_destroy(&pb);
-     ovsdb_idl_condition_destroy(&lf);
-     ovsdb_idl_condition_destroy(&mb);
-@@ -237,6 +249,7 @@ out:
-     ovsdb_idl_condition_destroy(&ce);
-     ovsdb_idl_condition_destroy(&ip_mcast);
-     ovsdb_idl_condition_destroy(&igmp);
-+    ovsdb_idl_condition_destroy(&chprv);
- }
- 
- static const char *
-@@ -2090,6 +2103,8 @@ main(int argc, char *argv[])
- 
-     struct ovsdb_idl_index *sbrec_chassis_by_name
-         = chassis_index_create(ovnsb_idl_loop.idl);
-+    struct ovsdb_idl_index *sbrec_chassis_private_by_name
-+        = chassis_private_index_create(ovnsb_idl_loop.idl);
-     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
-         = mcast_group_index_create(ovnsb_idl_loop.idl);
-     struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath
-@@ -2118,7 +2133,8 @@ main(int argc, char *argv[])
-         = igmp_group_index_create(ovnsb_idl_loop.idl);
- 
-     ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
--    ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
-+    ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
-+                         &sbrec_chassis_private_col_nb_cfg);
- 
-     /* Omit the external_ids column of all the tables except for -
-      *  - DNS. pinctrl.c uses the external_ids column of DNS,
-@@ -2155,6 +2171,10 @@ main(int argc, char *argv[])
-      * other_config column so we no longer need to monitor it */
-     ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_external_ids);
- 
-+    /* Do not monitor Chassis_Private external_ids */
-+    ovsdb_idl_omit(ovnsb_idl_loop.idl,
-+                   &sbrec_chassis_private_col_external_ids);
-+
-     update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL, false);
- 
-     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
-@@ -2361,10 +2381,13 @@ main(int argc, char *argv[])
-                 process_br_int(ovs_idl_txn, bridge_table, ovs_table);
-             const char *chassis_id = get_ovs_chassis_id(ovs_table);
-             const struct sbrec_chassis *chassis = NULL;
-+            const struct sbrec_chassis_private *chassis_private = NULL;
-             if (chassis_id) {
-                 chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
-+                                      sbrec_chassis_private_by_name,
-                                       ovs_table, chassis_table, chassis_id,
--                                      br_int, &transport_zones);
-+                                      br_int, &transport_zones,
-+                                      &chassis_private);
-             }
- 
-             if (br_int) {
-@@ -2489,10 +2512,10 @@ main(int argc, char *argv[])
-                 engine_set_force_recompute(false);
-             }
- 
--            if (ovnsb_idl_txn && chassis) {
-+            if (ovnsb_idl_txn && chassis_private) {
-                 int64_t cur_cfg = ofctrl_get_cur_cfg();
--                if (cur_cfg && cur_cfg != chassis->nb_cfg) {
--                    sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
-+                if (cur_cfg && cur_cfg != chassis_private->nb_cfg) {
-+                    sbrec_chassis_private_set_nb_cfg(chassis_private, cur_cfg);
-                 }
-             }
- 
-@@ -2595,10 +2618,17 @@ main(int argc, char *argv[])
-                    ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id)
-                    : NULL);
- 
-+            const struct sbrec_chassis_private *chassis_private
-+                = (chassis_id
-+                   ? chassis_private_lookup_by_name(
-+                         sbrec_chassis_private_by_name, chassis_id)
-+                   : NULL);
-+
-             /* Run all of the cleanup functions, even if one of them returns
-              * false. We're done if all of them return true. */
-             done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis);
--            done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
-+            done = chassis_cleanup(ovnsb_idl_txn,
-+                                   chassis, chassis_private) && done;
-             done = encaps_cleanup(ovs_idl_txn, br_int) && done;
-             done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) && done;
-             if (done) {
-diff --git a/lib/chassis-index.c b/lib/chassis-index.c
-index 39066f4cc..13120fe3e 100644
---- a/lib/chassis-index.c
-+++ b/lib/chassis-index.c
-@@ -40,6 +40,32 @@ chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name,
-     return retval;
- }
- 
-+struct ovsdb_idl_index *
-+chassis_private_index_create(struct ovsdb_idl *idl)
-+{
-+    return ovsdb_idl_index_create1(idl,
-+                                   &sbrec_chassis_private_col_name);
-+}
-+
-+/* Finds and returns the chassis with the given 'name', or NULL if no such
-+ * chassis exists. */
-+const struct sbrec_chassis_private *
-+chassis_private_lookup_by_name(
-+    struct ovsdb_idl_index *sbrec_chassis_private_by_name,
-+    const char *name)
-+{
-+    struct sbrec_chassis_private *target =
-+        sbrec_chassis_private_index_init_row(sbrec_chassis_private_by_name);
-+    sbrec_chassis_private_index_set_name(target, name);
-+
-+    struct sbrec_chassis_private *retval = sbrec_chassis_private_index_find(
-+        sbrec_chassis_private_by_name, target);
-+
-+    sbrec_chassis_private_index_destroy_row(target);
-+
-+    return retval;
-+}
-+
- struct ovsdb_idl_index *
- ha_chassis_group_index_create(struct ovsdb_idl *idl)
- {
-diff --git a/lib/chassis-index.h b/lib/chassis-index.h
-index 302e5f0fd..b9b331f34 100644
---- a/lib/chassis-index.h
-+++ b/lib/chassis-index.h
-@@ -23,6 +23,12 @@ struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *);
- const struct sbrec_chassis *chassis_lookup_by_name(
-     struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name);
- 
-+struct ovsdb_idl_index *chassis_private_index_create(struct ovsdb_idl *);
-+
-+const struct sbrec_chassis_private *
-+chassis_private_lookup_by_name(
-+    struct ovsdb_idl_index *sbrec_chassis_private_by_name, const char *name);
-+
- struct ovsdb_idl_index *ha_chassis_group_index_create(struct ovsdb_idl *idl);
- const struct sbrec_ha_chassis_group *ha_chassis_group_lookup_by_name(
-     struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const char *name);
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index fc05accde..c83f9d5c2 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -12024,6 +12024,11 @@ static const char *rbac_chassis_update[] =
-     {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches",
-      "other_config"};
- 
-+static const char *rbac_chassis_private_auth[] =
-+    {"name"};
-+static const char *rbac_chassis_private_update[] =
-+    {"nb_cfg", "chassis"};
-+
- static const char *rbac_encap_auth[] =
-     {"chassis_name"};
- static const char *rbac_encap_update[] =
-@@ -12061,6 +12066,14 @@ static struct rbac_perm_cfg {
-         .update = rbac_chassis_update,
-         .n_update = ARRAY_SIZE(rbac_chassis_update),
-         .row = NULL
-+    },{
-+        .table = "Chassis_Private",
-+        .auth = rbac_chassis_private_auth,
-+        .n_auth = ARRAY_SIZE(rbac_chassis_private_auth),
-+        .insdel = true,
-+        .update = rbac_chassis_private_update,
-+        .n_update = ARRAY_SIZE(rbac_chassis_private_update),
-+        .row = NULL
-     },{
-         .table = "Encap",
-         .auth = rbac_encap_auth,
-@@ -12230,12 +12243,23 @@ update_northbound_cfg(struct northd_context *ctx,
-     /* Update northbound hv_cfg if appropriate. */
-     if (nbg) {
-         /* Find minimum nb_cfg among all chassis. */
--        const struct sbrec_chassis *chassis;
-+        const struct sbrec_chassis_private *chassis_priv;
-         int64_t hv_cfg = nbg->nb_cfg;
--        SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
--            if (!smap_get_bool(&chassis->other_config, "is-remote", false) &&
--                chassis->nb_cfg < hv_cfg) {
--                hv_cfg = chassis->nb_cfg;
-+        SBREC_CHASSIS_PRIVATE_FOR_EACH (chassis_priv, ctx->ovnsb_idl) {
-+            const struct sbrec_chassis *chassis = chassis_priv->chassis;
-+            if (chassis) {
-+                if (smap_get_bool(&chassis->other_config,
-+                                  "is-remote", false)) {
-+                    /* Skip remote chassises. */
-+                    continue;
-+                }
-+            } else {
-+                VLOG_WARN("Chassis not exist for Chassis_Private record, "
-+                          "name: %s", chassis_priv->name);
-+            }
-+
-+            if (chassis_priv->nb_cfg < hv_cfg) {
-+                hv_cfg = chassis_priv->nb_cfg;
-             }
-         }
- 
-@@ -12248,7 +12272,8 @@ update_northbound_cfg(struct northd_context *ctx,
- 
- /* Handle a fairly small set of changes in the southbound database. */
- static void
--ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop,
-+ovnsb_db_run(struct northd_context *ctx,
-+             struct ovsdb_idl_loop *sb_loop,
-              struct hmap *ports)
- {
-     if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
-@@ -12529,10 +12554,17 @@ main(int argc, char *argv[])
-     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);
- 
-     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
--    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
-     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
-     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);
- 
-+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private);
-+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-+                         &sbrec_chassis_private_col_name);
-+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-+                         &sbrec_chassis_private_col_chassis);
-+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-+                         &sbrec_chassis_private_col_nb_cfg);
-+
-     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
-     add_column_noalert(ovnsb_idl_loop.idl,
-                        &sbrec_ha_chassis_col_chassis);
-diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
-index 99c5de822..3af76540a 100644
---- a/ovn-sb.ovsschema
-+++ b/ovn-sb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Southbound",
--    "version": "2.8.2",
--    "cksum": "464326363 21916",
-+    "version": "2.9.0",
-+    "cksum": "223619766 22548",
-     "tables": {
-         "SB_Global": {
-             "columns": {
-@@ -46,6 +46,19 @@
-                                               "max": "unlimited"}}},
-             "isRoot": true,
-             "indexes": [["name"]]},
-+        "Chassis_Private": {
-+            "columns": {
-+                "name": {"type": "string"},
-+                "chassis": {"type": {"key": {"type": "uuid",
-+                                             "refTable": "Chassis",
-+                                             "refType": "weak"},
-+                                     "min": 0, "max": 1}},
-+                "nb_cfg": {"type": {"key": "integer"}},
-+                "external_ids": {
-+                    "type": {"key": "string", "value": "string",
-+                             "min": 0, "max": "unlimited"}}},
-+            "isRoot": true,
-+            "indexes": [["name"]]},
-         "Encap": {
-             "columns": {
-                 "type": {"type": {"key": {
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index a74d9c3ea..59b21711b 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -256,10 +256,8 @@
-     </column>
- 
-     <column name="nb_cfg">
--      Sequence number for the configuration.  When <code>ovn-controller</code>
--      updates the configuration of a chassis from the contents of the
--      southbound database, it copies <ref table="SB_Global" column="nb_cfg"/>
--      from the <ref table="SB_Global"/> table into this column.
-+      Deprecated. This column is replaced by the <ref table="Chassis_Private"
-+      column="nb_cfg"/> column of the <ref table="Chassis_Private"/> table.
-     </column>
- 
-     <column name="other_config" key="ovn-bridge-mappings">
-@@ -366,6 +364,42 @@
-     </group>
-   </table>
- 
-+  <table name="Chassis_Private" title="Chassis Private">
-+    <p>
-+      Each row in this table maintains per chassis private data that are
-+      accessed only by the owning chassis (write only) and ovn-northd, not by
-+      any other chassis.  These data are stored in this separate table instead
-+      of the <ref table="Chassis"/> table for performance considerations:
-+      the rows in this table can be conditionally monitored by chassises so
-+      that each chassis only get update notifications for its own row, to avoid
-+      unnecessary chassis private data update flooding in a large scale
-+      deployment.
-+    </p>
-+
-+    <column name="name">
-+      The name of the chassis that owns these chassis-private data.
-+    </column>
-+
-+    <column name="chassis">
-+      The reference to <ref table="Chassis"/> table for the chassis that owns
-+      these chassis-private data.
-+    </column>
-+
-+    <column name="nb_cfg">
-+      Sequence number for the configuration.  When <code>ovn-controller</code>
-+      updates the configuration of a chassis from the contents of the
-+      southbound database, it copies <ref table="SB_Global" column="nb_cfg"/>
-+      from the <ref table="SB_Global"/> table into this column.
-+    </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="Encap" title="Encapsulation Types">
-     <p>
-       The <ref column="encaps" table="Chassis"/> column in the <ref
-diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
-index 77936c776..1b96934b1 100644
---- a/tests/ovn-controller.at
-+++ b/tests/ovn-controller.at
-@@ -321,3 +321,29 @@ as ovn-sb
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
- 
- AT_CLEANUP
-+
-+# Checks that ovn-controller increments the nb_cfg value in the Chassis_Private table
-+AT_SETUP([ovn-controller - Bump Chassis_Private nb_cfg value])
-+AT_KEYWORDS([ovn])
-+ovn_start
-+
-+net_add n1
-+sim_add hv
-+as hv
-+ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+
-+OVS_WAIT_UNTIL([test xhv = x`ovn-sbctl --columns name --bare find chassis`])
-+
-+# Bump the NB_Global nb_cfg value
-+nb_global_id=$(ovn-nbctl --columns _uuid --bare find nb_global)
-+ovn-nbctl set NB_Global ${nb_global_id} nb_cfg=999
-+
-+# ovn-controller should bump the nb_cfg in the chassis_private table
-+OVS_WAIT_UNTIL([test x999 = x`ovn-sbctl --columns nb_cfg --bare find chassis_private`])
-+
-+# Assert that the the nb_cfg from the Chassis table was not incremented
-+OVS_WAIT_UNTIL([test x0 = x`ovn-sbctl --columns nb_cfg --bare find chassis`])
-+
-+OVN_CLEANUP([hv])
-+AT_CLEANUP
--- 
-2.26.2
-
diff --git a/SPECS/ovn2.13.spec b/SPECS/ovn2.13.spec
index c0698b5..27cf835 100644
--- a/SPECS/ovn2.13.spec
+++ b/SPECS/ovn2.13.spec
@@ -16,7 +16,7 @@
 %define pkgver 2.13
 %define pkgname ovn%{pkgver}
 
-%define upstreamver 20.06
+%define upstreamver 20.09
 
 #%%global commit0 7886ac9ed807d6ff942edde624a3f9331da7332a
 #%%global date 20200217
@@ -59,8 +59,8 @@ Name: %{pkgname}
 Summary: Open Virtual Network support
 Group: System Environment/Daemons
 URL: http://www.openvswitch.org/
-Version: %{upstreamver}.2
-Release: 11%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Version: %{upstreamver}.0
+Release: 2%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
 Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
 Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
 
@@ -74,7 +74,7 @@ Source: https://github.com/ovn-org/ovn/archive/%{commit0}.tar.gz#/ovn-%{shortcom
 # Upstream version is called 20.03, not 2.13. Once we switch to using the
 # same versioning scheme for RH, we can reference %{version} here.
 # XXX Are OVN releases listed on openvswitch.org?
-Source: https://www.openvswitch.org/releases/ovn-%{version}.tar.gz
+Source: https://github.com/ovn-org/ovn/archive/v%{version}.tar.gz#/ovn-%{version}.tar.gz
 %endif
 
 
@@ -107,100 +107,23 @@ Source506: x86_64-native-linuxapp-gcc-config
 
 # ovn-patches
 
-# Bug 1871056
-Patch01: 0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch
-Patch02: 0002-Add-support-for-DHCP-domain-search-option-119.patch
-
 # OVN (including OVS if required) backports (0 - 399)
-# Bug 1861294
-# Bug 1829762
-# Bug 1846300
-# Bug 1857537
-# Bug 1858191
-# Bug 1861042
-# Bug 1860053
-# Bug 1861298
-# Bug 1853716
-# Bug 1866820
-Patch03: 0003-Support-packet-metadata-marking-for-logical-router-p.patch
-Patch04: 0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch
-Patch05: 0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
-Patch06: 0006-Introduce-icmp6_error-action.patch
-Patch07: 0007-Introduce-icmp6.frag_mtu-action.patch
-Patch08: 0008-northd-introduce-icmp6_error-logical-flows-in-router.patch
-Patch09: 0009-Add-IP-address-normalization-to-ovn-utils.patch
-Patch10: 0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch
-Patch11: 0011-Add-expression-writeability-scopes.patch
-Patch12: 0012-Used-nested-actions-in-ct_commit.patch
-Patch13: 0013-Add-ECMP-symmetric-replies.patch
-Patch14: 0014-expr.c-Fix-argument-type-of-expr_write_scope.patch
-Patch15: 0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch
-
-# Bug 1823003
-Patch16: 0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch
-
-# Bug 1836804
-Patch17: 0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch
-Patch18: 0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch
-
-# Bug 1861294
-# Bug 1829762
-
-# Bug 1829109
-Patch19: 0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch
-Patch20: 0021-Fix-compilation-error-when-configured-with-enable-sp.patch
-
-# Bug 1872681
-Patch30: 0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch
-
-# Bug 1871054
-Patch80: 0022-Avoid-nb_cfg-update-notification-flooding.patch
-
-# Bug 1871961
-Patch90: 0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch
-
-# Bug 1874745
-Patch100: 0001-ovn-northd-Rate-limit-missing-chassis-log.patch
-
-# Bug 1812820
-Patch110: 0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch
-
-# Bug 1873455
-Patch115: 0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch
-
-# Bug 1875727
-Patch120: 0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch
 
-# Bug 1873032
-Patch130: 0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch
-Patch131: 0002-chassis-Fix-chassis_private-record-updates-when-the-.patch
+# Bug 1845109
+Patch001: 0001-ovn-nbctl-add-may-exist-if-exists-options-for-policy.patch
 
-# Bug 1875337
-Patch140: 0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch
-
-# Bug 1857563
-Patch150: 0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch
-
-# Bug 1831558
-Patch160: 0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch
-
-# Bug 1878451
-Patch170: 0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch
-
-# Bug 1858878
-Patch180: 0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch
-Patch181: 0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch
+# Bug 1886314
+Patch010: 0001-ovn-northd-Add-localnet-ports-to-Multicast_Groups-cr.patch
 
 # OpenvSwitch backports (800-) if required.
 Patch800: 0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch
-
-Patch810: 0003-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
+Patch801: 0002-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
 
 # Bug 1808580
-Patch820: 0001-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
+Patch802: 0003-ovsdb-idl-Avoid-inconsistent-IDL-state-with-OVSDB_MO.patch
 
 # Bug 1829762
-Patch830: 0019-ovsdb-idl-Add-function-to-reset-min_index.patch
+Patch803: 0004-ovsdb-idl-Add-function-to-reset-min_index.patch
 
 # FIXME Sphinx is used to generate some manpages, unfortunately, on RHEL, it's
 # in the -optional repository and so we can't require it directly since RHV
@@ -644,11 +567,32 @@ fi
 %{_unitdir}/ovn-controller-vtep.service
 
 %changelog
-* Tue Sep 16 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-11
+* Fri Oct 9 2020 Dumitru Ceara <dceara@redhat.com> - 20.09.0-2
+- Backport "ovn-northd: Add localnet ports to Multicast_Groups created by IGMP_Group." (#1886314)
+
+* Tue Sep 29 2020 Numan Siddique <nusiddiq@redhat.com> - 20.09.0-1
+- Rebase to upstream v20.09.0.
+
+* Mon Sep 28 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.2-15
+- Backport "binding: Rely on qos_map for consider_localnet_lport" (#1878943)
+- Backport "binding: fix localnet QoS configuration after I-P" (#1878943)
+
+* Mon Sep 28 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.2-14
+- Backport "ovn-nbctl: add --may-exist/--if-exists options for policy routing" (#1845109)
+
+* Mon Sep 21 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-13
+- Backport "ovn-ctl: Handle cluster db upgrades for run_(nb/sb)_ovsdb" (#1868392)
+- Backport "northd: Add lflows to send all pkts to conntrack if LB is configured on a lswitch." (#1870359)
+- Backport "ovn-northd: Fix router policy pkt mark over flow if the value is greater than signed int." (#1878248)
+
+* Tue Sep 15 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-12
+- Fixed the date typo in changelog.
+
+* Tue Sep 15 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-11
 - Backport "ovn-controller: Persist the conjunction ids allocated for conjuctive matches." (#1858878)
 - Backport "I-P engine: Provide the option to store client data in engine ctx." (#1858878)
 
-* Mon Sep 15 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-10
+* Mon Sep 14 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-10
 - Backport "ovn-northd: Fix multiple ARP replies for SNAT entries configured on a distributed router." (#1878451)
 
 * Tue Sep 8 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.2-9