Re: [ovs-dev] [PATCH ovn v14] Implement RARP activation strategy for ports

2022-06-13 Thread Ihar Hrachyshka
On Mon, Jun 13, 2022 at 3:17 PM Han Zhou  wrote:
>
>
>
> On Mon, Jun 13, 2022 at 11:40 AM Ihar Hrachyshka  wrote:
> >
> > On Thu, Jun 9, 2022 at 3:01 AM Han Zhou  wrote:
> > >
> > >
> > >
> > > On Tue, Jun 7, 2022 at 7:06 PM Ihar Hrachyshka  
> > > 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 
> > >
> > > Thanks for the revision. Please see my comments below:
> > >
> > > > +
> > > > +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;
> > > > +
> > > > +if (!data->activated_ports) {
> > > > +get_activated_ports(&data->activated_ports);
> > > > +if (data->activated_ports) {
> > > > +state = EN_UPDATED;
> > > > +}
> > > > +} else {
> > >
> > > This is not possible because en_activated_ports_clear_tracked_data() will 
> > > always be called before engine_run(). So this branch is not really needed.
> > >
> > > > +struct ovs_list *new_activated_ports;
> > > > +bool ap_changed = get_activated_ports(&new_activated_ports);
> > > > +if (ap_changed && new_activated_ports) {
> > > > +en_activated_ports_cleanup(data);
> > > > +data->activated_ports = new_activated_ports;
> > > > +state = EN_UPDATED;
> > > > +}
> > > > +}
> > > > +engine_set_node_state(node, state);
> > > > +}
> > > > +
> > > > +static bool
> > > > +runtime_data_activated_ports_handler(struct engine_node *node, void 
> > > > *data)
> > >
> > > nit: better to put this function together with runtime_data_xxx 
> > > functions, since this is how we organize the functions for each engine 
> > > node.
> > >
> >
> > Hm. Is it not? Right after this function,
> > runtime_data_ovs_interface_shadow_handler goes.
> >
> That's because the new en_activated_ports related data structure and 
> functions you added in the middle of the en_runtime_data functions :)
> But I think there is no need for another revision just for this. Anyone who 
> applies the change can move the block of code to a proper place.
>

Ouch, sorry. :( I'll move if I need another revision.

> > > ...
> > > > +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

Re: [ovs-dev] [PATCH ovn v14] Implement RARP activation strategy for ports

2022-06-13 Thread Han Zhou
On Mon, Jun 13, 2022 at 11:40 AM Ihar Hrachyshka 
wrote:
>
> On Thu, Jun 9, 2022 at 3:01 AM Han Zhou  wrote:
> >
> >
> >
> > On Tue, Jun 7, 2022 at 7:06 PM Ihar Hrachyshka 
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 
> >
> > Thanks for the revision. Please see my comments below:
> >
> > > +
> > > +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;
> > > +
> > > +if (!data->activated_ports) {
> > > +get_activated_ports(&data->activated_ports);
> > > +if (data->activated_ports) {
> > > +state = EN_UPDATED;
> > > +}
> > > +} else {
> >
> > This is not possible because en_activated_ports_clear_tracked_data()
will always be called before engine_run(). So this branch is not really
needed.
> >
> > > +struct ovs_list *new_activated_ports;
> > > +bool ap_changed = get_activated_ports(&new_activated_ports);
> > > +if (ap_changed && new_activated_ports) {
> > > +en_activated_ports_cleanup(data);
> > > +data->activated_ports = new_activated_ports;
> > > +state = EN_UPDATED;
> > > +}
> > > +}
> > > +engine_set_node_state(node, state);
> > > +}
> > > +
> > > +static bool
> > > +runtime_data_activated_ports_handler(struct engine_node *node, void
*data)
> >
> > nit: better to put this function together with runtime_data_xxx
functions, since this is how we organize the functions for each engine node.
> >
>
> Hm. Is it not? Right after this function,
> runtime_data_ovs_interface_shadow_handler goes.
>
That's because the new en_activated_ports related data structure and
functions you added in the middle of the en_runtime_data functions :)
But I think there is no need for another revision just for this. Anyone who
applies the change can move the block of code to a proper place.

> > ...
> > > +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));
> >

Re: [ovs-dev] [PATCH ovn v14] Implement RARP activation strategy for ports

2022-06-13 Thread Ihar Hrachyshka
On Thu, Jun 9, 2022 at 3:01 AM Han Zhou  wrote:
>
>
>
> On Tue, Jun 7, 2022 at 7:06 PM Ihar Hrachyshka  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 
>
> Thanks for the revision. Please see my comments below:
>
> > +
> > +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;
> > +
> > +if (!data->activated_ports) {
> > +get_activated_ports(&data->activated_ports);
> > +if (data->activated_ports) {
> > +state = EN_UPDATED;
> > +}
> > +} else {
>
> This is not possible because en_activated_ports_clear_tracked_data() will 
> always be called before engine_run(). So this branch is not really needed.
>
> > +struct ovs_list *new_activated_ports;
> > +bool ap_changed = get_activated_ports(&new_activated_ports);
> > +if (ap_changed && new_activated_ports) {
> > +en_activated_ports_cleanup(data);
> > +data->activated_ports = new_activated_ports;
> > +state = EN_UPDATED;
> > +}
> > +}
> > +engine_set_node_state(node, state);
> > +}
> > +
> > +static bool
> > +runtime_data_activated_ports_handler(struct engine_node *node, void *data)
>
> nit: better to put this function together with runtime_data_xxx functions, 
> since this is how we organize the functions for each engine node.
>

Hm. Is it not? Right after this function,
runtime_data_ovs_interface_shadow_handler goes.

> ...
> > +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);
>
> Recall my comment for v6:
> >> What if another LSP on the same chassis (an additional chassis of the 
> >> migrating LSP) wants to send a packet to the migrating LSP on the main 
> >> chassis? Would it be blocked, too?
>
> > Packets are cloned to all chassis, delivered to all of t

Re: [ovs-dev] [PATCH ovn v14] Implement RARP activation strategy for ports

2022-06-09 Thread Han Zhou
On Tue, Jun 7, 2022 at 7:06 PM Ihar Hrachyshka  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 

Thanks for the revision. Please see my comments below:

> +
> +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;
> +
> +if (!data->activated_ports) {
> +get_activated_ports(&data->activated_ports);
> +if (data->activated_ports) {
> +state = EN_UPDATED;
> +}
> +} else {

This is not possible because en_activated_ports_clear_tracked_data() will
always be called before engine_run(). So this branch is not really needed.

> +struct ovs_list *new_activated_ports;
> +bool ap_changed = get_activated_ports(&new_activated_ports);
> +if (ap_changed && new_activated_ports) {
> +en_activated_ports_cleanup(data);
> +data->activated_ports = new_activated_ports;
> +state = EN_UPDATED;
> +}
> +}
> +engine_set_node_state(node, state);
> +}
> +
> +static bool
> +runtime_data_activated_ports_handler(struct engine_node *node, void
*data)

nit: better to put this function together with runtime_data_xxx functions,
since this is how we organize the functions for each engine node.

...
> +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);

Recall my comment for v6:
>> What if another LSP on the same chassis (an additional chassis of the
migrating LSP) wants to send a packet to the migrating LSP on the main
chassis? Would it be blocked, too?

> Packets are cloned to all chassis, delivered to all of them, and each
of them decides on its own if it's ok to deliver it (depending on
whether activation flows are still present). I'll update the test case
to cover the scenario you describe.

What I meant was that: the flow is added to a datapath of LS, with the
outport matching the LSP that is being migrated, consider the example below:
LSP1 is migrating from HV1 to HV2. Now LSP2 on th

Re: [ovs-dev] [PATCH ovn v14] Implement RARP activation strategy for ports

2022-06-08 Thread Ihar Hrachyshka
Sorry, should have included changelog for this revision.

===

v14: introduce clear_tracked_data for activated_ports
v14: don't modify activated_ports node data inside handler
v14: remove unnecessary I-P input dependency
v14: make get_activated_ports track if any new entries were added to a
list to avoid unnecessary I-P pflow node activation.
v14: fixed a bug in _run handler for activated_ports where it marked
as UNCHANGED when activated_ports list was not empty.

On Tue, Jun 7, 2022 at 10:06 PM Ihar Hrachyshka  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 
> ---
>  NEWS|   2 +
>  controller/lport.c  |  22 +++
>  controller/lport.h  |   3 +
>  controller/ovn-controller.c |  97 ++
>  controller/physical.c   |  94 ++
>  controller/pinctrl.c| 155 ++-
>  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, 792 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 
>- 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 2793c8687..78f58e312 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -1417,6 +1417,100 @@ en_runtime_data_run(struct engine_node *node, void 
> *data)
>  engine_set_node_state(node, EN_UPDATED);
>  }
>
> +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;
> +