On Mon, Jun 6, 2022 at 3:48 AM Han Zhou <hz...@ovn.org> wrote: > > > > On Fri, Jun 3, 2022 at 1:20 PM Ihar Hrachyshka <ihrac...@redhat.com> wrote: > > > > When options:activation-strategy is set to "rarp" for LSP, when used in > > combination with multiple chassis names listed in > > options:requested-chassis, additional chassis will install special flows > > that would block all ingress and egress traffic for the port until a > > special activation event happens. > > > > For "rarp" strategy, an observation of a RARP packet sent from the port > > on the additional chassis is such an event. When it occurs, a special > > flow passes control to a controller() action handler that eventually > > removes the installed blocking flows and also marks the port as > > options:additional-chassis-activated in southbound db. > > > > This feature is useful in live migration scenarios where it's not > > advisable to unlock the destination port location prematurily to avoid > > duplicate packets originating from the port. > > > > Signed-off-by: Ihar Hrachyshka <ihrac...@redhat.com> > > --- > > v13: use resubmit() action to reinject RARP into pipeline. > > v13: lock / unlock pinctrl_mutex in functions invoked from main thread. > > v13: db_is_port_activated->lport_is_activated_by_activation_strategy. > > Thanks Ihar for the revision. Sorry that I didn't follow up in time after v6. > Please see some of my quick comments for this version regarding I-P: > > > --- > > NEWS | 2 + > > controller/lport.c | 22 +++ > > controller/lport.h | 3 + > > controller/ovn-controller.c | 87 +++++++++ > > controller/physical.c | 94 ++++++++++ > > controller/pinctrl.c | 145 +++++++++++++- > > controller/pinctrl.h | 13 ++ > > include/ovn/actions.h | 3 + > > northd/northd.c | 10 + > > northd/ovn-northd.c | 5 +- > > ovn-nb.xml | 11 ++ > > ovn-sb.xml | 15 ++ > > tests/ovn.at | 365 ++++++++++++++++++++++++++++++++++++ > > 13 files changed, 772 insertions(+), 3 deletions(-) > > > > diff --git a/NEWS b/NEWS > > index 2ee283a56..7c54670ed 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -29,6 +29,8 @@ OVN v22.06.0 - XX XXX XXXX > > - 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/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 b597c0e37..a37dfcb78 100644 > > --- a/controller/ovn-controller.c > > +++ b/controller/ovn-controller.c > > @@ -1047,6 +1047,50 @@ en_ofctrl_is_connected_run(struct engine_node *node, > > void *data) > > engine_set_node_state(node, EN_UNCHANGED); > > } > > > > +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_; > > + > > + struct activated_port *pp; > > + if (!data->activated_ports) { > > + return; > > + } > > + > > + LIST_FOR_EACH_POP (pp, list, data->activated_ports) { > > + free(pp); > > + } > > + free(data->activated_ports); > > + data->activated_ports = NULL; > > +} > > + > > +static void > > +en_activated_ports_run(struct engine_node *node, void *data_) > > +{ > > + struct ed_type_activated_ports *data = data_; > > + > > + en_activated_ports_cleanup(data); > > + data->activated_ports = get_activated_ports(); > > + if (data->activated_ports) { > > + engine_set_node_state(node, EN_UNCHANGED); > > Sorry that I don't understand here. If there are activated_ports why it is > UNCHANGED? Shouldn't it be UPDATED so that the runtime_data can be triggered > to handle it? >
This is a mistake. Should be fixed now. > > + } else { > > + engine_set_node_state(node, EN_UPDATED); > > + } > > +} > > + > > /* This engine node is to wrap the OVS_interface input and maintain a copy > > of > > * the old version of data for the column external_ids. > > * > > @@ -1421,6 +1465,44 @@ en_runtime_data_run(struct engine_node *node, void > > *data) > > engine_set_node_state(node, EN_UPDATED); > > } > > > > +static bool > > +runtime_data_activated_ports_handler(struct engine_node *node, void *data) > > +{ > > + struct ed_type_runtime_data *rt_data = data; > > + > > + struct ed_type_activated_ports *ap = > > + engine_get_input_data("activated_ports", node); > > + > > + if (!ap->activated_ports) { > > + return true; > > + } > > + > > + struct activated_port *pp; > > + LIST_FOR_EACH_POP (pp, list, ap->activated_ports) { > > Here the runtime_data node not only reads the activated_ports data but also > writes to (cleans) it. This is not expected for a change-handler in I-P. A > change handler is not guaranteed to be executed in an engine run. It is > possible that in the same run there are some other changes that triggers I-P > engine recompute, which calls only the xxx_run() functions but not the > handlers. In that case the activated_ports are not cleared? > > In this case, the activated_ports is not like usual engine data. It is in > fact tracked-changes, and there is no real persistent data for this engine > node. So it seems the right place to clean it is the clear_tracked_data() > member function. > > There are other reasons why a change handler is not the right place to clear > its input changes. For example, there can be other nodes that depend on the > same input (although it is not the case here). > > In addition, if the change needs to be handled in the change handler, it is > reasonable to be handled in the runtime_data_run(). If there are special > reasons it is not needed there, it deserves some comment to help understand. > Moved activated_ports management to _run and _clear_tracked_data as I believe you suggested. > > + 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) { > > + rt_data->tracked = true; > > + tracked_datapath_lport_add(pb, TRACKED_RESOURCE_UPDATED, > > + &rt_data->tracked_dp_bindings); > > + engine_set_node_state(node, EN_UPDATED); > > + } > > + free(pp); > > + } > > + free(ap->activated_ports); > > + ap->activated_ports = NULL; > > + return true; > > +} > > + > > static bool > > runtime_data_ovs_interface_shadow_handler(struct engine_node *node, void > > *data) > > { > > @@ -3453,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(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"); > > @@ -3500,6 +3583,8 @@ main(int argc, char *argv[]) > > engine_add_input(&en_pflow_output, &en_sb_multicast_group, > > pflow_output_sb_multicast_group_handler); > > > > + engine_add_input(&en_pflow_output, &en_sb_datapath_binding, > > + engine_noop_handler); > > Usually, if a node depends on some input, if the input changes, it should > handle it. Could you put some comment here why sb_datapath_binding is > required by pflow_output but doesn't need to be handled? > I thought this is needed to get access to dp_binding index inside the runtime_data handler but that's not the case and it works without this dependency. Removed. > Thanks, > Han > > > engine_add_input(&en_pflow_output, &en_runtime_data, > > pflow_output_runtime_data_handler); > > engine_add_input(&en_pflow_output, &en_sb_encap, NULL); > > @@ -3584,6 +3669,8 @@ main(int argc, char *argv[]) > > runtime_data_sb_datapath_binding_handler); > > engine_add_input(&en_runtime_data, &en_sb_port_binding, > > runtime_data_sb_port_binding_handler); > > + engine_add_input(&en_runtime_data, &en_activated_ports, > > + runtime_data_activated_ports_handler); > > > > /* The OVS interface handler for runtime_data changes MUST be executed > > * after the sb_port_binding_handler as port_binding deletes must be > > 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..3d9f2c195 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', 'activated_ports' 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(struct ovsdb_idl_txn *ovnsb_idl_txn); > > +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); > > } > > > > @@ -4036,6 +4058,7 @@ pinctrl_wait(struct ovsdb_idl_txn *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(ovnsb_idl_txn); > > } > > > > /* Called by ovn-controller. */ > > @@ -4050,6 +4073,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 +7751,123 @@ pinctrl_handle_svc_check(struct rconn *swconn, > > const struct flow *ip_flow, > > } > > } > > > > +static struct ovs_list activated_ports = OVS_LIST_INITIALIZER( > > + &activated_ports); > > + > > +struct ovs_list * > > +get_activated_ports(void) > > +{ > > + ovs_mutex_lock(&pinctrl_mutex); > > + if (ovs_list_is_empty(&activated_ports)) { > > + ovs_mutex_unlock(&pinctrl_mutex); > > + return NULL; > > + } > > + > > + struct activated_port *pp; > > + struct ovs_list *res = xmalloc(sizeof *res); > > + ovs_list_init(res); > > + > > + LIST_FOR_EACH (pp, list, &activated_ports) { > > + struct activated_port *pp_copy = xmalloc(sizeof *pp_copy); > > + pp_copy->port_key = pp->port_key; > > + pp_copy->dp_key = pp->dp_key; > > + ovs_list_push_front(res, &pp_copy->list); > > + } > > + ovs_mutex_unlock(&pinctrl_mutex); > > + return res; > > +} > > + > > +static void > > +init_activated_ports(void) > > + OVS_REQUIRES(pinctrl_mutex) > > +{ > > + ovs_list_init(&activated_ports); > > +} > > + > > +static void > > +destroy_activated_ports(void) > > + OVS_REQUIRES(pinctrl_mutex) > > +{ > > + struct activated_port *pp; > > + LIST_FOR_EACH_POP (pp, list, &activated_ports) { > > + free(pp); > > + } > > +} > > + > > +static void > > +wait_activated_ports(struct ovsdb_idl_txn *ovnsb_idl_txn) > > + OVS_REQUIRES(pinctrl_mutex) > > +{ > > + if (ovnsb_idl_txn && !ovs_list_is_empty(&activated_ports)) { > > + 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, &activated_ports) { > > + 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, &activated_ports) { > > + 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); > > + } > > + } > > +} > > + > > +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(&activated_ports, &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..3a29d1896 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,15 @@ 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; > > +}; > > + > > +struct ovs_list *get_activated_ports(void); > > +bool pinctrl_is_port_activated(int64_t dp_key, int64_t port_key); > > +bool db_is_port_activated(const struct sbrec_port_binding *pb, > > + const struct sbrec_chassis *chassis); > > #endif /* controller/pinctrl.h */ > > 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 450e05ad6..988b57acc 100644 > > --- a/northd/northd.c > > +++ b/northd/northd.c > > @@ -3469,6 +3469,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)) { > > 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 2dc0d5bea..9d37dd3cf 100644 > > --- a/ovn-sb.xml > > +++ b/ovn-sb.xml > > @@ -3354,6 +3354,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.at b/tests/ovn.at > > index 3c079e0fb..b210d4a28 100644 > > --- a/tests/ovn.at > > +++ b/tests/ovn.at > > @@ -14924,6 +14924,371 @@ OVN_CLEANUP([hv1],[hv2],[hv3]) > > AT_CLEANUP > > ]) > > > > +OVN_FOR_EACH_NORTHD([ > > +AT_SETUP([options:activation-strategy for logical port]) > > +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]) > > +# TODO: remove it when we find a way to make vswitchd forward packets to > > +# controller() handler when ovsdb-server is down > > +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 hv2 > > +SVCPID=$(cat $OVS_RUNDIR/ovsdb-server.pid) > > +kill -9 $SVCPID > > + > > +# 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 > > + > > +OVN_CLEANUP([hv1],[hv2]) > > + > > +AT_CLEANUP > > +]) > > + > > OVN_FOR_EACH_NORTHD([ > > AT_SETUP([options:requested-chassis for logical port]) > > ovn_start > > -- > > 2.34.1 > > > > > > _______________________________________________ > > dev mailing list > > d...@openvswitch.org > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev