diff --git a/.ovn.metadata b/.ovn.metadata
index 5e00597..df4ddae 100644
--- a/.ovn.metadata
+++ b/.ovn.metadata
@@ -1,5 +1,5 @@
 002450621b33c5690060345b0aac25bc2426d675 SOURCES/docutils-0.12.tar.gz
-5953db6f7d13eeaa4335f558659865c07abd3d95 SOURCES/openvswitch-ba159ee.tar.gz
-54b0b39b351e7b650d4de61ac8029b4b1186896c SOURCES/ovn-22.03.0.tar.gz
+18ee4650f5907758dbd949b7b82bfb029666d8f8 SOURCES/openvswitch-6f24c2b.tar.gz
+8fc7b574476db8f38307923fd6e476df9b65b009 SOURCES/ovn-22.06.0.tar.gz
 d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz
 6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a SOURCES/Sphinx-1.1.3.tar.gz
diff --git a/SOURCES/ovn22.03.patch b/SOURCES/ovn22.03.patch
deleted file mode 100644
index ef85019..0000000
--- a/SOURCES/ovn22.03.patch
+++ /dev/null
@@ -1,1351 +0,0 @@
-diff --git a/AUTHORS.rst b/AUTHORS.rst
-index 8572c24c8..d3747f8d1 100644
---- a/AUTHORS.rst
-+++ b/AUTHORS.rst
-@@ -147,6 +147,7 @@ Fabrizio D'Angelo                  fdangelo@redhat.com
- Flavio Fernandes                   flavio@flaviof.com
- Flavio Leitner                     fbl@redhat.com
- Francesco Fusco                    ffusco@redhat.com
-+François Rigault                   frigo@amadeus.com
- Frank Wang                         wangpeihuixyz@126.com
- Frédéric Tobias Christ             fchrist@live.de
- Frode Nordahl                      frode.nordahl@gmail.com
-diff --git a/NEWS b/NEWS
-index 9f3ce3cf3..15fa545d2 100644
---- a/NEWS
-+++ b/NEWS
-@@ -1,4 +1,7 @@
--OVN v22.03.0 - XX XXX XXXX
-+OVN v22.03.1 - xx xxx xxxx
-+--------------------------
-+
-+OVN v22.03.0 - 11 Mar 2022
- --------------------------
-   - Refactor CoPP commands introducing a unique name index in CoPP NB table.
-     Add following new CoPP commands to manage CoPP table:
-diff --git a/configure.ac b/configure.ac
-index 283381b4e..70f86e1f5 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -13,7 +13,7 @@
- # limitations under the License.
- 
- AC_PREREQ(2.63)
--AC_INIT(ovn, 22.03.0, bugs@openvswitch.org)
-+AC_INIT(ovn, 22.03.1, bugs@openvswitch.org)
- AC_CONFIG_MACRO_DIR([m4])
- AC_CONFIG_AUX_DIR([build-aux])
- AC_CONFIG_HEADERS([config.h])
-diff --git a/controller-vtep/binding.c b/controller-vtep/binding.c
-index 01d5a16d2..1ee52b592 100644
---- a/controller-vtep/binding.c
-+++ b/controller-vtep/binding.c
-@@ -109,12 +109,10 @@ update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
-                      port_binding_rec->chassis->name,
-                      chassis_rec->name);
-         }
--
-         sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
--        if (port_binding_rec->n_up) {
--            bool up = true;
--            sbrec_port_binding_set_up(port_binding_rec, &up, 1);
--        }
-+    } else if (port_binding_rec->n_up) {
-+        bool up = true;
-+        sbrec_port_binding_set_up(port_binding_rec, &up, 1);
-     }
- }
- 
-diff --git a/controller/binding.c b/controller/binding.c
-index 4d62b0858..1259e6b3b 100644
---- a/controller/binding.c
-+++ b/controller/binding.c
-@@ -481,6 +481,16 @@ remove_related_lport(const struct sbrec_port_binding *pb,
-     }
- }
- 
-+static void
-+delete_active_pb_ras_pd(const struct sbrec_port_binding *pb,
-+                        struct shash *ras_pd_map)
-+{
-+    struct pb_ld_binding *ras_pd =
-+        shash_find_and_delete(ras_pd_map, pb->logical_port);
-+
-+    free(ras_pd);
-+}
-+
- static void
- update_active_pb_ras_pd(const struct sbrec_port_binding *pb,
-                         struct hmap *local_datapaths,
-@@ -2251,6 +2261,9 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in,
-             continue;
-         }
- 
-+        delete_active_pb_ras_pd(pb, b_ctx_out->local_active_ports_ipv6_pd);
-+        delete_active_pb_ras_pd(pb, b_ctx_out->local_active_ports_ras);
-+
-         enum en_lport_type lport_type = get_lport_type(pb);
- 
-         struct binding_lport *b_lport =
-diff --git a/controller/ofctrl.c b/controller/ofctrl.c
-index a7c2d2011..3b9d71733 100644
---- a/controller/ofctrl.c
-+++ b/controller/ofctrl.c
-@@ -943,7 +943,12 @@ link_installed_to_desired(struct installed_flow *i, struct desired_flow *d)
-             break;
-         }
-     }
--    ovs_list_insert(&f->installed_ref_list_node, &d->installed_ref_list_node);
-+    if (!f) {
-+        ovs_list_insert(&i->desired_refs, &d->installed_ref_list_node);
-+    } else {
-+        ovs_list_insert(&f->installed_ref_list_node,
-+                        &d->installed_ref_list_node);
-+    }
-     d->installed_flow = i;
-     return installed_flow_get_active(i) == d;
- }
-@@ -2324,7 +2329,20 @@ deleted_flow_lookup(struct hmap *deleted_flows, struct ovn_flow *target)
-             && f->cookie == target->cookie
-             && ofpacts_equal(f->ofpacts, f->ofpacts_len, target->ofpacts,
-                              target->ofpacts_len)) {
--            return d;
-+            /* del_f must have been installed, otherwise it should have
-+             * been removed during track_flow_del. */
-+            ovs_assert(d->installed_flow);
-+
-+            /* Now we also need to make sure the desired flow being
-+             * added/updated has exact same action and cookie as the installed
-+             * flow of d. Otherwise, don't merge them, so that the
-+             * installed flow can be updated later. */
-+            struct ovn_flow *f_i = &d->installed_flow->flow;
-+            if (f_i->cookie == target->cookie
-+                && ofpacts_equal(f_i->ofpacts, f_i->ofpacts_len,
-+                                 target->ofpacts, target->ofpacts_len)) {
-+                return d;
-+            }
-         }
-     }
-     return NULL;
-@@ -2353,10 +2371,6 @@ merge_tracked_flows(struct ovn_desired_flow_table *flow_table)
-                 continue;
-             }
- 
--            /* del_f must have been installed, otherwise it should have been
--             * removed during track_flow_add_or_modify. */
--            ovs_assert(del_f->installed_flow);
--
-             if (!f->installed_flow) {
-                 /* f is not installed yet. */
-                 replace_installed_to_desired(del_f->installed_flow, del_f, f);
-@@ -2665,6 +2679,13 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
-     EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter, meters) {
-         /* Delete the meter. */
-         ofctrl_meter_bands_erase(m_installed, &msgs);
-+        if (!strncmp(m_installed->name, "__string: ", 10)) {
-+            struct ofputil_meter_mod mm = {
-+                .command = OFPMC13_DELETE,
-+                .meter = { .meter_id = m_installed->table_id },
-+            };
-+            add_meter_mod(&mm, &msgs);
-+        }
-         ovn_extend_table_remove_existing(meters, m_installed);
-     }
- 
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 25b37ee88..2f718aca7 100644
---- a/controller/pinctrl.c
-+++ b/controller/pinctrl.c
-@@ -5523,7 +5523,7 @@ get_localnet_vifs_l3gwports(
-             }
-             const struct sbrec_port_binding *pb
-                 = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
--            if (!pb) {
-+            if (!pb || pb->chassis != chassis) {
-                 continue;
-             }
-             struct local_datapath *ld
-@@ -5554,7 +5554,7 @@ get_localnet_vifs_l3gwports(
-         sbrec_port_binding_index_set_datapath(target, ld->datapath);
-         SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
-                                            sbrec_port_binding_by_datapath) {
--            if (!strcmp(pb->type, "l3gateway")
-+            if ((!strcmp(pb->type, "l3gateway") && pb->chassis == chassis)
-                 || !strcmp(pb->type, "patch")) {
-                 sset_add(local_l3gw_ports, pb->logical_port);
-             }
-@@ -5781,7 +5781,8 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
-         const struct sbrec_port_binding *pb = lport_lookup_by_name(
-             sbrec_port_binding_by_name, iface_id);
-         if (pb) {
--            send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
-+            send_garp_rarp_update(ovnsb_idl_txn,
-+                                  sbrec_mac_binding_by_lport_ip,
-                                   local_datapaths, pb, &nat_addresses);
-         }
-     }
-diff --git a/debian/changelog b/debian/changelog
-index f1167591b..18a1a042e 100644
---- a/debian/changelog
-+++ b/debian/changelog
-@@ -1,3 +1,9 @@
-+OVN (22.03.1-1) unstable; urgency=low
-+   [ OVN team ]
-+   * New upstream version
-+
-+ -- OVN team <dev@openvswitch.org>  Fri, 11 Mar 2022 13:22:32 -0500
-+
- ovn (22.03.0-1) unstable; urgency=low
- 
-    * New upstream version
-diff --git a/lib/actions.c b/lib/actions.c
-index 5d3caaf2b..2219c5b37 100644
---- a/lib/actions.c
-+++ b/lib/actions.c
-@@ -2336,7 +2336,7 @@ validate_empty_lb_backends(struct action_context *ctx,
- 
-         switch (o->option->code) {
-         case EMPTY_LB_VIP:
--            if (!inet_parse_active(c->string, 0, &ss, false)) {
-+            if (!inet_parse_active(c->string, 0, &ss, false, NULL)) {
-                 lexer_error(ctx->lexer, "Invalid load balancer VIP '%s'",
-                             c->string);
-                 return;
-diff --git a/lib/expr.c b/lib/expr.c
-index 47ef6108e..058390a16 100644
---- a/lib/expr.c
-+++ b/lib/expr.c
-@@ -211,16 +211,17 @@ expr_combine(enum expr_type type, struct expr *a, struct expr *b)
- }
- 
- static void
--expr_insert_andor(struct expr *andor, struct expr *before, struct expr *new)
-+expr_insert_andor(struct expr *andor, struct ovs_list *before,
-+                  struct expr *new)
- {
-     if (new->type == andor->type) {
-         if (andor->type == EXPR_T_AND) {
-             /* Conjunction junction, what's your function? */
-         }
--        ovs_list_splice(&before->node, new->andor.next, &new->andor);
-+        ovs_list_splice(before, new->andor.next, &new->andor);
-         expr_destroy(new);
-     } else {
--        ovs_list_insert(&before->node, &new->node);
-+        ovs_list_insert(before, &new->node);
-     }
- }
- 
-@@ -2101,7 +2102,8 @@ expr_annotate__(struct expr *expr, const struct shash *symtab,
-                 expr_destroy(expr);
-                 return NULL;
-             }
--            expr_insert_andor(expr, next, new_sub);
-+            expr_insert_andor(expr, next ? &next->node : &expr->andor,
-+                              new_sub);
-         }
-         *errorp = NULL;
-         return expr;
-@@ -2301,7 +2303,7 @@ expr_evaluate_condition(struct expr *expr,
-             struct expr *e = expr_evaluate_condition(sub, is_chassis_resident,
-                                                      c_aux);
-             e = expr_fix(e);
--            expr_insert_andor(expr, next, e);
-+            expr_insert_andor(expr, next ? &next->node : &expr->andor, e);
-         }
-         return expr_fix(expr);
- 
-@@ -2334,7 +2336,8 @@ expr_simplify(struct expr *expr)
-     case EXPR_T_OR:
-         LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
-             ovs_list_remove(&sub->node);
--            expr_insert_andor(expr, next, expr_simplify(sub));
-+            expr_insert_andor(expr, next ? &next->node : &expr->andor,
-+                              expr_simplify(sub));
-         }
-         return expr_fix(expr);
- 
-@@ -2444,12 +2447,13 @@ crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
-      * EXPR_T_OR with EXPR_T_CMP subexpressions. */
-     struct expr *sub, *next = NULL;
-     LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
-+        struct ovs_list *next_list = next ? &next->node : &expr->andor;
-         ovs_list_remove(&sub->node);
-         struct expr *new = crush_cmps(sub, symbol);
-         switch (new->type) {
-         case EXPR_T_CMP:
-             if (!singleton) {
--                ovs_list_insert(&next->node, &new->node);
-+                ovs_list_insert(next_list, &new->node);
-                 singleton = new;
-             } else {
-                 bool match = !strcmp(new->cmp.string, singleton->cmp.string);
-@@ -2463,7 +2467,7 @@ crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
-         case EXPR_T_AND:
-             OVS_NOT_REACHED();
-         case EXPR_T_OR:
--            ovs_list_insert(&next->node, &new->node);
-+            ovs_list_insert(next_list, &new->node);
-             break;
-         case EXPR_T_BOOLEAN:
-             if (!new->boolean) {
-@@ -2559,7 +2563,7 @@ crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol)
-         case EXPR_T_AND:
-             OVS_NOT_REACHED();
-         case EXPR_T_OR:
--            ovs_list_insert(&next->node, &new->node);
-+            ovs_list_insert(next ? &next->node : &expr->andor, &new->node);
-             break;
-         case EXPR_T_BOOLEAN:
-             if (!new->boolean) {
-@@ -2725,7 +2729,8 @@ crush_or(struct expr *expr, const struct expr_symbol *symbol)
-      * is now a disjunction of cmps over the same symbol. */
-     LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
-         ovs_list_remove(&sub->node);
--        expr_insert_andor(expr, next, crush_cmps(sub, symbol));
-+        expr_insert_andor(expr, next ? &next->node : &expr->andor,
-+                          crush_cmps(sub, symbol));
-     }
-     expr = expr_fix(expr);
-     if (expr->type != EXPR_T_OR) {
-@@ -2886,8 +2891,7 @@ expr_normalize_and(struct expr *expr)
- 
-     struct expr *a, *b;
-     LIST_FOR_EACH_SAFE (a, b, node, &expr->andor) {
--        if (&b->node == &expr->andor
--            || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP
-+        if (!b || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP
-             || a->cmp.symbol != b->cmp.symbol) {
-             continue;
-         } else if (a->cmp.symbol->width
-@@ -2964,7 +2968,8 @@ expr_normalize_or(struct expr *expr)
-                 }
-                 expr_destroy(new);
-             } else {
--                expr_insert_andor(expr, next, new);
-+                expr_insert_andor(expr, next ? &next->node : &expr->andor,
-+                                  new);
-             }
-         } else {
-             ovs_assert(sub->type == EXPR_T_CMP ||
-diff --git a/lib/inc-proc-eng.c b/lib/inc-proc-eng.c
-index 7b4391700..575b774ae 100644
---- a/lib/inc-proc-eng.c
-+++ b/lib/inc-proc-eng.c
-@@ -354,14 +354,11 @@ engine_recompute(struct engine_node *node, bool allowed,
-                  const char *reason_fmt, ...)
- {
-     char *reason = NULL;
-+    va_list reason_args;
- 
--    if (VLOG_IS_DBG_ENABLED()) {
--        va_list reason_args;
--
--        va_start(reason_args, reason_fmt);
--        reason = xvasprintf(reason_fmt, reason_args);
--        va_end(reason_args);
--    }
-+    va_start(reason_args, reason_fmt);
-+    reason = xvasprintf(reason_fmt, reason_args);
-+    va_end(reason_args);
- 
-     if (!allowed) {
-         VLOG_DBG("node: %s, recompute (%s) aborted", node->name, reason);
-diff --git a/lib/ovn-parallel-hmap.h b/lib/ovn-parallel-hmap.h
-index 897208ef8..0f7d68770 100644
---- a/lib/ovn-parallel-hmap.h
-+++ b/lib/ovn-parallel-hmap.h
-@@ -58,11 +58,11 @@ extern "C" {
-  * ThreadID + step * i as the JOBID parameter.
-  */
- 
--#define HMAP_FOR_EACH_IN_PARALLEL(NODE, MEMBER, JOBID, HMAP) \
--   for (INIT_CONTAINER(NODE, hmap_first_in_bucket_num(HMAP, JOBID), MEMBER); \
--        (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \
--       || ((NODE = NULL), false); \
--       ASSIGN_CONTAINER(NODE, hmap_next_in_bucket(&(NODE)->MEMBER), MEMBER))
-+#define HMAP_FOR_EACH_IN_PARALLEL(NODE, MEMBER, JOBID, HMAP)                \
-+   for (INIT_MULTIVAR(NODE, MEMBER, hmap_first_in_bucket_num(HMAP, JOBID),  \
-+                      struct hmap_node);                                    \
-+        CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);           \
-+        UPDATE_MULTIVAR(NODE, hmap_next_in_bucket(ITER_VAR(NODE))))
- 
- /* We do not have a SAFE version of the macro, because the hash size is not
-  * atomic and hash removal operations would need to be wrapped with
-diff --git a/lib/ovn-util.c b/lib/ovn-util.c
-index a22ae84fe..2f2f7ae39 100644
---- a/lib/ovn-util.c
-+++ b/lib/ovn-util.c
-@@ -735,7 +735,7 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address,
-                                 uint16_t *port, int *addr_family)
- {
-     struct sockaddr_storage ss;
--    if (!inet_parse_active(key, 0, &ss, false)) {
-+    if (!inet_parse_active(key, 0, &ss, false, NULL)) {
-         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-         VLOG_WARN_RL(&rl, "bad ip address or port for load balancer key %s",
-                      key);
-diff --git a/northd/northd.c b/northd/northd.c
-index b22da67e9..217f180eb 100644
---- a/northd/northd.c
-+++ b/northd/northd.c
-@@ -763,16 +763,6 @@ init_nat_entries(struct ovn_datapath *od)
-         return;
-     }
- 
--    if (od->n_l3dgw_ports > 1) {
--        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
--        VLOG_WARN_RL(&rl, "NAT is configured on logical router %s, which has %"
--                     PRIuSIZE" distributed gateway ports. NAT is not supported"
--                     " yet when there is more than one distributed gateway "
--                     "port on the router.",
--                     od->nbr->name, od->n_l3dgw_ports);
--        return;
--    }
--
-     od->nat_entries = xmalloc(od->nbr->n_nat * sizeof *od->nat_entries);
- 
-     for (size_t i = 0; i < od->nbr->n_nat; i++) {
-@@ -6418,6 +6408,72 @@ ovn_port_group_destroy(struct hmap *pgs, struct ovn_port_group *pg)
-     }
- }
- 
-+static void
-+copy_ra_to_sb(struct ovn_port *op, const char *address_mode);
-+
-+static void
-+ovn_update_ipv6_options(struct hmap *ports)
-+{
-+    struct ovn_port *op;
-+    HMAP_FOR_EACH (op, key_node, ports) {
-+        if (!op->nbrp || op->nbrp->peer || !op->peer) {
-+            continue;
-+        }
-+
-+        if (!op->lrp_networks.n_ipv6_addrs) {
-+            continue;
-+        }
-+
-+        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 (!lrport_is_enabled(op->nbrp)) {
-+            prefix_delegation = false;
-+        }
-+        if (smap_get_bool(&options, "ipv6_prefix_delegation",
-+                          false) != prefix_delegation) {
-+            smap_add(&options, "ipv6_prefix_delegation",
-+                     prefix_delegation ? "true" : "false");
-+        }
-+
-+        bool ipv6_prefix = smap_get_bool(&op->nbrp->options,
-+                                     "prefix", false);
-+        if (!lrport_is_enabled(op->nbrp)) {
-+            ipv6_prefix = false;
-+        }
-+        if (smap_get_bool(&options, "ipv6_prefix", false) != ipv6_prefix) {
-+            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");
-+
-+        if (!address_mode) {
-+            continue;
-+        }
-+        if (strcmp(address_mode, "slaac") &&
-+            strcmp(address_mode, "dhcpv6_stateful") &&
-+            strcmp(address_mode, "dhcpv6_stateless")) {
-+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-+            VLOG_WARN_RL(&rl, "Invalid address mode [%s] defined",
-+                         address_mode);
-+            continue;
-+        }
-+
-+        if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic",
-+                          false)) {
-+            copy_ra_to_sb(op, address_mode);
-+        }
-+    }
-+}
-+
- static void
- build_port_group_lswitches(struct northd_input *input_data,
-                            struct hmap *pgs,
-@@ -10759,6 +10815,12 @@ build_neigh_learning_flows_for_lrouter(
-                           copp_meter_get(COPP_ARP, od->nbr->copp,
-                                          meter_groups));
- 
-+        ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 95,
-+                          "nd_na && nd.tll == 0",
-+                          "put_nd(inport, nd.target, eth.src); next;",
-+                          copp_meter_get(COPP_ND_NA, od->nbr->copp,
-+                                         meter_groups));
-+
-         ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
-                           "nd_na", "put_nd(inport, nd.target, nd.tll); next;",
-                           copp_meter_get(COPP_ND_NA, od->nbr->copp,
-@@ -10851,34 +10913,6 @@ build_ND_RA_flows_for_lrouter_port(
-         return;
-     }
- 
--    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 (!lrport_is_enabled(op->nbrp)) {
--        prefix_delegation = false;
--    }
--    if (smap_get_bool(&options, "ipv6_prefix_delegation",
--                      false) != prefix_delegation) {
--        smap_add(&options, "ipv6_prefix_delegation",
--                 prefix_delegation ? "true" : "false");
--    }
--
--    bool ipv6_prefix = smap_get_bool(&op->nbrp->options,
--                                     "prefix", false);
--    if (!lrport_is_enabled(op->nbrp)) {
--        ipv6_prefix = false;
--    }
--    if (smap_get_bool(&options, "ipv6_prefix", false) != ipv6_prefix) {
--        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");
- 
-@@ -10894,11 +10928,6 @@ build_ND_RA_flows_for_lrouter_port(
-         return;
-     }
- 
--    if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic",
--                      false)) {
--        copy_ra_to_sb(op, address_mode);
--    }
--
-     ds_clear(match);
-     ds_put_format(match, "inport == %s && ip6.dst == ff02::2 && nd_rs",
-                           op->json_key);
-@@ -12805,6 +12834,7 @@ build_lrouter_out_snat_flow(struct hmap *lflows, struct ovn_datapath *od,
-             ds_put_format(actions, "ip%s.src=%s; next;",
-                           is_v6 ? "6" : "4", nat->external_ip);
-         } else {
-+            ds_put_format(match, " && (!ct.trk || !ct.rpl)");
-             ds_put_format(actions, "ct_snat(%s", nat->external_ip);
- 
-             if (nat->external_port_range[0]) {
-@@ -13155,6 +13185,18 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od, struct hmap *lflows,
-         return;
-     }
- 
-+    /* NAT rules are not currently supported on logical routers with multiple
-+     * distributed gateway ports. */
-+    if (od->n_l3dgw_ports > 1) {
-+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-+        VLOG_WARN_RL(&rl, "NAT is configured on logical router %s, which has %"
-+                     PRIuSIZE" distributed gateway ports. NAT is not supported"
-+                     " yet when there is more than one distributed gateway "
-+                     "port on the router.",
-+                     od->nbr->name, od->n_l3dgw_ports);
-+        return;
-+    }
-+
-     struct sset nat_entries = SSET_INITIALIZER(&nat_entries);
- 
-     bool dnat_force_snat_ip =
-@@ -15048,6 +15090,7 @@ ovnnb_db_run(struct northd_input *input_data,
-     build_meter_groups(input_data, &data->meter_groups);
-     stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
-     stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
-+    ovn_update_ipv6_options(&data->ports);
-     ovn_update_ipv6_prefix(&data->ports);
- 
-     sync_address_sets(input_data,  ovnsb_txn, &data->datapaths);
-diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index 1c9d408af..4cb70e185 100644
---- a/northd/ovn-northd.8.xml
-+++ b/northd/ovn-northd.8.xml
-@@ -2301,6 +2301,12 @@ next;
-         <code>put_arp(inport, arp.spa, arp.sha); next;</code>
-       </li>
- 
-+      <li>
-+        A priority-95 flow with the match <code>nd_na  &amp;&amp;
-+        nd.tll == 0</code> and applies the action
-+        <code>put_nd(inport, nd.target, eth.src); next;</code>
-+      </li>
-+
-       <li>
-         A priority-90 flow with the match <code>nd_na</code> and
-         applies the action
-@@ -4452,7 +4458,8 @@ nd_ns {
-           to change the source IP address of a packet from an IP address of
-           <var>A</var> or to change the source IP address of a packet that
-           belongs to network <var>A</var> to <var>B</var>, a flow matches
--          <code>ip &amp;&amp; ip4.src == <var>A</var></code> with an action
-+          <code>ip &amp;&amp; ip4.src == <var>A</var> &amp;&amp;
-+          (!ct.trk || !ct.rpl)</code> with an action
-           <code>ct_snat(<var>B</var>);</code>.  The priority of the flow
-           is calculated based on the mask of <var>A</var>, with matches
-           having larger masks getting higher priorities. If the NAT rule is
-diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in
-index 3fb854a37..821eb03cc 100644
---- a/rhel/ovn-fedora.spec.in
-+++ b/rhel/ovn-fedora.spec.in
-@@ -323,7 +323,7 @@ ln -sf ovn_detrace.py %{_bindir}/ovn-detrace
- %if %{with libcapng}
- if [ $1 -eq 1 ]; then
-     sed -i 's:^#OVN_USER_ID=:OVN_USER_ID=:' %{_sysconfdir}/sysconfig/ovn
--    sed -i 's:\(.*su\).*:\1 ovn ovn:' %{_sysconfdir}/logrotate.d/ovn
-+    sed -i 's:\(.*su\).*:\1 openvswitch openvswitch:' %{_sysconfdir}/logrotate.d/ovn
- fi
- %endif
- 
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 3865003bf..b7dfcd151 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1030,7 +1030,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows | sed 's/table=../table=??/' | sort], [0
- AT_CHECK([grep -e "lr_out_snat" crflows | sed 's/table=../table=??/' | sort], [0], [dnl
-   table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
-   table=??(lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range), action=(ct_snat(172.16.1.1);)
-+  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
- ])
- 
- 
-@@ -1062,7 +1062,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows2 | sed 's/table=../table=??/' | sort], [
- AT_CHECK([grep -e "lr_out_snat" crflows2 | sed 's/table=../table=??/' | sort], [0], [dnl
-   table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
-   table=??(lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11), action=(ct_snat(172.16.1.1);)
-+  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
-   table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
- ])
- 
-@@ -1091,7 +1091,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows3 | sed 's/table=../table=??/' | sort], [
- AT_CHECK([grep -e "lr_out_snat" crflows3 | sed 's/table=../table=??/' | sort], [0], [dnl
-   table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
-   table=??(lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range), action=(ct_snat(172.16.1.2);)
-+  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
- ])
- 
- # Stateful FIP with DISALLOWED_IPs
-@@ -1120,7 +1120,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 | sed 's/table=../table=??/' | sort], [
- AT_CHECK([grep -e "lr_out_snat" crflows4 | sed 's/table=../table=??/' | sort], [0], [dnl
-   table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
-   table=??(lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11), action=(ct_snat(172.16.1.2);)
-+  table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
-   table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
- ])
- 
-@@ -3447,7 +3447,7 @@ ls_copp_uuid=$(fetch_column nb:Logical_Switch copp)
- AT_CHECK([test "$ls_copp_uuid" = "$copp_uuid"])
- 
- check ovn-nbctl --wait=hv copp-add $copp_uuid igmp meter0
--AT_CHECK([ovn-nbctl copp-list copp0], [0], [dnl
-+AT_CHECK([ovn-nbctl copp-list copp0 | sort], [0], [dnl
- arp: meter0
- igmp: meter0
- ])
-@@ -5140,11 +5140,12 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
- AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
-   table=? (lr_out_snat        ), priority=0    , match=(1), action=(next;)
-   table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24), action=(ct_snat(172.168.0.10);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10), action=(ct_snat(172.168.0.30);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3), action=(ct_snat(172.168.0.20);)
-+  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
- ])
- 
-+
- # Set lb force snat logical router.
- check ovn-nbctl --wait=sb set logical_router lr0 options:lb_force_snat_ip="router_ip"
- check ovn-nbctl --wait=sb sync
-@@ -5201,9 +5202,9 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"), action=(ct_snat(172.168.0.10);)
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"), action=(ct_snat(10.0.0.1);)
-   table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24), action=(ct_snat(172.168.0.10);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10), action=(ct_snat(172.168.0.30);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3), action=(ct_snat(172.168.0.20);)
-+  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
- ])
- 
- # Add a LB VIP same as router ip.
-@@ -5266,9 +5267,9 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"), action=(ct_snat(172.168.0.10);)
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"), action=(ct_snat(10.0.0.1);)
-   table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24), action=(ct_snat(172.168.0.10);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10), action=(ct_snat(172.168.0.30);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3), action=(ct_snat(172.168.0.20);)
-+  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
- ])
- 
- # Add IPv6 router port and LB.
-@@ -5346,9 +5347,9 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"), action=(ct_snat(def0::10);)
-   table=? (lr_out_snat        ), priority=110  , match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-sw0"), action=(ct_snat(aef0::1);)
-   table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
--  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24), action=(ct_snat(172.168.0.10);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10), action=(ct_snat(172.168.0.30);)
--  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3), action=(ct_snat(172.168.0.20);)
-+  table=? (lr_out_snat        ), priority=25   , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
-+  table=? (lr_out_snat        ), priority=33   , match=(ip && ip4.src == 10.0.0.3 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
- ])
- 
- check ovn-nbctl lrp-del lr0-sw0
-@@ -5804,6 +5805,12 @@ AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR | sed 's/table=../table=??
-   table=??(lr_in_gw_redirect  ), priority=50   , match=(outport == "DR-S3"), action=(outport = "cr-DR-S3"; next;)
- ])
- 
-+# Check that ovn-northd logs a warning when trying to configure NAT
-+# on the router with multiple distributed gw ports.  Such configurations are
-+# not supported yet.
-+check ovn-nbctl lr-nat-add DR dnat_and_snat 42.42.42.1 20.0.0.2
-+AT_CHECK([grep -q 'NAT is configured on logical router DR, which has 2 distributed gateway ports. NAT is not supported yet when there is more than one distributed gateway port on the router.' northd/ovn-northd.log], [0])
-+
- AT_CLEANUP
- ])
- 
-@@ -6426,3 +6433,28 @@ AT_CHECK([grep -e "ls_in_stateful" lsflows | sed 's/table=../table=??/' | sort],
- 
- AT_CLEANUP
- ])
-+
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([LR neighbor lookup and learning flows])
-+ovn_start
-+
-+# Create logical routers
-+ovn-nbctl --wait=sb lr-add lr0
-+
-+ovn-sbctl dump-flows lr0 > lrflows
-+AT_CAPTURE_FILE([lrflows])
-+
-+AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor | sort], [0], [dnl
-+  table=1 (lr_in_lookup_neighbor), priority=0    , match=(1), action=(reg9[[2]] = 1; next;)
-+  table=1 (lr_in_lookup_neighbor), priority=100  , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;)
-+  table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
-+  table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
-+  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] == 1), action=(next;)
-+  table=2 (lr_in_learn_neighbor), priority=90   , match=(arp), action=(put_arp(inport, arp.spa, arp.sha); next;)
-+  table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_na), action=(put_nd(inport, nd.target, nd.tll); next;)
-+  table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_ns), action=(put_nd(inport, ip6.src, nd.sll); next;)
-+  table=2 (lr_in_learn_neighbor), priority=95   , match=(nd_na && nd.tll == 0), action=(put_nd(inport, nd.target, eth.src); next;)
-+])
-+
-+AT_CLEANUP
-+])
-diff --git a/tests/ovn.at b/tests/ovn.at
-index 4f65d1ecd..4ee9aebc9 100644
---- a/tests/ovn.at
-+++ b/tests/ovn.at
-@@ -8588,6 +8588,114 @@ OVN_CLEANUP([hv1])
- AT_CLEANUP
- ])
- 
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([send gratuitous arp for l3gateway only on selected chassis])
-+ovn_start
-+
-+# Create logical switch
-+ovn-nbctl ls-add ls0
-+# Create gateway router
-+ovn-nbctl lr-add lr0
-+# Add router port to gateway router
-+ovn-nbctl lrp-add lr0 lr0-ls0 f0:00:00:00:00:01 192.168.0.1/24
-+ovn-nbctl lsp-add ls0 ls0-lr0 -- set Logical_Switch_Port ls0-lr0 \
-+    type=router options:router-port=lr0-ls0 addresses='"f0:00:00:00:00:01"'
-+
-+# Create a localnet port.
-+ovn-nbctl lsp-add ls0 ln_port
-+ovn-nbctl lsp-set-addresses ln_port unknown
-+ovn-nbctl lsp-set-type ln_port localnet
-+ovn-nbctl --wait=hv lsp-set-options ln_port network_name=physnet1
-+
-+# Prepare packets
-+touch empty_expected
-+echo "fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" > arp_expected
-+
-+net_add n1
-+sim_add hv1
-+as hv1
-+ovs-vsctl \
-+    -- add-br br-phys \
-+    -- add-br br-eth0
-+
-+ovn_attach n1 br-phys 192.168.0.10
-+
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-+AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
-+
-+sim_add hv2
-+as hv2
-+ovs-vsctl \
-+    -- add-br br-phys \
-+    -- add-br br-eth0
-+
-+ovn_attach n1 br-phys 192.168.0.20
-+
-+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-+AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv2/snoopvif-tx.pcap options:rxq_pcap=hv2/snoopvif-rx.pcap])
-+
-+ovn-sbctl dump-flows > sbflows
-+AT_CAPTURE_FILE([sbflows])
-+
-+# Wait until the patch ports are created in hv1 and hv2 to connect br-int to br-eth0
-+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv1])
-+OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-vsctl show | \
-+grep "Port patch-br-int-to-ln_port" | wc -l`])
-+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv2])
-+OVS_WAIT_UNTIL([test 1 = `as hv2 ovs-vsctl show | \
-+grep "Port patch-br-int-to-ln_port" | wc -l`])
-+
-+# Temporarily remove lr0 chassis
-+AT_CHECK([ovn-nbctl remove logical_router lr0 options chassis])
-+
-+reset_pcap_file() {
-+    local hv=$1
-+    local iface=$2
-+    local pcap_file=$3
-+    as $hv
-+    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
-+}
-+
-+reset_pcap_file hv1 snoopvif hv1/snoopvif
-+reset_pcap_file hv2 snoopvif hv2/snoopvif
-+
-+hv1_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv1)
-+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv1])
-+OVS_WAIT_UNTIL([
-+    ls0_lr0=$(ovn-sbctl --bare --columns chassis list port_binding ls0-lr0)
-+    test "$ls0_lr0" = $hv1_uuid
-+])
-+
-+sleep 2
-+OVN_CHECK_PACKETS_CONTAIN([hv1/snoopvif-tx.pcap], [arp_expected])
-+OVN_CHECK_PACKETS([hv2/snoopvif-tx.pcap], [empty_expected])
-+
-+# Temporarily remove lr0 chassis
-+AT_CHECK([ovn-nbctl remove logical_router lr0 options chassis])
-+
-+reset_pcap_file hv1 snoopvif hv1/snoopvif
-+reset_pcap_file hv2 snoopvif hv2/snoopvif
-+
-+hv2_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv2)
-+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv2])
-+OVS_WAIT_UNTIL([
-+    ls0_lr0=$(ovn-sbctl --bare --columns chassis list port_binding ls0-lr0)
-+    test "$ls0_lr0" = $hv2_uuid
-+])
-+
-+sleep 2
-+OVN_CHECK_PACKETS_CONTAIN([hv2/snoopvif-tx.pcap], [arp_expected])
-+OVN_CHECK_PACKETS([hv1/snoopvif-tx.pcap], [empty_expected])
-+
-+OVN_CLEANUP([hv1],[hv2])
-+
-+AT_CLEANUP
-+])
-+
- OVN_FOR_EACH_NORTHD([
- AT_SETUP([send gratuitous arp with nat-addresses router in localnet])
- ovn_start
-@@ -9403,6 +9511,10 @@ check ovn-nbctl --wait=hv qos-add lsw0 to-lport 1002 'inport=="lp2" && is_chassi
- AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [4
- ])
- 
-+check ovn-nbctl qos-del lsw0
-+AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [0
-+])
-+
- OVN_CLEANUP([hv])
- AT_CLEANUP
- ])
-@@ -13988,6 +14100,99 @@ OVN_CLEANUP([hv1],[hv2])
- AT_CLEANUP
- ])
- 
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([garps disabled when port no longer bound to chassis])
-+ovn_start
-+
-+net_add n1
-+for i in 1 2; do
-+    sim_add hv$i
-+    as hv$i
-+    check ovs-vsctl add-br br-phys
-+    ovn_attach n1 br-phys 192.168.0.$i
-+    check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-+done
-+
-+check ovn-nbctl ls-add ls0
-+check ovn-nbctl lsp-add ls0 port
-+check ovn-nbctl lsp-set-addresses port "00:00:00:00:00:01 10.0.0.1"
-+
-+check ovn-nbctl lsp-add ls0 public
-+check ovn-nbctl lsp-set-addresses public unknown
-+check ovn-nbctl lsp-set-type public localnet
-+check ovn-nbctl lsp-set-options public network_name=phys
-+
-+for hv in hv1 hv2; do
-+    as $hv check ovs-vsctl -- add-port br-int port -- \
-+        set Interface port external-ids:iface-id=port \
-+        options:tx_pcap=$hv/port-tx.pcap \
-+        options:rxq_pcap=$hv/port-rx.pcap
-+done
-+
-+reset_pcap_file() {
-+    local hv=$1
-+    local iface=$2
-+    local pcap_file=$3
-+    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-+                                                   options:rxq_pcap=dummy-rx.pcap
-+    check rm -f ${pcap_file}*.pcap
-+    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-+                                                   options:rxq_pcap=${pcap_file}-rx.pcap
-+}
-+
-+reset_env() {
-+    reset_pcap_file hv1 br-phys_n1 hv1/br-phys_n1
-+    reset_pcap_file hv2 br-phys_n1 hv2/br-phys_n1
-+
-+    for port in hv1/n1 hv2/n1; do
-+        : > $port.expected
-+    done
-+}
-+
-+for hv in hv1 hv2; do
-+    wait_row_count Chassis 1 name=$hv
-+done
-+hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
-+hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
-+
-+OVN_POPULATE_ARP
-+
-+# Activate port on each hv giving a chance to each chassis to enable garps
-+check ovn-nbctl lsp-set-options port requested-chassis=hv1
-+wait_column "$hv1_uuid" Port_Binding chassis logical_port=port
-+wait_column "$hv1_uuid" Port_Binding requested_chassis logical_port=port
-+wait_for_ports_up
-+reset_env
-+
-+# give chassis some time to generate garps
-+sleep 2
-+
-+expected_garp=ffffffffffff000000000001080600010800060400010000000000010a0000010000000000000a000001
-+
-+# check hv1 sends garps and hv2 doesn't
-+echo $expected_garp >> hv1/n1.expected
-+OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [hv1/n1.expected])
-+OVN_CHECK_PACKETS([hv2/br-phys_n1-tx.pcap], [hv2/n1.expected])
-+
-+check ovn-nbctl lsp-set-options port requested-chassis=hv2
-+wait_column "$hv2_uuid" Port_Binding chassis logical_port=port
-+wait_column "$hv2_uuid" Port_Binding requested_chassis logical_port=port
-+wait_for_ports_up
-+reset_env
-+
-+# give chassis some time to generate garps
-+sleep 2
-+
-+# check hv2 sends garps and hv1 doesn't
-+echo $expected_garp >> hv2/n1.expected
-+OVN_CHECK_PACKETS([hv1/br-phys_n1-tx.pcap], [hv1/n1.expected])
-+OVN_CHECK_PACKETS_CONTAIN([hv2/br-phys_n1-tx.pcap], [hv2/n1.expected])
-+
-+OVN_CLEANUP([hv1],[hv2])
-+
-+AT_CLEANUP
-+])
-+
- OVN_FOR_EACH_NORTHD([
- AT_SETUP([IPv6 periodic RA disabled for localnet adjacent switch ports])
- ovn_start
-@@ -15243,6 +15448,92 @@ OVN_CLEANUP([hv1])
- AT_CLEANUP
- ])
- 
-+# This test ensures that the incremental flow installation works well when
-+# handling update->delete->add/update for the same OVS flow.
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([ACL conjunction append and reprocess])
-+ovn_start
-+
-+net_add n1
-+sim_add hv1
-+as hv1
-+check ovs-vsctl add-br br-phys
-+ovn_attach n1 br-phys 192.168.0.1
-+
-+# Setup the desired state:
-+# - Two ACLs, each matches its own port-group (pg1 & pg2), and matches the same
-+#   set of IP addresses.
-+# - pg1 includes p1, p2, p3
-+# - pg2 includes p4, p5
-+check ovn-nbctl ls-add sw
-+check ovn-nbctl lsp-add sw p1 -- lsp-set-addresses p1 "00:00:00:00:00:02 192.168.0.2"
-+check ovn-nbctl lsp-add sw p2 -- lsp-set-addresses p2 "00:00:00:00:00:03 192.168.0.3"
-+check ovn-nbctl lsp-add sw p3 -- lsp-set-addresses p3 "00:00:00:00:00:04 192.168.0.4"
-+check ovn-nbctl lsp-add sw p4 -- lsp-set-addresses p4 "00:00:00:00:00:05 192.168.0.5"
-+check ovn-nbctl lsp-add sw p5 -- lsp-set-addresses p5 "00:00:00:00:00:06 192.168.0.6"
-+check ovn-nbctl pg-add pg1 p1 p2 p3
-+check ovn-nbctl pg-add pg2 p4 p5
-+check ovs-vsctl add-port br-int p1 -- set Interface p1 external_ids:iface-id=p1
-+check ovs-vsctl add-port br-int p2 -- set Interface p2 external_ids:iface-id=p2
-+check ovs-vsctl add-port br-int p3 -- set Interface p3 external_ids:iface-id=p3
-+check ovs-vsctl add-port br-int p4 -- set Interface p4 external_ids:iface-id=p4
-+check ovs-vsctl add-port br-int p5 -- set Interface p5 external_ids:iface-id=p5
-+check ovn-nbctl acl-add pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow
-+check ovn-nbctl acl-add pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow
-+check ovn-nbctl --wait=hv sync
-+
-+# Now we should have two flows with combined conjunctions.
-+OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
-+grep conjunction.*conjunction | wc -l`])
-+
-+
-+# Test the scenario 10 times to give enough chance to hit the
-+# "update->delete->add/update" scenario, because we can't decide the order of
-+# change handling inside ovn-controller.
-+for i in $(seq 10); do
-+# Unbind the p3 and p5, the combined conjunctions should be gone.
-+ovs-vsctl del-port br-int p3
-+ovs-vsctl del-port br-int p5
-+OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
-+grep conjunction.*conjunction | wc -l`])
-+
-+# Delete and re-add the ACLs, just to bring some randomness in the lflow
-+# processing order, so that there is a chance that the order of adding and
-+# appending are the same before & after the flow deletion, so that the
-+# generated combined conjunctions are the same before & after the flow
-+# deletion. (If the order is different, the combined conjunctions order is
-+# different and the action comparison would fail, so won't trigger the tracked
-+# flow merging. We want to make sure that we test the merging scenario)
-+ovn-nbctl acl-del pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}"
-+ovn-nbctl acl-del pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}"
-+ovn-nbctl acl-add pg1 to-lport 1000 "outport==@pg1 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow
-+ovn-nbctl acl-add pg2 to-lport 1000 "outport==@pg2 && ip4 && ip4.src == {10.0.0.1, 10.0.0.2}" allow
-+ovn-nbctl --wait=hv sync
-+
-+# Now re-bind p3 and p5 in the same transaction, so that pg1 and pg2 update are
-+# handled in the same I-P engine run. The order of pg1 and pg2 can be random.
-+# If the order is pg2 -> pg1, then it should trigger the OVS flow
-+# "update->delete->add/update" scenario:
-+# 1) when pg2 update is handled, the ACL-2 would append conjunctions to
-+#    the conjunction flows of ACL-1
-+# 2) when pg1 update is handled, it would flood remove flows of both ACL-1 and
-+#    ACL-2, including the "appended" conjunction flows. And then reprocess
-+#    ACL-1 and ACL-2 would re-add and re-append the conjunction flows with
-+#    combined conjunctions.
-+ovs-vsctl add-port br-int p3 -- set Interface p3 external_ids:iface-id=p3 -- \
-+    add-port br-int p5 -- set Interface p5 external_ids:iface-id=p5
-+ovn-nbctl --wait=hv sync
-+
-+# Now making sure we end up with two combined conjunctions.
-+OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
-+grep conjunction.*conjunction | wc -l`])
-+
-+done
-+
-+OVN_CLEANUP([hv1])
-+AT_CLEANUP
-+])
-+
- OVN_FOR_EACH_NORTHD([
- AT_SETUP([Superseding ACLs with conjunction])
- ovn_start
-diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index c4a2c39f6..018dcea2a 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -6922,10 +6922,10 @@ p = IP(src="192.168.1.2", dst="192.168.1.1") / UDP(dport = 12345) / Raw(b"X"*64)
- send (p, iface='sw01', loop = 0, verbose = 0, count = 20)
- EOF
- 
--# 1pps + 1 burst size
-+# 1pps
- OVS_WAIT_UNTIL([
-     n_reject=$(grep unreachable reject.pcap | wc -l)
--    test "${n_reject}" = "2"
-+    test "${n_reject}" = "1"
- ])
- kill $(pidof tcpdump)
- rm -f reject.pcap
-@@ -6938,10 +6938,10 @@ p = IP(src="192.168.1.2", dst="192.168.1.1") / UDP(dport = 12345) / Raw(b"X"*64)
- send (p, iface='sw01', loop = 0, verbose = 0, count = 100)
- EOF
- 
--# 10pps + 1 burst size
-+# 10pps
- OVS_WAIT_UNTIL([
-     n_reject=$(grep unreachable reject.pcap | wc -l)
--    test "${n_reject}" = "20"
-+    test "${n_reject}" = "10"
- ])
- 
- kill $(pidof tcpdump)
-@@ -6974,10 +6974,10 @@ p = IP(src="192.168.1.2", dst="172.16.1.100") / TCP(dport = 80, flags="S") / Raw
- send (p, iface='sw01', loop = 0, verbose = 0, count = 100)
- EOF
- 
--# 1pps + 1 burst size
-+# 1pps
- OVS_WAIT_UNTIL([
-     n_arp=$(grep ARP arp.pcap | wc -l)
--    test "${n_arp}" = "2"
-+    test "${n_arp}" = "1"
- ])
- kill $(pidof tcpdump)
- 
-@@ -6994,10 +6994,10 @@ p = IP(src="192.168.1.2", dst="172.16.1.100", ttl=1) / TCP(dport = 8080, flags="
- send (p, iface='sw01', loop = 0, verbose = 0, count = 100)
- EOF
- 
--# 1pps + 1 burst size
-+# 1pps
- OVS_WAIT_UNTIL([
-     n_icmp=$(grep ICMP icmp.pcap | wc -l)
--    test "${n_icmp}" = "2"
-+    test "${n_icmp}" = "1"
- ])
- kill $(pidof tcpdump)
- 
-@@ -7010,7 +7010,7 @@ bfd: bfd-meter
- 
- check ovn-nbctl --bfd lr-route-add R1 240.0.0.0/8 172.16.1.50 rp-public
- printf "%08x" $(ovn-sbctl get bfd . disc) > /tmp/disc
--NS_EXEC([server], [tcpdump -l -n -i s1 udp port 3784 -Q in > bfd.pcap &])
-+NS_EXEC([server], [tcpdump -l -nn -i s1 udp port 3784 and ip[[29]]==0x90 -Q in > bfd.pcap &])
- ip netns exec server scapy -H <<-EOF
- import binascii
- f = open("/tmp/disc", "r")
-@@ -7023,10 +7023,10 @@ f.close()
- EOF
- rm /tmp/disc
- 
--# 1pps + 1 burst size
-+# 1pps
- OVS_WAIT_UNTIL([
--    n_tcp_rst=$(grep Final bfd.pcap | wc -l)
--    test "${n_tcp_rst}" = "2"
-+    n_bfd=$(grep 3784 bfd.pcap | wc -l)
-+    test "${n_bfd}" = "1"
- ])
- kill $(pidof tcpdump)
- 
-@@ -7992,3 +7992,122 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
- 
- AT_CLEANUP
- ])
-+
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([East-West traffic with gateway router if DNAT configured])
-+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:
-+# One LR - R1  has two switches: sw0 and sw1
-+#    sw0 -- R1 -- sw1
-+# Logical port 'sw01' in switch 'sw0'.
-+# Logical port 'sw11' in switch 'sw1'.
-+# nc server running in sw01
-+# nc client running on sw11
-+
-+check ovn-nbctl lr-add R1
-+check ovn-nbctl ls-add sw0
-+check ovn-nbctl ls-add sw1
-+
-+check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
-+check ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24
-+check ovn-nbctl set logical_router R1 options:chassis=hv1
-+
-+check 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
-+check 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
-+
-+ADD_NAMESPACES(sw01)
-+ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
-+       "192.168.1.1")
-+check 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")
-+check ovn-nbctl lsp-add sw1 sw11 \
-+    -- lsp-set-addresses sw11 "f0:00:00:02:02:03 192.168.2.2"
-+
-+NETNS_DAEMONIZE([sw01], [nc -k -l 8000], [nc-sw01.pid])
-+
-+test_ping() {
-+    NS_CHECK_EXEC([$1],  [ping -q -c 1 $2 -w 2 | FORMAT_PING], \
-+[0], [dnl
-+1 packets transmitted, 1 received, 0% packet loss, time 0ms
-+])
-+}
-+
-+# Only SNAT
-+check ovn-nbctl --wait=hv lr-nat-add R1 snat 172.16.1.21 192.168.2.0/24
-+
-+echo "foo" > foo
-+NS_CHECK_EXEC([sw11], [nc 192.168.1.2 8000 < foo])
-+test_ping sw11 192.168.1.2
-+
-+# Ensure nat has been hit
-+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" | grep 'nat(src=172.16.1.21)'])
-+# Ensure conntrack entry is present
-+OVS_WAIT_FOR_OUTPUT([
-+    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-+      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
-+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+# SNAT and DNAT. using Logical IP
-+ovn-nbctl --wait=hv lr-nat-add R1 dnat_and_snat 172.16.1.2 192.168.1.2
-+NS_CHECK_EXEC([sw11], [nc 192.168.1.2 8000 < foo ])
-+test_ping sw11 192.168.1.2
-+
-+# Ensure conntrack entry is present
-+OVS_WAIT_FOR_OUTPUT([
-+    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-+      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
-+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+# SNAT and DNAT. using floating IP
-+NS_CHECK_EXEC([sw11], [nc 172.16.1.2 8000 < foo ])
-+test_ping sw11 172.16.1.2
-+
-+# Ensure conntrack entry is present
-+OVS_WAIT_FOR_OUTPUT([
-+    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-+      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-+icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
-+tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-+])
-+
-+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-+
-+OVS_APP_EXIT_AND_WAIT([ovn-controller])
-+as
-+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-+/connection dropped.*/d"])
-+
-+AT_CLEANUP
-+])
-diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
-index 7bcc2c66a..7fcfa50d4 100644
---- a/utilities/ovn-nbctl.c
-+++ b/utilities/ovn-nbctl.c
-@@ -2856,7 +2856,7 @@ nbctl_lb_add(struct ctl_context *ctx)
-     }
- 
-     struct sockaddr_storage ss_vip;
--    if (!inet_parse_active(lb_vip, 0, &ss_vip, false)) {
-+    if (!inet_parse_active(lb_vip, 0, &ss_vip, false, NULL)) {
-         ctl_error(ctx, "%s: should be an IP address (or an IP address "
-                   "and a port number with : as a separator).", lb_vip);
-         return;
-@@ -2886,7 +2886,7 @@ nbctl_lb_add(struct ctl_context *ctx)
-         struct sockaddr_storage ss_dst;
- 
-         if (lb_vip_port) {
--            if (!inet_parse_active(token, -1, &ss_dst, false)) {
-+            if (!inet_parse_active(token, -1, &ss_dst, false, NULL)) {
-                 ctl_error(ctx, "%s: should be an IP address and a port "
-                           "number with : as a separator.", token);
-                 goto out;
-@@ -3032,7 +3032,7 @@ lb_info_add_smap(const struct nbrec_load_balancer *lb,
-             const struct smap_node *node = nodes[i];
- 
-             struct sockaddr_storage ss;
--            if (!inet_parse_active(node->key, 0, &ss, false)) {
-+            if (!inet_parse_active(node->key, 0, &ss, false, NULL)) {
-                 continue;
-             }
- 
-diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index ece5803f2..096d691d5 100644
---- a/utilities/ovn-trace.c
-+++ b/utilities/ovn-trace.c
-@@ -214,7 +214,7 @@ static void
- parse_lb_option(const char *s)
- {
-     struct sockaddr_storage ss;
--    if (!inet_parse_active(s, 0, &ss, false)) {
-+    if (!inet_parse_active(s, 0, &ss, false, NULL)) {
-         ovs_fatal(0, "%s: bad address", s);
-     }
- 
-@@ -1388,7 +1388,8 @@ ovntrace_node_prune_summary(struct ovs_list *nodes)
-             sub->type == OVNTRACE_NODE_TABLE) {
-             /* Replace 'sub' by its children, if any, */
-             ovs_list_remove(&sub->node);
--            ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
-+            ovs_list_splice(next ? &next->node : nodes, sub->subs.next,
-+                            &sub->subs);
-             ovntrace_node_destroy(sub);
-         }
-     }
-@@ -1432,7 +1433,8 @@ ovntrace_node_prune_hard(struct ovs_list *nodes)
-             sub->type == OVNTRACE_NODE_OUTPUT) {
-             /* Replace 'sub' by its children, if any, */
-             ovs_list_remove(&sub->node);
--            ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
-+            ovs_list_splice(next ? &next->node : nodes, sub->subs.next,
-+                            &sub->subs);
-             ovntrace_node_destroy(sub);
-         }
-     }
diff --git a/SOURCES/ovn22.06.patch b/SOURCES/ovn22.06.patch
new file mode 100644
index 0000000..426711d
--- /dev/null
+++ b/SOURCES/ovn22.06.patch
@@ -0,0 +1,1519 @@
+diff --git a/NEWS b/NEWS
+index e335f64c2..32e342a18 100644
+--- a/NEWS
++++ b/NEWS
+@@ -1,3 +1,6 @@
++OVN v22.06.1 - xx xxx xxxx
++--------------------------
++
+ OVN v22.06.0 - 03 Jun 2022
+ --------------------------
+   - Support IGMP and MLD snooping on transit logical switches that connect
+@@ -24,6 +27,8 @@ OVN v22.06.0 - 03 Jun 2022
+   - Added support for setting the Next server IP in the DHCP header
+     using the private DHCP option - 253 in native OVN DHCPv4 responder.
+   - Support list of chassis for Logical_Switch_Port:options:requested-chassis.
++  - Support Logical_Switch_Port:options:activation-strategy for live migration
++    scenarios.
+ 
+ OVN v22.03.0 - 11 Mar 2022
+ --------------------------
+diff --git a/configure.ac b/configure.ac
+index b649441bc..739e0295e 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -13,7 +13,7 @@
+ # limitations under the License.
+ 
+ AC_PREREQ(2.63)
+-AC_INIT(ovn, 22.06.0, bugs@openvswitch.org)
++AC_INIT(ovn, 22.06.1, bugs@openvswitch.org)
+ AC_CONFIG_MACRO_DIR([m4])
+ AC_CONFIG_AUX_DIR([build-aux])
+ AC_CONFIG_HEADERS([config.h])
+diff --git a/controller/lport.c b/controller/lport.c
+index bf55d83f2..add7e91aa 100644
+--- a/controller/lport.c
++++ b/controller/lport.c
+@@ -197,3 +197,25 @@ get_peer_lport(const struct sbrec_port_binding *pb,
+                                 peer_name);
+     return (peer && peer->datapath) ? peer : NULL;
+ }
++
++bool
++lport_is_activated_by_activation_strategy(const struct sbrec_port_binding *pb,
++                                          const struct sbrec_chassis *chassis)
++{
++    const char *activated_chassis = smap_get(&pb->options,
++                                             "additional-chassis-activated");
++    if (activated_chassis) {
++        char *save_ptr;
++        char *tokstr = xstrdup(activated_chassis);
++        for (const char *chassis_name = strtok_r(tokstr, ",", &save_ptr);
++             chassis_name != NULL;
++             chassis_name = strtok_r(NULL, ",", &save_ptr)) {
++            if (!strcmp(chassis_name, chassis->name)) {
++                free(tokstr);
++                return true;
++            }
++        }
++        free(tokstr);
++    }
++    return false;
++}
+diff --git a/controller/lport.h b/controller/lport.h
+index 115881655..644c67255 100644
+--- a/controller/lport.h
++++ b/controller/lport.h
+@@ -70,4 +70,7 @@ const struct sbrec_port_binding *lport_get_peer(
+ const struct sbrec_port_binding *lport_get_l3gw_peer(
+     const struct sbrec_port_binding *,
+     struct ovsdb_idl_index *sbrec_port_binding_by_name);
++bool
++lport_is_activated_by_activation_strategy(const struct sbrec_port_binding *pb,
++                                          const struct sbrec_chassis *chassis);
+ #endif /* controller/lport.h */
+diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
+index 2793c8687..69615308e 100644
+--- a/controller/ovn-controller.c
++++ b/controller/ovn-controller.c
+@@ -1128,6 +1128,53 @@ ovs_interface_shadow_ovs_interface_handler(struct engine_node *node,
+     return true;
+ }
+ 
++struct ed_type_activated_ports {
++    struct ovs_list *activated_ports;
++};
++
++static void *
++en_activated_ports_init(struct engine_node *node OVS_UNUSED,
++                        struct engine_arg *arg OVS_UNUSED)
++{
++    struct ed_type_activated_ports *data = xzalloc(sizeof *data);
++    data->activated_ports = NULL;
++    return data;
++}
++
++static void
++en_activated_ports_cleanup(void *data_)
++{
++    struct ed_type_activated_ports *data = data_;
++    if (!data->activated_ports) {
++        return;
++    }
++
++    struct activated_port *pp;
++    LIST_FOR_EACH_POP (pp, list, data->activated_ports) {
++        free(pp);
++    }
++    free(data->activated_ports);
++    data->activated_ports = NULL;
++}
++
++static void
++en_activated_ports_clear_tracked_data(void *data)
++{
++    en_activated_ports_cleanup(data);
++}
++
++static void
++en_activated_ports_run(struct engine_node *node, void *data_)
++{
++    struct ed_type_activated_ports *data = data_;
++    enum engine_node_state state = EN_UNCHANGED;
++    data->activated_ports = get_ports_to_activate_in_engine();
++    if (data->activated_ports) {
++        state = EN_UPDATED;
++    }
++    engine_set_node_state(node, state);
++}
++
+ struct ed_type_runtime_data {
+     /* Contains "struct local_datapath" nodes. */
+     struct hmap local_datapaths;
+@@ -3164,6 +3211,49 @@ pflow_output_ct_zones_handler(struct engine_node *node OVS_UNUSED,
+     return !ct_zones_data->recomputed;
+ }
+ 
++static bool
++pflow_output_activated_ports_handler(struct engine_node *node, void *data)
++{
++    struct ed_type_activated_ports *ap =
++        engine_get_input_data("activated_ports", node);
++    if (!ap->activated_ports) {
++        return true;
++    }
++
++    struct ed_type_pflow_output *pfo = data;
++    struct ed_type_runtime_data *rt_data =
++        engine_get_input_data("runtime_data", node);
++    struct ed_type_non_vif_data *non_vif_data =
++        engine_get_input_data("non_vif_data", node);
++
++    struct physical_ctx p_ctx;
++    init_physical_ctx(node, rt_data, non_vif_data, &p_ctx);
++
++    struct activated_port *pp;
++    LIST_FOR_EACH (pp, list, ap->activated_ports) {
++        struct ovsdb_idl_index *sbrec_datapath_binding_by_key =
++            engine_ovsdb_node_get_index(
++                    engine_get_input("SB_datapath_binding", node),
++                    "key");
++        struct ovsdb_idl_index *sbrec_port_binding_by_key =
++            engine_ovsdb_node_get_index(
++                    engine_get_input("SB_port_binding", node),
++                    "key");
++        const struct sbrec_port_binding *pb = lport_lookup_by_key(
++            sbrec_datapath_binding_by_key, sbrec_port_binding_by_key,
++            pp->dp_key, pp->port_key);
++        if (pb) {
++            if (!physical_handle_flows_for_lport(pb, false, &p_ctx,
++                                                 &pfo->flow_table)) {
++                return false;
++            }
++            tag_port_as_activated_in_engine(pp);
++        }
++    }
++    engine_set_node_state(node, EN_UPDATED);
++    return true;
++}
++
+ static void *
+ en_flow_output_init(struct engine_node *node OVS_UNUSED,
+                     struct engine_arg *arg OVS_UNUSED)
+@@ -3445,6 +3535,7 @@ main(int argc, char *argv[])
+     ENGINE_NODE(non_vif_data, "non_vif_data");
+     ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
+     ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
++    ENGINE_NODE_WITH_CLEAR_TRACK_DATA(activated_ports, "activated_ports");
+     ENGINE_NODE(pflow_output, "physical_flow_output");
+     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lflow_output, "logical_flow_output");
+     ENGINE_NODE(flow_output, "flow_output");
+@@ -3492,6 +3583,14 @@ main(int argc, char *argv[])
+     engine_add_input(&en_pflow_output, &en_sb_multicast_group,
+                      pflow_output_sb_multicast_group_handler);
+ 
++    /* pflow_output needs to access the SB datapath binding and hence a noop
++     * handler.
++     */
++    engine_add_input(&en_pflow_output, &en_sb_datapath_binding,
++                     engine_noop_handler);
++    engine_add_input(&en_pflow_output, &en_activated_ports,
++                     pflow_output_activated_ports_handler);
++
+     engine_add_input(&en_pflow_output, &en_runtime_data,
+                      pflow_output_runtime_data_handler);
+     engine_add_input(&en_pflow_output, &en_sb_encap, NULL);
+diff --git a/controller/physical.c b/controller/physical.c
+index 24de86f24..fc8280a99 100644
+--- a/controller/physical.c
++++ b/controller/physical.c
+@@ -40,7 +40,9 @@
+ #include "lib/mcast-group-index.h"
+ #include "lib/ovn-sb-idl.h"
+ #include "lib/ovn-util.h"
++#include "ovn/actions.h"
+ #include "physical.h"
++#include "pinctrl.h"
+ #include "openvswitch/shash.h"
+ #include "simap.h"
+ #include "smap.h"
+@@ -984,6 +986,94 @@ enum access_type {
+     PORT_HA_REMOTE,
+ };
+ 
++static void
++setup_rarp_activation_strategy(const struct sbrec_port_binding *binding,
++                               ofp_port_t ofport, struct zone_ids *zone_ids,
++                               struct ovn_desired_flow_table *flow_table,
++                               struct ofpbuf *ofpacts_p)
++{
++    struct match match = MATCH_CATCHALL_INITIALIZER;
++
++    /* Unblock the port on ingress RARP. */
++    match_set_dl_type(&match, htons(ETH_TYPE_RARP));
++    match_set_in_port(&match, ofport);
++    ofpbuf_clear(ofpacts_p);
++
++    load_logical_ingress_metadata(binding, zone_ids, ofpacts_p);
++
++    size_t ofs = ofpacts_p->size;
++    struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts_p);
++    oc->max_len = UINT16_MAX;
++    oc->reason = OFPR_ACTION;
++
++    struct action_header ah = {
++        .opcode = htonl(ACTION_OPCODE_ACTIVATION_STRATEGY_RARP)
++    };
++    ofpbuf_put(ofpacts_p, &ah, sizeof ah);
++
++    ofpacts_p->header = oc;
++    oc->userdata_len = ofpacts_p->size - (ofs + sizeof *oc);
++    ofpact_finish_CONTROLLER(ofpacts_p, &oc);
++    put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
++
++    ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1010,
++                    binding->header_.uuid.parts[0],
++                    &match, ofpacts_p, &binding->header_.uuid);
++    ofpbuf_clear(ofpacts_p);
++
++    /* Block all non-RARP traffic for the port, both directions. */
++    match_init_catchall(&match);
++    match_set_in_port(&match, ofport);
++
++    ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1000,
++                    binding->header_.uuid.parts[0],
++                    &match, ofpacts_p, &binding->header_.uuid);
++
++    match_init_catchall(&match);
++    uint32_t dp_key = binding->datapath->tunnel_key;
++    uint32_t port_key = binding->tunnel_key;
++    match_set_metadata(&match, htonll(dp_key));
++    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
++
++    ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 1000,
++                    binding->header_.uuid.parts[0],
++                    &match, ofpacts_p, &binding->header_.uuid);
++}
++
++static void
++setup_activation_strategy(const struct sbrec_port_binding *binding,
++                          const struct sbrec_chassis *chassis,
++                          uint32_t dp_key, uint32_t port_key,
++                          ofp_port_t ofport, struct zone_ids *zone_ids,
++                          struct ovn_desired_flow_table *flow_table,
++                          struct ofpbuf *ofpacts_p)
++{
++    for (size_t i = 0; i < binding->n_additional_chassis; i++) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
++        if (binding->additional_chassis[i] == chassis) {
++            const char *strategy = smap_get(&binding->options,
++                                            "activation-strategy");
++            if (strategy
++                    && !lport_is_activated_by_activation_strategy(binding,
++                                                                  chassis)
++                    && !pinctrl_is_port_activated(dp_key, port_key)) {
++                if (!strcmp(strategy, "rarp")) {
++                    setup_rarp_activation_strategy(binding, ofport,
++                                                   zone_ids, flow_table,
++                                                   ofpacts_p);
++                } else {
++                    VLOG_WARN_RL(&rl,
++                                 "Unknown activation strategy defined for "
++                                 "port %s: %s",
++                                 binding->logical_port, strategy);
++                    return;
++                }
++            }
++            return;
++        }
++    }
++}
++
+ static void
+ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                       enum mf_field_id mff_ovn_geneve,
+@@ -1239,6 +1329,10 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+             }
+         }
+ 
++        setup_activation_strategy(binding, chassis, dp_key, port_key,
++                                  ofport, &zone_ids, flow_table,
++                                  ofpacts_p);
++
+         /* Remember the size with just strip vlan added so far,
+          * as we're going to remove this with ofpbuf_pull() later. */
+         uint32_t ofpacts_orig_size = ofpacts_p->size;
+diff --git a/controller/pinctrl.c b/controller/pinctrl.c
+index 428863293..f954362b7 100644
+--- a/controller/pinctrl.c
++++ b/controller/pinctrl.c
+@@ -29,10 +29,12 @@
+ #include "lport.h"
+ #include "mac-learn.h"
+ #include "nx-match.h"
++#include "ofctrl.h"
+ #include "latch.h"
+ #include "lib/packets.h"
+ #include "lib/sset.h"
+ #include "openvswitch/ofp-actions.h"
++#include "openvswitch/ofp-flow.h"
+ #include "openvswitch/ofp-msgs.h"
+ #include "openvswitch/ofp-packet.h"
+ #include "openvswitch/ofp-print.h"
+@@ -152,8 +154,8 @@ VLOG_DEFINE_THIS_MODULE(pinctrl);
+  *  and pinctrl_run().
+  *  'pinctrl_handler_seq' is used by pinctrl_run() to
+  *  wake up pinctrl_handler thread from poll_block() if any changes happened
+- *  in 'send_garp_rarp_data', 'ipv6_ras' and 'buffered_mac_bindings'
+- *  structures.
++ *  in 'send_garp_rarp_data', 'ipv6_ras', 'ports_to_activate_in_db' and
++ *  'buffered_mac_bindings' structures.
+  *
+  *  'pinctrl_main_seq' is used by pinctrl_handler() thread to wake up
+  *  the main thread from poll_block() when mac bindings/igmp groups need to
+@@ -198,6 +200,17 @@ static void wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
+ static void send_mac_binding_buffered_pkts(struct rconn *swconn)
+     OVS_REQUIRES(pinctrl_mutex);
+ 
++static void pinctrl_rarp_activation_strategy_handler(const struct match *md);
++
++static void init_activated_ports(void);
++static void destroy_activated_ports(void);
++static void wait_activated_ports(void);
++static void run_activated_ports(
++    struct ovsdb_idl_txn *ovnsb_idl_txn,
++    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
++    struct ovsdb_idl_index *sbrec_port_binding_by_name,
++    const struct sbrec_chassis *chassis);
++
+ static void init_send_garps_rarps(void);
+ static void destroy_send_garps_rarps(void);
+ static void send_garp_rarp_wait(long long int send_garp_rarp_time);
+@@ -522,6 +535,7 @@ pinctrl_init(void)
+     init_ipv6_ras();
+     init_ipv6_prefixd();
+     init_buffered_packets_map();
++    init_activated_ports();
+     init_event_table();
+     ip_mcast_snoop_init();
+     init_put_vport_bindings();
+@@ -3269,6 +3283,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
+         ovs_mutex_unlock(&pinctrl_mutex);
+         break;
+ 
++    case ACTION_OPCODE_ACTIVATION_STRATEGY_RARP:
++        ovs_mutex_lock(&pinctrl_mutex);
++        pinctrl_rarp_activation_strategy_handler(&pin.flow_metadata);
++        ovs_mutex_unlock(&pinctrl_mutex);
++        break;
++
+     default:
+         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
+                      ntohl(ah->opcode));
+@@ -3533,6 +3553,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+     bfd_monitor_run(ovnsb_idl_txn, bfd_table, sbrec_port_binding_by_name,
+                     chassis, active_tunnels);
+     run_put_fdbs(ovnsb_idl_txn, sbrec_fdb_by_dp_key_mac);
++    run_activated_ports(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
++                        sbrec_port_binding_by_key, chassis);
+     ovs_mutex_unlock(&pinctrl_mutex);
+ }
+ 
+@@ -4030,12 +4052,15 @@ prepare_ipv6_ras(const struct shash *local_active_ports_ras,
+ void
+ pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn)
+ {
++    ovs_mutex_lock(&pinctrl_mutex);
+     wait_put_mac_bindings(ovnsb_idl_txn);
+     wait_controller_event(ovnsb_idl_txn);
+     wait_put_vport_bindings(ovnsb_idl_txn);
+     int64_t new_seq = seq_read(pinctrl_main_seq);
+     seq_wait(pinctrl_main_seq, new_seq);
+     wait_put_fdbs(ovnsb_idl_txn);
++    wait_activated_ports();
++    ovs_mutex_unlock(&pinctrl_mutex);
+ }
+ 
+ /* Called by ovn-controller. */
+@@ -4050,6 +4075,7 @@ pinctrl_destroy(void)
+     destroy_ipv6_ras();
+     destroy_ipv6_prefixd();
+     destroy_buffered_packets_map();
++    destroy_activated_ports();
+     event_table_destroy();
+     destroy_put_mac_bindings();
+     destroy_put_vport_bindings();
+@@ -7727,6 +7753,152 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
+     }
+ }
+ 
++static struct ovs_list ports_to_activate_in_db = OVS_LIST_INITIALIZER(
++    &ports_to_activate_in_db);
++static struct ovs_list ports_to_activate_in_engine = OVS_LIST_INITIALIZER(
++    &ports_to_activate_in_engine);
++
++struct ovs_list *
++get_ports_to_activate_in_engine(void)
++{
++    ovs_mutex_lock(&pinctrl_mutex);
++    if (ovs_list_is_empty(&ports_to_activate_in_engine)) {
++        ovs_mutex_unlock(&pinctrl_mutex);
++        return NULL;
++    }
++
++    struct ovs_list *ap = xmalloc(sizeof *ap);
++    ovs_list_init(ap);
++    struct activated_port *pp;
++    LIST_FOR_EACH (pp, list, &ports_to_activate_in_engine) {
++        struct activated_port *new = xmalloc(sizeof *new);
++        new->dp_key = pp->dp_key;
++        new->port_key = pp->port_key;
++        ovs_list_push_front(ap, &new->list);
++    }
++    ovs_mutex_unlock(&pinctrl_mutex);
++    return ap;
++}
++
++static void
++init_activated_ports(void)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    ovs_list_init(&ports_to_activate_in_db);
++    ovs_list_init(&ports_to_activate_in_engine);
++}
++
++static void
++destroy_activated_ports(void)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    struct activated_port *pp;
++    LIST_FOR_EACH_POP (pp, list, &ports_to_activate_in_db) {
++        free(pp);
++    }
++    LIST_FOR_EACH_POP (pp, list, &ports_to_activate_in_engine) {
++        free(pp);
++    }
++}
++
++static void
++wait_activated_ports(void)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    if (!ovs_list_is_empty(&ports_to_activate_in_engine)) {
++        poll_immediate_wake();
++    }
++}
++
++bool pinctrl_is_port_activated(int64_t dp_key, int64_t port_key)
++{
++    const struct activated_port *pp;
++    ovs_mutex_lock(&pinctrl_mutex);
++    LIST_FOR_EACH (pp, list, &ports_to_activate_in_db) {
++        if (pp->dp_key == dp_key && pp->port_key == port_key) {
++            ovs_mutex_unlock(&pinctrl_mutex);
++            return true;
++        }
++    }
++    LIST_FOR_EACH (pp, list, &ports_to_activate_in_engine) {
++        if (pp->dp_key == dp_key && pp->port_key == port_key) {
++            ovs_mutex_unlock(&pinctrl_mutex);
++            return true;
++        }
++    }
++    ovs_mutex_unlock(&pinctrl_mutex);
++    return false;
++}
++
++static void
++run_activated_ports(struct ovsdb_idl_txn *ovnsb_idl_txn,
++                    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
++                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
++                    const struct sbrec_chassis *chassis)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    if (!ovnsb_idl_txn) {
++        return;
++    }
++
++    struct activated_port *pp;
++    LIST_FOR_EACH_SAFE (pp, list, &ports_to_activate_in_db) {
++        const struct sbrec_port_binding *pb = lport_lookup_by_key(
++            sbrec_datapath_binding_by_key, sbrec_port_binding_by_key,
++            pp->dp_key, pp->port_key);
++        if (!pb || lport_is_activated_by_activation_strategy(pb, chassis)) {
++            ovs_list_remove(&pp->list);
++            free(pp);
++            continue;
++        }
++        const char *activated_chassis = smap_get(
++            &pb->options, "additional-chassis-activated");
++        char *activated_str;
++        if (activated_chassis) {
++            activated_str = xasprintf(
++                "%s,%s", activated_chassis, chassis->name);
++            sbrec_port_binding_update_options_setkey(
++                pb, "additional-chassis-activated", activated_str);
++            free(activated_str);
++        } else {
++            sbrec_port_binding_update_options_setkey(
++                pb, "additional-chassis-activated", chassis->name);
++        }
++    }
++}
++
++void
++tag_port_as_activated_in_engine(struct activated_port *ap) {
++    ovs_mutex_lock(&pinctrl_mutex);
++    struct activated_port *pp;
++    LIST_FOR_EACH_SAFE (pp, list, &ports_to_activate_in_engine) {
++        if (pp->dp_key == ap->dp_key && pp->port_key == ap->port_key) {
++            ovs_list_remove(&pp->list);
++            free(pp);
++        }
++    }
++    ovs_mutex_unlock(&pinctrl_mutex);
++}
++
++static void
++pinctrl_rarp_activation_strategy_handler(const struct match *md)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    /* Tag the port as activated in-memory. */
++    struct activated_port *pp = xmalloc(sizeof *pp);
++    pp->port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
++    pp->dp_key = ntohll(md->flow.metadata);
++    ovs_list_push_front(&ports_to_activate_in_db, &pp->list);
++
++    pp = xmalloc(sizeof *pp);
++    pp->port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
++    pp->dp_key = ntohll(md->flow.metadata);
++    ovs_list_push_front(&ports_to_activate_in_engine, &pp->list);
++
++    /* Notify main thread on pending additional-chassis-activated updates. */
++    notify_pinctrl_main();
++}
++
+ static struct hmap put_fdbs;
+ 
+ /* MAC learning (fdb) related functions.  Runs within the main
+diff --git a/controller/pinctrl.h b/controller/pinctrl.h
+index 88f18e983..d4f52e94d 100644
+--- a/controller/pinctrl.h
++++ b/controller/pinctrl.h
+@@ -20,6 +20,7 @@
+ #include <stdint.h>
+ 
+ #include "lib/sset.h"
++#include "openvswitch/list.h"
+ #include "openvswitch/meta-flow.h"
+ 
+ struct hmap;
+@@ -33,6 +34,7 @@ struct sbrec_dns_table;
+ struct sbrec_controller_event_table;
+ struct sbrec_service_monitor_table;
+ struct sbrec_bfd_table;
++struct sbrec_port_binding;
+ 
+ void pinctrl_init(void);
+ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -56,4 +58,14 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+ void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
+ void pinctrl_destroy(void);
+ void pinctrl_set_br_int_name(char *br_int_name);
++
++struct activated_port {
++    uint32_t dp_key;
++    uint32_t port_key;
++    struct ovs_list list;
++};
++
++void tag_port_as_activated_in_engine(struct activated_port *ap);
++struct ovs_list *get_ports_to_activate_in_engine(void);
++bool pinctrl_is_port_activated(int64_t dp_key, int64_t port_key);
+ #endif /* controller/pinctrl.h */
+diff --git a/debian/changelog b/debian/changelog
+index caef68452..1c2de53bd 100644
+--- a/debian/changelog
++++ b/debian/changelog
+@@ -1,3 +1,9 @@
++OVN (22.06.1-1) unstable; urgency=low
++   [ OVN team ]
++   * New upstream version
++
++ -- OVN team <dev@openvswitch.org>  Fri, 03 Jun 2022 11:54:08 -0400
++
+ ovn (22.06.0-1) unstable; urgency=low
+ 
+    * New upstream version
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index 1ae496960..33c319f1c 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -683,6 +683,9 @@ enum action_opcode {
+     /* put_fdb(inport, eth.src).
+      */
+     ACTION_OPCODE_PUT_FDB,
++
++    /* activation_strategy_rarp() */
++    ACTION_OPCODE_ACTIVATION_STRATEGY_RARP,
+ };
+ 
+ /* Header. */
+diff --git a/northd/northd.c b/northd/northd.c
+index f120c2366..dd62c4098 100644
+--- a/northd/northd.c
++++ b/northd/northd.c
+@@ -3470,6 +3470,16 @@ ovn_port_update_sbrec(struct northd_input *input_data,
+                 smap_add(&options, "vlan-passthru", "true");
+             }
+ 
++            /* Retain activated chassis flags. */
++            if (op->sb->requested_additional_chassis) {
++                const char *activated_str = smap_get(
++                    &op->sb->options, "additional-chassis-activated");
++                if (activated_str) {
++                    smap_add(&options, "additional-chassis-activated",
++                             activated_str);
++                }
++            }
++
+             sbrec_port_binding_set_options(op->sb, &options);
+             smap_destroy(&options);
+             if (ovn_is_known_nb_lsp_type(op->nbsp->type)) {
+@@ -10976,6 +10986,9 @@ build_neigh_learning_flows_for_lrouter(
+                           copp_meter_get(COPP_ARP, od->nbr->copp,
+                                          meter_groups));
+ 
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 95,
++                      "nd_ns && (ip6.src == 0 || nd.sll == 0)", "next;");
++
+         ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 95,
+                           "nd_na && nd.tll == 0",
+                           "put_nd(inport, nd.target, eth.src); next;",
+@@ -12021,6 +12034,7 @@ build_gateway_redirect_flows_for_lrouter(
+     }
+     for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
+         const struct ovsdb_idl_row *stage_hint = NULL;
++        bool add_def_flow = true;
+ 
+         if (od->l3dgw_ports[i]->nbrp) {
+             stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
+@@ -12039,22 +12053,42 @@ build_gateway_redirect_flows_for_lrouter(
+         ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
+                                 ds_cstr(match), ds_cstr(actions),
+                                 stage_hint);
+-    }
++        for (int j = 0; j < od->n_nat_entries; j++) {
++            const struct ovn_nat *nat = &od->nat_entries[j];
+ 
+-    for (int i = 0; i < od->n_nat_entries; i++) {
+-        const struct ovn_nat *nat = &od->nat_entries[i];
++            if (!lrouter_nat_is_stateless(nat->nb) ||
++                strcmp(nat->nb->type, "dnat_and_snat") ||
++                (!nat->nb->allowed_ext_ips && !nat->nb->exempted_ext_ips)) {
++                continue;
++            }
+ 
+-        if (!lrouter_nat_is_stateless(nat->nb) ||
+-            strcmp(nat->nb->type, "dnat_and_snat")) {
+-           continue;
+-        }
++            struct ds match_ext = DS_EMPTY_INITIALIZER;
++            struct nbrec_address_set  *as = nat->nb->allowed_ext_ips
++                ? nat->nb->allowed_ext_ips : nat->nb->exempted_ext_ips;
++            ds_put_format(&match_ext, "%s && ip%s.src == $%s",
++                          ds_cstr(match), nat_entry_is_v6(nat) ? "6" : "4",
++                          as->name);
+ 
+-        ds_clear(match);
+-        ds_put_format(match, "ip && ip%s.dst == %s",
+-                      nat_entry_is_v6(nat) ? "6" : "4",
+-                      nat->nb->external_ip);
+-        ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
+-                      ds_cstr(match), "drop;");
++            if (nat->nb->allowed_ext_ips) {
++                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
++                                        75, ds_cstr(&match_ext),
++                                        ds_cstr(actions), stage_hint);
++                if (add_def_flow) {
++                    ds_clear(&match_ext);
++                    ds_put_format(&match_ext, "ip && ip%s.dst == %s",
++                                  nat_entry_is_v6(nat) ? "6" : "4",
++                                  nat->nb->external_ip);
++                    ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 70,
++                                  ds_cstr(&match_ext), "drop;");
++                    add_def_flow = false;
++                }
++            } else if (nat->nb->exempted_ext_ips) {
++                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
++                                        75, ds_cstr(&match_ext), "drop;",
++                                        stage_hint);
++            }
++            ds_destroy(&match_ext);
++        }
+     }
+ 
+     /* Packets are allowed by default. */
+diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
+index 1f7022490..32858c0b4 100644
+--- a/northd/ovn-northd.8.xml
++++ b/northd/ovn-northd.8.xml
+@@ -2323,6 +2323,12 @@ next;
+         to learn the neighbor.
+       </li>
+ 
++      <li>
++        A priority-95 flow with the match <code>nd_ns &amp;&amp;
++          (ip6.src == 0 || nd.sll == 0)</code> and applies the action
++        <code>next;</code>
++      </li>
++
+       <li>
+         A priority-90 flow with the match <code>arp</code> and
+         applies the action
+@@ -3018,8 +3024,7 @@ icmp6 {
+           <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
+           with an action <code>ct_snat; </code>. If the NAT rule is of type
+           dnat_and_snat and has <code>stateless=true</code> in the
+-          options, then the action would be <code>ip4/6.dst=
+-          (<var>B</var>)</code>.
++          options, then the action would be <code>next;</code>.
+         </p>
+ 
+         <p>
+@@ -3059,7 +3064,7 @@ icmp6 {
+               action <code>ct_snat_in_czone;</code> to unSNAT in the common
+               zone.  If the NAT rule is of type dnat_and_snat and has
+               <code>stateless=true</code> in the options, then the action
+-              would be <code>ip4/6.dst=(<var>B</var>)</code>.
++              would be <code>next;</code>.
+             </p>
+ 
+             <p>
+@@ -4217,6 +4222,26 @@ icmp6 {
+         external ip and <var>D</var> is NAT external mac.
+       </li>
+ 
++      <li>
++        For each <code>dnat_and_snat</code> NAT rule with
++        <code>stateless=true</code> and <code>allowed_ext_ips</code>
++        configured, a priority-75 flow is programmed with match
++        <code>ip4.dst == <var>B</var></code> and action
++        <code>outport = <var>CR</var>; next;</code> where <var>B</var>
++        is the NAT rule external IP and <var>CR</var> is the
++        <code>chassisredirect</code> port representing the instance
++        of the logical router distributed gateway port on the
++        gateway chassis. Moreover a priority-70 flow is programmed
++        with same match and action <code>drop;</code>.
++        For each <code>dnat_and_snat</code> NAT rule with
++        <code>stateless=true</code> and <code>exempted_ext_ips</code>
++        configured, a priority-75 flow is programmed with match
++        <code>ip4.dst == <var>B</var></code> and action
++        <code>drop;</code> where <var>B</var> is the NAT rule
++        external IP.
++        A similar flow is added for IPv6 traffic.
++      </li>
++
+       <li>
+         For each NAT rule in the OVN Northbound database that can
+         be handled in a distributed manner, a priority-80 logical flow
+@@ -4415,8 +4440,7 @@ nd_ns {
+           is the logical router gateway port, with an action
+           <code>ct_dnat_in_czone;</code>. If the NAT rule is of type
+           dnat_and_snat and has <code>stateless=true</code> in the
+-          options, then the action would be <code>ip4/6.src=
+-          (<var>B</var>)</code>.
++          options, then the action would be <code>next;</code>.
+         </p>
+ 
+         <p>
+diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
+index e4e980720..ab28756af 100644
+--- a/northd/ovn-northd.c
++++ b/northd/ovn-northd.c
+@@ -107,7 +107,10 @@ static const char *rbac_port_binding_auth[] =
+ static const char *rbac_port_binding_update[] =
+     {"chassis", "additional_chassis",
+      "encap", "additional_encap",
+-     "up", "virtual_parent"};
++     "up", "virtual_parent",
++     /* NOTE: we only need to update the additional-chassis-activated key,
++      * but RBAC_Role doesn't support mutate operation for subkeys. */
++     "options"};
+ 
+ static const char *rbac_mac_binding_auth[] =
+     {""};
+diff --git a/ovn-nb.xml b/ovn-nb.xml
+index c197f431f..e700b2e88 100644
+--- a/ovn-nb.xml
++++ b/ovn-nb.xml
+@@ -1045,6 +1045,17 @@
+           </p>
+         </column>
+ 
++        <column name="options" key="activation-strategy">
++          If used with multiple chassis set in
++          <ref column="requested-chassis"/>, specifies an activation strategy
++          for all additional chassis. By default, no activation strategy is
++          used, meaning additional port locations are immediately available for
++          use. When set to "rarp", the port is blocked for ingress and egress
++          communication until a RARP packet is sent from a new location. The
++          "rarp" strategy is useful in live migration scenarios for virtual
++          machines.
++        </column>
++
+         <column name="options" key="iface-id-ver">
+           If set, this port will be bound by <code>ovn-controller</code>
+           only if this same key and value is configured in the
+diff --git a/ovn-sb.xml b/ovn-sb.xml
+index 9f47a037e..49e851e2a 100644
+--- a/ovn-sb.xml
++++ b/ovn-sb.xml
+@@ -3374,6 +3374,21 @@ tcp.flags = RST;
+         </p>
+       </column>
+ 
++      <column name="options" key="activation-strategy">
++        If used with multiple chassis set in <ref column="requested-chassis"/>,
++        specifies an activation strategy for all additional chassis. By
++        default, no activation strategy is used, meaning additional port
++        locations are immediately available for use. When set to "rarp", the
++        port is blocked for ingress and egress communication until a RARP
++        packet is sent from a new location. The "rarp" strategy is useful
++        in live migration scenarios for virtual machines.
++      </column>
++
++      <column name="options" key="additional-chassis-activated">
++        When <ref column="activation-strategy"/> is set, this option indicates
++        that the port was activated using the strategy specified.
++      </column>
++
+       <column name="options" key="iface-id-ver">
+         If set, this port will be bound by <code>ovn-controller</code>
+         only if this same key and value is configured in the
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index a071b3689..76e5a5c2b 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -6734,6 +6734,7 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
+   table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_na), action=(put_nd(inport, nd.target, nd.tll); next;)
+   table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_ns), action=(put_nd(inport, ip6.src, nd.sll); next;)
+   table=2 (lr_in_learn_neighbor), priority=95   , match=(nd_na && nd.tll == 0), action=(put_nd(inport, nd.target, eth.src); next;)
++  table=2 (lr_in_learn_neighbor), priority=95   , match=(nd_ns && (ip6.src == 0 || nd.sll == 0)), action=(next;)
+ ])
+ 
+ AT_CLEANUP
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 3c079e0fb..1aa562de5 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -7432,7 +7432,7 @@ ovs-vsctl -- add-port br-int vif2 -- \
+ # Allow some time for ovn-northd and ovn-controller to catch up.
+ wait_for_ports_up
+ check ovn-nbctl --wait=hv sync
+-ovn-nbctl dump-flows > sbflows
++ovn-sbctl dump-flows > sbflows
+ AT_CAPTURE_FILE([sbflows])
+ 
+ for i in 1 2; do
+@@ -8037,7 +8037,7 @@ wait_for_ports_up
+ check ovn-nbctl --wait=hv sync
+ sleep 1
+ 
+-ovn-nbctl dump-flows > sbflows
++ovn-sbctl dump-flows > sbflows
+ AT_CAPTURE_FILE([sbflows])
+ 
+ for i in 1 2; do
+@@ -14015,6 +14015,7 @@ AT_CLEANUP
+ 
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([options:multiple requested-chassis for logical port])
++AT_KEYWORDS([multi-chassis])
+ ovn_start
+ 
+ net_add n1
+@@ -14104,6 +14105,7 @@ AT_CLEANUP
+ 
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([options:multiple requested-chassis for logical port: change chassis role])
++AT_KEYWORDS([multi-chassis])
+ ovn_start
+ 
+ net_add n1
+@@ -14153,6 +14155,7 @@ AT_CLEANUP
+ 
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([options:multiple requested-chassis for logical port: unclaimed behavior])
++AT_KEYWORDS([multi-chassis])
+ ovn_start
+ 
+ net_add n1
+@@ -14233,6 +14236,7 @@ AT_CLEANUP
+ 
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([basic connectivity with multiple requested-chassis])
++AT_KEYWORDS([multi-chassis])
+ ovn_start
+ 
+ net_add n1
+@@ -14567,6 +14571,7 @@ AT_CLEANUP
+ 
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([localnet connectivity with multiple requested-chassis])
++AT_KEYWORDS([multi-chassis])
+ ovn_start
+ 
+ net_add n1
+@@ -14924,6 +14929,391 @@ OVN_CLEANUP([hv1],[hv2],[hv3])
+ AT_CLEANUP
+ ])
+ 
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([options:activation-strategy for logical port])
++AT_KEYWORDS([multi-chassis])
++ovn_start
++
++net_add n1
++
++sim_add hv1
++as hv1
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.11
++
++sim_add hv2
++as hv2
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.12
++
++sim_add hv3
++as hv3
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.13
++
++# Disable local ARP responder to pass ARP requests through tunnels
++check ovn-nbctl ls-add ls0 -- add Logical_Switch ls0 other_config vlan-passthru=true
++
++check ovn-nbctl lsp-add ls0 migrator
++check ovn-nbctl lsp-set-options migrator requested-chassis=hv1,hv2 \
++                                         activation-strategy=rarp
++
++check ovn-nbctl lsp-add ls0 first
++check ovn-nbctl lsp-set-options first requested-chassis=hv1
++check ovn-nbctl lsp-add ls0 second
++check ovn-nbctl lsp-set-options second requested-chassis=hv2
++check ovn-nbctl lsp-add ls0 outside
++check ovn-nbctl lsp-set-options outside requested-chassis=hv3
++
++check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:10 10.0.0.10"
++check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1"
++check ovn-nbctl lsp-set-addresses second "00:00:00:00:00:02 10.0.0.2"
++check ovn-nbctl lsp-set-addresses outside "00:00:00:00:00:03 10.0.0.3"
++
++for hv in hv1 hv2; do
++    as $hv check ovs-vsctl -- add-port br-int migrator -- \
++        set Interface migrator external-ids:iface-id=migrator \
++                               options:tx_pcap=$hv/migrator-tx.pcap \
++                               options:rxq_pcap=$hv/migrator-rx.pcap
++done
++
++as hv1 check ovs-vsctl -- add-port br-int first -- \
++    set Interface first external-ids:iface-id=first
++as hv2 check ovs-vsctl -- add-port br-int second -- \
++    set Interface second external-ids:iface-id=second
++as hv3 check ovs-vsctl -- add-port br-int outside -- \
++    set Interface outside external-ids:iface-id=outside
++
++for hv in hv1 hv2 hv3; do
++    wait_row_count Chassis 1 name=$hv
++done
++hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
++hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
++hv3_uuid=$(fetch_column Chassis _uuid name=hv3)
++
++wait_column "$hv1_uuid" Port_Binding chassis logical_port=migrator
++wait_column "$hv1_uuid" Port_Binding requested_chassis logical_port=migrator
++wait_column "$hv2_uuid" Port_Binding additional_chassis logical_port=migrator
++wait_column "$hv2_uuid" Port_Binding requested_additional_chassis logical_port=migrator
++
++wait_column "$hv1_uuid" Port_Binding chassis logical_port=first
++wait_column "$hv2_uuid" Port_Binding chassis logical_port=second
++wait_column "$hv3_uuid" Port_Binding chassis logical_port=outside
++
++OVN_POPULATE_ARP
++
++send_arp() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa}
++    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
++    echo "${request}"
++}
++
++send_rarp() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}80350001080006040001${eth_src}${spa}${eth_dst}${tpa}
++    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
++    echo "${request}"
++}
++
++reset_pcap_file() {
++    local hv=$1
++    local iface=$2
++    local pcap_file=$3
++    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
++                                                   options:rxq_pcap=dummy-rx.pcap
++    check rm -f ${pcap_file}*.pcap
++    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
++                                                   options:rxq_pcap=${pcap_file}-rx.pcap
++}
++
++reset_env() {
++    reset_pcap_file hv1 migrator hv1/migrator
++    reset_pcap_file hv2 migrator hv2/migrator
++    reset_pcap_file hv1 first hv1/first
++    reset_pcap_file hv2 second hv2/second
++    reset_pcap_file hv3 outside hv3/outside
++
++    for port in hv1/migrator hv2/migrator hv1/first hv2/second hv3/outside; do
++        : > $port.expected
++    done
++}
++
++check_packets() {
++    OVN_CHECK_PACKETS([hv1/migrator-tx.pcap], [hv1/migrator.expected])
++    OVN_CHECK_PACKETS([hv2/migrator-tx.pcap], [hv2/migrator.expected])
++    OVN_CHECK_PACKETS([hv3/outside-tx.pcap], [hv3/outside.expected])
++    OVN_CHECK_PACKETS([hv1/first-tx.pcap], [hv1/first.expected])
++    OVN_CHECK_PACKETS([hv2/second-tx.pcap], [hv2/second.expected])
++}
++
++migrator_spa=$(ip_to_hex 10 0 0 10)
++first_spa=$(ip_to_hex 10 0 0 1)
++second_spa=$(ip_to_hex 10 0 0 2)
++outside_spa=$(ip_to_hex 10 0 0 3)
++
++reset_env
++
++# Packet from hv3:Outside arrives to hv1:Migrator
++# hv3:Outside cannot reach hv2:Migrator because it is blocked by RARP strategy
++request=$(send_arp hv3 outside 000000000003 000000000010 $outside_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++
++# Packet from hv1:First arrives to hv1:Migrator
++# hv1:First cannot reach hv2:Migrator because it is blocked by RARP strategy
++request=$(send_arp hv1 first 000000000001 000000000010 $first_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++
++# Packet from hv2:Second arrives to hv1:Migrator
++# hv2:Second cannot reach hv2:Migrator because it is blocked by RARP strategy
++request=$(send_arp hv2 second 000000000002 000000000010 $second_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++
++check_packets
++reset_env
++
++# Packet from hv1:Migrator arrives to hv3:Outside
++request=$(send_arp hv1 migrator 000000000010 000000000003 $migrator_spa $outside_spa)
++echo $request >> hv3/outside.expected
++
++# Packet from hv1:Migrator arrives to hv1:First
++request=$(send_arp hv1 migrator 000000000010 000000000001 $migrator_spa $first_spa)
++echo $request >> hv1/first.expected
++
++# Packet from hv1:Migrator arrives to hv2:Second
++request=$(send_arp hv1 migrator 000000000010 000000000002 $migrator_spa $second_spa)
++echo $request >> hv2/second.expected
++
++check_packets
++reset_env
++
++# hv2:Migrator cannot reach to hv3:Outside because it is blocked by RARP strategy
++request=$(send_arp hv2 migrator 000000000010 000000000003 $migrator_spa $outside_spa)
++
++check_packets
++reset_env
++
++AT_CHECK([ovn-sbctl find port_binding logical_port=migrator | grep -q additional-chassis-activated], [1])
++
++# Now activate hv2:Migrator location
++request=$(send_rarp hv2 migrator 000000000010 ffffffffffff $migrator_spa $migrator_spa)
++
++# RARP was reinjected into the pipeline
++echo $request >> hv3/outside.expected
++echo $request >> hv1/first.expected
++echo $request >> hv2/second.expected
++
++check_packets
++reset_env
++
++pb_uuid=$(ovn-sbctl --bare --columns _uuid find Port_Binding logical_port=migrator)
++OVS_WAIT_UNTIL([test xhv2 = x$(ovn-sbctl get Port_Binding $pb_uuid options:additional-chassis-activated | tr -d '""')])
++
++# Now packet arrives to both locations
++request=$(send_arp hv3 outside 000000000003 000000000010 $outside_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++echo $request >> hv2/migrator.expected
++
++check_packets
++reset_env
++
++# Packet from hv1:Migrator still arrives to hv3:Outside
++request=$(send_arp hv1 migrator 000000000010 000000000003 $migrator_spa $outside_spa)
++echo $request >> hv3/outside.expected
++
++check_packets
++reset_env
++
++# hv2:Migrator can now reach to hv3:Outside because RARP strategy activated it
++request=$(send_arp hv2 migrator 000000000010 000000000003 $migrator_spa $outside_spa)
++echo $request >> hv3/outside.expected
++
++check_packets
++
++# complete port migration and check that -activated flag is reset
++check ovn-nbctl lsp-set-options migrator requested-chassis=hv2
++OVS_WAIT_UNTIL([test x = x$(ovn-sbctl get Port_Binding $pb_uuid options:additional-chassis-activated)])
++
++OVN_CLEANUP([hv1],[hv2],[hv3])
++
++AT_CLEANUP
++])
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([options:activation-strategy=rarp is not waiting for southbound db])
++AT_KEYWORDS([multi-chassis])
++# unskip when ovn-controller is able to process incremental updates to flow
++# table without ovsdb transaction available
++AT_SKIP_IF([true])
++
++ovn_start
++
++net_add n1
++
++sim_add hv1
++as hv1
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.11
++
++sim_add hv2
++as hv2
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.12
++
++# Disable local ARP responder to pass ARP requests through tunnels
++check ovn-nbctl ls-add ls0 -- add Logical_Switch ls0 other_config vlan-passthru=true
++
++check ovn-nbctl lsp-add ls0 migrator
++check ovn-nbctl lsp-set-options migrator requested-chassis=hv1,hv2 \
++                                         activation-strategy=rarp
++
++check ovn-nbctl lsp-add ls0 first
++check ovn-nbctl lsp-set-options first requested-chassis=hv1
++
++check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:10 10.0.0.10"
++check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1"
++
++for hv in hv1 hv2; do
++    as $hv check ovs-vsctl -- add-port br-int migrator -- \
++        set Interface migrator external-ids:iface-id=migrator \
++                               options:tx_pcap=$hv/migrator-tx.pcap \
++                               options:rxq_pcap=$hv/migrator-rx.pcap
++done
++
++as hv1 check ovs-vsctl -- add-port br-int first -- \
++    set Interface first external-ids:iface-id=first
++
++for hv in hv1 hv2; do
++    wait_row_count Chassis 1 name=$hv
++done
++hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
++hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
++
++wait_column "$hv1_uuid" Port_Binding chassis logical_port=migrator
++wait_column "$hv1_uuid" Port_Binding requested_chassis logical_port=migrator
++wait_column "$hv2_uuid" Port_Binding additional_chassis logical_port=migrator
++wait_column "$hv2_uuid" Port_Binding requested_additional_chassis logical_port=migrator
++
++wait_column "$hv1_uuid" Port_Binding chassis logical_port=first
++
++OVN_POPULATE_ARP
++
++send_arp() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa}
++    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
++    echo "${request}"
++}
++
++send_rarp() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}80350001080006040001${eth_src}${spa}${eth_dst}${tpa}
++    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
++    echo "${request}"
++}
++
++reset_pcap_file() {
++    local hv=$1
++    local iface=$2
++    local pcap_file=$3
++    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
++                                                   options:rxq_pcap=dummy-rx.pcap
++    check rm -f ${pcap_file}*.pcap
++    as $hv check ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
++                                                   options:rxq_pcap=${pcap_file}-rx.pcap
++}
++
++reset_env() {
++    reset_pcap_file hv1 migrator hv1/migrator
++    reset_pcap_file hv2 migrator hv2/migrator
++    reset_pcap_file hv1 first hv1/first
++
++    for port in hv1/migrator hv2/migrator hv1/first; do
++        : > $port.expected
++    done
++}
++
++check_packets() {
++    OVN_CHECK_PACKETS([hv1/migrator-tx.pcap], [hv1/migrator.expected])
++    OVN_CHECK_PACKETS([hv2/migrator-tx.pcap], [hv2/migrator.expected])
++    OVN_CHECK_PACKETS([hv1/first-tx.pcap], [hv1/first.expected])
++}
++
++migrator_spa=$(ip_to_hex 10 0 0 10)
++first_spa=$(ip_to_hex 10 0 0 1)
++
++reset_env
++
++# Packet from hv1:First arrives to hv1:Migrator
++# hv1:First cannot reach hv2:Migrator because it is blocked by RARP strategy
++request=$(send_arp hv1 first 000000000001 000000000010 $first_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++
++check_packets
++reset_env
++
++# Packet from hv1:Migrator arrives to hv1:First
++request=$(send_arp hv1 migrator 000000000010 000000000001 $migrator_spa $first_spa)
++echo $request >> hv1/first.expected
++
++check_packets
++reset_env
++
++# hv2:Migrator cannot reach to hv1:First because it is blocked by RARP strategy
++request=$(send_arp hv2 migrator 000000000010 000000000001 $migrator_spa $first_spa)
++
++check_packets
++reset_env
++
++# Before proceeding, stop ovsdb-server to make sure we test in the environment
++# that can't remove flows triggered by updates to database
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++# Now activate hv2:Migrator location
++request=$(send_rarp hv2 migrator 000000000010 ffffffffffff $migrator_spa $migrator_spa)
++
++# RARP was reinjected into the pipeline
++echo $request >> hv1/first.expected
++
++# Now packet from hv1:First arrives to both locations
++request=$(send_arp hv1 first 000000000001 000000000010 $first_spa $migrator_spa)
++echo $request >> hv1/migrator.expected
++echo $request >> hv2/migrator.expected
++
++# Packet from hv1:Migrator still arrives to hv1:First
++request=$(send_arp hv1 migrator 000000000010 000000000001 $migrator_spa $first_spa)
++echo $request >> hv1/first.expected
++
++# hv2:Migrator can now reach to hv1:First because RARP strategy activated it
++request=$(send_arp hv2 migrator 000000000010 000000000001 $migrator_spa $first_spa)
++echo $request >> hv1/first.expected
++
++check_packets
++
++# restart ovsdb-server before cleaning up to give ovn-controller a chance to
++# exit gracefully
++mv $ovs_base/ovn-sb/ovsdb-server.log $ovs_base/ovn-sb/ovsdb-server.log.prev
++as ovn-sb start_daemon ovsdb-server \
++        -vjsonrpc \
++        --remote=punix:$ovs_base/ovn-sb/$1.sock \
++        --remote=db:OVN_Southbound,SB_Global,connections \
++        --private-key=$PKIDIR/testpki-test-privkey.pem \
++        --certificate=$PKIDIR/testpki-test-cert.pem \
++        --ca-cert=$PKIDIR/testpki-cacert.pem \
++        $ovs_base/ovn-sb/ovn-sb.db
++
++PARSE_LISTENING_PORT([$ovs_base/ovn-sb/ovsdb-server.log], [TCP_PORT])
++for i in 1 2; do
++    as hv$i
++    ovs-vsctl \
++        -- set Open_vSwitch . external-ids:ovn-remote=ssl:127.0.0.1:$TCP_PORT
++done
++OVN_CLEANUP([hv1],[hv2])
++
++AT_CLEANUP
++])
++
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([options:requested-chassis for logical port])
+ ovn_start
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index 4bf22593a..df2da3408 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -6741,6 +6741,21 @@ NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \
+ [0], [dnl
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
++
++dnat_and_snat_uuid=$(fetch_column nb:NAT _uuid external_ip=172.18.2.10)
++ovn-nbctl set NAT $dnat_and_snat_uuid options:stateless=true
++
++# A ping from vm1 should hairpin in lr1 and successfully DNAT to vm2
++NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++# A ping from vm2 should hairpin in lr1 and successfully DNAT to vm2
++NS_CHECK_EXEC([vm2], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
+ kill $(pidof ovn-controller)
+ 
+ as ovn-sb
+diff --git a/utilities/ovn-ctl b/utilities/ovn-ctl
+index d733aa42d..93be9b84b 100755
+--- a/utilities/ovn-ctl
++++ b/utilities/ovn-ctl
+@@ -42,8 +42,21 @@ ovn_ic_db_conf_file="$ovn_etcdir/ovn-ic-db-params.conf"
+ 
+ pidfile_is_running () {
+     pidfile=$1
+-    test -e "$pidfile" && [ -s "$pidfile" ] && pid=`cat "$pidfile"` && pid_exists "$pid"
+-} >/dev/null 2>&1
++    cmd=$2
++    if [ ! -s "$pidfile" ]; then
++        # file missing or empty
++        return 1
++    fi
++    pid=`cat "$pidfile"`
++    if ! pid_exists $pid; then
++        # pid is dead
++        return 1
++    fi
++    if [ -n "$cmd" ]; then
++        return $(pid_comm_check "$cmd" "$pid")
++    fi
++    return 0
++}
+ 
+ stop_nb_ovsdb() {
+     OVS_RUNDIR=${OVS_RUNDIR} stop_ovn_daemon ovnnb_db $DB_NB_PIDFILE $DB_NB_CTRL_SOCK
+@@ -199,7 +212,7 @@ start_ovsdb__() {
+     ovn_install_dir "$ovn_etcdir"
+ 
+     # Check and eventually start ovsdb-server for DB
+-    if pidfile_is_running $db_pid_file; then
++    if pidfile_is_running $db_pid_file ovsdb-server; then
+         return
+     fi
+ 
+@@ -298,6 +311,10 @@ $cluster_remote_port
+         set "$@" --sync-from=`cat $active_conf_file`
+     fi
+ 
++    if test X"$extra_args" != X; then
++        set "$@" $extra_args
++    fi
++
+     local run_ovsdb_in_bg="no"
+     local process_id=
+     if test X$detach = Xno && test $mode = cluster && test -z "$cluster_remote_addr" ; then
+@@ -528,6 +545,10 @@ start_ic () {
+ 
+         set "$@" $OVN_IC_LOG $ovn_ic_params
+ 
++        if test X"$extra_args" != X; then
++            set "$@" $extra_args
++        fi
++
+         OVS_RUNDIR=${OVS_RUNDIR} start_ovn_daemon "$OVN_IC_PRIORITY" "$OVN_IC_WRAPPER" "$@"
+     fi
+ }
+@@ -550,6 +571,10 @@ start_controller () {
+ 
+     [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
+ 
++    if test X"$extra_args" != X; then
++        set "$@" $extra_args
++    fi
++
+     OVS_RUNDIR=${OVS_RUNDIR} start_ovn_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@"
+ }
+ 
+@@ -577,6 +602,10 @@ start_controller_vtep () {
+ 
+     [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
+ 
++    if test X"$extra_args" != X; then
++        set "$@" $extra_args
++    fi
++
+     OVS_RUNDIR=${OVS_RUNDIR} start_ovn_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@"
+ }
+ 
+@@ -1093,8 +1122,10 @@ EOF
+ 
+ set_defaults
+ command=
++extra_args=
+ for arg
+ do
++    shift
+     case $arg in
+         -h | --help)
+             usage
+@@ -1117,6 +1148,10 @@ do
+             type=bool
+             set_option
+             ;;
++        --)
++            extra_args=$@
++            break
++            ;;
+         -*)
+             echo >&2 "$0: unknown option \"$arg\" (use --help for help)"
+             exit 1
+diff --git a/utilities/ovn-ctl.8.xml b/utilities/ovn-ctl.8.xml
+index a1d39b22b..42d16fabc 100644
+--- a/utilities/ovn-ctl.8.xml
++++ b/utilities/ovn-ctl.8.xml
+@@ -4,7 +4,10 @@
+     <p>ovn-ctl -- Open Virtual Network northbound daemon lifecycle utility</p>
+ 
+     <h1>Synopsis</h1>
+-    <p><code>ovn-ctl</code> [<var>options</var>] <var>command</var></p>
++    <p>
++      <code>ovn-ctl</code> [<var>options</var>] <var>command</var>
++      [--- <var>extra_args</var>]
++    </p>
+ 
+     <h1>Description</h1>
+     <p>This program is intended to be invoked internally by Open Virtual Network
+@@ -156,6 +159,15 @@
+     <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> Extra Options </h1>
++    <p>
++      Any options after '--' will be passed on to the binary run by
++      <var>command</var> with the exception of start_northd, which can have
++      options specified in ovn-northd-db-params.conf. Any <var>extra_args</var>
++      passed to start_northd will be passed to the ovsdb-servers if
++      <code>--ovn-manage-ovsdb=yes</code>
++    </p>
++
+     <h1>Configuration files</h1>
+     <p>Following are the optional configuration files. If present, it should be located in the etc dir</p>
+ 
+diff --git a/utilities/ovn-dbctl.c b/utilities/ovn-dbctl.c
+index a292e589d..c4cc8c9b2 100644
+--- a/utilities/ovn-dbctl.c
++++ b/utilities/ovn-dbctl.c
+@@ -202,6 +202,13 @@ ovn_dbctl_main(int argc, char *argv[],
+         error = ctl_parse_commands(argc - optind, argv_ + optind,
+                                    &local_options, &commands, &n_commands);
+         if (error) {
++            ovsdb_idl_destroy(idl);
++            idl = the_idl = NULL;
++
++            for (int i = 0; i < argc; i++) {
++                free(argv_[i]);
++            }
++            free(argv_);
+             ctl_fatal("%s", error);
+         }
+ 
diff --git a/SPECS/ovn22.03.spec b/SPECS/ovn22.03.spec
deleted file mode 100644
index 4da4fd4..0000000
--- a/SPECS/ovn22.03.spec
+++ /dev/null
@@ -1,625 +0,0 @@
-# Copyright (C) 2009, 2010, 2013, 2014 Nicira Networks, Inc.
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved.  This file is offered as-is,
-# without warranty of any kind.
-#
-# If tests have to be skipped while building, specify the '--without check'
-# option. For example:
-# rpmbuild -bb --without check rhel/openvswitch-fedora.spec
-
-# This defines the base package name's version.
-
-%define pkgver 2.13
-%define pkgname ovn22.03
-
-# If libcap-ng isn't available and there is no need for running OVS
-# as regular user, specify the '--without libcapng'
-%bcond_without libcapng
-
-# Enable PIE, bz#955181
-%global _hardened_build 1
-
-# RHEL-7 doesn't define _rundir macro yet
-# Fedora 15 onwards uses /run as _rundir
-%if 0%{!?_rundir:1}
-%define _rundir /run
-%endif
-
-# Build python2 (that provides python) and python3 subpackages on Fedora
-# Build only python3 (that provides python) subpackage on RHEL8
-# Build only python subpackage on RHEL7
-%if 0%{?rhel} > 7 || 0%{?fedora}
-# On RHEL8 Sphinx is included in buildroot
-%global external_sphinx 1
-%else
-# Don't use external sphinx (RHV doesn't have optional repositories enabled)
-%global external_sphinx 0
-%endif
-
-# We would see rpmlinit error - E: hardcoded-library-path in '% {_prefix}/lib'.
-# But there is no solution to fix this. Using {_lib} macro will solve the
-# rpmlink error, but will install the files in /usr/lib64/.
-# OVN pacemaker ocf script file is copied in /usr/lib/ocf/resource.d/ovn/
-# and we are not sure if pacemaker looks into this path to find the
-# OVN resource agent script.
-%global ovnlibdir %{_prefix}/lib
-
-Name: %{pkgname}
-Summary: Open Virtual Network support
-Group: System Environment/Daemons
-URL: http://www.ovn.org/
-Version: 22.03.0
-Release: 22%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
-Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
-Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
-
-# Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
-# lib/sflow*.[ch] files are SISSL
-License: ASL 2.0 and LGPLv2+ and SISSL
-
-# Always pull an upstream release, since this is what we rebase to.
-Source: https://github.com/ovn-org/ovn/archive/v%{version}.tar.gz#/ovn-%{version}.tar.gz
-
-%define ovscommit ba159ee0f97ed770c244cd6710d34fe20595541d
-%define ovsshortcommit ba159ee
-
-Source10: https://github.com/openvswitch/ovs/archive/%{ovscommit}.tar.gz#/openvswitch-%{ovsshortcommit}.tar.gz
-%define ovsdir ovs-%{ovscommit}
-
-%define docutilsver 0.12
-%define pygmentsver 1.4
-%define sphinxver   1.1.3
-Source100: https://pypi.io/packages/source/d/docutils/docutils-%{docutilsver}.tar.gz
-Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.tar.gz
-Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz
-
-Source500: configlib.sh
-Source501: gen_config_group.sh
-Source502: set_config.sh
-
-# Important: source503 is used as the actual copy file
-# @TODO: this causes a warning - fix it?
-Source504: arm64-armv8a-linuxapp-gcc-config
-Source505: ppc_64-power8-linuxapp-gcc-config
-Source506: x86_64-native-linuxapp-gcc-config
-
-Patch:     %{pkgname}.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
-%if %{external_sphinx}
-BuildRequires: python3-sphinx
-%else
-# Sphinx dependencies
-BuildRequires: python-devel
-BuildRequires: python-setuptools
-#BuildRequires: python2-docutils
-BuildRequires: python-jinja2
-BuildRequires: python-nose
-#BuildRequires: python2-pygments
-# docutils dependencies
-BuildRequires: python-imaging
-# pygments dependencies
-BuildRequires: python-nose
-%endif
-
-BuildRequires: gcc gcc-c++ make
-BuildRequires: autoconf automake libtool
-BuildRequires: systemd-units openssl openssl-devel
-BuildRequires: python3-devel python3-setuptools
-BuildRequires: desktop-file-utils
-BuildRequires: groff-base graphviz
-BuildRequires: unbound-devel
-
-# make check dependencies
-BuildRequires: procps-ng
-%if 0%{?rhel} == 8 || 0%{?fedora}
-BuildRequires: python3-pyOpenSSL
-%endif
-BuildRequires: tcpdump
-
-%if %{with libcapng}
-BuildRequires: libcap-ng libcap-ng-devel
-%endif
-
-Requires: hostname openssl iproute module-init-tools
-
-Requires(post): systemd-units
-Requires(preun): systemd-units
-Requires(postun): systemd-units
-
-# to skip running checks, pass --without check
-%bcond_without check
-
-%description
-OVN, the Open Virtual Network, is a system to support virtual network
-abstraction.  OVN complements the existing capabilities of OVS to add
-native support for virtual network abstractions, such as virtual L2 and L3
-overlays and security groups.
-
-%package central
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: %{pkgname}
-Requires: firewalld-filesystem
-Provides: openvswitch%{pkgver}-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release}
-Obsoletes: openvswitch%{pkgver}-ovn-central < 2.11.0-1
-
-%description central
-OVN DB servers and ovn-northd running on a central node.
-
-%package host
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: %{pkgname}
-Requires: firewalld-filesystem
-Provides: openvswitch%{pkgver}-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release}
-Obsoletes: openvswitch%{pkgver}-ovn-host < 2.11.0-1
-
-%description host
-OVN controller running on each host.
-
-%package vtep
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: %{pkgname}
-Provides: openvswitch%{pkgver}-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release}
-Obsoletes: openvswitch%{pkgver}-ovn-vtep < 2.11.0-1
-
-%description vtep
-OVN vtep controller
-
-%prep
-%if 0%{?commit0:1}
-%autosetup -n ovn-%{commit0} -a 10 -p 1
-%else
-%autosetup -n ovn-%{version} -a 10 -p 1
-%endif
-
-%build
-%if 0%{?commit0:1}
-# fix the snapshot unreleased version to be the released one.
-sed -i.old -e "s/^AC_INIT(openvswitch,.*,/AC_INIT(openvswitch, %{version},/" configure.ac
-%endif
-./boot.sh
-
-# OVN source code is now separate.
-# Build openvswitch first.
-# XXX Current openvswitch2.13 doesn't
-# use "2.13.0" for version. It's a commit hash
-pushd %{ovsdir}
-./boot.sh
-%configure \
-%if %{with libcapng}
-        --enable-libcapng \
-%else
-        --disable-libcapng \
-%endif
-        --enable-ssl \
-        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
-
-make %{?_smp_mflags}
-popd
-
-# Build OVN.
-# XXX OVS version needs to be updated when ovs2.13 is updated.
-%configure \
-        --with-ovs-source=$PWD/%{ovsdir} \
-%if %{with libcapng}
-        --enable-libcapng \
-%else
-        --disable-libcapng \
-%endif
-        --enable-ssl \
-        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
-
-make %{?_smp_mflags}
-
-%install
-%make_install
-install -p -D -m 0644 \
-        rhel/usr_share_ovn_scripts_systemd_sysconfig.template \
-        $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ovn
-
-for service in ovn-controller ovn-controller-vtep ovn-northd; do
-        install -p -D -m 0644 \
-                        rhel/usr_lib_systemd_system_${service}.service \
-                        $RPM_BUILD_ROOT%{_unitdir}/${service}.service
-done
-
-install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/ovn
-
-install -d $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/
-install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
-        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
-install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
-        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
-
-install -d -m 0755 $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn
-ln -s %{_datadir}/ovn/scripts/ovndb-servers.ocf \
-      $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
-
-install -p -D -m 0644 rhel/etc_logrotate.d_ovn \
-        $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/ovn
-
-# remove unneeded files.
-rm -f $RPM_BUILD_ROOT%{_bindir}/ovs*
-rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl
-rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep*
-rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/python
-rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovs*
-rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/bugtool-plugins
-rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
-rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
-rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/*
-rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash
-rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash
-rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch
-rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovn-bugtool*
-rm -f $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-overlay-driver \
-        $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-underlay-driver
-
-%check
-%if %{with check}
-    touch resolv.conf
-    export OVS_RESOLV_CONF=$(pwd)/resolv.conf
-    if ! make check TESTSUITEFLAGS='%{_smp_mflags}'; then
-        cat tests/testsuite.log
-        if ! make check TESTSUITEFLAGS='--recheck'; then
-            cat tests/testsuite.log
-            # Presently a test case - "2796: ovn -- ovn-controller incremental processing"
-            # is failing on aarch64 arch. Let's not exit for this arch
-            # until we figure out why it is failing.
-            # Test case 93: ovn.at:12105       ovn -- ACLs on Port Groups is failing
-            # repeatedly on s390x. This needs to be investigated.
-            %ifnarch aarch64
-            %ifnarch ppc64le
-            %ifnarch s390x
-                exit 1
-            %endif
-            %endif
-            %endif
-        fi
-    fi
-%endif
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%pre central
-if [ $1 -eq 1 ] ; then
-    # Package install.
-    /bin/systemctl status ovn-northd.service >/dev/null
-    ovn_status=$?
-    rpm -ql openvswitch-ovn-central > /dev/null
-    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
-        # ovn-northd service is running which means old openvswitch-ovn-central
-        # is already installed and it will be cleaned up. So start ovn-northd
-        # service when posttrans central is called.
-        touch %{_localstatedir}/lib/rpm-state/ovn-northd
-    fi
-fi
-
-%pre host
-if [ $1 -eq 1 ] ; then
-    # Package install.
-    /bin/systemctl status ovn-controller.service >/dev/null
-    ovn_status=$?
-    rpm -ql openvswitch-ovn-host > /dev/null
-    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
-        # ovn-controller service is running which means old
-        # openvswitch-ovn-host is installed and it will be cleaned up. So
-        # start ovn-controller service when posttrans host is called.
-        touch %{_localstatedir}/lib/rpm-state/ovn-controller
-    fi
-fi
-
-%pre vtep
-if [ $1 -eq 1 ] ; then
-    # Package install.
-    /bin/systemctl status ovn-controller-vtep.service >/dev/null
-    ovn_status=$?
-    rpm -ql openvswitch-ovn-vtep > /dev/null
-    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
-        # ovn-controller-vtep service is running which means old
-        # openvswitch-ovn-vtep is installed and it will be cleaned up. So
-        # start ovn-controller-vtep service when posttrans host is called.
-        touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
-    fi
-fi
-
-%preun central
-%if 0%{?systemd_preun:1}
-    %systemd_preun ovn-northd.service
-%else
-    if [ $1 -eq 0 ] ; then
-        # Package removal, not upgrade
-        /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || :
-        /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%preun host
-%if 0%{?systemd_preun:1}
-    %systemd_preun ovn-controller.service
-%else
-    if [ $1 -eq 0 ] ; then
-        # Package removal, not upgrade
-        /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || :
-        /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%preun vtep
-%if 0%{?systemd_preun:1}
-    %systemd_preun ovn-controller-vtep.service
-%else
-    if [ $1 -eq 0 ] ; then
-        # Package removal, not upgrade
-        /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || :
-        /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%post
-%if %{with libcapng}
-if [ $1 -eq 1 ]; then
-    sed -i 's:^#OVN_USER_ID=:OVN_USER_ID=:' %{_sysconfdir}/sysconfig/ovn
-    sed -i 's:\(.*su\).*:\1 openvswitch openvswitch:' %{_sysconfdir}/logrotate.d/ovn
-fi
-%endif
-
-%post central
-%if 0%{?systemd_post:1}
-    %systemd_post ovn-northd.service
-%else
-    # Package install, not upgrade
-    if [ $1 -eq 1 ]; then
-        /bin/systemctl daemon-reload >dev/null || :
-    fi
-%endif
-
-%post host
-%if 0%{?systemd_post:1}
-    %systemd_post ovn-controller.service
-%else
-    # Package install, not upgrade
-    if [ $1 -eq 1 ]; then
-        /bin/systemctl daemon-reload >dev/null || :
-    fi
-%endif
-
-%post vtep
-%if 0%{?systemd_post:1}
-    %systemd_post ovn-controller-vtep.service
-%else
-    # Package install, not upgrade
-    if [ $1 -eq 1 ]; then
-        /bin/systemctl daemon-reload >dev/null || :
-    fi
-%endif
-
-%postun
-
-%postun central
-%if 0%{?systemd_postun_with_restart:1}
-    %systemd_postun_with_restart ovn-northd.service
-%else
-    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
-    if [ "$1" -ge "1" ] ; then
-    # Package upgrade, not uninstall
-        /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%postun host
-%if 0%{?systemd_postun_with_restart:1}
-    %systemd_postun_with_restart ovn-controller.service
-%else
-    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
-    if [ "$1" -ge "1" ] ; then
-        # Package upgrade, not uninstall
-        /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%postun vtep
-%if 0%{?systemd_postun_with_restart:1}
-    %systemd_postun_with_restart ovn-controller-vtep.service
-%else
-    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
-    if [ "$1" -ge "1" ] ; then
-        # Package upgrade, not uninstall
-        /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || :
-    fi
-%endif
-
-%posttrans central
-if [ $1 -eq 1 ]; then
-    # Package install, not upgrade
-    if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then
-        rm %{_localstatedir}/lib/rpm-state/ovn-northd
-        /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || :
-    fi
-fi
-
-
-%posttrans host
-if [ $1 -eq 1 ]; then
-    # Package install, not upgrade
-    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then
-        rm %{_localstatedir}/lib/rpm-state/ovn-controller
-        /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || :
-    fi
-fi
-
-%posttrans vtep
-if [ $1 -eq 1 ]; then
-    # Package install, not upgrade
-    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then
-        rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
-        /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || :
-    fi
-fi
-
-%files
-%{_bindir}/ovn-nbctl
-%{_bindir}/ovn-sbctl
-%{_bindir}/ovn-trace
-%{_bindir}/ovn-detrace
-%{_bindir}/ovn_detrace.py
-%{_bindir}/ovn-appctl
-%{_bindir}/ovn-ic-nbctl
-%{_bindir}/ovn-ic-sbctl
-%dir %{_datadir}/ovn/
-%dir %{_datadir}/ovn/scripts/
-%{_datadir}/ovn/scripts/ovn-ctl
-%{_datadir}/ovn/scripts/ovn-lib
-%{_datadir}/ovn/scripts/ovndb-servers.ocf
-%{_mandir}/man8/ovn-ctl.8*
-%{_mandir}/man8/ovn-appctl.8*
-%{_mandir}/man8/ovn-nbctl.8*
-%{_mandir}/man8/ovn-ic-nbctl.8*
-%{_mandir}/man8/ovn-trace.8*
-%{_mandir}/man1/ovn-detrace.1*
-%{_mandir}/man7/ovn-architecture.7*
-%{_mandir}/man8/ovn-sbctl.8*
-%{_mandir}/man8/ovn-ic-sbctl.8*
-%{_mandir}/man5/ovn-nb.5*
-%{_mandir}/man5/ovn-ic-nb.5*
-%{_mandir}/man5/ovn-sb.5*
-%{_mandir}/man5/ovn-ic-sb.5*
-%dir %{ovnlibdir}/ocf/resource.d/ovn/
-%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
-%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/logrotate.d/ovn
-%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/sysconfig/ovn
-
-%files central
-%{_bindir}/ovn-northd
-%{_bindir}/ovn-ic
-%{_mandir}/man8/ovn-northd.8*
-%{_mandir}/man8/ovn-ic.8*
-%{_datadir}/ovn/ovn-nb.ovsschema
-%{_datadir}/ovn/ovn-ic-nb.ovsschema
-%{_datadir}/ovn/ovn-sb.ovsschema
-%{_datadir}/ovn/ovn-ic-sb.ovsschema
-%{_unitdir}/ovn-northd.service
-%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
-
-%files host
-%{_bindir}/ovn-controller
-%{_mandir}/man8/ovn-controller.8*
-%{_unitdir}/ovn-controller.service
-%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
-
-%files vtep
-%{_bindir}/ovn-controller-vtep
-%{_mandir}/man8/ovn-controller-vtep.8*
-%{_unitdir}/ovn-controller-vtep.service
-
-%changelog
-* Tue Apr 26 2022 Adrian Moreno <amorenoz@redhat.com> - 22.03.0-22
-- parallel-hmap: rewrite iterator using multivar helpers
-[Gerrit: 3da85a6660776548ddafaa9759b260b0dac38b4b]
-[Upstream: 3da85a6660776548ddafaa9759b260b0dac38b4b]
-
-* Tue Apr 26 2022 Adrian Moreno <amorenoz@redhat.com> - 22.03.0-21
-- treewide: bump ovs and fix problematic loops
-[Gerrit: 985ef4580ac6558859e104bad6fd5e2a68af79f7]
-[Upstream: 985ef4580ac6558859e104bad6fd5e2a68af79f7]
-
-* Tue Apr 26 2022 Mark Michelson <mmichels@redhat.com> - 22.03.0-20
-- Get OVS branch updated to proper base version for upcoming changes.
-[Gerrit: 367051dacab28dbe875aba9e1920b5693cf284c3]
-[Upstream: 367051dacab28dbe875aba9e1920b5693cf284c3]
-
-* Mon Apr 25 2022 Numan Siddique <numans@ovn.org> - 22.03.0-19
-- ovn-northd: Add flow to use eth.src if nd.tll is 0 in put_nd() action. (#2078026)
-[Gerrit: bd0ee4c1103ca5eaf834e48093205cacddaadf31]
-[Upstream: 80187a8031b6abe01fb23657a9bed2372ae23af5]
-
-* Thu Apr 21 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.03.0-18
-- Stop sending garps when binding not bound to chassis
-[Gerrit: 65088824dbca07dfdf3402d1775d3af76a304ef4]
-[Upstream: 65088824dbca07dfdf3402d1775d3af76a304ef4]
-
-* Thu Apr 21 2022 Ales Musil <amusil@redhat.com> - 22.03.0-17
-- pinctrl.c: Send GARP only on chassis atached to l3gw (#2062580)
-[Gerrit: 77ec310dda51c78c1897011ba851612787fcc6c6]
-[Upstream: 77ec310dda51c78c1897011ba851612787fcc6c6]
-
-* Sun Apr 17 2022 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 22.03.0-16
-- system-ovn: fix CoPP test failures (#2073060)
-[Gerrit: 388446ff27650773716ebcbc332424b1bf82bad1]
-[Upstream: e922c05dbb0c0ca70c98bd0b24833248ff5d458b]
-
-* Wed Apr 13 2022 Han Zhou <hzhou@ovn.org> - 22.03.0-15
-- ofctrl.c: Check installed flow when merging tracked flow changes. (#2071272)
-[Gerrit: 92cdada0e150e937e19f52c98a0c6a6dba7a84e0]
-[Upstream: 9c6d285ef2a65aef032cd60f9a9fe16cad7c4222]
-
-* Mon Apr 11 2022 Xavier Simonart <xsimonar@redhat.com> - 22.03.0-14
-- northd: avoid writing to IDL in parallel when using northd parallelization
-[Gerrit: 6410cb7906121bf2ebec3698d53bcfcb580ca4f6]
-[Upstream: 741a135fa1db9e10b725d4b69c6da455f732b89b]
-
-* Thu Apr 07 2022 Mohammad Heib <mheib@redhat.com> - 22.03.0-13
-- controller/pinctrl: avoid accessing invalid memory (#2052945)
-[Gerrit: 55a0ab5d2971daaf22e3d935f623bf7548a3df98]
-[Upstream: 6bc6002601ea161a287bcf53524e9282ab97c31b]
-
-* Thu Apr 07 2022 Xavier Simonart <xsimonar@redhat.com> - 22.03.0-12
-- northd: avoid snat on reply packets (#2061593)
-[Gerrit: be49d5145e10b1e4a525eed3b12d7ffb7818f2d1]
-[Upstream: 8b3e1afc30f3cf0ef9857fdc68f619b6fbed10dc]
-
-* Wed Apr 06 2022 Vladislav Odintsov <odivlad@gmail.com> - 22.03.0-11
-- vtep: correctly bring vtep lport up in SBDB
-[Gerrit: 655c3aa74d45791b0de5548c787ee24a147f2456]
-[Upstream: b35c98c923f4972198f741ff1d2b28671d6ff50d]
-
-* Wed Apr 06 2022 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 22.03.0-10
-- controller: properly remove qos policy meters
-[Gerrit: a814b865fbec460c91f9b5037d936b9d518c7a2f]
-[Upstream: 1e1d75c725a3445a853dd792b28ff02bb3ab1218]
-
-* Wed Mar 30 2022 Mark Michelson <mmichels@redhat.com> - 22.03.0-9
-- tests: Make "check CoPP config" more reliable.
-[Gerrit: 93e21802ac27ed29f2a4daf58a10912ebd581db5]
-[Upstream: 93e21802ac27ed29f2a4daf58a10912ebd581db5]
-
-* Tue Mar 29 2022 Dumitru Ceara <dceara@redhat.com> - 22.03.0-8
-- inc-proc-eng: Properly log recompute reason.
-[Gerrit: 3b07a5db4abd921a87b6fdfb851cf27e6975b079]
-[Upstream: 3b07a5db4abd921a87b6fdfb851cf27e6975b079]
-
-* Thu Mar 24 2022 Mark Michelson <mmichels@redhat.com> - 22.03.0-7
-- NEWS: Update the 22.03.0 release date.
-[Gerrit: e62886efda51adadd6c01c4e7ee0bbd97994216c]
-[Upstream: e62886efda51adadd6c01c4e7ee0bbd97994216c]
-
-* Thu Mar 24 2022 Vladislav Odintsov <odivlad@gmail.com> - 22.03.0-6
-- rhel: fix logrotate user config option
-[Gerrit: 135310cae98e84a637f13e99439566846debcb29]
-[Upstream: e8800ddd9e919e2ffc9d8c5c9ab27f0a5a6ec2e5]
-
-* Thu Mar 17 2022 Dumitru Ceara <dceara@redhat.com> - 22.03.0-5
-- northd: Properly warn for NAT on LR with multiple gw ports.
-[Gerrit: 622d15d078178e854027358d5130690edbe75a4c]
-[Upstream: b8194738c99ee09ad6d4762a3c999e04d58d1a0f]
-
-* Fri Mar 11 2022 Mark Michelson <mmichels@redhat.com> - 22.03.0-4
-- Prepare for 22.03.1.
-[Gerrit: b3d273f73c4aa8d246e652f1fb505779d3828c5b]
-[Upstream: b3d273f73c4aa8d246e652f1fb505779d3828c5b]
-
diff --git a/SPECS/ovn22.06.spec b/SPECS/ovn22.06.spec
new file mode 100644
index 0000000..23798b6
--- /dev/null
+++ b/SPECS/ovn22.06.spec
@@ -0,0 +1,591 @@
+# Copyright (C) 2009, 2010, 2013, 2014 Nicira Networks, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+#
+# If tests have to be skipped while building, specify the '--without check'
+# option. For example:
+# rpmbuild -bb --without check rhel/openvswitch-fedora.spec
+
+# This defines the base package name's version.
+
+%define pkgver 2.13
+%define pkgname ovn22.06
+
+# If libcap-ng isn't available and there is no need for running OVS
+# as regular user, specify the '--without libcapng'
+%bcond_without libcapng
+
+# Enable PIE, bz#955181
+%global _hardened_build 1
+
+# RHEL-7 doesn't define _rundir macro yet
+# Fedora 15 onwards uses /run as _rundir
+%if 0%{!?_rundir:1}
+%define _rundir /run
+%endif
+
+# Build python2 (that provides python) and python3 subpackages on Fedora
+# Build only python3 (that provides python) subpackage on RHEL8
+# Build only python subpackage on RHEL7
+%if 0%{?rhel} > 7 || 0%{?fedora}
+# On RHEL8 Sphinx is included in buildroot
+%global external_sphinx 1
+%else
+# Don't use external sphinx (RHV doesn't have optional repositories enabled)
+%global external_sphinx 0
+%endif
+
+# We would see rpmlinit error - E: hardcoded-library-path in '% {_prefix}/lib'.
+# But there is no solution to fix this. Using {_lib} macro will solve the
+# rpmlink error, but will install the files in /usr/lib64/.
+# OVN pacemaker ocf script file is copied in /usr/lib/ocf/resource.d/ovn/
+# and we are not sure if pacemaker looks into this path to find the
+# OVN resource agent script.
+%global ovnlibdir %{_prefix}/lib
+
+Name: %{pkgname}
+Summary: Open Virtual Network support
+Group: System Environment/Daemons
+URL: http://www.ovn.org/
+Version: 22.06.0
+Release: 16%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
+
+# Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
+# lib/sflow*.[ch] files are SISSL
+License: ASL 2.0 and LGPLv2+ and SISSL
+
+# Always pull an upstream release, since this is what we rebase to.
+Source: https://github.com/ovn-org/ovn/archive/v%{version}.tar.gz#/ovn-%{version}.tar.gz
+
+%define ovscommit 6f24c2bc769afde0a390ce344de1a7d9c592e5a6
+%define ovsshortcommit 6f24c2b
+
+Source10: https://github.com/openvswitch/ovs/archive/%{ovscommit}.tar.gz#/openvswitch-%{ovsshortcommit}.tar.gz
+%define ovsdir ovs-%{ovscommit}
+
+%define docutilsver 0.12
+%define pygmentsver 1.4
+%define sphinxver   1.1.3
+Source100: https://pypi.io/packages/source/d/docutils/docutils-%{docutilsver}.tar.gz
+Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.tar.gz
+Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz
+
+Source500: configlib.sh
+Source501: gen_config_group.sh
+Source502: set_config.sh
+
+# Important: source503 is used as the actual copy file
+# @TODO: this causes a warning - fix it?
+Source504: arm64-armv8a-linuxapp-gcc-config
+Source505: ppc_64-power8-linuxapp-gcc-config
+Source506: x86_64-native-linuxapp-gcc-config
+
+Patch:     %{pkgname}.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
+%if %{external_sphinx}
+BuildRequires: python3-sphinx
+%else
+# Sphinx dependencies
+BuildRequires: python-devel
+BuildRequires: python-setuptools
+#BuildRequires: python2-docutils
+BuildRequires: python-jinja2
+BuildRequires: python-nose
+#BuildRequires: python2-pygments
+# docutils dependencies
+BuildRequires: python-imaging
+# pygments dependencies
+BuildRequires: python-nose
+%endif
+
+BuildRequires: gcc gcc-c++ make
+BuildRequires: autoconf automake libtool
+BuildRequires: systemd-units openssl openssl-devel
+BuildRequires: python3-devel python3-setuptools
+BuildRequires: desktop-file-utils
+BuildRequires: groff-base graphviz
+BuildRequires: unbound-devel
+
+# make check dependencies
+BuildRequires: procps-ng
+%if 0%{?rhel} == 8 || 0%{?fedora}
+BuildRequires: python3-pyOpenSSL
+%endif
+BuildRequires: tcpdump
+
+%if %{with libcapng}
+BuildRequires: libcap-ng libcap-ng-devel
+%endif
+
+Requires: hostname openssl iproute module-init-tools
+
+Requires(post): systemd-units
+Requires(preun): systemd-units
+Requires(postun): systemd-units
+
+# to skip running checks, pass --without check
+%bcond_without check
+
+%description
+OVN, the Open Virtual Network, is a system to support virtual network
+abstraction.  OVN complements the existing capabilities of OVS to add
+native support for virtual network abstractions, such as virtual L2 and L3
+overlays and security groups.
+
+%package central
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-central < 2.11.0-1
+
+%description central
+OVN DB servers and ovn-northd running on a central node.
+
+%package host
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-host < 2.11.0-1
+
+%description host
+OVN controller running on each host.
+
+%package vtep
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Provides: openvswitch%{pkgver}-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-vtep < 2.11.0-1
+
+%description vtep
+OVN vtep controller
+
+%prep
+%autosetup -n ovn-%{version} -a 10 -p 1
+
+%build
+%if 0%{?commit0:1}
+# fix the snapshot unreleased version to be the released one.
+sed -i.old -e "s/^AC_INIT(openvswitch,.*,/AC_INIT(openvswitch, %{version},/" configure.ac
+%endif
+./boot.sh
+
+# OVN source code is now separate.
+# Build openvswitch first.
+# XXX Current openvswitch2.13 doesn't
+# use "2.13.0" for version. It's a commit hash
+pushd %{ovsdir}
+./boot.sh
+%configure \
+%if %{with libcapng}
+        --enable-libcapng \
+%else
+        --disable-libcapng \
+%endif
+        --enable-ssl \
+        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
+
+make %{?_smp_mflags}
+popd
+
+# Build OVN.
+# XXX OVS version needs to be updated when ovs2.13 is updated.
+%configure \
+        --with-ovs-source=$PWD/%{ovsdir} \
+%if %{with libcapng}
+        --enable-libcapng \
+%else
+        --disable-libcapng \
+%endif
+        --enable-ssl \
+        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
+
+make %{?_smp_mflags}
+
+%install
+%make_install
+install -p -D -m 0644 \
+        rhel/usr_share_ovn_scripts_systemd_sysconfig.template \
+        $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ovn
+
+for service in ovn-controller ovn-controller-vtep ovn-northd; do
+        install -p -D -m 0644 \
+                        rhel/usr_lib_systemd_system_${service}.service \
+                        $RPM_BUILD_ROOT%{_unitdir}/${service}.service
+done
+
+install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/ovn
+
+install -d $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
+        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
+        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
+
+install -d -m 0755 $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn
+ln -s %{_datadir}/ovn/scripts/ovndb-servers.ocf \
+      $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
+
+install -p -D -m 0644 rhel/etc_logrotate.d_ovn \
+        $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/ovn
+
+# remove unneeded files.
+rm -f $RPM_BUILD_ROOT%{_bindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl
+rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep*
+rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/python
+rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovs*
+rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/bugtool-plugins
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
+rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/*
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash
+rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch
+rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovn-bugtool*
+rm -f $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-overlay-driver \
+        $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-underlay-driver
+
+%check
+%if %{with check}
+    touch resolv.conf
+    export OVS_RESOLV_CONF=$(pwd)/resolv.conf
+    if ! make check TESTSUITEFLAGS='%{_smp_mflags}'; then
+        cat tests/testsuite.log
+        if ! make check TESTSUITEFLAGS='--recheck'; then
+            cat tests/testsuite.log
+            # Presently a test case - "2796: ovn -- ovn-controller incremental processing"
+            # is failing on aarch64 arch. Let's not exit for this arch
+            # until we figure out why it is failing.
+            # Test case 93: ovn.at:12105       ovn -- ACLs on Port Groups is failing
+            # repeatedly on s390x. This needs to be investigated.
+            %ifnarch aarch64
+            %ifnarch ppc64le
+            %ifnarch s390x
+                exit 1
+            %endif
+            %endif
+            %endif
+        fi
+    fi
+%endif
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre central
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-northd.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-central > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-northd service is running which means old openvswitch-ovn-central
+        # is already installed and it will be cleaned up. So start ovn-northd
+        # service when posttrans central is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-northd
+    fi
+fi
+
+%pre host
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-host > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller service is running which means old
+        # openvswitch-ovn-host is installed and it will be cleaned up. So
+        # start ovn-controller service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller
+    fi
+fi
+
+%pre vtep
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller-vtep.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-vtep > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller-vtep service is running which means old
+        # openvswitch-ovn-vtep is installed and it will be cleaned up. So
+        # start ovn-controller-vtep service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+    fi
+fi
+
+%preun central
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-northd.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun host
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun vtep
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller-vtep.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%post
+%if %{with libcapng}
+if [ $1 -eq 1 ]; then
+    sed -i 's:^#OVN_USER_ID=:OVN_USER_ID=:' %{_sysconfdir}/sysconfig/ovn
+    sed -i 's:\(.*su\).*:\1 openvswitch openvswitch:' %{_sysconfdir}/logrotate.d/ovn
+fi
+%endif
+
+%post central
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-northd.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post host
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post vtep
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller-vtep.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%postun
+
+%postun central
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-northd.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+    # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun host
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun vtep
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller-vtep.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%posttrans central
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-northd
+        /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || :
+    fi
+fi
+
+
+%posttrans host
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller
+        /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%posttrans vtep
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+        /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%files
+%{_bindir}/ovn-nbctl
+%{_bindir}/ovn-sbctl
+%{_bindir}/ovn-trace
+%{_bindir}/ovn-detrace
+%{_bindir}/ovn_detrace.py
+%{_bindir}/ovn-appctl
+%{_bindir}/ovn-ic-nbctl
+%{_bindir}/ovn-ic-sbctl
+%dir %{_datadir}/ovn/
+%dir %{_datadir}/ovn/scripts/
+%{_datadir}/ovn/scripts/ovn-ctl
+%{_datadir}/ovn/scripts/ovn-lib
+%{_datadir}/ovn/scripts/ovndb-servers.ocf
+%{_mandir}/man8/ovn-ctl.8*
+%{_mandir}/man8/ovn-appctl.8*
+%{_mandir}/man8/ovn-nbctl.8*
+%{_mandir}/man8/ovn-ic-nbctl.8*
+%{_mandir}/man8/ovn-trace.8*
+%{_mandir}/man1/ovn-detrace.1*
+%{_mandir}/man7/ovn-architecture.7*
+%{_mandir}/man8/ovn-sbctl.8*
+%{_mandir}/man8/ovn-ic-sbctl.8*
+%{_mandir}/man5/ovn-nb.5*
+%{_mandir}/man5/ovn-ic-nb.5*
+%{_mandir}/man5/ovn-sb.5*
+%{_mandir}/man5/ovn-ic-sb.5*
+%dir %{ovnlibdir}/ocf/resource.d/ovn/
+%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
+%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/logrotate.d/ovn
+%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/sysconfig/ovn
+
+%files central
+%{_bindir}/ovn-northd
+%{_bindir}/ovn-ic
+%{_mandir}/man8/ovn-northd.8*
+%{_mandir}/man8/ovn-ic.8*
+%{_datadir}/ovn/ovn-nb.ovsschema
+%{_datadir}/ovn/ovn-ic-nb.ovsschema
+%{_datadir}/ovn/ovn-sb.ovsschema
+%{_datadir}/ovn/ovn-ic-sb.ovsschema
+%{_unitdir}/ovn-northd.service
+%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
+
+%files host
+%{_bindir}/ovn-controller
+%{_mandir}/man8/ovn-controller.8*
+%{_unitdir}/ovn-controller.service
+%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
+
+%files vtep
+%{_bindir}/ovn-controller-vtep
+%{_mandir}/man8/ovn-controller-vtep.8*
+%{_unitdir}/ovn-controller-vtep.service
+
+%changelog
+* Fri Jul 01 2022 Mark Michelson <mmichels@redhat.com> - 22.06.0-16
+- ovs: Bump submodule to newer version (#2102618)
+[Gerrit: 5593044a8be0d2ce730e4890cf213ee760916889]
+[Upstream: 5593044a8be0d2ce730e4890cf213ee760916889]
+
+* Wed Jun 29 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-15
+- tests: add multi-chassis keyword to relevant test cases
+[Gerrit: 4bdade3593ae3c6604822716d22ab04b0af962da]
+[Upstream: 4bdade3593ae3c6604822716d22ab04b0af962da]
+
+* Wed Jun 29 2022 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 22.06.0-14
+- northd: add condition for stateless nat drop flow in S_ROUTER_IN_GW_REDIRECT pipeline (#2094980)
+[Gerrit: 9885154fc41005dbb8fbbdca9e3a38d892870f53]
+[Upstream: 9885154fc41005dbb8fbbdca9e3a38d892870f53]
+
+* Wed Jun 29 2022 Ales Musil <amusil@redhat.com> - 22.06.0-13
+- northd.c: Add flow to skip put_nd action if ip6.src or nd.sll is 0
+[Gerrit: c5596102cb84e22086d7807b31cbe5c9e893ac38]
+[Upstream: 0a4bc2075f7df1bb38338cde1973c931aa5e1942]
+
+* Wed Jun 29 2022 Terry Wilson <twilson@redhat.com> - 22.06.0-12
+- Allow arbitrary args to be passed to called binary
+[Gerrit: 849106f6ee75d7b1ff990763325d41d45c843a64]
+[Upstream: 849106f6ee75d7b1ff990763325d41d45c843a64]
+
+* Mon Jun 27 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-11
+- tests: ovn-nbctl dump-flows -> ovn-sbctl dump-flows
+[Gerrit: 656a91b6b5cd9d9e48b8e938fcffb81ead2d9fda]
+[Upstream: 656a91b6b5cd9d9e48b8e938fcffb81ead2d9fda]
+
+* Mon Jun 27 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-10
+- Fix memleak in ovn-nbctl when args can't be parsed
+[Gerrit: 70b360325c13f399835b1f5c7ac0c0acd4832773]
+[Upstream: 70b360325c13f399835b1f5c7ac0c0acd4832773]
+
+* Mon Jun 20 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-9
+- Implement RARP activation strategy for ports
+[Gerrit: c75ddaf804cc8ea8499706430b438ae53f2f1224]
+[Upstream: ee20c48c2f5ce9d512adfcbea3ee300f8bb09625]
+
+* Tue Jun 14 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-8
+- Fix pidfile_is_running when $cmd is not passed
+[Gerrit: 44e047d6f1be5ae4f564e82a928cf1f08f1908ef]
+[Upstream: 6e57adfe5a35fc36ef290c6e9ae7e616f73cd3d2]
+
+* Thu Jun 09 2022 Ihar Hrachyshka <ihrachys@redhat.com> - 22.06.0-7
+- Lock pinctrl_mutex for pinctrl_wait
+[Gerrit: 6e36ad7a52ec4e6443ccb3185bda6421cdf6747e]
+[Upstream: 6e36ad7a52ec4e6443ccb3185bda6421cdf6747e]
+
+* Thu Jun 09 2022 Terry Wilson <twilson@redhat.com> - 22.06.0-6
+- Ensure pid belongs to ovsdb-server in ovn-ctl
+[Gerrit: d0847f56913245c19a33e087f730c084ca674c83]
+[Upstream: d0847f56913245c19a33e087f730c084ca674c83]
+
+* Thu Jun 09 2022 Terry Wilson <twilson@redhat.com> - 22.06.0-5
+- Handle re-used pids in pidfile_is_running
+[Gerrit: 98e7d4b3675b237c770763377382075ac08c64ba]
+[Upstream: 98e7d4b3675b237c770763377382075ac08c64ba]
+
+* Fri Jun 03 2022 Mark Michelson <mmichels@redhat.com> - 22.06.0-4
+- Prepare for 22.06.1.
+[Gerrit: 437dc3a01266d7f128bba532c674c5827af1bd6f]
+[Upstream: 437dc3a01266d7f128bba532c674c5827af1bd6f]
+