diff --git a/.ovn2.13.metadata b/.ovn2.13.metadata
index 543429b..e30eba6 100644
--- a/.ovn2.13.metadata
+++ b/.ovn2.13.metadata
@@ -1,5 +1,5 @@
 002450621b33c5690060345b0aac25bc2426d675 SOURCES/docutils-0.12.tar.gz
 b5734e2bdf09d15d8950b3423dbecec9825d129a SOURCES/openvswitch-2.13.0.tar.gz
-3fac0d814f52e9744195042bb9a5e93561eec9bc SOURCES/ovn-2.13.0.tar.gz
+e2761d2e4a5fe7d0fc9f9900dda176ba1a2fc049 SOURCES/ovn-20.06.2.tar.gz
 d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz
 6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a SOURCES/Sphinx-1.1.3.tar.gz
diff --git a/SOURCES/0001-Add-SCTP-support-to-load-balancers.patch b/SOURCES/0001-Add-SCTP-support-to-load-balancers.patch
deleted file mode 100644
index 2c966fa..0000000
--- a/SOURCES/0001-Add-SCTP-support-to-load-balancers.patch
+++ /dev/null
@@ -1,364 +0,0 @@
-From 080b77af805d1c48f48c617c1fab095edcebaffd Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 9 Mar 2020 17:09:15 -0400
-Subject: [PATCH] Add SCTP support to load balancers.
-
-This allows for load balancers to use SCTP as a supported protocol in
-addition to the already-supported UDP and TCP.
-
-With this patch, health checks are not supported for SCTP load
-balancers. A test has been added to ensure that this is the case. Health
-checks should be added for SCTP load balancers in the near future. When
-that's done, the existing test can be updated to ensure that the SCTP
-health check works properly.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit c4700eed17da8615107553aec82852a37d401821)
-
-Change-Id: Iecbc4a2329716aaa0f37a5f53acd5b5cfa74d133
----
- lib/actions.c         |   8 ++--
- northd/ovn-northd.c   |  55 ++++++++++++++---------
- ovn-nb.ovsschema      |   6 +--
- ovn-nb.xml            |  10 ++---
- tests/ovn.at          | 121 +++++++++++++++++++++++++++++++++++++++++++++++++-
- utilities/ovn-nbctl.c |   8 ++--
- 6 files changed, 170 insertions(+), 38 deletions(-)
-
-diff --git a/lib/actions.c b/lib/actions.c
-index f22acdd..6351db7 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -1957,10 +1957,12 @@ validate_empty_lb_backends(struct action_context *ctx,
-             }
-             break;
-         case EMPTY_LB_PROTOCOL:
--            if (strcmp(c->string, "tcp") && strcmp(c->string, "udp")) {
-+            if (strcmp(c->string, "tcp") &&
-+                strcmp(c->string, "udp") &&
-+                strcmp(c->string, "sctp")) {
-                 lexer_error(ctx->lexer,
--                    "Load balancer protocol '%s' is not 'tcp' or 'udp'",
--                    c->string);
-+                    "Load balancer protocol '%s' is not 'tcp', 'udp', "
-+                    "or 'sctp'", c->string);
-                 return;
-             }
-             break;
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 75c19df..bb68b8f 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -3173,10 +3173,21 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
-         lb->vips[n_vips].backend_ips = xstrdup(node->value);
- 
-         struct nbrec_load_balancer_health_check *lb_health_check = NULL;
--        for (size_t i = 0; i < nbrec_lb->n_health_check; i++) {
--            if (!strcmp(nbrec_lb->health_check[i]->vip, node->key)) {
--                lb_health_check = nbrec_lb->health_check[i];
--                break;
-+        if (nbrec_lb->protocol && !strcmp(nbrec_lb->protocol, "sctp")) {
-+            if (nbrec_lb->n_health_check > 0) {
-+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-+                VLOG_WARN_RL(&rl,
-+                             "SCTP load balancers do not currently support "
-+                             "health checks. Not creating health checks for "
-+                             "load balancer " UUID_FMT,
-+                             UUID_ARGS(&nbrec_lb->header_.uuid));
-+            }
-+        } else {
-+            for (size_t i = 0; i < nbrec_lb->n_health_check; i++) {
-+                if (!strcmp(nbrec_lb->health_check[i]->vip, node->key)) {
-+                    lb_health_check = nbrec_lb->health_check[i];
-+                    break;
-+                }
-             }
-         }
- 
-@@ -5558,10 +5569,13 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, struct ovn_lb *lb)
- 
-         const char *proto = NULL;
-         if (lb_vip->vip_port) {
--            if (lb->nlb->protocol && !strcmp(lb->nlb->protocol, "udp")) {
--                proto = "udp";
--            } else {
--                proto = "tcp";
-+            proto = "tcp";
-+            if (lb->nlb->protocol) {
-+                if (!strcmp(lb->nlb->protocol, "udp")) {
-+                    proto = "udp";
-+                } else if (!strcmp(lb->nlb->protocol, "sctp")) {
-+                    proto = "sctp";
-+                }
-             }
-         }
- 
-@@ -7569,7 +7583,7 @@ 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 is_udp, struct nbrec_load_balancer *lb,
-+                   const char *proto, struct nbrec_load_balancer *lb,
-                    struct shash *meter_groups, struct sset *nat_entries)
- {
-     build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT,
-@@ -7624,11 +7638,10 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
-          * S_ROUTER_IN_DNAT stage. */
-         struct ds unsnat_match = DS_EMPTY_INITIALIZER;
-         ds_put_format(&unsnat_match, "%s && %s.dst == %s && %s",
--                      ip_match, ip_match, lb_vip->vip,
--                      is_udp ? "udp" : "tcp");
-+                      ip_match, ip_match, lb_vip->vip, proto);
-         if (lb_vip->vip_port) {
--            ds_put_format(&unsnat_match, " && %s.dst == %d",
--                          is_udp ? "udp" : "tcp", lb_vip->vip_port);
-+            ds_put_format(&unsnat_match, " && %s.dst == %d", proto,
-+                          lb_vip->vip_port);
-         }
- 
-         ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT, 120,
-@@ -7654,7 +7667,7 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
- 
-         if (backend->port) {
-             ds_put_format(&undnat_match, " && %s.src == %d) || ",
--                          is_udp ? "udp" : "tcp", backend->port);
-+                          proto, backend->port);
-         } else {
-             ds_put_cstr(&undnat_match, ") || ");
-         }
-@@ -9203,15 +9216,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
- 
-                 int prio = 110;
-                 bool is_udp = nullable_string_is_equal(nb_lb->protocol, "udp");
-+                bool is_sctp = nullable_string_is_equal(nb_lb->protocol,
-+                                                        "sctp");
-+                const char *proto = is_udp ? "udp" : is_sctp ? "sctp" : "tcp";
- 
-                 if (lb_vip->vip_port) {
--                    if (is_udp) {
--                        ds_put_format(&match, " && udp && udp.dst == %d",
--                                      lb_vip->vip_port);
--                    } else {
--                        ds_put_format(&match, " && tcp && tcp.dst == %d",
--                                      lb_vip->vip_port);
--                    }
-+                    ds_put_format(&match, " && %s && %s.dst == %d", proto,
-+                                  proto, lb_vip->vip_port);
-                     prio = 120;
-                 }
- 
-@@ -9220,7 +9231,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                                   od->l3redirect_port->json_key);
-                 }
-                 add_router_lb_flow(lflows, od, &match, &actions, prio,
--                                   lb_force_snat_ip, lb_vip, is_udp,
-+                                   lb_force_snat_ip, lb_vip, proto,
-                                    nb_lb, meter_groups, &nat_entries);
-             }
-         }
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index 843e979..ea6f4e3 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
--    "version": "5.20.0",
--    "cksum": "2846067333 25243",
-+    "version": "5.20.1",
-+    "cksum": "721375950 25251",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -168,7 +168,7 @@
-                              "min": 0, "max": "unlimited"}},
-                 "protocol": {
-                     "type": {"key": {"type": "string",
--                             "enum": ["set", ["tcp", "udp"]]},
-+                             "enum": ["set", ["tcp", "udp", "sctp"]]},
-                              "min": 0, "max": 1}},
-                 "health_check": {"type": {
-                     "key": {"type": "uuid",
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index 4a422bb..f7ba9c3 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -1458,11 +1458,11 @@
- 
-     <column name="protocol">
-       <p>
--        Valid protocols are <code>tcp</code> or <code>udp</code>.  This column
--        is useful when a port number is provided as part of the
--        <code>vips</code> column.  If this column is empty and a port number
--        is provided as part of <code>vips</code> column, OVN assumes the
--        protocol to be <code>tcp</code>.
-+        Valid protocols are <code>tcp</code>, <code>udp</code>, or
-+        <code>sctp</code>.  This column is useful when a port number is
-+        provided as part of the <code>vips</code> column.  If this column is
-+        empty and a port number is provided as part of <code>vips</code>
-+        column, OVN assumes the protocol to be <code>tcp</code>.
-       </p>
-     </column>
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 1402fae..9f3d9d3 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1423,8 +1423,8 @@ trigger_event(event = "empty_lb_backends", meter="event-elb" vip = "10.0.0.1:80"
-     encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63,meter_id=5)
- 
- # Testing invalid vip results in extra error messages from socket-util.c
--trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "sctp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
--    Load balancer protocol 'sctp' is not 'tcp' or 'udp'
-+trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "aarp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
-+    Load balancer protocol 'aarp' is not 'tcp', 'udp', or 'sctp'
- trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "bacon");
-     Load balancer 'bacon' is not a UUID
- 
-@@ -17894,6 +17894,123 @@ AT_CHECK([cat lflows.txt], [0], [dnl
- OVN_CLEANUP([hv1], [hv2])
- AT_CLEANUP
- 
-+AT_SETUP([ovn -- SCTP Load balancer health checks])
-+AT_KEYWORDS([lb sctp])
-+
-+# Currently this test just ensures that no service monitors get created when
-+# An SCTP load balancer is configured to use health checks. Once SCTP load
-+# balancers are modified to allow health checks, this test should be altered
-+# to ensure the health check succeeds.
-+
-+ovn_start
-+
-+# Set up same network as previous health check test. As long as health checks
-+# aren't allowed for SCTP load balancers, the network will not be used for
-+# much. However, having the network in place will make it easy to alter when
-+# health checks are allowed.
-+
-+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-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
-+
-+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=sw1-p1 \
-+    options:tx_pcap=hv2/vif1-tx.pcap \
-+    options:rxq_pcap=hv2/vif1-rx.pcap \
-+    ofport-request=1
-+
-+ovn-nbctl ls-add sw0
-+
-+ovn-nbctl lsp-add sw0 sw0-p1
-+ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
-+ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
-+
-+ovn-nbctl lsp-add sw0 sw0-p2
-+ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
-+ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
-+
-+# Create the second logical switch with one port
-+ovn-nbctl ls-add sw1
-+ovn-nbctl lsp-add sw1 sw1-p1
-+ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
-+ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
-+
-+# Create a logical router and attach both logical switches
-+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 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
-+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 lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 sctp
-+
-+ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
-+ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:10.0.0.2
-+
-+ovn-nbctl --wait=sb -- --id=@hc create \
-+Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
-+health_check @hc
-+
-+ovn-nbctl --wait=sb ls-lb-add sw0 lb1
-+ovn-nbctl --wait=sb ls-lb-add sw1 lb1
-+ovn-nbctl --wait=sb lr-lb-add lr0 lb1
-+
-+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 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
-+
-+# schedule the gw router port to a chassis. Change the name of the chassis
-+ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20
-+
-+OVN_POPULATE_ARP
-+ovn-nbctl --wait=hv sync
-+
-+# And now for the anticlimax. We need to ensure that there is no
-+# service monitor in the southbound db.
-+
-+AT_CHECK([test 0 = `ovn-sbctl --bare --columns _uuid find \
-+service_monitor | sed '/^$/d' | wc -l`])
-+
-+# Let's also be sure the warning message about SCTP load balancers is
-+# is in the ovn-northd log
-+
-+AT_CHECK([test 1 = `grep -c "SCTP load balancers do not currently support health checks" northd/ovn-northd.log`])
-+
-+AT_CLEANUP
-+
- AT_SETUP([ovn -- ARP/ND request broadcast limiting])
- ovn_start
- 
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index e80058e..59abe00 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -2734,9 +2734,11 @@ nbctl_lb_add(struct ctl_context *ctx)
-         /* Validate protocol. */
-         lb_proto = ctx->argv[4];
-         is_update_proto = true;
--        if (strcmp(lb_proto, "tcp") && strcmp(lb_proto, "udp")) {
--            ctl_error(ctx, "%s: protocol must be one of \"tcp\", \"udp\".",
--                      lb_proto);
-+        if (strcmp(lb_proto, "tcp") &&
-+            strcmp(lb_proto, "udp") &&
-+            strcmp(lb_proto, "sctp")) {
-+            ctl_error(ctx, "%s: protocol must be one of \"tcp\", \"udp\", "
-+                      " or \"sctp\".", lb_proto);
-             return;
-         }
-     }
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-Add-external_ids-column-for-tables-in-nb-schema.patch b/SOURCES/0001-Add-external_ids-column-for-tables-in-nb-schema.patch
deleted file mode 100644
index 928a1f1..0000000
--- a/SOURCES/0001-Add-external_ids-column-for-tables-in-nb-schema.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From aff9c19d5fb9bb2cfba220d32ec68111d1adb1c5 Mon Sep 17 00:00:00 2001
-From: Tao YunXiang <taoyunxiang@cmss.chinamobile.com>
-Date: Wed, 11 Mar 2020 11:37:53 +0800
-Subject: [PATCH] Add external_ids column for tables in nb schema
-
-"Logical_Router_Policy"  and "Forwarding_Group" tables doesn't have
-"external_ids" column. I think it is better to add it, so CMS could
-fill it with useful information.
-
-Author: Tao YunXiang <taoyunxiang@cmss.chinamobile.com>
-Co-authored-by: Liu Chang <liuchang@cmss.chinamobile.com>
-Co-authored-by: Rong Yin <rongyin@cmss.chinamobile.com>
-Signed-off-by: Tao YunXiang <taoyunxiang@cmss.chinamobile.com>
-Signed-off-by: Liu Chang <liuchang@cmss.chinamobile.com>
-Signed-off-by: Rong Yin <rongyin@cmss.chinamobile.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 45de84bff0224e61847ff52d480c6500153ce699)
-
-Change-Id: I85671ce34238dd8989756b395d85785c25c8842c
----
- ovn-nb.ovsschema | 10 ++++++++--
- ovn-nb.xml       | 12 ++++++++++++
- 2 files changed, 20 insertions(+), 2 deletions(-)
-
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index bbd6c25..843e979 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
-     "version": "5.20.0",
--    "cksum": "987891875 24923",
-+    "cksum": "2846067333 25243",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -125,6 +125,9 @@
-                 "vip": {"type": "string"},
-                 "vmac": {"type": "string"},
-                 "liveness": {"type": "boolean"},
-+                "external_ids": {
-+                    "type": {"key": "string", "value": "string",
-+                             "min": 0, "max": "unlimited"}},
-                 "child_port": {"type": {"key": "string",
-                                         "min": 1, "max": "unlimited"}}},
-             "isRoot": false},
-@@ -366,7 +369,10 @@
-                 "action": {"type": {
-                     "key": {"type": "string",
-                             "enum": ["set", ["allow", "drop", "reroute"]]}}},
--                "nexthop": {"type": {"key": "string", "min": 0, "max": 1}}},
-+                "nexthop": {"type": {"key": "string", "min": 0, "max": 1}},
-+                "external_ids": {
-+                    "type": {"key": "string", "value": "string",
-+                             "min": 0, "max": "unlimited"}}},
-             "isRoot": false},
-         "NAT": {
-             "columns": {
-diff --git a/ovn-nb.xml b/ovn-nb.xml
-index f30cc9e..4a422bb 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -1313,6 +1313,12 @@
-     <column name="child_port">
-       List of child ports in the forwarding group.
-     </column>
-+
-+    <group title="Common Columns">
-+      <column name="external_ids">
-+        See <em>External IDs</em> at the beginning of this document.
-+      </column>
-+    </group>
-   </table>
- 
-   <table name="Address_Set" title="Address Sets">
-@@ -2498,6 +2504,12 @@
-         address of a connected router port or the IP address of a logical port.
-       </p>
-     </column>
-+
-+    <group title="Common Columns">
-+      <column name="external_ids">
-+        See <em>External IDs</em> at the beginning of this document.
-+      </column>
-+    </group>
-   </table>
- 
-   <table name="NAT" title="NAT rules">
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch b/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch
deleted file mode 100644
index f4c91ae..0000000
--- a/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch
+++ /dev/null
@@ -1,269 +0,0 @@
-From a60b2826bd4eb0144d2dc9b25b63b3a6ca5106c7 Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Mon, 20 Apr 2020 09:25:09 -0400
-Subject: [PATCH 1/2] DNS: Make DNS lookups case insensitive.
-
-From RFC 1035 Section 2.3.3:
-
-"For all parts of the DNS that are part of the official protocol, all
-comparisons between character strings (e.g., labels, domain names, etc.)
-are done in a case-insensitive manner."
-
-OVN was using case-sensitive lookups and therefore was not complying.
-This change makes lookups case insensitive by storing lowercase record
-names in the southbound database and converting incoming query names to
-lowercase.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819069
-Reported-by: Jianlin Shi <jishi@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c |  7 ++++-
- lib/ovn-util.c       | 15 +++++++++++
- lib/ovn-util.h       |  5 ++++
- northd/ovn-northd.c  | 15 ++++++++++-
- ovn-sb.xml           |  3 ++-
- tests/ovn.at         | 61 ++++++++++++++++++++++++++++++++------------
- 6 files changed, 87 insertions(+), 19 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 8703641c2..8592d4e3f 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -2368,7 +2368,12 @@ pinctrl_handle_dns_lookup(
-         struct dns_data *d = iter->data;
-         for (size_t i = 0; i < d->n_dps; i++) {
-             if (d->dps[i] == dp_key) {
--                answer_ips = smap_get(&d->records, ds_cstr(&query_name));
-+                /* DNS records in SBDB are stored in lowercase. Convert to
-+                 * lowercase to perform case insensitive lookup
-+                 */
-+                char *query_name_lower = str_tolower(ds_cstr(&query_name));
-+                answer_ips = smap_get(&d->records, query_name_lower);
-+                free(query_name_lower);
-                 if (answer_ips) {
-                     break;
-                 }
-diff --git a/lib/ovn-util.c b/lib/ovn-util.c
-index 514e2489f..1b30c2e9a 100644
---- a/lib/ovn-util.c
-+++ b/lib/ovn-util.c
-@@ -21,6 +21,7 @@
- #include "openvswitch/ofp-parse.h"
- #include "ovn-nb-idl.h"
- #include "ovn-sb-idl.h"
-+#include <ctype.h>
- 
- VLOG_DEFINE_THIS_MODULE(ovn_util);
- 
-@@ -550,3 +551,17 @@ ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2)
-             (addr1->family == AF_INET ? addr1->ipv4 == addr2->ipv4 :
-              IN6_ARE_ADDR_EQUAL(&addr1->ipv6, &addr2->ipv6)));
- }
-+
-+char *
-+str_tolower(const char *orig)
-+{
-+    char *copy = xmalloc(strlen(orig) + 1);
-+    char *p = copy;
-+
-+    while (*orig) {
-+        *p++ = tolower(*orig++);
-+    }
-+    *p = '\0';
-+
-+    return copy;
-+}
-diff --git a/lib/ovn-util.h b/lib/ovn-util.h
-index 11238f61c..4076e8b9a 100644
---- a/lib/ovn-util.h
-+++ b/lib/ovn-util.h
-@@ -124,4 +124,9 @@ struct v46_ip {
- 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);
-+
-+/* Returns a lowercase copy of orig.
-+ * Caller must free the returned string.
-+ */
-+char *str_tolower(const char *orig);
- #endif
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index f7d3988d7..515722c5d 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -10698,7 +10698,20 @@ sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
-             dns_info->sb_dns,
-             (struct sbrec_datapath_binding **)dns_info->sbs,
-             dns_info->n_sbs);
--        sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
-+
-+        /* DNS lookups are case-insensitive. Convert records to lowercase so
-+         * we can do consistent lookups when DNS requests arrive
-+         */
-+        struct smap lower_records = SMAP_INITIALIZER(&lower_records);
-+        struct smap_node *node;
-+        SMAP_FOR_EACH (node, &dns_info->nb_dns->records) {
-+            smap_add_nocopy(&lower_records, xstrdup(node->key),
-+                            str_tolower(node->value));
-+        }
-+
-+        sbrec_dns_set_records(dns_info->sb_dns, &lower_records);
-+
-+        smap_destroy(&lower_records);
-         free(dns_info->sbs);
-         free(dns_info);
-     }
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 72466b97e..5f8da534c 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -3597,7 +3597,8 @@ tcp.flags = RST;
-     <column name="records">
-       Key-value pair of DNS records with <code>DNS query name</code> as the key
-       and a string of IP address(es) separated by comma or space as the
--      value.
-+      value. ovn-northd stores the DNS query name in all lowercase in order to
-+      facilitate case-insensitive lookups.
- 
-       <p><b>Example: </b> "vm1.ovn.org" = "10.0.0.4 aef0::4"</p>
-     </column>
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 0f02e8144..b78637044 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -8328,6 +8328,12 @@ set_dns_params() {
-         # IPv4 address - 10.0.0.4
-         expected_dns_answer=${query_name}00010001${ttl}00040a000004
-         ;;
-+    VM1)
-+        # VM1.OVN.ORG
-+        query_name=03564d31034f564e034f524700
-+        # IPv4 address - 10.0.0.4
-+        expected_dns_answer=${query_name}00010001${ttl}00040a000004
-+        ;;
-     vm2)
-         # vm2.ovn.org
-         query_name=03766d32036f766e036f726700
-@@ -8490,6 +8496,29 @@ reset_pcap_file hv1-vif2 hv1/vif2
- rm -f 1.expected
- rm -f 2.expected
- 
-+# Try vm1 again but an all-caps query name
-+
-+set_dns_params VM1
-+src_ip=`ip_to_hex 10 0 0 6`
-+dst_ip=`ip_to_hex 10 0 0 1`
-+dns_reply=1
-+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-+
-+# NXT_RESUMEs should be 3.
-+OVS_WAIT_UNTIL([test 3 = `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
-+
- # Clear the query name options for ls1-lp2
- ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
- 
-@@ -8499,8 +8528,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=0
- test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
- 
--# NXT_RESUMEs should be 3.
--OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 4.
-+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
- AT_CHECK([cat 1.packets], [0], [])
-@@ -8521,8 +8550,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=0
- test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
- 
--# NXT_RESUMEs should be 3 only.
--OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 4 only.
-+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
- AT_CHECK([cat 2.packets], [0], [])
-@@ -8542,8 +8571,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=1
- test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
- 
--# NXT_RESUMEs should be 4.
--OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 5.
-+OVS_WAIT_UNTIL([test 5 = `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
-@@ -8564,8 +8593,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=1
- test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
- 
--# NXT_RESUMEs should be 5.
--OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 6.
-+OVS_WAIT_UNTIL([test 6 = `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
-@@ -8586,8 +8615,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=0
- test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
- 
--# NXT_RESUMEs should be 6.
--OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 7.
-+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
- AT_CHECK([cat 2.packets], [0], [])
-@@ -8604,8 +8633,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=0
- test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
- 
--# NXT_RESUMEs should be 7.
--OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 8.
-+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
- AT_CHECK([cat 2.packets], [0], [])
-@@ -8624,8 +8653,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
- dns_reply=1
- test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
- 
--# NXT_RESUMEs should be 8.
--OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# NXT_RESUMEs should be 9.
-+OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- 
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
- cat 1.expected | cut -c -48 > expout
-@@ -8646,8 +8675,8 @@ dst_ip=aef00000000000000000000000000001
- dns_reply=1
- test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
- 
--# NXT_RESUMEs should be 9.
--OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+# 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/vif1-tx.pcap > 1.packets
- # Skipping the UDP checksum.
--- 
-2.25.1
-
diff --git a/SOURCES/0001-Disable-IPv6-prefix-reporting-if-IPv6-PD-is-disabled.patch b/SOURCES/0001-Disable-IPv6-prefix-reporting-if-IPv6-PD-is-disabled.patch
deleted file mode 100644
index 5168b84..0000000
--- a/SOURCES/0001-Disable-IPv6-prefix-reporting-if-IPv6-PD-is-disabled.patch
+++ /dev/null
@@ -1,88 +0,0 @@
-From 0b9d16670d5561d8300d2448cbd4686a3acdc57e Mon Sep 17 00:00:00 2001
-Message-Id: <0b9d16670d5561d8300d2448cbd4686a3acdc57e.1588608928.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 22 Apr 2020 16:13:03 +0200
-Subject: [PATCH 1/3] Disable IPv6 prefix reporting if IPv6 PD is disabled
-
-Disable IPv6 prefix delegation reporting in Logical_Router_Port table if
-IPv6 prefix delegation state machine has been disabled for the related
-logical router port
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.c | 28 ++++++++++++++++------------
- tests/system-ovn.at |  7 +++++++
- 2 files changed, 23 insertions(+), 12 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index bc1ea0bd3..431c511c3 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -2703,6 +2703,10 @@ ovn_update_ipv6_prefix(struct hmap *ports)
-             continue;
-         }
- 
-+        if (!smap_get_bool(&op->nbrp->options, "prefix", false)) {
-+            continue;
-+        }
-+
-         char prefix[IPV6_SCAN_LEN + 6];
-         unsigned aid;
-         const char *ipv6_pd_list = smap_get(&op->sb->options,
-@@ -9364,22 +9368,22 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         }
- 
-         struct smap options;
-+        smap_clone(&options, &op->sb->options);
-+
-         /* enable IPv6 prefix delegation */
-         bool prefix_delegation = smap_get_bool(&op->nbrp->options,
-                                                "prefix_delegation", false);
--        if (prefix_delegation) {
--            smap_clone(&options, &op->sb->options);
--            smap_add(&options, "ipv6_prefix_delegation", "true");
--            sbrec_port_binding_set_options(op->sb, &options);
--            smap_destroy(&options);
--        }
-+        smap_add(&options, "ipv6_prefix_delegation",
-+                 prefix_delegation ? "true" : "false");
-+        sbrec_port_binding_set_options(op->sb, &options);
- 
--        if (smap_get_bool(&op->nbrp->options, "prefix", false)) {
--            smap_clone(&options, &op->sb->options);
--            smap_add(&options, "ipv6_prefix", "true");
--            sbrec_port_binding_set_options(op->sb, &options);
--            smap_destroy(&options);
--        }
-+        bool ipv6_prefix = smap_get_bool(&op->nbrp->options,
-+                                         "prefix", false);
-+        smap_add(&options, "ipv6_prefix",
-+                 ipv6_prefix ? "true" : "false");
-+        sbrec_port_binding_set_options(op->sb, &options);
-+
-+        smap_destroy(&options);
- 
-         const char *address_mode = smap_get(
-             &op->nbrp->ipv6_ra_configs, "address_mode");
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 3b11cf92b..fa3b83cb1 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3946,6 +3946,13 @@ OVS_WAIT_UNTIL([
-     test "${total_pkts}" = "1"
- ])
- 
-+ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
-+ovn-nbctl clear logical_router_port rp-sw0 ipv6_prefix
-+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16)" = "[2001:1db8:3333]"])
-+AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0], [dnl
-+[]
-+])
-+
- kill $(pidof tcpdump)
- kill $(pidof ovn-controller)
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0001-Fix-ACL-reject-action-for-UDP-packets.patch b/SOURCES/0001-Fix-ACL-reject-action-for-UDP-packets.patch
deleted file mode 100644
index c32d975..0000000
--- a/SOURCES/0001-Fix-ACL-reject-action-for-UDP-packets.patch
+++ /dev/null
@@ -1,525 +0,0 @@
-From cfb0f49b644f2a253cc1365c219d1bb78c2cacac Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 24 Apr 2020 12:19:09 +0530
-Subject: [PATCH] Fix ACL reject action for UDP packets.
-
-The icmp packet generated by ovn-controller for reject ACL action
-for non TCP packets is not getting delivered to the sender of
-the original packet. This is because the icmp packets are skipped
-from out_pre_lb/out_pre_acl logical switch egress pipeline and this
-results in these icmp packets getting dropped in the ACL stage because
-of invalid ct flags. This patch fixes this issue by removing those logical
-flows. The IP checksum generated by ovn-controller is invalid. This patch
-fixes this issue as well.
-
-Tested-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream commit f792b1a00b439a949e3b7aae4951f8513340c1a1)
-
-Change-Id: I9837991cf0981f57dc92d1309f0f453c800d7937
----
- controller/pinctrl.c | 102 ++++++++++++++++++++++++++++---------------
- northd/ovn-northd.c  |  22 +++++-----
- tests/ovn.at         |  46 +++++++++----------
- tests/system-ovn.at  |  95 ++++++++++++++++++++++++++++++++--------
- 4 files changed, 177 insertions(+), 88 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index f0d63b9a6..9d5b7c3c0 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -1465,7 +1465,7 @@ static void
- pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
-                     struct dp_packet *pkt_in,
-                     const struct match *md, struct ofpbuf *userdata,
--                    bool include_orig_ip_datagram)
-+                    bool set_icmp_code)
- {
-     /* This action only works for IP packets, and the switch should only send
-      * us IP packets this way, but check here just to be sure. */
-@@ -1512,46 +1512,51 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
-         packet_set_ipv4(&packet, ip_flow->nw_src, ip_flow->nw_dst,
-                         ip_flow->nw_tos, 255);
- 
-+        uint8_t icmp_code =  1;
-+        if (set_icmp_code && in_ip->ip_proto == IPPROTO_UDP) {
-+            icmp_code = 3;
-+        }
-+
-         struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih);
-         dp_packet_set_l4(&packet, ih);
--        packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1);
--
--        if (include_orig_ip_datagram) {
--            /* RFC 1122: 3.2.2	MUST send at least the IP header and 8 bytes
--             * of header. MAY send more.
--             * RFC says return as much as we can without exceeding 576
--             * bytes.
--             * So, lets return as much as we can. */
--
--            /* Calculate available room to include the original IP + data. */
--            nh = dp_packet_l3(&packet);
--            uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len));
--            if (in_ip_len > room) {
--                in_ip_len = room;
--            }
--            dp_packet_put(&packet, in_ip, in_ip_len);
--
--            /* dp_packet_put may reallocate the buffer. Get the l3 and l4
--             * header pointers again. */
--            nh = dp_packet_l3(&packet);
--            ih = dp_packet_l4(&packet);
--            uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len;
--            nh->ip_tot_len = htons(ip_total_len);
--            ih->icmp_csum = 0;
--            ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len);
--            nh->ip_csum = 0;
--            nh->ip_csum = csum(nh, sizeof *nh);
--        }
-+        packet_set_icmp(&packet, ICMP4_DST_UNREACH, icmp_code);
-+
-+        /* RFC 1122: 3.2.2	MUST send at least the IP header and 8 bytes
-+         * of header. MAY send more.
-+         * RFC says return as much as we can without exceeding 576
-+         * bytes.
-+         * So, lets return as much as we can. */
-+
-+        /* Calculate available room to include the original IP + data. */
-+        nh = dp_packet_l3(&packet);
-+        uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len));
-+        if (in_ip_len > room) {
-+            in_ip_len = room;
-+        }
-+        dp_packet_put(&packet, in_ip, in_ip_len);
-+
-+        /* dp_packet_put may reallocate the buffer. Get the l3 and l4
-+            * header pointers again. */
-+        nh = dp_packet_l3(&packet);
-+        ih = dp_packet_l4(&packet);
-+        uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len;
-+        nh->ip_tot_len = htons(ip_total_len);
-+        ih->icmp_csum = 0;
-+        ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len);
-+        nh->ip_csum = 0;
-+        nh->ip_csum = csum(nh, sizeof *nh);
-+
-     } else {
-         struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-         struct icmp6_data_header *ih;
-         uint32_t icmpv6_csum;
-+        struct ip6_hdr *in_ip = dp_packet_l3(pkt_in);
- 
-         eh->eth_type = htons(ETH_TYPE_IPV6);
-         dp_packet_set_l3(&packet, nh);
-         nh->ip6_vfc = 0x60;
-         nh->ip6_nxt = IPPROTO_ICMPV6;
--        nh->ip6_plen = htons(sizeof(*nh) + ICMP6_DATA_HEADER_LEN);
-+        nh->ip6_plen = htons(ICMP6_DATA_HEADER_LEN);
-         packet_set_ipv6(&packet, &ip_flow->ipv6_src, &ip_flow->ipv6_dst,
-                         ip_flow->nw_tos, ip_flow->ipv6_label, 255);
- 
-@@ -1559,15 +1564,42 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
-         dp_packet_set_l4(&packet, ih);
-         ih->icmp6_base.icmp6_type = ICMP6_DST_UNREACH;
-         ih->icmp6_base.icmp6_code = 1;
-+
-+        if (set_icmp_code && in_ip->ip6_nxt == IPPROTO_UDP) {
-+            ih->icmp6_base.icmp6_code = ICMP6_DST_UNREACH_NOPORT;
-+        }
-         ih->icmp6_base.icmp6_cksum = 0;
- 
--        uint8_t *data = dp_packet_put_zeros(&packet, sizeof *nh);
--        memcpy(data, dp_packet_l3(pkt_in), sizeof(*nh));
-+        nh = dp_packet_l3(&packet);
-+
-+        /* RFC 4443: 3.1.
-+         *
-+         * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-+         * |     Type      |     Code      |          Checksum             |
-+         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-+         * |                             Unused                            |
-+         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-+         * |                    As much of invoking packet                 |
-+         * +                as possible without the ICMPv6 packet          +
-+         * |                exceeding the minimum IPv6 MTU [IPv6]          |
-+         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-+         */
-+
-+        uint16_t room = 1280 - (sizeof *eh + sizeof *nh +
-+                                ICMP6_DATA_HEADER_LEN);
-+        uint16_t in_ip_len = (uint16_t) sizeof *in_ip + ntohs(in_ip->ip6_plen);
-+        if (in_ip_len > room) {
-+            in_ip_len = room;
-+        }
-+
-+        dp_packet_put(&packet, in_ip, in_ip_len);
-+        nh->ip6_plen = htons(ICMP6_DATA_HEADER_LEN + in_ip_len);
- 
-         icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
-         ih->icmp6_base.icmp6_cksum = csum_finish(
-             csum_continue(icmpv6_csum, ih,
--                          sizeof(*nh) + ICMP6_DATA_HEADER_LEN));
-+                          in_ip_len + ICMP6_DATA_HEADER_LEN));
-     }
- 
-     if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
-@@ -2658,12 +2690,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
- 
-     case ACTION_OPCODE_ICMP:
-         pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata,
--                            &userdata, false);
-+                            &userdata, true);
-         break;
- 
-     case ACTION_OPCODE_ICMP4_ERROR:
-         pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata,
--                            &userdata, true);
-+                            &userdata, false);
-         break;
- 
-     case ACTION_OPCODE_TCP_RESET:
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 4efe4a0c3..ec77ae1a8 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4736,12 +4736,10 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-          * Not to do conntrack on ND and ICMP destination
-          * unreachable packets. */
-         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
--                      "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || "
-+                      "nd || nd_rs || nd_ra || "
-                       "(udp && udp.src == 546 && udp.dst == 547)", "next;");
-         ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
--                      "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || "
-+                      "nd || nd_rs || nd_ra || "
-                       "(udp && udp.src == 546 && udp.dst == 547)", "next;");
- 
-         /* Ingress and Egress Pre-ACL Table (Priority 100).
-@@ -4853,12 +4851,10 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
- {
-     /* Do not send ND packets to conntrack */
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
--                  "nd || nd_rs || nd_ra || icmp4.type == 3 ||"
--                  "icmp6.type == 1",
-+                  "nd || nd_rs || nd_ra",
-                   "next;");
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
--                  "nd || nd_rs || nd_ra || icmp4.type == 3 ||"
--                  "icmp6.type == 1",
-+                  "nd || nd_rs || nd_ra",
-                   "next;");
- 
-     /* Do not send service monitor packets to conntrack. */
-@@ -5037,9 +5033,10 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
-         ds_put_format(&actions, "%s ", extra_actions->string);
-     }
-     ds_put_format(&actions, "reg0 = 0; "
--                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
--                  "icmp4 { outport <-> inport; %s };",
--                  ingress ? "output;" : "next(pipeline=ingress,table=0);");
-+                  "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
-+                  "outport <-> inport; %s };",
-+                  ingress ? "next(pipeline=egress,table=5);"
-+                          : "next(pipeline=ingress,table=19);");
-     ovn_lflow_add_with_hint(lflows, od, stage,
-                             acl->priority + OVN_ACL_PRI_OFFSET,
-                             ds_cstr(&match), ds_cstr(&actions), stage_hint);
-@@ -5056,7 +5053,8 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
-     ds_put_format(&actions, "reg0 = 0; icmp6 { "
-                   "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
-                   "outport <-> inport; %s };",
--                  ingress ? "output;" : "next(pipeline=ingress,table=0);");
-+                  ingress ? "next(pipeline=egress,table=5);"
-+                          : "next(pipeline=ingress,table=19);");
-     ovn_lflow_add_with_hint(lflows, od, stage,
-                             acl->priority + OVN_ACL_PRI_OFFSET,
-                             ds_cstr(&match), ds_cstr(&actions), stage_hint);
-diff --git a/tests/ovn.at b/tests/ovn.at
-index defe00a40..35415f2b6 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -11737,13 +11737,13 @@ test_ip_packet() {
- 
-     local ip_ttl=ff
-     local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
--
-+    local orig_pkt_in_reply=4500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
-     local reply_icmp_ttl=ff
-     local icmp_type_code_response=0301
-     local icmp_data=00000000
-     local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_data}
--    local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload}
--    echo $reply >> vif$inport.expected
-+    local reply=${eth_src}${eth_dst}08004500003000004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload}
-+    echo $reply$orig_pkt_in_reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
- }
-@@ -11760,7 +11760,7 @@ test_ipv6_packet() {
-     local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst}
-     local packet=${eth_dst}${eth_src}86dd${ip6_hdr}0000000000000000
- 
--    local reply=${eth_src}${eth_dst}86dd6000000000303aff${ipv6_dst}${ipv6_src}0101${exp_icmp_chksum}00000000${ip6_hdr}
-+    local reply=${eth_src}${eth_dst}86dd6000000000383aff${ipv6_dst}${ipv6_src}0101${exp_icmp_chksum}00000000${ip6_hdr}0000000000000000
-     echo $reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-@@ -11838,11 +11838,11 @@ ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p21\"" reject
- # Allow some time for ovn-northd and ovn-controller to catch up.
- ovn-nbctl --timeout=3 --wait=hv sync
- 
--test_ip_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 7d8d fcfe
--test_ip_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 7d8d fcfe
--test_ip_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 7d82 fcfe
-+test_ip_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 f85b f576
-+test_ip_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 f85b f576
-+test_ip_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 f850 f56b
- 
--test_ipv6_packet 11 1 000000000011 000000000021 fe80000000000000020001fffe000001 fe80000000000000020001fffe000002 6183
-+test_ipv6_packet 11 1 000000000011 000000000021 fe80000000000000020001fffe000001 fe80000000000000020001fffe000002 617b
- 
- test_tcp_syn_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 8b40 3039 0000 b85f 70e4
- test_tcp_syn_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 8b40 3039 0000 b85f 70e4
-@@ -12795,13 +12795,13 @@ test_ip_packet() {
- 
-     local ip_ttl=01
-     local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
--
-+    local orig_pkt_in_reply=4500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
-     local reply_icmp_ttl=fe
-     local icmp_type_code_response=0b00
-     local icmp_data=00000000
-     local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_data}
--    local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
--    echo $reply >> vif$inport.expected
-+    local reply=${eth_src}${eth_dst}08004500003000004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
-+    echo $reply$orig_pkt_in_reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
- }
-@@ -12819,7 +12819,7 @@ test_ip6_packet() {
-     local ip6_hdr=6000000000151101${ipv6_src}${ipv6_dst}
-     local packet=${eth_dst}${eth_src}86dd${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
- 
--    local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_router}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}
-+    local reply=${eth_src}${eth_dst}86dd6000000000453afe${ipv6_router}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
-     echo $reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-@@ -12861,8 +12861,8 @@ OVN_POPULATE_ARP
- # allow some time for ovn-northd and ovn-controller to catch up.
- ovn-nbctl --wait=hv sync
- 
--test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 1 254) 0000 7dae f4ff
--test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000200000000000000000011 20010db8000100000000000000000001 d461
-+test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 1 254) 0000 f87c ea96
-+test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000200000000000000000011 20010db8000100000000000000000001 1c22
- OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
- 
- OVN_CLEANUP([hv1], [hv2])
-@@ -12891,12 +12891,12 @@ test_ip_packet() {
- 
-     local ip_ttl=ff
-     local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}${l4_proto}${ip_chksum}${ipv4_src}${ip_router}
--
-+    local orig_pkt_in_reply=4500001400004000${ip_ttl}${l4_proto}${ip_chksum}${ipv4_src}${ip_router}
-     local reply_icmp_ttl=fe
-     local icmp_data=00000000
-     local reply_icmp_payload=${exp_icmp_code}${exp_icmp_chksum}${icmp_data}
--    local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
--    echo $reply >> vif$inport.expected
-+    local reply=${eth_src}${eth_dst}08004500003000004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
-+    echo $reply$orig_pkt_in_reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
- }
-@@ -12962,7 +12962,9 @@ test_ip6_packet() {
-     local ip6_hdr=60000000${ipv6_len}${ipv6_proto}ff${ipv6_src}${ipv6_dst}
-     local packet=${eth_dst}${eth_src}86dd${ip6_hdr}${data}
- 
--    local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_dst}${ipv6_src}${exp_icmp_code}${exp_icmp_chksum}00000000${ip6_hdr}
-+    local reply_ip_len=`expr 48 + ${#data} / 2`
-+    reply_ip_len=$(printf "%x" $reply_ip_len)
-+    local reply=${eth_src}${eth_dst}86dd6000000000${reply_ip_len}3afe${ipv6_dst}${ipv6_src}${exp_icmp_code}${exp_icmp_chksum}00000000${ip6_hdr}${data}
-     echo $reply >> vif$inport.expected
- 
-     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-@@ -13004,13 +13006,13 @@ OVN_POPULATE_ARP
- # allow some time for ovn-northd and ovn-controller to catch up.
- ovn-nbctl --wait=hv sync
- 
--test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 11 0000 7dae fcfc 0303
--test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 84 0000 7dae fcfd 0302
--test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000100000000000000000001 11 0015 dbb8303900155bac6b646f65206676676e6d66720a 0104 d570
-+test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 11 0000 f87c f485 0303
-+test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 84 0000 f87c f413 0302
-+test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000100000000000000000001 11 0015 dbb8303900155bac6b646f65206676676e6d66720a 0104 1d31
- OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
- 
- test_tcp_syn_packet 2 2 000000000002 00000000ff02 $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 2 254) 0000 8b40 3039 0000 b680 6e05
--test_ip6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 84 0004 01020304 0103 627e
-+test_ip6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 84 0004 01020304 0103 5e74
- test_tcp6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 8b40 3039 0000 98cd
- OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [vif2.expected])
- 
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index fa3b83cb1..117f1e835 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3697,7 +3697,7 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
- AT_CLEANUP
- 
- 
--AT_SETUP([ovn -- ACL reject - TCP reset])
-+AT_SETUP([ovn -- ACL reject])
- AT_SKIP_IF([test $HAVE_NC = no])
- AT_KEYWORDS([lb])
- 
-@@ -3736,13 +3736,14 @@ ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
- ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
- 
- ovn-nbctl pg-add pg0 sw0-p1-rej sw0-p2-rej
--ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
-+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip" allow-related
- ovn-nbctl --log acl-add pg0 from-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 80" reject
-+ovn-nbctl --log acl-add pg0 from-lport 1004 "inport == @pg0 && ip && udp && udp.dst == 90" reject
- 
--ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
- ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 82" allow-related
- ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 82" allow-related
- ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 84" reject
-+ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && udp && udp.dst == 94" reject
- 
- OVN_POPULATE_ARP
- ovn-nbctl --wait=hv sync
-@@ -3758,33 +3759,38 @@ ADD_VETH(sw0-p2-rej, sw0-p2-rej, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
- NS_CHECK_EXEC([sw0-p1-rej], [ip a a aef0::3/64 dev sw0-p1-rej], [0])
- NS_CHECK_EXEC([sw0-p2-rej], [ip a a aef0::4/64 dev sw0-p2-rej], [0])
- 
--# Capture packets in sw0-p1-rej.
--NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 2 -i sw0-p1-rej tcp port 80 > sw0-p1-rej-ip4.pcap &], [0])
- sleep 1
- 
--NS_CHECK_EXEC([sw0-p1-rej], [nc 10.0.0.4 80], [1], [],
--[dnl
--Ncat: Connection refused.
--])
-+# Capture packets in sw0-p1-rej.
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 4 -i sw0-p1-rej tcp > sw0-p1-rej-ip4.pcap &], [0])
-+
-+sleep 1
- 
- OVS_WAIT_UNTIL([
--    total=`cat sw0-p1-rej-ip4.pcap |  wc -l`
--    echo "total = $total"
--    test "${total}" = "2"
-+    ip netns exec sw0-p1-rej nc  10.0.0.4 80 2> r
-+    res=$(cat r)
-+    test "$res" = "Ncat: Connection refused."
- ])
- 
- # Now send traffic to port 84
--NS_CHECK_EXEC([sw0-p1-rej], [nc 10.0.0.4 84], [1], [],
--[dnl
--Ncat: Connection refused.
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p1-rej nc  10.0.0.4 84 2> r
-+    res=$(cat r)
-+    test "$res" = "Ncat: Connection refused."
- ])
- 
--AT_CHECK([
-+OVS_WAIT_UNTIL([
-     n_pkt=$(ovs-ofctl dump-flows br-int table=44 | grep -v n_packets=0 | \
- grep controller | grep tp_dst=84 -c)
-     test $n_pkt -eq 1
- ])
- 
-+OVS_WAIT_UNTIL([
-+    total=`cat sw0-p1-rej-ip4.pcap |  wc -l`
-+    echo "total = $total"
-+    test "${total}" = "4"
-+])
-+
- # Without this sleep, test case fails intermittently.
- sleep 3
- 
-@@ -3792,17 +3798,68 @@ NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 2 -i sw0-p2-rej tcp port 80 > sw0-p2-
- 
- sleep 1
- 
--NS_CHECK_EXEC([sw0-p2-rej], [nc -6 aef0::3 80], [1], [],
--[dnl
--Ncat: Connection refused.
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p2-rej nc -6 aef0::3 80 2> r
-+    res=$(cat r)
-+    test "$res" = "Ncat: Connection refused."
- ])
- 
-+
- OVS_WAIT_UNTIL([
-     total=`cat sw0-p2-rej-ip6.pcap |  wc -l`
-     echo "total = $total"
-     test "${total}" = "2"
- ])
- 
-+# Now test for IPv4 UDP.
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej udp port 90 > sw0-p1-rej-udp.pcap &], [0])
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0])
-+
-+echo "foo" > foo
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p1-rej nc -u 10.0.0.4 90 < foo
-+    c=$(cat sw0-p1-rej-icmp.pcap | grep \
-+"10.0.0.4 > 10.0.0.3: ICMP 10.0.0.4 udp port dnsix unreachable" | uniq | wc -l)
-+    test $c -eq 1
-+])
-+
-+rm -f *.pcap
-+
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej udp port 94 > sw0-p1-rej-udp.pcap &], [0])
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0])
-+
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p1-rej nc -u 10.0.0.4 94 < foo
-+    c=$(cat sw0-p1-rej-icmp.pcap | grep \
-+"10.0.0.4 > 10.0.0.3: ICMP 10.0.0.4 udp port objcall unreachable" | uniq | wc -l)
-+    test $c -eq 1
-+])
-+
-+# Now test for IPv6 UDP.
-+NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 1 -i sw0-p2-rej udp port 90 > sw0-p2-rej-ip6-udp.pcap &], [0])
-+NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 1 -i sw0-p2-rej icmp6 > sw0-p2-rej-icmp6.pcap &], [0])
-+
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p2-rej nc -u -6 aef0::3 90 < foo
-+    c=$(cat sw0-p2-rej-icmp6.pcap | grep \
-+"IP6 aef0::3 > aef0::4: ICMP6, destination unreachable, unreachable port, \
-+aef0::3 udp port dnsix" | uniq | wc -l)
-+    test $c -eq 1
-+])
-+
-+rm -f *.pcap
-+
-+NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 1 -i sw0-p2-rej udp port 94 > sw0-p2-rej-ip6-udp.pcap &], [0])
-+NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 1 -i sw0-p2-rej icmp6 > sw0-p2-rej-icmp6.pcap &], [0])
-+
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw0-p2-rej nc -u -6 aef0::3 94 < foo
-+    c=$(cat sw0-p2-rej-icmp6.pcap | grep \
-+"IP6 aef0::3 > aef0::4: ICMP6, destination unreachable, unreachable port, \
-+aef0::3 udp port objcall" | uniq | wc -l)
-+    test $c -eq 1
-+])
-+
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
- as ovn-sb
--- 
-2.26.2
-
diff --git a/SOURCES/0001-Fix-conntrack-entry-leaks-because-of-TCP-RST-packets.patch b/SOURCES/0001-Fix-conntrack-entry-leaks-because-of-TCP-RST-packets.patch
deleted file mode 100644
index 2ec0cb6..0000000
--- a/SOURCES/0001-Fix-conntrack-entry-leaks-because-of-TCP-RST-packets.patch
+++ /dev/null
@@ -1,473 +0,0 @@
-From 685c685b0070731524869459f96b7b690e12ae74 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 23 Apr 2020 18:08:48 +0530
-Subject: [PATCH] Fix conntrack entry leaks because of TCP RST packets not sent
- to conntrack.
-
-The commit [1] - 28097d5adb95("Fix tcp_reset action handling") fixed an issue
-with tcp_reset OVN action. In order to fix that issue, this commit added
-logical flows to skip all the TCP RST packets from conntrack.
-Ideally it should have skipped only the TCP RST packets generated by
-ovn-controller from conntrack. Since all the TCP RST packets are
-skipped from conntrack, the connections in conntrack remain in
-ESTABLISHED state even if the client/server sends TCP RST to close the
-connection.  And these entries live for a long time and this is
-causing performance issues as reported in the BZ.
-
-This patch reverts the logical flows added in [1] and modifies the inner
-actions of tcp_reset in the ingress logical switch pipeline
-from - "tcp_reset { outport <-> inport; output; }"
-to "tcp_reset { output <-> inport; next(pipeline=egress,table=5); }".
-This causes the packet to resubmit to the egress table ls_out_qos_mark
-skipping the egress ACL stage. Prior to this packet, next action was
-not allowing a resubmit from ingress to egress pipeline. This patch
-relaxes this limitation.
-
-For the tcp_reset action in the egress logical switch pipeline, this patch
-modifies the inner action
-from - "tcp_reset { outport <-> inport; next(pipeline=ingress,table=0); }"
-to - "tcp_reset { outport <-> inport; next(pipeline=ingress,table=19); }".
-This causes the packet to enter the ingress table ls_in_l2_lkup.
-
-We don't see similar conntrack leaks with UDP. Although there is an issue
-with the acl reject action for UDP packets. When ovn-controller generates icmp
-destination unreachable packet, it doesn't get delivered. And the IP checksum is
-incorrect in this packet. A follow up patch will fix these issues.
-
-[1] - 28097d5adb95("Fix tcp_reset action handling")
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819785
-Co-Authored-by: Tim Rozet <trozet@redhat.com>
-Signed-off-by: Tim Rozet <trozet@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- AUTHORS.rst             |   1 +
- lib/actions.c           |   6 +-
- northd/ovn-northd.8.xml |   8 ++
- northd/ovn-northd.c     |  14 ++--
- ovn-sb.xml              |  10 ++-
- tests/automake.mk       |   3 +-
- tests/ovn.at            |   6 +-
- tests/system-ovn.at     | 170 +++++++++++++++++++++++++++++++++++-----
- tests/test-tcp-rst.py   |  37 +++++++++
- 9 files changed, 217 insertions(+), 38 deletions(-)
- create mode 100644 tests/test-tcp-rst.py
-
-diff --git a/AUTHORS.rst b/AUTHORS.rst
-index 230e487f0..c80fc1bae 100644
---- a/AUTHORS.rst
-+++ b/AUTHORS.rst
-@@ -355,6 +355,7 @@ Thomas F. Herbert                  thomasfherbert@gmail.com
- Thomas Goirand                     zigo@debian.org
- Thomas Graf                        tgraf@noironetworks.com
- Thomas Lacroix                     thomas.lacroix@citrix.com
-+Tim Rozet                          trozet@redhat.com
- Timo Puha                          timox.puha@intel.com
- Timothy Redaelli                   tredaelli@redhat.com
- Todd Deshane                       deshantm@gmail.com
-diff --git a/lib/actions.c b/lib/actions.c
-index 02141af30..41a742064 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -319,11 +319,7 @@ parse_NEXT(struct action_context *ctx)
-         }
-     }
- 
--    if (pipeline == OVNACT_P_EGRESS && ctx->pp->pipeline == OVNACT_P_INGRESS) {
--        lexer_error(ctx->lexer,
--                    "\"next\" action cannot advance from ingress to egress "
--                    "pipeline (use \"output\" action instead)");
--    } else if (table >= ctx->pp->n_tables) {
-+    if (table >= ctx->pp->n_tables) {
-         lexer_error(ctx->lexer,
-                     "\"next\" action cannot advance beyond table %d.",
-                     ctx->pp->n_tables - 1);
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index efcc4b7fc..d39e259f6 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -373,6 +373,14 @@
-         for new connections and <code>reg0[1] = 1; next;</code> for existing
-         connections.
-       </li>
-+      <li>
-+        <code>reject</code> ACLs translate into logical
-+        flows with the
-+        <code>tcp_reset { output &lt;-&gt; inport;
-+        next(pipeline=egress,table=5);}</code>
-+        action for TCP connections and <code>icmp4/icmp6</code> action
-+        for UDP connections.
-+      </li>
-       <li>
-         Other ACLs translate to <code>drop;</code> for new or untracked
-         connections and <code>ct_commit(ct_label=1/1);</code> for known
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index d3d481ab8..0082e2e8b 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4717,11 +4717,11 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-          * unreachable packets. */
-         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
-                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || (tcp && tcp.flags == 20) || "
-+                      "icmp6.type == 1 || "
-                       "(udp && udp.src == 546 && udp.dst == 547)", "next;");
-         ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
-                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || (tcp && tcp.flags == 20) ||"
-+                      "icmp6.type == 1 || "
-                       "(udp && udp.src == 546 && udp.dst == 547)", "next;");
- 
-         /* Ingress and Egress Pre-ACL Table (Priority 100).
-@@ -4834,11 +4834,11 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
-     /* Do not send ND packets to conntrack */
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
-                   "nd || nd_rs || nd_ra || icmp4.type == 3 ||"
--                  "icmp6.type == 1 || (tcp && tcp.flags == 20)",
-+                  "icmp6.type == 1",
-                   "next;");
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
-                   "nd || nd_rs || nd_ra || icmp4.type == 3 ||"
--                  "icmp6.type == 1 || (tcp && tcp.flags == 20)",
-+                  "icmp6.type == 1",
-                   "next;");
- 
-     /* Do not send service monitor packets to conntrack. */
-@@ -4984,7 +4984,8 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
-     ds_put_format(&actions, "reg0 = 0; "
-                   "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
-                   "tcp_reset { outport <-> inport; %s };",
--                  ingress ? "output;" : "next(pipeline=ingress,table=0);");
-+                  ingress ? "next(pipeline=egress,table=5);"
-+                          : "next(pipeline=ingress,table=19);");
-     ovn_lflow_add_with_hint(lflows, od, stage,
-                             acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                             ds_cstr(&match), ds_cstr(&actions), stage_hint);
-@@ -4998,7 +4999,8 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
-     ds_put_format(&actions, "reg0 = 0; "
-                   "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
-                   "tcp_reset { outport <-> inport; %s };",
--                  ingress ? "output;" : "next(pipeline=ingress,table=0);");
-+                  ingress ? "next(pipeline=egress,table=5);"
-+                          : "next(pipeline=ingress,table=19);");
-     ovn_lflow_add_with_hint(lflows, od, stage,
-                             acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                             ds_cstr(&match), ds_cstr(&actions), stage_hint);
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 5f8da534c..3aa7cd4da 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -1112,10 +1112,12 @@
-           <var>pipeline</var> as a subroutine.  The default <var>table</var> is
-           just after the current one.  If <var>pipeline</var> is specified, it
-           may be <code>ingress</code> or <code>egress</code>; the default
--          <var>pipeline</var> is the one currently executing.  Actions in the
--          ingress pipeline may not use <code>next</code> to jump into the
--          egress pipeline (use the <code>output</code> instead), but
--          transitions in the opposite direction are allowed.
-+          <var>pipeline</var> is the one currently executing. Actions in the
-+          both ingress and egress pipeline can use <code>next</code> to jump
-+          across the other pipeline.  Actions in the ingress pipeline should
-+          use <code>next</code> to jump into the specific table of egress
-+          pipeline only if it is certain that the packets are local and not
-+          tunnelled and wants to skip certain stages in the packet processing.
-         </dd>
- 
-         <dt><code><var>field</var> = <var>constant</var>;</code></dt>
-diff --git a/tests/automake.mk b/tests/automake.mk
-index 215fb432b..ed530dd77 100644
---- a/tests/automake.mk
-+++ b/tests/automake.mk
-@@ -205,7 +205,8 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la
- # Python tests.
- CHECK_PYFILES = \
- 	tests/test-l7.py \
--	tests/uuidfilt.py
-+	tests/uuidfilt.py \
-+	tests/test-tcp-rst.py
- 
- EXTRA_DIST += $(CHECK_PYFILES)
- PYCOV_CLEAN_FILES += $(CHECK_PYFILES:.py=.py,cover) .coverage
-diff --git a/tests/ovn.at b/tests/ovn.at
-index b78637044..6da975554 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -850,7 +850,11 @@ next(pipeline=ingress, table=11);
-     encodes as resubmit(,19)
- 
- next(pipeline=egress);
--    "next" action cannot advance from ingress to egress pipeline (use "output" action instead)
-+    formats as next(pipeline=egress, table=11);
-+    encodes as resubmit(,51)
-+
-+next(pipeline=egress, table=5);
-+    encodes as resubmit(,45)
- 
- next(table=10);
-     formats as next(10);
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index bdb9768d2..3b11cf92b 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3719,60 +3719,86 @@ start_daemon ovn-controller
- 
- ovn-nbctl ls-add sw0
- 
--ovn-nbctl lsp-add sw0 sw0-p1
--ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3 aef0::3"
--ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3 aef0::3"
-+ovn-nbctl lsp-add sw0 sw0-p1-rej
-+ovn-nbctl lsp-set-addresses sw0-p1-rej "50:54:00:00:00:03 10.0.0.3 aef0::3"
-+ovn-nbctl lsp-set-port-security sw0-p1-rej "50:54:00:00:00:03 10.0.0.3 aef0::3"
- 
--ovn-nbctl lsp-add sw0 sw0-p2
--ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4 aef0::4"
--ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4 aef0::4"
-+ovn-nbctl lsp-add sw0 sw0-p2-rej
-+ovn-nbctl lsp-set-addresses sw0-p2-rej "50:54:00:00:00:04 10.0.0.4 aef0::4"
-+ovn-nbctl lsp-set-port-security sw0-p2-rej "50:54:00:00:00:04 10.0.0.4 aef0::4"
-+
-+#ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p1\" && tcp && tcp.dst == 80" reject
-+#ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p2\" && ip6 && tcp && tcp.dst == 80" reject
-+
-+# Create port group and ACLs for sw0 ports.
-+ovn-nbctl pg-add pg0_drop sw0-p1-rej sw0-p2-rej
-+ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
-+ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
-+
-+ovn-nbctl pg-add pg0 sw0-p1-rej sw0-p2-rej
-+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
-+ovn-nbctl --log acl-add pg0 from-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 80" reject
- 
--ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p1\" && tcp && tcp.dst == 80" reject
--ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p2\" && ip6 && tcp && tcp.dst == 80" reject
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 82" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 82" allow-related
-+ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 84" reject
- 
- OVN_POPULATE_ARP
- ovn-nbctl --wait=hv sync
- 
--ADD_NAMESPACES(sw0-p1)
--ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
-+ADD_NAMESPACES(sw0-p1-rej)
-+ADD_VETH(sw0-p1-rej, sw0-p1-rej, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
-          "10.0.0.1")
- 
--ADD_NAMESPACES(sw0-p2)
--ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
-+ADD_NAMESPACES(sw0-p2-rej)
-+ADD_VETH(sw0-p2-rej, sw0-p2-rej, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
-          "10.0.0.1")
- 
--NS_CHECK_EXEC([sw0-p1], [ip a a aef0::3/64 dev sw0-p1], [0])
--NS_CHECK_EXEC([sw0-p2], [ip a a aef0::4/64 dev sw0-p2], [0])
-+NS_CHECK_EXEC([sw0-p1-rej], [ip a a aef0::3/64 dev sw0-p1-rej], [0])
-+NS_CHECK_EXEC([sw0-p2-rej], [ip a a aef0::4/64 dev sw0-p2-rej], [0])
- 
--# Capture packets in sw0-p1.
--NS_CHECK_EXEC([sw0-p1], [tcpdump -n -c 2 -i sw0-p1 tcp port 80 > sw0-p1-ip4.pcap &], [0])
-+# Capture packets in sw0-p1-rej.
-+NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 2 -i sw0-p1-rej tcp port 80 > sw0-p1-rej-ip4.pcap &], [0])
- sleep 1
- 
--NS_CHECK_EXEC([sw0-p1], [nc 10.0.0.4 80], [1], [],
-+NS_CHECK_EXEC([sw0-p1-rej], [nc 10.0.0.4 80], [1], [],
- [dnl
- Ncat: Connection refused.
- ])
- 
- OVS_WAIT_UNTIL([
--    total=`cat sw0-p1-ip4.pcap |  wc -l`
-+    total=`cat sw0-p1-rej-ip4.pcap |  wc -l`
-     echo "total = $total"
-     test "${total}" = "2"
- ])
- 
-+# Now send traffic to port 84
-+NS_CHECK_EXEC([sw0-p1-rej], [nc 10.0.0.4 84], [1], [],
-+[dnl
-+Ncat: Connection refused.
-+])
-+
-+AT_CHECK([
-+    n_pkt=$(ovs-ofctl dump-flows br-int table=44 | grep -v n_packets=0 | \
-+grep controller | grep tp_dst=84 -c)
-+    test $n_pkt -eq 1
-+])
-+
- # Without this sleep, test case fails intermittently.
- sleep 3
- 
--NS_CHECK_EXEC([sw0-p2], [tcpdump -n -c 2 -i sw0-p2 tcp port 80 > sw0-p2-ip6.pcap &], [0])
-+NS_CHECK_EXEC([sw0-p2-rej], [tcpdump -n -c 2 -i sw0-p2-rej tcp port 80 > sw0-p2-rej-ip6.pcap &], [0])
- 
- sleep 1
- 
--NS_CHECK_EXEC([sw0-p2], [nc -6 aef0::3 80], [1], [],
-+NS_CHECK_EXEC([sw0-p2-rej], [nc -6 aef0::3 80], [1], [],
- [dnl
- Ncat: Connection refused.
- ])
- 
- OVS_WAIT_UNTIL([
--    total=`cat sw0-p2-ip6.pcap |  wc -l`
-+    total=`cat sw0-p2-rej-ip6.pcap |  wc -l`
-     echo "total = $total"
-     test "${total}" = "2"
- ])
-@@ -3936,3 +3962,105 @@ as
- OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
- /.*terminating with signal 15.*/d"])
- AT_CLEANUP
-+
-+# Tests that when an established connection sends TCP reset,
-+# the conntrack entry is not in established state.
-+AT_SETUP([ovn -- conntrack TCP reset])
-+AT_KEYWORDS([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
-+
-+ovn-nbctl ls-add sw0
-+
-+ovn-nbctl lsp-add sw0 rst-p1
-+ovn-nbctl lsp-set-addresses rst-p1 "50:54:00:00:00:03"
-+ovn-nbctl lsp-set-port-security rst-p1 "50:54:00:00:00:03"
-+
-+ovn-nbctl lsp-add sw0 rst-p2
-+ovn-nbctl lsp-set-addresses rst-p2 "50:54:00:00:00:04 10.0.0.4"
-+ovn-nbctl lsp-set-port-security rst-p2 "50:54:00:00:00:04 10.0.0.4"
-+
-+# Create port group and ACLs for sw0 ports.
-+ovn-nbctl pg-add pg0_drop rst-p1 rst-p2
-+ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
-+ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
-+
-+ovn-nbctl pg-add pg0 rst-p1 rst-p2
-+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
-+
-+# Create a logical router and attach to logical switch.
-+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 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 lb-add lb1 10.0.0.10:80 10.0.0.3:80
-+ovn-nbctl --wait=sb ls-lb-add sw0 lb1
-+ovn-nbctl --wait=sb lr-lb-add lr0 lb1
-+
-+OVN_POPULATE_ARP
-+ovn-nbctl --wait=hv sync
-+
-+ADD_NAMESPACES(rst-p1)
-+ADD_VETH(rst-p1, rst-p1, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
-+         "10.0.0.1")
-+
-+ADD_NAMESPACES(rst-p2)
-+ADD_VETH(rst-p2, rst-p2, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
-+         "10.0.0.1")
-+
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up rst-p1) = xup])
-+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up rst-p2) = xup])
-+
-+# Start webservers in 'rst-p1'.
-+OVS_START_L7([rst-p1], [http])
-+
-+NS_CHECK_EXEC([rst-p2], [$PYTHON $srcdir/test-tcp-rst.py --dst-port 80 --dst-ip 10.0.0.10])
-+
-+# When tcp reset is sent, conntrack entry should be in the state - CLOSED or CLOSING.
-+# But there is a bug where tcp reset packet was not sent to the conntrack.
-+# This test case checks that the tcp reset packet is sent to conntrack
-+# and the state is not in established state.
-+AT_CHECK([
-+    ct_est_count=$(ovs-appctl dpctl/dump-conntrack | grep 10.0.0.10 | grep state=ESTABLISHED -c)
-+    test $ct_est_count -eq 0
-+
-+    ct_est_count=$(ovs-appctl dpctl/dump-conntrack | grep 10.0.0.10 | grep state=CLOS -c)
-+    test $ct_est_count -eq 1
-+])
-+
-+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
-+/Service monitor not found.*/d"])
-+
-+AT_CLEANUP
-diff --git a/tests/test-tcp-rst.py b/tests/test-tcp-rst.py
-new file mode 100644
-index 000000000..6f96c5706
---- /dev/null
-+++ b/tests/test-tcp-rst.py
-@@ -0,0 +1,37 @@
-+#!/usr/bin/env python3
-+# Copyright (c) 2020 Red Hat, Inc.
-+#
-+# Licensed under the Apache License, Version 2.0 (the "License");
-+# you may not use this file except in compliance with the License.
-+# You may obtain a copy of the License at:
-+#
-+#     http://www.apache.org/licenses/LICENSE-2.0
-+#
-+# Unless required by applicable law or agreed to in writing, software
-+# distributed under the License is distributed on an "AS IS" BASIS,
-+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+# See the License for the specific language governing permissions and
-+# limitations under the License.
-+
-+# Simple python script which connects to tcp server and then
-+# resets the connection.
-+import argparse
-+import socket
-+import sys
-+import struct
-+import time
-+
-+parser = argparse.ArgumentParser(description='')
-+parser.add_argument("--src-port", type=int, default=11337, help="source port to use")
-+parser.add_argument("--dst-port", type=int, help="dst port to use")
-+parser.add_argument("--dst-ip", help="server ip to use")
-+args = parser.parse_args()
-+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-+server_address = (args.dst_ip, args.dst_port)
-+sock.bind(('0.0.0.0', args.src_port))
-+sock.connect(server_address)
-+l_onoff = 1
-+l_linger = 0
-+time.sleep(1)
-+sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', l_onoff, l_linger))
-+sock.close()
--- 
-2.25.1
-
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
new file mode 100644
index 0000000..d73b70f
--- /dev/null
+++ b/SOURCES/0001-Fix-ovn-controller-crash-when-a-lport-of-type-virtua.patch
@@ -0,0 +1,86 @@
+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-ovn-controller-generated-packets-from-getting-dr.patch b/SOURCES/0001-Fix-ovn-controller-generated-packets-from-getting-dr.patch
deleted file mode 100644
index 1aadd22..0000000
--- a/SOURCES/0001-Fix-ovn-controller-generated-packets-from-getting-dr.patch
+++ /dev/null
@@ -1,152 +0,0 @@
-From 14f9bf6ba4bfa459f2e924dbf273a6337aab4107 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Sun, 14 Jun 2020 18:40:07 +0530
-Subject: [PATCH 1/3] Fix ovn-controller generated packets from getting dropped
- for reject ACL action.
-
-TCP reset/ICMP packet generated by ovn-controller for the ACL reject action
-gets dropped by ovs-vswithd with the below messages in ovs-vswitchd log
-even though ovn-controller sets the in_port as OFPP_CONTROLLER.
-
-----
-ofproto_dpif_upcall(handler1)|INFO|received packet on unassociated datapath port 4294967295
-ofproto_dpif_upcall(revalidator37)|WARN|Failed to acquire udpif_key corresponding to
-unexpected flow (Invalid argument): ufid:0daac824-bda7-44d8-ad38-cdd9c5f0fc97
-----
-
-ovs-vswitchd drops the packet because the in_port is 0.
-
-The below OF flow sets the in_port to 0 if 'MLF_ALLOW_LOOPBACK_BIT' is set in the REG0
-in table 64.
-
-priority=100,reg10=0x1/0x1,reg15=0x2,metadata=0x2 actions=push:NXM_OF_IN_PORT[],load:0->NXM_OF_IN_PORT[],resubmit(,65),pop:NXM_OF_IN_PORT[]
-
-This patch fixes this issue by setting the in_port to OFPP_NONE so that ovs-vswitchd
-doesn't drop the packet.
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1832176
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream master commit cfa5478211318b686ad0981e7b0620f96edd7168)
-
-Change-Id: Ia8e134013c30a6865322083c8054fa45b57c9353
----
- controller/physical.c | 18 ++++++++++++------
- tests/system-ovn.at   | 32 ++++++++++++++++++++++++++++++++
- 2 files changed, 44 insertions(+), 6 deletions(-)
-
-diff --git a/controller/physical.c b/controller/physical.c
-index 144aeb7bd..3c5bbe027 100644
---- a/controller/physical.c
-+++ b/controller/physical.c
-@@ -765,12 +765,18 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
-      *   - or if the destination is a nested container
-      *   - or if "nested_container" flag is set and the destination is the
-      *     parent port,
--     * temporarily set the in_port to zero, resubmit to
-+     * temporarily set the in_port to OFPP_NONE, resubmit to
-      * table 65 for logical-to-physical translation, then restore
-      * the port number.
-      *
-      * If 'parent_port_key' is set, then the 'port_key' represents a nested
--     * container. */
-+     * container.
-+     *
-+     * Note:We can set in_port to 0 too. But if recirculation happens
-+     * later (eg. clone action to enter peer pipeline and a subsequent
-+     * ct action), ovs-vswitchd will drop the packet if the frozen metadata
-+     * in_port is 0.
-+     * */
- 
-     bool nested_container = parent_port_key ? true: false;
-     match_init_catchall(&match);
-@@ -783,7 +789,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
-     }
- 
-     put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
--    put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
-+    put_load(ofp_to_u16(OFPP_NONE), MFF_IN_PORT, 0, 16, ofpacts_p);
-     put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
-     put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
-     ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
-@@ -792,8 +798,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
-     if (nested_container) {
-         /* It's a nested container and when the packet from the nested
-          * container is to be sent to the parent port, "nested_container"
--         * flag will be set. We need to temporarily set the in_port to zero
--         * as mentioned in the comment above.
-+         * flag will be set. We need to temporarily set the in_port to
-+         * OFPP_NONE as mentioned in the comment above.
-          *
-          * If a parent port has multiple child ports, then this if condition
-          * will be hit multiple times, but we want to add only one flow.
-@@ -814,7 +820,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
-                              MLF_NESTED_CONTAINER, MLF_NESTED_CONTAINER);
- 
-         put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
--        put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
-+        put_load(ofp_to_u16(OFPP_NONE), MFF_IN_PORT, 0, 16, ofpacts_p);
-         put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
-         put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
-         ofctrl_check_and_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 9dfe6a4ad..52f05f07e 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3927,6 +3927,24 @@ ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.
- ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && tcp && tcp.dst == 84" reject
- ovn-nbctl --log acl-add pg0 to-lport 1004 "inport == @pg0 && ip && udp && udp.dst == 94" reject
- 
-+ovn-nbctl ls-add sw1
-+ovn-nbctl lsp-add sw1 sw1-p1-rej
-+ovn-nbctl lsp-set-addresses sw1-p1-rej "40:54:00:00:00:03 20.0.0.3"
-+ovn-nbctl lsp-set-port-security sw1-p1-rej "40:54:00:00:00:03 20.0.0.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 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
-+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_POPULATE_ARP
- ovn-nbctl --wait=hv sync
- 
-@@ -3941,6 +3959,10 @@ ADD_VETH(sw0-p2-rej, sw0-p2-rej, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
- NS_CHECK_EXEC([sw0-p1-rej], [ip a a aef0::3/64 dev sw0-p1-rej], [0])
- NS_CHECK_EXEC([sw0-p2-rej], [ip a a aef0::4/64 dev sw0-p2-rej], [0])
- 
-+ADD_NAMESPACES(sw1-p1-rej)
-+ADD_VETH(sw1-p1-rej, sw1-p1-rej, br-int, "20.0.0.3/24", "40:54:00:00:00:03", \
-+         "20.0.0.1")
-+
- sleep 1
- 
- # Capture packets in sw0-p1-rej.
-@@ -3993,6 +4015,16 @@ OVS_WAIT_UNTIL([
-     test "${total}" = "2"
- ])
- 
-+ovn-nbctl acl-add sw1 from-lport 1004 "ip" allow-related
-+ovn-nbctl acl-add sw1 to-lport 1004 "ip" allow-related
-+ovn-nbctl --log acl-add pg0 to-lport 1004 "outport == @pg0 && ip && tcp && tcp.dst == 84" reject
-+
-+OVS_WAIT_UNTIL([
-+    ip netns exec sw1-p1-rej nc  10.0.0.4 84 2> r
-+    res=$(cat r)
-+    test "$res" = "Ncat: Connection refused."
-+])
-+
- # Now test for IPv4 UDP.
- NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej udp port 90 > sw0-p1-rej-udp.pcap &], [0])
- NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0])
--- 
-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
new file mode 100644
index 0000000..36246f3
--- /dev/null
+++ b/SOURCES/0001-Fix-the-data-type-for-DHCP-option-tftp_server-66.patch
@@ -0,0 +1,279 @@
+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
new file mode 100644
index 0000000..a4614fa
--- /dev/null
+++ b/SOURCES/0001-I-P-engine-Provide-the-option-to-store-client-data-i.patch
@@ -0,0 +1,33 @@
+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-IPv6-PD-assume-status-to-be-Success-if-not-present.patch b/SOURCES/0001-IPv6-PD-assume-status-to-be-Success-if-not-present.patch
deleted file mode 100644
index 2ef8f34..0000000
--- a/SOURCES/0001-IPv6-PD-assume-status-to-be-Success-if-not-present.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From f3f604a41e44a17b1953ebd3d2162c1fc046f49f Mon Sep 17 00:00:00 2001
-Message-Id: <f3f604a41e44a17b1953ebd3d2162c1fc046f49f.1588582791.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Sat, 25 Apr 2020 12:18:12 +0200
-Subject: [PATCH] IPv6 PD: assume status to be Success if not present
-
-According to the RFC3315 (section 22.13. Status Code Option),
-if status code option is not present in the delegation server
-reply, it will be assumed to be Success. In this particular case,
-do not stop IPv6 PD state machine
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -835,12 +835,15 @@ pinctrl_parse_dhcpv6_reply(struct dp_pac
-                     plife_time = ntohl(ia_hdr->plife_time);
-                     vlife_time = ntohl(ia_hdr->vlife_time);
-                     memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr));
-+                    status = true;
-                 }
-                 if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) {
-                    struct dhcpv6_opt_status *status_hdr;
- 
-                    status_hdr = (struct dhcpv6_opt_status *)in_opt;
--                   status = ntohs(status_hdr->status_code) == 0;
-+                   if (ntohs(status_hdr->status_code)) {
-+                       status = false;
-+                   }
-                 }
-                 size += sizeof *in_opt + ntohs(in_opt->len);
-                 in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
diff --git a/SOURCES/0001-IPv6-PD-time-parameter-checks.patch b/SOURCES/0001-IPv6-PD-time-parameter-checks.patch
deleted file mode 100644
index 1221fb8..0000000
--- a/SOURCES/0001-IPv6-PD-time-parameter-checks.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 942f7b2b9e3acfc7b1d6ea5c48fc22171b14549a Mon Sep 17 00:00:00 2001
-Message-Id: <942f7b2b9e3acfc7b1d6ea5c48fc22171b14549a.1588586761.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Thu, 23 Apr 2020 18:25:20 +0200
-Subject: [PATCH] IPv6 PD: time parameter checks
-
-RFC3633 imposes the following constraints for IPv6 pd time parameters:
-
-Identity Association for Prefix Delegation Option:
---------------------------------------------------
-t1 must not be greater than t2 if both of them are greater than 0
-
-IA_PD Prefix option:
---------------------
-preferred lifetime must not be greater than valid lifetime
-
-Add checks for previous constraints in ovn implementation
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c | 19 ++++++++++++++++++-
- 1 file changed, 18 insertions(+), 1 deletion(-)
-
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -653,6 +653,11 @@ pinctrl_parse_dhcpv6_advt(struct rconn *
-         case DHCPV6_OPT_IA_PD: {
-             struct dhcpv6_opt_ia_na *ia_na = (struct dhcpv6_opt_ia_na *)in_opt;
-             int orig_len = len, hdr_len = 0, size = sizeof *in_opt + 12;
-+            uint32_t t1 = ntohl(ia_na->t1), t2 = ntohl(ia_na->t2);
-+
-+            if (t1 > t2 && t2 > 0) {
-+                goto out;
-+            }
- 
-             aid = ntohl(ia_na->iaid);
-             memcpy(&data[len], in_opt, size);
-@@ -667,6 +672,15 @@ pinctrl_parse_dhcpv6_advt(struct rconn *
-                 }
- 
-                 if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) {
-+                    struct dhcpv6_opt_ia_prefix *ia_hdr =
-+                        (struct dhcpv6_opt_ia_prefix *)in_opt;
-+                    uint32_t plife_time = ntohl(ia_hdr->plife_time);
-+                    uint32_t vlife_time = ntohl(ia_hdr->vlife_time);
-+
-+                    if (plife_time > vlife_time) {
-+                        goto out;
-+                    }
-+
-                     memcpy(&data[len], in_opt, flen);
-                     hdr_len += flen;
-                     len += flen;
-@@ -831,9 +845,12 @@ pinctrl_parse_dhcpv6_reply(struct dp_pac
-                     struct dhcpv6_opt_ia_prefix *ia_hdr =
-                         (struct dhcpv6_opt_ia_prefix *)(in_dhcpv6_data + size);
- 
--                    prefix_len = ia_hdr->plen;
-                     plife_time = ntohl(ia_hdr->plife_time);
-                     vlife_time = ntohl(ia_hdr->vlife_time);
-+                    if (plife_time > vlife_time) {
-+                        break;
-+                    }
-+                    prefix_len = ia_hdr->plen;
-                     memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr));
-                     status = true;
-                 }
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
new file mode 100644
index 0000000..8607655
--- /dev/null
+++ b/SOURCES/0001-Introduce-DHCPDECLINE-msg-support-to-OVN-DHCP-server.patch
@@ -0,0 +1,74 @@
+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-Make-the-notify-calls-work-with-IPv6-in-the-OCF-reso.patch b/SOURCES/0001-Make-the-notify-calls-work-with-IPv6-in-the-OCF-reso.patch
deleted file mode 100644
index 262e59b..0000000
--- a/SOURCES/0001-Make-the-notify-calls-work-with-IPv6-in-the-OCF-reso.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 5d47f08c60600708aae354b021da9779a9c8e5e6 Mon Sep 17 00:00:00 2001
-From: Michele Baldessari <michele@acksyn.org>
-Date: Wed, 3 Jun 2020 14:43:47 +0200
-Subject: [PATCH] Make the notify() calls work with IPv6 in the OCF
- resource-agent
-
-When the VIP is an IPv6 address we get the following error in the
-resource agent:
-ovndb_servers_notify_0:355:stderr [ + ovn-sbctl -- --id=@conn_uuid create Connection 'target=ptcp\:6642\:[fd00:fd00:fd00:2000::a2]' inactivity_probe=180000 -- set SB_Global . connections=@conn_uuid ]
-ovndb_servers_notify_0:355:stderr [ ovn-sbctl: ptcp\:6642\:[fd00:fd00:fd00:2000::a2]: unexpected "[" parsing string ]
-
-This is because MASTER_IP is an IPv6 address and is being passed to
-ovn-[ns]bctl without being escaped and the command errors out with
-unexpected parsing string errors. The rest of the create Connection
-command was already escaping the columns, we are just missing the ip
-address bits in case of IPv6.
-
-Let's make sure we escape the '[]:' characters and avoid this problem.
-Tested this on an OpenStack environment on both IPv6 and IPv4.
-
-Signed-off-by: Michele Baldessari <michele@acksyn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- utilities/ovndb-servers.ocf | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/utilities/ovndb-servers.ocf b/utilities/ovndb-servers.ocf
-index 56c2bc322..7351c7d64 100755
---- a/utilities/ovndb-servers.ocf
-+++ b/utilities/ovndb-servers.ocf
-@@ -249,7 +249,9 @@ ovsdb_server_notify() {
-         if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xno ]; then
-            LISTEN_ON_IP="0.0.0.0"
-         else
--           LISTEN_ON_IP=${MASTER_IP}
-+           # ovn-[sn]bctl want ':[]' characters to be escaped. We do so in
-+           # order to make this work when MASTER_IP is an IPv6 address.
-+           LISTEN_ON_IP=$(sed -e 's/\(\[\|\]\|:\)/\\\1/g' <<< ${MASTER_IP})
-         fi
-         conn=`ovn-nbctl get NB_global . connections`
-         if [ "$conn" == "[]" ]
--- 
-2.26.2
-
diff --git a/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch b/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch
deleted file mode 100644
index 04bf3fd..0000000
--- a/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From ab7a370f24ec88a019f1aa4da76f1a050bf398c6 Mon Sep 17 00:00:00 2001
-Message-Id: <ab7a370f24ec88a019f1aa4da76f1a050bf398c6.1588167268.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Mon, 27 Apr 2020 17:45:20 +0200
-Subject: [PATCH] Rely on unique name for ovn qos meters
-
-ovn currently identifies qos meters according to the rate and burst values
-configured. Doing so 2 meters on the same hv assigned to 2 different logical
-switch ports and configured with the same values for rate and burst will be
-mapped to the same ovs kernel mater and will share the bandwidth.
-Fix this behavior making qos meter name unique
-
-Tested-By: Maciej Jozefczyk <mjozefcz@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/ofctrl.c |  2 +-
- lib/actions.c       | 11 ++++++-----
- tests/ovn.at        | 10 ++++++++++
- 3 files changed, 17 insertions(+), 6 deletions(-)
-
---- a/controller/ofctrl.c
-+++ b/controller/ofctrl.c
-@@ -970,7 +970,7 @@ add_meter_string(struct ovn_extend_table
-     enum ofputil_protocol usable_protocols;
-     char *meter_string = xasprintf("meter=%"PRIu32",%s",
-                                    m_desired->table_id,
--                                   &m_desired->name[9]);
-+                                   &m_desired->name[52]);
-     char *error = parse_ofp_meter_mod_str(&mm, meter_string, OFPMC13_ADD,
-                                           &usable_protocols);
-     if (!error) {
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2796,12 +2796,13 @@ encode_SET_METER(const struct ovnact_set
-      * describes the meter itself. */
-     char *name;
-     if (cl->burst) {
--        name = xasprintf("__string: kbps burst stats bands=type=drop "
--                         "rate=%"PRId64" burst_size=%"PRId64"", cl->rate,
--                         cl->burst);
-+        name = xasprintf("__string: uuid "UUID_FMT" kbps burst stats "
-+                         "bands=type=drop rate=%"PRId64" burst_size=%"PRId64,
-+                         UUID_ARGS(&ep->lflow_uuid), cl->rate, cl->burst);
-     } else {
--        name = xasprintf("__string: kbps stats bands=type=drop "
--                         "rate=%"PRId64"", cl->rate);
-+        name = xasprintf("__string: uuid "UUID_FMT" kbps stats "
-+                         "bands=type=drop rate=%"PRId64,
-+                         UUID_ARGS(&ep->lflow_uuid), cl->rate);
-     }
- 
-     table_id = ovn_extend_table_assign_id(ep->meter_table, name,
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -7653,6 +7653,16 @@ AT_CHECK([as hv ovs-ofctl dump-flows br-
- AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep rate=11123 | wc -l], [0], [0
- ])
- 
-+# Check multiple qos meters
-+ovn-nbctl qos-del lsw0
-+ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp1" && is_chassis_resident("lp1")' rate=100000 burst=100000
-+ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp2" && is_chassis_resident("lp2")' rate=100000 burst=100000
-+ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp1" && is_chassis_resident("lp1")' rate=100001 burst=100001
-+ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp2" && is_chassis_resident("lp2")' rate=100001 burst=100001
-+
-+AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [4
-+])
-+
- OVN_CLEANUP([hv])
- AT_CLEANUP
- 
diff --git a/SOURCES/0001-Split-SB-Port_Group-per-datapath.patch b/SOURCES/0001-Split-SB-Port_Group-per-datapath.patch
deleted file mode 100644
index 695a0fe..0000000
--- a/SOURCES/0001-Split-SB-Port_Group-per-datapath.patch
+++ /dev/null
@@ -1,556 +0,0 @@
-From 3cdc2f9cdd9b4911a236c731dfa76535e3af38e6 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Mon, 29 Jun 2020 17:24:41 +0200
-Subject: [PATCH] Split SB Port_Group per datapath.
-
-In order to avoid ovn-controller reinstalling all logical flows that
-refer a port_group when some ports are added/removed from the port group
-we now change the way ovn-northd populates the Southbound DB Port_Group
-table.
-
-Instead of copying NB.Port_Group.name to SB.Port_Group.name we now
-create one SB.Port_Group record for every datapath that has ports
-referenced by the NB.Port_Group.ports field. In order to maintain the
-SB.Port_Group.name uniqueness constraint, ovn-northd populates the field
-with the value: <SB.Logical_Datapath.tunnel_key>_<NB.Port_Group.name>.
-
-In specific scenarios we see significant improvements in time to
-install/remove all logical flows to/from OVS. One such scenario, in the
-BZ referenced below has:
-
-$ ovn-nbctl acl-list pg
-  from-lport  1001 (inport == @pg && ip) drop
-    to-lport  1001 (outport == @pg && ip) drop
-
-Then, incrementally, creates new logical ports on different logical
-switches, binds them to OVS interfaces and adds them to the port_group.
-
-Measuring the total time to perform the above steps 500 times (for 500
-new ports attached to 100 switches, 5 per switch) on a test setup
-we observe an improvement of 50% in time it takes to install all
-openflow rules when port_groups are split in the SB database.
-
-Suggested-by: Numan Siddique <numans@ovn.org>
-Reported-by: Venkata Anil <anilvenkata@redhat.com>
-Reported-at: https://bugzilla.redhat.com/1818128
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-
-Conflicts:
-	TODO.rst
-	lib/ovn-util.h
-
-Change-Id: Ibb8ffc5dbf4deb33a46e94b3f7b57c248d669073
----
- TODO.rst              |  8 ++++++
- controller/lflow.c    |  4 ++-
- include/ovn/expr.h    |  4 ++-
- lib/actions.c         |  2 +-
- lib/expr.c            | 48 ++++++++++++++++++++++++-------
- lib/ovn-util.h        |  7 +++++
- northd/ovn-northd.c   | 79 ++++++++++++++++++++++++++++++++++-----------------
- tests/ovn-northd.at   | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
- tests/test-ovn.c      | 10 +++----
- utilities/ovn-trace.c |  3 +-
- 10 files changed, 198 insertions(+), 46 deletions(-)
-
-diff --git a/TODO.rst b/TODO.rst
-index 809d1c9..cfd33be 100644
---- a/TODO.rst
-+++ b/TODO.rst
-@@ -149,3 +149,11 @@ OVN To-do List
- * OVN Interconnection
- 
-   * Packaging for RHEL, Debian, etc.
-+
-+* ovn-controller: Remove backwards compatibility for Southbound DB Port_Group
-+  names in expr.c a few releases after the 20.09 version. Right now
-+  ovn-controller maintains backwards compatibility when connecting to a
-+  SB database that doesn't store Port_Group.name as
-+  <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.
-diff --git a/controller/lflow.c b/controller/lflow.c
-index 01214a3..0e57327 100644
---- a/controller/lflow.c
-+++ b/controller/lflow.c
-@@ -552,7 +552,9 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
-     struct sset port_groups_ref = SSET_INITIALIZER(&port_groups_ref);
-     expr = expr_parse_string(lflow->match, &symtab, l_ctx_in->addr_sets,
-                              l_ctx_in->port_groups,
--                             &addr_sets_ref, &port_groups_ref, &error);
-+                             &addr_sets_ref, &port_groups_ref,
-+                             lflow->logical_datapath->tunnel_key,
-+                             &error);
-     const char *addr_set_name;
-     SSET_FOR_EACH (addr_set_name, &addr_sets_ref) {
-         lflow_resource_add(l_ctx_out->lfrr, REF_TYPE_ADDRSET, addr_set_name,
-diff --git a/include/ovn/expr.h b/include/ovn/expr.h
-index 21bf51c..9838251 100644
---- a/include/ovn/expr.h
-+++ b/include/ovn/expr.h
-@@ -391,12 +391,14 @@ struct expr *expr_parse(struct lexer *, const struct shash *symtab,
-                         const struct shash *addr_sets,
-                         const struct shash *port_groups,
-                         struct sset *addr_sets_ref,
--                        struct sset *port_groups_ref);
-+                        struct sset *port_groups_ref,
-+                        int64_t dp_id);
- struct expr *expr_parse_string(const char *, const struct shash *symtab,
-                                const struct shash *addr_sets,
-                                const struct shash *port_groups,
-                                struct sset *addr_sets_ref,
-                                struct sset *port_groups_ref,
-+                               int64_t dp_id,
-                                char **errorp);
- 
- struct expr *expr_clone(struct expr *);
-diff --git a/lib/actions.c b/lib/actions.c
-index 3181126..d107871 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -242,7 +242,7 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite)
-     char *error;
- 
-     expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL,
--                             NULL, NULL, &error);
-+                             NULL, NULL, 0, &error);
-     ovs_assert(!error);
-     ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
- }
-diff --git a/lib/expr.c b/lib/expr.c
-index 078d178..497b2ac 100644
---- a/lib/expr.c
-+++ b/lib/expr.c
-@@ -29,6 +29,7 @@
- #include "simap.h"
- #include "sset.h"
- #include "util.h"
-+#include "ovn-util.h"
- 
- VLOG_DEFINE_THIS_MODULE(expr);
- 
-@@ -482,6 +483,10 @@ struct expr_context {
-     const struct shash *port_groups; /* Port group table. */
-     struct sset *addr_sets_ref;      /* The set of address set referenced. */
-     struct sset *port_groups_ref;    /* The set of port groups referenced. */
-+    int64_t dp_id;                   /* The tunnel_key of the datapath for
-+                                        which we're parsing the current
-+                                        expression. */
-+
-     bool not;                    /* True inside odd number of NOT operators. */
-     unsigned int paren_depth;    /* Depth of nested parentheses. */
- };
-@@ -783,14 +788,32 @@ static bool
- parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs,
-                  size_t *allocated_values)
- {
-+    struct ds sb_name = DS_EMPTY_INITIALIZER;
-+
-+    get_sb_port_group_name(ctx->lexer->token.s, ctx->dp_id, &sb_name);
-     if (ctx->port_groups_ref) {
--        sset_add(ctx->port_groups_ref, ctx->lexer->token.s);
-+        sset_add(ctx->port_groups_ref, ds_cstr(&sb_name));
-+    }
-+
-+    struct expr_constant_set *port_group = NULL;
-+
-+    if (ctx->port_groups) {
-+        port_group = shash_find_data(ctx->port_groups, ds_cstr(&sb_name));
-+        if (!port_group) {
-+            /* For backwards compatibility (e.g., ovn-controller was
-+             * upgraded but ovn-northd not yet), perform an additional
-+             * lookup because the NB Port_Group.name might have been
-+             * stored as is in the SB Port_Group.name field.
-+             */
-+            port_group = shash_find_data(ctx->port_groups,
-+                                         ctx->lexer->token.s);
-+            if (ctx->port_groups_ref) {
-+                sset_add(ctx->port_groups_ref, ctx->lexer->token.s);
-+            }
-+        }
-     }
-+    ds_destroy(&sb_name);
- 
--    struct expr_constant_set *port_group
--        = (ctx->port_groups
--           ? shash_find_data(ctx->port_groups, ctx->lexer->token.s)
--           : NULL);
-     if (!port_group) {
-         lexer_syntax_error(ctx->lexer, "expecting port group name");
-         return false;
-@@ -1302,14 +1325,16 @@ expr_parse(struct lexer *lexer, const struct shash *symtab,
-            const struct shash *addr_sets,
-            const struct shash *port_groups,
-            struct sset *addr_sets_ref,
--           struct sset *port_groups_ref)
-+           struct sset *port_groups_ref,
-+           int64_t dp_id)
- {
-     struct expr_context ctx = { .lexer = lexer,
-                                 .symtab = symtab,
-                                 .addr_sets = addr_sets,
-                                 .port_groups = port_groups,
-                                 .addr_sets_ref = addr_sets_ref,
--                                .port_groups_ref = port_groups_ref };
-+                                .port_groups_ref = port_groups_ref,
-+                                .dp_id = dp_id };
-     return lexer->error ? NULL : expr_parse__(&ctx);
- }
- 
-@@ -1325,6 +1350,7 @@ expr_parse_string(const char *s, const struct shash *symtab,
-                   const struct shash *port_groups,
-                   struct sset *addr_sets_ref,
-                   struct sset *port_groups_ref,
-+                  int64_t dp_id,
-                   char **errorp)
- {
-     struct lexer lexer;
-@@ -1332,7 +1358,7 @@ expr_parse_string(const char *s, const struct shash *symtab,
-     lexer_init(&lexer, s);
-     lexer_get(&lexer);
-     struct expr *expr = expr_parse(&lexer, symtab, addr_sets, port_groups,
--                                   addr_sets_ref, port_groups_ref);
-+                                   addr_sets_ref, port_groups_ref, dp_id);
-     lexer_force_end(&lexer);
-     *errorp = lexer_steal_error(&lexer);
-     if (*errorp) {
-@@ -1558,7 +1584,7 @@ expr_get_level(const struct expr *expr)
- static enum expr_level
- expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
- {
--    struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL,
-+    struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, 0,
-                                           errorp);
-     enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
-     expr_destroy(expr);
-@@ -1730,7 +1756,7 @@ parse_and_annotate(const char *s, const struct shash *symtab,
-     char *error;
-     struct expr *expr;
- 
--    expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, &error);
-+    expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, 0, &error);
-     if (expr) {
-         expr = expr_annotate_(expr, symtab, nesting, &error);
-     }
-@@ -3456,7 +3482,7 @@ expr_parse_microflow(const char *s, const struct shash *symtab,
-     lexer_get(&lexer);
- 
-     struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups,
--                                NULL, NULL);
-+                                NULL, NULL, 0);
-     lexer_force_end(&lexer);
- 
-     if (e) {
-diff --git a/lib/ovn-util.h b/lib/ovn-util.h
-index ec5f2cf..9c6d357 100644
---- a/lib/ovn-util.h
-+++ b/lib/ovn-util.h
-@@ -111,6 +111,13 @@ bool ovn_tnlid_in_use(const struct hmap *set, uint32_t tnlid);
- uint32_t ovn_allocate_tnlid(struct hmap *set, const char *name, uint32_t min,
-                             uint32_t max, uint32_t *hint);
- 
-+static inline void
-+get_sb_port_group_name(const char *nb_pg_name, int64_t dp_tunnel_key,
-+                       struct ds *sb_pg_name)
-+{
-+    ds_put_format(sb_pg_name, "%"PRId64"_%s", dp_tunnel_key, nb_pg_name);
-+}
-+
- char *ovn_chassis_redirect_name(const char *port_name);
- void ovn_set_pidfile(const char *name);
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index fc25031..8a809d0 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4457,7 +4457,11 @@ build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip,
- struct ovn_port_group_ls {
-     struct hmap_node key_node;  /* Index on 'key'. */
-     struct uuid key;            /* nb_ls->header_.uuid. */
--    const struct nbrec_logical_switch *nb_ls;
-+    struct ovn_datapath *od;
-+
-+    struct ovn_port **ports; /* Ports in 'od' referrenced by the PG. */
-+    size_t n_ports;
-+    size_t n_allocated_ports;
- };
- 
- struct ovn_port_group {
-@@ -4467,14 +4471,14 @@ struct ovn_port_group {
-     struct hmap nb_lswitches;   /* NB lswitches related to the port group */
- };
- 
--static void
--ovn_port_group_ls_add(struct ovn_port_group *pg,
--                      const struct nbrec_logical_switch *nb_ls)
-+static struct ovn_port_group_ls *
-+ovn_port_group_ls_add(struct ovn_port_group *pg, struct ovn_datapath *od)
- {
-     struct ovn_port_group_ls *pg_ls = xzalloc(sizeof *pg_ls);
--    pg_ls->key = nb_ls->header_.uuid;
--    pg_ls->nb_ls = nb_ls;
-+    pg_ls->key = od->nbs->header_.uuid;
-+    pg_ls->od = od;
-     hmap_insert(&pg->nb_lswitches, &pg_ls->key_node, uuid_hash(&pg_ls->key));
-+    return pg_ls;
- }
- 
- static struct ovn_port_group_ls *
-@@ -4491,6 +4495,18 @@ ovn_port_group_ls_find(struct ovn_port_group *pg, const struct uuid *ls_uuid)
-     return NULL;
- }
- 
-+static void
-+ovn_port_group_ls_add_port(struct ovn_port_group_ls *pg_ls,
-+                           struct ovn_port *op)
-+{
-+    if (pg_ls->n_ports == pg_ls->n_allocated_ports) {
-+        pg_ls->ports = x2nrealloc(pg_ls->ports,
-+                                  &pg_ls->n_allocated_ports,
-+                                  sizeof *pg_ls->ports);
-+    }
-+    pg_ls->ports[pg_ls->n_ports++] = op;
-+}
-+
- struct ovn_ls_port_group {
-     struct hmap_node key_node;  /* Index on 'key'. */
-     struct uuid key;            /* nb_pg->header_.uuid. */
-@@ -5250,6 +5266,7 @@ ovn_port_group_destroy(struct hmap *pgs, struct ovn_port_group *pg)
-         hmap_remove(pgs, &pg->key_node);
-         struct ovn_port_group_ls *ls;
-         HMAP_FOR_EACH_POP (ls, key_node, &pg->nb_lswitches) {
-+            free(ls->ports);
-             free(ls);
-         }
-         hmap_destroy(&pg->nb_lswitches);
-@@ -5287,9 +5304,10 @@ build_port_group_lswitches(struct northd_context *ctx, struct hmap *pgs,
-             struct ovn_port_group_ls *pg_ls =
-                 ovn_port_group_ls_find(pg, &op->od->nbs->header_.uuid);
-             if (!pg_ls) {
--                ovn_port_group_ls_add(pg, op->od->nbs);
-+                pg_ls = ovn_port_group_ls_add(pg, op->od);
-                 ovn_ls_port_group_add(&op->od->nb_pgs, nb_pg);
-             }
-+            ovn_port_group_ls_add_port(pg_ls, op);
-         }
-     }
- }
-@@ -10454,7 +10472,7 @@ sync_address_sets(struct northd_context *ctx)
-  * contains lport uuids, while in OVN_Southbound we store the lport names.
-  */
- static void
--sync_port_groups(struct northd_context *ctx)
-+sync_port_groups(struct northd_context *ctx, struct hmap *pgs)
- {
-     struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
- 
-@@ -10463,26 +10481,35 @@ sync_port_groups(struct northd_context *ctx)
-         shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
-     }
- 
--    const struct nbrec_port_group *nb_port_group;
--    NBREC_PORT_GROUP_FOR_EACH (nb_port_group, ctx->ovnnb_idl) {
--        sb_port_group = shash_find_and_delete(&sb_port_groups,
--                                               nb_port_group->name);
--        if (!sb_port_group) {
--            sb_port_group = sbrec_port_group_insert(ctx->ovnsb_txn);
--            sbrec_port_group_set_name(sb_port_group, nb_port_group->name);
--        }
-+    struct ds sb_name = DS_EMPTY_INITIALIZER;
- 
--        const char **nb_port_names = xcalloc(nb_port_group->n_ports,
--                                             sizeof *nb_port_names);
--        int i;
--        for (i = 0; i < nb_port_group->n_ports; i++) {
--            nb_port_names[i] = nb_port_group->ports[i]->name;
-+    struct ovn_port_group *pg;
-+    HMAP_FOR_EACH (pg, key_node, pgs) {
-+
-+        struct ovn_port_group_ls *pg_ls;
-+        HMAP_FOR_EACH (pg_ls, key_node, &pg->nb_lswitches) {
-+            ds_clear(&sb_name);
-+            get_sb_port_group_name(pg->nb_pg->name, pg_ls->od->sb->tunnel_key,
-+                                   &sb_name);
-+            sb_port_group = shash_find_and_delete(&sb_port_groups,
-+                                                  ds_cstr(&sb_name));
-+            if (!sb_port_group) {
-+                sb_port_group = sbrec_port_group_insert(ctx->ovnsb_txn);
-+                sbrec_port_group_set_name(sb_port_group, ds_cstr(&sb_name));
-+            }
-+
-+            const char **nb_port_names = xcalloc(pg_ls->n_ports,
-+                                                 sizeof *nb_port_names);
-+            for (size_t i = 0; i < pg_ls->n_ports; i++) {
-+                nb_port_names[i] = pg_ls->ports[i]->nbsp->name;
-+            }
-+            sbrec_port_group_set_ports(sb_port_group,
-+                                       nb_port_names,
-+                                       pg_ls->n_ports);
-+            free(nb_port_names);
-         }
--        sbrec_port_group_set_ports(sb_port_group,
--                                   nb_port_names,
--                                   nb_port_group->n_ports);
--        free(nb_port_names);
-     }
-+    ds_destroy(&sb_name);
- 
-     struct shash_node *node, *next;
-     SHASH_FOR_EACH_SAFE (node, next, &sb_port_groups) {
-@@ -11081,7 +11108,7 @@ ovnnb_db_run(struct northd_context *ctx,
-     ovn_update_ipv6_prefix(ports);
- 
-     sync_address_sets(ctx);
--    sync_port_groups(ctx);
-+    sync_port_groups(ctx, &port_groups);
-     sync_meters(ctx);
-     sync_dns_entries(ctx, datapaths);
-     destroy_ovn_lbs(&lbs);
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index e6a8c04..37805d3 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1406,3 +1406,82 @@ AT_CHECK([ovn-nbctl --wait=sb sync], [0])
- AT_CHECK([test 0 = $(ovn-sbctl list Ha_Chassis_Group | wc -l)])
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- check NB/SB Port_Group translation (lsp add/del)])
-+ovn_start
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl ls-add ls2
-+ovn-nbctl lsp-add ls1 lsp1
-+ovn-nbctl lsp-add ls2 lsp2
-+ovn-nbct --wait=sb sync
-+ls1_key=$(ovn-sbctl --columns tunnel_key --bare list Datapath_Binding ls1)
-+ls2_key=$(ovn-sbctl --columns tunnel_key --bare list Datapath_Binding ls2)
-+
-+# Add an empty port group. This should generate no entry in the SB.
-+ovn-nbctl --wait=sb pg-add pg_test
-+AT_CHECK([test 0 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+
-+# Add lsp1 to the port group. This should generate an entry in the SB only
-+# for ls1.
-+ovn-nbctl --wait=sb pg-set-ports pg_test lsp1
-+AT_CHECK([test 1 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls1_key}_pg_test], [0], [dnl
-+lsp1
-+])
-+
-+# Add lsp2 to the port group. This should generate a new entry in the SB, for
-+# ls2.
-+ovn-nbctl --wait=sb pg-set-ports pg_test lsp1 lsp2
-+AT_CHECK([test 2 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls1_key}_pg_test], [0], [dnl
-+lsp1
-+])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
-+lsp2
-+])
-+
-+# Remove lsp1 from the port group. The SB Port_Group for ls1 should be
-+# removed.
-+ovn-nbctl --wait=sb pg-set-ports pg_test lsp2
-+AT_CHECK([test 1 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
-+lsp2
-+])
-+
-+# Remove lsp2 from the port group. All SB Port_Groups should be purged.
-+ovn-nbctl --wait=sb clear Port_Group pg_test ports
-+AT_CHECK([test 0 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+
-+AT_CLEANUP
-+
-+AT_SETUP([ovn -- check NB/SB Port_Group translation (ls del)])
-+ovn_start
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl ls-add ls2
-+ovn-nbctl lsp-add ls1 lsp1
-+ovn-nbctl lsp-add ls2 lsp2
-+ovn-nbct --wait=sb sync
-+ls1_key=$(ovn-sbctl --columns tunnel_key --bare list Datapath_Binding ls1)
-+ls2_key=$(ovn-sbctl --columns tunnel_key --bare list Datapath_Binding ls2)
-+
-+# Add lsp1 & lsp2 to a port group. This should generate two entries in the
-+# SB (one per logical switch).
-+ovn-nbctl --wait=sb pg-add pg_test lsp1 lsp2
-+AT_CHECK([test 2 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls1_key}_pg_test], [0], [dnl
-+lsp1
-+])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
-+lsp2
-+])
-+
-+# Delete logical switch ls1. This should remove the associated SB Port_Group.
-+ovn-nbctl --wait=sb ls-del ls1
-+AT_CHECK([test 1 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
-+AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
-+lsp2
-+])
-+
-+AT_CLEANUP
-diff --git a/tests/test-ovn.c b/tests/test-ovn.c
-index 11697eb..9f74c5c 100644
---- a/tests/test-ovn.c
-+++ b/tests/test-ovn.c
-@@ -235,8 +235,8 @@ create_port_groups(struct shash *port_groups)
-     };
-     static const char *const pg2[] = { NULL };
- 
--    expr_const_sets_add(port_groups, "pg1", pg1, 3, false);
--    expr_const_sets_add(port_groups, "pg_empty", pg2, 0, false);
-+    expr_const_sets_add(port_groups, "0_pg1", pg1, 3, false);
-+    expr_const_sets_add(port_groups, "0_pg_empty", pg2, 0, false);
- }
- 
- static bool
-@@ -302,7 +302,7 @@ test_parse_expr__(int steps)
-         char *error;
- 
-         expr = expr_parse_string(ds_cstr(&input), &symtab, &addr_sets,
--                                 &port_groups, NULL, NULL, &error);
-+                                 &port_groups, NULL, NULL, 0, &error);
-         if (!error && steps > 0) {
-             expr = expr_annotate(expr, &symtab, &error);
-         }
-@@ -428,7 +428,7 @@ test_evaluate_expr(struct ovs_cmdl_context *ctx)
-         struct expr *expr;
- 
-         expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL,
--                                 NULL, NULL, &error);
-+                                 NULL, NULL, 0, &error);
-         if (!error) {
-             expr = expr_annotate(expr, &symtab, &error);
-         }
-@@ -903,7 +903,7 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
- 
-             char *error;
-             modified = expr_parse_string(ds_cstr(&s), symtab, NULL,
--                                         NULL, NULL, NULL, &error);
-+                                         NULL, NULL, NULL, 0, &error);
-             if (error) {
-                 fprintf(stderr, "%s fails to parse (%s)\n",
-                         ds_cstr(&s), error);
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index d7251e7..2666c10 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -889,7 +889,8 @@ read_flows(void)
-         char *error;
-         struct expr *match;
-         match = expr_parse_string(sblf->match, &symtab, &address_sets,
--                                  &port_groups, NULL, NULL, &error);
-+                                  &port_groups, NULL, NULL, dp->tunnel_key,
-+                                  &error);
-         if (error) {
-             VLOG_WARN("%s: parsing expression failed (%s)",
-                       sblf->match, error);
--- 
-1.8.3.1
-
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
new file mode 100644
index 0000000..ee4a429
--- /dev/null
+++ b/SOURCES/0001-chassis-Fix-the-way-encaps-are-updated-for-a-chassis.patch
@@ -0,0 +1,175 @@
+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-controller-Use-OpenFlow-version-1.5.patch b/SOURCES/0001-controller-Use-OpenFlow-version-1.5.patch
deleted file mode 100644
index b979eea..0000000
--- a/SOURCES/0001-controller-Use-OpenFlow-version-1.5.patch
+++ /dev/null
@@ -1,259 +0,0 @@
-From a8fcc8cc07ff9acbf9ff328e6ac2e781d73d3f8b Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 21 Apr 2020 19:28:23 +0530
-Subject: [PATCH 1/4] controller: Use OpenFlow version 1.5
-
-When adding flows to the group table, we need to use OFP15_VERSION to
-set the selection_method. Right now ovn-controller is setting
-select_method=dp_hash for OVN load balancers, but when encoding the
-group mod, it is ignored.
-
-Acked-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- NEWS                        |  1 +
- controller/ofctrl.c         | 14 +++++++-------
- controller/ovn-controller.c |  2 +-
- controller/pinctrl.c        |  2 +-
- lib/actions.c               | 10 +++++-----
- lib/expr.c                  |  2 +-
- tests/ovn.at                |  6 +++---
- utilities/ovn-sbctl.c       |  4 ++--
- utilities/ovn-trace.c       |  4 ++--
- 9 files changed, 23 insertions(+), 22 deletions(-)
-
-diff --git a/NEWS b/NEWS
-index 21c80f0dc..e77343c89 100644
---- a/NEWS
-+++ b/NEWS
-@@ -8,6 +8,7 @@ OVN v20.03.0 - 28 Feb 2020
-    - Added support for MLD Snooping and MLD Querier.
-    - Added support for ECMP routes in OVN router.
-    - Added IPv6 Prefix Delegation support in OVN.
-+   - OVN now uses OpenFlow 1.5.
- 
-    - OVN Interconnection:
-      * Support for L3 interconnection of multiple OVN deployments with tunnels
-diff --git a/controller/ofctrl.c b/controller/ofctrl.c
-index 485a857d1..4b51cd86e 100644
---- a/controller/ofctrl.c
-+++ b/controller/ofctrl.c
-@@ -178,7 +178,7 @@ ofctrl_init(struct ovn_extend_table *group_table,
-             int inactivity_probe_interval)
- {
-     swconn = rconn_create(inactivity_probe_interval, 0,
--                          DSCP_DEFAULT, 1 << OFP13_VERSION);
-+                          DSCP_DEFAULT, 1 << OFP15_VERSION);
-     tx_counter = rconn_packet_counter_create();
-     hmap_init(&installed_flows);
-     ovs_list_init(&flow_updates);
-@@ -282,8 +282,8 @@ process_tlv_table_reply(const struct ofputil_tlv_table_reply *reply)
-     ovs_list_init(&ttm.mappings);
-     ovs_list_push_back(&ttm.mappings, &tm.list_node);
- 
--    xid = queue_msg(ofputil_encode_tlv_table_mod(OFP13_VERSION, &ttm));
--    xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION));
-+    xid = queue_msg(ofputil_encode_tlv_table_mod(OFP15_VERSION, &ttm));
-+    xid2 = queue_msg(ofputil_encode_barrier_request(OFP15_VERSION));
-     state = S_TLV_TABLE_MOD_SENT;
- 
-     return true;
-@@ -911,7 +911,7 @@ encode_flow_mod(struct ofputil_flow_mod *fm)
-     fm->buffer_id = UINT32_MAX;
-     fm->out_port = OFPP_ANY;
-     fm->out_group = OFPG_ANY;
--    return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM);
-+    return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF15_OXM);
- }
- 
- static void
-@@ -926,7 +926,7 @@ add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs)
- static struct ofpbuf *
- encode_group_mod(const struct ofputil_group_mod *gm)
- {
--    return ofputil_encode_group_mod(OFP13_VERSION, gm, NULL, -1);
-+    return ofputil_encode_group_mod(OFP15_VERSION, gm, NULL, -1);
- }
- 
- static void
-@@ -940,7 +940,7 @@ add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs)
- static struct ofpbuf *
- encode_meter_mod(const struct ofputil_meter_mod *mm)
- {
--    return ofputil_encode_meter_mod(OFP13_VERSION, mm);
-+    return ofputil_encode_meter_mod(OFP15_VERSION, mm);
- }
- 
- static void
-@@ -1281,7 +1281,7 @@ ofctrl_put(struct ovn_desired_flow_table *flow_table,
- 
-     if (!ovs_list_is_empty(&msgs)) {
-         /* Add a barrier to the list of messages. */
--        struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP13_VERSION);
-+        struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP15_VERSION);
-         const struct ofp_header *oh = barrier->data;
-         ovs_be32 xid_ = oh->xid;
-         ovs_list_push_back(&msgs, &barrier->list_node);
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 6ff897325..a2d92429c 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -2297,7 +2297,7 @@ parse_options(int argc, char *argv[])
-             usage();
- 
-         case 'V':
--            ovs_print_version(OFP13_VERSION, OFP13_VERSION);
-+            ovs_print_version(OFP15_VERSION, OFP15_VERSION);
-             exit(EXIT_SUCCESS);
- 
-         VLOG_OPTION_HANDLERS
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 9d5b7c3c0..6b0ac3483 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -2805,7 +2805,7 @@ pinctrl_handler(void *arg_)
-     static long long int svc_monitors_next_run_time = LLONG_MAX;
-     static long long int send_prefixd_time = LLONG_MAX;
- 
--    swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
-+    swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
- 
-     while (!latch_is_set(&pctrl->pinctrl_thread_exit)) {
-         if (pctrl->br_int_name) {
-diff --git a/lib/actions.c b/lib/actions.c
-index 2dba9a922..605dbffe4 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -1457,7 +1457,7 @@ encode_nested_actions(const struct ovnact_nest *on,
-     size_t oc_offset = encode_start_controller_op(opcode, false,
-                                                   NX_CTLR_NO_METER, ofpacts);
-     ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size,
--                                 ofpacts, OFP13_VERSION);
-+                                 ofpacts, OFP15_VERSION);
-     encode_finish_controller_op(oc_offset, ofpacts);
- 
-     /* Free memory. */
-@@ -2260,7 +2260,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
-     size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_PUT_DHCP_OPTS,
-                                                   true, NX_CTLR_NO_METER,
-                                                   ofpacts);
--    nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
-+    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
-     ovs_be32 ofs = htonl(dst.ofs);
-     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
- 
-@@ -2291,7 +2291,7 @@ encode_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo,
- 
-     size_t oc_offset = encode_start_controller_op(
-         ACTION_OPCODE_PUT_DHCPV6_OPTS, true, NX_CTLR_NO_METER, ofpacts);
--    nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
-+    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
-     ovs_be32 ofs = htonl(dst.ofs);
-     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
- 
-@@ -2401,7 +2401,7 @@ encode_DNS_LOOKUP(const struct ovnact_dns_lookup *dl,
-     size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_DNS_LOOKUP,
-                                                   true, NX_CTLR_NO_METER,
-                                                   ofpacts);
--    nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
-+    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
-     ovs_be32 ofs = htonl(dst.ofs);
-     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
-     encode_finish_controller_op(oc_offset, ofpacts);
-@@ -2565,7 +2565,7 @@ encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po,
- 
-     size_t oc_offset = encode_start_controller_op(
-         ACTION_OPCODE_PUT_ND_RA_OPTS, true, NX_CTLR_NO_METER, ofpacts);
--    nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
-+    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
-     ovs_be32 ofs = htonl(dst.ofs);
-     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
- 
-diff --git a/lib/expr.c b/lib/expr.c
-index 78646a1af..078d17840 100644
---- a/lib/expr.c
-+++ b/lib/expr.c
-@@ -1414,7 +1414,7 @@ expr_symbol_format(const struct expr_symbol *symbol, struct ds *s)
-     } else if (symbol->ovn_field) {
-         ds_put_cstr(s, symbol->name);
-     } else {
--        nx_format_field_name(symbol->field->id, OFP13_VERSION, s);
-+        nx_format_field_name(symbol->field->id, OFP15_VERSION, s);
-     }
- }
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 35415f2b6..5fb100ad4 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1186,7 +1186,7 @@ reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_name=1.2.3.4);
- 
- # nd_ns
- nd_ns { nd.target = xxreg0; output; };
--    encodes as controller(userdata=00.00.00.09.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.80.00.00.00.00.00.01.de.10.00.01.2e.10.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-+    encodes as controller(userdata=00.00.00.09.00.00.00.00.00.1c.00.18.00.80.00.00.00.00.00.00.00.01.de.10.80.00.3e.10.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-     has prereqs ip6
- 
- nd_ns { };
-@@ -1197,12 +1197,12 @@ nd_ns { };
- # nd_na
- nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; };
-     formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; };
--    encodes as controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-+    encodes as controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.00.1c.00.18.00.20.00.00.00.00.00.00.00.01.1c.04.00.01.1e.04.00.00.00.00.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-     has prereqs nd_ns
- # nd_na_router
- nd_na_router { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; };
-     formats as nd_na_router { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; };
--    encodes as controller(userdata=00.00.00.0c.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-+    encodes as controller(userdata=00.00.00.0c.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.00.1c.00.18.00.20.00.00.00.00.00.00.00.01.1c.04.00.01.1e.04.00.00.00.00.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
-     has prereqs nd_ns
- 
- # get_nd
-diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
-index d8bb3dcbc..04e082c70 100644
---- a/utilities/ovn-sbctl.c
-+++ b/utilities/ovn-sbctl.c
-@@ -795,7 +795,7 @@ sbctl_open_vconn(struct shash *options)
- 
-     char *remote = ovs->data ? xstrdup(ovs->data) : default_ovs();
-     struct vconn *vconn;
--    int retval = vconn_open_block(remote, 1 << OFP13_VERSION, 0, -1, &vconn);
-+    int retval = vconn_open_block(remote, 1 << OFP15_VERSION, 0, -1, &vconn);
-     if (retval) {
-         VLOG_WARN("%s: connection failed (%s)", remote, ovs_strerror(retval));
-     }
-@@ -816,7 +816,7 @@ sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats)
- 
-     struct ofputil_flow_stats *fses;
-     size_t n_fses;
--    int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM,
-+    int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF15_OXM,
-                                  &fses, &n_fses);
-     if (error) {
-         VLOG_WARN("%s: error obtaining flow stats (%s)",
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index c9d72285c..d7251e7ed 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -2326,7 +2326,7 @@ trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
- 
-     struct ofputil_flow_stats *fses;
-     size_t n_fses;
--    int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM,
-+    int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF15_OXM,
-                                  &fses, &n_fses);
-     if (error) {
-         ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
-@@ -2435,7 +2435,7 @@ trace(const char *dp_s, const char *flow_s)
-     ds_put_char(&output, '\n');
- 
-     if (ovs) {
--        int retval = vconn_open_block(ovs, 1 << OFP13_VERSION, 0, -1, &vconn);
-+        int retval = vconn_open_block(ovs, 1 << OFP15_VERSION, 0, -1, &vconn);
-         if (retval) {
-             VLOG_WARN("%s: connection failed (%s)", ovs, ovs_strerror(retval));
-         }
--- 
-2.26.2
-
diff --git a/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch b/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
deleted file mode 100644
index 091ee2d..0000000
--- a/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-From 7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68 Mon Sep 17 00:00:00 2001
-Message-Id: <7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68.1585835882.git.me@lorenzobianconi.net>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Tue, 24 Mar 2020 20:33:27 +0100
-Subject: [PATCH] controller: use LLA IPv6 address as NS source address
-
-Use router LLA IPv6 address as IPv6 source address for Neighbor
-Solicitation packets
-
-Fixes: c0bf32d72 ("Manage ARP process locally in a DVR scenario")
-Change-Id: Iafa26f4b3c20e181bd5b54a357d468ce61b589b6
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Lorenzo Bianconi <me@lorenzobianconi.net>
----
- controller/pinctrl.c |  4 +++-
- tests/ovn.at         | 15 +++++++++------
- 2 files changed, 12 insertions(+), 7 deletions(-)
-
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -4563,9 +4563,11 @@ pinctrl_handle_nd_ns(struct rconn *swcon
- 
-     uint64_t packet_stub[128 / 8];
-     struct dp_packet packet;
-+    struct in6_addr ipv6_src;
-     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- 
--    compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,
-+    in6_generate_lla(ip_flow->dl_src, &ipv6_src);
-+    compose_nd_ns(&packet, ip_flow->dl_src, &ipv6_src,
-                   &ip_flow->ipv6_dst);
- 
-     /* Reload previous packet metadata and set actions from userdata. */
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -11280,13 +11280,13 @@ options:rxq_pcap=${pcap_file}-rx.pcap
- # This function sends ipv6 packet
- test_ipv6() {
-     local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
--    local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8
-+    local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8 nd_src_ip=$9
- 
-     local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}
-     packet=${packet}8000000000000000
- 
-     src_mac=000002010204
--    expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${src_ip}
-+    expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${nd_src_ip}
-     expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000
-     expected_packet=${expected_packet}${nd_target}0101${src_mac}
- 
-@@ -11298,6 +11298,7 @@ test_ipv6() {
- src_mac=506400000002
- dst_mac=00000000af01
- src_ip=aef0000000000000526400fffe000002
-+nd_src_ip=fe80000000000000020002fffe010204
- dst_ip=20010db800010000020002fffe010205
- dst_mcast_mac=3333ff010205
- mcast_node_ip=ff0200000000000000000001ff010205
-@@ -11305,7 +11306,7 @@ nd_target=20010db800010000020002fffe0102
- # Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet
- # should be received by the ports attached to br-phys.
- test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
--$mcast_node_ip $nd_target
-+$mcast_node_ip $nd_target $nd_src_ip
- 
- OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
- OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
-@@ -11338,7 +11339,7 @@ dst_mcast_mac=3333ff011305
- mcast_node_ip=ff0200000000000000000001ff011305
- nd_target=20010db800010000020002fffe011305
- test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
--$mcast_node_ip $nd_target
-+$mcast_node_ip $nd_target $nd_src_ip
- 
- OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
- OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
-@@ -14262,7 +14263,7 @@ send_na() {
- get_nd() {
-     local eth_src=$1 src_ip=$2 dst_ip=$3 ta=$4
-     local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
--    request=3333ff000010${eth_src}86dd${ip6_hdr}8700357600000000${ta}0101${eth_src}
-+    request=3333ff000010${eth_src}86dd${ip6_hdr}870051f400000000${ta}0101${eth_src}
- 
-     echo $request
- }
-@@ -14325,6 +14326,8 @@ router_mac1=000002010203
- router_ip=$(ip_to_hex 172 16 1 1)
- router_ip6=20020000000000000000000000000001
- 
-+nd_src_ip6=fe80000000000000020002fffe010203
-+
- dst_mac=001122334455
- dst_ip=$(ip_to_hex 172 16 1 10)
- dst_ip6=20020000000000000000000000000010
-@@ -14342,7 +14345,7 @@ nd_ip=ff0200000000000000000001ff000010
- ip6_hdr=6000000000083afe${src_ip6}${dst_ip6}
- 
- send_icmp6_packet 1 1 $src_mac $router_mac0 $src_ip6 $dst_ip6
--echo $(get_nd $router_mac1 $src_ip6 $nd_ip $dst_ip6) >> expected
-+echo $(get_nd $router_mac1 $nd_src_ip6 $nd_ip $dst_ip6) >> expected
- echo "${dst_mac}${router_mac1}86dd${ip6_hdr}8000dcb662f00001" >> expected
- send_na 2 1 $dst_mac $router_mac1 $dst_ip6 $router_ip6
- 
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
new file mode 100644
index 0000000..e1d5078
--- /dev/null
+++ b/SOURCES/0001-lex-Allow-unmasked-bits-in-value-mask-tokens.patch
@@ -0,0 +1,95 @@
+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-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch b/SOURCES/0001-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
deleted file mode 100644
index 2352aab..0000000
--- a/SOURCES/0001-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From d64f501d787571a50eb2e5380947d1d0a3e2ca74 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 11 Jun 2020 18:44:41 +0530
-Subject: [PATCH] northd: By pass IPv6 Router Adv and Router Solicitation
- packets from ACL stages.
-
-We already add below logical flows to by pass IPv6 Neighbor discovery packets
-from in/out ACL stage.
-
-table=6 (ls_in_acl          ), priority=65535, match=(nd), action=(next;)
-table=4 (ls_out_acl         ), priority=65535, match=(nd), action=(next;)
-
-This patch also adds nd_rs and nd_ra to these logical flows. Without these
-the IPv6 Router Adv packets generated by ovn-controller are dropped if
-CMS has configured ACLs.
-
-Reported-by: Jakub Libosvar <jlibosva@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-
-(cherry-picked from upstream master commit 90e5971018277ab0f383a56f59ffcfe17466a2c6)
-
-Change-Id: I33fcb3032fe946f2b2333a8cf2791af75dceaf44
----
- northd/ovn-northd.8.xml | 6 ++++++
- northd/ovn-northd.c     | 6 ++++--
- 2 files changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index dc56de273..081536ab4 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -439,6 +439,12 @@
-         ACL re-allow this connection.
-       </li>
- 
-+      <li>
-+        A priority-65535 flow that allows IPv6 Neighbor solicitation,
-+        Neighbor discover, Router solicitation and Router advertisement
-+        packets.
-+      </li>
-+
-       <li>
-         A priority 34000 logical flow is added for each logical switch datapath
-         with the match <code>eth.dst = <var>E</var></code> to allow the service
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index cffe3de17..fc250318f 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -5390,8 +5390,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
-         /* Ingress and Egress ACL Table (Priority 65535).
-          *
-          * Not to do conntrack on ND packets. */
--        ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "nd", "next;");
--        ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "nd", "next;");
-+        ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
-+                      "nd || nd_ra || nd_rs", "next;");
-+        ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
-+                      "nd || nd_ra || nd_rs", "next;");
-     }
- 
-     /* Ingress or Egress ACL Table (Various priorities). */
--- 
-2.26.2
-
diff --git a/SOURCES/0001-northd-do-not-insert-identical-lflows-in-S_ROUTER_IN.patch b/SOURCES/0001-northd-do-not-insert-identical-lflows-in-S_ROUTER_IN.patch
deleted file mode 100644
index d000815..0000000
--- a/SOURCES/0001-northd-do-not-insert-identical-lflows-in-S_ROUTER_IN.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 5f3e15c3d5809134d70892b4f65031e5bd110c8f Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 11 Mar 2020 17:41:59 +0100
-Subject: [PATCH 1/2] northd: do not insert identical lflows in
- S_ROUTER_IN_ARP_RESOLVE
-
-Avoid to configure multiple identical logical flows in
-S_ROUTER_IN_ARP_RESOLVE stage. This can happen adding L2 destination
-address info about snat since multiple nat entries will use the same
-external_ip
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry picked from upstream OVS branch20.03 commit 20aa8c3c5a1930805a32ec8121affa07b2ac7dff)
-
-Change-Id: Ic5c1df529363469092a55454fdfbcae31a06ccf5
----
- northd/ovn-northd.c | 36 ++++++++++++++++++++++--------------
- 1 file changed, 22 insertions(+), 14 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 787ca2f80..cdaeff401 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -8630,6 +8630,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             continue;
-         }
- 
-+        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);
-@@ -8855,20 +8857,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                                             &nat->header_);
-                 }
- 
--                ds_clear(&match);
--                ds_put_format(
--                    &match, "outport == %s && %s == %s",
--                    od->l3dgw_port->json_key,
--                    is_v6 ? "xxreg0" : "reg0", nat->external_ip);
--                ds_clear(&actions);
--                ds_put_format(
--                    &actions, "eth.dst = %s; next;",
--                    distributed ? nat->external_mac :
--                    od->l3dgw_port->lrp_networks.ea_s);
--                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_RESOLVE,
--                                        100, ds_cstr(&match),
--                                        ds_cstr(&actions),
--                                        &nat->header_);
-+                if (!sset_contains(&nat_entries, nat->external_ip)) {
-+                    ds_clear(&match);
-+                    ds_put_format(
-+                        &match, "outport == %s && %s == %s",
-+                        od->l3dgw_port->json_key,
-+                        is_v6 ? "xxreg0" : "reg0", nat->external_ip);
-+                    ds_clear(&actions);
-+                    ds_put_format(
-+                        &actions, "eth.dst = %s; next;",
-+                        distributed ? nat->external_mac :
-+                        od->l3dgw_port->lrp_networks.ea_s);
-+                    ovn_lflow_add_with_hint(lflows, od,
-+                                            S_ROUTER_IN_ARP_RESOLVE,
-+                                            100, ds_cstr(&match),
-+                                            ds_cstr(&actions),
-+                                            &nat->header_);
-+                    sset_add(&nat_entries, nat->external_ip);
-+                }
-             }
- 
-             /* Egress UNDNAT table: It is for already established connections'
-@@ -9049,6 +9055,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             }
-         }
- 
-+        sset_destroy(&nat_entries);
-+
-         /* 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
--- 
-2.25.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
new file mode 100644
index 0000000..51d2497
--- /dev/null
+++ b/SOURCES/0001-northd-fix-empty_lb_backends-controller_event-for-IP.patch
@@ -0,0 +1,115 @@
+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-ofctrl-Split-large-group_mod-messages-up.patch b/SOURCES/0001-ofctrl-Split-large-group_mod-messages-up.patch
deleted file mode 100644
index 6f7f2ec..0000000
--- a/SOURCES/0001-ofctrl-Split-large-group_mod-messages-up.patch
+++ /dev/null
@@ -1,161 +0,0 @@
-From 88056d15bffe67c033322de16c01a013e7bc7c7c Mon Sep 17 00:00:00 2001
-From: Mark Michelson <mmichels@redhat.com>
-Date: Wed, 6 May 2020 09:49:55 -0400
-Subject: [PATCH] ofctrl: Split large group_mod messages up.
-
-Group mod messages have the possibility of growing very large if OVN
-installs a load balancer with a great many backends. The current
-approach is to send a single ADD message with the entire group contents.
-If the size of this message exceeds UINT16_MAX, then OpenFlow cannot
-properly express the length of the message since the OpenFlow header's
-length is limited to 16 bits.
-
-This patch solves the problem by breaking the message into pieces. The
-first piece is an ADD, and subsequent messages are INSERT_BUCKET
-messages. This way, we end up being able to express the entire size of
-the group through multiple OpenFlow messages.
-
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
----
- controller/ofctrl.c | 70 ++++++++++++++++++++++++++++++++++++++++++---
- tests/ovn.at        | 29 +++++++++++++++++++
- 2 files changed, 95 insertions(+), 4 deletions(-)
-
-diff --git a/controller/ofctrl.c b/controller/ofctrl.c
-index 4b51cd86e..073e076c7 100644
---- a/controller/ofctrl.c
-+++ b/controller/ofctrl.c
-@@ -930,10 +930,72 @@ encode_group_mod(const struct ofputil_group_mod *gm)
- }
- 
- static void
--add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs)
-+add_group_mod(struct ofputil_group_mod *gm, struct ovs_list *msgs)
- {
-     struct ofpbuf *msg = encode_group_mod(gm);
--    ovs_list_push_back(msgs, &msg->list_node);
-+    if (msg->size <= UINT16_MAX) {
-+        ovs_list_push_back(msgs, &msg->list_node);
-+        return;
-+    }
-+    /* This group mod request is too large to fit in a single OF message
-+     * since the header can only specify a 16-bit size. We need to break
-+     * this into multiple group_mod requests.
-+     */
-+
-+    /* Pull the first bucket. All buckets are approximately the same length
-+     * since they contain near-identical actions. Using its length can give
-+     * us a good approximation of how many buckets we can fit in a single
-+     * OF message.
-+     */
-+    ofpraw_pull_assert(msg);
-+    struct ofp15_group_mod *ogm = ofpbuf_pull(msg, sizeof(*ogm));
-+    struct ofp15_bucket *of_bucket = ofpbuf_pull(msg, sizeof(*of_bucket));
-+    uint16_t bucket_size = ntohs(of_bucket->len);
-+
-+    ofpbuf_delete(msg);
-+
-+    /* Dividing by 2 here ensures that just in case there are variations in
-+     * the size of the buckets, we will not put too many in our new group_mod
-+     * message.
-+     */
-+    size_t max_buckets = ((UINT16_MAX - sizeof *ogm) / bucket_size) / 2;
-+
-+    ovs_assert(max_buckets < ovs_list_size(&gm->buckets));
-+
-+    uint16_t command = OFPGC15_INSERT_BUCKET;
-+    if (gm->command == OFPGC15_DELETE ||
-+        gm->command == OFPGC15_REMOVE_BUCKET) {
-+        command = OFPGC15_REMOVE_BUCKET;
-+    }
-+    struct ofputil_group_mod split = {
-+        .command = command,
-+        .type = gm->type,
-+        .group_id = gm->group_id,
-+        .command_bucket_id = OFPG15_BUCKET_LAST,
-+    };
-+    ovs_list_init(&split.buckets);
-+
-+    size_t i = 0;
-+    struct ofputil_bucket *bucket;
-+    LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
-+        if (i++ < max_buckets) {
-+            continue;
-+        }
-+        break;
-+    }
-+
-+    ovs_list_splice(&split.buckets, &bucket->list_node, &gm->buckets);
-+
-+    struct ofpbuf *orig = encode_group_mod(gm);
-+    ovs_list_push_back(msgs, &orig->list_node);
-+
-+    /* We call this recursively just in case our new
-+     * INSERT_BUCKET/REMOVE_BUCKET group_mod is still too
-+     * large for an OF message. This will allow for it to
-+     * be broken into pieces, too.
-+     */
-+    add_group_mod(&split, msgs);
-+    ofputil_uninit_group_mod(&split);
- }
- 
- 
-@@ -1124,7 +1186,7 @@ ofctrl_put(struct ovn_desired_flow_table *flow_table,
-         char *group_string = xasprintf("group_id=%"PRIu32",%s",
-                                        desired->table_id,
-                                        desired->name);
--        char *error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD, group_string,
-+        char *error = parse_ofp_group_mod_str(&gm, OFPGC15_ADD, group_string,
-                                               NULL, NULL, &usable_protocols);
-         if (!error) {
-             add_group_mod(&gm, &msgs);
-@@ -1243,7 +1305,7 @@ ofctrl_put(struct ovn_desired_flow_table *flow_table,
-         enum ofputil_protocol usable_protocols;
-         char *group_string = xasprintf("group_id=%"PRIu32"",
-                                        installed->table_id);
--        char *error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
-+        char *error = parse_ofp_group_mod_str(&gm, OFPGC15_DELETE,
-                                               group_string, NULL, NULL,
-                                               &usable_protocols);
-         if (!error) {
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 52d994972..f39fda2e4 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -19179,3 +19179,32 @@ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
- 
- OVN_CLEANUP([hv1])
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- Big Load Balancer])
-+ovn_start
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl lsp-add ls1 lsp1
-+
-+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 p1 -- set Interface p1 external-ids:iface-id=lsp1
-+
-+IPS=192.169.0.1:80
-+for i in `seq 1 9` ; do
-+    for j in `seq 1 254` ; do
-+        IPS=${IPS},192.169.$i.$j:80
-+    done
-+done
-+
-+ovn-nbctl lb-add lb0 172.172.0.1:8080 "${IPS}"
-+ovn-nbctl --wait=hv ls-lb-add ls1 lb0
-+
-+AT_CHECK([test 2287 = `ovs-ofctl dump-group-stats br-int | grep -o bucket | wc -l`])
-+
-+OVN_CLEANUP([hv1])
-+AT_CLEANUP
--- 
-2.25.4
-
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
new file mode 100644
index 0000000..e525ef5
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Fix-incremental-processing-of-Port_Bi.patch
@@ -0,0 +1,207 @@
+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-controller-Fix-potential-segfault-with-virtual-p.patch b/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
deleted file mode 100644
index 34b4c29..0000000
--- a/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
+++ /dev/null
@@ -1,104 +0,0 @@
-From 08dfddbe4b1559dd91747cee435eb8945555b348 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 31 Mar 2020 13:47:04 +0200
-Subject: [PATCH] ovn-controller: Fix potential segfault with "virtual" port
- bindings.
-
-Even though ovn-controller tries to set port_binding->chassis to NULL
-every time port_binding->virtual_parent is set to NULL for bindings of
-type="virtual", there's no way to enforce that an operator doesn't
-manually clear the "virtual_parent" column in the Southbound database.
-
-In such scenario ovn-controller would crash because of trying to
-dereference the NULL port_binding->virtual_parent column.
-
-Add an extra check and release "virtual" port bindings that have
-"virtual_parent" NULL.
-
-Reported-at: https://bugzilla.redhat.com/1818844
-CC: Numan Siddique <nusiddiq@redhat.com>
-Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 5b3e9879be2b6c9b07ed5c9e073f1c24080a49f7)
-
-Change-Id: I10c2a8dd3731b34f606c4fa2db42711c81f431cc
----
- controller/binding.c | 26 +++++++++++++++-----------
- tests/ovn.at         | 18 ++++++++++++++++++
- 2 files changed, 33 insertions(+), 11 deletions(-)
-
-diff --git a/controller/binding.c b/controller/binding.c
-index c3376e2..5ea12a8 100644
---- a/controller/binding.c
-+++ b/controller/binding.c
-@@ -625,22 +625,26 @@ consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
-                             const struct sbrec_chassis *chassis_rec,
-                             const struct sbrec_port_binding *binding_rec)
- {
-+    if (binding_rec->virtual_parent) {
-+        const struct sbrec_port_binding *parent =
-+            lport_lookup_by_name(sbrec_port_binding_by_name,
-+                                 binding_rec->virtual_parent);
-+        if (parent && parent->chassis == chassis_rec) {
-+            return;
-+        }
-+    }
-+
-     /* pinctrl module takes care of binding the ports of type 'virtual'.
-      * Release such ports if their virtual parents are no longer claimed by
-      * this chassis.
-      */
--    const struct sbrec_port_binding *parent =
--        lport_lookup_by_name(sbrec_port_binding_by_name,
--                             binding_rec->virtual_parent);
--    if (!parent || parent->chassis != chassis_rec) {
--        VLOG_INFO("Releasing lport %s from this chassis.",
--                  binding_rec->logical_port);
--        if (binding_rec->encap) {
--            sbrec_port_binding_set_encap(binding_rec, NULL);
--        }
--        sbrec_port_binding_set_chassis(binding_rec, NULL);
--        sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
-+    VLOG_INFO("Releasing lport %s from this chassis.",
-+              binding_rec->logical_port);
-+    if (binding_rec->encap) {
-+        sbrec_port_binding_set_encap(binding_rec, NULL);
-     }
-+    sbrec_port_binding_set_chassis(binding_rec, NULL);
-+    sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
- }
- 
- static void
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 9a44f0a..1402fae 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -15007,6 +15007,24 @@ 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;)
- ])
- 
-+# Forcibly clear virtual_parent. ovn-controller should release the binding
-+# gracefully.
-+pb_uuid=$(ovn-sbctl --bare --columns _uuid find port_binding logical_port=sw0-vir)
-+ovn-sbctl clear port_binding $pb_uuid virtual_parent
-+
-+OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-+logical_port=sw0-vir) = x])
-+
-+# From sw0-p0 resend GARP for 10.0.0.10. hv1 should reclaim sw0-vir
-+# and sw0-p1 should be its virtual_parent.
-+send_garp 1 1 $eth_src $eth_dst $spa $tpa
-+
-+OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-+logical_port=sw0-vir) = x$hv1_ch_uuid], [0], [])
-+
-+AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
-+logical_port=sw0-vir) = xsw0-p1])
-+
- # From sw0-p3 send GARP for 10.0.0.10. hv1 should claim sw0-vir
- # and sw0-p3 should be its virtual_parent.
- eth_src=505400000005
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch b/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
deleted file mode 100644
index 85088c5..0000000
--- a/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
+++ /dev/null
@@ -1,90 +0,0 @@
-From a8acc52e37d5a74487b0a787bf8a519debc3a031 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 2 Apr 2020 10:35:32 +0200
-Subject: [PATCH] ovn-controller: Skip vport bindings done through OVS
- external_ids:iface-id.
-
-Port bindings of type "virtual" should not have an associated OVS port
-in the integration bridge. If this is the case, it's a misconfig and
-ovn-controller should ignore it.
-
-If such a situation is detected, ovn-controller will also log a warning
-message to inform the user about the wrong configuration.
-
-Reported-at: https://bugzilla.redhat.com/1818844
-CC: Numan Siddique <nusiddiq@redhat.com>
-Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 523b1f5f45682bd6dd454281a97a09c3f429c457)
-
-Change-Id: Ie35818921a6e67c637feaea3be41c59880bb1b96
----
- controller/binding.c | 12 ++++++++++++
- tests/ovn.at         | 20 ++++++++++++++++++++
- 2 files changed, 32 insertions(+)
-
-diff --git a/controller/binding.c b/controller/binding.c
-index 5ea12a8..20a89d0 100644
---- a/controller/binding.c
-+++ b/controller/binding.c
-@@ -447,6 +447,18 @@ is_our_chassis(const struct sbrec_chassis *chassis_rec,
-     const struct ovsrec_interface *iface_rec
-         = shash_find_data(lport_to_iface, binding_rec->logical_port);
- 
-+    /* Ports of type "virtual" should never be explicitly bound to an OVS
-+     * port in the integration bridge. If that's the case, ignore the binding
-+     * and log a warning.
-+     */
-+    if (iface_rec && !strcmp(binding_rec->type, "virtual")) {
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-+        VLOG_WARN_RL(&rl,
-+                     "Virtual port %s should not be bound to OVS port %s",
-+                     binding_rec->logical_port, iface_rec->name);
-+        return false;
-+    }
-+
-     bool our_chassis = false;
-     if (iface_rec
-         || (binding_rec->parent_port && binding_rec->parent_port[0] &&
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 0135838..e8554f6 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -14894,6 +14894,11 @@ ovs-vsctl -- add-port br-int hv1-vif2 -- \
-     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 \
-+    options:tx_pcap=hv1/vif3-tx.pcap \
-+    options:rxq_pcap=hv1/vif3-rx.pcap \
-+    ofport-request=3
- 
- sim_add hv2
- as hv2
-@@ -14987,6 +14992,21 @@ logical_port=sw0-vir) = x], [0], [])
- AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
- logical_port=sw0-vir) = x])
- 
-+# Try to bind sw0-vir directly to an OVS port. This should be ignored by
-+# ovn-controller.
-+as hv1
-+ovs-vsctl set interface hv1-vif3 external-ids:iface-id=sw0-vir
-+
-+AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-+logical_port=sw0-vir) = x], [0], [])
-+
-+# Cleanup hv1-vif3.
-+as hv1
-+ovs-vsctl del-port hv1-vif3
-+
-+AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-+logical_port=sw0-vir) = x], [0], [])
-+
- # From sw0-p0 send GARP for 10.0.0.10. hv1 should claim sw0-vir
- # and sw0-p1 should be its virtual_parent.
- eth_src=505400000003
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch b/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
deleted file mode 100644
index 8f7266e..0000000
--- a/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
+++ /dev/null
@@ -1,182 +0,0 @@
-From 1a34ed1dee90ad3ae82d91725bf8f5e86cf007c6 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Mon, 17 Feb 2020 11:23:45 +0530
-Subject: [PATCH] ovn-ctl: Provide the option to configure inactive probe from
- standby to active.
-
-Recently ovsdb-server supported an unixctl command -
-ovsdb-server/set-active-ovsdb-server-probe-interval to configure inactive probe
-interval from standby connection to the active. This patch provides the
-option to configure this from ovn-ctl and the pacemaker OVN OCF script.
-
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Acked-by: Han Zhou <hzhou@ovn.org>
----
- utilities/ovn-ctl           | 14 +++++++++++---
- utilities/ovn-ctl.8.xml     |  8 ++++++--
- utilities/ovndb-servers.ocf | 23 +++++++++++++++++++----
- 3 files changed, 36 insertions(+), 9 deletions(-)
-
-diff --git a/utilities/ovn-ctl b/utilities/ovn-ctl
-index c7cb42bc1..2a337ae27 100755
---- a/utilities/ovn-ctl
-+++ b/utilities/ovn-ctl
-@@ -82,7 +82,8 @@ demote_xx_ovsdb () {
-     local sync_from_proto=$2
-     local sync_from_port=$3
-     local active_conf_file=$4
--    local ctl_file=$5
-+    local inactive_probe_to_active=$5
-+    local ctl_file=$6
- 
-     if test ! -z "$sync_from_addr"; then
-         echo "$sync_from_proto:$sync_from_addr:$sync_from_port" > $active_conf_file
-@@ -91,6 +92,7 @@ demote_xx_ovsdb () {
-     if test -e $active_conf_file; then
-         ovn-appctl -t $OVN_RUNDIR/$ctl_file ovsdb-server/set-active-ovsdb-server `cat $active_conf_file`
-         ovn-appctl -t $OVN_RUNDIR/$ctl_file ovsdb-server/connect-active-ovsdb-server
-+        ovn-appctl -t $OVN_RUNDIR/$ctl_file ovsdb-server/set-active-ovsdb-server-probe-interval $inactive_probe_to_active
-     else
-         echo >&2 "$0: active server details not set"
-         exit 1
-@@ -99,12 +101,14 @@ demote_xx_ovsdb () {
- 
- demote_ovnnb() {
-     demote_xx_ovsdb $DB_NB_SYNC_FROM_ADDR $DB_NB_SYNC_FROM_PROTO \
--                    $DB_NB_SYNC_FROM_PORT $ovnnb_active_conf_file ovnnb_db.ctl
-+                    $DB_NB_SYNC_FROM_PORT $ovnnb_active_conf_file \
-+                    $DB_NB_PROBE_INTERVAL_TO_ACTIVE ovnnb_db.ctl
- }
- 
- demote_ovnsb() {
-     demote_xx_ovsdb $DB_SB_SYNC_FROM_ADDR $DB_SB_SYNC_FROM_PROTO \
--                    $DB_SB_SYNC_FROM_PORT $ovnsb_active_conf_file ovnsb_db.ctl
-+                    $DB_SB_SYNC_FROM_PORT $ovnsb_active_conf_file \
-+                    $DB_SB_PROBE_INTERVAL_TO_ACTIVE ovnsb_db.ctl
- }
- 
- demote_ic_nb() {
-@@ -642,6 +646,7 @@ set_defaults () {
-     DB_NB_SYNC_FROM_PROTO=tcp
-     DB_NB_SYNC_FROM_ADDR=
-     DB_NB_SYNC_FROM_PORT=6641
-+    DB_NB_PROBE_INTERVAL_TO_ACTIVE=60000
- 
-     DB_SB_SOCK=$OVN_RUNDIR/ovnsb_db.sock
-     DB_SB_PID=$OVN_RUNDIR/ovnsb_db.pid
-@@ -652,6 +657,7 @@ set_defaults () {
-     DB_SB_SYNC_FROM_PROTO=tcp
-     DB_SB_SYNC_FROM_ADDR=
-     DB_SB_SYNC_FROM_PORT=6642
-+    DB_SB_PROBE_INTERVAL_TO_ACTIVE=60000
- 
-     DB_IC_NB_SOCK=$OVN_RUNDIR/ovn_ic_nb_db.sock
-     DB_IC_NB_PID=$OVN_RUNDIR/ovn_ic_nb_db.pid
-@@ -923,10 +929,12 @@ File location options:
-   --db-nb-sync-from-port=PORT OVN Northbound active db tcp port (default: $DB_NB_SYNC_FROM_PORT)
-   --db-nb-sync-from-proto=PROTO OVN Northbound active db transport (default: $DB_NB_SYNC_FROM_PROTO)
-   --db-nb-create-insecure-remote=yes|no Create ptcp OVN Northbound remote (default: $DB_NB_CREATE_INSECURE_REMOTE)
-+  --db-nb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_NB_PROBE_INTERVAL_TO_ACTIVE)
-   --db-sb-sync-from-addr=ADDR OVN Southbound active db tcp address (default: $DB_SB_SYNC_FROM_ADDR)
-   --db-sb-sync-from-port=ADDR OVN Southbound active db tcp port (default: $DB_SB_SYNC_FROM_PORT)
-   --db-sb-sync-from-proto=PROTO OVN Southbound active db transport (default: $DB_SB_SYNC_FROM_PROTO)
-   --db-sb-create-insecure-remote=yes|no Create ptcp OVN Southbound remote (default: $DB_SB_CREATE_INSECURE_REMOTE)
-+  --db-sb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_SB_PROBE_INTERVAL_TO_ACTIVE)
-   --db-nb-cluster-local-addr=ADDR OVN_Northbound cluster local address \
-   (default: $DB_NB_CLUSTER_LOCAL_ADDR)
-   --db-nb-cluster-local-port=PORT OVN_Northbound cluster local tcp port \
-diff --git a/utilities/ovn-ctl.8.xml b/utilities/ovn-ctl.8.xml
-index 816701379..f5b7f7aeb 100644
---- a/utilities/ovn-ctl.8.xml
-+++ b/utilities/ovn-ctl.8.xml
-@@ -150,6 +150,10 @@
-     <p><code>--db-ic-sb-cluster-remote-port=<var>PORT NUMBER</var></code></p>
-     <p><code>--db-ic-sb-cluster-remote-proto=<var>PROTO (tcp/ssl)</var></code></p>
- 
-+    <h1> Probe interval options </h1>
-+    <p><code>--db-nb-probe-interval-to-active=<var>Time in milliseconds</var></code></p>
-+    <p><code>--db-sb-probe-interval-to-active=<var>Time in milliseconds</var></code></p>
-+
-     <h1>Configuration files</h1>
-     <p>Following are the optional configuration files. If present, it should be located in the etc dir</p>
- 
-@@ -241,8 +245,8 @@
-     <h2>Promote and demote ovsdb servers</h2>
-     <p><code># ovn-ctl promote_ovnnb</code></p>
-     <p><code># ovn-ctl promote_ovnsb</code></p>
--    <p><code># ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 demote_ovnnb</code></p>
--    <p><code># ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 demote_ovnsb</code></p>
-+    <p><code># ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 --db-nb-probe-interval-to-active=60000 demote_ovnnb</code></p>
-+    <p><code># ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 --db-sb-probe-interval-to-active=60000 demote_ovnsb</code></p>
- 
-     <h2>Creating a clustered db on 3 nodes with IPs x.x.x.x, y.y.y.y and z.z.z.z</h2>
-     <h3>Starting OVN ovsdb servers and ovn-northd on the node with IP x.x.x.x</h3>
-diff --git a/utilities/ovndb-servers.ocf b/utilities/ovndb-servers.ocf
-index 42e0412ad..56c2bc322 100755
---- a/utilities/ovndb-servers.ocf
-+++ b/utilities/ovndb-servers.ocf
-@@ -9,6 +9,7 @@
- : ${SB_MASTER_PROTO_DEFAULT="tcp"}
- : ${MANAGE_NORTHD_DEFAULT="no"}
- : ${INACTIVE_PROBE_DEFAULT="5000"}
-+: ${INACTIVE_PROBE_TO_MASTER_DEFAULT="60000"}
- : ${LISTEN_ON_MASTER_IP_ONLY_DEFAULT="yes"}
- : ${NB_SSL_KEY_DEFAULT="/etc/openvswitch/ovnnb-privkey.pem"}
- : ${NB_SSL_CERT_DEFAULT="/etc/openvswitch/ovnnb-cert.pem"}
-@@ -27,6 +28,7 @@ SB_MASTER_PORT=${OCF_RESKEY_sb_master_port:-${SB_MASTER_PORT_DEFAULT}}
- SB_MASTER_PROTO=${OCF_RESKEY_sb_master_protocol:-${SB_MASTER_PROTO_DEFAULT}}
- MANAGE_NORTHD=${OCF_RESKEY_manage_northd:-${MANAGE_NORTHD_DEFAULT}}
- INACTIVE_PROBE=${OCF_RESKEY_inactive_probe_interval:-${INACTIVE_PROBE_DEFAULT}}
-+INACTIVE_PROBE_TO_MASTER=${OCF_RESKEY_inactive_probe_interval_to_master:-${INACTIVE_PROBE_TO_MASTER_DEFAULT}}
- NB_PRIVKEY=${OCF_RESKEY_ovn_nb_db_privkey:-${NB_SSL_KEY_DEFAULT}}
- NB_CERT=${OCF_RESKEY_ovn_nb_db_cert:-${NB_SSL_CERT_DEFAULT}}
- NB_CACERT=${OCF_RESKEY_ovn_nb_db_cacert:-${NB_SSL_CACERT_DEFAULT}}
-@@ -135,6 +137,15 @@ ovsdb_server_metadata() {
-   <content type="string" />
-   </parameter>
- 
-+  <parameter name="inactive_probe_interval_to_master" unique="1">
-+  <longdesc lang="en">
-+  Inactive probe interval to use for the connection from standby
-+  ovsdb-server to master ovsdb-server.
-+  </longdesc>
-+  <shortdesc lang="en">Set inactive probe interval to master</shortdesc>
-+  <content type="string" />
-+  </parameter>
-+
-   <parameter name="listen_on_master_ip_only" unique="1">
-   <longdesc lang="en">
-   If set to yes, the OVNDBs will listen on master IP. Otherwise, it will
-@@ -266,10 +277,12 @@ inactivity_probe=$INACTIVE_PROBE -- set SB_Global . connections=@conn_uuid
-         ocf_log debug "ovndb_server: Connecting to the new master ${OCF_RESKEY_CRM_meta_notify_promote_uname}"
-         ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
-                                 --db-nb-sync-from-port=${NB_MASTER_PORT} \
--                                --db-nb-sync-from-proto=${NB_MASTER_PROTO}
-+                                --db-nb-sync-from-proto=${NB_MASTER_PROTO} \
-+                                --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
-         ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
-                                 --db-sb-sync-from-port=${SB_MASTER_PORT} \
--                                --db-sb-sync-from-proto=${SB_MASTER_PROTO}
-+                                --db-sb-sync-from-proto=${SB_MASTER_PROTO} \
-+                                --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
-     fi
- }
- 
-@@ -596,10 +609,12 @@ ovsdb_server_demote() {
-         # being demoted. Sync to the surviving one
-         ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
-                                 --db-nb-sync-from-port=${NB_MASTER_PORT} \
--                                --db-nb-sync-from-proto=${NB_MASTER_PROTO}
-+                                --db-nb-sync-from-proto=${NB_MASTER_PROTO} \
-+                                --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
-         ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
-                                 --db-sb-sync-from-port=${SB_MASTER_PORT} \
--                                --db-sb-sync-from-proto=${SB_MASTER_PROTO}
-+                                --db-sb-sync-from-proto=${SB_MASTER_PROTO} \
-+                                --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
- 
-     else
-         # For completeness, should never be called
--- 
-2.25.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
new file mode 100644
index 0000000..c2fe3b1
--- /dev/null
+++ b/SOURCES/0001-ovn-ctl-introduce-ovsdb-n-s-b-wrapper-options.patch
@@ -0,0 +1,175 @@
+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-Create-daemon-control-socket-in-ovn-run-di.patch b/SOURCES/0001-ovn-nbctl-Create-daemon-control-socket-in-ovn-run-di.patch
deleted file mode 100644
index 999a5dc..0000000
--- a/SOURCES/0001-ovn-nbctl-Create-daemon-control-socket-in-ovn-run-di.patch
+++ /dev/null
@@ -1,180 +0,0 @@
-From 0319176e1d1fa741868d822100d0dde89a585ca3 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Wed, 8 Apr 2020 20:16:49 +0530
-Subject: [PATCH] ovn-nbctl: Create daemon control socket in ovn run dir
-
-ovn-nbctl when run as a daemon is creating the ctl socket in
-the ovs rundir. This patch fixes this issue by creating it in
-the ovn rundir.
-
-When an ovn service is run with -u option (which specifies the
-ctl socket path) and if this path is not absolute, the ovn
-ctl socket path is created in the ovs run dir. This patch
-also fixes this issue by creating it in the ovn run dir.
-
-Reported-by: Dan Williams <dcbw@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream branch-20.03 commit 29927708242044696a49051c77c3d4b38ba02392)
-
-Change-Id: I17b3ab45f591fa9793ae8d95cf78c2113a1e4d65
----
- controller-vtep/ovn-controller-vtep.c |  5 ++++-
- controller/ovn-controller.c           |  2 +-
- ic/ovn-ic.c                           | 10 +++-------
- lib/ovn-util.c                        |  9 +++++----
- lib/ovn-util.h                        |  2 +-
- northd/ovn-northd.c                   | 10 +++-------
- utilities/ovn-nbctl.c                 |  6 +++++-
- utilities/ovn-trace.c                 |  6 +++++-
- 8 files changed, 27 insertions(+), 23 deletions(-)
-
-diff --git a/controller-vtep/ovn-controller-vtep.c b/controller-vtep/ovn-controller-vtep.c
-index b30a731d4..253a709ab 100644
---- a/controller-vtep/ovn-controller-vtep.c
-+++ b/controller-vtep/ovn-controller-vtep.c
-@@ -67,7 +67,10 @@ main(int argc, char *argv[])
- 
-     daemonize_start(false);
- 
--    retval = unixctl_server_create(NULL, &unixctl);
-+    char *abs_unixctl_path = get_abs_unix_ctl_path(NULL);
-+    retval = unixctl_server_create(abs_unixctl_path, &unixctl);
-+    free(abs_unixctl_path);
-+
-     if (retval) {
-         exit(EXIT_FAILURE);
-     }
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 2893eaac1..4d21ba0fd 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -1729,7 +1729,7 @@ main(int argc, char *argv[])
- 
-     daemonize_start(true);
- 
--    char *abs_unixctl_path = get_abs_unix_ctl_path();
-+    char *abs_unixctl_path = get_abs_unix_ctl_path(NULL);
-     retval = unixctl_server_create(abs_unixctl_path, &unixctl);
-     free(abs_unixctl_path);
-     if (retval) {
-diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
-index bf8205de2..d931ca50f 100644
---- a/ic/ovn-ic.c
-+++ b/ic/ovn-ic.c
-@@ -1575,13 +1575,9 @@ main(int argc, char *argv[])
- 
-     daemonize_start(false);
- 
--    if (!unixctl_path) {
--        char *abs_unixctl_path = get_abs_unix_ctl_path();
--        retval = unixctl_server_create(abs_unixctl_path, &unixctl);
--        free(abs_unixctl_path);
--    } else {
--        retval = unixctl_server_create(unixctl_path, &unixctl);
--    }
-+    char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
-+    retval = unixctl_server_create(abs_unixctl_path, &unixctl);
-+    free(abs_unixctl_path);
- 
-     if (retval) {
-         exit(EXIT_FAILURE);
-diff --git a/lib/ovn-util.c b/lib/ovn-util.c
-index df18fda89..514e2489f 100644
---- a/lib/ovn-util.c
-+++ b/lib/ovn-util.c
-@@ -377,7 +377,7 @@ default_ic_sb_db(void)
- }
- 
- char *
--get_abs_unix_ctl_path(void)
-+get_abs_unix_ctl_path(const char *path)
- {
- #ifdef _WIN32
-     enum { WINDOWS = 1 };
-@@ -386,9 +386,10 @@ get_abs_unix_ctl_path(void)
- #endif
- 
-     long int pid = getpid();
--    char *abs_path =
--        WINDOWS ? xasprintf("%s/%s.ctl", ovn_rundir(), program_name)
--                : xasprintf("%s/%s.%ld.ctl", ovn_rundir(), program_name, pid);
-+    char *abs_path
-+        = (path ? abs_file_name(ovn_rundir(), path)
-+           : WINDOWS ? xasprintf("%s/%s.ctl", ovn_rundir(), program_name)
-+           : xasprintf("%s/%s.%ld.ctl", ovn_rundir(), program_name, pid));
-     return abs_path;
- }
- 
-diff --git a/lib/ovn-util.h b/lib/ovn-util.h
-index 32c8334b0..11238f61c 100644
---- a/lib/ovn-util.h
-+++ b/lib/ovn-util.h
-@@ -82,7 +82,7 @@ const char *default_nb_db(void);
- const char *default_sb_db(void);
- const char *default_ic_nb_db(void);
- const char *default_ic_sb_db(void);
--char *get_abs_unix_ctl_path(void);
-+char *get_abs_unix_ctl_path(const char *path);
- 
- struct ovsdb_idl_table_class;
- const char *db_table_usage(struct ds *tables,
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index fd1be5b27..1f1238d23 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -11673,13 +11673,9 @@ main(int argc, char *argv[])
- 
-     daemonize_start(false);
- 
--    if (!unixctl_path) {
--        char *abs_unixctl_path = get_abs_unix_ctl_path();
--        retval = unixctl_server_create(abs_unixctl_path, &unixctl);
--        free(abs_unixctl_path);
--    } else {
--        retval = unixctl_server_create(unixctl_path, &unixctl);
--    }
-+    char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
-+    retval = unixctl_server_create(abs_unixctl_path, &unixctl);
-+    free(abs_unixctl_path);
- 
-     if (retval) {
-         exit(EXIT_FAILURE);
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index 59abe0051..a88c1ddc2 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -6436,7 +6436,11 @@ server_loop(struct ovsdb_idl *idl, int argc, char *argv[])
- 
-     service_start(&argc, &argv);
-     daemonize_start(false);
--    int error = unixctl_server_create(unixctl_path, &server);
-+
-+    char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
-+    int error = unixctl_server_create(abs_unixctl_path, &server);
-+    free(abs_unixctl_path);
-+
-     if (error) {
-         ctl_fatal("failed to create unixctl server (%s)",
-                   ovs_retval_to_string(error));
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index e59698ec4..eae9622d3 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -125,7 +125,11 @@ main(int argc, char *argv[])
-     bool exiting = false;
-     if (get_detach()) {
-         daemonize_start(false);
--        int error = unixctl_server_create(unixctl_path, &server);
-+
-+        char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
-+        int error = unixctl_server_create(abs_unixctl_path, &server);
-+        free(abs_unixctl_path);
-+
-         if (error) {
-             ovs_fatal(error, "failed to create unixctl server");
-         }
--- 
-2.25.1
-
diff --git a/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch b/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch
new file mode 100644
index 0000000..c1a1dae
--- /dev/null
+++ b/SOURCES/0001-ovn-nbctl-Deal-with-nb_cfg-overflows.patch
@@ -0,0 +1,38 @@
+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-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch b/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
deleted file mode 100644
index 5e35cab..0000000
--- a/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
+++ /dev/null
@@ -1,197 +0,0 @@
-From 8e0b2c54726c666db7163dd18673683dfd06d89c Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 12 Mar 2020 15:58:38 +0530
-Subject: [PATCH] ovn-northd: Add lflows to by pass the svc monitor packets
- from conntrack.
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The commit [1] added lflows to by pass the service monitor health check
-packets from conntrack. But it missed out adding in the ingress pre_acl
-and egress pre_acl of logical switch pipeline.
-
-This patch adds these missing lflows. It also enhanced the system lb health
-check tests to add the acls to test this scenario.
-
-[1] - bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline)
-Fixes: bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline)
-
-Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Maciej Jozefczyk <mjozefcz@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream branch-20.03 commit f70a0f7c485f13cbbae8bc6f8d78225238c308b9)
-
-Change-Id: I8980d306ea67c2aaa3bfa8f907a0f71a55fe0f9d
----
- northd/ovn-northd.8.xml | 22 +++++++++++++++++++++-
- northd/ovn-northd.c     | 15 ++++++++++++++-
- tests/ovn.at            | 22 ++++++++++++++++++++++
- tests/system-ovn.at     | 22 ++++++++++++++++++++++
- 4 files changed, 79 insertions(+), 2 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index b6cfa3e90..9b44720d1 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -293,6 +293,16 @@
-       priority-110 flow is added to skip over stateful ACLs.
-     </p>
- 
-+    <p>
-+      This table also has a priority-110 flow with the match
-+      <code>eth.dst == <var>E</var></code> for all logical switch
-+      datapaths to move traffic to the next table. Where <var>E</var>
-+      is the service monitor mac defined in the
-+      <ref column="options:svc_monitor_mac" table="NB_Global"
-+      db="OVN_Northbound"/> colum of <ref table="NB_Global"
-+      db="OVN_Northbound"/> table.
-+    </p>
-+
-     <h3>Ingress Table 4: Pre-LB</h3>
- 
-     <p>
-@@ -320,7 +330,7 @@
- 
-     <p>
-       This table also has a priority-110 flow with the match
--      <code>eth.src == <var>E</var></code> for all logical switch
-+      <code>eth.dst == <var>E</var></code> for all logical switch
-       datapaths to move traffic to the next table. Where <var>E</var>
-       is the service monitor mac defined in the
-       <ref column="options:svc_monitor_mac" table="NB_Global"
-@@ -1295,6 +1305,16 @@ output;
-      <code>to-lport</code> traffic.
-     </p>
- 
-+    <p>
-+      This table also has a priority-110 flow with the match
-+      <code>eth.src == <var>E</var></code> for all logical switch
-+      datapaths to move traffic to the next table. Where <var>E</var>
-+      is the service monitor mac defined in the
-+      <ref column="options:svc_monitor_mac" table="NB_Global"
-+      db="OVN_Northbound"/> colum of <ref table="NB_Global"
-+      db="OVN_Northbound"/> table.
-+    </p>
-+
-     <h3>Egress Table 2: Pre-stateful</h3>
- 
-     <p>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 217a8c894..3a77f2e3a 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4601,6 +4601,16 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;");
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;");
- 
-+    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
-+    ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, svc_check_match,
-+                  "next;");
-+    free(svc_check_match);
-+
-+    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
-+    ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, svc_check_match,
-+                  "next;");
-+    free(svc_check_match);
-+
-     /* If there are any stateful ACL rules in this datapath, we must
-      * send all IP packets through the conntrack action, which handles
-      * defragmentation, in order to match L4 headers. */
-@@ -4784,9 +4794,12 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
-                   "next;");
- 
-     /* Do not send service monitor packets to conntrack. */
--    char *svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
-+    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
-     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
-                   svc_check_match, "next;");
-+    free(svc_check_match);
-+
-+    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
-     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
-                   svc_check_match, "next;");
-     free(svc_check_match);
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 8de4b5ceb..8cdbad743 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -17739,12 +17739,34 @@ ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
- ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
- ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
- 
-+# Create port group and ACLs for sw0 ports.
-+ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2
-+ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
-+ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
-+
-+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
-+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
-+
- # Create the second logical switch with one port
- ovn-nbctl ls-add sw1
- ovn-nbctl lsp-add sw1 sw1-p1
- ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
- ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
- 
-+# Create port group and ACLs for sw1 ports.
-+ovn-nbctl pg-add pg1_drop sw1-p1
-+ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
-+ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
-+
-+ovn-nbctl pg-add pg1 sw1-p1
-+ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
-+
- # Create a logical router and attach both logical switches
- ovn-nbctl lr-add lr0
- ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 9ed3df754..3b3379840 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3314,12 +3314,34 @@ ovn-nbctl lsp-add sw0 sw0-p2
- ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
- ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
- 
-+# Create port group and ACLs for sw0 ports.
-+ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2
-+ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
-+ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
-+
-+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
-+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
-+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
-+
- # Create the second logical switch with one port
- ovn-nbctl ls-add sw1
- ovn-nbctl lsp-add sw1 sw1-p1
- ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
- ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
- 
-+# Create port group and ACLs for sw1 ports.
-+ovn-nbctl pg-add pg1_drop sw1-p1
-+ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
-+ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
-+
-+ovn-nbctl pg-add pg1 sw1-p1
-+ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
-+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
-+
- # Create a logical router and attach both logical switches
- ovn-nbctl lr-add lr0
- ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
--- 
-2.24.1
-
diff --git a/SOURCES/0001-ovn-northd-Clear-SB-records-depending-on-stale-datap.patch b/SOURCES/0001-ovn-northd-Clear-SB-records-depending-on-stale-datap.patch
deleted file mode 100644
index 3538655..0000000
--- a/SOURCES/0001-ovn-northd-Clear-SB-records-depending-on-stale-datap.patch
+++ /dev/null
@@ -1,189 +0,0 @@
-From e5b87cf915c0061355f9c4cdea0df1fe1c26cd38 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 30 Apr 2020 20:32:17 +0200
-Subject: [PATCH 1/2] ovn-northd: Clear SB records depending on stale
- datapaths.
-
-When purging stale SB Datapath_Binding records ovn-northd doesn't
-properly clean records from other tables that might refer the
-datapaths being deleted.
-
-One way to reproduce the issue is:
-$ ovn-nbctl lr-add lr
-$ ovn-nbctl lrp-add lr p 00:00:00:00:00:01 1.1.1.1/24
-$ ovn-nbctl --wait=sb sync
-$ dp=$(ovn-sbctl --bare --columns _uuid list datapath .)
-$ ovn-sbctl create mac_binding logical_port="p" ip="1.1.1.2" datapath="$dp"
-$ ovn-nbctl lrp-del p -- lr-del lr -- \
-    lr-add lr -- lrp-add lr p 00:00:00:00:00:01 1.1.1.1/24
-
-Reported-by: Dan Williams <dcbw@redhat.com>
-Reported-at: https://bugzilla.redhat.com/1828637
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-(cherry picked from upstream commit 6856adc616a7181723ce5201110cc95de1aba92b)
-
-Change-Id: I1cbcb5fc34927368e6655420126b2492c4fce9df
----
- northd/ovn-northd.c | 45 ++++++++++++++++++++++++++++++++-------------
- tests/ovn-northd.at | 24 ++++++++++++++++++++++++
- 2 files changed, 56 insertions(+), 13 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 5ada3ae..5e649d0 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -634,6 +634,12 @@ ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid)
-     return NULL;
- }
- 
-+static bool
-+ovn_datapath_is_stale(const struct ovn_datapath *od)
-+{
-+    return !od->nbr && !od->nbs;
-+}
-+
- static struct ovn_datapath *
- ovn_datapath_from_sbrec(struct hmap *datapaths,
-                         const struct sbrec_datapath_binding *sb)
-@@ -3067,11 +3073,16 @@ ovn_port_update_sbrec(struct northd_context *ctx,
- /* Remove mac_binding entries that refer to logical_ports which are
-  * deleted. */
- static void
--cleanup_mac_bindings(struct northd_context *ctx, struct hmap *ports)
-+cleanup_mac_bindings(struct northd_context *ctx, struct hmap *datapaths,
-+                     struct hmap *ports)
- {
-     const struct sbrec_mac_binding *b, *n;
-     SBREC_MAC_BINDING_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
--        if (!ovn_port_find(ports, b->logical_port)) {
-+        const struct ovn_datapath *od =
-+            ovn_datapath_from_sbrec(datapaths, b->datapath);
-+
-+        if (!od || ovn_datapath_is_stale(od) ||
-+                !ovn_port_find(ports, b->logical_port)) {
-             sbrec_mac_binding_delete(b);
-         }
-     }
-@@ -3439,6 +3450,9 @@ build_ports(struct northd_context *ctx,
-     join_logical_ports(ctx, datapaths, ports, &chassis_qdisc_queues,
-                        &tag_alloc_table, &sb_only, &nb_only, &both);
- 
-+    /* Purge stale Mac_Bindings if ports are deleted. */
-+    bool remove_mac_bindings = !ovs_list_is_empty(&sb_only);
-+
-     struct ovn_port *op, *next;
-     /* For logical ports that are in both databases, index the in-use
-      * tunnel_keys. */
-@@ -3453,6 +3467,12 @@ build_ports(struct northd_context *ctx,
-      * For logical ports that are in NB database, do any tag allocation
-      * needed. */
-     LIST_FOR_EACH_SAFE (op, next, list, &both) {
-+        /* When reusing stale Port_Bindings, make sure that stale
-+         * Mac_Bindings are purged.
-+         */
-+        if (op->od->sb != op->sb->datapath) {
-+            remove_mac_bindings = true;
-+        }
-         if (op->nbsp) {
-             tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
-         }
-@@ -3488,19 +3508,15 @@ build_ports(struct northd_context *ctx,
-         sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);
-     }
- 
--    bool remove_mac_bindings = false;
--    if (!ovs_list_is_empty(&sb_only)) {
--        remove_mac_bindings = true;
--    }
--
-     /* Delete southbound records without northbound matches. */
-     LIST_FOR_EACH_SAFE(op, next, list, &sb_only) {
-         ovs_list_remove(&op->list);
-         sbrec_port_binding_delete(op->sb);
-         ovn_port_destroy(ports, op);
-     }
-+
-     if (remove_mac_bindings) {
--        cleanup_mac_bindings(ctx, ports);
-+        cleanup_mac_bindings(ctx, datapaths, ports);
-     }
- 
-     tag_alloc_destroy(&tag_alloc_table);
-@@ -10258,7 +10274,8 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
-     SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) {
-         struct ovn_datapath *od
-             = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath);
--        if (!od) {
-+
-+        if (!od || ovn_datapath_is_stale(od)) {
-             sbrec_logical_flow_delete(sbflow);
-             continue;
-         }
-@@ -10318,7 +10335,8 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
-     SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
-         struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
-                                                           sbmc->datapath);
--        if (!od) {
-+
-+        if (!od || ovn_datapath_is_stale(od)) {
-             sbrec_multicast_group_delete(sbmc);
-             continue;
-         }
-@@ -10800,8 +10818,8 @@ build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
-     const struct sbrec_ip_multicast *sb, *sb_next;
- 
-     SBREC_IP_MULTICAST_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
--        if (!sb->datapath ||
--                !ovn_datapath_from_sbrec(datapaths, sb->datapath)) {
-+        od = ovn_datapath_from_sbrec(datapaths, sb->datapath);
-+        if (!od || ovn_datapath_is_stale(od)) {
-             sbrec_ip_multicast_delete(sb);
-         }
-     }
-@@ -10870,7 +10888,8 @@ build_mcast_groups(struct northd_context *ctx,
-         /* If the datapath value is stale, purge the group. */
-         struct ovn_datapath *od =
-             ovn_datapath_from_sbrec(datapaths, sb_igmp->datapath);
--        if (!od) {
-+
-+        if (!od || ovn_datapath_is_stale(od)) {
-             sbrec_igmp_group_delete(sb_igmp);
-             continue;
-         }
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index d127152..94f892b 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1326,3 +1326,27 @@ AT_CHECK([test 0 = $(ovn-sbctl dump-flows lr0 | grep lr_in_unsnat | \
- grep "ip4 && ip4.dst == 192.168.2.6 && tcp && tcp.dst == 8080" -c) ])
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- check reconcile stale Datapath_Binding])
-+ovn_start
-+
-+ovn-nbctl lr-add lr
-+ovn-nbctl lrp-add lr p 00:00:00:00:00:01 1.1.1.1/24
-+
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+# Create a MAC_Binding referring the router datapath.
-+dp=$(ovn-sbctl --bare --columns _uuid list datapath .)
-+ovn-sbctl create mac_binding logical_port="p" ip="1.1.1.2" datapath="$dp"
-+
-+ovn-nbctl lrp-del p -- lr-del lr -- \
-+    lr-add lr -- lrp-add lr p 00:00:00:00:00:01 1.1.1.1/24
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+AT_CHECK([test 1 = $(ovn-sbctl --columns _uuid list Datapath_Binding | wc -l)])
-+
-+nb_uuid=$(ovn-sbctl get Datapath_Binding . external_ids:logical-router)
-+lr_uuid=$(ovn-nbctl --columns _uuid list Logical_Router .)
-+AT_CHECK[test ${nb_uuid} = ${lr_uuid}]
-+
-+AT_CLEANUP
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch b/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
deleted file mode 100644
index da692e0..0000000
--- a/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
+++ /dev/null
@@ -1,126 +0,0 @@
-From 5521da70830446373265999b6d994d986a02ce01 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 19 Mar 2020 16:52:17 +0530
-Subject: [PATCH] ovn-northd: Don't add arp responder flows for lports with
- 'unknown' address.
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If a logical port has 'unknown' address, it means it can send and receive
-packet with any IP and MAC and generally port security is not set for
-such logical ports. If an lport has addresses set to - ["MAC1 IP1", unknown],
-right now we add arp responder flows for IP1 and respond MAC1 in the arp
-response. But it's possible that the VIF of the logical port can use the IP1
-with a different MAC. This patch supports this usecase. When another logical port
-sends ARP request for IP1, the VIF of the logical port will anyway respond.
-
-Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.8.xml |  5 +++--
- northd/ovn-northd.c     | 13 ++++++++-----
- tests/ovn.at            | 16 ++++++++++++----
- 3 files changed, 23 insertions(+), 11 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 9b44720d1..7d03cbc83 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -699,8 +699,9 @@ output;
- 
-         <p>
-           These flows are omitted for logical ports (other than router ports or
--          <code>localport</code> ports) that are down and for logical ports of
--          type <code>virtual</code>.
-+          <code>localport</code> ports) that are down, for logical ports of
-+          type <code>virtual</code> and for logical ports with 'unknown'
-+          address set.
-         </p>
-       </li>
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 3a77f2e3a..356c5436c 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -1148,7 +1148,7 @@ struct ovn_port {
- 
-     bool derived; /* Indicates whether this is an additional port
-                    * derived from nbsp or nbrp. */
--
-+    bool has_unknown; /* If the addresses have 'unknown' defined. */
-     /* The port's peer:
-      *
-      *     - A switch port S of type "router" has a router port R as a peer,
-@@ -2055,8 +2055,11 @@ join_logical_ports(struct northd_context *ctx,
-                 op->lsp_addrs
-                     = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
-                 for (size_t j = 0; j < nbsp->n_addresses; j++) {
--                    if (!strcmp(nbsp->addresses[j], "unknown")
--                        || !strcmp(nbsp->addresses[j], "router")) {
-+                    if (!strcmp(nbsp->addresses[j], "unknown")) {
-+                        op->has_unknown = true;
-+                        continue;
-+                    }
-+                    if (!strcmp(nbsp->addresses[j], "router")) {
-                         continue;
-                     }
-                     if (is_dynamic_lsp_address(nbsp->addresses[j])) {
-@@ -6123,7 +6126,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
-         } else {
-             /*
-              * Add ARP/ND reply flows if either the
--             *  - port is up or
-+             *  - port is up and it doesn't have 'unknown' address defined or
-              *  - port type is router or
-              *  - port type is localport
-              */
-@@ -6132,7 +6135,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
-                 continue;
-             }
- 
--            if (lsp_is_external(op->nbsp)) {
-+            if (lsp_is_external(op->nbsp) || op->has_unknown) {
-                 continue;
-             }
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 8cdbad743..1b6073ff0 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1758,11 +1758,13 @@ for is in 1 2 3; do
-                 sip=`ip_to_hex 192 168 0 $is$js`
-                 tip=`ip_to_hex 192 168 0 $id$jd`
-                 tip_unknown=`ip_to_hex 11 11 11 11`
-+                reply_ha=;
-                 if test $d != $s; then
--                    reply_ha=f000000000$d
--                else
--                    reply_ha=
-+                    if test $jd != 1; then
-+                        reply_ha=f000000000$d
-+                    fi
-                 fi
-+
-                 test_arp $s f000000000$s $sip $tip $reply_ha               #9
-                 test_arp $s f000000000$s $sip $tip_unknown                 #10
- 
-@@ -2199,7 +2201,13 @@ for s in 1 2 3; do
-         sip=192.168.0.$s
-         tip=192.168.0.$d
-         tip_unknown=11.11.11.11
--        if test $d != $s; then reply_ha=f0:00:00:00:00:0$d; else reply_ha=; fi
-+        reply_ha=;
-+        if test $d != $s; then
-+            if test $d != 1; then
-+                reply_ha=f0:00:00:00:00:0$d;
-+            fi
-+        fi
-+
-         test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha                 #9
-         test_arp $s f0:00:00:00:00:0$s $sip $tip_unknown                   #10
- 
--- 
-2.24.1
-
diff --git a/SOURCES/0001-ovn-northd-Fix-leak-of-lport-addresses-during-DHCPv6.patch b/SOURCES/0001-ovn-northd-Fix-leak-of-lport-addresses-during-DHCPv6.patch
deleted file mode 100644
index 7d1d91b..0000000
--- a/SOURCES/0001-ovn-northd-Fix-leak-of-lport-addresses-during-DHCPv6.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From c0d305f985cb1a8533950d4f17107d9a71635644 Mon Sep 17 00:00:00 2001
-From: Ilya Maximets <i.maximets@ovn.org>
-Date: Tue, 12 May 2020 12:44:06 +0200
-Subject: [PATCH 1/2] ovn-northd: Fix leak of lport addresses during DHCPv6
- reply handling.
-
-'lrp_networks' never destroyed but constantly overwritten in a loop that
-handles DHCPv6 replies.  In some cases this point leaks several MB per
-minute making ovn-northd to constantly growing its memory consumption:
-
- 399,820,764 bytes in 1,885,947 blocks are definitely lost in loss record 182 of 182
-    at 0x4839748: malloc (vg_replace_malloc.c:308)
-    by 0x483BD63: realloc (vg_replace_malloc.c:836)
-    by 0x1E7BF8: xrealloc (util.c:149)
-    by 0x152723: add_ipv6_netaddr.isra.0 (ovn-util.c:55)
-    by 0x152F1C: extract_lrp_networks (ovn-util.c:275)
-    by 0x142EE2: build_lrouter_flows (ovn-northd.c:8607)
-    by 0x142EE2: build_lflows.isra.0 (ovn-northd.c:10296)
-    by 0x14E4F8: ovnnb_db_run (ovn-northd.c:11128)
-    by 0x14E4F8: ovn_db_run (ovn-northd.c:11672)
-    by 0x13304D: main (ovn-northd.c:12035)
-
-In fact, there is no need to allocate this memory at all, since all the
-required information is already available in 'op->lrp_networks'.
-
-Reported-by: Joe Talerico <jtaleric@redhat.com>
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1827769
-Fixes: 5c1d2d230773 ("northd: Add logical flows for dhcpv6 pfd parsing")
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.c | 9 ++-------
- 1 file changed, 2 insertions(+), 7 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index b07e68cfa..c1cdb2280 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -8603,17 +8603,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             continue;
-         }
- 
--        struct lport_addresses lrp_networks;
--        if (!extract_lrp_networks(op->nbrp, &lrp_networks)) {
--            continue;
--        }
--
--        for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) {
-+        for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
-             ds_clear(&actions);
-             ds_clear(&match);
-             ds_put_format(&match, "ip6.dst == %s && udp.src == 547 &&"
-                           " udp.dst == 546",
--                          lrp_networks.ipv6_addrs[i].addr_s);
-+                          op->lrp_networks.ipv6_addrs[i].addr_s);
-             ds_put_format(&actions, "reg0 = 0; handle_dhcpv6_reply;");
-             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
-                           ds_cstr(&match), ds_cstr(&actions));
--- 
-2.26.2
-
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
new file mode 100644
index 0000000..f370ad9
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Fix-multiple-ARP-replies-for-SNAT-entries.patch
@@ -0,0 +1,393 @@
+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-Fix-the-missing-lflow-issue-in-LS_OUT_PRE.patch b/SOURCES/0001-ovn-northd-Fix-the-missing-lflow-issue-in-LS_OUT_PRE.patch
deleted file mode 100644
index 3d0d04c..0000000
--- a/SOURCES/0001-ovn-northd-Fix-the-missing-lflow-issue-in-LS_OUT_PRE.patch
+++ /dev/null
@@ -1,131 +0,0 @@
-From 7aa75981dfc17eb7f0ac9ee7300e346f3b6a0c8e Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 7 Jul 2020 18:30:20 +0530
-Subject: [PATCH 1/5] ovn-northd: Fix the missing lflow issue in LS_OUT_PRE_LB.
-
-When load balancer(s) configured with VIPs are associated to a logical switch,
-then ovn-northd adds the below logical flow so that the packets in the egress
-switch pipeline enter the conntrack.
-
-table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-
-ovn-northd maintains a local boolean variable 'vip_configured' in
-build_pre_lb() and adds the above lflow if this is true at the end.
-But this variable is overriden as -> vip_configured = !!lb->n_vips;
-when it loops through every load balancer associated with the logical switch.
-
-This is wrong and this patch fixes this issue.
-
-A test case is addd to test this scenario and the test case fails without the
-fix in this patch.
-
-Fixes: bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline")
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1849162
-Reported-by: Tim Rozet <trozet@redhat.com>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from master commit 59af6f9048946e16813ad7ad4e453b85989670e4)
----
- northd/ovn-northd.c |  2 +-
- tests/ovn-northd.at | 73 +++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 74 insertions(+), 1 deletion(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 8a809d020..2b891c68f 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4932,7 +4932,7 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
-              * table, we will eventually look at L4 information. */
-         }
- 
--        vip_configured = !!lb->n_vips;
-+        vip_configured = (vip_configured || lb->n_vips);
-     }
- 
-     /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 37805d3d8..842800b90 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1485,3 +1485,76 @@ lsp2
- ])
- 
- AT_CLEANUP
-+
-+# This test case tests that when a logical switch has load balancers associated
-+# (with VIPs configured), the below logical flow is added by ovn-northd.
-+# table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+# This test case is added for the BZ -
-+# https://bugzilla.redhat.com/show_bug.cgi?id=1849162
-+#
-+# ovn-northd was not adding the above lflow if the last load balancer associated
-+# to the logical switch doesn't have the VIP configured even if other load
-+# balancers before the last one in the last have VIPs configured.
-+# So make sure that the above lflow is added even if one load balancer has VIP
-+# associated.
-+
-+AT_SETUP([ovn -- Load balancer - missing ls_out_pre_lb flows])
-+ovn_start
-+
-+ovn-nbctl ls-add sw0
-+ovn-nbctl lsp-add sw0 sw0-p1
-+
-+ovn-nbctl lb-add lb1 "10.0.0.10" "10.0.0.3"
-+ovn-nbctl lb-add lb2 "10.0.0.11" "10.0.0.4"
-+
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+])
-+
-+ovn-nbctl ls-lb-add sw0 lb1
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+])
-+
-+ovn-nbctl ls-lb-add sw0 lb2
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+])
-+
-+lb1_uuid=$(ovn-nbctl --bare --columns _uuid find load_balancer name=lb1)
-+lb2_uuid=$(ovn-nbctl --bare --columns _uuid find load_balancer name=lb2)
-+
-+ovn-nbctl clear load_balancer $lb1_uuid vips
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+])
-+
-+ovn-nbctl clear load_balancer $lb2_uuid vips
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+])
-+
-+ovn-nbctl set load_balancer $lb1_uuid vips:"10.0.0.10"="10.0.0.3"
-+ovn-nbctl set load_balancer $lb2_uuid vips:"10.0.0.11"="10.0.0.4"
-+
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+])
-+
-+# Now reverse the order of clearing the vip.
-+ovn-nbctl clear load_balancer $lb2_uuid vips
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
-+])
-+
-+ovn-nbctl clear load_balancer $lb1_uuid vips
-+ovn-nbctl --wait=sb sync
-+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
-+])
-+
-+AT_CLEANUP
--- 
-2.26.2
-
diff --git a/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch b/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
deleted file mode 100644
index 742c212..0000000
--- a/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
+++ /dev/null
@@ -1,321 +0,0 @@
-From 20bf8b836703f3ad984e6679510ea215655479a5 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 24 Mar 2020 11:03:29 +0100
-Subject: [PATCH ovn] ovn-northd: Forward ARP requests on localnet ports.
-
-Commit 32f5ebb06226 limited the ARP/ND broadcast domain but in scenarios
-where ARP responder flows are installed only on chassis that own the
-associated logical ports ARP requests should still be forwarded on
-localnet ports because the router pipeline should be executed on the
-chassis that owns the logical port. Only that chassis will reply to the
-ARP/ND request.
-
-Reported-by: Michael Plato <michael.plato@tu-berlin.de>
-Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-March/049856.html
-Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 9f13732ae232aa0c872527d948435125b3a6cbce)
-
-Change-Id: I86b18043821f24bf491c9b85381c5d008f7f2307
----
- northd/ovn-northd.8.xml |   3 +-
- northd/ovn-northd.c     |   6 +-
- tests/ovn.at            | 169 ++++++++++++++++++++++++++++++++++++++++++++----
- 3 files changed, 162 insertions(+), 16 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 7d03cbc..1e0993e 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -1194,7 +1194,8 @@ output;
-         Priority-75 flows for each IP address/VIP/NAT address owned by a
-         router port connected to the switch. These flows match ARP requests
-         and ND packets for the specific IP addresses.  Matched packets are
--        forwarded only to the router that owns the IP address.
-+        forwarded only to the router that owns the IP address and, if
-+        present, to the localnet port of the logical switch.
-       </li>
- 
-       <li>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 356c543..787ca2f 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -5899,8 +5899,12 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
-     ds_put_cstr(&match, "}");
- 
-     /* Send a the packet only to the router pipeline and skip flooding it
--     * in the broadcast domain.
-+     * in the broadcast domain (except for the localnet port).
-      */
-+    if (od->localnet_port) {
-+        ds_put_format(&actions, "clone { outport = %s; output; }; ",
-+                      od->localnet_port->json_key);
-+    }
-     ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
-     ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                             ds_cstr(&match), ds_cstr(&actions), stage_hint);
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 1b6073f..4baf2e9 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -17928,13 +17928,14 @@ net_add n1
- sim_add hv1
- as hv1
- ovs-vsctl add-br br-phys
-+ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys: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=sw-agg-ext \
--    options:tx_pcap=hv1/vif1-tx.pcap \
--    options:rxq_pcap=hv1/vif1-rx.pcap \
--    ofport-request=1
-+sim_add hv2
-+as hv2
-+ovs-vsctl add-br br-phys
-+ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-+ovn_attach n1 br-phys 192.168.0.2
- 
- # One Aggregation Switch connected to two Logical networks (routers).
- ovn-nbctl ls-add sw-agg
-@@ -17950,18 +17951,66 @@ ovn-nbctl lsp-add sw-agg sw-rtr2                   \
-     -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \
-     -- lsp-set-options sw-rtr2 router-port=rtr2-sw
- 
--# Configure L3 interface IPv4 & IPv6 on both routers
-+# Localnet port on the Aggregation Switch.
-+ovn-nbctl lsp-add sw-agg sw-agg-ln
-+ovn-nbctl lsp-set-addresses sw-agg-ln unknown
-+ovn-nbctl lsp-set-type sw-agg-ln localnet
-+ovn-nbctl lsp-set-options sw-agg-ln network_name=phys
-+
-+# Configure L3 interface IPv4 & IPv6 on both routers.
- ovn-nbctl lr-add rtr1
- ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64
- 
-+ovn-nbctl lrp-add rtr1 rtr1-sw1 00:00:01:00:00:00 20.0.0.1/24 20::1/64
-+
- ovn-nbctl lr-add rtr2
- ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64
- 
-+# Configure router gateway ports.
-+ovn-nbctl lrp-set-gateway-chassis rtr1-sw hv1 20
-+ovn-nbctl lrp-set-gateway-chassis rtr2-sw hv1 20
-+
-+# One private network behind rtr1 with two VMs.
-+ovn-nbctl ls-add sw1
-+ovn-nbctl lsp-add sw1 sw1-p1 \
-+    -- lsp-set-addresses sw1-p1 00:00:00:01:00:00
-+ovn-nbctl lsp-add sw1 sw1-p2 \
-+    -- lsp-set-addresses sw1-p2 00:00:00:02:00:00
-+ovn-nbctl lsp-add sw1 sw1-rtr1                       \
-+    -- lsp-set-type sw1-rtr1 router                  \
-+    -- lsp-set-addresses sw1-rtr1 00:00:01:00:00:00  \
-+    -- lsp-set-options sw1-rtr1 router-port=rtr1-sw1
-+
-+# Bind a "VM" connected to sw-agg on hv1.
-+as hv1
-+ovs-vsctl -- add-port br-int hv1-vif0 -- \
-+    set interface hv1-vif0 external-ids:iface-id=sw-agg-ext \
-+    options:tx_pcap=hv1/vif0-tx.pcap \
-+    options:rxq_pcap=hv1/vif0-rx.pcap \
-+    ofport-request=1
-+
-+# Bind a "VM" connected to sw1 on hv1.
-+as hv1
-+ovs-vsctl -- add-port br-int hv1-vif1 -- \
-+    set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
-+    options:tx_pcap=hv1/vif1-tx.pcap \
-+    options:rxq_pcap=hv1/vif1-rx.pcap \
-+    ofport-request=2
-+
-+# Bind a "VM" connected to sw1 on hv2.
-+as hv2
-+ovs-vsctl -- add-port br-int hv1-vif2 -- \
-+    set interface hv1-vif2 external-ids:iface-id=sw1-p2 \
-+    options:tx_pcap=hv1/vif2-tx.pcap \
-+    options:rxq_pcap=hv1/vif2-rx.pcap \
-+    ofport-request=3
-+
- OVN_POPULATE_ARP
- ovn-nbctl --wait=hv sync
- 
- sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg)
- sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg)
-+r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1)
- 
- r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1)
- r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2)
-@@ -17970,9 +18019,10 @@ mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${s
- mc_key=$(printf "%04x" $mc_key)
- 
- match_sw_metadata="metadata=0x${sw_dp_key}"
-+match_r1_metadata="metadata=0x${r1_dp_key}"
- 
- # Inject ARP request for first router owned IP address.
--send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
-+send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
- 
- # Verify that the ARP request is sent only to rtr1.
- match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1"
-@@ -18001,7 +18051,7 @@ OVS_WAIT_UNTIL([
- # Inject ND_NS for ofirst router owned IP address.
- src_ipv6=00100000000000000000000000000254
- dst_ipv6=00100000000000000000000000000001
--send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
-+send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
- 
- # Verify that the ND_NS is sent only to rtr1.
- match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1"
-@@ -18038,7 +18088,7 @@ ovn-nbctl lr-lb-add rtr2 lb2-v6
- ovn-nbctl --wait=hv sync
- 
- # Inject ARP request for first router owned VIP address.
--send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
-+send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
- 
- # Verify that the ARP request is sent only to rtr1.
- match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1"
-@@ -18067,7 +18117,7 @@ OVS_WAIT_UNTIL([
- # Inject ND_NS for first router owned VIP address.
- src_ipv6=00100000000000000000000000000254
- dst_ipv6=00100000000000000000000000000011
--send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
-+send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
- 
- # Verify that the ND_NS is sent only to rtr1.
- match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11"
-@@ -18091,14 +18141,21 @@ OVS_WAIT_UNTIL([
-     test "0" = "${pkts_flooded}"
- ])
- 
--# Configure NAT on both routers
-+# Configure NAT on both routers.
- ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1
- ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1
- ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2
- ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2
- 
-+# Configure FIP1 and FIP2 on rtr1 for sw1-p1 and sw1-p2.
-+ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.121 20.0.0.11 sw1-p1 00:00:00:01:00:00
-+ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::121 20::11 sw1-p1 00:00:00:01:00:00
-+ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.122 20.0.0.12 sw1-p2 00:00:00:02:00:00
-+ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::122 20::12 sw1-p2 00:00:00:02:00:00
-+ovn-nbctl --wait=hv sync
-+
- # Inject ARP request for first router owned NAT address.
--send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
-+send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
- 
- # Verify that the ARP request is sent only to rtr1.
- match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1"
-@@ -18124,10 +18181,50 @@ OVS_WAIT_UNTIL([
-     test "0" = "${pkts_flooded}"
- ])
- 
-+# Inject ARP request for FIP1.
-+send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121)
-+
-+# Verify that the ARP request is replied to from hv1 and not hv2.
-+match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1"
-+
-+as hv1
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_arp_req}" | grep n_packets=1 -c)
-+    test "1" = "${pkts_on_rtr1}"
-+])
-+
-+as hv2
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_arp_req}" | grep n_packets=1 -c)
-+    test "0" = "${pkts_on_rtr1}"
-+])
-+
-+# Inject ARP request for FIP2.
-+send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 122)
-+
-+# Verify that the ARP request is replied to from hv2 and not hv1.
-+match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1"
-+
-+as hv2
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_arp_req}" | grep n_packets=1 -c)
-+    test "1" = "${pkts_on_rtr1}"
-+])
-+
-+as hv1
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_arp_req}" | grep n_packets=1 -c)
-+    test "0" = "${pkts_on_rtr1}"
-+])
-+
- # Inject ND_NS for first router owned IP address.
- src_ipv6=00100000000000000000000000000254
- dst_ipv6=00100000000000000000000000000111
--send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
-+send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
- 
- # Verify that the ND_NS is sent only to rtr1.
- match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111"
-@@ -18151,7 +18248,51 @@ OVS_WAIT_UNTIL([
-     test "0" = "${pkts_flooded}"
- ])
- 
--OVN_CLEANUP([hv1])
-+# Inject ND_NS for FIP1.
-+src_ipv6=00100000000000000000000000000254
-+dst_ipv6=00100000000000000000000000000121
-+send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72dd
-+
-+# Verify that the ND_NS is replied to from hv1 and not hv2.
-+match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121"
-+
-+as hv1
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
-+    test "1" = "${pkts_on_rtr1}"
-+])
-+
-+as hv2
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
-+    test "0" = "${pkts_on_rtr1}"
-+])
-+
-+# Inject ND_NS for FIP2.
-+src_ipv6=00100000000000000000000000000254
-+dst_ipv6=00100000000000000000000000000122
-+send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72db
-+
-+# Verify that the ND_NS is replied to from hv2 and not hv1.
-+match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122"
-+
-+as hv2
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
-+    test "1" = "${pkts_on_rtr1}"
-+])
-+
-+as hv1
-+OVS_WAIT_UNTIL([
-+    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
-+    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
-+    test "0" = "${pkts_on_rtr1}"
-+])
-+
-+OVN_CLEANUP([hv1], [hv2])
- AT_CLEANUP
- 
- AT_SETUP([ovn -- trace when flow cookie updated])
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-northd-Limit-IPv6-ND-NS-RA-RS-to-the-local-netwo.patch b/SOURCES/0001-ovn-northd-Limit-IPv6-ND-NS-RA-RS-to-the-local-netwo.patch
deleted file mode 100644
index 9ef0d78..0000000
--- a/SOURCES/0001-ovn-northd-Limit-IPv6-ND-NS-RA-RS-to-the-local-netwo.patch
+++ /dev/null
@@ -1,205 +0,0 @@
-From d46b2e1f3b31509849441cde28475a8d48a6624f Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Fri, 17 Apr 2020 23:54:25 +0200
-Subject: [PATCH] ovn-northd: Limit IPv6 ND NS/RA/RS to the local network.
-
-Neighbor solicitation packets for router owned IPs are replied to in
-table IN_IP_INPUT at a higher priority than flows relay IPv6 multicast
-traffic when needed. All other NS/NA packets received at this point can
-be safely dropped.
-
-However, router advertisement and router solicitation packets are
-processed at a later stage, in ND_RA_OPTIONS/ND_RA_RESPONSE. These
-packets need to be allowed in table IN_IP_INPUT.
-
-Commit 677a3ba4d66b incorrectly allowed all IPv6 multicast traffic
-destined to all-nodes in table IN_IP_INPUT. Instead, only ND_RA and
-ND_RS packets should be allowed. All others were either already
-processed or should be dropped. If multicast relay is enabled then IPv6
-multicast traffic that's not destined to reserved groups should also be
-allowed.
-
-Furthermore, router solicitation and advertisement packets that don't
-get processed in tables ND_RA_OPTIONS/ND_RA_RESPONSE should be dropped
-in IN_IP_ROUTING because they should never be routed.
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1825334
-Reported-by: Jakub Libosvar <jlibosva@redhat.com>
-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 upstream branch-20.03 commit 0924bcb07ef25f93fde683fe8f15d376eca005ec)
-
-Change-Id: I1d060e5d40f344b890974e6ad0c0960ea280f050
----
- northd/ovn-northd.8.xml | 49 +++++++++++++++++++++++++++--------------
- northd/ovn-northd.c     | 43 +++++++++++++++++++++++++-----------
- 2 files changed, 62 insertions(+), 30 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 82c86f636..efcc4b7fc 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -1668,22 +1668,6 @@ next;
-           router.
-       </li>
- 
--      <li>
--        <p>
--          A priority-87 flow explicitly allows IPv6 multicast traffic that is
--          supposed to reach the router pipeline (e.g., neighbor solicitations
--          and traffic destined to the All-Routers multicast group).
--        </p>
--      </li>
--
--      <li>
--        <p>
--          A priority-86 flow allows IP multicast traffic if
--          <ref column="options" table="Logical_Router"/>:mcast_relay='true',
--          otherwise drops it.
--        </p>
--      </li>
--
-       <li>
-         <p>
-           ICMP echo reply.  These flows reply to ICMP echo requests received
-@@ -1944,6 +1928,29 @@ nd.tll = <var>external_mac</var>;
-         packets.
-       </li>
- 
-+      <li>
-+        <p>
-+          A priority-84 flow explicitly allows IPv6 multicast traffic that is
-+          supposed to reach the router pipeline (i.e., router solicitation
-+          and router advertisement packets).
-+        </p>
-+      </li>
-+
-+      <li>
-+        <p>
-+          A priority-83 flow explicitly drops IPv6 multicast traffic that is
-+          destined to reserved multicast groups.
-+        </p>
-+      </li>
-+
-+      <li>
-+        <p>
-+          A priority-82 flow allows IP multicast traffic if
-+          <ref column="options" table="Logical_Router"/>:mcast_relay='true',
-+          otherwise drops it.
-+        </p>
-+      </li>
-+
-       <li>
-         <p>
-           UDP port unreachable.  Priority-80 flows generate ICMP port
-@@ -2440,6 +2447,13 @@ output;
-     </p>
- 
-     <ul>
-+      <li>
-+        <p>
-+          Priority-550 flow that drops IPv6 Router Solicitation/Advertisement
-+          packets that were not processed in previous tables.
-+        </p>
-+      </li>
-+
-       <li>
-         <p>
-           Priority-500 flows that match IP multicast traffic destined to
-@@ -2457,7 +2471,8 @@ output;
-           multicast group, which <code>ovn-northd</code> populates with the
-           logical ports that have
-           <ref column="options" table="Logical_Router_Port"/>
--          <code>:mcast_flood='true'</code>.
-+          <code>:mcast_flood='true'</code>. If no router ports are configured
-+          to flood multicast traffic the packets are dropped.
-         </p>
-       </li>
- 
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 1f1238d23..f7d3988d7 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -8002,17 +8002,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
- 
-         /* Priority-90 flows reply to ARP requests and ND packets. */
- 
--        /* Allow IPv6 multicast traffic that's supposed to reach the
--         * router pipeline (e.g., neighbor solicitations).
--         */
--        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 87, "ip6.mcast_flood",
--                      "next;");
--
--        /* Allow multicast if relay enabled (priority 86). */
--        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 86,
--                      "ip4.mcast || ip6.mcast",
--                      od->mcast_info.rtr.relay ? "next;" : "drop;");
--
-         /* Drop ARP packets (priority 85). ARP request packets for router's own
-          * IPs are handled with priority-90 flows.
-          * Drop IPv6 ND packets (priority 85). ND NA packets for router's own
-@@ -8021,6 +8010,21 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 85,
-                       "arp || nd", "drop;");
- 
-+        /* Allow IPv6 multicast traffic that's supposed to reach the
-+         * router pipeline (e.g., router solicitations).
-+         */
-+        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 84, "nd_rs || nd_ra",
-+                      "next;");
-+
-+        /* Drop other reserved multicast. */
-+        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 83,
-+                      "ip6.mcast_rsvd", "drop;");
-+
-+        /* Allow other multicast if relay enabled (priority 82). */
-+        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 82,
-+                      "ip4.mcast || ip6.mcast",
-+                      od->mcast_info.rtr.relay ? "next;" : "drop;");
-+
-         /* Drop Ethernet local broadcast.  By definition this traffic should
-          * not be forwarded.*/
-         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50,
-@@ -9520,7 +9524,17 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-      * advance to next table (priority 500).
-      */
-     HMAP_FOR_EACH (od, key_node, datapaths) {
--        if (!od->nbr || !od->mcast_info.rtr.relay) {
-+        if (!od->nbr) {
-+            continue;
-+        }
-+
-+        /* Drop IPv6 multicast traffic that shouldn't be forwarded,
-+         * i.e., router solicitation and router advertisement.
-+         */
-+        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 550,
-+                      "nd_rs || nd_ra", "drop;");
-+
-+        if (!od->mcast_info.rtr.relay) {
-             continue;
-         }
- 
-@@ -9551,7 +9565,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         }
- 
-         /* If needed, flood unregistered multicast on statically configured
--         * ports.
-+         * ports. Otherwise drop any multicast traffic.
-          */
-         if (od->mcast_info.rtr.flood_static) {
-             ds_clear(&actions);
-@@ -9562,6 +9576,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                                 "ip.ttl--; "
-                                 "next; "
-                           "};");
-+        } else {
-+            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 450,
-+                          "ip4.mcast || ip6.mcast", "drop;");
-         }
-     }
- 
--- 
-2.25.1
-
diff --git a/SOURCES/0001-ovn-northd-Optimize-flows-for-LB-Hairpin-traffic.patch b/SOURCES/0001-ovn-northd-Optimize-flows-for-LB-Hairpin-traffic.patch
deleted file mode 100644
index 6299b4e..0000000
--- a/SOURCES/0001-ovn-northd-Optimize-flows-for-LB-Hairpin-traffic.patch
+++ /dev/null
@@ -1,185 +0,0 @@
-From 74e1bf4dd0f6c62602ab708eabb5534a274a4d75 Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 28 Apr 2020 12:39:26 +0200
-Subject: [PATCH] ovn-northd: Optimize flows for LB Hairpin traffic.
-
-In order to detect that traffic was hairpinned due to logical switch load
-balancers we need to match on source and destination IPs of packets (and
-protocol ports potentially) in table ls_in_pre_hairpin.
-
-For this, until now, we created 2 logical flows for each backend of a load
-balancer VIP. However, in scenarios where large load balancers (i.e.,
-with large numbers of backends) are applied to multiple logical
-switches, this might generate logical flow count explosion.
-
-One optimization is to generate a single logical flow per VIP that
-combines all conditions generated for each backend. This reduces load on
-the SB DB because of lower number of logical flows and also reduces
-overall DB size because of less overhead due to other fields on the
-logical_flow records.
-
-Comparison of various performance aspects when running OVN with the NB
-database attached to the bug report on a deployment with all VIFs bound
-to a single node (62 load balancer VIPs with 513 load balancer
-backends, applied on 106 logical switches):
-
-Without this patch:
-- SB database size: 60MB
-- # of pre-hairpin logical flows: 109074
-- # of logical flows: 159414
-- ovn-controller max loop iteration time when processing SB DB: 8803ms
-- ovn-northd max loop iteration time: 3988ms
-
-With this patch:
-- SB database size: 29MB (~50% decrease)
-- # of pre-hairpin logical flows: 13250 (~88% decrease)
-- # of logical flows: 63590 (~60% decrease)
-- ovn-controller max loop iteration time when processing SB DB: 5585ms
-- ovn-northd max loop iteration time: 1594ms
-
-Reported-by: Aniket Bhat <anbhat@redhat.com>
-Reported-at: https://bugzilla.redhat.com/1827403
-Fixes: 1be8ac65bc60 ("ovn-northd: Support hairpinning for logical switch load balancing.")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 97e82ae5f135a088c9e95b49122d8217718d23f4)
-
-Change-Id: Id713209f8bd159e8ad924e91681bab784606faff
----
- northd/ovn-northd.8.xml |  4 +--
- northd/ovn-northd.c     | 79 ++++++++++++++++++++++++++++++++-----------------
- 2 files changed, 54 insertions(+), 29 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index d39e259..1f81742 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -559,14 +559,14 @@
-     <h3>Ingress Table 11: Pre-Hairpin</h3>
-     <ul>
-       <li>
--        For all configured load balancer backends a priority-2 flow that
-+        For all configured load balancer VIPs a priority-2 flow that
-         matches on traffic that needs to be hairpinned, i.e., after load
-         balancing the destination IP matches the source IP, which sets
-         <code>reg0[6] = 1 </code> and executes <code>ct_snat(VIP)</code>
-         to force replies to these packets to come back through OVN.
-       </li>
-       <li>
--        For all configured load balancer backends a priority-1 flow that
-+        For all configured load balancer VIPs a priority-1 flow that
-         matches on replies to hairpinned traffic, i.e., destination IP is VIP,
-         source IP is the backend IP and source L4 port is backend port, which
-         sets <code>reg0[6] = 1 </code> and executes <code>ct_snat;</code>.
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 0082e2e..5ada3ae 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -5542,52 +5542,77 @@ build_lb_hairpin_rules(struct ovn_datapath *od, struct hmap *lflows,
-                        struct ovn_lb *lb, struct lb_vip *lb_vip,
-                        const char *ip_match, const char *proto)
- {
-+    if (lb_vip->n_backends == 0) {
-+        return;
-+    }
-+
-+    struct ds action = DS_EMPTY_INITIALIZER;
-+    struct ds match_initiator = DS_EMPTY_INITIALIZER;
-+    struct ds match_reply = DS_EMPTY_INITIALIZER;
-+    struct ds proto_match = DS_EMPTY_INITIALIZER;
-+
-     /* Ingress Pre-Hairpin table.
--     * - Priority 2: SNAT load balanced traffic that needs to be hairpinned.
-+     * - Priority 2: SNAT load balanced traffic that needs to be hairpinned:
-+     *   - Both SRC and DST IP match backend->ip and destination port
-+     *     matches backend->port.
-      * - Priority 1: unSNAT replies to hairpinned load balanced traffic.
-+     *   - SRC IP matches backend->ip, DST IP matches LB VIP and source port
-+     *     matches backend->port.
-      */
-+    ds_put_char(&match_reply, '(');
-     for (size_t i = 0; i < lb_vip->n_backends; i++) {
-         struct lb_vip_backend *backend = &lb_vip->backends[i];
--        struct ds action = DS_EMPTY_INITIALIZER;
--        struct ds match = DS_EMPTY_INITIALIZER;
--        struct ds proto_match = DS_EMPTY_INITIALIZER;
- 
-         /* Packets that after load balancing have equal source and
--         * destination IPs should be hairpinned. SNAT them so that the reply
--         * traffic is directed also through OVN.
-+         * destination IPs should be hairpinned.
-          */
-         if (lb_vip->vip_port) {
--            ds_put_format(&proto_match, " && %s && %s.dst == %"PRIu16,
--                          proto, proto, backend->port);
-+            ds_put_format(&proto_match, " && %s.dst == %"PRIu16,
-+                          proto, backend->port);
-         }
--        ds_put_format(&match, "%s.src == %s && %s.dst == %s%s",
-+        ds_put_format(&match_initiator, "(%s.src == %s && %s.dst == %s%s)",
-                       ip_match, backend->ip, ip_match, backend->ip,
-                       ds_cstr(&proto_match));
--        ds_put_format(&action, REGBIT_HAIRPIN " = 1; ct_snat(%s);",
--                      lb_vip->vip);
--        ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_HAIRPIN, 2,
--                                ds_cstr(&match), ds_cstr(&action),
--                                &lb->nlb->header_);
- 
--        /* If the packets are replies for hairpinned traffic, UNSNAT them. */
-+        /* Replies to hairpinned traffic are originated by backend->ip:port. */
-         ds_clear(&proto_match);
--        ds_clear(&match);
-         if (lb_vip->vip_port) {
--            ds_put_format(&proto_match, " && %s && %s.src == %"PRIu16,
--                          proto, proto, backend->port);
-+            ds_put_format(&proto_match, " && %s.src == %"PRIu16, proto,
-+                          backend->port);
-         }
--        ds_put_format(&match, "%s.src == %s && %s.dst == %s%s",
--                      ip_match, backend->ip, ip_match, lb_vip->vip,
-+        ds_put_format(&match_reply, "(%s.src == %s%s)", ip_match, backend->ip,
-                       ds_cstr(&proto_match));
--        ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_HAIRPIN, 1,
--                                ds_cstr(&match),
--                                REGBIT_HAIRPIN " = 1; ct_snat;",
--                                &lb->nlb->header_);
-+        ds_clear(&proto_match);
- 
--        ds_destroy(&action);
--        ds_destroy(&match);
--        ds_destroy(&proto_match);
-+        if (i < lb_vip->n_backends - 1) {
-+            ds_put_cstr(&match_initiator, " || ");
-+            ds_put_cstr(&match_reply, " || ");
-+        }
-     }
-+    ds_put_char(&match_reply, ')');
-+
-+    /* SNAT hairpinned initiator traffic so that the reply traffic is
-+     * also directed through OVN.
-+     */
-+    ds_put_format(&action, REGBIT_HAIRPIN " = 1; ct_snat(%s);",
-+                  lb_vip->vip);
-+    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_HAIRPIN, 2,
-+                            ds_cstr(&match_initiator), ds_cstr(&action),
-+                            &lb->nlb->header_);
-+
-+    /* Replies to hairpinned traffic are destined to the LB VIP. */
-+    ds_put_format(&match_reply, " && %s.dst == %s", ip_match, lb_vip->vip);
-+
-+    /* UNSNAT replies for hairpinned traffic. */
-+    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_HAIRPIN, 1,
-+                            ds_cstr(&match_reply),
-+                            REGBIT_HAIRPIN " = 1; ct_snat;",
-+                            &lb->nlb->header_);
-+
-+    ds_destroy(&action);
-+    ds_destroy(&match_initiator);
-+    ds_destroy(&match_reply);
-+    ds_destroy(&proto_match);
- }
- 
- static void
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch b/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch
new file mode 100644
index 0000000..5eb3010
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Rate-limit-missing-chassis-log.patch
@@ -0,0 +1,39 @@
+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-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch b/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
deleted file mode 100644
index 89b38d8..0000000
--- a/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
+++ /dev/null
@@ -1,74 +0,0 @@
-From 32972260b50f39d493cc42a78d9648ed668c2aec Mon Sep 17 00:00:00 2001
-Message-Id: <32972260b50f39d493cc42a78d9648ed668c2aec.1590584789.git.lorenzo.bianconi@redhat.com>
-From: Han Zhou <hzhou@ovn.org>
-Date: Tue, 5 May 2020 23:16:09 -0700
-Subject: [PATCH ovn] ovn-northd: Remove useless flow for GW_REDIRECT.
-
-Remove the flow in lr_in_gw_redirect stage:
-
-        A priority-150 logical flow with match
-        <code>outport == <var>GW</var> &amp;&amp;
-        eth.dst == 00:00:00:00:00:00</code> has actions
-        <code>outport = <var>CR</var>; next;</code>, where
-        <var>GW</var> is the logical router distributed gateway
-        port and <var>CR</var> is the <code>chassisredirect</code>
-        port representing the instance of the logical router
-        distributed gateway port on the
-        <code>redirect-chassis</code>.
-
-The commit c0bf32d ("Manage ARP process locally in a DVR scenario") updated
-the priority-100 flow in this stage to priority 200, which makes this
-priority-150 flow useless, because whatever packets matching this flow
-would also match the priority-50 flow.
-
-Cc: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Tested-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
----
- northd/ovn-northd.8.xml | 12 ------------
- northd/ovn-northd.c     | 11 -----------
- 2 files changed, 23 deletions(-)
-
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2909,18 +2909,6 @@ icmp4 {
- 
-     <ul>
-       <li>
--        A priority-150 logical flow with match
--        <code>outport == <var>GW</var> &amp;&amp;
--        eth.dst == 00:00:00:00:00:00</code> has actions
--        <code>outport = <var>CR</var>; next;</code>, where
--        <var>GW</var> is the logical router distributed gateway
--        port and <var>CR</var> is the <code>chassisredirect</code>
--        port representing the instance of the logical router
--        distributed gateway port on the
--        <code>redirect-chassis</code>.
--      </li>
--
--      <li>
-         For each NAT rule in the OVN Northbound database that can
-         be handled in a distributed manner, a priority-200 logical
-         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -10143,17 +10143,6 @@ build_lrouter_flows(struct hmap *datapat
-             ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
-                                     ds_cstr(&match), ds_cstr(&actions),
-                                     stage_hint);
--
--            /* If the Ethernet destination has not been resolved,
--             * redirect to the central instance of the l3dgw_port.
--             * Such traffic will be replaced by an ARP request or ND
--             * Neighbor Solicitation in the ARP request ingress
--             * table, before being redirected to the central instance.
--             */
--            ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
--            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
--                                    ds_cstr(&match), ds_cstr(&actions),
--                                    stage_hint);
-         }
- 
-         /* Packets are allowed by default. */
diff --git a/SOURCES/0001-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch b/SOURCES/0001-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
deleted file mode 100644
index ffc9db8..0000000
--- a/SOURCES/0001-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From 356501f3246ddb99aef8cd6016467b7c1861b3ff Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Tue, 24 Mar 2020 14:35:41 +0100
-Subject: [PATCH ovn] ovn.at: Fix ARP test that fails due to timing.
-
-The test for "ARP/ND request broadcast limiting" checks that injected
-ARP packets are not flooded using the MC_FLOOD multicast group. However,
-this introduces a race condition in the test because GARPs generated by
-OVN would also hit the same openflow rules.
-
-Remove the checks that use the MC_FLOOD group. They are also redundant
-as the rest of the test checks that packets are forwarded according to
-the newly added, higher priority rules.
-
-Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-(cherry picked from upstream commit 598a07cd240d7d01de3d7f04ca7abc58a33977a1)
-
-Change-Id: I4519f441245a5f1ecf7da73257f97aff0c7bc967
----
- tests/ovn.at | 33 ---------------------------------
- 1 file changed, 33 deletions(-)
-
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 4baf2e9..9a44f0a 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -18015,9 +18015,6 @@ r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1)
- r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1)
- r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2)
- 
--mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood")
--mc_key=$(printf "%04x" $mc_key)
--
- match_sw_metadata="metadata=0x${sw_dp_key}"
- match_r1_metadata="metadata=0x${r1_dp_key}"
- 
-@@ -18042,11 +18039,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Inject ND_NS for ofirst router owned IP address.
- src_ipv6=00100000000000000000000000000254
-@@ -18069,11 +18061,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Configure load balancing on both routers.
- ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1
-@@ -18108,11 +18095,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Inject ND_NS for first router owned VIP address.
- src_ipv6=00100000000000000000000000000254
-@@ -18135,11 +18117,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Configure NAT on both routers.
- ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1
-@@ -18175,11 +18152,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Inject ARP request for FIP1.
- send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121)
-@@ -18242,11 +18214,6 @@ OVS_WAIT_UNTIL([
-     grep n_packets=1 -c)
-     test "0" = "${pkts_to_rtr2}"
- ])
--OVS_WAIT_UNTIL([
--    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
--    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
--    test "0" = "${pkts_flooded}"
--])
- 
- # Inject ND_NS for FIP1.
- src_ipv6=00100000000000000000000000000254
--- 
-1.8.3.1
-
diff --git a/SOURCES/0001-pinctrl-Fix-icmp6-packet-corruption-issue.patch b/SOURCES/0001-pinctrl-Fix-icmp6-packet-corruption-issue.patch
deleted file mode 100644
index adc0e60..0000000
--- a/SOURCES/0001-pinctrl-Fix-icmp6-packet-corruption-issue.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From a069dc4106bc641cbc8596e90b95308950b0bffa Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Tue, 12 May 2020 14:21:33 +0530
-Subject: [PATCH] pinctrl: Fix icmp6 packet corruption issue
-
-The commit f792b1a00b43("Fix ACL reject action for UDP packets.")
-didn't updated the 'struct ip6_hdr' pointer after calling
-dp_packet_put(), as dp_packet_put() can reallocate memory making the
-old references to packet pointers invalid.
-
-This patch fixes this issue.
-
-Fixes: f792b1a00b43("Fix ACL reject action for UDP packets.")
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1834655
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c | 4 ++--
- tests/system-ovn.at  | 2 +-
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 6b0ac3483..d976ec82b 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -1570,8 +1570,6 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
-         }
-         ih->icmp6_base.icmp6_cksum = 0;
- 
--        nh = dp_packet_l3(&packet);
--
-         /* RFC 4443: 3.1.
-          *
-          * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-@@ -1594,9 +1592,11 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
-         }
- 
-         dp_packet_put(&packet, in_ip, in_ip_len);
-+        nh = dp_packet_l3(&packet);
-         nh->ip6_plen = htons(ICMP6_DATA_HEADER_LEN + in_ip_len);
- 
-         icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
-+        ih = dp_packet_l4(&packet);
-         ih->icmp6_base.icmp6_cksum = csum_finish(
-             csum_continue(icmpv6_csum, ih,
-                           in_ip_len + ICMP6_DATA_HEADER_LEN));
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 9a5ef1ec3..9ae6c6b1f 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3967,7 +3967,7 @@ OVS_WAIT_UNTIL([
- NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej udp port 90 > sw0-p1-rej-udp.pcap &], [0])
- NS_CHECK_EXEC([sw0-p1-rej], [tcpdump -n -c 1 -i sw0-p1-rej icmp > sw0-p1-rej-icmp.pcap &], [0])
- 
--echo "foo" > foo
-+printf '.%.0s' {1..100} > foo
- OVS_WAIT_UNTIL([
-     ip netns exec sw0-p1-rej nc -u 10.0.0.4 90 < foo
-     c=$(cat sw0-p1-rej-icmp.pcap | grep \
--- 
-2.26.2
-
diff --git a/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch b/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch
new file mode 100644
index 0000000..522da2d
--- /dev/null
+++ b/SOURCES/0001-pinctrl-Fix-incorrect-warning-message-for-multicast-.patch
@@ -0,0 +1,89 @@
+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/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch b/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
deleted file mode 100644
index 5932a4f..0000000
--- a/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From 88677e5c2051a1880db61cbb22e4ab48659bc1a8 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 17 Apr 2020 11:45:56 +0530
-Subject: [PATCH] pinctrl: Handle service monitors even if the lport doesn't
- have IPv4 addresses set.
-
-If a logical port is only configured with MAC address(es) in the
-Logical_Switch_Port.addresses, pinctrl is ignoring the service monitors
-configured for that logical port. This patch allows that.
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801058
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c |  5 +++++
- tests/ovn.at         | 31 +++++++++++++++++++++++++++++++
- tests/system-ovn.at  |  4 ++--
- 3 files changed, 38 insertions(+), 2 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index a053938ec..8703641c2 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -5786,6 +5786,11 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
- 
-             if (mac_found) {
-                 break;
-+            } else if (!laddrs.n_ipv4_addrs) {
-+                /* IPv4 address(es) are not configured. Use the first mac. */
-+                ea = laddrs.ea;
-+                mac_found = true;
-+                break;
-             }
-         }
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 3bc435e6d..0f02e8144 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -17918,6 +17918,37 @@ AT_CHECK([cat lflows.txt], [0], [dnl
-   table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;)
- ])
- 
-+# Delete sw0-p1
-+ovn-nbctl lsp-del sw0-p1
-+
-+OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
-+service_monitor | sed '/^$/d' | wc -l)])
-+
-+# Add back sw0-p1 but without any IP address.
-+ovn-nbctl lsp-add sw0 sw0-p1
-+ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" -- \
-+lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
-+
-+OVS_WAIT_UNTIL([test 2 = $(ovn-sbctl --bare --columns status find \
-+service_monitor | grep offline | wc -l)])
-+
-+ovn-nbctl lsp-del sw0-p1
-+ovn-nbctl lsp-del sw1-p1
-+OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns _uuid find \
-+service_monitor | sed '/^$/d' | wc -l)])
-+
-+# Add back sw0-p1 but without any address set.
-+ovn-nbctl lsp-add sw0 sw0-p1
-+
-+OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
-+service_monitor | sed '/^$/d' | wc -l)])
-+
-+OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
-+service_monitor | grep offline | wc -l)])
-+
-+OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
-+service_monitor | grep online | wc -l)])
-+
- OVN_CLEANUP([hv1], [hv2])
- AT_CLEANUP
- 
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 000b3f13b..bdb9768d2 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3356,8 +3356,8 @@ start_daemon ovn-controller
- ovn-nbctl ls-add sw0
- 
- ovn-nbctl lsp-add sw0 sw0-p1
--ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
--ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
-+ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03"
-+ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
- 
- ovn-nbctl lsp-add sw0 sw0-p2
- ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
--- 
-2.25.1
-
diff --git a/SOURCES/0001-system-tests-Fix-occasional-failure-of-the-test-Load.patch b/SOURCES/0001-system-tests-Fix-occasional-failure-of-the-test-Load.patch
deleted file mode 100644
index 335fe6d..0000000
--- a/SOURCES/0001-system-tests-Fix-occasional-failure-of-the-test-Load.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From b1a0a1aecc5e49e465f7ff42b179f6c5ce683397 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 28 Feb 2020 22:09:43 +0530
-Subject: [PATCH 1/3] system tests: Fix occasional failure of the test - "Load
- balancer health checks".
-
-Due to some timing issues, ovn-controller logs the warning message -
-"handle service check: Service monitor not found". This can happen
-if the service monitor is created in SB DB before the port for
-the service is bound. This patch adds this warning message to the
-WHITELIST of OVS_TRAFFIC_VSWITCHD_STOP.
-
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
----
- tests/system-ovn.at | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 53da910cb..9ed3df754 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3462,7 +3462,8 @@ OVS_APP_EXIT_AND_WAIT([ovn-northd])
- 
- as
- OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
--/connection dropped.*/d"])
-+/connection dropped.*/d
-+/Service monitor not found.*/d"])
- 
- AT_CLEANUP
- 
--- 
-2.24.1
-
diff --git a/SOURCES/0001-tests-Wait-up-to-OVS_CTL_TIMEOUT-seconds.patch b/SOURCES/0001-tests-Wait-up-to-OVS_CTL_TIMEOUT-seconds.patch
deleted file mode 100644
index bedc1da..0000000
--- a/SOURCES/0001-tests-Wait-up-to-OVS_CTL_TIMEOUT-seconds.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 2e84aada0b45d2f8739c2fdbc351098fc1c09c26 Mon Sep 17 00:00:00 2001
-Message-Id: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
-From: Ilya Maximets <i.maximets@ovn.org>
-Date: Wed, 6 Nov 2019 17:29:58 +0100
-Subject: [PATCH 1/3] tests: Wait up to OVS_CTL_TIMEOUT seconds.
-
-While running tests under valgrind, it could take more than 10 seconds
-for process to disappear after successful 'ovs-appctl exit' command.
-
-Same applies to some other events that tests are waiting for with
-OVS_WAIT macro.  This makes tests to fail frequently under valgrind.
-
-Using OVS_CTL_TIMEOUT variable instead of constant 10 seconds seems
-reasonable to avoid this issue because it controls timeouts of all
-control utilities and needs to be adjusted while running under valgrind
-anyway.
-
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-Acked-by: Aaron Conole <aconole@redhat.com>
-Signed-off-by: William Tu <u9012063@gmail.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
----
- tests/ovs-macros.at | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
-index b2e619f76..3dcf8f96d 100644
---- a/tests/ovs-macros.at
-+++ b/tests/ovs-macros.at
-@@ -208,9 +208,9 @@ ovs_wait () {
-     sleep 0.1
-     if ovs_wait_cond; then echo "$1: wait succeeded quickly" >&AS_MESSAGE_LOG_FD; return 0; fi
- 
--    # Then wait up to 10 seconds.
-+    # Then wait up to OVS_CTL_TIMEOUT seconds.
-     local d
--    for d in 1 2 3 4 5 6 7 8 9 10; do
-+    for d in `seq 1 "$OVS_CTL_TIMEOUT"`; do
-         sleep 1
-         if ovs_wait_cond; then echo "$1: wait succeeded after $d seconds" >&AS_MESSAGE_LOG_FD; return 0; fi
-     done
--- 
-2.25.2
-
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
new file mode 100644
index 0000000..594f6f7
--- /dev/null
+++ b/SOURCES/0002-Add-support-for-DHCP-domain-search-option-119.patch
@@ -0,0 +1,309 @@
+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-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch b/SOURCES/0002-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
deleted file mode 100644
index f11664d..0000000
--- a/SOURCES/0002-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
+++ /dev/null
@@ -1,400 +0,0 @@
-From e982e99e5ee4eb9b65e0d3fe59d0975505ea625b Mon Sep 17 00:00:00 2001
-From: Ihar Hrachyshka <ihrachys@redhat.com>
-Date: Thu, 5 Mar 2020 20:44:24 -0500
-Subject: [PATCH 2/3] Broadcast DHCPREPLY when BROADCAST flag is set
-
-As per RFC2131, section 4.1:
-   A server or relay agent sending or relaying a DHCP message directly
-   to a DHCP client (i.e., not to a relay agent specified in the
-   'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags'
-   field.  If this bit is set to 1, the DHCP message SHOULD be sent as
-   an IP broadcast using an IP broadcast address (preferably 0xffffffff)
-   as the IP destination address and the link-layer broadcast address as
-   the link-layer destination address.
-
-This patch changes destination IP address to 255.255.255.255 when client
-set BROADCAST flag in their DHCPREQUEST. Note: the offered IP address is
-still part of the DHCP payload.
-
-While the new DHCP response is sent as a broadcast IP frame, it's
-handled locally, as any other DHCP reply by the native responder.
-Meaning, the reply is sent to the client port that initiated the DHCP
-session only.
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801006
-
-Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
-
-(cherry-picked from upstream commit 4f8045b3b5f2c3376f86f5edc4e3f7507c2b1148)
----
- controller/pinctrl.c    | 15 +++++++
- lib/ovn-l7.h            |  2 +
- northd/ovn-northd.8.xml |  5 +--
- northd/ovn-northd.c     |  7 ++-
- tests/ovn.at            | 98 +++++++++++++++++++++++++++++------------
- 5 files changed, 93 insertions(+), 34 deletions(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index dc8d3fd28..8bf19776c 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -966,6 +966,12 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
-     dp_packet_uninit(&packet);
- }
- 
-+static bool
-+is_dhcp_flags_broadcast(ovs_be16 flags)
-+{
-+    return flags & htons(DHCP_BROADCAST_FLAG);
-+}
-+
- /* Called with in the pinctrl_handler thread context. */
- static void
- pinctrl_handle_put_dhcp_opts(
-@@ -1190,7 +1196,16 @@ pinctrl_handle_put_dhcp_opts(
- 
-     udp->udp_len = htons(new_l4_size);
- 
-+    /* Send a broadcast IP frame when BROADCAST flag is set. */
-     struct ip_header *out_ip = dp_packet_l3(&pkt_out);
-+    ovs_be32 ip_dst;
-+    if (!is_dhcp_flags_broadcast(dhcp_data->flags)) {
-+        ip_dst = *offer_ip;
-+    } else {
-+        ip_dst = htonl(0xffffffff);
-+    }
-+    put_16aligned_be32(&out_ip->ip_dst, ip_dst);
-+
-     out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
-     udp->udp_csum = 0;
-     /* Checksum needs to be initialized to zero. */
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index f20d86c39..931e6ffcf 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -34,6 +34,8 @@ struct gen_opts_map {
-     size_t code;
- };
- 
-+#define DHCP_BROADCAST_FLAG 0x8000
-+
- #define DHCP_OPTION(NAME, CODE, TYPE) \
-     {.name = NAME, .code = CODE, .type = TYPE}
- 
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index d80f8cf8d..b6cfa3e90 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -937,7 +937,6 @@ next;
-         <pre>
- eth.dst = eth.src;
- eth.src = <var>E</var>;
--ip4.dst = <var>A</var>;
- ip4.src = <var>S</var>;
- udp.src = 67;
- udp.dst = 68;
-@@ -948,8 +947,8 @@ output;
- 
-         <p>
-           where <var>E</var> is the server MAC address and <var>S</var> is the
--          server IPv4 address defined in the DHCPv4 options and <var>A</var> is
--          the IPv4 address defined in the logical port's addresses column.
-+          server IPv4 address defined in the DHCPv4 options. Note that
-+          <code>ip4.dst</code> field is handled by <code>put_dhcp_opts</code>.
-         </p>
- 
-         <p>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 0d43322cf..217a8c894 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -4276,10 +4276,9 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
-     ds_put_cstr(options_action, "); next;");
- 
-     ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
--                  "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; "
--                  "udp.dst = 68; outport = inport; flags.loopback = 1; "
--                  "output;",
--                  server_mac, IP_ARGS(offer_ip), server_ip);
-+                  "ip4.src = %s; udp.src = 67; udp.dst = 68; "
-+                  "outport = inport; flags.loopback = 1; output;",
-+                  server_mac, server_ip);
- 
-     ds_put_format(ipv4_addr_match,
-                   "ip4.src == "IP_FMT" && ip4.dst == {%s, 255.255.255.255}",
-diff --git a/tests/ovn.at b/tests/ovn.at
-index a04f22c4c..8de4b5ceb 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -4595,10 +4595,11 @@ sleep 2
- as hv1 ovs-vsctl show
- 
- # This shell function sends a DHCP request packet
--# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP REQUEST_IP ...
-+# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ...
- test_dhcp() {
--    local inport=$1 src_mac=$2 dhcp_type=$3 ciaddr=$4 offer_ip=$5 request_ip=$6 use_ip=$7
--    shift; shift; shift; shift; shift; shift; shift;
-+    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8
-+    shift; shift; shift; shift; shift; shift; shift; shift;
-+
-     if test $use_ip != 0; then
-         src_ip=$1
-         dst_ip=$2
-@@ -4607,6 +4608,7 @@ test_dhcp() {
-         src_ip=`ip_to_hex 0 0 0 0`
-         dst_ip=`ip_to_hex 255 255 255 255`
-     fi
-+
-     if test $request_ip != 0; then
-         ip_len=0120
-         udp_len=010b
-@@ -4614,10 +4616,19 @@ test_dhcp() {
-         ip_len=011a
-         udp_len=0106
-     fi
-+
-+    if test $broadcast != 0; then
-+        flags=8000
-+        reply_dst_ip=`ip_to_hex 255 255 255 255`
-+    else
-+        flags=0000
-+        reply_dst_ip=${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
--    request=${request}010106006359aa7600000000${ciaddr}000000000000000000000000${src_mac}
-+    request=${request}010106006359aa760000${flags}${ciaddr}000000000000000000000000${src_mac}
-     # client hardware padding
-     request=${request}00000000000000000000
-     # server hostname
-@@ -4655,10 +4666,10 @@ test_dhcp() {
-         ip_len=$(printf "%x" $ip_len)
-         udp_len=$(printf "%x" $udp_len)
-         # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
--        local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
-+        local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip}
-         # udp header and dhcp header.
-         # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
--        reply=${reply}004300440${udp_len}0000020106006359aa7600000000${ciaddr}
-+        reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}${ciaddr}
-         # your ip address; 0 for NAK
-         if test $dhcp_reply_type = 06; then
-             reply=${reply}00000000
-@@ -4729,7 +4740,7 @@ server_ip=`ip_to_hex 10 0 0 1`
- ciaddr=`ip_to_hex 0 0 0 0`
- request_ip=0
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 1 f00000000001 01 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
-+test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 1.
- OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4755,7 +4766,7 @@ server_ip=`ip_to_hex 10 0 0 1`
- ciaddr=`ip_to_hex 0 0 0 0`
- request_ip=$offer_ip
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 2.
- OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4779,7 +4790,7 @@ server_ip=`ip_to_hex 10 0 0 1`
- ciaddr=`ip_to_hex 0 0 0 0`
- request_ip=`ip_to_hex 10 0 0 7`
- expected_dhcp_opts=""
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 3.
- OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4803,7 +4814,7 @@ rm -f 2.expected
- ciaddr=`ip_to_hex 0 0 0 0`
- offer_ip=0
- request_ip=0
--test_dhcp 2 f00000000002 08 $ciaddr $offer_ip $request_ip 0 1 1
-+test_dhcp 2 f00000000002 08 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`])
-@@ -4820,12 +4831,12 @@ rm -f 2.expected
- # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
- 
- ciaddr=`ip_to_hex 0 0 0 0`
--test_dhcp 3 f00000000003 01 $ciaddr 0 0 4 0
-+test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0
- 
- # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
- # this lport.
- ciaddr=`ip_to_hex 0 0 0 0`
--test_dhcp 4 f00000000004 01 $ciaddr 0 0 3 0
-+test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0
- 
- # NXT_RESUMEs should be 4.
- OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4842,7 +4853,7 @@ request_ip=0
- src_ip=$offer_ip
- dst_ip=$server_ip
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-+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 5.
- OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4868,7 +4879,7 @@ request_ip=0
- src_ip=$offer_ip
- dst_ip=`ip_to_hex 255 255 255 255`
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-+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 6.
- OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4894,7 +4905,7 @@ request_ip=0
- src_ip=$offer_ip
- dst_ip=`ip_to_hex 255 255 255 255`
- expected_dhcp_opts=""
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 7.
- OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4920,7 +4931,7 @@ request_ip=0
- src_ip=$offer_ip
- dst_ip=`ip_to_hex 255 255 255 255`
- expected_dhcp_opts=""
--test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
-+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 8.
- OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4942,7 +4953,7 @@ rm -f 2.expected
- ciaddr=`ip_to_hex 0 0 0 0`
- src_ip=`ip_to_hex 10 0 0 6`
- dst_ip=`ip_to_hex 10 0 0 4`
--test_dhcp 2 f00000000002 03 $ciaddr 0 0 1 $src_ip $dst_ip 1
-+test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1
- 
- # NXT_RESUMEs should be 8.
- OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-@@ -4950,6 +4961,29 @@ OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
- # vif1-tx.pcap should have received the DHCPv4 request packet
- OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
- 
-+reset_pcap_file hv1-vif1 hv1/vif1
-+reset_pcap_file hv1-vif2 hv1/vif2
-+rm -f 1.expected
-+rm -f 2.expected
-+
-+# Send DHCPDISCOVER with BROADCAST flag on.
-+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=330400000e100104ffffff0003040a00000136040a000001
-+test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
-+
-+# NXT_RESUMEs should be 9.
-+OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-+
-+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-+cat 1.expected | cut -c -48 > expout
-+AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
-+# Skipping the IPv4 checksum.
-+cat 1.expected | cut -c 53- > expout
-+AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
-+
- OVN_CLEANUP([hv1])
- 
- AT_CLEANUP
-@@ -13220,10 +13254,11 @@ as hv1
- ovs-vsctl show
- 
- # This shell function sends a DHCP request packet
--# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ...
-+# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST OFFER_IP ...
- test_dhcp() {
--    local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5
--    shift; shift; shift; shift; shift;
-+    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 offer_ip=$5 use_ip=$6
-+    shift; shift; shift; shift; shift; shift;
-+
-     if test $use_ip != 0; then
-         src_ip=$1
-         dst_ip=$2
-@@ -13232,10 +13267,19 @@ test_dhcp() {
-         src_ip=`ip_to_hex 0 0 0 0`
-         dst_ip=`ip_to_hex 255 255 255 255`
-     fi
-+
-+    if test $broadcast != 0; then
-+        flags=8000
-+        reply_dst_ip=`ip_to_hex 255 255 255 255`
-+    else
-+        flags=0000
-+        reply_dst_ip=${offer_ip}
-+    fi
-+
-     local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip}
-     # udp header and dhcp header
-     request=${request}0044004300fc0000
--    request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac}
-+    request=${request}010106006359aa760000${flags}00000000000000000000000000000000${src_mac}
-     # client hardware padding
-     request=${request}00000000000000000000
-     # server hostname
-@@ -13259,10 +13303,10 @@ test_dhcp() {
-     ip_len=$(printf "%x" $ip_len)
-     udp_len=$(printf "%x" $udp_len)
-     # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
--    local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
-+    local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip}
-     # udp header and dhcp header.
-     # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
--    reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000
-+    reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}00000000
-     # your ip address
-     reply=${reply}${offer_ip}
-     # next server ip address, relay agent ip address, client mac address
-@@ -13381,7 +13425,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
- server_ip=`ip_to_hex 10 0 0 1`
- server_mac=ff1000000001
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-+test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
- $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 1 in hv1.
-@@ -13479,7 +13523,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
- server_ip=`ip_to_hex 10 0 0 1`
- server_mac=ff1000000001
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-+test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
- $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 2 in hv1.
-@@ -13589,7 +13633,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
- server_ip=`ip_to_hex 10 0 0 1`
- server_mac=ff1000000001
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-+test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
- $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 3 in hv1.
-@@ -13669,7 +13713,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
- server_ip=`ip_to_hex 10 0 0 1`
- server_mac=ff1000000001
- expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
--test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-+test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
- $expected_dhcp_opts
- 
- # NXT_RESUMEs should be 4 in hv1.
--- 
-2.24.1
-
diff --git a/SOURCES/0002-Create-daemon-pidfiles-in-ovn-run-dir.patch b/SOURCES/0002-Create-daemon-pidfiles-in-ovn-run-dir.patch
deleted file mode 100644
index 45129b5..0000000
--- a/SOURCES/0002-Create-daemon-pidfiles-in-ovn-run-dir.patch
+++ /dev/null
@@ -1,351 +0,0 @@
-From d80be07ca318603524508402e044474571c1f642 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 23 Apr 2020 12:53:23 +0530
-Subject: [PATCH 2/2] Create daemon pidfiles in ovn run dir.
-
-If an OVN service is started with --pidfile option, the pidfile
-is created in the ovs rundir. This patch fixes it by using the ovn rundir
-if either the pidfile is not specified or if specified, it is not
-absolute path.
-
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Acked-by: Dumitru Ceara <dceara@redhat.com>
-Signed-off-by: Mark Michelson <mmichels@redhat.com>
----
- controller-vtep/ovn-controller-vtep.c |  6 +--
- controller/ovn-controller.c           |  6 +--
- ic/ovn-ic.c                           |  6 +--
- lib/ovn-util.c                        | 26 ++++++++++++
- lib/ovn-util.h                        | 60 +++++++++++++++++++++++++++
- northd/ovn-northd.c                   |  6 +--
- utilities/ovn-nbctl.c                 | 10 ++---
- utilities/ovn-trace.c                 |  6 +--
- 8 files changed, 106 insertions(+), 20 deletions(-)
-
-diff --git a/controller-vtep/ovn-controller-vtep.c b/controller-vtep/ovn-controller-vtep.c
-index 253a709ab..c13280bc0 100644
---- a/controller-vtep/ovn-controller-vtep.c
-+++ b/controller-vtep/ovn-controller-vtep.c
-@@ -169,7 +169,7 @@ parse_options(int argc, char *argv[])
-         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
-         OPT_BOOTSTRAP_CA_CERT,
-         VLOG_OPTION_ENUMS,
--        DAEMON_OPTION_ENUMS,
-+        OVN_DAEMON_OPTION_ENUMS,
-         SSL_OPTION_ENUMS,
-     };
- 
-@@ -179,7 +179,7 @@ parse_options(int argc, char *argv[])
-         {"help", no_argument, NULL, 'h'},
-         {"version", no_argument, NULL, 'V'},
-         VLOG_LONG_OPTIONS,
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
-         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
-@@ -212,7 +212,7 @@ parse_options(int argc, char *argv[])
-             exit(EXIT_SUCCESS);
- 
-         VLOG_OPTION_HANDLERS
--        DAEMON_OPTION_HANDLERS
-+        OVN_DAEMON_OPTION_HANDLERS
-         STREAM_SSL_OPTION_HANDLERS
- 
-         case OPT_PEER_CA_CERT:
-diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index 4d21ba0fd..6ff897325 100644
---- a/controller/ovn-controller.c
-+++ b/controller/ovn-controller.c
-@@ -2268,7 +2268,7 @@ parse_options(int argc, char *argv[])
-         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
-         OPT_BOOTSTRAP_CA_CERT,
-         VLOG_OPTION_ENUMS,
--        DAEMON_OPTION_ENUMS,
-+        OVN_DAEMON_OPTION_ENUMS,
-         SSL_OPTION_ENUMS,
-     };
- 
-@@ -2276,7 +2276,7 @@ parse_options(int argc, char *argv[])
-         {"help", no_argument, NULL, 'h'},
-         {"version", no_argument, NULL, 'V'},
-         VLOG_LONG_OPTIONS,
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
-         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
-@@ -2301,7 +2301,7 @@ parse_options(int argc, char *argv[])
-             exit(EXIT_SUCCESS);
- 
-         VLOG_OPTION_HANDLERS
--        DAEMON_OPTION_HANDLERS
-+        OVN_DAEMON_OPTION_HANDLERS
-         STREAM_SSL_OPTION_HANDLERS
- 
-         case OPT_PEER_CA_CERT:
-diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
-index d931ca50f..a1ed25623 100644
---- a/ic/ovn-ic.c
-+++ b/ic/ovn-ic.c
-@@ -1461,7 +1461,7 @@ static void
- parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
- {
-     enum {
--        DAEMON_OPTION_ENUMS,
-+        OVN_DAEMON_OPTION_ENUMS,
-         VLOG_OPTION_ENUMS,
-         SSL_OPTION_ENUMS,
-     };
-@@ -1474,7 +1474,7 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-         {"help", no_argument, NULL, 'h'},
-         {"options", no_argument, NULL, 'o'},
-         {"version", no_argument, NULL, 'V'},
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         VLOG_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {NULL, 0, NULL, 0},
-@@ -1490,7 +1490,7 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-         }
- 
-         switch (c) {
--        DAEMON_OPTION_HANDLERS;
-+        OVN_DAEMON_OPTION_HANDLERS;
-         VLOG_OPTION_HANDLERS;
-         STREAM_SSL_OPTION_HANDLERS;
- 
-diff --git a/lib/ovn-util.c b/lib/ovn-util.c
-index 1b30c2e9a..3482edb8d 100644
---- a/lib/ovn-util.c
-+++ b/lib/ovn-util.c
-@@ -15,6 +15,7 @@
- #include <config.h>
- #include <unistd.h>
- 
-+#include "daemon.h"
- #include "ovn-util.h"
- #include "ovn-dirs.h"
- #include "openvswitch/vlog.h"
-@@ -394,6 +395,31 @@ get_abs_unix_ctl_path(const char *path)
-     return abs_path;
- }
- 
-+void
-+ovn_set_pidfile(const char *name)
-+{
-+    char *pidfile_name = NULL;
-+
-+#ifndef _WIN32
-+    pidfile_name = name ? abs_file_name(ovn_rundir(), name)
-+                        : xasprintf("%s/%s.pid", ovn_rundir(), program_name);
-+#else
-+    if (name) {
-+        if (strchr(name, ':')) {
-+            pidfile_name = xstrdup(name);
-+        } else {
-+            pidfile_name = xasprintf("%s/%s", ovn_rundir(), name);
-+        }
-+    } else {
-+        pidfile_name = xasprintf("%s/%s.pid", ovn_rundir(), program_name);
-+    }
-+#endif
-+
-+    /* Call openvswitch lib function. */
-+    set_pidfile(pidfile_name);
-+    free(pidfile_name);
-+}
-+
- /* l3gateway, chassisredirect, and patch
-  * are not in this list since they are
-  * only set in the SB DB by northd
-diff --git a/lib/ovn-util.h b/lib/ovn-util.h
-index 4076e8b9a..ec5f2cf5a 100644
---- a/lib/ovn-util.h
-+++ b/lib/ovn-util.h
-@@ -112,6 +112,7 @@ uint32_t ovn_allocate_tnlid(struct hmap *set, const char *name, uint32_t min,
-                             uint32_t max, uint32_t *hint);
- 
- char *ovn_chassis_redirect_name(const char *port_name);
-+void ovn_set_pidfile(const char *name);
- 
- /* An IPv4 or IPv6 address */
- struct v46_ip {
-@@ -129,4 +130,63 @@ bool ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2);
-  * Caller must free the returned string.
-  */
- char *str_tolower(const char *orig);
-+
-+/* OVN daemon options. Taken from ovs/lib/daemon.h. */
-+#define OVN_DAEMON_OPTION_ENUMS                     \
-+    OVN_OPT_DETACH,                                 \
-+    OVN_OPT_NO_SELF_CONFINEMENT,                    \
-+    OVN_OPT_NO_CHDIR,                               \
-+    OVN_OPT_OVERWRITE_PIDFILE,                      \
-+    OVN_OPT_PIDFILE,                                \
-+    OVN_OPT_MONITOR,                                \
-+    OVN_OPT_USER_GROUP
-+
-+#define OVN_DAEMON_LONG_OPTIONS                                              \
-+        {"detach",            no_argument, NULL, OVN_OPT_DETACH},            \
-+        {"no-self-confinement", no_argument, NULL,                           \
-+         OVN_OPT_NO_SELF_CONFINEMENT},                                       \
-+        {"no-chdir",          no_argument, NULL, OVN_OPT_NO_CHDIR},          \
-+        {"pidfile",           optional_argument, NULL, OVN_OPT_PIDFILE},     \
-+        {"overwrite-pidfile", no_argument, NULL, OVN_OPT_OVERWRITE_PIDFILE}, \
-+        {"monitor",           no_argument, NULL, OVN_OPT_MONITOR},           \
-+        {"user",              required_argument, NULL, OVN_OPT_USER_GROUP}
-+
-+#define OVN_DAEMON_OPTION_HANDLERS                  \
-+        case OVN_OPT_DETACH:                        \
-+            set_detach();                           \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_NO_SELF_CONFINEMENT:           \
-+            daemon_disable_self_confinement();      \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_NO_CHDIR:                      \
-+            set_no_chdir();                         \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_PIDFILE:                       \
-+            ovn_set_pidfile(optarg);                \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_OVERWRITE_PIDFILE:             \
-+            ignore_existing_pidfile();              \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_MONITOR:                       \
-+            daemon_set_monitor();                   \
-+            break;                                  \
-+                                                    \
-+        case OVN_OPT_USER_GROUP:                    \
-+            daemon_set_new_user(optarg);            \
-+            break;
-+
-+#define OVN_DAEMON_OPTION_CASES                     \
-+        case OVN_OPT_DETACH:                        \
-+        case OVN_OPT_NO_SELF_CONFINEMENT:           \
-+        case OVN_OPT_NO_CHDIR:                      \
-+        case OVN_OPT_PIDFILE:                       \
-+        case OVN_OPT_OVERWRITE_PIDFILE:             \
-+        case OVN_OPT_MONITOR:                       \
-+        case OVN_OPT_USER_GROUP:
-+
- #endif
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 515722c5d..d3d481ab8 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -11607,7 +11607,7 @@ static void
- parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
- {
-     enum {
--        DAEMON_OPTION_ENUMS,
-+        OVN_DAEMON_OPTION_ENUMS,
-         VLOG_OPTION_ENUMS,
-         SSL_OPTION_ENUMS,
-     };
-@@ -11618,7 +11618,7 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-         {"help", no_argument, NULL, 'h'},
-         {"options", no_argument, NULL, 'o'},
-         {"version", no_argument, NULL, 'V'},
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         VLOG_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {NULL, 0, NULL, 0},
-@@ -11634,7 +11634,7 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-         }
- 
-         switch (c) {
--        DAEMON_OPTION_HANDLERS;
-+        OVN_DAEMON_OPTION_HANDLERS;
-         VLOG_OPTION_HANDLERS;
-         STREAM_SSL_OPTION_HANDLERS;
- 
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index a88c1ddc2..3f4731d40 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -324,7 +324,7 @@ enum {
-     OPT_NO_SHUFFLE_REMOTES,
-     OPT_BOOTSTRAP_CA_CERT,
-     MAIN_LOOP_OPTION_ENUMS,
--    DAEMON_OPTION_ENUMS,
-+    OVN_DAEMON_OPTION_ENUMS,
-     VLOG_OPTION_ENUMS,
-     TABLE_OPTION_ENUMS,
-     SSL_OPTION_ENUMS,
-@@ -428,7 +428,7 @@ get_all_options(void)
-         {"version", no_argument, NULL, 'V'},
-         {"unixctl", required_argument, NULL, 'u'},
-         MAIN_LOOP_LONG_OPTIONS,
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         VLOG_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
-@@ -460,7 +460,7 @@ has_option(const struct ovs_cmdl_parsed_option *parsed_options, size_t n,
- static bool
- will_detach(const struct ovs_cmdl_parsed_option *parsed_options, size_t n)
- {
--    return has_option(parsed_options, n, OPT_DETACH);
-+    return has_option(parsed_options, n, OVN_OPT_DETACH);
- }
- 
- static char * OVS_WARN_UNUSED_RESULT
-@@ -547,7 +547,7 @@ apply_options_direct(const struct ovs_cmdl_parsed_option *parsed_options,
-             printf("DB Schema %s\n", nbrec_get_db_version());
-             exit(EXIT_SUCCESS);
- 
--        DAEMON_OPTION_HANDLERS
-+        OVN_DAEMON_OPTION_HANDLERS
-         VLOG_OPTION_HANDLERS
-         TABLE_OPTION_HANDLERS(&table_style)
-         STREAM_SSL_OPTION_HANDLERS
-@@ -6512,7 +6512,7 @@ nbctl_client(const char *socket_name,
-         case OPT_NO_SHUFFLE_REMOTES:
-         case OPT_BOOTSTRAP_CA_CERT:
-         STREAM_SSL_CASES
--        DAEMON_OPTION_CASES
-+        OVN_DAEMON_OPTION_CASES
-             VLOG_INFO("using ovn-nbctl daemon, ignoring %s option",
-                       po->o->name);
-             break;
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index eae9622d3..c9d72285c 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -239,7 +239,7 @@ parse_options(int argc, char *argv[])
-         OPT_CT,
-         OPT_FRIENDLY_NAMES,
-         OPT_NO_FRIENDLY_NAMES,
--        DAEMON_OPTION_ENUMS,
-+        OVN_DAEMON_OPTION_ENUMS,
-         SSL_OPTION_ENUMS,
-         VLOG_OPTION_ENUMS,
-         OPT_LB_DST,
-@@ -260,7 +260,7 @@ parse_options(int argc, char *argv[])
-         {"version", no_argument, NULL, 'V'},
-         {"lb-dst", required_argument, NULL, OPT_LB_DST},
-         {"select-id", required_argument, NULL, OPT_SELECT_ID},
--        DAEMON_LONG_OPTIONS,
-+        OVN_DAEMON_LONG_OPTIONS,
-         VLOG_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS,
-         {NULL, 0, NULL, 0},
-@@ -333,7 +333,7 @@ parse_options(int argc, char *argv[])
-             printf("DB Schema %s\n", sbrec_get_db_version());
-             exit(EXIT_SUCCESS);
- 
--        DAEMON_OPTION_HANDLERS
-+        OVN_DAEMON_OPTION_HANDLERS
-         VLOG_OPTION_HANDLERS
-         STREAM_SSL_OPTION_HANDLERS
- 
--- 
-2.25.1
-
diff --git a/SOURCES/0002-Honour-router_preference-for-solicited-RA.patch b/SOURCES/0002-Honour-router_preference-for-solicited-RA.patch
deleted file mode 100644
index a73b677..0000000
--- a/SOURCES/0002-Honour-router_preference-for-solicited-RA.patch
+++ /dev/null
@@ -1,209 +0,0 @@
-From 17d3f4f18878ef706008575cd1565745c5936819 Mon Sep 17 00:00:00 2001
-From: Gabriele Cerami <gcerami@redhat.com>
-Date: Sat, 13 Jun 2020 10:20:23 +0100
-Subject: [PATCH 2/3] Honour router_preference for solicited RA
-
-Replies to router solicitation follow a different flow than periodic RA.
-This flow currently does not honour the router_preference configuration.
-
-This patch modifies the flow to honour the flag, and send
-router-preference indications in the reply RA following RFC4191
-specifications
-
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1804576
-Signed-off-by: Gabriele Cerami <gcerami@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- AUTHORS.rst         |  1 +
- lib/actions.c       | 29 +++++++++++++++++++++++++++--
- lib/ovn-l7.h        |  5 +++++
- northd/ovn-northd.c |  6 ++++++
- tests/ovn.at        | 26 ++++++++++++++++----------
- 5 files changed, 55 insertions(+), 12 deletions(-)
-
-diff --git a/AUTHORS.rst b/AUTHORS.rst
-index c80fc1bae..bba0d1d6f 100644
---- a/AUTHORS.rst
-+++ b/AUTHORS.rst
-@@ -145,6 +145,7 @@ Frédéric Tobias Christ             fchrist@live.de
- Frode Nordahl                      frode.nordahl@gmail.com
- FUJITA Tomonori                    fujita.tomonori@lab.ntt.co.jp
- Gabe Beged-Dov                     gabe@begeddov.com
-+Gabriele Cerami                    gcerami@redhat.com
- Gaetano Catalli                    gaetano.catalli@gmail.com
- Gal Sagie                          gal.sagie@gmail.com
- Genevieve LEsperance               glesperance@pivotal.io
-diff --git a/lib/actions.c b/lib/actions.c
-index ee7ccae0d..3181126e6 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2484,6 +2484,12 @@ parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst,
-             }
-             break;
- 
-+        case ND_RA_FLAG_PRF:
-+            ok = (c->string && (!strcmp(c->string, "MEDIUM") ||
-+                                !strcmp(c->string, "HIGH") ||
-+                                !strcmp(c->string, "LOW")));
-+            break;
-+
-         case ND_OPT_SOURCE_LINKADDR:
-             ok = c->format == LEX_F_ETHERNET;
-             slla_present = true;
-@@ -2538,9 +2544,22 @@ encode_put_nd_ra_option(const struct ovnact_gen_option *o,
-     {
-         struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
-         if (!strcmp(c->string, "dhcpv6_stateful")) {
--            ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
-+            ra->mo_flags |= IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
-         } else if (!strcmp(c->string, "dhcpv6_stateless")) {
--            ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
-+            ra->mo_flags |= IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
-+        }
-+        break;
-+    }
-+
-+    case ND_RA_FLAG_PRF:
-+    {
-+        struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
-+        if (!strcmp(c->string, "LOW")) {
-+            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_LOW;
-+        } else if (!strcmp(c->string, "HIGH")) {
-+            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_HIGH;
-+        } else {
-+            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_NORMAL;
-         }
-         break;
-     }
-@@ -2621,6 +2640,12 @@ encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po,
-         encode_put_nd_ra_option(o, ofpacts, ra_offset);
-     }
- 
-+    /* RFC4191 section 2.2 */
-+    struct ovs_ra_msg *new_ra = ofpbuf_at(ofpacts, ra_offset, sizeof *new_ra);
-+    if (ntohs(new_ra->router_lifetime) == 0) {
-+        new_ra->mo_flags &= IPV6_ND_RA_OPT_PRF_RESET_MASK;
-+    }
-+
-     encode_finish_controller_op(oc_offset, ofpacts);
- }
- 
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index cbea2a0c8..2da38fb65 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -304,6 +304,9 @@ nd_ra_opts_destroy(struct hmap *nd_ra_opts)
- 
- 
- #define ND_RA_FLAG_ADDR_MODE    0
-+/* all small numbers seems to be all already taken but nothing guarantees this
-+ * code will not be assigned by IANA to another option */
-+#define ND_RA_FLAG_PRF          255
- 
- 
- /* Default values of various IPv6 Neighbor Discovery protocol options and
-@@ -325,11 +328,13 @@ nd_ra_opts_destroy(struct hmap *nd_ra_opts)
- #define IPV6_ND_RA_OPT_PRF_NORMAL                   0x00
- #define IPV6_ND_RA_OPT_PRF_HIGH                     0x08
- #define IPV6_ND_RA_OPT_PRF_LOW                      0x18
-+#define IPV6_ND_RA_OPT_PRF_RESET_MASK               0xe7
- 
- static inline void
- nd_ra_opts_init(struct hmap *nd_ra_opts)
- {
-     nd_ra_opt_add(nd_ra_opts, "addr_mode", ND_RA_FLAG_ADDR_MODE, "str");
-+    nd_ra_opt_add(nd_ra_opts, "router_preference", ND_RA_FLAG_PRF, "str");
-     nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac");
-     nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION, "ipv6");
-     nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32");
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index c1b4c13b7..cffe3de17 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -9408,6 +9408,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             ds_put_format(&actions, ", mtu = %u", mtu);
-         }
- 
-+        const char *prf = smap_get_def(
-+            &op->nbrp->ipv6_ra_configs, "router_preference", "MEDIUM");
-+        if (strcmp(prf, "MEDIUM")) {
-+            ds_put_format(&actions, ", router_preference = \"%s\"", prf);
-+        }
-+
-         bool add_rs_response_flow = false;
- 
-         for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 57c1d90e4..d9df393d5 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1346,14 +1346,14 @@ log(severity=notice);
-     Syntax error at `;' expecting verdict.
- 
- # put_nd_ra_opts
--reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64, slla = ae:01:02:03:04:05);
--    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
-+reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, router_preference = "HIGH", prefix = aef0::/64, slla = ae:01:02:03:04:05);
-+    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.08.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
-     has prereqs ip6
--reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", slla = ae:01:02:03:04:10, mtu = 1450);
-+reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", router_preference = "MEDIUM", slla = ae:01:02:03:04:10, mtu = 1450);
-     encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10.05.01.00.00.00.00.05.aa,pause)
-     has prereqs ip6
--reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla = ae:01:02:03:04:06, prefix = aef0::/64);
--    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.40.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause)
-+reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", router_preference = "LOW", slla = ae:01:02:03:04:06, prefix = aef0::/64);
-+    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.58.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause)
-     has prereqs ip6
- reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64);
-     slla option not present
-@@ -10083,13 +10083,16 @@ reset_pcap_file hv1-vif1 hv1/vif1
- reset_pcap_file hv1-vif2 hv1/vif2
- reset_pcap_file hv1-vif3 hv1/vif3
- 
--# Set the MTU to 1500
-+# Set the MTU to 1500, send_periodic to false, preference to LOW
- ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500
-+ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:send_periodic="false"
-+ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="LOW"
- 
- # Make sure that ovn-controller has installed the corresponding OF Flow.
- OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
- 
--addr_mode=00
-+# addr_mode byte also includes router preference information
-+addr_mode=18
- default_prefix_option_config=030440c0ffffffffffffffff00000000
- src_mac=fa163e000003
- src_lla=fe80000000000000f8163efffe000003
-@@ -10114,12 +10117,14 @@ reset_pcap_file hv1-vif1 hv1/vif1
- reset_pcap_file hv1-vif2 hv1/vif2
- reset_pcap_file hv1-vif3 hv1/vif3
- 
--# Set the address mode to dhcpv6_stateful
-+# Set the address mode to dhcpv6_stateful, router_preference to HIGH
- ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful
-+ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="HIGH"
- # Make sure that ovn-controller has installed the corresponding OF Flow.
- OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
- 
--addr_mode=80
-+# addr_mode byte also includes router preference information
-+addr_mode=88
- default_prefix_option_config=03044080ffffffffffffffff00000000
- src_mac=fa163e000004
- src_lla=fe80000000000000f8163efffe000004
-@@ -10144,8 +10149,9 @@ reset_pcap_file hv1-vif1 hv1/vif1
- reset_pcap_file hv1-vif2 hv1/vif2
- reset_pcap_file hv1-vif3 hv1/vif3
- 
--# Set the address mode to dhcpv6_stateless
-+# Set the address mode to dhcpv6_stateless, reset router preference to default
- ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless
-+ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="MEDIUM"
- # Make sure that ovn-controller has installed the corresponding OF Flow.
- OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch b/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
deleted file mode 100644
index d6bd9d1..0000000
--- a/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
+++ /dev/null
@@ -1,186 +0,0 @@
-From d9ed450713eda62af1bec5009694b2d206c9f435 Mon Sep 17 00:00:00 2001
-Message-Id: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Mon, 25 May 2020 23:55:06 +0200
-Subject: [PATCH ovn 1/3] Revert "Manage ARP process locally in a DVR scenario"
-
-This reverts commit c0bf32d72f8b893bbe3cb64912b0fd259d71555f.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
----
- northd/ovn-northd.8.xml | 37 ++--------------------------
- northd/ovn-northd.c     | 53 +----------------------------------------
- tests/ovn.at            | 14 -----------
- 3 files changed, 3 insertions(+), 101 deletions(-)
-
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2486,44 +2486,11 @@ output;
- 
-       <li>
-         <p>
--          For distributed logical routers where one of the logical router ports
--          specifies a <code>redirect-chassis</code>, a priority-400 logical
--          flow for each <code>dnat_and_snat</code> NAT rules configured.
--          These flows will allow to properly forward traffic to the external
--          connections if available and avoid sending it through the tunnel.
--          Assuming the following NAT rule has been configured:
--        </p>
--
--        <pre>
--external_ip = <var>A</var>;
--external_mac = <var>B</var>;
--logical_ip = <var>C</var>;
--        </pre>
--
--        <p>
--          the following action will be applied:
--        </p>
--
--        <pre>
--ip.ttl--;
--reg0 = <var>ip.dst</var>;
--reg1 = <var>A</var>;
--eth.src = <var>B</var>;
--outport = <var>router-port</var>;
--next;
--        </pre>
--
--      </li>
--
--      <li>
--        <p>
-           IPv4 routing table.  For each route to IPv4 network <var>N</var> with
-           netmask <var>M</var>, on router port <var>P</var> with IP address
-           <var>A</var> and Ethernet
-           address <var>E</var>, a logical flow with match <code>ip4.dst ==
--          <var>N</var>/<var>M</var></code>, whose priority is <code>400</code>
--          + the number of 1-bits in <var>M</var> if the router port is not a
--          distributed gateway port, else the priority is the number of
-+          <var>N</var>/<var>M</var></code>, whose priority is the number of
-           1-bits in <var>M</var>, has the following actions:
-         </p>
- 
-@@ -2910,7 +2877,7 @@ icmp4 {
-     <ul>
-       <li>
-         For each NAT rule in the OVN Northbound database that can
--        be handled in a distributed manner, a priority-200 logical
-+        be handled in a distributed manner, a priority-100 logical
-         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
-         outport == <var>GW</var></code>, where <var>GW</var> is
-         the logical router distributed gateway port, with actions
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -7102,8 +7102,6 @@ build_routing_policy_flow(struct hmap *l
-     ds_destroy(&actions);
- }
- 
--/* default logical flow prioriry for distributed routes */
--#define DROUTE_PRIO 400
- struct parsed_route {
-     struct ovs_list list_node;
-     struct v46_ip prefix;
-@@ -7492,40 +7490,6 @@ build_ecmp_route_flow(struct hmap *lflow
- }
- 
- static void
--add_distributed_routes(struct hmap *lflows, struct ovn_datapath *od)
--{
--    struct ds actions = DS_EMPTY_INITIALIZER;
--    struct ds match = DS_EMPTY_INITIALIZER;
--
--    for (size_t i = 0; i < od->nbr->n_nat; i++) {
--        const struct nbrec_nat *nat = od->nbr->nat[i];
--
--        if (strcmp(nat->type, "dnat_and_snat") ||
--            !nat->external_mac) {
--            continue;
--        }
--
--        bool is_ipv4 = strchr(nat->logical_ip, '.') ? true : false;
--        ds_put_format(&match, "ip%s.src == %s && is_chassis_resident(\"%s\")",
--                      is_ipv4 ? "4" : "6", nat->logical_ip,
--                      nat->logical_port);
--        char *prefix = is_ipv4 ? "" : "xx";
--        ds_put_format(&actions, "outport = %s; eth.src = %s; "
--                      "%sreg0 = ip%s.dst; %sreg1 = %s; next;",
--                      od->l3dgw_port->json_key, nat->external_mac,
--                      prefix, is_ipv4 ? "4" : "6",
--                      prefix, nat->external_ip);
--        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, DROUTE_PRIO,
--                      ds_cstr(&match), ds_cstr(&actions));
--        ds_clear(&match);
--        ds_clear(&actions);
--    }
--
--    ds_destroy(&actions);
--    ds_destroy(&match);
--}
--
--static void
- add_route(struct hmap *lflows, const struct ovn_port *op,
-           const char *lrp_addr_s, const char *network_s, int plen,
-           const char *gateway, bool is_src_route,
-@@ -7546,12 +7510,6 @@ add_route(struct hmap *lflows, const str
-     }
-     build_route_match(op_inport, network_s, plen, is_src_route, is_ipv4,
-                       &match, &priority);
--    /* traffic for internal IPs of logical switch ports must be sent to
--     * the gw controller through the overlay tunnels
--     */
--    if (op->nbrp && !op->nbrp->n_gateway_chassis) {
--        priority += DROUTE_PRIO;
--    }
- 
-     struct ds actions = DS_EMPTY_INITIALIZER;
-     ds_put_format(&actions, "ip.ttl--; "REG_ECMP_GROUP_ID" = 0; %sreg0 = ",
-@@ -9187,7 +9145,7 @@ build_lrouter_flows(struct hmap *datapat
-                               nat->logical_ip,
-                               od->l3dgw_port->json_key);
-                 ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
--                                        200, ds_cstr(&match), "next;",
-+                                        100, ds_cstr(&match), "next;",
-                                         &nat->header_);
-             }
- 
-@@ -9493,15 +9451,6 @@ build_lrouter_flows(struct hmap *datapat
-         ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;");
-     }
- 
--    /* Logical router ingress table IP_ROUTING - IP routing for distributed
--     * logical router
--     */
--    HMAP_FOR_EACH (od, key_node, datapaths) {
--        if (od->nbr && od->l3dgw_port) {
--            add_distributed_routes(lflows, od);
--        }
--    }
--
-     /* Logical router ingress table IP_ROUTING & IP_ROUTING_ECMP: IP Routing.
-      *
-      * A packet that arrives at this table is an IP packet that should be
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -9605,20 +9605,6 @@ AT_CHECK([as hv3 ovs-vsctl set Open_vSwi
- OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \
- grep "Port patch-br-int-to-ln_port" | wc -l`])
- 
--AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
--grep "ip4.src == 10.0.0.3 && is_chassis_resident(\"foo1\")" -c`])
--AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
--grep "ip4.src == 10.0.0.4 && is_chassis_resident(\"foo2\")" -c`])
--
--key=`ovn-sbctl --bare --columns tunnel_key list datapath_Binding lr0`
--# Check that the OVS flows appear for the dnat_and_snat entries in
--# lr_in_ip_routing table.
--OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
--grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.3" -c`])
--
--OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
--grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.4" -c`])
--
- # Re-add nat-addresses option
- ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
- 
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
new file mode 100644
index 0000000..b9e4333
--- /dev/null
+++ b/SOURCES/0002-chassis-Fix-chassis_private-record-updates-when-the-.patch
@@ -0,0 +1,274 @@
+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-controller-Add-garbage-collector-for-ipv6_prefixd.patch b/SOURCES/0002-controller-Add-garbage-collector-for-ipv6_prefixd.patch
deleted file mode 100644
index 7b48c56..0000000
--- a/SOURCES/0002-controller-Add-garbage-collector-for-ipv6_prefixd.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 85b13870020facf5f4441df51df64ee647c6abd9 Mon Sep 17 00:00:00 2001
-Message-Id: <85b13870020facf5f4441df51df64ee647c6abd9.1588608928.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <0b9d16670d5561d8300d2448cbd4686a3acdc57e.1588608928.git.lorenzo.bianconi@redhat.com>
-References: <0b9d16670d5561d8300d2448cbd4686a3acdc57e.1588608928.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 29 Apr 2020 18:05:30 +0200
-Subject: [PATCH 2/3] controller: Add garbage collector for ipv6_prefixd.
-
-Introduce a garbage collector for stale entries in ipv6_prefixd that are
-no longer managed by the controller (e.g. if the processing has been
-disabled setting ipv6_prefix_delegation to false on all logical router
-ports).
-
-Tested-by: Jianlin Shi <jishi@redhat.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 3230bb386..f0d63b9a6 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -578,6 +578,7 @@ enum {
- 
- struct ipv6_prefixd_state {
-     long long int next_announce;
-+    long long int last_used;
-     struct in6_addr ipv6_addr;
-     struct eth_addr ea;
-     struct eth_addr cmac;
-@@ -1128,11 +1129,13 @@ fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
-             sbrec_port_binding_set_options(pb, &options);
-             smap_destroy(&options);
-         }
-+        pfd->last_used = time_msec();
-     }
- 
-     return changed;
- }
- 
-+#define IPV6_PREFIXD_STALE_TIMEOUT  180000LL
- static void
- prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                      struct ovsdb_idl_index *sbrec_port_binding_by_name,
-@@ -1210,6 +1213,15 @@ prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
-         }
-     }
- 
-+    struct shash_node *iter, *next;
-+    SHASH_FOR_EACH_SAFE (iter, next, &ipv6_prefixd) {
-+        struct ipv6_prefixd_state *pfd = iter->data;
-+        if (pfd->last_used + IPV6_PREFIXD_STALE_TIMEOUT < time_msec()) {
-+            free(pfd);
-+            shash_delete(&ipv6_prefixd, iter);
-+        }
-+    }
-+
-     if (changed) {
-         notify_pinctrl_handler();
-     }
--- 
-2.26.2
-
diff --git a/SOURCES/0002-controller-Add-ipv6-prefix-delegation-state-machine.patch b/SOURCES/0002-controller-Add-ipv6-prefix-delegation-state-machine.patch
deleted file mode 100644
index b715219..0000000
--- a/SOURCES/0002-controller-Add-ipv6-prefix-delegation-state-machine.patch
+++ /dev/null
@@ -1,934 +0,0 @@
-From aa0139f28628bb869866e4c35cb31f8005b99994 Mon Sep 17 00:00:00 2001
-Message-Id: <aa0139f28628bb869866e4c35cb31f8005b99994.1586727203.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
-References: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 1 Apr 2020 18:37:30 +0200
-Subject: [PATCH 2/3] controller: Add ipv6 prefix delegation state machine
-
-Introduce IPv6 Prefix delegation state machine according to RFC 3633
-https://tools.ietf.org/html/rfc3633.
-Add handle_dhcpv6_reply controller action to parse advertise/reply from
-IPv6 delegation server. Advertise/reply are parsed running respectively:
-- pinctrl_parse_dhcv6_advt
-- pinctrl_parse_dhcv6_reply
-The IPv6 requesting router starts sending dhcpv6 solicit through the logical
-router port marked with ipv6_prefix_delegation set to true.
-An IPv6 prefix will be requested for each logical router port marked
-with "prefix" set to true in option column of logical router port table.
-Save IPv6 prefix received by IPv6 delegation router in the options columns of
-SB port binding table in order to be reused by Router Advertisement framework
-run by ovn logical router pipeline.
-IPv6 Prefix delegation state machine is enabled on Gateway Router or on
-a Gateway Router Port
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- controller/pinctrl.c  | 651 ++++++++++++++++++++++++++++++++++++++++++
- include/ovn/actions.h |   8 +-
- lib/actions.c         |  16 ++
- lib/ovn-l7.h          |  19 ++
- ovn-sb.xml            |  18 ++
- tests/ovn.at          |   4 +
- utilities/ovn-trace.c |   2 +
- 7 files changed, 717 insertions(+), 1 deletion(-)
-
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 3fa8923e7..a053938ec 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -270,6 +270,8 @@ static void pinctrl_ip_mcast_handle(
-     const struct match *md,
-     struct ofpbuf *userdata);
- 
-+static void init_ipv6_prefixd(void);
-+
- static bool may_inject_pkts(void);
- 
- static void init_put_vport_bindings(void);
-@@ -313,6 +315,13 @@ static void pinctrl_compose_ipv6(struct dp_packet *packet,
-                                  uint8_t ip_proto, uint8_t ttl,
-                                  uint16_t ip_payload_len);
- 
-+static void
-+put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
-+         struct ofpbuf *ofpacts);
-+
-+static void notify_pinctrl_main(void);
-+static void notify_pinctrl_handler(void);
-+
- COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
- COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
- COVERAGE_DEFINE(pinctrl_drop_controller_event);
-@@ -470,6 +479,7 @@ pinctrl_init(void)
-     init_put_mac_bindings();
-     init_send_garps_rarps();
-     init_ipv6_ras();
-+    init_ipv6_prefixd();
-     init_buffered_packets_map();
-     init_event_table();
-     ip_mcast_snoop_init();
-@@ -557,6 +567,634 @@ set_actions_and_enqueue_msg(struct rconn *swconn,
-     ofpbuf_uninit(&ofpacts);
- }
- 
-+static struct shash ipv6_prefixd;
-+
-+enum {
-+    PREFIX_SOLICIT,
-+    PREFIX_REQUEST,
-+    PREFIX_PENDING,
-+    PREFIX_DONE,
-+};
-+
-+struct ipv6_prefixd_state {
-+    long long int next_announce;
-+    struct in6_addr ipv6_addr;
-+    struct eth_addr ea;
-+    struct eth_addr cmac;
-+    int64_t port_key;
-+    int64_t metadata;
-+    struct in6_addr prefix;
-+    uint32_t plife_time;
-+    uint32_t vlife_time;
-+    uint32_t aid;
-+    uint32_t t1;
-+    uint32_t t2;
-+    uint32_t plen;
-+    int state;
-+};
-+
-+static void
-+init_ipv6_prefixd(void)
-+{
-+    shash_init(&ipv6_prefixd);
-+}
-+
-+static void
-+destroy_ipv6_prefixd(void)
-+{
-+    struct shash_node *iter, *next;
-+    SHASH_FOR_EACH_SAFE (iter, next, &ipv6_prefixd) {
-+        struct ipv6_prefixd_state *pfd = iter->data;
-+        free(pfd);
-+        shash_delete(&ipv6_prefixd, iter);
-+    }
-+    shash_destroy(&ipv6_prefixd);
-+}
-+
-+static struct ipv6_prefixd_state *
-+pinctrl_find_prefixd_state(const struct flow *ip_flow, unsigned aid)
-+{
-+    struct shash_node *iter;
-+
-+    SHASH_FOR_EACH (iter, &ipv6_prefixd) {
-+        struct ipv6_prefixd_state *pfd = iter->data;
-+        if (IN6_ARE_ADDR_EQUAL(&pfd->ipv6_addr, &ip_flow->ipv6_dst) &&
-+            eth_addr_equals(pfd->ea, ip_flow->dl_dst) &&
-+            pfd->aid == aid) {
-+            return pfd;
-+        }
-+    }
-+    return NULL;
-+}
-+
-+static void
-+pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const struct flow *ip_flow,
-+                          struct dp_packet *pkt_in, const struct match *md)
-+{
-+    struct udp_header *udp_in = dp_packet_l4(pkt_in);
-+    size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in));
-+    unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1);
-+    uint8_t *data, *end = (uint8_t *)udp_in + dlen;
-+    int len = 0, aid = 0;
-+
-+    data = xmalloc(dlen);
-+    /* skip DHCPv6 common header */
-+    in_dhcpv6_data += 4;
-+    while (in_dhcpv6_data < end) {
-+        struct dhcpv6_opt_header *in_opt =
-+             (struct dhcpv6_opt_header *)in_dhcpv6_data;
-+        int opt_len = sizeof *in_opt + ntohs(in_opt->len);
-+
-+        if (dlen < opt_len + len) {
-+            goto out;
-+        }
-+
-+        switch (ntohs(in_opt->code)) {
-+        case DHCPV6_OPT_IA_PD: {
-+            struct dhcpv6_opt_ia_na *ia_na = (struct dhcpv6_opt_ia_na *)in_opt;
-+            int orig_len = len, hdr_len = 0, size = sizeof *in_opt + 12;
-+
-+            aid = ntohl(ia_na->iaid);
-+            memcpy(&data[len], in_opt, size);
-+            in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
-+            len += size;
-+
-+            while (size < opt_len) {
-+                int flen = sizeof *in_opt + ntohs(in_opt->len);
-+
-+                if (dlen < flen + len) {
-+                    goto out;
-+                }
-+
-+                if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) {
-+                    memcpy(&data[len], in_opt, flen);
-+                    hdr_len += flen;
-+                    len += flen;
-+                }
-+                if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) {
-+                   struct dhcpv6_opt_status *status;
-+
-+                   status = (struct dhcpv6_opt_status *)in_opt;
-+                   if (ntohs(status->status_code)) {
-+                       goto out;
-+                   }
-+                }
-+                size += flen;
-+                in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
-+            }
-+            in_opt = (struct dhcpv6_opt_header *)&data[orig_len];
-+            in_opt->len = htons(hdr_len + 12);
-+            break;
-+        }
-+        case DHCPV6_OPT_SERVER_ID_CODE:
-+        case DHCPV6_OPT_CLIENT_ID_CODE:
-+            memcpy(&data[len], in_opt, opt_len);
-+            len += opt_len;
-+            break;
-+        default:
-+            break;
-+        }
-+        in_dhcpv6_data += opt_len;
-+    }
-+
-+    struct ipv6_prefixd_state *pfd = pinctrl_find_prefixd_state(ip_flow, aid);
-+    if (!pfd) {
-+        goto out;
-+    }
-+
-+    pfd->state = PREFIX_REQUEST;
-+
-+    uint64_t packet_stub[256 / 8];
-+    struct dp_packet packet;
-+
-+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-+    eth_compose(&packet, ip_flow->dl_src, ip_flow->dl_dst, ETH_TYPE_IPV6,
-+                IPV6_HEADER_LEN);
-+
-+    struct udp_header *udp_h = compose_ipv6(&packet, IPPROTO_UDP,
-+                                            &ip_flow->ipv6_dst,
-+                                            &ip_flow->ipv6_src, 0, 0, 255,
-+                                            len + UDP_HEADER_LEN + 4);
-+    udp_h->udp_len = htons(len + UDP_HEADER_LEN + 4);
-+    udp_h->udp_csum = 0;
-+    packet_set_udp_port(&packet, htons(546), htons(547));
-+
-+    unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1);
-+    *dhcp_hdr = DHCPV6_MSG_TYPE_REQUEST;
-+    memcpy(dhcp_hdr + 4, data, len);
-+
-+    uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
-+    csum = csum_continue(csum, udp_h, dp_packet_size(&packet) -
-+                         ((const unsigned char *)udp_h -
-+                          (const unsigned char *)dp_packet_eth(&packet)));
-+    udp_h->udp_csum = csum_finish(csum);
-+    if (!udp_h->udp_csum) {
-+        udp_h->udp_csum = htons(0xffff);
-+    }
-+
-+    if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
-+        eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
-+                      ip_flow->vlans[0].tci);
-+    }
-+
-+    uint64_t ofpacts_stub[4096 / 8];
-+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
-+    enum ofp_version version = rconn_get_version(swconn);
-+    put_load(ntohll(md->flow.metadata), MFF_LOG_DATAPATH, 0, 64, &ofpacts);
-+    put_load(md->flow.regs[MFF_LOG_INPORT - MFF_REG0], MFF_LOG_OUTPORT,
-+             0, 32, &ofpacts);
-+    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
-+    resubmit->in_port = OFPP_CONTROLLER;
-+    resubmit->table_id = OFTABLE_REMOTE_OUTPUT;
-+
-+    struct ofputil_packet_out po = {
-+        .packet = dp_packet_data(&packet),
-+        .packet_len = dp_packet_size(&packet),
-+        .buffer_id = UINT32_MAX,
-+        .ofpacts = ofpacts.data,
-+        .ofpacts_len = ofpacts.size,
-+    };
-+    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
-+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-+    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
-+    dp_packet_uninit(&packet);
-+    ofpbuf_uninit(&ofpacts);
-+
-+out:
-+    free(data);
-+}
-+
-+static void
-+pinctrl_prefixd_state_handler(const struct flow *ip_flow,
-+                              struct in6_addr addr, unsigned aid,
-+                              char prefix_len, unsigned t1, unsigned t2,
-+                              unsigned plife_time, unsigned vlife_time)
-+{
-+    struct ipv6_prefixd_state *pfd;
-+
-+    pfd = pinctrl_find_prefixd_state(ip_flow, aid);
-+    if (pfd) {
-+        pfd->state = PREFIX_PENDING;
-+        pfd->plife_time = plife_time;
-+        pfd->vlife_time = vlife_time;
-+        pfd->plen = prefix_len;
-+        pfd->prefix = addr;
-+        pfd->t1 = t1;
-+        pfd->t2 = t2;
-+        notify_pinctrl_main();
-+    }
-+}
-+
-+static void
-+pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
-+                           const struct flow *ip_flow)
-+    OVS_REQUIRES(pinctrl_mutex)
-+{
-+    struct udp_header *udp_in = dp_packet_l4(pkt_in);
-+    unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1);
-+    size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in));
-+    unsigned t1 = 0, t2 = 0, vlife_time = 0, plife_time = 0;
-+    uint8_t *end = (uint8_t *)udp_in + dlen;
-+    uint8_t prefix_len = 0;
-+    struct in6_addr ipv6;
-+    bool status = false;
-+    unsigned aid = 0;
-+
-+    memset(&ipv6, 0, sizeof (struct in6_addr));
-+    /* skip DHCPv6 common header */
-+    in_dhcpv6_data += 4;
-+    while (in_dhcpv6_data < end) {
-+        struct dhcpv6_opt_header *in_opt =
-+             (struct dhcpv6_opt_header *)in_dhcpv6_data;
-+        int opt_len = sizeof *in_opt + ntohs(in_opt->len);
-+
-+        if (in_dhcpv6_data + opt_len > end) {
-+            break;
-+        }
-+
-+        switch (ntohs(in_opt->code)) {
-+        case DHCPV6_OPT_IA_PD: {
-+            int size = sizeof *in_opt + 12;
-+            in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
-+            struct dhcpv6_opt_ia_na *ia_na =
-+                (struct dhcpv6_opt_ia_na *)in_dhcpv6_data;
-+
-+            aid = ntohl(ia_na->iaid);
-+            t1 = ntohl(ia_na->t1);
-+            t2 = ntohl(ia_na->t2);
-+            if (t1 > t2 && t2 > 0) {
-+                break;
-+            }
-+
-+            while (size < opt_len) {
-+                if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) {
-+                    struct dhcpv6_opt_ia_prefix *ia_hdr =
-+                        (struct dhcpv6_opt_ia_prefix *)(in_dhcpv6_data + size);
-+
-+                    prefix_len = ia_hdr->plen;
-+                    plife_time = ntohl(ia_hdr->plife_time);
-+                    vlife_time = ntohl(ia_hdr->vlife_time);
-+                    memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr));
-+                }
-+                if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) {
-+                   struct dhcpv6_opt_status *status_hdr;
-+
-+                   status_hdr = (struct dhcpv6_opt_status *)in_opt;
-+                   status = ntohs(status_hdr->status_code) == 0;
-+                }
-+                size += sizeof *in_opt + ntohs(in_opt->len);
-+                in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
-+            }
-+            break;
-+        }
-+        default:
-+            break;
-+        }
-+        in_dhcpv6_data += opt_len;
-+    }
-+    if (status) {
-+        pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, prefix_len,
-+                                      t1, t2, plife_time, vlife_time);
-+    }
-+}
-+
-+static void
-+pinctrl_handle_dhcp6_server(struct rconn *swconn, const struct flow *ip_flow,
-+                            struct dp_packet *pkt_in, const struct match *md)
-+    OVS_REQUIRES(pinctrl_mutex)
-+{
-+    if (ip_flow->dl_type != htons(ETH_TYPE_IPV6) ||
-+        ip_flow->nw_proto != IPPROTO_UDP) {
-+        return;
-+    }
-+
-+    struct udp_header *udp_in = dp_packet_l4(pkt_in);
-+    unsigned char *dhcp_hdr = (unsigned char *)(udp_in + 1);
-+
-+    switch (*dhcp_hdr) {
-+    case DHCPV6_MSG_TYPE_ADVT:
-+        pinctrl_parse_dhcpv6_advt(swconn, ip_flow, pkt_in, md);
-+        break;
-+    case DHCPV6_MSG_TYPE_REPLY:
-+        pinctrl_parse_dhcpv6_reply(pkt_in, ip_flow);
-+        break;
-+    default:
-+        break;
-+    }
-+}
-+
-+static void
-+compose_prefixd_solicit(struct dp_packet *b,
-+                        struct ipv6_prefixd_state *pfd,
-+                        const struct eth_addr eth_dst,
-+                        const struct in6_addr *ipv6_dst)
-+{
-+    eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
-+
-+    int payload = sizeof(struct dhcpv6_opt_server_id) +
-+                  sizeof(struct dhcpv6_opt_ia_na);
-+    if (ipv6_addr_is_set(&pfd->prefix)) {
-+        payload += sizeof(struct dhcpv6_opt_ia_prefix);
-+    }
-+    int len = UDP_HEADER_LEN + 4 + payload;
-+    struct udp_header *udp_h = compose_ipv6(b, IPPROTO_UDP, &pfd->ipv6_addr,
-+                                            ipv6_dst, 0, 0, 255, len);
-+    udp_h->udp_len = htons(len);
-+    udp_h->udp_csum = 0;
-+    packet_set_udp_port(b, htons(546), htons(547));
-+
-+    unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1);
-+    *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT;
-+
-+    struct dhcpv6_opt_server_id *opt_client_id =
-+        (struct dhcpv6_opt_server_id *)(dhcp_hdr + 4);
-+    opt_client_id->opt.code = htons(DHCPV6_OPT_CLIENT_ID_CODE);
-+    opt_client_id->opt.len = htons(sizeof(struct dhcpv6_opt_server_id) -
-+                                   sizeof(struct dhcpv6_opt_header));
-+    opt_client_id->duid_type = htons(DHCPV6_DUID_LL);
-+    opt_client_id->hw_type = htons(DHCPV6_HW_TYPE_ETH);
-+    opt_client_id->mac = pfd->cmac;
-+
-+    if (!ipv6_addr_is_set(&pfd->prefix)) {
-+        pfd->aid = random_uint16();
-+    }
-+    struct dhcpv6_opt_ia_na *ia_pd =
-+            (struct dhcpv6_opt_ia_na *)(opt_client_id + 1);
-+    ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
-+    int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
-+                  sizeof(struct dhcpv6_opt_header);
-+    if (ipv6_addr_is_set(&pfd->prefix)) {
-+        opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
-+    }
-+    ia_pd->opt.len = htons(opt_len);
-+    ia_pd->iaid = htonl(pfd->aid);
-+    ia_pd->t1 = OVS_BE32_MAX;
-+    ia_pd->t2 = OVS_BE32_MAX;
-+    if (ipv6_addr_is_set(&pfd->prefix)) {
-+        struct dhcpv6_opt_ia_prefix *ia_prefix =
-+            (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
-+        ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
-+        ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
-+                                   sizeof(struct dhcpv6_opt_header));
-+        ia_prefix->plife_time = OVS_BE32_MAX;
-+        ia_prefix->vlife_time = OVS_BE32_MAX;
-+        ia_prefix->plen = pfd->plen;
-+        ia_prefix->ipv6 = pfd->prefix;
-+    }
-+
-+    uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(b));
-+    csum = csum_continue(csum, udp_h, dp_packet_size(b) -
-+                         ((const unsigned char *)udp_h -
-+                          (const unsigned char *)dp_packet_eth(b)));
-+    udp_h->udp_csum = csum_finish(csum);
-+    if (!udp_h->udp_csum) {
-+        udp_h->udp_csum = htons(0xffff);
-+    }
-+}
-+
-+#define IPV6_PREFIXD_TIMEOUT    3000LL
-+static long long int
-+ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd)
-+{
-+    long long int cur_time = time_msec();
-+    if (cur_time < pfd->next_announce) {
-+        return pfd->next_announce;
-+    }
-+
-+    uint64_t packet_stub[256 / 8];
-+    struct dp_packet packet;
-+
-+    struct eth_addr eth_dst;
-+    eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02);
-+    struct in6_addr ipv6_dst;
-+    ipv6_parse("ff02::1:2", &ipv6_dst);
-+
-+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-+    compose_prefixd_solicit(&packet, pfd, eth_dst, &ipv6_dst);
-+
-+    uint64_t ofpacts_stub[4096 / 8];
-+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
-+
-+    /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
-+    uint32_t dp_key = pfd->metadata;
-+    uint32_t port_key = pfd->port_key;
-+    put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
-+    put_load(port_key, MFF_LOG_INPORT, 0, 32, &ofpacts);
-+    put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY_BIT, 1, &ofpacts);
-+    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
-+    resubmit->in_port = OFPP_CONTROLLER;
-+    resubmit->table_id = OFTABLE_LOG_INGRESS_PIPELINE;
-+
-+    struct ofputil_packet_out po = {
-+        .packet = dp_packet_data(&packet),
-+        .packet_len = dp_packet_size(&packet),
-+        .buffer_id = UINT32_MAX,
-+        .ofpacts = ofpacts.data,
-+        .ofpacts_len = ofpacts.size,
-+    };
-+
-+    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
-+    enum ofp_version version = rconn_get_version(swconn);
-+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-+    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
-+    dp_packet_uninit(&packet);
-+    ofpbuf_uninit(&ofpacts);
-+    pfd->next_announce = cur_time + random_range(IPV6_PREFIXD_TIMEOUT);
-+    pfd->state = PREFIX_SOLICIT;
-+
-+    return pfd->next_announce;
-+}
-+
-+static bool ipv6_prefixd_should_inject(void)
-+{
-+    struct shash_node *iter;
-+
-+    SHASH_FOR_EACH (iter, &ipv6_prefixd) {
-+        struct ipv6_prefixd_state *pfd = iter->data;
-+        if (pfd->state == PREFIX_SOLICIT) {
-+            return true;
-+        }
-+        if (pfd->state && pfd->next_announce < time_msec()) {
-+            return true;
-+        }
-+    }
-+    return false;
-+}
-+
-+static void
-+ipv6_prefixd_wait(long long int timeout)
-+{
-+    if (ipv6_prefixd_should_inject()) {
-+        poll_timer_wait_until(timeout);
-+    }
-+}
-+
-+static void
-+send_ipv6_prefixd(struct rconn *swconn, long long int *send_prefixd_time)
-+    OVS_REQUIRES(pinctrl_mutex)
-+{
-+    struct shash_node *iter;
-+
-+    *send_prefixd_time = LLONG_MAX;
-+    SHASH_FOR_EACH (iter, &ipv6_prefixd) {
-+        struct ipv6_prefixd_state *pfd = iter->data;
-+        long long int next_msg = ipv6_prefixd_send(swconn, pfd);
-+        if (*send_prefixd_time > next_msg) {
-+            *send_prefixd_time = next_msg;
-+        }
-+    }
-+}
-+
-+static bool
-+fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
-+                       const struct local_datapath *ld,
-+                       struct eth_addr ea, struct in6_addr ipv6_addr,
-+                       int64_t tunnel_key, int64_t dp_tunnel_key)
-+    OVS_REQUIRES(pinctrl_mutex)
-+{
-+    bool changed = false;
-+
-+    for (size_t i = 0; i < ld->n_peer_ports; i++) {
-+        const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
-+        struct ipv6_prefixd_state *pfd;
-+
-+        if (!smap_get_bool(&pb->options, "ipv6_prefix", false)) {
-+            pfd = shash_find_and_delete(&ipv6_prefixd, pb->logical_port);
-+            if (pfd) {
-+                free(pfd);
-+            }
-+            continue;
-+        }
-+
-+        struct lport_addresses c_addrs;
-+        for (size_t j = 0; j < pb->n_mac; j++) {
-+            if (extract_lsp_addresses(pb->mac[j], &c_addrs)) {
-+                    break;
-+            }
-+        }
-+
-+        pfd = shash_find_data(&ipv6_prefixd, pb->logical_port);
-+        if (!pfd) {
-+            pfd = xzalloc(sizeof *pfd);
-+            pfd->ipv6_addr = ipv6_addr;
-+            pfd->ea = ea;
-+            pfd->cmac = c_addrs.ea;
-+            pfd->metadata = dp_tunnel_key;
-+            pfd->port_key = tunnel_key;
-+            shash_add(&ipv6_prefixd, pb->logical_port, pfd);
-+            pfd->next_announce = time_msec() +
-+                                 random_range(IPV6_PREFIXD_TIMEOUT);
-+            changed = true;
-+
-+            char prefix_s[IPV6_SCAN_LEN + 6];
-+            const char *ipv6_pd_list = smap_get(&pb->options,
-+                                                "ipv6_ra_pd_list");
-+            if (!ipv6_pd_list ||
-+                !ovs_scan(ipv6_pd_list, "%u:"IPV6_SCAN_FMT"/%d",
-+                          &pfd->aid, prefix_s, &pfd->plen) ||
-+                !ipv6_parse(prefix_s, &pfd->prefix)) {
-+                pfd->prefix = in6addr_any;
-+            }
-+        } else if (pfd->state == PREFIX_PENDING && ovnsb_idl_txn) {
-+            char prefix_str[INET6_ADDRSTRLEN + 1] = {};
-+            struct smap options;
-+
-+            pfd->state = PREFIX_DONE;
-+            pfd->next_announce = time_msec() + pfd->t1 * 1000;
-+            ipv6_string_mapped(prefix_str, &pfd->prefix);
-+            smap_clone(&options, &pb->options);
-+            smap_add_format(&options, "ipv6_ra_pd_list", "%d:%s/%d",
-+                            pfd->aid, prefix_str, pfd->plen);
-+            sbrec_port_binding_set_options(pb, &options);
-+            smap_destroy(&options);
-+        }
-+    }
-+
-+    return changed;
-+}
-+
-+static void
-+prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
-+                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
-+                     const struct hmap *local_datapaths,
-+                     const struct sbrec_chassis *chassis,
-+                     const struct sset *active_tunnels)
-+    OVS_REQUIRES(pinctrl_mutex)
-+{
-+    const struct local_datapath *ld;
-+    bool changed = false;
-+
-+    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
-+        if (datapath_is_switch(ld->datapath)) {
-+            /* logical switch */
-+            continue;
-+        }
-+
-+        for (size_t i = 0; i < ld->n_peer_ports; i++) {
-+            const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
-+            int j;
-+
-+            if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation",
-+                               false)) {
-+                continue;
-+            }
-+
-+            const char *peer_s = smap_get(&pb->options, "peer");
-+            if (!peer_s) {
-+                continue;
-+            }
-+
-+            const struct sbrec_port_binding *peer
-+                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
-+            if (!peer) {
-+                continue;
-+            }
-+
-+            char *redirect_name = xasprintf("cr-%s", pb->logical_port);
-+            bool resident = lport_is_chassis_resident(
-+                    sbrec_port_binding_by_name, chassis, active_tunnels,
-+                    redirect_name);
-+            free(redirect_name);
-+            if (!resident && strcmp(pb->type, "l3gateway")) {
-+                continue;
-+            }
-+
-+            struct in6_addr ip6_addr;
-+            struct eth_addr ea;
-+            for (j = 0; j < pb->n_mac; j++) {
-+                struct lport_addresses laddrs;
-+
-+                if (!extract_lsp_addresses(pb->mac[j], &laddrs)) {
-+                    continue;
-+                }
-+
-+                ea = laddrs.ea;
-+                if (laddrs.n_ipv6_addrs > 0) {
-+                    ip6_addr = laddrs.ipv6_addrs[0].addr;
-+                    break;
-+                }
-+            }
-+
-+            if (eth_addr_is_zero(ea)) {
-+                continue;
-+            }
-+
-+            if (j == pb->n_mac) {
-+                in6_generate_lla(ea, &ip6_addr);
-+            }
-+
-+            changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, ld,
-+                                              ea, ip6_addr,
-+                                              peer->tunnel_key,
-+                                              peer->datapath->tunnel_key);
-+        }
-+    }
-+
-+    if (changed) {
-+        notify_pinctrl_handler();
-+    }
-+}
-+
- struct buffer_info {
-     struct ofpbuf ofpacts;
-     ofp_port_t ofp_port;
-@@ -2012,6 +2650,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
-         pinctrl_handle_bind_vport(&pin.flow_metadata.flow, &userdata);
-         ovs_mutex_unlock(&pinctrl_mutex);
-         break;
-+    case ACTION_OPCODE_DHCP6_SERVER:
-+        ovs_mutex_lock(&pinctrl_mutex);
-+        pinctrl_handle_dhcp6_server(swconn, &headers, &packet,
-+                                    &pin.flow_metadata);
-+        ovs_mutex_unlock(&pinctrl_mutex);
-+        break;
- 
-     case ACTION_OPCODE_HANDLE_SVC_CHECK:
-         ovs_mutex_lock(&pinctrl_mutex);
-@@ -2090,6 +2734,7 @@ pinctrl_handler(void *arg_)
-     /* Next multicast query (IGMP) in ms. */
-     static long long int send_mcast_query_time = LLONG_MAX;
-     static long long int svc_monitors_next_run_time = LLONG_MAX;
-+    static long long int send_prefixd_time = LLONG_MAX;
- 
-     swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
- 
-@@ -2143,6 +2788,7 @@ pinctrl_handler(void *arg_)
-                 ovs_mutex_lock(&pinctrl_mutex);
-                 send_garp_rarp_run(swconn, &send_garp_rarp_time);
-                 send_ipv6_ras(swconn, &send_ipv6_ra_time);
-+                send_ipv6_prefixd(swconn, &send_prefixd_time);
-                 send_mac_binding_buffered_pkts(swconn);
-                 ovs_mutex_unlock(&pinctrl_mutex);
- 
-@@ -2160,6 +2806,7 @@ pinctrl_handler(void *arg_)
-         ipv6_ra_wait(send_ipv6_ra_time);
-         ip_mcast_querier_wait(send_mcast_query_time);
-         svc_monitors_wait(svc_monitors_next_run_time);
-+        ipv6_prefixd_wait(send_prefixd_time);
- 
-         new_seq = seq_read(pinctrl_handler_seq);
-         seq_wait(pinctrl_handler_seq, new_seq);
-@@ -2211,6 +2858,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                            sbrec_port_binding_by_name, br_int, chassis,
-                            local_datapaths, active_tunnels);
-     prepare_ipv6_ras(local_datapaths);
-+    prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name,
-+                         local_datapaths, chassis, active_tunnels);
-     sync_dns_cache(dns_table);
-     controller_event_run(ovnsb_idl_txn, ce_table, chassis);
-     ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
-@@ -2757,6 +3406,7 @@ pinctrl_destroy(void)
-     free(pinctrl.br_int_name);
-     destroy_send_garps_rarps();
-     destroy_ipv6_ras();
-+    destroy_ipv6_prefixd();
-     destroy_buffered_packets_map();
-     event_table_destroy();
-     destroy_put_mac_bindings();
-@@ -4471,6 +5121,7 @@ may_inject_pkts(void)
- {
-     return (!shash_is_empty(&ipv6_ras) ||
-             !shash_is_empty(&send_garp_rarp_data) ||
-+            ipv6_prefixd_should_inject() ||
-             !ovs_list_is_empty(&mcast_query_list) ||
-             !ovs_list_is_empty(&buffered_mac_bindings));
- }
-diff --git a/include/ovn/actions.h b/include/ovn/actions.h
-index 9b014925b..e3dec99b2 100644
---- a/include/ovn/actions.h
-+++ b/include/ovn/actions.h
-@@ -91,7 +91,8 @@ struct ovn_extend_table;
-     OVNACT(TRIGGER_EVENT,     ovnact_controller_event) \
-     OVNACT(BIND_VPORT,        ovnact_bind_vport)       \
-     OVNACT(HANDLE_SVC_CHECK,  ovnact_handle_svc_check) \
--    OVNACT(FWD_GROUP,         ovnact_fwd_group)
-+    OVNACT(FWD_GROUP,         ovnact_fwd_group)       \
-+    OVNACT(DHCP6_REPLY,       ovnact_null)
- 
- /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
- enum OVS_PACKED_ENUM ovnact_type {
-@@ -577,6 +578,11 @@ enum action_opcode {
-      *     MFF_LOG_INPORT = port
-      */
-     ACTION_OPCODE_HANDLE_SVC_CHECK,
-+    /* handle_dhcpv6_reply { ...actions ...}."
-+     *
-+     *  The actions, in OpenFlow 1.3 format, follow the action_header.
-+     */
-+    ACTION_OPCODE_DHCP6_SERVER,
- };
- 
- /* Header. */
-diff --git a/lib/actions.c b/lib/actions.c
-index 6351db765..02141af30 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2313,6 +2313,20 @@ ovnact_put_opts_free(struct ovnact_put_opts *pdo)
-     free_gen_options(pdo->options, pdo->n_options);
- }
- 
-+static void
-+format_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED, struct ds *s)
-+{
-+    ds_put_cstr(s, "handle_dhcpv6_reply;");
-+}
-+
-+static void
-+encode_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED,
-+                   const struct ovnact_encode_params *ep OVS_UNUSED,
-+                   struct ofpbuf *ofpacts)
-+{
-+    encode_controller_op(ACTION_OPCODE_DHCP6_SERVER, ofpacts);
-+}
-+
- static void
- parse_SET_QUEUE(struct action_context *ctx)
- {
-@@ -3260,6 +3274,8 @@ parse_action(struct action_context *ctx)
-         parse_handle_svc_check(ctx);
-     } else if (lexer_match_id(ctx->lexer, "fwd_group")) {
-         parse_fwd_group_action(ctx);
-+    } else if (lexer_match_id(ctx->lexer, "handle_dhcpv6_reply")) {
-+        ovnact_put_DHCP6_REPLY(ctx->ovnacts);
-     } else {
-         lexer_syntax_error(ctx->lexer, "expecting action");
-     }
-diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
-index 931e6ffcf..cbea2a0c8 100644
---- a/lib/ovn-l7.h
-+++ b/lib/ovn-l7.h
-@@ -178,8 +178,11 @@ struct dhcp_opt6_header {
- #define DHCPV6_OPT_SERVER_ID_CODE        2
- #define DHCPV6_OPT_IA_NA_CODE            3
- #define DHCPV6_OPT_IA_ADDR_CODE          5
-+#define DHCPV6_OPT_STATUS_CODE           13
- #define DHCPV6_OPT_DNS_SERVER_CODE       23
- #define DHCPV6_OPT_DOMAIN_SEARCH_CODE    24
-+#define DHCPV6_OPT_IA_PD                 25
-+#define DHCPV6_OPT_IA_PREFIX             26
- 
- #define DHCPV6_OPT_SERVER_ID \
-     DHCP_OPTION("server_id", DHCPV6_OPT_SERVER_ID_CODE, "mac")
-@@ -258,6 +261,22 @@ struct ovs_nd_route_info {
- };
- BUILD_ASSERT_DECL(ND_ROUTE_INFO_OPT_LEN == sizeof(struct ovs_nd_route_info));
- 
-+OVS_PACKED(
-+struct dhcpv6_opt_ia_prefix {
-+    struct dhcpv6_opt_header opt;
-+    ovs_be32 plife_time;
-+    ovs_be32 vlife_time;
-+    uint8_t plen;
-+    struct in6_addr ipv6;
-+});
-+
-+OVS_PACKED(
-+struct dhcpv6_opt_status {
-+    struct dhcpv6_opt_header opt;
-+    ovs_be16 status_code;
-+    uint8_t msg[];
-+});
-+
- #define DHCPV6_DUID_LL      3
- #define DHCPV6_HW_TYPE_ETH  1
- 
-diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 3ae9d4f92..72466b97e 100644
---- a/ovn-sb.xml
-+++ b/ovn-sb.xml
-@@ -2149,6 +2149,17 @@ tcp.flags = RST;
-           <p><b>Example:</b> <code>handle_svc_check(inport);</code></p>
-         </dd>
- 
-+        <dt><code>handle_dhcpv6_reply;</code></dt>
-+        <dd>
-+          <p>
-+            Handle DHCPv6 prefix delegation advertisements/replies from
-+            a IPv6 delegation server. <code>ovn-controller</code> will
-+            add an entry <code>ipv6_ra_pd_list</code> in the
-+            <ref table="Port_Binding" column="options"/> table for each
-+            prefix received from the delegation server
-+          </p>
-+        </dd>
-+
-         <dt><code><var>R</var> = select(<var>N1</var>[=<var>W1</var>], <var>N2</var>[=<var>W2</var>], ...);</code></dt>
-         <dd>
-           <p>
-@@ -2182,6 +2193,13 @@ tcp.flags = RST;
-           </p>
-         </dd>
- 
-+        <dt><code>handle_dhcpv6_reply;</code></dt>
-+        <dd>
-+          <p>
-+            This action is used to parse DHCPv6 replies from IPv6
-+            Delegation Router and managed IPv6 Prefix delegation state machine
-+          </p>
-+        </dd>
-       </dl>
-     </column>
- 
-diff --git a/tests/ovn.at b/tests/ovn.at
-index e8554f60d..3bc435e6d 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -1525,6 +1525,10 @@ fwd_group();
- fwd_group(liveness="false", childports="eth0", "lsp1");
-     Syntax error at `"false"' expecting `,'.
- 
-+# prefix delegation
-+handle_dhcpv6_reply;
-+    encodes as controller(userdata=00.00.00.13.00.00.00.00)
-+
- # Miscellaneous negative tests.
- ;
-     Syntax error at `;'.
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index 84e5f2b5c..e59698ec4 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -2292,6 +2292,8 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
- 
-         case OVNACT_FWD_GROUP:
-             break;
-+        case OVNACT_DHCP6_REPLY:
-+            break;
-         }
-     }
-     ds_destroy(&s);
--- 
-2.25.2
-
diff --git a/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch b/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch
new file mode 100644
index 0000000..6410d70
--- /dev/null
+++ b/SOURCES/0002-ovn-controller-Persist-the-conjunction-ids-allocated.patch
@@ -0,0 +1,745 @@
+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-ovn-northd-Fix-memory-leak-and-incorrect-limiting-of.patch b/SOURCES/0002-ovn-northd-Fix-memory-leak-and-incorrect-limiting-of.patch
deleted file mode 100644
index b82edf3..0000000
--- a/SOURCES/0002-ovn-northd-Fix-memory-leak-and-incorrect-limiting-of.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From ccaeae4261bc7d35d48a511f71cfc25728786b24 Mon Sep 17 00:00:00 2001
-From: Ilya Maximets <i.maximets@ovn.org>
-Date: Tue, 5 May 2020 13:08:50 +0200
-Subject: [PATCH 2/4] ovn-northd: Fix memory leak and incorrect limiting of
- ECMP routes.
-
-If route count reaches UINT16_MAX, ecmp_groups_add_route() will leak the
-allocated route structure.  Also, since group->route_count incremented
-unconditionally, next attempt to add new route will succeed, because the
-value of 'route_count' is zero now and out of sync with the real number
-of routes.
-
-Fixes: 4e53974bdc4e ("ovn-northd: Support ECMP routes.")
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
----
- northd/ovn-northd.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index ec77ae1a8..dc647d7c5 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -7188,15 +7188,15 @@ static void
- ecmp_groups_add_route(struct ecmp_groups_node *group,
-                       const struct parsed_route *route)
- {
--    struct ecmp_route_list_node *er = xmalloc(sizeof *er);
--    er->route = route;
--    er->id = ++group->route_count;
--    if (er->id == 0) {
-+   if (group->route_count == UINT16_MAX) {
-         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-         VLOG_WARN_RL(&rl, "too many routes in a single ecmp group.");
-         return;
-     }
- 
-+    struct ecmp_route_list_node *er = xmalloc(sizeof *er);
-+    er->route = route;
-+    er->id = ++group->route_count;
-     ovs_list_insert(&group->route_list, &er->list_node);
- }
- 
--- 
-2.26.2
-
diff --git a/SOURCES/0002-ovn-northd-Fix-memory-leak-in-case-of-duplicate-logi.patch b/SOURCES/0002-ovn-northd-Fix-memory-leak-in-case-of-duplicate-logi.patch
deleted file mode 100644
index 3c0eb4c..0000000
--- a/SOURCES/0002-ovn-northd-Fix-memory-leak-in-case-of-duplicate-logi.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 2fdce8ec0e631b759bb03a35457b17060605d887 Mon Sep 17 00:00:00 2001
-From: Ilya Maximets <i.maximets@ovn.org>
-Date: Tue, 12 May 2020 12:46:18 +0200
-Subject: [PATCH 2/2] ovn-northd: Fix memory leak in case of duplicate logical
- router port.
-
-'lrp_networks' must be destroyed on error path.
-
-Fixes: 8e83e561879a ("ovn: Support multiple addresses on a single logical router port.")
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index c1cdb2280..a41e3d46e 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -2125,6 +2125,7 @@ join_logical_ports(struct northd_context *ctx,
-                             = VLOG_RATE_LIMIT_INIT(5, 1);
-                         VLOG_WARN_RL(&rl, "duplicate logical router port %s",
-                                      nbrp->name);
-+                        destroy_lport_addresses(&lrp_networks);
-                         continue;
-                     }
-                     ovn_port_set_nb(op, NULL, nbrp);
--- 
-2.26.2
-
diff --git a/SOURCES/0002-ovn-northd-Fix-tunnel_key-allocation-for-SB-Port_Bin.patch b/SOURCES/0002-ovn-northd-Fix-tunnel_key-allocation-for-SB-Port_Bin.patch
deleted file mode 100644
index 0da2b8f..0000000
--- a/SOURCES/0002-ovn-northd-Fix-tunnel_key-allocation-for-SB-Port_Bin.patch
+++ /dev/null
@@ -1,146 +0,0 @@
-From 0cda584a6ee05830c0611650fe22c642cb39b09f Mon Sep 17 00:00:00 2001
-From: Dumitru Ceara <dceara@redhat.com>
-Date: Thu, 30 Apr 2020 20:32:35 +0200
-Subject: [PATCH 2/2] ovn-northd: Fix tunnel_key allocation for SB
- Port_Bindings.
-
-When generating Port_Binding records ovn-northd tries to reuse the
-tunnel_key value from the original SB record, if any available.
-
-However, there's no check for tunnel_keys that would conflict with
-newly allocated keys for new records. In order to avoid that, we
-don't reuse stale Port_Binding entries, i.e., their "datapath" field
-doesn't match the Datapath_Binding record associated with the
-logical switch/router they're part of.
-
-One way to reproduce the issue is:
-$ ovn-nbctl ls-add ls1
-$ ovn-nbctl ls-add ls2
-$ ovn-nbctl lsp-add ls1 lsp1
-$ ovn-nbctl lsp-add ls2 lsp2
-$ ovn-nbctl --wait=sb sync
-$ ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
-
-Another option to reproduce the issue is with HA_Chassis_Group:
-$ ovn-nbctl ls-add ls1
-$ ovn-nbctl ls-add ls2
-$ ovn-nbctl lsp-add ls1 lsp1
-$ ovn-nbctl lsp-add ls2 lsp2
-$ ovn-nbctl lsp-set-type lsp2 external
-$ ovn-nbctl ha-chassis-group-add chg1
-$ ovn-nbctl ha-chassis-group-add-chassis chg1 chassis-1 30
-$ chg1_uuid=$(ovn-nbctl --bare --columns _uuid list ha_Chassis_Group .)
-$ ovn-nbctl set logical_switch_port lsp2 ha_chassis_group=${chg1_uuid}
-$ ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
-
-Reported-by: Dan Williams <dcbw@redhat.com>
-Reported-at: https://bugzilla.redhat.com/1828637
-Signed-off-by: Dumitru Ceara <dceara@redhat.com>
-Acked-by: Numan Siddique <numans@ovn.org>
-Signed-off-by: Han Zhou <hzhou@ovn.org>
-(cherry picked from upstream commit 8bf9075968ac8b26f1d4d32697f4b117a61a2c49)
-
-Change-Id: I03146f0778ff50375c3f843c0e650e76008bba94
----
- northd/ovn-northd.c |  6 +++---
- tests/ovn-northd.at | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 59 insertions(+), 3 deletions(-)
-
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 5e649d0..bc1ea0b 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -2025,7 +2025,7 @@ join_logical_ports(struct northd_context *ctx,
-                 const struct nbrec_logical_switch_port *nbsp
-                     = od->nbs->ports[i];
-                 struct ovn_port *op = ovn_port_find(ports, nbsp->name);
--                if (op) {
-+                if (op && op->sb->datapath == od->sb) {
-                     if (op->nbsp || op->nbrp) {
-                         static struct vlog_rate_limit rl
-                             = VLOG_RATE_LIMIT_INIT(5, 1);
-@@ -2119,7 +2119,7 @@ join_logical_ports(struct northd_context *ctx,
-                 }
- 
-                 struct ovn_port *op = ovn_port_find(ports, nbrp->name);
--                if (op) {
-+                if (op && op->sb->datapath == od->sb) {
-                     if (op->nbsp || op->nbrp) {
-                         static struct vlog_rate_limit rl
-                             = VLOG_RATE_LIMIT_INIT(5, 1);
-@@ -2171,7 +2171,7 @@ join_logical_ports(struct northd_context *ctx,
-                     char *redirect_name =
-                         ovn_chassis_redirect_name(nbrp->name);
-                     struct ovn_port *crp = ovn_port_find(ports, redirect_name);
--                    if (crp) {
-+                    if (crp && crp->sb->datapath == od->sb) {
-                         crp->derived = true;
-                         ovn_port_set_nb(crp, NULL, nbrp);
-                         ovs_list_remove(&crp->list);
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 94f892b..569390c 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1350,3 +1350,59 @@ lr_uuid=$(ovn-nbctl --columns _uuid list Logical_Router .)
- AT_CHECK[test ${nb_uuid} = ${lr_uuid}]
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- check reconcile stale tunnel keys])
-+ovn_start
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl ls-add ls2
-+ovn-nbctl lsp-add ls1 lsp1
-+ovn-nbctl lsp-add ls2 lsp2
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+# Ports are bound on different datapaths so it's expected that they both
-+# get tunnel_key == 1.
-+AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
-+port_binding logical_port=lsp1)])
-+AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
-+port_binding logical_port=lsp2)])
-+
-+ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
-+port_binding logical_port=lsp1)])
-+AT_CHECK([test 2 = $(ovn-sbctl --bare --columns tunnel_key find \
-+port_binding logical_port=lsp2)])
-+
-+# ovn-northd should allocate a new tunnel_key for lsp1 or lsp2 to maintain
-+# unique DB indices.
-+AT_CHECK([test ${pb1_key} != ${pb2_key}])
-+
-+AT_CLEANUP
-+
-+AT_SETUP([ovn -- check reconcile stale Ha_Chassis_Group])
-+ovn_start
-+
-+ovn-nbctl ls-add ls1
-+ovn-nbctl ls-add ls2
-+ovn-nbctl lsp-add ls1 lsp1
-+ovn-nbctl lsp-add ls2 lsp2
-+
-+ovn-nbctl lsp-set-type lsp2 external
-+
-+ovn-nbctl ha-chassis-group-add chg1
-+ovn-nbctl ha-chassis-group-add-chassis chg1 chassis-1 30
-+
-+chg1_uuid=$(ovn-nbctl --bare --columns _uuid list Ha_Chassis_Group .)
-+ovn-nbctl set logical_switch_port lsp2 ha_chassis_group=${chg1_uuid}
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+# Move lsp2 from ls2 to ls1. This should also remove the SB HA_Chassis_Group
-+# record.
-+ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
-+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
-+
-+AT_CHECK([test 0 = $(ovn-sbctl list Ha_Chassis_Group | wc -l)])
-+
-+AT_CLEANUP
--- 
-1.8.3.1
-
diff --git a/SOURCES/0002-ovn-northd-Skip-unsnat-flows-for-load-balancer-vips-.patch b/SOURCES/0002-ovn-northd-Skip-unsnat-flows-for-load-balancer-vips-.patch
deleted file mode 100644
index c64e616..0000000
--- a/SOURCES/0002-ovn-northd-Skip-unsnat-flows-for-load-balancer-vips-.patch
+++ /dev/null
@@ -1,340 +0,0 @@
-From f075920452dbcaab7c185efd4f63a02bd6e384ce Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Thu, 26 Mar 2020 20:19:12 +0530
-Subject: [PATCH 2/2] ovn-northd: Skip unsnat flows for load balancer vips in
- router ingress pipeline
-
-Suppose there is below NAT entry with external_ip = 172.168.0.100
-
-nat <UUID>
-    external ip: "172.168.0.100"
-    logical ip: "10.0.0.0/24"
-    type: "snat"
-
-And a load balancer with the VIP - 172.168.0.100
-
-_uuid               : <UUID>
-external_ids        : {}
-name                : lb1
-protocol            : tcp
-vips                : {"172.168.0.100:8080"="10.0.0.4:8080"}
-
-And if these are associated to a gateway logical router
-
-Then we will see the below lflows in the router pipeline
-
-...
-table=5 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.100), action=(ct_snat;)
-...
-table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8080), action=(ct_lb(10.0.0.4:8080);)
-table=6 (lr_in_dnat         ), priority=120  , match=(ct.est && ip && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8080), action=(ct_dnat;)
-
-When a new connection packet destinated for the lb vip 172.168.0.100 and tcp.dst = 8080
-is received, the ct.new flow in the lr_in_dnat is hit and the packet's ip4.dst is
-dnatted to 10.0.0.4 in the dnat conntrack zone.
-
-But for the subsequent packet destined to the vip, the ct.est lflow in the lr_in_dnat
-stage doesn't get hit. In this case, the packet first hits the lr_in_unsnat pri 90 flow
-as mentioned above with the action ct_snat. Even though ct_snat should have no effect,
-looks like it is resetting the ct flags.
-
-In the case of tcp, the ct.new flow is hit instead of ct.est. In the the case of sctp, neither of the above
-lflows in lr_in_dnat stage hit.
-
-This needs to be investigated further. But we can avoid this scenario in OVN
-by adding the below lflow.
-
-table=5 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 172.168.0.100 && tcp.dst == 8080), action=(next;)
-
-This patch adds the above lflow if the lb vip also has an entry in the NAT table.
-
-This patch is also required to support sctp load balancers in OVN.
-
-Reported-by: Tim Rozet <trozet@redhat.com>
-Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1815217
-Signed-off-by: Numan Siddique <numans@ovn.org>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-(cherry picked from upstream OVS branch20.03 commit 3cf40e8b35c5d6a4e593b42b96c284a8742235d5)
-
-Change-Id: Ibf609537189e80a9e69e6c968b8e4041ecc9cc40
----
- northd/ovn-northd.8.xml | 27 +++++++++++++++++++
- northd/ovn-northd.c     | 59 +++++++++++++++++++++++++++++++----------
- tests/ovn-northd.at     | 38 ++++++++++++++++++++++++++
- tests/system-ovn.at     | 51 ++++++++++++++++++++++++++++++++++-
- 4 files changed, 160 insertions(+), 15 deletions(-)
-
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 1e0993e07..b5e4d6d84 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2075,6 +2075,33 @@ icmp6 {
-       unSNATted here.
-     </p>
- 
-+    <p>Ingress Table 5: UNSNAT on Gateway and Distributed Routers</p>
-+    <ul>
-+      <li>
-+        <p>
-+          If the Router (Gateway or Distributed) is configured with
-+          load balancers, then below lflows are added:
-+        </p>
-+
-+        <p>
-+          For each IPv4 address <var>A</var> defined as load balancer
-+          VIP with the protocol <var>P</var> (and the protocol port
-+          <var>T</var> if defined) is also present as an
-+          <code>external_ip</code> in the NAT table,
-+          a priority-120 logical flow is added with the match
-+          <code>ip4 &amp;&amp; ip4.dst == <var>A</var> &amp;&amp;
-+          <var>P</var></code> with the action <code>next;</code> to
-+          advance the packet to the next table. If the load balancer
-+          has protocol port <code>B</code> defined, then the match also has
-+          <code><var>P</var>.dst == <var>B</var></code>.
-+        </p>
-+
-+        <p>
-+          The above flows are also added for IPv6 load balancers.
-+        </p>
-+      </li>
-+    </ul>
-+
-     <p>Ingress Table 5: UNSNAT on Gateway Routers</p>
- 
-     <ul>
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index cdaeff401..75c19df62 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -7570,7 +7570,7 @@ 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 is_udp, struct nbrec_load_balancer *lb,
--                   struct shash *meter_groups)
-+                   struct shash *meter_groups, struct sset *nat_entries)
- {
-     build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT,
-                               meter_groups);
-@@ -7603,6 +7603,40 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
-     free(new_match);
-     free(est_match);
- 
-+    const char *ip_match = NULL;
-+    if (lb_vip->addr_family == AF_INET) {
-+        ip_match = "ip4";
-+    } else {
-+        ip_match = "ip6";
-+    }
-+
-+    if (sset_contains(nat_entries, lb_vip->vip)) {
-+        /* The load balancer vip is also present in the NAT entries.
-+         * So add a high priority lflow to advance the the packet
-+         * destined to the vip (and the vip port if defined)
-+         * in the S_ROUTER_IN_UNSNAT stage.
-+         * There seems to be an issue with ovs-vswitchd. When the new
-+         * connection packet destined for the lb vip is received,
-+         * it is dnat'ed in the S_ROUTER_IN_DNAT stage in the dnat
-+         * conntrack zone. For the next packet, if it goes through
-+         * unsnat stage, the conntrack flags are not set properly, and
-+         * it doesn't hit the established state flows in
-+         * S_ROUTER_IN_DNAT stage. */
-+        struct ds unsnat_match = DS_EMPTY_INITIALIZER;
-+        ds_put_format(&unsnat_match, "%s && %s.dst == %s && %s",
-+                      ip_match, ip_match, lb_vip->vip,
-+                      is_udp ? "udp" : "tcp");
-+        if (lb_vip->vip_port) {
-+            ds_put_format(&unsnat_match, " && %s.dst == %d",
-+                          is_udp ? "udp" : "tcp", lb_vip->vip_port);
-+        }
-+
-+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT, 120,
-+                                ds_cstr(&unsnat_match), "next;", &lb->header_);
-+
-+        ds_destroy(&unsnat_match);
-+    }
-+
-     if (!od->l3dgw_port || !od->l3redirect_port || !lb_vip->n_backends) {
-         return;
-     }
-@@ -7612,19 +7646,11 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
-      * router has a gateway router port associated.
-      */
-     struct ds undnat_match = DS_EMPTY_INITIALIZER;
--    if (lb_vip->addr_family == AF_INET) {
--        ds_put_cstr(&undnat_match, "ip4 && (");
--    } else {
--        ds_put_cstr(&undnat_match, "ip6 && (");
--    }
-+    ds_put_format(&undnat_match, "%s && (", ip_match);
- 
-     for (size_t i = 0; i < lb_vip->n_backends; i++) {
-         struct lb_vip_backend *backend = &lb_vip->backends[i];
--        if (backend->addr_family == AF_INET) {
--            ds_put_format(&undnat_match, "(ip4.src == %s", backend->ip);
--        } else {
--            ds_put_format(&undnat_match, "(ip6.src == %s", backend->ip);
--        }
-+        ds_put_format(&undnat_match, "(%s.src == %s", ip_match, backend->ip);
- 
-         if (backend->port) {
-             ds_put_format(&undnat_match, " && %s.src == %d) || ",
-@@ -8875,6 +8901,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                                             &nat->header_);
-                     sset_add(&nat_entries, nat->external_ip);
-                 }
-+            } else {
-+                /* Add the NAT external_ip to the nat_entries even for
-+                 * gateway routers. This is required for adding load balancer
-+                 * flows.*/
-+                sset_add(&nat_entries, nat->external_ip);
-             }
- 
-             /* Egress UNDNAT table: It is for already established connections'
-@@ -9055,8 +9086,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             }
-         }
- 
--        sset_destroy(&nat_entries);
--
-         /* 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
-@@ -9117,6 +9146,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         /* 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) {
-+            sset_destroy(&nat_entries);
-             continue;
-         }
- 
-@@ -9191,10 +9221,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-                 }
-                 add_router_lb_flow(lflows, od, &match, &actions, prio,
-                                    lb_force_snat_ip, lb_vip, is_udp,
--                                   nb_lb, meter_groups);
-+                                   nb_lb, meter_groups, &nat_entries);
-             }
-         }
-         sset_destroy(&all_ips);
-+        sset_destroy(&nat_entries);
-     }
- 
-     /* Logical router ingress table ND_RA_OPTIONS & ND_RA_RESPONSE: IPv6 Router
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index a2989e78e..d127152f5 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1288,3 +1288,41 @@ ovn-nbctl --wait=sb lb-del lb2
- OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- Load balancer VIP in NAT entries])
-+AT_SKIP_IF([test $HAVE_PYTHON = no])
-+ovn_start
-+
-+ovn-nbctl lr-add lr0
-+ovn-nbctl lrp-add lr0 lr0-public 00:00:01:01:02:04 192.168.2.1/24
-+ovn-nbctl lrp-add lr0 lr0-join 00:00:01:01:02:04 10.10.0.1/24
-+
-+ovn-nbctl set logical_router lr0 options:chassis=ch1
-+
-+ovn-nbctl lb-add lb1 "192.168.2.1:8080" "10.0.0.4:8080"
-+ovn-nbctl lb-add lb2 "192.168.2.4:8080" "10.0.0.5:8080" udp
-+ovn-nbctl lb-add lb3 "192.168.2.5:8080" "10.0.0.6:8080"
-+ovn-nbctl lb-add lb4 "192.168.2.6:8080" "10.0.0.7:8080"
-+
-+ovn-nbctl lr-lb-add lr0 lb1
-+ovn-nbctl lr-lb-add lr0 lb2
-+ovn-nbctl lr-lb-add lr0 lb3
-+ovn-nbctl lr-lb-add lr0 lb4
-+
-+ovn-nbctl lr-nat-add lr0 snat 192.168.2.1 10.0.0.0/24
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.2.4 10.0.0.4
-+ovn-nbctl lr-nat-add lr0 dnat 192.168.2.5 10.0.0.5
-+
-+OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl dump-flows lr0 | grep lr_in_unsnat | \
-+grep "ip4 && ip4.dst == 192.168.2.1 && tcp && tcp.dst == 8080" -c) ])
-+
-+AT_CHECK([test 1 = $(ovn-sbctl dump-flows lr0 | grep lr_in_unsnat | \
-+grep "ip4 && ip4.dst == 192.168.2.4 && udp && udp.dst == 8080" -c) ])
-+
-+AT_CHECK([test 1 = $(ovn-sbctl dump-flows lr0 | grep lr_in_unsnat | \
-+grep "ip4 && ip4.dst == 192.168.2.5 && tcp && tcp.dst == 8080" -c) ])
-+
-+AT_CHECK([test 0 = $(ovn-sbctl dump-flows lr0 | grep lr_in_unsnat | \
-+grep "ip4 && ip4.dst == 192.168.2.6 && tcp && tcp.dst == 8080" -c) ])
-+
-+AT_CLEANUP
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 3b3379840..f1ae69b20 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -1635,7 +1635,6 @@ ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:80,192.16
- ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \
-     external_ip=30.0.0.2 -- add logical_router R2 nat @nat
- 
--
- # Wait for ovn-controller to catch up.
- ovn-nbctl --wait=hv sync
- OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-@@ -1671,6 +1670,56 @@ tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(sr
- 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>)
- ])
- 
-+check_est_flows () {
-+    n=$(ovs-ofctl dump-flows br-int table=14 | grep \
-+"priority=120,ct_state=+est+trk,tcp,metadata=0x2,nw_dst=30.0.0.2,tp_dst=8000" \
-+| grep nat | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p')
-+
-+    echo "n_packets=$n"
-+    test "$n" != 0
-+}
-+
-+OVS_WAIT_UNTIL([check_est_flows], [check established flows])
-+
-+
-+ovn-nbctl set logical_router R2 options:lb_force_snat_ip="20.0.0.2"
-+
-+# Destroy the load balancer and create again. ovn-controller will
-+# clear the OF flows and re add again and clears the n_packets
-+# for these flows.
-+ovn-nbctl destroy load_balancer $uuid
-+uuid=`ovn-nbctl  create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2"`
-+ovn-nbctl set logical_router R2 load_balancer=$uuid
-+
-+# Config OVN load-balancer with another VIP (this time with ports).
-+ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:80,192.168.2.2:80"'
-+
-+ovn-nbctl list load_balancer
-+ovn-sbctl dump-flows R2
-+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-flows br-int table=41 | \
-+grep 'nat(src=20.0.0.2)'])
-+
-+dnl Test load-balancing that includes L4 ports in NAT.
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    NS_CHECK_EXEC([alice1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-+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>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) |
-+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+tcp,orig=(src=172.16.1.2,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.2,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>)
-+])
-+
-+OVS_WAIT_UNTIL([check_est_flows], [check established flows])
-+
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
- as ovn-sb
--- 
-2.25.1
-
diff --git a/SOURCES/0003-IPv6-PD-Disable-pd-processing-if-the-router-port-is-.patch b/SOURCES/0003-IPv6-PD-Disable-pd-processing-if-the-router-port-is-.patch
deleted file mode 100644
index 57864aa..0000000
--- a/SOURCES/0003-IPv6-PD-Disable-pd-processing-if-the-router-port-is-.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From fe499ffd96efda2ffd3f7f066faebe6ec41a83f4 Mon Sep 17 00:00:00 2001
-Message-Id: <fe499ffd96efda2ffd3f7f066faebe6ec41a83f4.1588608928.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <0b9d16670d5561d8300d2448cbd4686a3acdc57e.1588608928.git.lorenzo.bianconi@redhat.com>
-References: <0b9d16670d5561d8300d2448cbd4686a3acdc57e.1588608928.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 29 Apr 2020 18:05:31 +0200
-Subject: [PATCH 3/3] IPv6 PD: Disable pd processing if the router port is
- disabled.
-
-Tested-by: Jianlin Shi <jishi@redhat.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- northd/ovn-northd.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -9373,12 +9373,18 @@ build_lrouter_flows(struct hmap *datapat
-         /* enable IPv6 prefix delegation */
-         bool prefix_delegation = smap_get_bool(&op->nbrp->options,
-                                                "prefix_delegation", false);
-+        if (!lrport_is_enabled(op->nbrp)) {
-+            prefix_delegation = false;
-+        }
-         smap_add(&options, "ipv6_prefix_delegation",
-                  prefix_delegation ? "true" : "false");
-         sbrec_port_binding_set_options(op->sb, &options);
- 
-         bool ipv6_prefix = smap_get_bool(&op->nbrp->options,
-                                          "prefix", false);
-+        if (!lrport_is_enabled(op->nbrp)) {
-+            ipv6_prefix = false;
-+        }
-         smap_add(&options, "ipv6_prefix",
-                  ipv6_prefix ? "true" : "false");
-         sbrec_port_binding_set_options(op->sb, &options);
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
new file mode 100644
index 0000000..8cfd40b
--- /dev/null
+++ b/SOURCES/0003-Support-packet-metadata-marking-for-logical-router-p.patch
@@ -0,0 +1,455 @@
+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-Support-selection-fields-in-load-balancer.patch b/SOURCES/0003-Support-selection-fields-in-load-balancer.patch
deleted file mode 100644
index 97a7296..0000000
--- a/SOURCES/0003-Support-selection-fields-in-load-balancer.patch
+++ /dev/null
@@ -1,741 +0,0 @@
-From 2ee7fb81b5f396972ce279547456fd6d57891180 Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Wed, 22 Apr 2020 18:03:57 +0530
-Subject: [PATCH 3/4] Support selection fields in load balancer.
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch add a new column 'selection_fields' in Load_Balancer
-table in NB DB. CMS can define a set of packet headers to use
-while selecting a backend. If this column is set, OVN will add the
-flow in group table with selection method as 'hash' with the set fields.
-Otherwise it will use the default 'dp_hash' selection method.
-
-If a load balancer is configured with the selection_fields as
-selection_fields    : [ip_dst, ip_src, tp_dst, tp_src]
-
-then with this patch, the modified ct_lb action will look like
- - ct_lb(backends=IP1:P1,IP2:P1; hash_fields="ip_dst,ip_src,tp_dst,tp_src");
-
-And the OF flow will look like
- - group_id=2,type=select,selection_method=hash,
-   fields(ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id:0,weight:100,actions=ct(....
-
-Change-Id: Iac595d50d77783fd28bcb1af7b63e9274b94f622
-Tested-by: Maciej Józefczyk <mjozefcz@redhat.com>
-Acked-by: Maciej Józefczyk <mjozefcz@redhat.com>
-Acked-by: Han Zhou <hzhou@ovn.org>
-Acked-by: Mark Michelson <mmichels@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- NEWS                  |   6 ++
- include/ovn/actions.h |   1 +
- lib/actions.c         |  45 +++++++++--
- northd/ovn-northd.c   |  30 ++++++--
- ovn-nb.ovsschema      |  10 ++-
- ovn-nb.xml            |  27 +++++++
- tests/ovn-northd.at   |  24 +++---
- tests/ovn.at          |  49 +++++++-----
- tests/system-ovn.at   | 172 +++++++++++++++++++++++++++++++++++++++---
- 9 files changed, 311 insertions(+), 53 deletions(-)
-
-diff --git a/NEWS b/NEWS
-index e77343c89..15c3453f8 100644
---- a/NEWS
-+++ b/NEWS
-@@ -9,6 +9,12 @@ OVN v20.03.0 - 28 Feb 2020
-    - Added support for ECMP routes in OVN router.
-    - Added IPv6 Prefix Delegation support in OVN.
-    - OVN now uses OpenFlow 1.5.
-+   - Added support to choose selection methods - dp_hash or
-+     hash (with specified hash fields) for OVN load balancer
-+     backend selection. This is incompatible with older versions.
-+     Care should be taken while upgrading as the existing
-+     load balancer traffic will be affected if ovn-controllers
-+     are not stopped before uprading northd services.
- 
-    - OVN Interconnection:
-      * Support for L3 interconnection of multiple OVN deployments with tunnels
-diff --git a/include/ovn/actions.h b/include/ovn/actions.h
-index e3dec99b2..df11a5713 100644
---- a/include/ovn/actions.h
-+++ b/include/ovn/actions.h
-@@ -252,6 +252,7 @@ struct ovnact_ct_lb {
-     struct ovnact_ct_lb_dst *dsts;
-     size_t n_dsts;
-     uint8_t ltable;             /* Logical table ID of next table. */
-+    char *hash_fields;
- };
- 
- struct ovnact_select_dst {
-diff --git a/lib/actions.c b/lib/actions.c
-index 605dbffe4..ee7ccae0d 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -900,9 +900,18 @@ parse_ct_lb_action(struct action_context *ctx)
-     struct ovnact_ct_lb_dst *dsts = NULL;
-     size_t allocated_dsts = 0;
-     size_t n_dsts = 0;
-+    char *hash_fields = NULL;
- 
--    if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
--        while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
-+    if (lexer_match(ctx->lexer, LEX_T_LPAREN) &&
-+        !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
-+        if (!lexer_match_id(ctx->lexer, "backends") ||
-+            !lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
-+            lexer_syntax_error(ctx->lexer, "expecting backends");
-+            return;
-+        }
-+
-+        while (!lexer_match(ctx->lexer, LEX_T_SEMICOLON) &&
-+               !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
-             struct ovnact_ct_lb_dst dst;
-             if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) {
-                 /* IPv6 address and port */
-@@ -969,12 +978,27 @@ parse_ct_lb_action(struct action_context *ctx)
-             }
-             dsts[n_dsts++] = dst;
-         }
-+
-+        if (lexer_match_id(ctx->lexer, "hash_fields")) {
-+            if (!lexer_match(ctx->lexer, LEX_T_EQUALS) ||
-+                ctx->lexer->token.type != LEX_T_STRING ||
-+                lexer_lookahead(ctx->lexer) != LEX_T_RPAREN) {
-+                lexer_syntax_error(ctx->lexer, "invalid hash_fields");
-+                free(dsts);
-+                return;
-+            }
-+
-+            hash_fields = xstrdup(ctx->lexer->token.s);
-+            lexer_get(ctx->lexer);
-+            lexer_get(ctx->lexer);
-+        }
-     }
- 
-     struct ovnact_ct_lb *cl = ovnact_put_CT_LB(ctx->ovnacts);
-     cl->ltable = ctx->pp->cur_ltable + 1;
-     cl->dsts = dsts;
-     cl->n_dsts = n_dsts;
-+    cl->hash_fields = hash_fields;
- }
- 
- static void
-@@ -982,10 +1006,10 @@ format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
- {
-     ds_put_cstr(s, "ct_lb");
-     if (cl->n_dsts) {
--        ds_put_char(s, '(');
-+        ds_put_cstr(s, "(backends=");
-         for (size_t i = 0; i < cl->n_dsts; i++) {
-             if (i) {
--                ds_put_cstr(s, ", ");
-+                ds_put_char(s, ',');
-             }
- 
-             const struct ovnact_ct_lb_dst *dst = &cl->dsts[i];
-@@ -1005,7 +1029,13 @@ format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
-             }
-         }
-         ds_put_char(s, ')');
-+
-+        if (cl->hash_fields) {
-+            ds_chomp(s, ')');
-+            ds_put_format(s, "; hash_fields=\"%s\")", cl->hash_fields);
-+        }
-     }
-+
-     ds_put_char(s, ';');
- }
- 
-@@ -1052,7 +1082,11 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
-                             : MFF_LOG_DNAT_ZONE - MFF_REG0;
- 
-     struct ds ds = DS_EMPTY_INITIALIZER;
--    ds_put_format(&ds, "type=select,selection_method=dp_hash");
-+    ds_put_format(&ds, "type=select,selection_method=%s",
-+                  cl->hash_fields ? "hash": "dp_hash");
-+    if (cl->hash_fields) {
-+        ds_put_format(&ds, ",fields(%s)", cl->hash_fields);
-+    }
- 
-     BUILD_ASSERT(MFF_LOG_CT_ZONE >= MFF_REG0);
-     BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS);
-@@ -1094,6 +1128,7 @@ static void
- ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb)
- {
-     free(ct_lb->dsts);
-+    free(ct_lb->hash_fields);
- }
- 
- static void
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index dc647d7c5..b07e68cfa 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -3108,7 +3108,7 @@ struct ovn_lb {
-     struct hmap_node hmap_node;
- 
-     const struct nbrec_load_balancer *nlb; /* May be NULL. */
--
-+    char *selection_fields;
-     struct lb_vip *vips;
-     size_t n_vips;
- };
-@@ -3336,6 +3336,15 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
-         n_vips++;
-     }
- 
-+    if (lb->nlb->n_selection_fields) {
-+        struct ds sel_fields = DS_EMPTY_INITIALIZER;
-+        for (size_t i = 0; i < lb->nlb->n_selection_fields; i++) {
-+            ds_put_format(&sel_fields, "%s,", lb->nlb->selection_fields[i]);
-+        }
-+        ds_chomp(&sel_fields, ',');
-+        lb->selection_fields = ds_steal_cstr(&sel_fields);
-+    }
-+
-     return lb;
- }
- 
-@@ -3354,13 +3363,15 @@ ovn_lb_destroy(struct ovn_lb *lb)
-         free(lb->vips[i].backends);
-     }
-     free(lb->vips);
-+    free(lb->selection_fields);
- }
- 
- static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
--                                       struct ds *action)
-+                                       struct ds *action,
-+                                       char *selection_fields)
- {
-     if (lb_vip->health_check) {
--        ds_put_cstr(action, "ct_lb(");
-+        ds_put_cstr(action, "ct_lb(backends=");
- 
-         size_t n_active_backends = 0;
-         for (size_t k = 0; k < lb_vip->n_backends; k++) {
-@@ -3384,7 +3395,13 @@ static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
-             ds_put_cstr(action, ");");
-         }
-     } else {
--        ds_put_format(action, "ct_lb(%s);", lb_vip->backend_ips);
-+        ds_put_format(action, "ct_lb(backends=%s);", lb_vip->backend_ips);
-+    }
-+
-+    if (selection_fields && selection_fields[0]) {
-+        ds_chomp(action, ';');
-+        ds_chomp(action, ')');
-+        ds_put_format(action, "; hash_fields=\"%s\");", selection_fields);
-     }
- }
- 
-@@ -5660,7 +5677,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, struct ovn_lb *lb)
- 
-         /* New connections in Ingress table. */
-         struct ds action = DS_EMPTY_INITIALIZER;
--        build_lb_vip_ct_lb_actions(lb_vip, &action);
-+        build_lb_vip_ct_lb_actions(lb_vip, &action, lb->selection_fields);
- 
-         struct ds match = DS_EMPTY_INITIALIZER;
-         ds_put_format(&match, "ct.new && %s.dst == %s", ip_match, lb_vip->vip);
-@@ -9290,7 +9307,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             for (size_t j = 0; j < lb->n_vips; j++) {
-                 struct lb_vip *lb_vip = &lb->vips[j];
-                 ds_clear(&actions);
--                build_lb_vip_ct_lb_actions(lb_vip, &actions);
-+                build_lb_vip_ct_lb_actions(lb_vip, &actions,
-+                                           lb->selection_fields);
- 
-                 if (!sset_contains(&all_ips, lb_vip->vip)) {
-                     sset_add(&all_ips, lb_vip->vip);
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index 949f6258b..359201c26 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
--    "version": "5.22.0",
--    "cksum": "170077561 25417",
-+    "version": "5.23.0",
-+    "cksum": "3367447924 25747",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -179,6 +179,12 @@
-                 "ip_port_mappings": {
-                     "type": {"key": "string", "value": "string",
-                              "min": 0, "max": "unlimited"}},
-+                "selection_fields": {
-+                    "type": {"key": {"type": "string",
-+                             "enum": ["set",
-+                                ["eth_src", "eth_dst", "ip_src", "ip_dst",
-+                                 "tp_src", "tp_dst"]]},
-+                             "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 045c63fb0..55f0ef9f6 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -1497,6 +1497,33 @@
-       </p>
-     </column>
- 
-+    <column name="selection_fields">
-+      <p>
-+        OVN native load balancers are supported using the OpenFlow groups
-+        of type <code>select</code>. OVS supports two selection methods:
-+        <code>dp_hash</code> and <code>hash (with optional fields
-+        specified)</code> in selecting the buckets of a group.
-+        Please see the OVS documentation (man ovs-ofctl)
-+        for more details on the selection methods. Each endpoint IP (and port
-+        if set) is mapped to a bucket in the group flow.
-+      </p>
-+
-+      <p>
-+        CMS can choose the <code>hash</code> selection method by setting the
-+        selection fields in this column. <code>ovs-vswitchd</code> uses the
-+        specified fields in generating the hash.
-+      </p>
-+
-+      <p>
-+        <code>dp_hash</code> selection method uses the assistance of
-+        datapath to calculate the hash and it is expected to be
-+        faster than <code>hash</code> selection method. So CMS should take
-+        this into consideration before using the <code>hash</code> method.
-+        Please consult the OVS documentation and OVS sources for the
-+        implementation details.
-+      </p>
-+    </column>
-+
-     <group title="Common Columns">
-       <column name="external_ids">
-         See <em>External IDs</em> at the beginning of this document.
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 569390cee..e6a8c04da 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1119,7 +1119,7 @@ ovn-nbctl --wait=sb ls-lb-add sw0 lb1
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- # Delete the Load_Balancer_Health_Check
-@@ -1128,7 +1128,7 @@ OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- # Create the Load_Balancer_Health_Check again.
-@@ -1141,7 +1141,7 @@ service_monitor | sed '/^$/d' | wc -l`])
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- # Get the uuid of both the service_monitor
-@@ -1157,7 +1157,7 @@ OVS_WAIT_UNTIL([
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
- ])
- 
- # Set the service monitor for sw0-p1 to offline
-@@ -1187,7 +1187,7 @@ OVS_WAIT_UNTIL([
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- # Set the service monitor for sw1-p1 to error
-@@ -1199,7 +1199,7 @@ OVS_WAIT_UNTIL([
- ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
- | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
- ])
- 
- # Add one more vip to lb1
-@@ -1229,8 +1229,8 @@ service_monitor port=1000 | sed '/^$/d' | wc -l`])
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000);)
- ])
- 
- # Set the service monitor for sw1-p1 to online
-@@ -1242,16 +1242,16 @@ OVS_WAIT_UNTIL([
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
- ])
- 
- # Associate lb1 to sw1
- ovn-nbctl --wait=sb ls-lb-add sw1 lb1
- ovn-sbctl dump-flows sw1 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
- ])
- 
- # Now create lb2 same as lb1 but udp protocol.
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 5fb100ad4..ae3b44cb3 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -968,29 +968,42 @@ ct_lb();
-     encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat)
-     has prereqs ip
- 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]))
-     has prereqs ip
--ct_lb(192.168.1.2, 192.168.1.3, );
--    formats as ct_lb(192.168.1.2, 192.168.1.3);
-+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]))
-     has prereqs ip
--ct_lb(fd0f::2, fd0f::3, );
--    formats as ct_lb(fd0f::2, fd0f::3);
-+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]))
-     has prereqs ip
- 
--ct_lb(192.168.1.2:);
-+ct_lb(backends=192.168.1.2:);
-     Syntax error at `)' expecting port number.
--ct_lb(192.168.1.2:123456);
-+ct_lb(backends=192.168.1.2:123456);
-     Syntax error at `123456' expecting port number.
--ct_lb(foo);
-+ct_lb(backends=foo);
-     Syntax error at `foo' expecting IP address.
--ct_lb([192.168.1.2]);
-+ct_lb(backends=[192.168.1.2]);
-     Syntax error at `192.168.1.2' expecting IPv6 address.
- 
-+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]))
-+    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]))
-+    has prereqs ip
-+
- # ct_next
- ct_next;
-     encodes as ct(table=19,zone=NXM_NX_REG13[0..15])
-@@ -1491,13 +1504,13 @@ handle_svc_check(reg0);
- # select
- reg9[16..31] = select(1=50, 2=100, 3, );
-     formats as reg9[16..31] = select(1=50, 2=100, 3=100);
--    encodes as group:4
--    uses group: id(4), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
-+    encodes as group:6
-+    uses group: id(6), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
- 
- reg0 = select(1, 2);
-     formats as reg0 = select(1=100, 2=100);
--    encodes as group:5
--    uses group: id(5), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
-+    encodes as group:7
-+    uses group: id(7), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
- 
- reg0 = select(1=, 2);
-     Syntax error at `,' expecting weight.
-@@ -1513,12 +1526,12 @@ reg0[0..14] = select(1, 2, 3);
-     cannot use 15-bit field reg0[0..14] for "select", which requires at least 16 bits.
- 
- fwd_group(liveness="true", childports="eth0", "lsp1");
--    encodes as group:6
--    uses group: id(6), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
-+    encodes as group:8
-+    uses group: id(8), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
- 
- fwd_group(childports="eth0", "lsp1");
--    encodes as group:7
--    uses group: id(7), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
-+    encodes as group:9
-+    uses group: id(9), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
- 
- fwd_group(childports=eth0);
-     Syntax error at `eth0' expecting logical switch port.
-@@ -17916,12 +17929,12 @@ service_monitor | sed '/^$/d' | wc -l`])
- 
- ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- ovn-sbctl dump-flows lr0 | grep ct_lb | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt], [0], [dnl
--  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
-+  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
- 
- # get the svc monitor mac.
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 117f1e835..9a5ef1ec3 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -1095,15 +1095,15 @@ ovn-nbctl lsp-add bar bar3 \
- -- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4"
- 
- # Config OVN load-balancer with a VIP.
--uuid=`ovn-nbctl  create load_balancer vips:30.0.0.1="172.16.1.2,172.16.1.3,172.16.1.4"`
--ovn-nbctl set logical_switch foo load_balancer=$uuid
-+ovn-nbctl lb-add lb1 30.0.0.1 "172.16.1.2,172.16.1.3,172.16.1.4"
-+ovn-nbctl ls-lb-add foo lb1
- 
- # Create another load-balancer with another VIP.
--uuid=`ovn-nbctl create load_balancer vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"`
--ovn-nbctl add logical_switch foo load_balancer $uuid
-+lb2_uuid=`ovn-nbctl create load_balancer name=lb2 vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"`
-+ovn-nbctl ls-lb-add foo lb2
- 
- # Config OVN load-balancer with another VIP (this time with ports).
--ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"'
-+ovn-nbctl set load_balancer $lb2_uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"'
- 
- # Wait for ovn-controller to catch up.
- ovn-nbctl --wait=hv sync
-@@ -1157,6 +1157,82 @@ tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(s
- 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>)
- ])
- 
-+# Configure selection_fields.
-+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
-+OVS_WAIT_UNTIL([
-+    test $(ovs-ofctl dump-groups br-int | \
-+    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+dnl Test load-balancing that includes L4 ports in NAT.
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-+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>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+echo "foo" > foo
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    ip netns exec foo1 nc -p 30000 30.0.0.2 8000 < foo
-+done
-+
-+dnl Only one backend should be chosen.
-+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 -c) -eq 1])
-+
-+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src"
-+OVS_WAIT_UNTIL([
-+    test $(ovs-ofctl dump-groups br-int | \
-+    grep "selection_method=hash,fields=ip_src" -c) -eq 2
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    ip netns exec foo1 nc 30.0.0.2 8000 < foo
-+done
-+
-+dnl Only one backend should be chosen as eth_src and ip_src is fixed.
-+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.2 -c)
-+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.3 -c)
-+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.4 -c)
-+
-+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1 -c) -ne 0])
-+
-+if [[ "$bar1_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar1_ct -eq 0])
-+fi
-+
-+if [[ "$bar2_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar2_ct -eq 0])
-+fi
-+
-+if [[ "$bar3_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar3_ct -eq 0])
-+fi
- 
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
-@@ -1246,11 +1322,11 @@ uuid=`ovn-nbctl  create load_balancer vips:\"fd03::1\"=\"fd02::2,fd02::3,fd02::4
- ovn-nbctl set logical_switch foo load_balancer=$uuid
- 
- # Create another load-balancer with another VIP.
--uuid=`ovn-nbctl create load_balancer vips:\"fd03::3\"=\"fd02::2,fd02::3,fd02::4\"`
--ovn-nbctl add logical_switch foo load_balancer $uuid
-+lb2_uuid=`ovn-nbctl create load_balancer vips:\"fd03::3\"=\"fd02::2,fd02::3,fd02::4\"`
-+ovn-nbctl add logical_switch foo load_balancer $lb2_uuid
- 
- # Config OVN load-balancer with another VIP (this time with ports).
--ovn-nbctl set load_balancer $uuid vips:'"[[fd03::2]]:8000"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
-+ovn-nbctl set load_balancer $lb2_uuid vips:'"[[fd03::2]]:8000"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
- 
- # Wait for ovn-controller to catch up.
- ovn-nbctl --wait=hv sync
-@@ -1304,7 +1380,83 @@ tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd
- 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>)
- ])
- 
-+# Configure selection_fields.
-+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
-+OVS_WAIT_UNTIL([
-+    test $(ovs-ofctl dump-groups br-int | \
-+    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+dnl Test load-balancing that includes L4 ports in NAT.
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    NS_CHECK_EXEC([foo1], [wget http://[[fd03::2]]:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-+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>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+echo "foo" > foo
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    ip netns exec foo1 nc -6 -p 30000 fd03::2 8000 < foo
-+done
-+
-+# Only one backend should be chosen. Since the source port is fixed,
-+# there should be only one conntrack entry.
-+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])
-+
-+ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ip_src"
-+OVS_WAIT_UNTIL([
-+    test $(ovs-ofctl dump-groups br-int | \
-+    grep "selection_method=hash,fields(eth_src,ip_src)" -c) -eq 2
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+for i in `seq 1 20`; do
-+    echo Request $i
-+    ip netns exec foo1 nc -6 fd03::2 8000 < foo
-+done
- 
-+dnl Only one backend should be chosen as eth_src and ip_src is fixed.
-+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd03::2 | grep fd02::2 -c)
-+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep fd02::3 -c)
-+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep fd02::4 -c)
-+
-+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 | grep fd02 -c) -ne 0])
-+
-+if [[ "$bar1_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar1_ct -eq 0])
-+fi
-+
-+if [[ "$bar2_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar2_ct -eq 0])
-+fi
-+
-+if [[ "$bar3_ct" == "20" ]]; then
-+    AT_CHECK([test $bar1_ct -eq 20])
-+    AT_CHECK([test $bar2_ct -eq 0])
-+    AT_CHECK([test $bar3_ct -eq 0])
-+else
-+    AT_CHECK([test $bar3_ct -eq 0])
-+fi
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
- as ovn-sb
-@@ -3448,7 +3600,7 @@ service_monitor | sed '/^$/d' | grep online | wc -l`])
- 
- OVS_WAIT_UNTIL(
-     [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
--     test 1 = `cat lflows.txt | grep "ct_lb(10.0.0.3:80,20.0.0.3:80)" | wc -l`]
-+     test 1 = `cat lflows.txt | grep "ct_lb(backends=10.0.0.3:80,20.0.0.3:80)" | wc -l`]
- )
- 
- # From sw0-p2 send traffic to vip - 10.0.0.10
-@@ -3474,7 +3626,7 @@ service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`])
- 
- OVS_WAIT_UNTIL(
-     [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
--     test 1 = `cat lflows.txt | grep "ct_lb(20.0.0.3:80)" | wc -l`]
-+     test 1 = `cat lflows.txt | grep "ct_lb(backends=20.0.0.3:80)" | wc -l`]
- )
- 
- ovs-appctl dpctl/flush-conntrack
--- 
-2.26.2
-
diff --git a/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch b/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch
deleted file mode 100644
index 9f3209d..0000000
--- a/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 754d5581fa9d5de97f7c2acf8c2900e105f588c9 Mon Sep 17 00:00:00 2001
-Message-Id: <754d5581fa9d5de97f7c2acf8c2900e105f588c9.1590585469.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
-References: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 20 May 2020 22:01:16 +0200
-Subject: [PATCH ovn 2/3] controller: fix ip buffering with static routes
-
-When the arp request is sent to a gw router and not to the final
-destination of the packet buffered_packets_map needs to be updated using
-next-hop ip address and not the destionation one.
-
-Fixes: 2e5cdb4b1392 ("OVN: add buffering support for ip packets")
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
----
- controller/pinctrl.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -1378,8 +1378,7 @@ pinctrl_find_buffered_packets(const stru
- 
- /* Called with in the pinctrl_handler thread context. */
- static int
--pinctrl_handle_buffered_packets(const struct flow *ip_flow,
--                                struct dp_packet *pkt_in,
-+pinctrl_handle_buffered_packets(struct dp_packet *pkt_in,
-                                 const struct match *md, bool is_arp)
-     OVS_REQUIRES(pinctrl_mutex)
- {
-@@ -1388,9 +1387,10 @@ pinctrl_handle_buffered_packets(const st
-     struct in6_addr addr;
- 
-     if (is_arp) {
--        addr = in6_addr_mapped_ipv4(ip_flow->nw_dst);
-+        addr = in6_addr_mapped_ipv4(htonl(md->flow.regs[0]));
-     } else {
--        addr = ip_flow->ipv6_dst;
-+        ovs_be128 ip6 = hton128(flow_get_xxreg(&md->flow, 0));
-+        memcpy(&addr, &ip6, sizeof addr);
-     }
- 
-     uint32_t hash = hash_bytes(&addr, sizeof addr, 0);
-@@ -1431,7 +1431,7 @@ pinctrl_handle_arp(struct rconn *swconn,
-     }
- 
-     ovs_mutex_lock(&pinctrl_mutex);
--    pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, true);
-+    pinctrl_handle_buffered_packets(pkt_in, md, true);
-     ovs_mutex_unlock(&pinctrl_mutex);
- 
-     /* Compose an ARP packet. */
-@@ -5278,7 +5278,7 @@ pinctrl_handle_nd_ns(struct rconn *swcon
-     }
- 
-     ovs_mutex_lock(&pinctrl_mutex);
--    pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, false);
-+    pinctrl_handle_buffered_packets(pkt_in, md, false);
-     ovs_mutex_unlock(&pinctrl_mutex);
- 
-     uint64_t packet_stub[128 / 8];
diff --git a/SOURCES/0003-logical-fields-fix-memory-leak-caused-by-initialize-.patch b/SOURCES/0003-logical-fields-fix-memory-leak-caused-by-initialize-.patch
deleted file mode 100644
index 26e8773..0000000
--- a/SOURCES/0003-logical-fields-fix-memory-leak-caused-by-initialize-.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-From e6b687cc23212e0c007d9f69dfa89536c8f92306 Mon Sep 17 00:00:00 2001
-From: Damijan Skvarc <damjan.skvarc@gmail.com>
-Date: Thu, 5 Mar 2020 07:21:41 +0100
-Subject: [PATCH 3/3] logical-fields: fix memory leak caused by initialize
- ovnfield_by_name twice
-
-ovnfield_by_name is hash of strings which is used to quickly find
-field by name. This hash is initialized from ovn_init_symtab(). In case
-the latter function is called multiple times then also ovnfield_by_name is
-initialized multiple times but without freeing previously allocated
-memory resources what cause memory leaks.  This actually happens in
-ovn-controller which calls ovn_init_symtab() function twice, once from
-ofctrl.c and the other time from lflow.c files.
-
-Problem was solved by initializing ovnfield_by_name entity only once
-and using design pattern from stopwatch.c or meta_flow.c files (ovs).
-
-Problem was reported by valgrind with flood of messages (190) while executing
-ovn test suite:
-
-    ==5999== 47 (32 direct, 15 indirect) bytes in 1 blocks are definitely lost in loss record 86 of 102
-    ==5999==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
-    ==5999==    by 0x50635D: xmalloc (util.c:138)
-    ==5999==    by 0x4F6513: shash_add_nocopy__ (shash.c:109)
-    ==5999==    by 0x4F6585: shash_add_nocopy (shash.c:121)
-    ==5999==    by 0x4F65BD: shash_add (shash.c:129)
-    ==5999==    by 0x4F6602: shash_add_once (shash.c:136)
-    ==5999==    by 0x4395B7: ovn_init_symtab (logical-fields.c:261)
-    ==5999==    by 0x406C91: main (ovn-controller.c:1750)
-
-Signed-off-by: Damijan Skvarc <damjan.skvarc@gmail.com>
-Signed-off-by: Ben Pfaff <blp@ovn.org>
----
- controller/lflow.c           |  1 -
- include/ovn/logical-fields.h |  1 -
- lib/logical-fields.c         | 39 +++++++++++++++++++++++++-----------
- 3 files changed, 27 insertions(+), 14 deletions(-)
-
-diff --git a/controller/lflow.c b/controller/lflow.c
-index ee11fc617..01214a3a6 100644
---- a/controller/lflow.c
-+++ b/controller/lflow.c
-@@ -846,5 +846,4 @@ lflow_destroy(void)
- {
-     expr_symtab_destroy(&symtab);
-     shash_destroy(&symtab);
--    ovn_destroy_ovnfields();
- }
-diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
-index 9b7c34fb7..c7bd2dba9 100644
---- a/include/ovn/logical-fields.h
-+++ b/include/ovn/logical-fields.h
-@@ -130,5 +130,4 @@ ovn_field_from_id(enum ovn_field_id id)
- const char *event_to_string(enum ovn_controller_event event);
- int string_to_event(const char *s);
- const struct ovn_field *ovn_field_from_name(const char *name);
--void ovn_destroy_ovnfields(void);
- #endif /* ovn/lib/logical-fields.h */
-diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index 25ace5840..a007085b3 100644
---- a/lib/logical-fields.c
-+++ b/lib/logical-fields.c
-@@ -254,12 +254,6 @@ 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);
- 
--    shash_init(&ovnfield_by_name);
--    for (int i = 0; i < OVN_FIELD_N_IDS; i++) {
--        const struct ovn_field *of = &ovn_fields[i];
--        ovs_assert(of->id == i); /* Fields must be in the enum order. */
--        shash_add_once(&ovnfield_by_name, of->name, of);
--    }
-     expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU);
- }
- 
-@@ -284,14 +278,35 @@ string_to_event(const char *s)
-     return -1;
- }
- 
--const struct ovn_field *
--ovn_field_from_name(const char *name)
-+static void
-+ovn_destroy_ovnfields(void)
- {
--    return shash_find_data(&ovnfield_by_name, name);
-+    shash_destroy(&ovnfield_by_name);
- }
- 
--void
--ovn_destroy_ovnfields(void)
-+static void
-+ovn_do_init_ovnfields(void)
- {
--    shash_destroy(&ovnfield_by_name);
-+    shash_init(&ovnfield_by_name);
-+    for (int i = 0; i < OVN_FIELD_N_IDS; i++) {
-+       const struct ovn_field *of = &ovn_fields[i];
-+       ovs_assert(of->id == i); /* Fields must be in the enum order. */
-+       shash_add_once(&ovnfield_by_name, of->name, of);
-+    }
-+    atexit(ovn_destroy_ovnfields);
-+}
-+
-+static void
-+ovn_init_ovnfields(void)
-+{
-+    static pthread_once_t once = PTHREAD_ONCE_INIT;
-+    pthread_once(&once, ovn_do_init_ovnfields);
-+}
-+
-+const struct ovn_field *
-+ovn_field_from_name(const char *name)
-+{
-+    ovn_init_ovnfields();
-+
-+    return shash_find_data(&ovnfield_by_name, name);
- }
--- 
-2.24.1
-
diff --git a/SOURCES/0003-northd-Add-logical-flows-for-dhcpv6-pfd-parsing.patch b/SOURCES/0003-northd-Add-logical-flows-for-dhcpv6-pfd-parsing.patch
deleted file mode 100644
index 79d5a4f..0000000
--- a/SOURCES/0003-northd-Add-logical-flows-for-dhcpv6-pfd-parsing.patch
+++ /dev/null
@@ -1,449 +0,0 @@
-From 643d4be1b3f40c3075af584d0cbc83e34a5e51ca Mon Sep 17 00:00:00 2001
-Message-Id: <643d4be1b3f40c3075af584d0cbc83e34a5e51ca.1586727203.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
-References: <2e84aada0b45d2f8739c2fdbc351098fc1c09c26.1586727203.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Wed, 1 Apr 2020 18:37:31 +0200
-Subject: [PATCH 3/3] northd: Add logical flows for dhcpv6 pfd parsing
-
-Introduce logical flows in ovn router pipeline in order to parse dhcpv6
-advertise/reply from IPv6 prefix delegation router.
-Do not overwrite ipv6_ra_pd_list info in options column of SB port_binding
-table written by ovn-controller
-Introduce ipv6_prefix column in NB Logical_router_port table to report
-IPv6 prefix received from delegation router to the CMS
-
-Change-Id: Ibc1bd83bf3d9d4671f70610df5635e6266580a18
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- NEWS                    |   1 +
- northd/ovn-northd.8.xml |   8 +++
- northd/ovn-northd.c     |  95 ++++++++++++++++++++++++--
- ovn-nb.ovsschema        |   7 +-
- ovn-nb.xml              |  21 ++++++
- tests/atlocal.in        |   5 +-
- tests/system-ovn.at     | 143 ++++++++++++++++++++++++++++++++++++++++
- 7 files changed, 272 insertions(+), 8 deletions(-)
-
-diff --git a/NEWS b/NEWS
-index 9b36bfd17..21c80f0dc 100644
---- a/NEWS
-+++ b/NEWS
-@@ -7,6 +7,7 @@ OVN v20.03.0 - 28 Feb 2020
-    - Added Forwarding Group support in OVN.
-    - Added support for MLD Snooping and MLD Querier.
-    - Added support for ECMP routes in OVN router.
-+   - Added IPv6 Prefix Delegation support in OVN.
- 
-    - OVN Interconnection:
-      * Support for L3 interconnection of multiple OVN deployments with tunnels
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index b5e4d6d84..82c86f636 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -1660,6 +1660,14 @@ next;
-         </ul>
-       </li>
- 
-+      <li>
-+          A priority-100 flow parses DHCPv6 replies from IPv6 prefix
-+          delegation routers (<code>udp.src == 547 &amp;&amp;
-+          udp.dst == 546</code>). The <code>handle_dhcpv6_reply</code>
-+          is used to send IPv6 prefix delegation messages to the delegation
-+          router.
-+      </li>
-+
-       <li>
-         <p>
-           A priority-87 flow explicitly allows IPv6 multicast traffic that is
-diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index bb68b8fe9..fd1be5b27 100644
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -2688,6 +2688,33 @@ op_get_name(const struct ovn_port *op)
-     return name;
- }
- 
-+static void
-+ovn_update_ipv6_prefix(struct hmap *ports)
-+{
-+    const struct ovn_port *op;
-+    HMAP_FOR_EACH (op, key_node, ports) {
-+        if (!op->nbrp) {
-+            continue;
-+        }
-+
-+        char prefix[IPV6_SCAN_LEN + 6];
-+        unsigned aid;
-+        const char *ipv6_pd_list = smap_get(&op->sb->options,
-+                                            "ipv6_ra_pd_list");
-+        if (!ipv6_pd_list ||
-+            !ovs_scan(ipv6_pd_list, "%u:%s", &aid, prefix)) {
-+            continue;
-+        }
-+
-+        struct sset ipv6_prefix_set = SSET_INITIALIZER(&ipv6_prefix_set);
-+        sset_add(&ipv6_prefix_set, prefix);
-+        nbrec_logical_router_port_set_ipv6_prefix(op->nbrp,
-+                                            sset_array(&ipv6_prefix_set),
-+                                            sset_count(&ipv6_prefix_set));
-+        sset_destroy(&ipv6_prefix_set);
-+    }
-+}
-+
- static void
- ovn_port_update_sbrec(struct northd_context *ctx,
-                       struct ovsdb_idl_index *sbrec_chassis_by_name,
-@@ -2818,6 +2845,13 @@ ovn_port_update_sbrec(struct northd_context *ctx,
-                 smap_add(&new, "l3gateway-chassis", chassis_name);
-             }
-         }
-+
-+        const char *ipv6_pd_list = smap_get(&op->sb->options,
-+                                            "ipv6_ra_pd_list");
-+        if (ipv6_pd_list) {
-+            smap_add(&new, "ipv6_ra_pd_list", ipv6_pd_list);
-+        }
-+
-         sbrec_port_binding_set_options(op->sb, &new);
-         smap_destroy(&new);
- 
-@@ -4683,12 +4717,12 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-          * unreachable packets. */
-         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
-                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || (tcp && tcp.flags == 20)",
--                      "next;");
-+                      "icmp6.type == 1 || (tcp && tcp.flags == 20) || "
-+                      "(udp && udp.src == 546 && udp.dst == 547)", "next;");
-         ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
-                       "nd || nd_rs || nd_ra || icmp4.type == 3 || "
--                      "icmp6.type == 1 || (tcp && tcp.flags == 20)",
--                      "next;");
-+                      "icmp6.type == 1 || (tcp && tcp.flags == 20) ||"
-+                      "(udp && udp.src == 546 && udp.dst == 547)", "next;");
- 
-         /* Ingress and Egress Pre-ACL Table (Priority 100).
-          *
-@@ -7744,6 +7778,11 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
-         }
-         ds_put_format(&s, "%s/%u ", addrs->network_s, addrs->plen);
-     }
-+
-+    const char *ra_pd_list = smap_get(&op->sb->options, "ipv6_ra_pd_list");
-+    if (ra_pd_list) {
-+        ds_put_cstr(&s, ra_pd_list);
-+    }
-     /* Remove trailing space */
-     ds_chomp(&s, ' ');
-     smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s));
-@@ -8488,7 +8527,34 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-         free(snat_ips);
-     }
- 
--    /* Logical router ingress table 3: IP Input for IPv6. */
-+    /* DHCPv6 reply handling */
-+    HMAP_FOR_EACH (op, key_node, ports) {
-+        if (!op->nbrp) {
-+            continue;
-+        }
-+
-+        if (op->derived) {
-+            continue;
-+        }
-+
-+        struct lport_addresses lrp_networks;
-+        if (!extract_lrp_networks(op->nbrp, &lrp_networks)) {
-+            continue;
-+        }
-+
-+        for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) {
-+            ds_clear(&actions);
-+            ds_clear(&match);
-+            ds_put_format(&match, "ip6.dst == %s && udp.src == 547 &&"
-+                          " udp.dst == 546",
-+                          lrp_networks.ipv6_addrs[i].addr_s);
-+            ds_put_format(&actions, "reg0 = 0; handle_dhcpv6_reply;");
-+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
-+                          ds_cstr(&match), ds_cstr(&actions));
-+        }
-+    }
-+
-+    /* Logical router ingress table 1: IP Input for IPv6. */
-     HMAP_FOR_EACH (op, key_node, ports) {
-         if (!op->nbrp) {
-             continue;
-@@ -9250,6 +9316,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
-             continue;
-         }
- 
-+        struct smap options;
-+        /* enable IPv6 prefix delegation */
-+        bool prefix_delegation = smap_get_bool(&op->nbrp->options,
-+                                               "prefix_delegation", false);
-+        if (prefix_delegation) {
-+            smap_clone(&options, &op->sb->options);
-+            smap_add(&options, "ipv6_prefix_delegation", "true");
-+            sbrec_port_binding_set_options(op->sb, &options);
-+            smap_destroy(&options);
-+        }
-+
-+        if (smap_get_bool(&op->nbrp->options, "prefix", false)) {
-+            smap_clone(&options, &op->sb->options);
-+            smap_add(&options, "ipv6_prefix", "true");
-+            sbrec_port_binding_set_options(op->sb, &options);
-+            smap_destroy(&options);
-+        }
-+
-         const char *address_mode = smap_get(
-             &op->nbrp->ipv6_ra_configs, "address_mode");
- 
-@@ -10941,6 +11025,7 @@ ovnnb_db_run(struct northd_context *ctx,
-     build_meter_groups(ctx, &meter_groups);
-     build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
-                  &igmp_groups, &meter_groups, &lbs);
-+    ovn_update_ipv6_prefix(ports);
- 
-     sync_address_sets(ctx);
-     sync_port_groups(ctx);
-diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
-index ea6f4e354..949f6258b 100644
---- a/ovn-nb.ovsschema
-+++ b/ovn-nb.ovsschema
-@@ -1,7 +1,7 @@
- {
-     "name": "OVN_Northbound",
--    "version": "5.20.1",
--    "cksum": "721375950 25251",
-+    "version": "5.22.0",
-+    "cksum": "170077561 25417",
-     "tables": {
-         "NB_Global": {
-             "columns": {
-@@ -342,6 +342,9 @@
-                 "ipv6_ra_configs": {
-                     "type": {"key": "string", "value": "string",
-                              "min": 0, "max": "unlimited"}},
-+                "ipv6_prefix": {"type": {"key": "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 f7ba9c334..045c63fb0 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -2065,6 +2065,11 @@
-       port has all ingress and egress traffic dropped.
-     </column>
- 
-+    <column name="ipv6_prefix">
-+       This column contains IPv6 prefix obtained by prefix delegation
-+       router according to RFC 3633
-+    </column>
-+
-     <group title="ipv6_ra_configs">
-       <p>
-         This column defines the IPv6 ND RA address mode and ND MTU Option to be
-@@ -2320,6 +2325,22 @@
-         <code>ovn-northd</code> honors the configured value.
-       </column>
- 
-+      <column name="options" key="prefix_delegation"
-+              type='{"type": "boolean"}'>
-+        <p>
-+          If set to <code>true</code>, enable IPv6 prefix delegation state
-+          machine on this logical router port (RFC3633). IPv6 prefix
-+          delegation is available just on a gateway router or on a gateway
-+          router port.
-+        </p>
-+      </column>
-+
-+      <column name="options" key="prefix" type='{"type": "boolean"}'>
-+        <p>
-+          If set to <code>true</code>, this interface will receive an IPv6
-+          prefix according to RFC3663
-+        </p>
-+      </column>
-     </group>
- 
-     <group title="Attachment">
-diff --git a/tests/atlocal.in b/tests/atlocal.in
-index 5f14c3da0..8f3ff03b9 100644
---- a/tests/atlocal.in
-+++ b/tests/atlocal.in
-@@ -157,7 +157,7 @@ find_command()
- {
-     which $1 > /dev/null 2>&1
-     status=$?
--    var=HAVE_`echo "$1" | tr '[a-z]' '[A-Z]'`
-+    var=HAVE_`echo "$1" | tr '-' '_' | tr '[a-z]' '[A-Z]'`
-     if test "$status" = "0"; then
-         eval ${var}="yes"
-     else
-@@ -192,6 +192,9 @@ else
-     DIFF_SUPPORTS_NORMAL_FORMAT=no
- fi
- 
-+# Set HAVE_DIBBLER-SERVER
-+find_command dibbler-server
-+
- # Turn off proxies.
- unset http_proxy
- unset https_proxy
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index f1ae69b20..000b3f13b 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -3793,3 +3793,146 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
- /connection dropped.*/d"])
- 
- AT_CLEANUP
-+
-+AT_SETUP([ovn -- IPv6 prefix delegation])
-+AT_SKIP_IF([test $HAVE_DIBBLER_SERVER = no])
-+AT_SKIP_IF([test $HAVE_TCPDUMP = no])
-+AT_KEYWORDS([ovn-ipv6-prefix_d])
-+
-+ovn_start
-+OVS_TRAFFIC_VSWITCHD_START()
-+
-+ADD_BR([br-int])
-+ADD_BR([br-ext])
-+
-+ovs-ofctl add-flow br-ext action=normal
-+# 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
-+
-+ovn-nbctl lr-add R1
-+
-+ovn-nbctl ls-add sw0
-+ovn-nbctl ls-add sw1
-+ovn-nbctl ls-add public
-+
-+ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
-+ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24
-+ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 \
-+    -- set Logical_Router_Port rp-public options:redirect-chassis=hv1
-+
-+ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
-+    type=router options:router-port=rp-sw0 \
-+    -- lsp-set-addresses sw0-rp router
-+ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \
-+    type=router options:router-port=rp-sw1 \
-+    -- lsp-set-addresses sw1-rp router
-+
-+ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
-+    type=router options:router-port=rp-public \
-+    -- lsp-set-addresses public-rp router
-+
-+ADD_NAMESPACES(sw01)
-+ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
-+         "192.168.1.1")
-+ovn-nbctl lsp-add sw0 sw01 \
-+    -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2"
-+
-+ADD_NAMESPACES(sw11)
-+ADD_VETH(sw11, sw11, br-int, "192.168.2.2/24", "f0:00:00:02:02:03", \
-+         "192.168.2.1")
-+ovn-nbctl lsp-add sw1 sw11 \
-+    -- lsp-set-addresses sw11 "f0:00:00:02:02:03 192.168.2.2"
-+
-+ADD_NAMESPACES(server)
-+ADD_VETH(s1, server, br-ext, "2001:1db8:3333::2/64", "f0:00:00:01:02:05", \
-+         "2001:1db8:3333::1")
-+
-+OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep 2001:1db8:3333::2 | grep tentative)" = ""])
-+OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep fe80 | grep tentative)" = ""])
-+
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext])
-+ovn-nbctl lsp-add public public1 \
-+        -- lsp-set-addresses public1 unknown \
-+        -- lsp-set-type public1 localnet \
-+        -- lsp-set-options public1 network_name=phynet
-+
-+ovn-nbctl set logical_router_port rp-public options:prefix_delegation=true
-+ovn-nbctl set logical_router_port rp-public options:prefix=true
-+ovn-nbctl set logical_router_port rp-sw0 options:prefix=true
-+ovn-nbctl set logical_router_port rp-sw1 options:prefix=true
-+
-+# reset dibbler state
-+sed s/^iface.*/"iface \"s1\" {"/g -i /etc/dibbler/server.conf
-+sed s/pd-pool.*/"pd-pool 2001:1db8:3333::\/80"/g -i /etc/dibbler/server.conf
-+sed s/t1.*/"t1 10"/g -i /etc/dibbler/server.conf
-+sed s/t2.*/"t2 15"/g -i /etc/dibbler/server.conf
-+cat > /var/lib/dibbler/server-AddrMgr.xml <<EOF
-+<AddrMgr>
-+  <timestamp>1575481348</timestamp>
-+  <replayDetection>0</replayDetection>
-+</AddrMgr>
-+EOF
-+cat > /var/lib/dibbler/server-CfgMgr.xml <<EOF
-+<SrvCfgMgr>
-+  <workDir>/var/lib/dibbler</workDir>
-+  <LogName>Server</LogName>
-+  <LogLevel>8</LogLevel>
-+  <InactiveMode>0</InactiveMode>
-+  <GuessMode>0</GuessMode>
-+</SrvCfgMgr>
-+EOF
-+
-+NS_CHECK_EXEC([server], [dibbler-server run > dibbler.log &])
-+ovn-nbctl --wait=hv sync
-+
-+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c4-15)" = ""])
-+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c4-15)" = ""])
-+OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c4-15)" = ""])
-+
-+AT_CHECK([ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c3-16], [0], [dnl
-+[2001:1db8:3333]
-+])
-+AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0], [dnl
-+[2001:1db8:3333]
-+])
-+AT_CHECK([ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c3-16], [0], [dnl
-+[2001:1db8:3333]
-+])
-+
-+kill $(pidof dibbler-server)
-+
-+prefix=$(ovn-nbctl list logical_router_port rp-public | awk -F/ '/ipv6_prefix/{print substr($1,25,9)}' | sed 's/://g')
-+ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
-+ovn-nbctl set logical_router_port rp-sw1 options:prefix=false
-+
-+NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[95:4]]=0x${prefix} > public.pcap &])
-+
-+OVS_WAIT_UNTIL([
-+    total_pkts=$(cat public.pcap | wc -l)
-+    test "${total_pkts}" = "1"
-+])
-+
-+kill $(pidof tcpdump)
-+kill $(pidof 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(["/.*error receiving.*/d
-+/.*terminating with signal 15.*/d"])
-+AT_CLEANUP
--- 
-2.25.2
-
diff --git a/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch b/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
deleted file mode 100644
index de2a192..0000000
--- a/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
+++ /dev/null
@@ -1,187 +0,0 @@
-From 92f6a2f668708c677a8b10b0ac861bfd712f6a20 Mon Sep 17 00:00:00 2001
-Message-Id: <92f6a2f668708c677a8b10b0ac861bfd712f6a20.1590585469.git.lorenzo.bianconi@redhat.com>
-In-Reply-To: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
-References: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
-From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
-Date: Mon, 25 May 2020 18:31:27 +0200
-Subject: [PATCH ovn 3/3] northd: manage ARP request locally for FIP traffic
-
-Modify 100-priority logical flows in Gateway Redirect table of
-logical router ingress pipeline (table 15) in order to manage ARP
-request locally for FIP traffic. In particular set reg1 and eth.src
-to NAT external ip and NAT external mac respectively and do not
-distribute ARP traffic using FIP
-
-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
----
- northd/ovn-northd.8.xml | 10 +++++++---
- northd/ovn-northd.c     | 23 ++++++++++++++++-------
- tests/ovn.at            | 28 +++++++++++++++++++++++++---
- tests/system-ovn.at     | 30 ++++++++++++++++++++++++++++++
- 4 files changed, 78 insertions(+), 13 deletions(-)
-
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2879,9 +2879,13 @@ icmp4 {
-         For each NAT rule in the OVN Northbound database that can
-         be handled in a distributed manner, a priority-100 logical
-         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
--        outport == <var>GW</var></code>, where <var>GW</var> is
--        the logical router distributed gateway port, with actions
--        <code>next;</code>.
-+        outport == <var>GW</var></code> &amp;&amp;
-+        is_chassis_resident(<var>P</var>), where <var>GW</var> is
-+        the logical router distributed gateway port and <var>P</var>
-+        is the NAT logical port. IP traffic matching the above rule
-+        will be managed locally setting <code>reg1</code> to <var>C</var>
-+        and <code>eth.src</code> to <var>D</var>, where <var>C</var> is NAT
-+        external ip and <var>D</var> is NAT external mac.
-       </li>
- 
-       <li>
---- a/northd/ovn-northd.c
-+++ b/northd/ovn-northd.c
-@@ -9137,16 +9137,25 @@ build_lrouter_flows(struct hmap *datapat
-             /* Ingress Gateway Redirect Table: For NAT on a distributed
-              * router, add flows that are specific to a NAT rule.  These
-              * flows indicate the presence of an applicable NAT rule that
--             * can be applied in a distributed manner. */
-+             * can be applied in a distributed manner.
-+             * In particulr reg1 and eth.src are set to NAT external IP and
-+             * NAT external mac so the ARP request generated in the following
-+             * stage is sent out with proper IP/MAC src addresses
-+             */
-             if (distributed) {
-                 ds_clear(&match);
--                ds_put_format(&match, "ip%s.src == %s && outport == %s",
--                              is_v6 ? "6" : "4",
--                              nat->logical_ip,
--                              od->l3dgw_port->json_key);
-+                ds_clear(&actions);
-+                ds_put_format(&match,
-+                              "ip%s.src == %s && outport == %s && "
-+                              "is_chassis_resident(\"%s\")",
-+                              is_v6 ? "6" : "4", nat->logical_ip,
-+                              od->l3dgw_port->json_key, nat->logical_port);
-+                ds_put_format(&actions, "eth.src = %s; %sreg1 = %s; next;",
-+                              nat->external_mac, is_v6 ? "xx" : "",
-+                              nat->external_ip);
-                 ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
--                                        100, ds_cstr(&match), "next;",
--                                        &nat->header_);
-+                                        100, ds_cstr(&match),
-+                                        ds_cstr(&actions), &nat->header_);
-             }
- 
-             /* Egress Loopback table: For NAT on a distributed router.
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -14353,9 +14353,14 @@ ovs-vsctl -- add-port br-int hv2-vif1 --
-     set interface hv2-vif1 external-ids:iface-id=sw1-p0 \
-     options:tx_pcap=hv2/vif1-tx.pcap \
-     options:rxq_pcap=hv2/vif1-rx.pcap \
--    ofport-request=1
-+    ofport-request=2
-+ovs-vsctl -- add-port br-int hv2-vif2 -- \
-+    set interface hv2-vif2 external-ids:iface-id=sw0-p1 \
-+    options:tx_pcap=hv2/vif2-tx.pcap \
-+    options:rxq_pcap=hv2/vif2-rx.pcap \
-+    ofport-request=3
- 
--ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
-+ovn-nbctl create Logical_Router name=lr0
- ovn-nbctl ls-add sw0
- ovn-nbctl ls-add sw1
- 
-@@ -14364,13 +14369,16 @@ ovn-nbctl lsp-add sw0 rp-sw0 -- set Logi
-     type=router options:router-port=sw0 \
-     -- lsp-set-addresses rp-sw0 router
- 
--ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64
-+ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 \
-+    -- set Logical_Router_Port sw1 options:redirect-chassis="hv2"
- ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
-     type=router options:router-port=sw1 \
-     -- lsp-set-addresses rp-sw1 router
- 
- ovn-nbctl lsp-add sw0 sw0-p0 \
-     -- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2"
-+ovn-nbctl lsp-add sw0 sw0-p1 \
-+    -- lsp-set-addresses sw0-p1 "f0:00:00:11:02:03 192.168.1.3 2001::3"
- 
- ovn-nbctl lsp-add sw1 sw1-p0 \
-     -- lsp-set-addresses sw1-p0 unknown
-@@ -14416,6 +14424,20 @@ send_na 2 1 $dst_mac $router_mac1 $dst_i
- 
- OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
- 
-+# Create FIP on sw0-p0, add a route on logical router pipeline and
-+# ARP request for a unkwon destination is sent using FIP MAC/IP
-+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.1.2 192.168.1.3 sw0-p1 f0:00:00:01:02:04
-+ovn-nbctl lr-route-add lr0 172.16.2.0/24 172.16.1.11
-+
-+dst_ip=$(ip_to_hex 172 16 2 10)
-+fip_ip=$(ip_to_hex 172 16 1 2)
-+src_ip=$(ip_to_hex 192 168 1 3)
-+gw_router=$(ip_to_hex 172 16 1 11)
-+send_icmp_packet 2 2 f00000110203 $router_mac0 $src_ip $dst_ip 0000 $data
-+echo $(get_arp_req f00000010204 $fip_ip $gw_router) >> expected
-+
-+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-+
- OVN_CLEANUP([hv1],[hv2])
- AT_CLEANUP
- 
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -2747,6 +2747,19 @@ ADD_VETH(alice1, alice1, br-int, "172.16
- ovn-nbctl lsp-add alice alice1 \
- -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
- 
-+# Add external network
-+ADD_NAMESPACES(ext-net)
-+AT_CHECK([ip link add alice-ext netns alice1 type veth peer name ext-veth netns ext-net])
-+NS_CHECK_EXEC([ext-net], [ip link set dev ext-veth up], [0], [])
-+NS_CHECK_EXEC([ext-net], [ip addr add 10.0.0.1/24 dev ext-veth], [0], [])
-+NS_CHECK_EXEC([ext-net], [ip route add default via 10.0.0.2], [0], [])
-+
-+NS_CHECK_EXEC([alice1], [ip link set dev alice-ext up], [0], [])
-+NS_CHECK_EXEC([alice1], [ip addr add 10.0.0.2/24 dev alice-ext], [0], [])
-+NS_CHECK_EXEC([alice1], [sysctl -w net.ipv4.conf.all.forwarding=1],[0], [dnl
-+net.ipv4.conf.all.forwarding = 1
-+])
-+
- # Add DNAT rules
- AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
- AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05])
-@@ -2754,6 +2767,9 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_a
- # Add a SNAT rule
- AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
- 
-+# Add default route to ext-net
-+AT_CHECK([ovn-nbctl lr-route-add R1 10.0.0.0/24 172.16.1.2])
-+
- ovn-nbctl --wait=hv sync
- OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
- 
-@@ -2797,6 +2813,20 @@ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'
- icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
- ])
- 
-+# Try to ping external network
-+NS_CHECK_EXEC([ext-net], [tcpdump -n -c 3 -i ext-veth dst 172.16.1.3 and icmp > ext-net.pcap &])
-+sleep 1
-+AT_CHECK([ovn-nbctl lr-nat-del R1 snat])
-+NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], \
-+[0], [dnl
-+3 packets transmitted, 3 received, 0% packet loss, time 0ms
-+])
-+
-+OVS_WAIT_UNTIL([
-+    total_pkts=$(cat ext-net.pcap | wc -l)
-+    test "${total_pkts}" = "3"
-+])
-+
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- 
- as ovn-sb
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
new file mode 100644
index 0000000..379c3df
--- /dev/null
+++ b/SOURCES/0004-ovn-nbctl-Enhance-lr-policy-add-to-set-the-options.patch
@@ -0,0 +1,254 @@
+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-tests-Fix-occasional-failures-for-test-85.patch b/SOURCES/0004-tests-Fix-occasional-failures-for-test-85.patch
deleted file mode 100644
index eaaf3c0..0000000
--- a/SOURCES/0004-tests-Fix-occasional-failures-for-test-85.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 9735eeff2b736145fe7ab444eb77686a595a9c8e Mon Sep 17 00:00:00 2001
-From: Numan Siddique <numans@ovn.org>
-Date: Fri, 17 Apr 2020 20:09:24 +0530
-Subject: [PATCH 4/4] tests: Fix occasional failures for test 85.
-
-The test case "85: ovn -- send gratuitous ARP for NAT rules on HA distributed router"
-fails occaionally. On faster systems, chances of failure are higher.
-
-This patch fixes this.
-
-Tested-by: Flavio Fernandes <flavio@flaviof.com>
-Signed-off-by: Numan Siddique <numans@ovn.org>
----
- tests/ovn.at | 21 +++++++++++++++++++--
- 1 file changed, 19 insertions(+), 2 deletions(-)
-
-diff --git a/tests/ovn.at b/tests/ovn.at
-index ae3b44cb3..91d0a8fec 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -11062,6 +11062,12 @@ only_broadcast_from_lrp1() {
- garp="fffffffffffff0000000000108060001080006040001f00000000001c0a80064000000000000c0a80064"
- echo $garp > expout
- 
-+OVS_WAIT_UNTIL(
-+    [$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > rcv_text
-+     exp_rcvd=$(cat rcv_text | grep $garp | wc -l)
-+     echo "expected received = $exp_rcvd"
-+     test $exp_rcvd -ge 1])
-+
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoop_tx
- echo "packets on hv1-snoopvif:"
- cat hv1_snoop_tx
-@@ -11090,12 +11096,17 @@ as hv1 reset_pcap_file snoopvif hv1/snoopvif
- as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
- as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
- 
--# Wait for packets to be received.
--OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
- trim_zeros() {
-     sed 's/\(00\)\{1,\}$//'
- }
- 
-+# Wait for packets to be received.
-+OVS_WAIT_UNTIL(
-+    [$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > rcv_text
-+     exp_rcvd=$(cat rcv_text | grep $garp | wc -l)
-+     echo "expected received = $exp_rcvd"
-+     test $exp_rcvd -ge 1])
-+
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq >  hv1_snoopvif_tx
- AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
-@@ -11141,6 +11152,12 @@ trim_zeros() {
- garp="fffffffffffff00000000001810007de08060001080006040001f00000000001c0a80064000000000000c0a80064"
- echo $garp > expout
- 
-+OVS_WAIT_UNTIL(
-+    [$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > rcv_text
-+     exp_rcvd=$(cat rcv_text | grep $garp | wc -l)
-+     echo "expected received = $exp_rcvd"
-+     test $exp_rcvd -ge 1])
-+
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq >  hv1_snoopvif_tx
- AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
- $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
--- 
-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
new file mode 100644
index 0000000..b4d6b18
--- /dev/null
+++ b/SOURCES/0005-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
@@ -0,0 +1,438 @@
+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
new file mode 100644
index 0000000..e03f1d6
--- /dev/null
+++ b/SOURCES/0006-Introduce-icmp6_error-action.patch
@@ -0,0 +1,178 @@
+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
new file mode 100644
index 0000000..7f39cba
--- /dev/null
+++ b/SOURCES/0007-Introduce-icmp6.frag_mtu-action.patch
@@ -0,0 +1,287 @@
+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
new file mode 100644
index 0000000..246780e
--- /dev/null
+++ b/SOURCES/0008-northd-introduce-icmp6_error-logical-flows-in-router.patch
@@ -0,0 +1,263 @@
+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
new file mode 100644
index 0000000..e7a69ce
--- /dev/null
+++ b/SOURCES/0009-Add-IP-address-normalization-to-ovn-utils.patch
@@ -0,0 +1,126 @@
+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
new file mode 100644
index 0000000..90d5f06
--- /dev/null
+++ b/SOURCES/0010-Don-t-check-for-writeability-of-rhs-during-assignmen.patch
@@ -0,0 +1,31 @@
+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
new file mode 100644
index 0000000..c5d0f1c
--- /dev/null
+++ b/SOURCES/0011-Add-expression-writeability-scopes.patch
@@ -0,0 +1,423 @@
+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
new file mode 100644
index 0000000..4c8db8e
--- /dev/null
+++ b/SOURCES/0012-Used-nested-actions-in-ct_commit.patch
@@ -0,0 +1,401 @@
+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
new file mode 100644
index 0000000..6a0a858
--- /dev/null
+++ b/SOURCES/0013-Add-ECMP-symmetric-replies.patch
@@ -0,0 +1,922 @@
+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
new file mode 100644
index 0000000..5e9b04a
--- /dev/null
+++ b/SOURCES/0014-expr.c-Fix-argument-type-of-expr_write_scope.patch
@@ -0,0 +1,39 @@
+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
new file mode 100644
index 0000000..e0e34c9
--- /dev/null
+++ b/SOURCES/0015-Allow-bare-ct_commits-when-no-nested-actions-are-req.patch
@@ -0,0 +1,75 @@
+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
new file mode 100644
index 0000000..f9cf520
--- /dev/null
+++ b/SOURCES/0016-Allow-force_snat-options-to-work-for-dual-stack-rout.patch
@@ -0,0 +1,887 @@
+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
new file mode 100644
index 0000000..8be01b7
--- /dev/null
+++ b/SOURCES/0017-ovn-northd-Don-t-send-the-pkt-to-conntrack-if-it-is-.patch
@@ -0,0 +1,187 @@
+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
new file mode 100644
index 0000000..17307a3
--- /dev/null
+++ b/SOURCES/0018-ovn-northd-Don-t-send-the-pkt-to-conntrack-for-NAT-i.patch
@@ -0,0 +1,475 @@
+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
new file mode 100644
index 0000000..e85d3ac
--- /dev/null
+++ b/SOURCES/0019-ovsdb-idl-Add-function-to-reset-min_index.patch
@@ -0,0 +1,68 @@
+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
new file mode 100644
index 0000000..73d6138
--- /dev/null
+++ b/SOURCES/0020-Add-northd-and-ovn-controller-cluster-status-reset-c.patch
@@ -0,0 +1,246 @@
+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
new file mode 100644
index 0000000..17b4266
--- /dev/null
+++ b/SOURCES/0021-Fix-compilation-error-when-configured-with-enable-sp.patch
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 0000000..184e0b3
--- /dev/null
+++ b/SOURCES/0022-Avoid-nb_cfg-update-notification-flooding.patch
@@ -0,0 +1,565 @@
+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 bc9e75b..c0698b5 100644
--- a/SPECS/ovn2.13.spec
+++ b/SPECS/ovn2.13.spec
@@ -16,6 +16,8 @@
 %define pkgver 2.13
 %define pkgname ovn%{pkgver}
 
+%define upstreamver 20.06
+
 #%%global commit0 7886ac9ed807d6ff942edde624a3f9331da7332a
 #%%global date 20200217
 #%%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
@@ -57,8 +59,8 @@ Name: %{pkgname}
 Summary: Open Virtual Network support
 Group: System Environment/Daemons
 URL: http://www.openvswitch.org/
-Version: %{pkgver}.0
-Release: 39%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Version: %{upstreamver}.2
+Release: 11%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
 Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
 Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
 
@@ -76,7 +78,7 @@ Source: https://www.openvswitch.org/releases/ovn-%{version}.tar.gz
 %endif
 
 
-%define ovsver %{version}
+%define ovsver %{pkgver}.0
 
 %if 0%{?commit1:1}
 Source10: https://github.com/openvswitch/ovs/archive/%{commit1}.tar.gz#/openvswitch-%{shortcommit1}.tar.gz
@@ -105,125 +107,89 @@ Source506: x86_64-native-linuxapp-gcc-config
 
 # ovn-patches
 
-# OVN (including OVS if required) backports (0 - 399)
-# Bug 1812038
-Patch0: 0001-system-tests-Fix-occasional-failure-of-the-test-Load.patch
-Patch1: 0002-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
-Patch2: 0003-logical-fields-fix-memory-leak-caused-by-initialize-.patch
-
-# Bug 1813046
-Patch10: 0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
-
-# Bug 1816087
-Patch20: 0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
-
-# Bug 1816616
-Patch30: 0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
-Patch31: 0001-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
-
-# Bug 1815217
-Patch040: 0001-northd-do-not-insert-identical-lflows-in-S_ROUTER_IN.patch
-Patch041: 0002-ovn-northd-Skip-unsnat-flows-for-load-balancer-vips-.patch
-
-# Bug 1819604
-Patch050: 0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
-Patch051: 0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
-
-# Bug 1718372
-Patch060: 0001-Add-external_ids-column-for-tables-in-nb-schema.patch
-Patch061: 0001-Add-SCTP-support-to-load-balancers.patch
-
-# Bug 1815009
-Patch070: 0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
-
-# Bug 1822859
-Patch080: 0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
-
-# Bug 1823226
-Patch090: 0001-tests-Wait-up-to-OVS_CTL_TIMEOUT-seconds.patch
-Patch091: 0002-controller-Add-ipv6-prefix-delegation-state-machine.patch
-Patch092: 0003-northd-Add-logical-flows-for-dhcpv6-pfd-parsing.patch
-
-# Bug 1778016
-Patch100: 0001-ovn-nbctl-Create-daemon-control-socket-in-ovn-run-di.patch
-
-# Bug 1801058
-Patch110: 0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
-
-# Bug 1825334
-Patch120: 0001-ovn-northd-Limit-IPv6-ND-NS-RA-RS-to-the-local-netwo.patch
-
-# Bug 1819069
-# Bug 1778016
-Patch130: 0001-DNS-Make-DNS-lookups-case-insensitive.patch
-Patch131: 0002-Create-daemon-pidfiles-in-ovn-run-dir.patch
-
-# Bug 1819785
-Patch140: 0001-Fix-conntrack-entry-leaks-because-of-TCP-RST-packets.patch
-
-# Bug 1827403
-Patch150: 0001-ovn-northd-Optimize-flows-for-LB-Hairpin-traffic.patch
-
-# Bug 1817606
-Patch160: 0001-Rely-on-unique-name-for-ovn-qos-meters.patch
-
-# Bug 1826623
-Patch170: 0001-IPv6-PD-assume-status-to-be-Success-if-not-present.patch
-
-# Bug 1827090
-Patch180: 0001-IPv6-PD-time-parameter-checks.patch
-
-# Bug 1828637
-Patch190: 0001-ovn-northd-Clear-SB-records-depending-on-stale-datap.patch
-Patch191: 0002-ovn-northd-Fix-tunnel_key-allocation-for-SB-Port_Bin.patch
-
-# Bug 1826683 and 1827084
-Patch200: 0001-Disable-IPv6-prefix-reporting-if-IPv6-PD-is-disabled.patch
-Patch201: 0002-controller-Add-garbage-collector-for-ipv6_prefixd.patch
-Patch202: 0003-IPv6-PD-Disable-pd-processing-if-the-router-port-is-.patch
-
-# Bug 1823755
-Patch210: 0001-Fix-ACL-reject-action-for-UDP-packets.patch
-
-# Bug 1707513
-# Bug 1825073
-Patch220: 0001-controller-Use-OpenFlow-version-1.5.patch
-Patch221: 0002-ovn-northd-Fix-memory-leak-and-incorrect-limiting-of.patch
-Patch222: 0003-Support-selection-fields-in-load-balancer.patch
-Patch223: 0004-tests-Fix-occasional-failures-for-test-85.patch
-
-# Bug 1834655
-Patch230: 0001-pinctrl-Fix-icmp6-packet-corruption-issue.patch
-
-# Bug 1827769
-Patch240: 0001-ovn-northd-Fix-leak-of-lport-addresses-during-DHCPv6.patch
-Patch241: 0002-ovn-northd-Fix-memory-leak-in-case-of-duplicate-logi.patch
-
-# Bug 1779854
-Patch250: 0001-ofctrl-Split-large-group_mod-messages-up.patch
-
-# Bug 1836976
-Patch260: 0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
-Patch261: 0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
-Patch262: 0003-controller-fix-ip-buffering-with-static-routes.patch
-Patch263: 0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
-
-# Bug 1843512
-Patch270: 0001-Make-the-notify-calls-work-with-IPv6-in-the-OCF-reso.patch
-
-# Bug 1832176
-# Bug 1804576
-Patch280: 0001-Fix-ovn-controller-generated-packets-from-getting-dr.patch
-Patch281: 0002-Honour-router_preference-for-solicited-RA.patch
-
-# Bug 1848398
-Patch290: 0001-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
-
-# Bug 1818128
-Patch300: 0001-Split-SB-Port_Group-per-datapath.patch
+# 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
 
-# Bug 1849162
-Patch310: 0001-ovn-northd-Fix-the-missing-lflow-issue-in-LS_OUT_PRE.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 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
 
 # OpenvSwitch backports (800-) if required.
 Patch800: 0001-Revert-ovsdb-idl-Avoid-sending-redundant-conditional.patch
@@ -233,6 +199,9 @@ Patch810: 0003-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
 
+# Bug 1829762
+Patch830: 0019-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
 # doesn't have the -optional repository enabled and so TPS fails
@@ -675,10 +644,123 @@ fi
 %{_unitdir}/ovn-controller-vtep.service
 
 %changelog
-* Wed Jul 08 2020 Numan Siddique <dceara@redhat.com> - 2.13.0-39
+* Tue Sep 16 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
+- 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
+- Backport "ovn-ctl: introduce ovsdb-{n, s}b-wrapper options" (#1831558)
+
+* Tue Sep 8 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.2-8
+- Backport "Introduce DHCPDECLINE msg support to OVN DHCP server" (#1857563)
+
+* Mon Sep 7 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.2-7
+- Backport "northd: fix empty_lb_backends controller_event for IPv6" (#1875337)
+
+* Fri Sep 4 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.2-6
+- Backport "chassis: Fix the way encaps are updated for a chassis record." (#1873032)
+- Backport "chassis: Fix chassis_private record updates when the system-id changes." (#1873032)
+
+* Fri Sep 4 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.2-5
+- Backport "lex: Allow unmasked bits in value/mask tokens." (#1812820)
+- Backport "ovn-nbctl: Deal with nb_cfg overflows." (#1873455)
+- Backport "pinctrl: Fix incorrect warning message for multicast querier." (#1875727)
+
+* Wed Sep 2 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.2-4
+- Backport "ovn-northd: Rate limit missing chassis log." (#1874745)
+
+* Mon Aug 31 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.2-3
+- Backport "ovn-controller: Fix incremental processing of Port_Binding deletes." (#1871961)
+
+* Wed Aug 26 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-2
+- Backport "Fix ovn-controller crash when a lport of type 'virtual' is deleted." (#1872681)
+
+* Sat Aug 22 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.2-1
+- Sync the OVN sources with the upstream v20.06.2 release and reorder
+  the other patches.
+
+* Fri Aug 21 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-18
+- Backport "Avoid nb_cfg update notification flooding" (#1871054)
+
+* Fri Aug 21 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-17
+- Backport "Fix the data type for DHCP option tftp_server (66)" (#1871056)
+- Backport "Add support for DHCP domain search option (119)" (#1871056)
+
+* Thu Aug 20 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-16
+- Backport "ovsdb-idl: Add function to reset min_index." (#1829109)
+- Backport "Add northd and ovn-controller cluster status reset commands." (#1829109)
+- Backport "Fix compilation error when configured with --enable-sparse." (#1829109)
+
+* Wed Aug 19 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-15
+- Backport "northd: Fix the routing for external logical ports of bridged logical switches." (#1829762)
+
+* Tue Aug 11 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-14
+- Backport "ovn-northd: Add ARP responder flows for SNAT entries." (#1861294)
+
+* Mon Aug 10 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-13
+- Backport "ovn-northd: Don't send the pkt to conntrack if it is to be routed in egress stage." (#1836804)
+- Backport "ovn-northd: Don't send the pkt to conntrack for NAT if its not destined for LB VIP." (#1836804)
+
+* Mon Aug 10 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-12
+- Backport "Allow force_snat options to work for dual-stack routers." (#1823003)
+
+* Mon Aug 10 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-11
+- Synced with the upstream branch-20.06 commit cd31e32f028fa7a06a7faaeeace307c1086e25b4
+- This includes the flow explosion patches. (#1847537)
+
+* Fri Aug 7 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.1-10
+- Backport "chassis: Propagate ovn-monitor-all external-id to Chassis:other_config." (#1866820)
+- Backport "pinctrl: Avoid flushing of non-local IGMP_Groups." (#1866820)
+
+* Thu Aug 6 2020 Mark Michelson <mmichels@redhat.com> - 20.06.1-9
+- Backport "Allow bare ct_commits when no nested actions are required."
+
+* Mon Aug 3 2020 Dumitru Ceara <dceara@redhat.com> - 20.06.1-8
+- Backport "expr.c: Fix argument type of expr_write_scope()." (#1849683)
+- Backport "ovn-detrace: Add support for multiple remotes." (#1853716)
+- Backport "ovn-detrace: Support SSL remotes." (#1853716)
+
+* Thu Jul 30 2020 Mark Michelson <mmichels@redhat.com> - 20.06.1-7
+- Backport ECMP symmetric reply patch series (#1849683)
+
+* Wed Jul 29 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-6
+- Backport "ovn-controller: Release lport if the ofport of the VIF is -1.". (#1861298)
+
+* Tue Jul 28 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-5
+- Backport "ovn-controller: Fix the missing flows when logical router port is added after its peer." (#1860053)
+- Backport "ovn-controller: Clear flows not associated with db rows in physical flow change handler." (#1861042)
+
+* Fri Jul 17 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-4
+- Backport "ovn-controller: Fix the missing ct zone entries for container ports." (#1858191)
+
+* Thu Jul 16 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-3
+- Backport "ovn-controller: Fix the missing flows with monitor-all set to True" (#1857537)
+
+* Thu Jul 9 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 20.06.1-2
+- Backport "Introduce icmp6_error action" (#1846300)
+- Backport "Introduce icmp6.frag_mtu action" (#1846300)
+- Backport "northd: introduce icmp6_error logical flows in router pipeline" (#1846300)
+
+* Thu Jul 09 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-1
+- Backport "Support packet metadata marking for logical router policies." (#1828933)
+- Backport "ovn-nbctl: Enhance lr-policy-add to set the options."(#1828933)
+- Backport "pinctrl: Support DHCPRELEASE and DHCPINFORM in native OVN dhcp responder." (#1801258)
+
+* Thu Jul 09 2020 Numan Siddique <nusiddiq@redhat.com> - 20.06.1-0
+- Synced the upstream v20.06.1 source.
+- This resolves the following BZs:
+-    1846189
+-    1850511
+-    1818128
+-    1805630
+
+* Wed Jul 08 2020 Numan Siddique <nusiddiq@redhat.com> - 2.13.0-39
 - Backport "ovn-northd: Fix the missing lflow issue in LS_OUT_PRE_LB." (#1849162)
 
-* Wed Jul 08 2020 Numan Siddique <dceara@redhat.com> - 2.13.0-38
+* Wed Jul 08 2020 Numan Siddique <nusiddiq@redhat.com> - 2.13.0-38
 - Backport "Split SB Port_Group per datapath." (#1818128)
 
 * Fri Jun 19 2020 Dumitru Ceara <dceara@redhat.com> - 2.13.0-37