diff --git a/.ovn.metadata b/.ovn.metadata
index df4ddae..2f44139 100644
--- a/.ovn.metadata
+++ b/.ovn.metadata
@@ -1,5 +1,5 @@
002450621b33c5690060345b0aac25bc2426d675 SOURCES/docutils-0.12.tar.gz
-18ee4650f5907758dbd949b7b82bfb029666d8f8 SOURCES/openvswitch-6f24c2b.tar.gz
-8fc7b574476db8f38307923fd6e476df9b65b009 SOURCES/ovn-22.06.0.tar.gz
+cae717fbee361a235064a1d79b012b2590908f7c SOURCES/ovn-22.09.0.tar.gz
+d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz
d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz
6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a SOURCES/Sphinx-1.1.3.tar.gz
diff --git a/SOURCES/ovn22.06.patch b/SOURCES/ovn22.06.patch
deleted file mode 100644
index e08a142..0000000
--- a/SOURCES/ovn22.06.patch
+++ /dev/null
@@ -1,2344 +0,0 @@
-diff --git a/AUTHORS.rst b/AUTHORS.rst
-index d3747f8d1..5d60088e4 100644
---- a/AUTHORS.rst
-+++ b/AUTHORS.rst
-@@ -395,6 +395,7 @@ Vishal Deep Ajmera vishal.deep.ajmera@ericsson.com
- Vivien Bernet-Rollande vbr@soprive.net
- Vladislav Odintsov odivlad@gmail.com
- wangqianyu wang.qianyu@zte.com.cn
-+wangchuanlei wangchuanlei@inspur.com
- Wang Sheng-Hui shhuiw@gmail.com
- Wang Zhike wangzhike@jd.com
- Wei Li liw@dtdream.com
-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/binding.c b/controller/binding.c
-index 2279570f9..9025681db 100644
---- a/controller/binding.c
-+++ b/controller/binding.c
-@@ -386,6 +386,23 @@ update_ld_external_ports(const struct sbrec_port_binding *binding_rec,
- }
- }
-
-+static void
-+update_ld_multichassis_ports(const struct sbrec_port_binding *binding_rec,
-+ struct hmap *local_datapaths)
-+{
-+ struct local_datapath *ld = get_local_datapath(
-+ local_datapaths, binding_rec->datapath->tunnel_key);
-+ if (!ld) {
-+ return;
-+ }
-+ if (binding_rec->additional_chassis) {
-+ add_local_datapath_multichassis_port(ld, binding_rec->logical_port,
-+ binding_rec);
-+ } else {
-+ remove_local_datapath_multichassis_port(ld, binding_rec->logical_port);
-+ }
-+}
-+
- static void
- update_ld_localnet_port(const struct sbrec_port_binding *binding_rec,
- struct shash *bridge_mappings,
-@@ -1752,6 +1769,8 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
-
- struct ovs_list localnet_lports = OVS_LIST_INITIALIZER(&localnet_lports);
- struct ovs_list external_lports = OVS_LIST_INITIALIZER(&external_lports);
-+ struct ovs_list multichassis_ports = OVS_LIST_INITIALIZER(
-+ &multichassis_ports);
-
- struct lport {
- struct ovs_list list_node;
-@@ -1787,6 +1806,13 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
-
- case LP_VIF:
- consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL, qos_map_ptr);
-+ if (pb->additional_chassis) {
-+ struct lport *multichassis_lport = xmalloc(
-+ sizeof *multichassis_lport);
-+ multichassis_lport->pb = pb;
-+ ovs_list_push_back(&multichassis_ports,
-+ &multichassis_lport->list_node);
-+ }
- break;
-
- case LP_CONTAINER:
-@@ -1862,6 +1888,16 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
- free(ext_lport);
- }
-
-+ /* Run through multichassis lport list to see if these are ports
-+ * on local datapaths discovered from above loop, and update the
-+ * corresponding local datapath accordingly. */
-+ struct lport *multichassis_lport;
-+ LIST_FOR_EACH_POP (multichassis_lport, list_node, &multichassis_ports) {
-+ update_ld_multichassis_ports(multichassis_lport->pb,
-+ b_ctx_out->local_datapaths);
-+ free(multichassis_lport);
-+ }
-+
- shash_destroy(&bridge_mappings);
-
- if (!sset_is_empty(b_ctx_out->egress_ifaces)
-@@ -1934,6 +1970,7 @@ remove_pb_from_local_datapath(const struct sbrec_port_binding *pb,
- } else if (!strcmp(pb->type, "external")) {
- remove_local_datapath_external_port(ld, pb->logical_port);
- }
-+ remove_local_datapath_multichassis_port(ld, pb->logical_port);
- }
-
- static void
-@@ -2677,6 +2714,7 @@ delete_done:
- case LP_VIF:
- case LP_CONTAINER:
- case LP_VIRTUAL:
-+ update_ld_multichassis_ports(pb, b_ctx_out->local_datapaths);
- handled = handle_updated_vif_lport(pb, lport_type, b_ctx_in,
- b_ctx_out, qos_map_ptr);
- break;
-diff --git a/controller/lflow.c b/controller/lflow.c
-index 934b23698..6055097b5 100644
---- a/controller/lflow.c
-+++ b/controller/lflow.c
-@@ -1775,6 +1775,7 @@ add_lb_vip_hairpin_reply_action(struct in6_addr *vip6, ovs_be32 vip,
- uint64_t cookie, struct ofpbuf *ofpacts)
- {
- struct match match = MATCH_CATCHALL_INITIALIZER;
-+ size_t ol_offset = ofpacts->size;
- struct ofpact_learn *ol = ofpact_put_LEARN(ofpacts);
- struct ofpact_learn_spec *ol_spec;
- unsigned int imm_bytes;
-@@ -1928,6 +1929,8 @@ add_lb_vip_hairpin_reply_action(struct in6_addr *vip6, ovs_be32 vip,
- src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(imm_bytes));
- memcpy(src_imm, &imm_reg_value, imm_bytes);
-
-+ /* Reload ol pointer since ofpacts buffer can be reallocated. */
-+ ol = ofpbuf_at_assert(ofpacts, ol_offset, sizeof *ol);
- ofpact_finish_LEARN(ofpacts, &ol);
- }
-
-diff --git a/controller/local_data.c b/controller/local_data.c
-index 98445902b..7f874fc19 100644
---- a/controller/local_data.c
-+++ b/controller/local_data.c
-@@ -72,6 +72,7 @@ local_datapath_alloc(const struct sbrec_datapath_binding *dp)
- ld->is_switch = datapath_is_switch(dp);
- ld->is_transit_switch = datapath_is_transit_switch(dp);
- shash_init(&ld->external_ports);
-+ shash_init(&ld->multichassis_ports);
- /* memory accounting - common part. */
- local_datapath_usage += sizeof *ld;
-
-@@ -97,13 +98,20 @@ local_datapath_destroy(struct local_datapath *ld)
- SHASH_FOR_EACH (node, &ld->external_ports) {
- local_datapath_usage -= strlen(node->name);
- }
-- local_datapath_usage -= shash_count(&ld->external_ports) * sizeof *node;
-+ SHASH_FOR_EACH (node, &ld->multichassis_ports) {
-+ local_datapath_usage -= strlen(node->name);
-+ }
-+ local_datapath_usage -= (shash_count(&ld->external_ports)
-+ * sizeof *node);
-+ local_datapath_usage -= (shash_count(&ld->multichassis_ports)
-+ * sizeof *node);
- local_datapath_usage -= sizeof *ld;
- local_datapath_usage -=
- ld->n_allocated_peer_ports * sizeof *ld->peer_ports;
-
- free(ld->peer_ports);
- shash_destroy(&ld->external_ports);
-+ shash_destroy(&ld->multichassis_ports);
- free(ld);
- }
-
-@@ -274,6 +282,26 @@ remove_local_datapath_external_port(struct local_datapath *ld,
- }
- }
-
-+void
-+add_local_datapath_multichassis_port(struct local_datapath *ld,
-+ char *logical_port, const void *data)
-+{
-+ if (!shash_replace(&ld->multichassis_ports, logical_port, data)) {
-+ local_datapath_usage += sizeof(struct shash_node) +
-+ strlen(logical_port);
-+ }
-+}
-+
-+void
-+remove_local_datapath_multichassis_port(struct local_datapath *ld,
-+ char *logical_port)
-+{
-+ if (shash_find_and_delete(&ld->multichassis_ports, logical_port)) {
-+ local_datapath_usage -= sizeof(struct shash_node) +
-+ strlen(logical_port);
-+ }
-+}
-+
- void
- local_datapath_memory_usage(struct simap *usage)
- {
-diff --git a/controller/local_data.h b/controller/local_data.h
-index 9306ddf15..d898c8aa5 100644
---- a/controller/local_data.h
-+++ b/controller/local_data.h
-@@ -58,6 +58,7 @@ struct local_datapath {
- size_t n_allocated_peer_ports;
-
- struct shash external_ports;
-+ struct shash multichassis_ports;
- };
-
- struct local_datapath *local_datapath_alloc(
-@@ -155,5 +156,10 @@ void add_local_datapath_external_port(struct local_datapath *ld,
- char *logical_port, const void *data);
- void remove_local_datapath_external_port(struct local_datapath *ld,
- char *logical_port);
-+void add_local_datapath_multichassis_port(struct local_datapath *ld,
-+ char *logical_port,
-+ const void *data);
-+void remove_local_datapath_multichassis_port(struct local_datapath *ld,
-+ char *logical_port);
-
- #endif /* controller/local_data.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..2e9138036 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;
-@@ -2953,6 +3000,11 @@ static void init_physical_ctx(struct engine_node *node,
- engine_get_input("SB_port_binding", node),
- "name");
-
-+ struct ovsdb_idl_index *sbrec_port_binding_by_datapath =
-+ engine_ovsdb_node_get_index(
-+ engine_get_input("SB_port_binding", node),
-+ "datapath");
-+
- struct sbrec_multicast_group_table *multicast_group_table =
- (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
- engine_get_input("SB_multicast_group", node));
-@@ -2992,6 +3044,7 @@ static void init_physical_ctx(struct engine_node *node,
- struct simap *ct_zones = &ct_zones_data->current;
-
- p_ctx->sbrec_port_binding_by_name = sbrec_port_binding_by_name;
-+ p_ctx->sbrec_port_binding_by_datapath = sbrec_port_binding_by_datapath;
- p_ctx->port_binding_table = port_binding_table;
- p_ctx->mc_group_table = multicast_group_table;
- p_ctx->br_int = br_int;
-@@ -3164,6 +3217,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 +3541,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 +3589,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..816a557e7 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,155 @@ 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
-+enforce_tunneling_for_multichassis_ports(
-+ struct local_datapath *ld,
-+ const struct sbrec_port_binding *binding,
-+ const struct sbrec_chassis *chassis,
-+ const struct hmap *chassis_tunnels,
-+ enum mf_field_id mff_ovn_geneve,
-+ struct ovn_desired_flow_table *flow_table)
-+{
-+ if (shash_is_empty(&ld->multichassis_ports)) {
-+ return;
-+ }
-+
-+ struct ovs_list *tuns = get_remote_tunnels(binding, chassis,
-+ chassis_tunnels);
-+ if (ovs_list_is_empty(tuns)) {
-+ free(tuns);
-+ return;
-+ }
-+
-+ uint32_t dp_key = binding->datapath->tunnel_key;
-+ uint32_t port_key = binding->tunnel_key;
-+
-+ struct shash_node *node;
-+ SHASH_FOR_EACH (node, &ld->multichassis_ports) {
-+ const struct sbrec_port_binding *mcp = node->data;
-+
-+ struct ofpbuf ofpacts;
-+ ofpbuf_init(&ofpacts, 0);
-+
-+ bool is_vtep_port = !strcmp(binding->type, "vtep");
-+ /* rewrite MFF_IN_PORT to bypass OpenFlow loopback check for ARP/ND
-+ * responder in L3 networks. */
-+ if (is_vtep_port) {
-+ put_load(ofp_to_u16(OFPP_NONE), MFF_IN_PORT, 0, 16, &ofpacts);
-+ }
-+
-+ struct match match;
-+ match_outport_dp_and_port_keys(&match, dp_key, port_key);
-+ match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, mcp->tunnel_key);
-+
-+ struct tunnel *tun;
-+ LIST_FOR_EACH (tun, list_node, tuns) {
-+ put_encapsulation(mff_ovn_geneve, tun->tun,
-+ binding->datapath, port_key, is_vtep_port,
-+ &ofpacts);
-+ ofpact_put_OUTPUT(&ofpacts)->port = tun->tun->ofport;
-+ }
-+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 110,
-+ binding->header_.uuid.parts[0], &match, &ofpacts,
-+ &binding->header_.uuid);
-+ ofpbuf_uninit(&ofpacts);
-+ }
-+
-+ struct tunnel *tun_elem;
-+ LIST_FOR_EACH_POP (tun_elem, list_node, tuns) {
-+ free(tun_elem);
-+ }
-+ free(tuns);
-+}
-+
- static void
- consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- enum mf_field_id mff_ovn_geneve,
-@@ -1239,6 +1390,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;
-@@ -1415,6 +1570,9 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- binding->header_.uuid.parts[0],
- &match, ofpacts_p, &binding->header_.uuid);
-
-+ enforce_tunneling_for_multichassis_ports(
-+ ld, binding, chassis, chassis_tunnels, mff_ovn_geneve, flow_table);
-+
- /* No more tunneling to set up. */
- goto out;
- }
-@@ -1733,20 +1891,49 @@ physical_handle_flows_for_lport(const struct sbrec_port_binding *pb,
-
- ofctrl_remove_flows(flow_table, &pb->header_.uuid);
-
-+ struct local_datapath *ldp =
-+ get_local_datapath(p_ctx->local_datapaths,
-+ pb->datapath->tunnel_key);
- if (!strcmp(pb->type, "external")) {
- /* External lports have a dependency on the localnet port.
- * We need to remove the flows of the localnet port as well
- * and re-consider adding the flows for it.
- */
-- struct local_datapath *ldp =
-- get_local_datapath(p_ctx->local_datapaths,
-- pb->datapath->tunnel_key);
- if (ldp && ldp->localnet_port) {
- ofctrl_remove_flows(flow_table, &ldp->localnet_port->header_.uuid);
- physical_eval_port_binding(p_ctx, ldp->localnet_port, flow_table);
- }
- }
-
-+ if (ldp) {
-+ bool multichassis_state_changed = (
-+ !!pb->additional_chassis ==
-+ !!shash_find(&ldp->multichassis_ports, pb->logical_port)
-+ );
-+ if (multichassis_state_changed) {
-+ if (pb->additional_chassis) {
-+ add_local_datapath_multichassis_port(
-+ ldp, pb->logical_port, pb);
-+ } else {
-+ remove_local_datapath_multichassis_port(
-+ ldp, pb->logical_port);
-+ }
-+
-+ struct sbrec_port_binding *target =
-+ sbrec_port_binding_index_init_row(
-+ p_ctx->sbrec_port_binding_by_datapath);
-+ sbrec_port_binding_index_set_datapath(target, ldp->datapath);
-+
-+ const struct sbrec_port_binding *port;
-+ SBREC_PORT_BINDING_FOR_EACH_EQUAL (
-+ port, target, p_ctx->sbrec_port_binding_by_datapath) {
-+ ofctrl_remove_flows(flow_table, &port->header_.uuid);
-+ physical_eval_port_binding(p_ctx, port, flow_table);
-+ }
-+ sbrec_port_binding_index_destroy_row(target);
-+ }
-+ }
-+
- if (!removed) {
- physical_eval_port_binding(p_ctx, pb, flow_table);
- if (!strcmp(pb->type, "patch")) {
-diff --git a/controller/physical.h b/controller/physical.h
-index ee4b1ae1f..1b8f1ea55 100644
---- a/controller/physical.h
-+++ b/controller/physical.h
-@@ -45,6 +45,7 @@ struct local_nonvif_data;
-
- struct physical_ctx {
- struct ovsdb_idl_index *sbrec_port_binding_by_name;
-+ struct ovsdb_idl_index *sbrec_port_binding_by_datapath;
- const struct sbrec_port_binding_table *port_binding_table;
- const struct sbrec_multicast_group_table *mc_group_table;
- const struct ovsrec_bridge *br_int;
-diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index 428863293..2fcf91bc9 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));
-@@ -3434,11 +3454,11 @@ pinctrl_handler(void *arg_)
-
- ip_mcast_querier_run(swconn, &send_mcast_query_time);
- }
-- }
-
-- ovs_mutex_lock(&pinctrl_mutex);
-- svc_monitors_run(swconn, &svc_monitors_next_run_time);
-- ovs_mutex_unlock(&pinctrl_mutex);
-+ ovs_mutex_lock(&pinctrl_mutex);
-+ svc_monitors_run(swconn, &svc_monitors_next_run_time);
-+ ovs_mutex_unlock(&pinctrl_mutex);
-+ }
-
- rconn_run_wait(swconn);
- rconn_recv_wait(swconn);
-@@ -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 nd_ns &&
-+ (ip6.src == 0 || nd.sll == 0)
and applies the action
-+ next;
-+ arp
and
- applies the action
-@@ -3018,8 +3024,7 @@ icmp6 {
- ip && ip6.dst == B
- with an action ct_snat;
. If the NAT rule is of type
- dnat_and_snat and has stateless=true
in the
-- options, then the action would be ip4/6.dst=
-- (B)
.
-+ options, then the action would be next;
.
-
-@@ -3059,7 +3064,7 @@ icmp6 {
- action ct_snat_in_czone;
to unSNAT in the common
- zone. If the NAT rule is of type dnat_and_snat and has
- stateless=true
in the options, then the action
-- would be ip4/6.dst=(B)
.
-+ would be next;
.
-
-@@ -4217,6 +4222,26 @@ icmp6 { - external ip and D is NAT external mac. - - -+
dnat_and_snat
NAT rule with
-+ stateless=true
and allowed_ext_ips
-+ configured, a priority-75 flow is programmed with match
-+ ip4.dst == B
and action
-+ outport = CR; next;
where B
-+ is the NAT rule external IP and CR is the
-+ chassisredirect
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 drop;
.
-+ For each dnat_and_snat
NAT rule with
-+ stateless=true
and exempted_ext_ips
-+ configured, a priority-75 flow is programmed with match
-+ ip4.dst == B
and action
-+ drop;
where B is the NAT rule
-+ external IP.
-+ A similar flow is added for IPv6 traffic.
-+ ct_dnat_in_czone;
. If the NAT rule is of type
- dnat_and_snat and has stateless=true
in the
-- options, then the action would be ip4/6.src=
-- (B)
.
-+ options, then the action would be next;
.
-
-
-
-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..47fd5a544 100644
---- a/ovn-nb.xml
-+++ b/ovn-nb.xml
-@@ -610,6 +610,7 @@
- Determines whether unregistered multicast traffic should be flooded
- or not. Only applicable if
- is enabled.
-+ Default: false
.
-
-
ovn-controller
- only if this same key and value is configured in the
-@@ -1129,12 +1141,13 @@
- type='{"type": "boolean"}'>
- If set to true
, multicast packets (except reports) are
- unconditionally forwarded to the specific port.
-+ Default: false
.
- true
, multicast reports are unconditionally
-- forwarded to the specific port.
-+ forwarded to the specific port. Default: false
.
- true
.
-
-+
-+
-+ Default: false
.
-+
ovn-controller
- only if this same key and value is configured in the
-diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
-index 05bd3e9a6..89f223562 100644
---- a/tests/ovn-ic.at
-+++ b/tests/ovn-ic.at
-@@ -492,6 +492,56 @@ OVN_CLEANUP_IC([az1], [az2])
- AT_CLEANUP
- ])
-
-+OVN_FOR_EACH_NORTHD([
-+AT_SETUP([ovn-ic -- route sync -- IPv6 route tables])
-+AT_KEYWORDS([IPv6-route-sync])
-+
-+ovn_init_ic_db
-+ovn-ic-nbctl ts-add ts1
-+
-+for i in 1 2; do
-+ ovn_start az$i
-+ ovn_as az$i
-+
-+ # Enable route learning at AZ level
-+ ovn-nbctl set nb_global . options:ic-route-learn=true
-+ # Enable route advertising at AZ level
-+ ovn-nbctl set nb_global . options:ic-route-adv=true
-+
-+ # Create LRP and connect to TS
-+ ovn-nbctl lr-add lr$i
-+ ovn-nbctl lrp-add lr$i lrp-lr$i-ts1 aa:aa:aa:aa:aa:0$i 2001:db8:1::$i/64
-+ ovn-nbctl lsp-add ts1 lsp-ts1-lr$i \
-+ -- lsp-set-addresses lsp-ts1-lr$i router \
-+ -- lsp-set-type lsp-ts1-lr$i router \
-+ -- lsp-set-options lsp-ts1-lr$i router-port=lrp-lr$i-ts1
-+
-+ ovn-nbctl lrp-add lr$i lrp-lr$i-p$i 00:00:00:00:00:0$i 2002:db8:1::$i/64
-+done
-+
-+for i in 1 2; do
-+ OVS_WAIT_UNTIL([ovn_as az$i ovn-nbctl lr-route-list lr$i | grep learned])
-+done
-+
-+AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1 | awk '/learned/{print $1, $2}'], [0], [dnl
-+2002:db8:1::/64 2001:db8:1::2
-+])
-+
-+# Do not learn routes from link-local nexthops
-+for i in 1 2; do
-+ ovn_as az$i
-+ ovn-nbctl lrp-del lrp-lr$i-ts1
-+ ovn-nbctl lrp-add lr$i lrp-lr$i-ts1 aa:aa:aa:aa:aa:0$i 169.254.100.$i/24
-+done
-+
-+OVS_WAIT_WHILE([ovn_as az1 ovn-nbctl lr-route-list lr1 | grep learned])
-+AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1 | grep -q learned], [1])
-+
-+OVN_CLEANUP_IC([az1], [az2])
-+
-+AT_CLEANUP
-+])
-+
- OVN_FOR_EACH_NORTHD([
- AT_SETUP([ovn-ic -- route sync -- route tables])
-
-diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index a071b3689..773904f95 100644
---- a/tests/ovn-northd.at
-+++ b/tests/ovn-northd.at
-@@ -1213,6 +1213,13 @@ ovn-nbctl ls-add sw1
- ovn-nbctl --wait=sb lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 \
- "02:00:00:00:00:03 20.0.0.3"
-
-+# service_monitors state online requires corresponding port_binding to be "up"
-+ovn-sbctl chassis-add hv1 geneve 127.0.0.1
-+ovn-sbctl lsp-bind sw0-p1 hv1
-+ovn-sbctl lsp-bind sw1-p1 hv1
-+wait_row_count nb:Logical_Switch_Port 1 name=sw0-p1 'up=true'
-+wait_row_count nb:Logical_Switch_Port 1 name=sw1-p1 'up=true'
-+
- wait_row_count Service_Monitor 0
-
- ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
-@@ -1226,7 +1233,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
- AT_CAPTURE_FILE([sbflows])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | sed 's/table=..//'], 0, [dnl
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
-
- AS_BOX([Delete the Load_Balancer_Health_Check])
-@@ -1236,7 +1243,7 @@ wait_row_count Service_Monitor 0
- AT_CAPTURE_FILE([sbflows2])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
-
- AS_BOX([Create the Load_Balancer_Health_Check again.])
-@@ -1248,7 +1255,7 @@ check ovn-nbctl --wait=sb sync
-
- ovn-sbctl dump-flows sw0 | grep backends | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
-
- AS_BOX([Get the uuid of both the service_monitor])
-@@ -1258,7 +1265,7 @@ sm_sw1_p1=$(fetch_column Service_Monitor _uuid logical_port=sw1-p1)
- AT_CAPTURE_FILE([sbflows3])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
-
- AS_BOX([Set the service monitor for sw1-p1 to offline])
-@@ -1269,7 +1276,7 @@ check ovn-nbctl --wait=sb sync
- AT_CAPTURE_FILE([sbflows4])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);)
- ])
-
- AS_BOX([Set the service monitor for sw0-p1 to offline])
-@@ -1298,7 +1305,7 @@ check ovn-nbctl --wait=sb sync
- AT_CAPTURE_FILE([sbflows7])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep backends | grep priority=120 | sed 's/table=..//'], 0,
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
- ])
-
- AS_BOX([Set the service monitor for sw1-p1 to error])
-@@ -1309,7 +1316,7 @@ check ovn-nbctl --wait=sb sync
- ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
- | grep priority=120 > lflows.txt
- AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);)
- ])
-
- AS_BOX([Add one more vip to lb1])
-@@ -1335,8 +1342,8 @@ AT_CAPTURE_FILE([sbflows9])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
- 0,
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80);)
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb_mark(backends=10.0.0.3:1000);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000);)
- ])
-
- AS_BOX([Set the service monitor for sw1-p1 to online])
-@@ -1349,8 +1356,8 @@ AT_CAPTURE_FILE([sbflows10])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
- 0,
--[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
-+[ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
- ])
-
- AS_BOX([Associate lb1 to sw1])
-@@ -1359,8 +1366,8 @@ AT_CAPTURE_FILE([sbflows11])
- OVS_WAIT_FOR_OUTPUT(
- [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
- 0, [dnl
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
-- (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
-+ (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg0[[1]] = 0; reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
- ])
-
- AS_BOX([Now create lb2 same as lb1 but udp protocol.])
-@@ -6734,6 +6741,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..fd064b999 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
-@@ -14660,6 +14665,14 @@ reset_env() {
- for port in hv1/migrator hv2/migrator hv1/first hv2/second hv3/third; do
- : > $port.expected
- done
-+
-+ for hv in hv1 hv2 hv3; do
-+ : > $hv/n1.expected
-+ done
-+
-+ reset_pcap_file hv1 br-phys_n1 hv1/br-phys_n1
-+ reset_pcap_file hv2 br-phys_n1 hv2/br-phys_n1
-+ reset_pcap_file hv3 br-phys_n1 hv3/br-phys_n1
- }
-
- check_packets() {
-@@ -14670,6 +14683,10 @@ check_packets() {
- OVN_CHECK_PACKETS_CONTAIN([hv1/first-tx.pcap], [hv1/first.expected])
- OVN_CHECK_PACKETS_CONTAIN([hv2/second-tx.pcap], [hv2/second.expected])
- OVN_CHECK_PACKETS_CONTAIN([hv3/third-tx.pcap], [hv3/third.expected])
-+
-+ OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [hv1/n1.expected])
-+ OVN_CHECK_PACKETS_CONTAIN([hv2/br-phys_n1-tx.pcap], [hv2/n1.expected])
-+ OVN_CHECK_PACKETS_CONTAIN([hv3/br-phys_n1-tx.pcap], [hv3/n1.expected])
- }
-
- migrator_tpa=$(ip_to_hex 10 0 0 100)
-@@ -14694,10 +14711,10 @@ wait_column "" Port_Binding requested_additional_chassis logical_port=migrator
- wait_for_ports_up
-
- # advertise location of ports through localnet port
--send_garp hv1 migrator 0000000000ff ffffffffffff $migrator_spa $migrator_tpa
--send_garp hv1 first 000000000001 ffffffffffff $first_spa $first_tpa
--send_garp hv2 second 000000000002 ffffffffffff $second_spa $second_tpa
--send_garp hv3 third 000000000003 ffffffffffff $third_spa $third_tpa
-+send_garp hv1 migrator 0000000000ff ffffffffffff $migrator_tpa $migrator_tpa
-+send_garp hv1 first 000000000001 ffffffffffff $first_spa $first_spa
-+send_garp hv2 second 000000000002 ffffffffffff $second_spa $second_spa
-+send_garp hv3 third 000000000003 ffffffffffff $third_spa $third_spa
- reset_env
-
- # check that...
-@@ -14717,6 +14734,7 @@ echo $request >> hv3/third.expected
- # unicast from Second doesn't arrive to hv2:Migrator
- request=$(send_arp hv2 second 000000000002 0000000000ff $second_spa $migrator_tpa)
- echo $request >> hv1/migrator.expected
-+echo $request >> hv2/n1.expected
-
- # mcast from Second arrives to hv1:Migrator
- # mcast from Second doesn't arrive to hv2:Migrator
-@@ -14724,11 +14742,13 @@ request=$(send_arp hv2 second 000000000002 ffffffffffff $second_spa $migrator_tp
- echo $request >> hv1/migrator.expected
- echo $request >> hv1/first.expected
- echo $request >> hv3/third.expected
-+echo $request >> hv2/n1.expected
-
- # unicast from Third arrives to hv1:Migrator
- # unicast from Third doesn't arrive to hv2:Migrator
- request=$(send_arp hv3 third 000000000003 0000000000ff $third_spa $migrator_tpa)
- echo $request >> hv1/migrator.expected
-+echo $request >> hv3/n1.expected
-
- # mcast from Third arrives to hv1:Migrator
- # mcast from Third doesn't arrive to hv2:Migrator
-@@ -14736,14 +14756,17 @@ request=$(send_arp hv3 third 000000000003 ffffffffffff $third_spa $migrator_tpa)
- echo $request >> hv1/migrator.expected
- echo $request >> hv1/first.expected
- echo $request >> hv2/second.expected
-+echo $request >> hv3/n1.expected
-
- # unicast from hv1:Migrator arrives to First, Second, and Third
- request=$(send_arp hv1 migrator 0000000000ff 000000000001 $migrator_tpa $first_spa)
- echo $request >> hv1/first.expected
- request=$(send_arp hv1 migrator 0000000000ff 000000000002 $migrator_tpa $second_spa)
- echo $request >> hv2/second.expected
-+echo $request >> hv1/n1.expected
- request=$(send_arp hv1 migrator 0000000000ff 000000000003 $migrator_tpa $third_spa)
- echo $request >> hv3/third.expected
-+echo $request >> hv1/n1.expected
-
- # unicast from hv2:Migrator doesn't arrive to First, Second, or Third
- request=$(send_arp hv2 migrator 0000000000ff 000000000001 $migrator_tpa $first_spa)
-@@ -14755,6 +14778,7 @@ request=$(send_arp hv1 migrator 0000000000ff ffffffffffff $migrator_tpa $first_s
- echo $request >> hv1/first.expected
- echo $request >> hv2/second.expected
- echo $request >> hv3/third.expected
-+echo $request >> hv1/n1.expected
-
- # mcast from hv2:Migrator doesn't arrive to First, Second, or Third
- request=$(send_arp hv2 migrator 0000000000ff ffffffffffff $migrator_tpa $first_spa)
-@@ -14841,8 +14865,18 @@ echo $request >> hv1/first.expected
- echo $request >> hv2/second.expected
- echo $request >> hv3/third.expected
-
-+# unicast from Second arrives to Third through localnet port
-+request=$(send_arp hv2 second 000000000002 000000000003 $second_spa $third_spa)
-+echo $request >> hv2/n1.expected
-+
- check_packets
-
-+# Wait for MAC address of migrator to be on hv1 related port in main switch.
-+# Hence the MAC will not migrate back unexpectedly later.
-+p1=$(as main ovs-ofctl show n1 | grep hv1_br-phys | awk '{print int($1)}')
-+p2=$(as main ovs-ofctl show n1 | grep hv2_br-phys | awk '{print int($1)}')
-+OVS_WAIT_UNTIL([test x`as main ovs-appctl fdb/show n1 | grep 00:00:00:00:00:ff | awk '{print $1}'` = x$p1])
-+
- # Complete migration: destination is bound
- check ovn-nbctl lsp-set-options migrator requested-chassis=hv2
- wait_column "$hv2_uuid" Port_Binding chassis logical_port=migrator
-@@ -14852,17 +14886,22 @@ wait_column "" Port_Binding requested_additional_chassis logical_port=migrator
- wait_for_ports_up
-
- check ovn-nbctl --wait=hv sync
--sleep 1
-+OVS_WAIT_UNTIL([test `as hv2 ovs-vsctl get Interface migrator external_ids:ovn-installed` = '"true"'])
-
- # advertise new location of the port through localnet port
--send_garp hv2 migrator 0000000000ff ffffffffffff $migrator_spa $migrator_tpa
-+send_garp hv2 migrator 0000000000ff ffffffffffff $migrator_tpa $migrator_tpa
-+
- reset_env
-
-+# Wait for MAC address of migrator to be on hv2 port in main switch
-+OVS_WAIT_UNTIL([test x`as main ovs-appctl fdb/show n1 | grep 00:00:00:00:00:ff | awk '{print $1}'` = x$p2])
-+
- # check that...
- # unicast from Third doesn't arrive to hv1:Migrator
- # unicast from Third arrives to hv2:Migrator
- request=$(send_arp hv3 third 000000000003 0000000000ff $third_spa $migrator_tpa)
- echo $request >> hv2/migrator.expected
-+echo $request >> hv3/n1.expected
-
- # mcast from Third doesn't arrive to hv1:Migrator
- # mcast from Third arrives to hv2:Migrator
-@@ -14870,11 +14909,13 @@ request=$(send_arp hv3 third 000000000003 ffffffffffff $third_spa $migrator_tpa)
- echo $request >> hv2/migrator.expected
- echo $request >> hv1/first.expected
- echo $request >> hv2/second.expected
-+echo $request >> hv3/n1.expected
-
- # unicast from First doesn't arrive to hv1:Migrator
- # unicast from First arrives to hv2:Migrator
- request=$(send_arp hv1 first 000000000001 0000000000ff $first_spa $migrator_tpa)
- echo $request >> hv2/migrator.expected
-+echo $request >> hv1/n1.expected
-
- # mcast from First doesn't arrive to hv1:Migrator
- # mcast from First arrives to hv2:Migrator binding
-@@ -14882,6 +14923,7 @@ request=$(send_arp hv1 first 000000000001 ffffffffffff $first_spa $migrator_tpa)
- echo $request >> hv2/migrator.expected
- echo $request >> hv2/second.expected
- echo $request >> hv3/third.expected
-+echo $request >> hv1/n1.expected
-
- # unicast from Second doesn't arrive to hv1:Migrator
- # unicast from Second arrives to hv2:Migrator
-@@ -14903,10 +14945,12 @@ request=$(send_arp hv1 migrator 0000000000ff 000000000003 $migrator_tpa $third_s
- # unicast from hv2:Migrator arrives to First, Second, and Third
- request=$(send_arp hv2 migrator 0000000000ff 000000000001 $migrator_tpa $first_spa)
- echo $request >> hv1/first.expected
-+echo $request >> hv2/n1.expected
- request=$(send_arp hv2 migrator 0000000000ff 000000000002 $migrator_tpa $second_spa)
- echo $request >> hv2/second.expected
- request=$(send_arp hv2 migrator 0000000000ff 000000000003 $migrator_tpa $third_spa)
- echo $request >> hv3/third.expected
-+echo $request >> hv2/n1.expected
-
- # mcast from hv1:Migrator doesn't arrive to First, Second, or Third
- request=$(send_arp hv1 migrator 0000000000ff ffffffffffff $migrator_tpa $first_spa)
-@@ -14916,14 +14960,400 @@ request=$(send_arp hv2 migrator 0000000000ff ffffffffffff $migrator_tpa $first_s
- echo $request >> hv1/first.expected
- echo $request >> hv2/second.expected
- echo $request >> hv3/third.expected
-+echo $request >> hv2/n1.expected
-+
-+check_packets
-+
-+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..1cabf1f31 100644
---- a/tests/system-ovn.at
-+++ b/tests/system-ovn.at
-@@ -4441,7 +4441,7 @@ tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=ovn-ctl -- Open Virtual Network northbound daemon lifecycle utility
- -ovn-ctl
[options] command
-+ ovn-ctl
[options] command
-+ [--- extra_args]
-+
This program is intended to be invoked internally by Open Virtual Network -@@ -156,6 +159,15 @@ -
--db-nb-probe-interval-to-active=Time in milliseconds
--db-sb-probe-interval-to-active=Time in milliseconds
-+ Any options after '--' will be passed on to the binary run by
-+ command with the exception of start_northd, which can have
-+ options specified in ovn-northd-db-params.conf. Any extra_args
-+ passed to start_northd will be passed to the ovsdb-servers if
-+ --ovn-manage-ovsdb=yes
-+
Following are the optional configuration files. If present, it should be located in the etc dir
- -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/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c -index 6a8ba6330..2918db1c6 100644 ---- a/utilities/ovn-nbctl.c -+++ b/utilities/ovn-nbctl.c -@@ -5345,7 +5345,7 @@ nbctl_lrp_set_gateway_chassis(struct ctl_context *ctx) - } - - const char *chassis_name = ctx->argv[2]; -- if (ctx->argv[3]) { -+ if (ctx->argc == 4) { - error = parse_priority(ctx->argv[3], &priority); - if (error) { - ctx->error = error; diff --git a/SOURCES/ovn22.09.patch b/SOURCES/ovn22.09.patch new file mode 100644 index 0000000..ba07376 --- /dev/null +++ b/SOURCES/ovn22.09.patch @@ -0,0 +1,1164 @@ +diff --git a/NEWS b/NEWS +index ef6a99fed..0392d8d23 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,3 +1,6 @@ ++OVN v22.09.1 - xx xxx xxxx ++-------------------------- ++ + OVN v22.09.0 - 16 Sep 2022 + -------------------------- + - ovn-controller: Add configuration knob, through OVS external-id +diff --git a/configure.ac b/configure.ac +index 765aacb17..c79d79ffe 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -13,7 +13,7 @@ + # limitations under the License. + + AC_PREREQ(2.63) +-AC_INIT(ovn, 22.09.0, bugs@openvswitch.org) ++AC_INIT(ovn, 22.09.1, bugs@openvswitch.org) + AC_CONFIG_MACRO_DIR([m4]) + AC_CONFIG_AUX_DIR([build-aux]) + AC_CONFIG_HEADERS([config.h]) +diff --git a/controller/binding.c b/controller/binding.c +index 8f6b4b19d..c3d2b2e42 100644 +--- a/controller/binding.c ++++ b/controller/binding.c +@@ -2666,7 +2666,7 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, + get_local_datapath(b_ctx_out->local_datapaths, + peer->datapath->tunnel_key); + } +- if (peer_ld && need_add_patch_peer_to_local( ++ if (peer_ld && need_add_peer_to_local( + b_ctx_in->sbrec_port_binding_by_name, peer, + b_ctx_in->chassis_rec)) { + add_local_datapath( +@@ -2681,7 +2681,7 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, + /* Add the peer datapath to the local datapaths if it's + * not present yet. + */ +- if (need_add_patch_peer_to_local( ++ if (need_add_peer_to_local( + b_ctx_in->sbrec_port_binding_by_name, pb, + b_ctx_in->chassis_rec)) { + add_local_datapath_peer_port( +diff --git a/controller/local_data.c b/controller/local_data.c +index 9eee568d1..035f10fff 100644 +--- a/controller/local_data.c ++++ b/controller/local_data.c +@@ -115,14 +115,19 @@ local_datapath_destroy(struct local_datapath *ld) + free(ld); + } + +-/* Checks if pb is a patch port and the peer datapath should be added to local +- * datapaths. */ ++/* Checks if pb is running on local gw router or pb is a patch port ++ * and the peer datapath should be added to local datapaths. */ + bool +-need_add_patch_peer_to_local( ++need_add_peer_to_local( + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_port_binding *pb, + const struct sbrec_chassis *chassis) + { ++ /* This port is running on local gw router. */ ++ if (!strcmp(pb->type, "l3gateway") && pb->chassis == chassis) { ++ return true; ++ } ++ + /* If it is not a patch port, no peer to add. */ + if (strcmp(pb->type, "patch")) { + return false; +@@ -571,7 +576,7 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + peer_name); + + if (peer && peer->datapath) { +- if (need_add_patch_peer_to_local( ++ if (need_add_peer_to_local( + sbrec_port_binding_by_name, pb, chassis)) { + struct local_datapath *peer_ld = + add_local_datapath__(sbrec_datapath_binding_by_key, +diff --git a/controller/local_data.h b/controller/local_data.h +index d898c8aa5..b5429eb58 100644 +--- a/controller/local_data.h ++++ b/controller/local_data.h +@@ -66,7 +66,7 @@ struct local_datapath *local_datapath_alloc( + struct local_datapath *get_local_datapath(const struct hmap *, + uint32_t tunnel_key); + bool +-need_add_patch_peer_to_local( ++need_add_peer_to_local( + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_port_binding *, + const struct sbrec_chassis *); +diff --git a/controller/physical.c b/controller/physical.c +index f3c8bddce..705146316 100644 +--- a/controller/physical.c ++++ b/controller/physical.c +@@ -803,6 +803,14 @@ put_replace_router_port_mac_flows(struct ovsdb_idl_index + + ofpact_put_OUTPUT(ofpacts_p)->port = ofport; + ++ /* Replace the MAC back and strip vlan. In case of l2 flooding ++ * traffic (ARP/ND) we need to restore previous state so other ports ++ * do not receive the traffic tagged and with wrong MAC. */ ++ ofpact_put_SET_ETH_SRC(ofpacts_p)->mac = router_port_mac; ++ if (tag) { ++ ofpact_put_STRIP_VLAN(ofpacts_p); ++ } ++ + ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 150, + localnet_port->header_.uuid.parts[0], + &match, ofpacts_p, &localnet_port->header_.uuid); +diff --git a/controller/pinctrl.c b/controller/pinctrl.c +index 3f5d0af79..1e4230ed3 100644 +--- a/controller/pinctrl.c ++++ b/controller/pinctrl.c +@@ -4378,7 +4378,7 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, + const struct sbrec_port_binding *pb; + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, + sbrec_port_binding_by_datapath) { +- if (strcmp(pb->type, "patch")) { ++ if (strcmp(pb->type, "patch") && strcmp(pb->type, "l3gateway")) { + continue; + } + struct buffered_packets *cur_qp; +diff --git a/debian/changelog b/debian/changelog +index 267e12baa..5ed83900b 100644 +--- a/debian/changelog ++++ b/debian/changelog +@@ -1,3 +1,9 @@ ++OVN (22.09.1-1) unstable; urgency=low ++ [ OVN team ] ++ * New upstream version ++ ++ -- OVN team
++ This table also installs a priority-50 logical flow for each logical
++ router that has NATs configured on it. The flow has match
++ ip && ct_label.natted == 1
and action
++ REGBIT_DST_NAT_IP_LOCAL = 1; next;
. This is intended
++ to ensure that traffic that was DNATted locally will use a separate
++ conntrack zone for SNAT if SNAT is required later in the egress
++ pipeline. Note that this flow checks the value of
++ ct_label.natted
, which is set in the ingress pipeline.
++ This means that ovn-northd assumes that this value is carried over
++ from the ingress pipeline to the egress pipeline and is not altered
++ or cleared. If conntrack label values are ever changed to be cleared
++ between the ingress and egress pipelines, then the match conditions
++ of this flow will be updated accordingly.
++
+diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in
+index 821eb03cc..57dc977c1 100644
+--- a/rhel/ovn-fedora.spec.in
++++ b/rhel/ovn-fedora.spec.in
+@@ -65,6 +65,7 @@ BuildRequires: tcpdump
+ BuildRequires: unbound unbound-devel
+
+ Requires: openssl hostname iproute module-init-tools openvswitch
++Requires: python3-openvswitch
+
+ Requires(post): systemd-units
+ Requires(preun): systemd-units
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index 7c3c84007..15588cc52 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -5019,6 +5019,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.10 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.20 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.30 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+@@ -5093,6 +5094,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.10 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.20 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ip4.dst == 172.168.0.30 && is_chassis_resident("cr-lr0-public")), action=(reg9[[4]] = 1; next;)
+@@ -5161,6 +5163,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ ])
+
+ AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+@@ -5221,6 +5224,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ ])
+
+ AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+@@ -5286,6 +5290,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ ])
+
+ AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+@@ -5364,6 +5369,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
+
+ AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+ table=? (lr_out_chk_dnat_local), priority=0 , match=(1), action=(reg9[[4]] = 0; next;)
++ table=? (lr_out_chk_dnat_local), priority=50 , match=(ip && ct_mark.natted == 1), action=(reg9[[4]] = 1; next;)
+ ])
+
+ AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
+@@ -6129,7 +6135,6 @@ AT_CHECK([grep -e "(lr_in_ip_routing ).*outport" lr0flows | sed 's/table=../ta
+ ])
+
+ AT_CLEANUP
+-])
+
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([check exclude-lb-vips-from-garp option])
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 80e9192ca..dd72996dd 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -32889,3 +32889,32 @@ check ovn-nbctl --wait=hv sync
+ OVN_CLEANUP([hv1])
+ AT_CLEANUP
+ ])
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([ovn-controller: batch add port and delete port in same IDL])
++ovn_start
++net_add n1
++
++sim_add hv1
++as hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++check ovs-vsctl add-port br-int p1
++
++check ovs-vsctl set interface p1 external-ids:iface-id=sw0-port1
++check ovn-nbctl --wait=hv sync
++ovn-appctl debug/pause
++OVS_WAIT_UNTIL([test x$(as hv1 ovn-appctl -t ovn-controller debug/status) = "xpaused"])
++
++check ovn-nbctl ls-add sw0 -- lsp-add sw0 sw0-port1
++check ovn-nbctl lsp-del sw0-port1
++check ovn-nbctl --wait=sb sync
++
++ovn-appctl debug/resume
++check ovn-nbctl --wait=hv sync
++
++check ovn-nbctl ls-del sw0
++check ovn-nbctl --wait=hv sync
++OVN_CLEANUP([hv1])
++AT_CLEANUP
++])
+diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
+index 616a87fcf..8e6cb415c 100644
+--- a/tests/system-common-macros.at
++++ b/tests/system-common-macros.at
+@@ -44,15 +44,38 @@ m4_define([NS_CHECK_EXEC],
+ # appropriate type, and allows additional arguments to be passed.
+ m4_define([ADD_BR], [ovs-vsctl _ADD_BR([$1]) -- $2])
+
+-# ADD_INT([port], [namespace], [ovs-br], [ip_addr])
++# ADD_INT([port], [namespace], [ovs-br], [ip_addr] [ip6_addr])
+ #
+ # Add an internal port to 'ovs-br', then shift it into 'namespace' and
+ # configure it with 'ip_addr' (specified in CIDR notation).
++# Optionally add an ipv6 address
+ m4_define([ADD_INT],
+ [ AT_CHECK([ovs-vsctl add-port $3 $1 -- set int $1 type=internal])
+ AT_CHECK([ip link set $1 netns $2])
+ NS_CHECK_EXEC([$2], [ip addr add $4 dev $1])
+ NS_CHECK_EXEC([$2], [ip link set dev $1 up])
++ if test -n "$5"; then
++ NS_CHECK_EXEC([$2], [ip -6 addr add $5 dev $1])
++ fi
++ ]
++)
++
++# NS_ADD_INT([port], [namespace], [ovs-br], [ip_addr] [mac_addr] [ip6_addr] [default_gw] [default_ipv6_gw])
++# Create a namespace
++# Add an internal port to 'ovs-br', then shift it into 'namespace'.
++# Configure it with 'ip_addr' (specified in CIDR notation) and ip6_addr.
++# Set mac_addr
++# Add default gw for ipv4 and ipv6
++m4_define([NS_ADD_INT],
++ [ AT_CHECK([ovs-vsctl add-port $3 $1 -- set int $1 type=internal external_ids:iface-id=$1])
++ ADD_NAMESPACES($2)
++ AT_CHECK([ip link set $1 netns $2])
++ NS_CHECK_EXEC([$2], [ip link set $1 address $5])
++ NS_CHECK_EXEC([$2], [ip link set dev $1 up])
++ NS_CHECK_EXEC([$2], [ip addr add $4 dev $1])
++ NS_CHECK_EXEC([$2], [ip addr add $6 dev $1])
++ NS_CHECK_EXEC([$2], [ip route add default via $7 dev $1])
++ NS_CHECK_EXEC([$2], [ip -6 route add default via $8 dev $1])
+ ]
+ )
+
+@@ -333,4 +356,166 @@ m4_define([OVS_CHECK_CT_CLEAR],
+
+ # OVS_CHECK_CT_ZERO_SNAT()
+ m4_define([OVS_CHECK_CT_ZERO_SNAT],
+- [AT_SKIP_IF([! grep -q "Datapath supports ct_zero_snat" ovs-vswitchd.log])]))
++ [AT_SKIP_IF([! grep -q "Datapath supports ct_zero_snat" ovs-vswitchd.log])])
++
++# OVN_TEST_IPV6_PREFIX_DELEGATION()
++m4_define([OVN_TEST_IPV6_PREFIX_DELEGATION],
++[
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++
++ADD_BR([br-int])
++ADD_BR([br-ext])
++
++ovs-ofctl add-flow br-ext action=normal
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++ -- set Open_vSwitch . external-ids:system-id=hv1 \
++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++ADD_NAMESPACES(sw01)
++ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
++ "192.168.1.1")
++ADD_NAMESPACES(sw11)
++ADD_VETH(sw11, sw11, br-int, "192.168.2.2/24", "f0:00:00:02:02:03", \
++ "192.168.2.1")
++ADD_NAMESPACES(server)
++ADD_VETH(s1, server, br-ext, "2001:1db8:3333::2/64", "f0:00:00:01:02:05", \
++ "2001:1db8:3333::1")
++
++if test X"$1" = X"GR"; then
++ ovn-nbctl create Logical_Router name=R1 options:chassis=hv1
++else
++ ovn-nbctl lr-add R1
++fi
++
++ovn-nbctl ls-add sw0
++ovn-nbctl ls-add sw1
++ovn-nbctl ls-add public
++
++ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
++ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24
++ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24
++
++if test X"$1" != X"GR"; then
++ ovn-nbctl lrp-set-gateway-chassis rp-public hv1
++fi
++
++ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
++ type=router options:router-port=rp-sw0 \
++ -- lsp-set-addresses sw0-rp router
++ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \
++ type=router options:router-port=rp-sw1 \
++ -- lsp-set-addresses sw1-rp router
++
++ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
++ type=router options:router-port=rp-public \
++ -- lsp-set-addresses public-rp router
++
++ovn-nbctl lsp-add sw0 sw01 \
++ -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2"
++
++ovn-nbctl lsp-add sw1 sw11 \
++ -- lsp-set-addresses sw11 "f0:00:00:02:02:03 192.168.2.2"
++
++OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep 2001:1db8:3333::2 | grep tentative)" = ""])
++OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep fe80 | grep tentative)" = ""])
++
++AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext])
++ovn-nbctl lsp-add public public1 \
++ -- lsp-set-addresses public1 unknown \
++ -- lsp-set-type public1 localnet \
++ -- lsp-set-options public1 network_name=phynet
++
++ovn-nbctl set logical_router_port rp-public options:prefix_delegation=true
++ovn-nbctl set logical_router_port rp-public options:prefix=true
++ovn-nbctl set logical_router_port rp-sw0 options:prefix=true
++ovn-nbctl set logical_router_port rp-sw1 options:prefix=true
++
++OVN_POPULATE_ARP
++
++ovn-nbctl --wait=hv sync
++
++cat > /etc/dhcp/dhcpd.conf <