[ovs-dev,ovn] ovn-northd: Set stage-hint for all applicable flows.
diff mbox series

Message ID 1579275884-16018-1-git-send-email-dceara@redhat.com
State Superseded
Headers show
Series
  • [ovs-dev,ovn] ovn-northd: Set stage-hint for all applicable flows.
Related show

Commit Message

Dumitru Ceara Jan. 17, 2020, 3:44 p.m. UTC
Until now the 'stage-hint' external-id was set only for logical flows
installed for ACLs. In order to simplify troubleshooting, extend the
approach and apply whenever possible. Set stage-hint for logical flows
generated by the following NB tables too:
- Logical_Switch_Port
- Logical_Router_Port
- Load_Balancer
- NAT

Also update ovn-detrace such that whenever stage-hints are available,
all the tables mentioned above are queried and if the stage-hint matches
a NB uuid, relevant information is dumped.

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/ovn-northd.c      | 721 +++++++++++++++++++++++++++++------------------
 utilities/ovn-detrace.in |  93 ++++--
 2 files changed, 524 insertions(+), 290 deletions(-)

Comments

Mark Michelson Jan. 21, 2020, 9:41 p.m. UTC | #1
Hi Dumitru,

It appears that some of the stage hints correspond to DHCPv4_Options and 
DHCPv6_Options, but these are not handled in ovn-detrace.

On 1/17/20 10:44 AM, Dumitru Ceara wrote:
> Until now the 'stage-hint' external-id was set only for logical flows
> installed for ACLs. In order to simplify troubleshooting, extend the
> approach and apply whenever possible. Set stage-hint for logical flows
> generated by the following NB tables too:
> - Logical_Switch_Port
> - Logical_Router_Port
> - Load_Balancer
> - NAT
> 
> Also update ovn-detrace such that whenever stage-hints are available,
> all the tables mentioned above are queried and if the stage-hint matches
> a NB uuid, relevant information is dumped.
> 
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>   northd/ovn-northd.c      | 721 +++++++++++++++++++++++++++++------------------
>   utilities/ovn-detrace.in |  93 ++++--
>   2 files changed, 524 insertions(+), 290 deletions(-)
> 
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index b6dc809..20b8429 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -3714,6 +3714,15 @@ ovn_lflow_hash(const struct ovn_lflow *lflow)
>                                    lflow->actions);
>   }
>   
> +static char *
> +ovn_lflow_hint(const struct ovsdb_idl_row *row)
> +{
> +    if (!row) {
> +        return NULL;
> +    }
> +    return xasprintf("%08x", row->uuid.parts[0]);
> +}
> +
>   static bool
>   ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
>   {
> @@ -3744,14 +3753,14 @@ static void
>   ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>                    enum ovn_stage stage, uint16_t priority,
>                    const char *match, const char *actions,
> -                 const char *stage_hint, const char *where)
> +                 const struct ovsdb_idl_row *stage_hint, const char *where)
>   {
>       ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
>   
>       struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
>       ovn_lflow_init(lflow, od, stage, priority,
>                      xstrdup(match), xstrdup(actions),
> -                   nullable_xstrdup(stage_hint), where);
> +                   ovn_lflow_hint(stage_hint), where);
>       hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
>   }
>   
> @@ -3902,7 +3911,8 @@ build_port_security_ipv6_flow(
>    *   - Priority 80 flow to drop ARP and IPv6 ND packets.
>    */
>   static void
> -build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
> +build_port_security_nd(struct ovn_port *op, struct hmap *lflows,
> +                       const struct ovsdb_idl_row *stage_hint)
>   {
>       struct ds match = DS_EMPTY_INITIALIZER;
>   
> @@ -3938,8 +3948,8 @@ build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
>                   ds_chomp(&match, ',');
>                   ds_put_cstr(&match, "}");
>               }
> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
> -                          ds_cstr(&match), "next;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
> +                                    90, ds_cstr(&match), "next;", stage_hint);
>           }
>   
>           if (ps->n_ipv6_addrs || no_ip) {
> @@ -3948,15 +3958,15 @@ build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
>                             op->json_key, ps->ea_s);
>               build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
>                                                ps->n_ipv6_addrs);
> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
> -                          ds_cstr(&match), "next;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
> +                                    90, ds_cstr(&match), "next;", stage_hint);
>           }
>       }
>   
>       ds_clear(&match);
>       ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
> -    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
> -                  ds_cstr(&match), "drop;");
> +    ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
> +                            ds_cstr(&match), "drop;", stage_hint);
>       ds_destroy(&match);
>   }
>   
> @@ -3977,7 +3987,8 @@ build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
>    */
>   static void
>   build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
> -                       struct hmap *lflows)
> +                       struct hmap *lflows,
> +                       const struct ovsdb_idl_row *stage_hint)
>   {
>       char *port_direction;
>       enum ovn_stage stage;
> @@ -4007,8 +4018,9 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>                                 " && ip4.dst == 255.255.255.255"
>                                 " && udp.src == 68 && udp.dst == 67",
>                                 op->json_key, ps->ea_s);
> -                ovn_lflow_add(lflows, op->od, stage, 90,
> -                              ds_cstr(&dhcp_match), "next;");
> +                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
> +                                        ds_cstr(&dhcp_match), "next;",
> +                                        stage_hint);
>                   ds_destroy(&dhcp_match);
>                   ds_put_format(&match, "inport == %s && eth.src == %s"
>                                 " && ip4.src == {", op->json_key,
> @@ -4047,7 +4059,9 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>               ds_chomp(&match, ' ');
>               ds_chomp(&match, ',');
>               ds_put_cstr(&match, "}");
> -            ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match), "next;");
> +            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
> +                                    ds_cstr(&match), "next;",
> +                                    stage_hint);
>               ds_destroy(&match);
>           }
>   
> @@ -4063,8 +4077,9 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>                                 " && ip6.dst == ff02::/16"
>                                 " && icmp6.type == {131, 135, 143}", op->json_key,
>                                 ps->ea_s);
> -                ovn_lflow_add(lflows, op->od, stage, 90,
> -                              ds_cstr(&dad_match), "next;");
> +                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
> +                                        ds_cstr(&dad_match), "next;",
> +                                        stage_hint);
>                   ds_destroy(&dad_match);
>               }
>               ds_put_format(&match, "%s == %s && %s == %s",
> @@ -4072,8 +4087,9 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>                             pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
>               build_port_security_ipv6_flow(pipeline, &match, ps->ea,
>                                             ps->ipv6_addrs, ps->n_ipv6_addrs);
> -            ovn_lflow_add(lflows, op->od, stage, 90,
> -                          ds_cstr(&match), "next;");
> +            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
> +                                    ds_cstr(&match), "next;",
> +                                    stage_hint);
>               ds_destroy(&match);
>           }
>   
> @@ -4081,7 +4097,8 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>                                   port_direction, op->json_key,
>                                   pipeline == P_IN ? "eth.src" : "eth.dst",
>                                   ps->ea_s);
> -        ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
> +        ovn_lflow_add_with_hint(lflows, op->od, stage, 80, match, "drop;",
> +                                stage_hint);
>           free(match);
>       }
>   
> @@ -4371,12 +4388,13 @@ build_lswitch_input_port_sec(struct hmap *ports, struct hmap *datapaths,
>               ds_put_format(&actions, "set_queue(%s); ", queue_id);
>           }
>           ds_put_cstr(&actions, "next;");
> -        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
> -                      ds_cstr(&match), ds_cstr(&actions));
> +        ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
> +                                ds_cstr(&match), ds_cstr(&actions),
> +                                &op->nbsp->header_);
>   
>           if (op->nbsp->n_port_security) {
> -            build_port_security_ip(P_IN, op, lflows);
> -            build_port_security_nd(op, lflows);
> +            build_port_security_ip(P_IN, op, lflows, &op->nbsp->header_);
> +            build_port_security_nd(op, lflows, &op->nbsp->header_);
>           }
>       }
>   
> @@ -4436,15 +4454,17 @@ build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths,
>                   }
>               }
>               ds_put_cstr(&actions, "output;");
> -            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2,
> +                                    50, ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbsp->header_);
>           } else {
> -            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
> -                          ds_cstr(&match), "drop;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2,
> +                                    150, ds_cstr(&match), "drop;",
> +                                    &op->nbsp->header_);
>           }
>   
>           if (op->nbsp->n_port_security) {
> -            build_port_security_ip(P_OUT, op, lflows);
> +            build_port_security_ip(P_OUT, op, lflows, &op->nbsp->header_);
>           }
>       }
>   
> @@ -4499,10 +4519,12 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
>   
>               ds_put_format(&match_in, "ip && inport == %s", op->json_key);
>               ds_put_format(&match_out, "ip && outport == %s", op->json_key);
> -            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
> -                          ds_cstr(&match_in), "next;");
> -            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
> -                          ds_cstr(&match_out), "next;");
> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
> +                                    ds_cstr(&match_in), "next;",
> +                                    &op->nbsp->header_);
> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
> +                                    ds_cstr(&match_out), "next;",
> +                                    &op->nbsp->header_);
>   
>               ds_destroy(&match_in);
>               ds_destroy(&match_out);
> @@ -4515,10 +4537,12 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
>                             od->localnet_port->json_key);
>               ds_put_format(&match_out, "ip && outport == %s",
>                             od->localnet_port->json_key);
> -            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
> -                          ds_cstr(&match_in), "next;");
> -            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
> -                          ds_cstr(&match_out), "next;");
> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
> +                                    ds_cstr(&match_in), "next;",
> +                                    &od->localnet_port->nbsp->header_);
> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
> +                                    ds_cstr(&match_out), "next;",
> +                                    &od->localnet_port->nbsp->header_);
>   
>               ds_destroy(&match_in);
>               ds_destroy(&match_out);
> @@ -4596,7 +4620,8 @@ static void
>   build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
>                             struct smap_node *node, char *ip_address,
>                             struct nbrec_load_balancer *lb, uint16_t port,
> -                          int addr_family, int pl, struct shash *meter_groups)
> +                          int addr_family, int pl, struct shash *meter_groups,
> +                          const struct ovsdb_idl_row *stage_hint)
>   {
>       if (!controller_event_en || node->value[0]) {
>           return;
> @@ -4627,7 +4652,8 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
>                          event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
>                          meter, node->key, lb->protocol,
>                          UUID_ARGS(&lb->header_.uuid));
> -    ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
> +    ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(&match), action,
> +                            stage_hint);
>       ds_destroy(&match);
>       free(action);
>   }
> @@ -4680,7 +4706,7 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
>   
>               build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
>                                         port, addr_family, S_SWITCH_IN_PRE_LB,
> -                                      meter_groups);
> +                                      meter_groups, &lb->header_);
>   
>               free(ip_address);
>   
> @@ -4772,7 +4798,8 @@ build_acl_log(struct ds *actions, const struct nbrec_acl *acl)
>   static void
>   build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                          enum ovn_stage stage, struct nbrec_acl *acl,
> -                       struct ds *extra_match, struct ds *extra_actions)
> +                       struct ds *extra_match, struct ds *extra_actions,
> +                       const struct ovsdb_idl_row *stage_hint)
>   {
>       struct ds match = DS_EMPTY_INITIALIZER;
>       struct ds actions = DS_EMPTY_INITIALIZER;
> @@ -4788,8 +4815,9 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                     "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
>                     "tcp_reset { outport <-> inport; %s };",
>                     ingress ? "output;" : "next(pipeline=ingress,table=0);");
> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, od, stage,
> +                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>       ds_clear(&match);
>       ds_clear(&actions);
>       build_acl_log(&actions, acl);
> @@ -4801,8 +4829,9 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                     "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
>                     "tcp_reset { outport <-> inport; %s };",
>                     ingress ? "output;" : "next(pipeline=ingress,table=0);");
> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, od, stage,
> +                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>   
>       /* IP traffic */
>       ds_clear(&match);
> @@ -4819,8 +4848,9 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                     "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
>                     "icmp4 { outport <-> inport; %s };",
>                     ingress ? "output;" : "next(pipeline=ingress,table=0);");
> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, od, stage,
> +                            acl->priority + OVN_ACL_PRI_OFFSET,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>       ds_clear(&match);
>       ds_clear(&actions);
>       build_acl_log(&actions, acl);
> @@ -4835,8 +4865,9 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                     "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
>                     "outport <-> inport; %s };",
>                     ingress ? "output;" : "next(pipeline=ingress,table=0);");
> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, od, stage,
> +                            acl->priority + OVN_ACL_PRI_OFFSET,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>   
>       ds_destroy(&match);
>       ds_destroy(&actions);
> @@ -4849,7 +4880,6 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>       bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
>       enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
>   
> -    char *stage_hint = xasprintf("%08x", acl->header_.uuid.parts[0]);
>       if (!strcmp(acl->action, "allow")
>           || !strcmp(acl->action, "allow-related")) {
>           /* If there are any stateful flows, we must even commit "allow"
> @@ -4864,7 +4894,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>               ovn_lflow_add_with_hint(lflows, od, stage,
>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>                                       acl->match, ds_cstr(&actions),
> -                                    stage_hint);
> +                                    &acl->header_);
>               ds_destroy(&actions);
>           } else {
>               struct ds match = DS_EMPTY_INITIALIZER;
> @@ -4893,7 +4923,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>                                       ds_cstr(&match),
>                                       ds_cstr(&actions),
> -                                    stage_hint);
> +                                    &acl->header_);
>   
>               /* Match on traffic in the request direction for an established
>                * connection tracking entry that has not been marked for
> @@ -4913,7 +4943,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>               ovn_lflow_add_with_hint(lflows, od, stage,
>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>                                       ds_cstr(&match), ds_cstr(&actions),
> -                                    stage_hint);
> +                                    &acl->header_);
>   
>               ds_destroy(&match);
>               ds_destroy(&actions);
> @@ -4935,14 +4965,15 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                           " || (ct.est && ct_label.blocked == 1))");
>               if (!strcmp(acl->action, "reject")) {
>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
> -                                       &actions);
> +                                       &actions, &acl->header_);
>               } else {
>                   ds_put_format(&match, " && (%s)", acl->match);
>                   build_acl_log(&actions, acl);
>                   ds_put_cstr(&actions, "/* drop */");
> -                ovn_lflow_add(lflows, od, stage,
> -                              acl->priority + OVN_ACL_PRI_OFFSET,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, stage,
> +                                        acl->priority + OVN_ACL_PRI_OFFSET,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &acl->header_);
>               }
>               /* For an existing connection without ct_label set, we've
>                * encountered a policy change. ACLs previously allowed
> @@ -4961,14 +4992,15 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>               ds_put_cstr(&actions, "ct_commit(ct_label=1/1); ");
>               if (!strcmp(acl->action, "reject")) {
>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
> -                                       &actions);
> +                                       &actions, &acl->header_);
>               } else {
>                   ds_put_format(&match, " && (%s)", acl->match);
>                   build_acl_log(&actions, acl);
>                   ds_put_cstr(&actions, "/* drop */");
> -                ovn_lflow_add(lflows, od, stage,
> -                              acl->priority + OVN_ACL_PRI_OFFSET,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, stage,
> +                                        acl->priority + OVN_ACL_PRI_OFFSET,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &acl->header_);
>               }
>           } else {
>               /* There are no stateful ACLs in use on this datapath,
> @@ -4976,19 +5008,19 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                * logical flow action in all cases. */
>               if (!strcmp(acl->action, "reject")) {
>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
> -                                       &actions);
> +                                       &actions, &acl->header_);
>               } else {
>                   build_acl_log(&actions, acl);
>                   ds_put_cstr(&actions, "/* drop */");
> -                ovn_lflow_add(lflows, od, stage,
> -                              acl->priority + OVN_ACL_PRI_OFFSET,
> -                              acl->match, ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, stage,
> +                                        acl->priority + OVN_ACL_PRI_OFFSET,
> +                                        acl->match, ds_cstr(&actions),
> +                                        &acl->header_);
>               }
>           }
>           ds_destroy(&match);
>           ds_destroy(&actions);
>       }
> -    free(stage_hint);
>   }
>   
>   static struct ovn_port_group *
> @@ -5191,9 +5223,9 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
>                                 "&& ip4.src == %s && udp && udp.src == 67 "
>                                 "&& udp.dst == 68", od->nbs->ports[i]->name,
>                                 server_mac, server_id);
> -                ovn_lflow_add(
> +                ovn_lflow_add_with_hint(
>                       lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
> -                    actions);
> +                    actions, &od->nbs->ports[i]->dhcpv4_options->header_);
>                   ds_destroy(&match);
>               }
>           }
> @@ -5218,9 +5250,9 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
>                                 "&& ip6.src == %s && udp && udp.src == 547 "
>                                 "&& udp.dst == 546", od->nbs->ports[i]->name,
>                                 server_mac, server_ip);
> -                ovn_lflow_add(
> +                ovn_lflow_add_with_hint(
>                       lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
> -                    actions);
> +                    actions, &od->nbs->ports[i]->dhcpv6_options->header_);
>                   ds_destroy(&match);
>               }
>           }
> @@ -5257,9 +5289,10 @@ build_qos(struct ovn_datapath *od, struct hmap *lflows) {
>   
>                   ds_put_format(&dscp_action, "ip.dscp = %"PRId64"; next;",
>                                 qos->value_action[j]);
> -                ovn_lflow_add(lflows, od, stage,
> -                              qos->priority,
> -                              qos->match, ds_cstr(&dscp_action));
> +                ovn_lflow_add_with_hint(lflows, od, stage,
> +                                        qos->priority,
> +                                        qos->match, ds_cstr(&dscp_action),
> +                                        &qos->header_);
>                   ds_destroy(&dscp_action);
>               }
>           }
> @@ -5288,9 +5321,10 @@ build_qos(struct ovn_datapath *od, struct hmap *lflows) {
>                *
>                * We limit the bandwidth of this flow by adding a meter table.
>                */
> -            ovn_lflow_add(lflows, od, stage,
> -                          qos->priority,
> -                          qos->match, ds_cstr(&meter_action));
> +            ovn_lflow_add_with_hint(lflows, od, stage,
> +                                    qos->priority,
> +                                    qos->match, ds_cstr(&meter_action),
> +                                    &qos->header_);
>               ds_destroy(&meter_action);
>           }
>       }
> @@ -5405,11 +5439,13 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
>                       ds_put_format(&match, " && tcp.dst == %d",
>                                     lb_vip->vip_port);
>                   }
> -                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
> -                              120, ds_cstr(&match), ds_cstr(&action));
> +                ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 120,
> +                                        ds_cstr(&match), ds_cstr(&action),
> +                                        &lb->nlb->header_);
>               } else {
> -                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
> -                              110, ds_cstr(&match), ds_cstr(&action));
> +                ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 110,
> +                                        ds_cstr(&match), ds_cstr(&action),
> +                                        &lb->nlb->header_);
>               }
>   
>               ds_destroy(&match);
> @@ -5574,7 +5610,8 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
>                                           struct ovn_port *patch_op,
>                                           struct ovn_datapath *od,
>                                           uint32_t priority,
> -                                        struct hmap *lflows)
> +                                        struct hmap *lflows,
> +                                        const struct ovsdb_idl_row *stage_hint)
>   {
>       struct ds match   = DS_EMPTY_INITIALIZER;
>       struct ds actions = DS_EMPTY_INITIALIZER;
> @@ -5605,8 +5642,8 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
>        * in the broadcast domain.
>        */
>       ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
> -    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>   
>       ds_destroy(&match);
>       ds_destroy(&actions);
> @@ -5623,7 +5660,8 @@ static void
>   build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>                                     struct ovn_datapath *sw_od,
>                                     struct ovn_port *sw_op,
> -                                  struct hmap *lflows)
> +                                  struct hmap *lflows,
> +                                  const struct ovsdb_idl_row *stage_hint)
>   {
>       if (!op || !op->nbrp) {
>           return;
> @@ -5677,11 +5715,13 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>   
>       if (!sset_is_empty(&all_ips_v4)) {
>           build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
> -                                                sw_od, 75, lflows);
> +                                                sw_od, 75, lflows,
> +                                                stage_hint);
>       }
>       if (!sset_is_empty(&all_ips_v6)) {
>           build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
> -                                                sw_od, 75, lflows);
> +                                                sw_od, 75, lflows,
> +                                                stage_hint);
>       }
>   
>       sset_destroy(&all_ips_v4);
> @@ -5752,8 +5792,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>               (!strcmp(op->nbsp->type, "vtep"))) {
>               ds_clear(&match);
>               ds_put_format(&match, "inport == %s", op->json_key);
> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
> -                          ds_cstr(&match), "next;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
> +                                    100, ds_cstr(&match), "next;",
> +                                    &op->nbsp->header_);
>           }
>       }
>   
> @@ -5806,8 +5847,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                       "bind_vport(%s, inport); "
>                       "next;",
>                       op->json_key);
> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, op->od,
> +                                        S_SWITCH_IN_ARP_ND_RSP, 100,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &vp->nbsp->header_);
>               }
>   
>               free(tokstr);
> @@ -5846,8 +5889,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                           "output;",
>                           op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
>                           op->lsp_addrs[i].ipv4_addrs[j].addr_s);
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
> -                                ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_ARP_ND_RSP, 50,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&actions),
> +                                            &op->nbsp->header_);
>   
>                       /* Do not reply to an ARP request from the port that owns
>                        * the address (otherwise a DHCP client that ARPs to check
> @@ -5862,8 +5908,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                        * network is not working as configured, so dropping the
>                        * request would frustrate that intent.) */
>                       ds_put_format(&match, " && inport == %s", op->json_key);
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
> -                                ds_cstr(&match), "next;");
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_ARP_ND_RSP, 100,
> +                                            ds_cstr(&match), "next;",
> +                                            &op->nbsp->header_);
>                   }
>   
>                   /* For ND solicitations, we need to listen for both the
> @@ -5894,14 +5942,19 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                               op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>                               op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>                               op->lsp_addrs[i].ea_s);
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
> -                                ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_ARP_ND_RSP, 50,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&actions),
> +                                            &op->nbsp->header_);
>   
>                       /* Do not reply to a solicitation from the port that owns
>                        * the address (otherwise DAD detection will fail). */
>                       ds_put_format(&match, " && inport == %s", op->json_key);
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
> -                                ds_cstr(&match), "next;");
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_ARP_ND_RSP, 100,
> +                                            ds_cstr(&match), "next;",
> +                                            &op->nbsp->header_);
>                   }
>               }
>           }
> @@ -5949,9 +6002,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                       "output;",
>                       svc_monitor_mac, svc_monitor_mac,
>                       lb->vips[i].backends[j].svc_mon_src_ip);
> -                ovn_lflow_add(lflows, lb->vips[i].backends[j].op->od,
> -                              S_SWITCH_IN_ARP_ND_RSP, 110,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows,
> +                                        lb->vips[i].backends[j].op->od,
> +                                        S_SWITCH_IN_ARP_ND_RSP, 110,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &lb->nlb->header_);
>               }
>           }
>       }
> @@ -5985,6 +6040,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>           }
>   
>           for (size_t i = 0; i < op->n_lsp_addrs; i++) {
> +            struct ovsdb_idl_row *stage_hint;
> +
> +            if (op->nbsp->dhcpv4_options) {
> +                stage_hint = &op->nbsp->dhcpv4_options->header_;
> +            } else {
> +                stage_hint = NULL;
> +            }
> +
>               for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
>                   struct ds options_action = DS_EMPTY_INITIALIZER;
>                   struct ds response_action = DS_EMPTY_INITIALIZER;
> @@ -6006,9 +6069,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                                         op->json_key);
>                       }
>   
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
> -                                  100, ds_cstr(&match),
> -                                  ds_cstr(&options_action));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&options_action),
> +                                            stage_hint);
>                       ds_clear(&match);
>                       /* Allow ip4.src = OFFER_IP and
>                        * ip4.dst = {SERVER_IP, 255.255.255.255} for the below
> @@ -6030,9 +6095,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                                         op->json_key);
>                       }
>   
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
> -                                  100, ds_cstr(&match),
> -                                  ds_cstr(&options_action));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&options_action),
> +                                            stage_hint);
>                       ds_clear(&match);
>   
>                       /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
> @@ -6050,9 +6117,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                                         op->json_key);
>                       }
>   
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
> -                                  100, ds_cstr(&match),
> -                                  ds_cstr(&response_action));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_DHCP_RESPONSE, 100,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&response_action),
> +                                            stage_hint);
>                       ds_destroy(&options_action);
>                       ds_destroy(&response_action);
>                       ds_destroy(&ipv4_addr_match);
> @@ -6060,6 +6129,12 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                   }
>               }
>   
> +            if (op->nbsp->dhcpv6_options) {
> +                stage_hint = &op->nbsp->dhcpv6_options->header_;
> +            } else {
> +                stage_hint = NULL;
> +            }
> +
>               for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
>                   struct ds options_action = DS_EMPTY_INITIALIZER;
>                   struct ds response_action = DS_EMPTY_INITIALIZER;
> @@ -6080,14 +6155,20 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                                         op->json_key);
>                       }
>   
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
> -                                  ds_cstr(&match), ds_cstr(&options_action));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&options_action),
> +                                            stage_hint);
>   
>                       /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
>                        * put_dhcpv6_opts action is successful */
>                       ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
> -                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100,
> -                                  ds_cstr(&match), ds_cstr(&response_action));
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_SWITCH_IN_DHCP_RESPONSE, 100,
> +                                            ds_cstr(&match),
> +                                            ds_cstr(&response_action),
> +                                            stage_hint);
>                       ds_destroy(&options_action);
>                       ds_destroy(&response_action);
>                       break;
> @@ -6172,9 +6253,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                               op->od->localnet_port->json_key,
>                               op->lsp_addrs[i].ea_s, op->json_key,
>                               rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
> -                        ovn_lflow_add(lflows, op->od,
> -                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
> -                                      ds_cstr(&match), "drop;");
> +                        ovn_lflow_add_with_hint(lflows, op->od,
> +                                                S_SWITCH_IN_EXTERNAL_PORT,
> +                                                100, ds_cstr(&match), "drop;",
> +                                                &op->nbsp->header_);
>                       }
>                       for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs;
>                            l++) {
> @@ -6189,9 +6271,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                               rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
>                               rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
>                               rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
> -                        ovn_lflow_add(lflows, op->od,
> -                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
> -                                      ds_cstr(&match), "drop;");
> +                        ovn_lflow_add_with_hint(lflows, op->od,
> +                                                S_SWITCH_IN_EXTERNAL_PORT, 100,
> +                                                ds_cstr(&match), "drop;",
> +                                                &op->nbsp->header_);
>                       }
>                   }
>               }
> @@ -6326,7 +6409,8 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>            * requests only to the router port that owns the IP address.
>            */
>           if (!strcmp(op->nbsp->type, "router")) {
> -            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows);
> +            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
> +                                              &op->nbsp->header_);
>           }
>   
>           for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
> @@ -6342,8 +6426,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                   ds_clear(&actions);
>                   ds_put_format(&actions, "outport = %s; output;", op->json_key);
> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
> +                                        50, ds_cstr(&match),
> +                                        ds_cstr(&actions),
> +                                        &op->nbsp->header_);
>               } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
>                   if (lsp_is_enabled(op->nbsp)) {
>                       ovn_multicast_add(mcgroups, &mc_unknown, op);
> @@ -6361,8 +6447,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                   ds_clear(&actions);
>                   ds_put_format(&actions, "outport = %s; output;", op->json_key);
> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
> +                                        50, ds_cstr(&match),
> +                                        ds_cstr(&actions),
> +                                        &op->nbsp->header_);
>               } else if (!strcmp(op->nbsp->addresses[i], "router")) {
>                   if (!op->peer || !op->peer->nbrp
>                       || !ovs_scan(op->peer->nbrp->mac,
> @@ -6403,8 +6491,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                   ds_clear(&actions);
>                   ds_put_format(&actions, "outport = %s; output;", op->json_key);
> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, op->od,
> +                                        S_SWITCH_IN_L2_LKUP, 50,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &op->nbsp->header_);
>   
>                   /* Add ethernet addresses specified in NAT rules on
>                    * distributed logical routers. */
> @@ -6426,9 +6516,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
>                               ds_clear(&actions);
>                               ds_put_format(&actions, "outport = %s; output;",
>                                             op->json_key);
> -                            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP,
> -                                          50, ds_cstr(&match),
> -                                          ds_cstr(&actions));
> +                            ovn_lflow_add_with_hint(lflows, op->od,
> +                                                    S_SWITCH_IN_L2_LKUP, 50,
> +                                                    ds_cstr(&match),
> +                                                    ds_cstr(&actions),
> +                                                    &op->nbsp->header_);
>                           }
>                       }
>                   }
> @@ -6544,7 +6636,8 @@ get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
>   static void
>   build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
>                             struct hmap *ports,
> -                          const struct nbrec_logical_router_policy *rule)
> +                          const struct nbrec_logical_router_policy *rule,
> +                          const struct ovsdb_idl_row *stage_hint)
>   {
>       struct ds match = DS_EMPTY_INITIALIZER;
>       struct ds actions = DS_EMPTY_INITIALIZER;
> @@ -6584,8 +6677,9 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
>           ds_put_cstr(&actions, "next;");
>       }
>       ds_put_format(&match, "%s", rule->match);
> -    ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +
> +    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>       ds_destroy(&match);
>       ds_destroy(&actions);
>   }
> @@ -6654,8 +6748,9 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
>                         REGBIT_NAT_REDIRECT" = 0; next;",
>                         op->od->l3dgw_port->json_key,
>                         nat->external_mac);
> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
> -                      ds_cstr(&match), ds_cstr(&actions));
> +        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
> +                                ds_cstr(&match), ds_cstr(&actions),
> +                                &nat->header_);
>           ds_clear(&match);
>           ds_clear(&actions);
>   
> @@ -6702,8 +6797,9 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
>                             family == AF_INET ? "4" : "6",
>                             family == AF_INET ? "" : "xx",
>                             nat->external_ip);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING,
> +                                    400, ds_cstr(&match), ds_cstr(&actions),
> +                                    &nat2->header_);
>               ds_clear(&match);
>               ds_clear(&actions);
>           }
> @@ -6715,7 +6811,8 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
>   static void
>   add_route(struct hmap *lflows, const struct ovn_port *op,
>             const char *lrp_addr_s, const char *network_s, int plen,
> -          const char *gateway, const char *policy)
> +          const char *gateway, const char *policy,
> +          const struct ovsdb_idl_row *stage_hint)
>   {
>       bool is_ipv4 = strchr(network_s, '.') ? true : false;
>       struct ds match = DS_EMPTY_INITIALIZER;
> @@ -6762,8 +6859,8 @@ add_route(struct hmap *lflows, const struct ovn_port *op,
>   
>       /* The priority here is calculated to implement longest-prefix-match
>        * routing. */
> -    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
> -                  ds_cstr(&match), ds_cstr(&actions));
> +    ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
> +                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
>       ds_destroy(&match);
>       ds_destroy(&actions);
>   }
> @@ -6771,7 +6868,8 @@ add_route(struct hmap *lflows, const struct ovn_port *op,
>   static void
>   build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
>                           struct hmap *ports,
> -                        const struct nbrec_logical_router_static_route *route)
> +                        const struct nbrec_logical_router_static_route *route,
> +                        const struct ovsdb_idl_row *stage_hint)
>   {
>       ovs_be32 nexthop;
>       const char *lrp_addr_s = NULL;
> @@ -6894,7 +6992,7 @@ build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
>   
>       char *policy = route->policy ? route->policy : "dst-ip";
>       add_route(lflows, out_port, lrp_addr_s, prefix_s, plen, route->nexthop,
> -              policy);
> +              policy, stage_hint);
>   
>   free_prefix_s:
>       free(prefix_s);
> @@ -6977,35 +7075,38 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
>                      const char *lb_force_snat_ip, struct smap_node *lb_info,
>                      bool is_udp, int addr_family, char *ip_addr,
>                      uint16_t l4_port, struct nbrec_load_balancer *lb,
> -                   struct shash *meter_groups)
> +                   struct shash *meter_groups,
> +                   const struct ovsdb_idl_row *stage_hint)
>   {
>       char *backend_ips = lb_info->value;
>   
>       build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
>                                 l4_port, addr_family, S_ROUTER_IN_DNAT,
> -                              meter_groups);
> +                              meter_groups, stage_hint);
>   
>       /* A match and actions for new connections. */
>       char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
>       if (lb_force_snat_ip) {
>           char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s",
>                                         ds_cstr(actions));
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
> -                      new_actions);
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                new_match, new_actions, stage_hint);
>           free(new_actions);
>       } else {
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
> -                      ds_cstr(actions));
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                new_match, ds_cstr(actions), stage_hint);
>       }
>   
>       /* A match and actions for established connections. */
>       char *est_match = xasprintf("ct.est && %s", ds_cstr(match));
>       if (lb_force_snat_ip) {
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
> -                      "flags.force_snat_for_lb = 1; ct_dnat;");
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                est_match,
> +                                "flags.force_snat_for_lb = 1; ct_dnat;",
> +                                stage_hint);
>       } else {
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
> -                      "ct_dnat;");
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                est_match, "ct_dnat;", stage_hint);
>       }
>   
>       free(new_match);
> @@ -7068,12 +7169,14 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
>                    "is_chassis_resident(%s)", od->l3dgw_port->json_key,
>                    od->l3redirect_port->json_key);
>       if (lb_force_snat_ip) {
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> -                      ds_cstr(&undnat_match),
> -                      "flags.force_snat_for_lb = 1; ct_dnat;");
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> +                                ds_cstr(&undnat_match),
> +                                "flags.force_snat_for_lb = 1; ct_dnat;",
> +                                stage_hint);
>       } else {
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> -                      ds_cstr(&undnat_match), "ct_dnat;");
> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> +                                ds_cstr(&undnat_match), "ct_dnat;",
> +                                stage_hint);
>       }
>   
>       ds_destroy(&undnat_match);
> @@ -7219,8 +7322,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>           ds_clear(&match);
>           ds_put_format(&match, "eth.mcast && inport == %s", op->json_key);
> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
> -                      ds_cstr(&match), "next;");
> +        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
> +                                ds_cstr(&match), "next;", &op->nbrp->header_);
>   
>           ds_clear(&match);
>           ds_put_format(&match, "eth.dst == %s && inport == %s",
> @@ -7232,8 +7335,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               ds_put_format(&match, " && is_chassis_resident(%s)",
>                             op->od->l3redirect_port->json_key);
>           }
> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
> -                      ds_cstr(&match), "next;");
> +        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
> +                                ds_cstr(&match), "next;", &op->nbrp->header_);
>       }
>   
>       /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
> @@ -7318,10 +7421,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                   ds_put_format(&match, " && is_chassis_resident(%s)",
>                                 op->od->l3redirect_port->json_key);
>               }
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
> -                          ds_cstr(&match),
> -                          REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
> -                          "lookup_arp(inport, arp.spa, arp.sha); next;");
> +            ovn_lflow_add_with_hint(lflows, op->od,
> +                                    S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
> +                                    ds_cstr(&match),
> +                                    REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
> +                                    "lookup_arp(inport, arp.spa, arp.sha); "
> +                                    "next;", &op->nbrp->header_);
>           }
>       }
>   
> @@ -7391,8 +7496,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               ds_put_cstr(&match, "ip4.src == ");
>               op_put_v4_networks(&match, op, true);
>               ds_put_cstr(&match, " && "REGBIT_EGRESS_LOOPBACK" == 0");
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
> -                          ds_cstr(&match), "drop;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
> +                                    ds_cstr(&match), "drop;",
> +                                    &op->nbrp->header_);
>   
>               /* ICMP echo reply.  These flows reply to ICMP echo requests
>                * received for the router's IP address. Since packets only
> @@ -7411,8 +7517,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                   "icmp4.type = 0; "
>                   "flags.loopback = 1; "
>                   "next; ");
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>   
>           /* ICMP time exceeded */
> @@ -7433,8 +7540,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                             "ip.ttl = 255; "
>                             "next; };",
>                             op->lrp_networks.ipv4_addrs[i].addr_s);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>   
>           /* ARP reply.  These flows reply to ARP requests for the router's own
> @@ -7495,8 +7603,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                   op->lrp_networks.ea_s,
>                   op->lrp_networks.ipv4_addrs[i].addr_s,
>                   op->json_key);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>   
>           /* A set to hold all load-balancer vips that need ARP responses. */
> @@ -7740,8 +7849,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       IP_ARGS(ip),
>                       op->json_key);
>               }
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &nat->header_);
>           }
>   
>           if (!smap_get(&op->od->nbr->options, "chassis")
> @@ -7759,8 +7870,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                        "icmp4.type = 3; "
>                                        "icmp4.code = 3; "
>                                        "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
> -                              ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        80, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>   
>                   ds_clear(&match);
>                   ds_put_format(&match,
> @@ -7770,8 +7882,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                            "eth.dst <-> eth.src; "
>                            "ip4.dst <-> ip4.src; "
>                            "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
> -                              ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        80, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>   
>                   ds_clear(&match);
>                   ds_put_format(&match,
> @@ -7784,8 +7897,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                            "icmp4.type = 3; "
>                            "icmp4.code = 2; "
>                            "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
> -                              ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        70, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>               }
>           }
>   
> @@ -7844,8 +7958,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>           if (has_drop_ips) {
>               /* Drop IP traffic to this router. */
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
> -                          ds_cstr(&match), "drop;");
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
> +                                    ds_cstr(&match), "drop;",
> +                                    &op->nbrp->header_);
>           }
>   
>           free(snat_ips);
> @@ -7878,8 +7993,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                           "icmp6.type = 129; "
>                           "flags.loopback = 1; "
>                           "next; ");
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>   
>           /* ND reply.  These flows reply to ND solicitations for the
> @@ -7919,8 +8035,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                             op->lrp_networks.ipv6_addrs[i].addr_s,
>                             op->lrp_networks.ipv6_addrs[i].addr_s,
>                             op->lrp_networks.ea_s);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>   
>           /* UDP/TCP port unreachable */
> @@ -7935,8 +8052,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                        "eth.dst <-> eth.src; "
>                                        "ip6.dst <-> ip6.src; "
>                                        "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
> -                          ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        80, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>   
>                   ds_clear(&match);
>                   ds_put_format(&match,
> @@ -7949,8 +8067,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                            "icmp6.type = 1; "
>                            "icmp6.code = 4; "
>                            "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
> -                              ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        80, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>   
>                   ds_clear(&match);
>                   ds_put_format(&match,
> @@ -7963,8 +8082,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                            "icmp6.type = 1; "
>                            "icmp6.code = 3; "
>                            "next; };";
> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
> -                              ds_cstr(&match), action);
> +                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                        70, ds_cstr(&match), action,
> +                                        &op->nbrp->header_);
>               }
>           }
>   
> @@ -7995,8 +8115,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                             "icmp6.code = 0; /* TTL exceeded in transit */ "
>                             "next; };",
>                             op->lrp_networks.ipv6_addrs[i].addr_s);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>       }
>   
> @@ -8135,8 +8256,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                          ds_put_cstr(&actions, "ct_snat;");
>                       }
>   
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
> -                                  ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
> +                                            90, ds_cstr(&match),
> +                                            ds_cstr(&actions),
> +                                            &nat->header_);
>                   } else {
>                       /* Distributed router. */
>   
> @@ -8162,8 +8285,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                           ds_put_cstr(&actions, "ct_snat;");
>                       }
>   
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
> -                                  ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
> +                                            100, ds_cstr(&match),
> +                                            ds_cstr(&actions),
> +                                            &nat->header_);
>   
>                       /* Traffic received on other router ports must be
>                        * redirected to the central instance of the l3dgw_port
> @@ -8172,9 +8297,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_put_format(&match, "ip && ip%s.dst == %s",
>                                     is_v6 ? "6" : "4",
>                                     nat->external_ip);
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
> -                                  ds_cstr(&match),
> -                                  REGBIT_NAT_REDIRECT" = 1; next;");
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
> +                                            50, ds_cstr(&match),
> +                                            REGBIT_NAT_REDIRECT" = 1; next;",
> +                                            &nat->header_);
>                   }
>               }
>   
> @@ -8210,8 +8336,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                         "ct_dnat(%s);", nat->logical_ip);
>                       }
>   
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
> -                                  ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &nat->header_);
>                   } else {
>                       /* Distributed router. */
>   
> @@ -8238,8 +8365,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                         nat->logical_ip);
>                       }
>   
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
> -                                  ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &nat->header_);
>   
>                       /* Traffic received on other router ports must be
>                        * redirected to the central instance of the l3dgw_port
> @@ -8248,9 +8376,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_put_format(&match, "ip && ip%s.dst == %s",
>                                     is_v6 ? "6" : "4",
>                                     nat->external_ip);
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
> -                                  ds_cstr(&match),
> -                                  REGBIT_NAT_REDIRECT" = 1; next;");
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 50,
> +                                            ds_cstr(&match),
> +                                            REGBIT_NAT_REDIRECT" = 1; next;",
> +                                            &nat->header_);
>                   }
>               }
>   
> @@ -8289,8 +8418,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_put_format(&actions, "ct_dnat;");
>                   }
>   
> -                ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &nat->header_);
>               }
>   
>               /* Egress SNAT table: Packets enter the egress pipeline with
> @@ -8317,9 +8447,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       /* The priority here is calculated such that the
>                        * nat->logical_ip with the longest mask gets a higher
>                        * priority. */
> -                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
> -                                  count_1bits(ntohl(mask)) + 1,
> -                                  ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_SNAT,
> +                                            count_1bits(ntohl(mask)) + 1,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &nat->header_);
>                   } else {
>                       uint16_t priority = count_1bits(ntohl(mask)) + 1;
>   
> @@ -8354,9 +8485,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       /* The priority here is calculated such that the
>                        * nat->logical_ip with the longest mask gets a higher
>                        * priority. */
> -                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
> -                                  priority, ds_cstr(&match),
> -                                  ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_SNAT,
> +                                            priority, ds_cstr(&match),
> +                                            ds_cstr(&actions),
> +                                            &nat->header_);
>                   }
>               }
>   
> @@ -8373,8 +8505,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                 ETH_ADDR_ARGS(mac),
>                                 od->l3dgw_port->json_key,
>                                 nat->logical_port);
> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 50,
> -                              ds_cstr(&match), "next;");
> +                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ADMISSION, 50,
> +                                        ds_cstr(&match), "next;",
> +                                        &nat->header_);
>               }
>   
>               /* Ingress Gateway Redirect Table: For NAT on a distributed
> @@ -8387,8 +8520,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                 is_v6 ? "6" : "4",
>                                 nat->logical_ip,
>                                 od->l3dgw_port->json_key);
> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
> -                              ds_cstr(&match), "next;");
> +                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
> +                                        100, ds_cstr(&match), "next;",
> +                                        &nat->header_);
>               }
>   
>               /* Egress Loopback table: For NAT on a distributed router.
> @@ -8422,16 +8556,21 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                                         "flags = 0; flags.loopback = 1; "
>                                         REGBIT_EGRESS_LOOPBACK" = 1; "
>                                         "next(pipeline=ingress, table=0); ");
> -                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300,
> -                                      ds_cstr(&match),  ds_cstr(&actions));
> +                        ovn_lflow_add_with_hint(lflows, od,
> +                                                S_ROUTER_OUT_EGR_LOOP, 300,
> +                                                ds_cstr(&match),
> +                                                ds_cstr(&actions),
> +                                                &nat2->header_);
>   
>                           ds_clear(&match);
>                           ds_put_format(&match,
>                                         "ip%s.src == %s && ip%s.dst == %s",
>                                         is_v6 ? "6" : "4", nat2->external_ip,
>                                         is_v6 ? "6" : "4", nat->external_ip);
> -                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
> -                                      ds_cstr(&match), "next;");
> +                        ovn_lflow_add_with_hint(lflows, od,
> +                                                S_ROUTER_OUT_EGR_LOOP, 200,
> +                                                ds_cstr(&match), "next;",
> +                                                &nat2->header_);
>                           ds_clear(&match);
>                       }
>                   }
> @@ -8451,8 +8590,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                   }
>                   ds_put_format(&actions, REGBIT_EGRESS_LOOPBACK" = 1; "
>                                 "next(pipeline=ingress, table=0); };");
> -                ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
> +                                        ds_cstr(&match), ds_cstr(&actions),
> +                                        &nat->header_);
>               }
>           }
>   
> @@ -8591,8 +8731,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                           ds_put_format(&match, "ip && ip6.dst == %s",
>                                         ip_address);
>                       }
> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
> -                                  100, ds_cstr(&match), "ct_next;");
> +                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG,
> +                                            100, ds_cstr(&match), "ct_next;",
> +                                            &lb->header_);
>                   }
>   
>                   /* Higher priority rules are added for load-balancing in DNAT
> @@ -8633,7 +8774,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                   add_router_lb_flow(lflows, od, &match, &actions, prio,
>                                      lb_force_snat_ip, node, is_udp,
>                                      addr_family, ip_address, port, lb,
> -                                   meter_groups);
> +                                   meter_groups,
> +                                   &lb->header_);
>   
>                   free(ip_address);
>               }
> @@ -8706,8 +8848,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>           if (add_rs_response_flow) {
>               ds_put_cstr(&actions, "); next;");
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, 50,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS,
> +                                    50, ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>               ds_clear(&actions);
>               ds_clear(&match);
>               ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && "
> @@ -8723,8 +8866,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                             "outport = inport; flags.loopback = 1; "
>                             "output;",
>                             op->lrp_networks.ea_s, ip6_str);
> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_RESPONSE, 50,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, op->od,
> +                                    S_ROUTER_IN_ND_RA_RESPONSE, 50,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &op->nbrp->header_);
>           }
>       }
>   
> @@ -8758,13 +8903,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>           for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>               add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
>                         op->lrp_networks.ipv4_addrs[i].network_s,
> -                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL);
> +                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL,
> +                      &op->nbrp->header_);
>           }
>   
>           for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
>               add_route(lflows, op, op->lrp_networks.ipv6_addrs[i].addr_s,
>                         op->lrp_networks.ipv6_addrs[i].network_s,
> -                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL);
> +                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL,
> +                      &op->nbrp->header_);
>           }
>       }
>   
> @@ -8778,7 +8925,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               const struct nbrec_logical_router_static_route *route;
>   
>               route = od->nbr->static_routes[i];
> -            build_static_route_flow(lflows, od, ports, route);
> +            build_static_route_flow(lflows, od, ports, route, &route->header_);
>           }
>       }
>   
> @@ -8849,7 +8996,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>           for (int i = 0; i < od->nbr->n_policies; i++) {
>               const struct nbrec_logical_router_policy *rule
>                   = od->nbr->policies[i];
> -            build_routing_policy_flow(lflows, od, ports, rule);
> +            build_routing_policy_flow(lflows, od, ports, rule, &rule->header_);
>           }
>       }
>   
> @@ -8900,8 +9047,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_clear(&actions);
>                       ds_put_format(&actions, "eth.dst = %s; next;",
>                                     op->lrp_networks.ea_s);
> -                    ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
> -                                  100, ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, op->peer->od,
> +                                            S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &op->nbrp->header_);
>                   }
>   
>                   if (op->lrp_networks.n_ipv6_addrs) {
> @@ -8913,8 +9062,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_clear(&actions);
>                       ds_put_format(&actions, "eth.dst = %s; next;",
>                                     op->lrp_networks.ea_s);
> -                    ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
> -                                  100, ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, op->peer->od,
> +                                            S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &op->nbrp->header_);
>                   }
>               }
>   
> @@ -8935,8 +9086,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_clear(&actions);
>                       ds_put_format(&actions, "eth.dst = %s; next;",
>                                     op->lrp_networks.ea_s);
> -                    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ARP_RESOLVE,
> -                                  50, ds_cstr(&match), ds_cstr(&actions));
> +
> +                    ovn_lflow_add_with_hint(lflows, op->od,
> +                                            S_ROUTER_IN_ARP_RESOLVE, 50,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &op->nbrp->header_);
>                   }
>               }
>           } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")
> @@ -8977,9 +9131,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                           ds_clear(&actions);
>                           ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
> -                        ovn_lflow_add(lflows, peer->od,
> -                                      S_ROUTER_IN_ARP_RESOLVE, 100,
> -                                      ds_cstr(&match), ds_cstr(&actions));
> +                        ovn_lflow_add_with_hint(lflows, peer->od,
> +                                                S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                                ds_cstr(&match),
> +                                                ds_cstr(&actions),
> +                                                &op->nbsp->header_);
>                       }
>                   }
>   
> @@ -9011,9 +9167,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                           ds_clear(&actions);
>                           ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
> -                        ovn_lflow_add(lflows, peer->od,
> -                                      S_ROUTER_IN_ARP_RESOLVE, 100,
> -                                      ds_cstr(&match), ds_cstr(&actions));
> +                        ovn_lflow_add_with_hint(lflows, peer->od,
> +                                                S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                                ds_cstr(&match),
> +                                                ds_cstr(&actions),
> +                                                &op->nbsp->header_);
>                       }
>                   }
>               }
> @@ -9061,9 +9219,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                           ds_clear(&actions);
>                           ds_put_format(&actions,
>                                         "eth.dst = 00:00:00:00:00:00; next;");
> -                        ovn_lflow_add(lflows, peer->od,
> -                                        S_ROUTER_IN_ARP_RESOLVE, 100,
> -                                        ds_cstr(&match), ds_cstr(&actions));
> +                        ovn_lflow_add_with_hint(lflows, peer->od,
> +                                                S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                                ds_cstr(&match),
> +                                                ds_cstr(&actions),
> +                                                &op->nbsp->header_);
>                           break;
>                       }
>                   }
> @@ -9104,9 +9264,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>   
>                           ds_clear(&actions);
>                           ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
> -                        ovn_lflow_add(lflows, peer->od,
> -                                        S_ROUTER_IN_ARP_RESOLVE, 100,
> -                                        ds_cstr(&match), ds_cstr(&actions));
> +                        ovn_lflow_add_with_hint(lflows, peer->od,
> +                                                S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                                ds_cstr(&match),
> +                                                ds_cstr(&actions),
> +                                                &op->nbsp->header_);
>                           found_vip_network = true;
>                           break;
>                       }
> @@ -9159,8 +9321,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_clear(&actions);
>                       ds_put_format(&actions, "eth.dst = %s; next;",
>                                                 router_port->lrp_networks.ea_s);
> -                    ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
> -                                  100, ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, peer->od,
> +                                            S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &op->nbsp->header_);
>                   }
>   
>                   if (router_port->lrp_networks.n_ipv6_addrs) {
> @@ -9172,8 +9336,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       ds_clear(&actions);
>                       ds_put_format(&actions, "eth.dst = %s; next;",
>                                     router_port->lrp_networks.ea_s);
> -                    ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
> -                                  100, ds_cstr(&match), ds_cstr(&actions));
> +                    ovn_lflow_add_with_hint(lflows, peer->od,
> +                                            S_ROUTER_IN_ARP_RESOLVE, 100,
> +                                            ds_cstr(&match), ds_cstr(&actions),
> +                                            &op->nbsp->header_);
>                   }
>               }
>           }
> @@ -9234,8 +9400,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               ds_put_format(&actions,
>                             REGBIT_PKT_LARGER" = check_pkt_larger(%d);"
>                             " next;", gw_mtu);
> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &od->l3dgw_port->nbrp->header_);
>   
>               for (size_t i = 0; i < od->nbr->n_ports; i++) {
>                   struct ovn_port *rp = ovn_port_find(ports,
> @@ -9266,8 +9433,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                       rp->lrp_networks.ea_s,
>                       rp->lrp_networks.ipv4_addrs[0].addr_s,
>                       gw_mtu - 18);
> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 50,
> -                              ds_cstr(&match), ds_cstr(&actions));
> +                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_LARGER_PKTS,
> +                                        50, ds_cstr(&match), ds_cstr(&actions),
> +                                        &rp->nbrp->header_);
>               }
>           }
>       }
> @@ -9284,8 +9452,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               continue;
>           }
>           if (od->l3dgw_port && od->l3redirect_port) {
> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
> -                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
> +            const struct ovsdb_idl_row *stage_hint = NULL;
> +
> +            if (od->l3dgw_port->nbrp) {
> +                stage_hint = &od->l3dgw_port->nbrp->header_;
> +            }
> +
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
> +                                    REGBIT_DISTRIBUTED_NAT" == 1", "next;",
> +                                    stage_hint);
>   
>               /* For traffic with outport == l3dgw_port, if the
>                * packet did not match any higher priority redirect
> @@ -9297,8 +9472,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               ds_clear(&actions);
>               ds_put_format(&actions, "outport = %s; next;",
>                             od->l3redirect_port->json_key);
> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
>   
>               /* If the Ethernet destination has not been resolved,
>                * redirect to the central instance of the l3dgw_port.
> @@ -9307,8 +9483,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                * table, before being redirected to the central instance.
>                */
>               ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
>           }
>   
>           /* Packets are allowed by default. */
> @@ -9357,8 +9534,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>                             "output; "
>                             "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
>                             route->nexthop);
> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
> -                          ds_cstr(&match), ds_cstr(&actions));
> +
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    &route->header_);
>           }
>   
>           ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
> @@ -9416,7 +9595,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
>               ds_put_format(&actions, "eth.src = %s; output;",
>                             op->lrp_networks.ea_s);
>               ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 110,
> -                        ds_cstr(&match), ds_cstr(&actions));
> +                          ds_cstr(&match), ds_cstr(&actions));
>           }
>   
>           ds_clear(&match);
> diff --git a/utilities/ovn-detrace.in b/utilities/ovn-detrace.in
> index 3bfb720..e9c3736 100755
> --- a/utilities/ovn-detrace.in
> +++ b/utilities/ovn-detrace.in
> @@ -135,9 +135,74 @@ class CookieHandlerByUUUID(CookieHandler):
>           cookie = cookie.zfill(8)
>           return self._db.find_rows_by_partial_uuid(self._table, cookie)
>   
> +class ACLHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(ACLHintHandler, self).__init__(ovnnb_db, 'ACL')
> +
> +    def print_record(self, acl):
> +        output = 'ACL: %s, priority=%s, ' \
> +                 'match=(%s), %s' % (acl.direction,
> +                                     acl.priority,
> +                                     acl.match.strip('"'),
> +                                     acl.action)
> +        if acl.log:
> +            output += ' (log)'
> +        print_h(output)
> +
> +class LSPHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(LSPHintHandler, self).__init__(ovnnb_db, 'Logical_Switch_Port')
> +
> +    def print_record(self, lsp):
> +        print_h('Logical Switch Port: %s type %s (addresses %s, dynamic addresses %s, security %s' % (
> +                    lsp.name, lsp.type, lsp.addresses, lsp.dynamic_addresses,
> +                    lsp.port_security))
> +
> +class LRPHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(LRPHintHandler, self).__init__(ovnnb_db, 'Logical_Router_Port')
> +
> +    def print_record(self, lrp):
> +        print_h('Logical Router Port: %s mac %s networks %s ipv6_ra_configs %s' % (
> +                    lrp.name, lrp.mac, lrp.networks, lrp.ipv6_ra_configs))
> +
> +class LoadBalancerHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(LoadBalancerHintHandler, self).__init__(ovnnb_db, 'Load_Balancer')
> +
> +    def print_record(self, lb):
> +        print_h('Load Balancer: %s protocol %s vips %s ip_port_mappings %s' % (
> +                    lb.name, lb.protocol, lb.vips, lb.ip_port_mappings))
> +
> +class NATHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(NATHintHandler, self).__init__(ovnnb_db, 'NAT')
> +
> +    def print_record(self, nat):
> +        print_h('NAT: external IP %s external_mac %s logical_ip %s logical_port %s type %s' % (
> +                    nat.external_ip, nat.external_mac, nat.logical_ip,
> +                    nat.logical_port, nat.type))
> +
> +class StaticRouteHintHandler(CookieHandlerByUUUID):
> +    def __init__(self, ovnnb_db):
> +        super(StaticRouteHintHandler, self).__init__(ovnnb_db,
> +                                                     'Logical_Router_Static_Route')
> +
> +    def print_record(self, route):
> +        print_h('Route: %s via %s (port %s), policy=%s' % (
> +                    route.ip_prefix, route.nexthop, route.output_port,
> +                    route.policy))
> +
>   class LogicalFlowHandler(CookieHandlerByUUUID):
> -    def __init__(self, ovnsb_db):
> +    def __init__(self, ovnnb_db, ovnsb_db):
>           super(LogicalFlowHandler, self).__init__(ovnsb_db, 'Logical_Flow')
> +        self._hint_handlers = [
> +            ACLHintHandler(ovnnb_db),
> +            LSPHintHandler(ovnnb_db),
> +            LRPHintHandler(ovnnb_db),
> +            LoadBalancerHintHandler(ovnnb_db),
> +            NATHintHandler(ovnnb_db)
> +        ]
>   
>       def print_record(self, lflow):
>           print_p('Logical datapath: %s [%s]' %
> @@ -151,24 +216,14 @@ class LogicalFlowHandler(CookieHandlerByUUUID):
>   
>       def print_hint(self, lflow, ovnnb_db):
>           external_ids = lflow.external_ids
> -        if external_ids.get('stage-name') in ['ls_in_acl',
> -                                              'ls_out_acl']:
> -            acl_hint = external_ids.get('stage-hint')
> -            if not acl_hint:
> -                return
> -            for i, acl in enumerate(
> -                    ovnnb_db.find_rows_by_partial_uuid('ACL', acl_hint)):
> +        hint = external_ids.get('stage-hint')
> +        if not hint:
> +            return
> +        for handler in self._hint_handlers:
> +            for i, record in enumerate(handler.get_records(hint)):
>                   if i > 0:
> -                    print_h('[Duplicate uuid ACL hint]')
> -
> -                output = 'ACL: %s, priority=%s, ' \
> -                        'match=(%s), %s' % (acl.direction,
> -                                            acl.priority,
> -                                            acl.match.strip('"'),
> -                                            acl.action)
> -                if acl.log:
> -                    output += ' (log)'
> -                print_h(output)
> +                    print_h('[Duplicate uuid hint]')
> +                handler.print_record(record)
>   
>   class PortBindingHandler(CookieHandlerByUUUID):
>       def __init__(self, ovnsb_db):
> @@ -283,7 +338,7 @@ def main():
>       ovsdb_ovnnb = OVSDB(ovnnb_db, 'OVN_Northbound')
>   
>       cookie_handlers = [
> -        LogicalFlowHandler(ovsdb_ovnsb),
> +        LogicalFlowHandler(ovsdb_ovnnb, ovsdb_ovnsb),
>           PortBindingHandler(ovsdb_ovnsb),
>           MacBindingHandler(ovsdb_ovnsb),
>           MulticastGroupHandler(ovsdb_ovnsb),
>
Dumitru Ceara Jan. 31, 2020, 11:43 a.m. UTC | #2
On 1/21/20 10:41 PM, Mark Michelson wrote:
> Hi Dumitru,
> 
> It appears that some of the stage hints correspond to DHCPv4_Options and
> DHCPv6_Options, but these are not handled in ovn-detrace.

Hi Mark,

Thanks for reviewing this. My bad, I had missed a few tables indeed (QoS
and Logical_Router_Policy too).

I just sent a v2 rebased on current master where I updated ovn-detrace
to handle these tables too. I also updated some more ovn_lflow_add()
calls which were introduced in the meantime in ovn-northd so that they
properly set the hint.

https://patchwork.ozlabs.org/patch/1231858/

Regards,
Dumitru

> 
> On 1/17/20 10:44 AM, Dumitru Ceara wrote:
>> Until now the 'stage-hint' external-id was set only for logical flows
>> installed for ACLs. In order to simplify troubleshooting, extend the
>> approach and apply whenever possible. Set stage-hint for logical flows
>> generated by the following NB tables too:
>> - Logical_Switch_Port
>> - Logical_Router_Port
>> - Load_Balancer
>> - NAT
>>
>> Also update ovn-detrace such that whenever stage-hints are available,
>> all the tables mentioned above are queried and if the stage-hint matches
>> a NB uuid, relevant information is dumped.
>>
>> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
>> ---
>>   northd/ovn-northd.c      | 721
>> +++++++++++++++++++++++++++++------------------
>>   utilities/ovn-detrace.in |  93 ++++--
>>   2 files changed, 524 insertions(+), 290 deletions(-)
>>
>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
>> index b6dc809..20b8429 100644
>> --- a/northd/ovn-northd.c
>> +++ b/northd/ovn-northd.c
>> @@ -3714,6 +3714,15 @@ ovn_lflow_hash(const struct ovn_lflow *lflow)
>>                                    lflow->actions);
>>   }
>>   +static char *
>> +ovn_lflow_hint(const struct ovsdb_idl_row *row)
>> +{
>> +    if (!row) {
>> +        return NULL;
>> +    }
>> +    return xasprintf("%08x", row->uuid.parts[0]);
>> +}
>> +
>>   static bool
>>   ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
>>   {
>> @@ -3744,14 +3753,14 @@ static void
>>   ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>>                    enum ovn_stage stage, uint16_t priority,
>>                    const char *match, const char *actions,
>> -                 const char *stage_hint, const char *where)
>> +                 const struct ovsdb_idl_row *stage_hint, const char
>> *where)
>>   {
>>       ovs_assert(ovn_stage_to_datapath_type(stage) ==
>> ovn_datapath_get_type(od));
>>         struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
>>       ovn_lflow_init(lflow, od, stage, priority,
>>                      xstrdup(match), xstrdup(actions),
>> -                   nullable_xstrdup(stage_hint), where);
>> +                   ovn_lflow_hint(stage_hint), where);
>>       hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
>>   }
>>   @@ -3902,7 +3911,8 @@ build_port_security_ipv6_flow(
>>    *   - Priority 80 flow to drop ARP and IPv6 ND packets.
>>    */
>>   static void
>> -build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
>> +build_port_security_nd(struct ovn_port *op, struct hmap *lflows,
>> +                       const struct ovsdb_idl_row *stage_hint)
>>   {
>>       struct ds match = DS_EMPTY_INITIALIZER;
>>   @@ -3938,8 +3948,8 @@ build_port_security_nd(struct ovn_port *op,
>> struct hmap *lflows)
>>                   ds_chomp(&match, ',');
>>                   ds_put_cstr(&match, "}");
>>               }
>> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
>> -                          ds_cstr(&match), "next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_PORT_SEC_ND,
>> +                                    90, ds_cstr(&match), "next;",
>> stage_hint);
>>           }
>>             if (ps->n_ipv6_addrs || no_ip) {
>> @@ -3948,15 +3958,15 @@ build_port_security_nd(struct ovn_port *op,
>> struct hmap *lflows)
>>                             op->json_key, ps->ea_s);
>>               build_port_security_ipv6_nd_flow(&match, ps->ea,
>> ps->ipv6_addrs,
>>                                                ps->n_ipv6_addrs);
>> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
>> -                          ds_cstr(&match), "next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_PORT_SEC_ND,
>> +                                    90, ds_cstr(&match), "next;",
>> stage_hint);
>>           }
>>       }
>>         ds_clear(&match);
>>       ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
>> -    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
>> -                  ds_cstr(&match), "drop;");
>> +    ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
>> +                            ds_cstr(&match), "drop;", stage_hint);
>>       ds_destroy(&match);
>>   }
>>   @@ -3977,7 +3987,8 @@ build_port_security_nd(struct ovn_port *op,
>> struct hmap *lflows)
>>    */
>>   static void
>>   build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
>> -                       struct hmap *lflows)
>> +                       struct hmap *lflows,
>> +                       const struct ovsdb_idl_row *stage_hint)
>>   {
>>       char *port_direction;
>>       enum ovn_stage stage;
>> @@ -4007,8 +4018,9 @@ build_port_security_ip(enum ovn_pipeline
>> pipeline, struct ovn_port *op,
>>                                 " && ip4.dst == 255.255.255.255"
>>                                 " && udp.src == 68 && udp.dst == 67",
>>                                 op->json_key, ps->ea_s);
>> -                ovn_lflow_add(lflows, op->od, stage, 90,
>> -                              ds_cstr(&dhcp_match), "next;");
>> +                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
>> +                                        ds_cstr(&dhcp_match), "next;",
>> +                                        stage_hint);
>>                   ds_destroy(&dhcp_match);
>>                   ds_put_format(&match, "inport == %s && eth.src == %s"
>>                                 " && ip4.src == {", op->json_key,
>> @@ -4047,7 +4059,9 @@ build_port_security_ip(enum ovn_pipeline
>> pipeline, struct ovn_port *op,
>>               ds_chomp(&match, ' ');
>>               ds_chomp(&match, ',');
>>               ds_put_cstr(&match, "}");
>> -            ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match),
>> "next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
>> +                                    ds_cstr(&match), "next;",
>> +                                    stage_hint);
>>               ds_destroy(&match);
>>           }
>>   @@ -4063,8 +4077,9 @@ build_port_security_ip(enum ovn_pipeline
>> pipeline, struct ovn_port *op,
>>                                 " && ip6.dst == ff02::/16"
>>                                 " && icmp6.type == {131, 135, 143}",
>> op->json_key,
>>                                 ps->ea_s);
>> -                ovn_lflow_add(lflows, op->od, stage, 90,
>> -                              ds_cstr(&dad_match), "next;");
>> +                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
>> +                                        ds_cstr(&dad_match), "next;",
>> +                                        stage_hint);
>>                   ds_destroy(&dad_match);
>>               }
>>               ds_put_format(&match, "%s == %s && %s == %s",
>> @@ -4072,8 +4087,9 @@ build_port_security_ip(enum ovn_pipeline
>> pipeline, struct ovn_port *op,
>>                             pipeline == P_IN ? "eth.src" : "eth.dst",
>> ps->ea_s);
>>               build_port_security_ipv6_flow(pipeline, &match, ps->ea,
>>                                             ps->ipv6_addrs,
>> ps->n_ipv6_addrs);
>> -            ovn_lflow_add(lflows, op->od, stage, 90,
>> -                          ds_cstr(&match), "next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
>> +                                    ds_cstr(&match), "next;",
>> +                                    stage_hint);
>>               ds_destroy(&match);
>>           }
>>   @@ -4081,7 +4097,8 @@ build_port_security_ip(enum ovn_pipeline
>> pipeline, struct ovn_port *op,
>>                                   port_direction, op->json_key,
>>                                   pipeline == P_IN ? "eth.src" :
>> "eth.dst",
>>                                   ps->ea_s);
>> -        ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
>> +        ovn_lflow_add_with_hint(lflows, op->od, stage, 80, match,
>> "drop;",
>> +                                stage_hint);
>>           free(match);
>>       }
>>   @@ -4371,12 +4388,13 @@ build_lswitch_input_port_sec(struct hmap
>> *ports, struct hmap *datapaths,
>>               ds_put_format(&actions, "set_queue(%s); ", queue_id);
>>           }
>>           ds_put_cstr(&actions, "next;");
>> -        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
>> -                      ds_cstr(&match), ds_cstr(&actions));
>> +        ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_PORT_SEC_L2, 50,
>> +                                ds_cstr(&match), ds_cstr(&actions),
>> +                                &op->nbsp->header_);
>>             if (op->nbsp->n_port_security) {
>> -            build_port_security_ip(P_IN, op, lflows);
>> -            build_port_security_nd(op, lflows);
>> +            build_port_security_ip(P_IN, op, lflows,
>> &op->nbsp->header_);
>> +            build_port_security_nd(op, lflows, &op->nbsp->header_);
>>           }
>>       }
>>   @@ -4436,15 +4454,17 @@ build_lswitch_output_port_sec(struct hmap
>> *ports, struct hmap *datapaths,
>>                   }
>>               }
>>               ds_put_cstr(&actions, "output;");
>> -            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_OUT_PORT_SEC_L2,
>> +                                    50, ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                    &op->nbsp->header_);
>>           } else {
>> -            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
>> -                          ds_cstr(&match), "drop;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_OUT_PORT_SEC_L2,
>> +                                    150, ds_cstr(&match), "drop;",
>> +                                    &op->nbsp->header_);
>>           }
>>             if (op->nbsp->n_port_security) {
>> -            build_port_security_ip(P_OUT, op, lflows);
>> +            build_port_security_ip(P_OUT, op, lflows,
>> &op->nbsp->header_);
>>           }
>>       }
>>   @@ -4499,10 +4519,12 @@ build_pre_acls(struct ovn_datapath *od,
>> struct hmap *lflows)
>>                 ds_put_format(&match_in, "ip && inport == %s",
>> op->json_key);
>>               ds_put_format(&match_out, "ip && outport == %s",
>> op->json_key);
>> -            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
>> -                          ds_cstr(&match_in), "next;");
>> -            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
>> -                          ds_cstr(&match_out), "next;");
>> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL,
>> 110,
>> +                                    ds_cstr(&match_in), "next;",
>> +                                    &op->nbsp->header_);
>> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL,
>> 110,
>> +                                    ds_cstr(&match_out), "next;",
>> +                                    &op->nbsp->header_);
>>                 ds_destroy(&match_in);
>>               ds_destroy(&match_out);
>> @@ -4515,10 +4537,12 @@ build_pre_acls(struct ovn_datapath *od, struct
>> hmap *lflows)
>>                             od->localnet_port->json_key);
>>               ds_put_format(&match_out, "ip && outport == %s",
>>                             od->localnet_port->json_key);
>> -            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
>> -                          ds_cstr(&match_in), "next;");
>> -            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
>> -                          ds_cstr(&match_out), "next;");
>> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL,
>> 110,
>> +                                    ds_cstr(&match_in), "next;",
>> +                                    &od->localnet_port->nbsp->header_);
>> +            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL,
>> 110,
>> +                                    ds_cstr(&match_out), "next;",
>> +                                    &od->localnet_port->nbsp->header_);
>>                 ds_destroy(&match_in);
>>               ds_destroy(&match_out);
>> @@ -4596,7 +4620,8 @@ static void
>>   build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
>>                             struct smap_node *node, char *ip_address,
>>                             struct nbrec_load_balancer *lb, uint16_t
>> port,
>> -                          int addr_family, int pl, struct shash
>> *meter_groups)
>> +                          int addr_family, int pl, struct shash
>> *meter_groups,
>> +                          const struct ovsdb_idl_row *stage_hint)
>>   {
>>       if (!controller_event_en || node->value[0]) {
>>           return;
>> @@ -4627,7 +4652,8 @@ build_empty_lb_event_flow(struct ovn_datapath
>> *od, struct hmap *lflows,
>>                          event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
>>                          meter, node->key, lb->protocol,
>>                          UUID_ARGS(&lb->header_.uuid));
>> -    ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
>> +    ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(&match),
>> action,
>> +                            stage_hint);
>>       ds_destroy(&match);
>>       free(action);
>>   }
>> @@ -4680,7 +4706,7 @@ build_pre_lb(struct ovn_datapath *od, struct
>> hmap *lflows,
>>                 build_empty_lb_event_flow(od, lflows, node,
>> ip_address, lb,
>>                                         port, addr_family,
>> S_SWITCH_IN_PRE_LB,
>> -                                      meter_groups);
>> +                                      meter_groups, &lb->header_);
>>                 free(ip_address);
>>   @@ -4772,7 +4798,8 @@ build_acl_log(struct ds *actions, const struct
>> nbrec_acl *acl)
>>   static void
>>   build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>>                          enum ovn_stage stage, struct nbrec_acl *acl,
>> -                       struct ds *extra_match, struct ds *extra_actions)
>> +                       struct ds *extra_match, struct ds *extra_actions,
>> +                       const struct ovsdb_idl_row *stage_hint)
>>   {
>>       struct ds match = DS_EMPTY_INITIALIZER;
>>       struct ds actions = DS_EMPTY_INITIALIZER;
>> @@ -4788,8 +4815,9 @@ build_reject_acl_rules(struct ovn_datapath *od,
>> struct hmap *lflows,
>>                     "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
>>                     "tcp_reset { outport <-> inport; %s };",
>>                     ingress ? "output;" :
>> "next(pipeline=ingress,table=0);");
>> -    ovn_lflow_add(lflows, od, stage, acl->priority +
>> OVN_ACL_PRI_OFFSET + 10,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, od, stage,
>> +                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>       ds_clear(&match);
>>       ds_clear(&actions);
>>       build_acl_log(&actions, acl);
>> @@ -4801,8 +4829,9 @@ build_reject_acl_rules(struct ovn_datapath *od,
>> struct hmap *lflows,
>>                     "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
>>                     "tcp_reset { outport <-> inport; %s };",
>>                     ingress ? "output;" :
>> "next(pipeline=ingress,table=0);");
>> -    ovn_lflow_add(lflows, od, stage, acl->priority +
>> OVN_ACL_PRI_OFFSET + 10,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, od, stage,
>> +                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>         /* IP traffic */
>>       ds_clear(&match);
>> @@ -4819,8 +4848,9 @@ build_reject_acl_rules(struct ovn_datapath *od,
>> struct hmap *lflows,
>>                     "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
>>                     "icmp4 { outport <-> inport; %s };",
>>                     ingress ? "output;" :
>> "next(pipeline=ingress,table=0);");
>> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, od, stage,
>> +                            acl->priority + OVN_ACL_PRI_OFFSET,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>       ds_clear(&match);
>>       ds_clear(&actions);
>>       build_acl_log(&actions, acl);
>> @@ -4835,8 +4865,9 @@ build_reject_acl_rules(struct ovn_datapath *od,
>> struct hmap *lflows,
>>                     "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
>>                     "outport <-> inport; %s };",
>>                     ingress ? "output;" :
>> "next(pipeline=ingress,table=0);");
>> -    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, od, stage,
>> +                            acl->priority + OVN_ACL_PRI_OFFSET,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>         ds_destroy(&match);
>>       ds_destroy(&actions);
>> @@ -4849,7 +4880,6 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>       bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
>>       enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL :
>> S_SWITCH_OUT_ACL;
>>   -    char *stage_hint = xasprintf("%08x", acl->header_.uuid.parts[0]);
>>       if (!strcmp(acl->action, "allow")
>>           || !strcmp(acl->action, "allow-related")) {
>>           /* If there are any stateful flows, we must even commit "allow"
>> @@ -4864,7 +4894,7 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>>                                       acl->match, ds_cstr(&actions),
>> -                                    stage_hint);
>> +                                    &acl->header_);
>>               ds_destroy(&actions);
>>           } else {
>>               struct ds match = DS_EMPTY_INITIALIZER;
>> @@ -4893,7 +4923,7 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>>                                       ds_cstr(&match),
>>                                       ds_cstr(&actions),
>> -                                    stage_hint);
>> +                                    &acl->header_);
>>                 /* Match on traffic in the request direction for an
>> established
>>                * connection tracking entry that has not been marked for
>> @@ -4913,7 +4943,7 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>>                                       ds_cstr(&match), ds_cstr(&actions),
>> -                                    stage_hint);
>> +                                    &acl->header_);
>>                 ds_destroy(&match);
>>               ds_destroy(&actions);
>> @@ -4935,14 +4965,15 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                           " || (ct.est && ct_label.blocked == 1))");
>>               if (!strcmp(acl->action, "reject")) {
>>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
>> -                                       &actions);
>> +                                       &actions, &acl->header_);
>>               } else {
>>                   ds_put_format(&match, " && (%s)", acl->match);
>>                   build_acl_log(&actions, acl);
>>                   ds_put_cstr(&actions, "/* drop */");
>> -                ovn_lflow_add(lflows, od, stage,
>> -                              acl->priority + OVN_ACL_PRI_OFFSET,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od, stage,
>> +                                        acl->priority +
>> OVN_ACL_PRI_OFFSET,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &acl->header_);
>>               }
>>               /* For an existing connection without ct_label set, we've
>>                * encountered a policy change. ACLs previously allowed
>> @@ -4961,14 +4992,15 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>               ds_put_cstr(&actions, "ct_commit(ct_label=1/1); ");
>>               if (!strcmp(acl->action, "reject")) {
>>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
>> -                                       &actions);
>> +                                       &actions, &acl->header_);
>>               } else {
>>                   ds_put_format(&match, " && (%s)", acl->match);
>>                   build_acl_log(&actions, acl);
>>                   ds_put_cstr(&actions, "/* drop */");
>> -                ovn_lflow_add(lflows, od, stage,
>> -                              acl->priority + OVN_ACL_PRI_OFFSET,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od, stage,
>> +                                        acl->priority +
>> OVN_ACL_PRI_OFFSET,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &acl->header_);
>>               }
>>           } else {
>>               /* There are no stateful ACLs in use on this datapath,
>> @@ -4976,19 +5008,19 @@ consider_acl(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                * logical flow action in all cases. */
>>               if (!strcmp(acl->action, "reject")) {
>>                   build_reject_acl_rules(od, lflows, stage, acl, &match,
>> -                                       &actions);
>> +                                       &actions, &acl->header_);
>>               } else {
>>                   build_acl_log(&actions, acl);
>>                   ds_put_cstr(&actions, "/* drop */");
>> -                ovn_lflow_add(lflows, od, stage,
>> -                              acl->priority + OVN_ACL_PRI_OFFSET,
>> -                              acl->match, ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od, stage,
>> +                                        acl->priority +
>> OVN_ACL_PRI_OFFSET,
>> +                                        acl->match, ds_cstr(&actions),
>> +                                        &acl->header_);
>>               }
>>           }
>>           ds_destroy(&match);
>>           ds_destroy(&actions);
>>       }
>> -    free(stage_hint);
>>   }
>>     static struct ovn_port_group *
>> @@ -5191,9 +5223,9 @@ build_acls(struct ovn_datapath *od, struct hmap
>> *lflows,
>>                                 "&& ip4.src == %s && udp && udp.src ==
>> 67 "
>>                                 "&& udp.dst == 68",
>> od->nbs->ports[i]->name,
>>                                 server_mac, server_id);
>> -                ovn_lflow_add(
>> +                ovn_lflow_add_with_hint(
>>                       lflows, od, S_SWITCH_OUT_ACL, 34000,
>> ds_cstr(&match),
>> -                    actions);
>> +                    actions,
>> &od->nbs->ports[i]->dhcpv4_options->header_);
>>                   ds_destroy(&match);
>>               }
>>           }
>> @@ -5218,9 +5250,9 @@ build_acls(struct ovn_datapath *od, struct hmap
>> *lflows,
>>                                 "&& ip6.src == %s && udp && udp.src ==
>> 547 "
>>                                 "&& udp.dst == 546",
>> od->nbs->ports[i]->name,
>>                                 server_mac, server_ip);
>> -                ovn_lflow_add(
>> +                ovn_lflow_add_with_hint(
>>                       lflows, od, S_SWITCH_OUT_ACL, 34000,
>> ds_cstr(&match),
>> -                    actions);
>> +                    actions,
>> &od->nbs->ports[i]->dhcpv6_options->header_);
>>                   ds_destroy(&match);
>>               }
>>           }
>> @@ -5257,9 +5289,10 @@ build_qos(struct ovn_datapath *od, struct hmap
>> *lflows) {
>>                     ds_put_format(&dscp_action, "ip.dscp = %"PRId64";
>> next;",
>>                                 qos->value_action[j]);
>> -                ovn_lflow_add(lflows, od, stage,
>> -                              qos->priority,
>> -                              qos->match, ds_cstr(&dscp_action));
>> +                ovn_lflow_add_with_hint(lflows, od, stage,
>> +                                        qos->priority,
>> +                                        qos->match,
>> ds_cstr(&dscp_action),
>> +                                        &qos->header_);
>>                   ds_destroy(&dscp_action);
>>               }
>>           }
>> @@ -5288,9 +5321,10 @@ build_qos(struct ovn_datapath *od, struct hmap
>> *lflows) {
>>                *
>>                * We limit the bandwidth of this flow by adding a meter
>> table.
>>                */
>> -            ovn_lflow_add(lflows, od, stage,
>> -                          qos->priority,
>> -                          qos->match, ds_cstr(&meter_action));
>> +            ovn_lflow_add_with_hint(lflows, od, stage,
>> +                                    qos->priority,
>> +                                    qos->match, ds_cstr(&meter_action),
>> +                                    &qos->header_);
>>               ds_destroy(&meter_action);
>>           }
>>       }
>> @@ -5405,11 +5439,13 @@ build_stateful(struct ovn_datapath *od, struct
>> hmap *lflows, struct hmap *lbs)
>>                       ds_put_format(&match, " && tcp.dst == %d",
>>                                     lb_vip->vip_port);
>>                   }
>> -                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
>> -                              120, ds_cstr(&match), ds_cstr(&action));
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_SWITCH_IN_STATEFUL, 120,
>> +                                        ds_cstr(&match),
>> ds_cstr(&action),
>> +                                        &lb->nlb->header_);
>>               } else {
>> -                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
>> -                              110, ds_cstr(&match), ds_cstr(&action));
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_SWITCH_IN_STATEFUL, 110,
>> +                                        ds_cstr(&match),
>> ds_cstr(&action),
>> +                                        &lb->nlb->header_);
>>               }
>>                 ds_destroy(&match);
>> @@ -5574,7 +5610,8 @@ build_lswitch_rport_arp_req_flow_for_ip(struct
>> sset *ips,
>>                                           struct ovn_port *patch_op,
>>                                           struct ovn_datapath *od,
>>                                           uint32_t priority,
>> -                                        struct hmap *lflows)
>> +                                        struct hmap *lflows,
>> +                                        const struct ovsdb_idl_row
>> *stage_hint)
>>   {
>>       struct ds match   = DS_EMPTY_INITIALIZER;
>>       struct ds actions = DS_EMPTY_INITIALIZER;
>> @@ -5605,8 +5642,8 @@ build_lswitch_rport_arp_req_flow_for_ip(struct
>> sset *ips,
>>        * in the broadcast domain.
>>        */
>>       ds_put_format(&actions, "outport = %s; output;",
>> patch_op->json_key);
>> -    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>         ds_destroy(&match);
>>       ds_destroy(&actions);
>> @@ -5623,7 +5660,8 @@ static void
>>   build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>>                                     struct ovn_datapath *sw_od,
>>                                     struct ovn_port *sw_op,
>> -                                  struct hmap *lflows)
>> +                                  struct hmap *lflows,
>> +                                  const struct ovsdb_idl_row
>> *stage_hint)
>>   {
>>       if (!op || !op->nbrp) {
>>           return;
>> @@ -5677,11 +5715,13 @@ build_lswitch_rport_arp_req_flows(struct
>> ovn_port *op,
>>         if (!sset_is_empty(&all_ips_v4)) {
>>           build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4,
>> AF_INET, sw_op,
>> -                                                sw_od, 75, lflows);
>> +                                                sw_od, 75, lflows,
>> +                                                stage_hint);
>>       }
>>       if (!sset_is_empty(&all_ips_v6)) {
>>           build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6,
>> AF_INET6, sw_op,
>> -                                                sw_od, 75, lflows);
>> +                                                sw_od, 75, lflows,
>> +                                                stage_hint);
>>       }
>>         sset_destroy(&all_ips_v4);
>> @@ -5752,8 +5792,9 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               (!strcmp(op->nbsp->type, "vtep"))) {
>>               ds_clear(&match);
>>               ds_put_format(&match, "inport == %s", op->json_key);
>> -            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
>> -                          ds_cstr(&match), "next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_ARP_ND_RSP,
>> +                                    100, ds_cstr(&match), "next;",
>> +                                    &op->nbsp->header_);
>>           }
>>       }
>>   @@ -5806,8 +5847,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       "bind_vport(%s, inport); "
>>                       "next;",
>>                       op->json_key);
>> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
>> 100,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> +                                        S_SWITCH_IN_ARP_ND_RSP, 100,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &vp->nbsp->header_);
>>               }
>>                 free(tokstr);
>> @@ -5846,8 +5889,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                           "output;",
>>                           op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
>>                           op->lsp_addrs[i].ipv4_addrs[j].addr_s);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_ARP_ND_RSP, 50,
>> -                                ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_ARP_ND_RSP, 50,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&actions),
>> +                                            &op->nbsp->header_);
>>                         /* Do not reply to an ARP request from the
>> port that owns
>>                        * the address (otherwise a DHCP client that
>> ARPs to check
>> @@ -5862,8 +5908,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                        * network is not working as configured, so
>> dropping the
>>                        * request would frustrate that intent.) */
>>                       ds_put_format(&match, " && inport == %s",
>> op->json_key);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_ARP_ND_RSP, 100,
>> -                                ds_cstr(&match), "next;");
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_ARP_ND_RSP, 100,
>> +                                            ds_cstr(&match), "next;",
>> +                                            &op->nbsp->header_);
>>                   }
>>                     /* For ND solicitations, we need to listen for
>> both the
>> @@ -5894,14 +5942,19 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                               op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>>                               op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>>                               op->lsp_addrs[i].ea_s);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_ARP_ND_RSP, 50,
>> -                                ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_ARP_ND_RSP, 50,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&actions),
>> +                                            &op->nbsp->header_);
>>                         /* Do not reply to a solicitation from the
>> port that owns
>>                        * the address (otherwise DAD detection will
>> fail). */
>>                       ds_put_format(&match, " && inport == %s",
>> op->json_key);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_ARP_ND_RSP, 100,
>> -                                ds_cstr(&match), "next;");
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_ARP_ND_RSP, 100,
>> +                                            ds_cstr(&match), "next;",
>> +                                            &op->nbsp->header_);
>>                   }
>>               }
>>           }
>> @@ -5949,9 +6002,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       "output;",
>>                       svc_monitor_mac, svc_monitor_mac,
>>                       lb->vips[i].backends[j].svc_mon_src_ip);
>> -                ovn_lflow_add(lflows, lb->vips[i].backends[j].op->od,
>> -                              S_SWITCH_IN_ARP_ND_RSP, 110,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows,
>> +                                        lb->vips[i].backends[j].op->od,
>> +                                        S_SWITCH_IN_ARP_ND_RSP, 110,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &lb->nlb->header_);
>>               }
>>           }
>>       }
>> @@ -5985,6 +6040,14 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>           }
>>             for (size_t i = 0; i < op->n_lsp_addrs; i++) {
>> +            struct ovsdb_idl_row *stage_hint;
>> +
>> +            if (op->nbsp->dhcpv4_options) {
>> +                stage_hint = &op->nbsp->dhcpv4_options->header_;
>> +            } else {
>> +                stage_hint = NULL;
>> +            }
>> +
>>               for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs;
>> j++) {
>>                   struct ds options_action = DS_EMPTY_INITIALIZER;
>>                   struct ds response_action = DS_EMPTY_INITIALIZER;
>> @@ -6006,9 +6069,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         op->json_key);
>>                       }
>>   -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_DHCP_OPTIONS,
>> -                                  100, ds_cstr(&match),
>> -                                  ds_cstr(&options_action));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_DHCP_OPTIONS,
>> 100,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&options_action),
>> +                                            stage_hint);
>>                       ds_clear(&match);
>>                       /* Allow ip4.src = OFFER_IP and
>>                        * ip4.dst = {SERVER_IP, 255.255.255.255} for
>> the below
>> @@ -6030,9 +6095,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         op->json_key);
>>                       }
>>   -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_DHCP_OPTIONS,
>> -                                  100, ds_cstr(&match),
>> -                                  ds_cstr(&options_action));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_DHCP_OPTIONS,
>> 100,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&options_action),
>> +                                            stage_hint);
>>                       ds_clear(&match);
>>                         /* If REGBIT_DHCP_OPTS_RESULT is set, it means
>> the
>> @@ -6050,9 +6117,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         op->json_key);
>>                       }
>>   -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_DHCP_RESPONSE,
>> -                                  100, ds_cstr(&match),
>> -                                  ds_cstr(&response_action));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                           
>> S_SWITCH_IN_DHCP_RESPONSE, 100,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&response_action),
>> +                                            stage_hint);
>>                       ds_destroy(&options_action);
>>                       ds_destroy(&response_action);
>>                       ds_destroy(&ipv4_addr_match);
>> @@ -6060,6 +6129,12 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   }
>>               }
>>   +            if (op->nbsp->dhcpv6_options) {
>> +                stage_hint = &op->nbsp->dhcpv6_options->header_;
>> +            } else {
>> +                stage_hint = NULL;
>> +            }
>> +
>>               for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs;
>> j++) {
>>                   struct ds options_action = DS_EMPTY_INITIALIZER;
>>                   struct ds response_action = DS_EMPTY_INITIALIZER;
>> @@ -6080,14 +6155,20 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         op->json_key);
>>                       }
>>   -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_DHCP_OPTIONS, 100,
>> -                                  ds_cstr(&match),
>> ds_cstr(&options_action));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_SWITCH_IN_DHCP_OPTIONS,
>> 100,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&options_action),
>> +                                            stage_hint);
>>                         /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it
>> means the
>>                        * put_dhcpv6_opts action is successful */
>>                       ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_DHCP_RESPONSE, 100,
>> -                                  ds_cstr(&match),
>> ds_cstr(&response_action));
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                           
>> S_SWITCH_IN_DHCP_RESPONSE, 100,
>> +                                            ds_cstr(&match),
>> +                                            ds_cstr(&response_action),
>> +                                            stage_hint);
>>                       ds_destroy(&options_action);
>>                       ds_destroy(&response_action);
>>                       break;
>> @@ -6172,9 +6253,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                               op->od->localnet_port->json_key,
>>                               op->lsp_addrs[i].ea_s, op->json_key,
>>                               rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
>> -                        ovn_lflow_add(lflows, op->od,
>> -                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
>> -                                      ds_cstr(&match), "drop;");
>> +                        ovn_lflow_add_with_hint(lflows, op->od,
>> +                                               
>> S_SWITCH_IN_EXTERNAL_PORT,
>> +                                                100, ds_cstr(&match),
>> "drop;",
>> +                                                &op->nbsp->header_);
>>                       }
>>                       for (size_t l = 0; l <
>> rp->lsp_addrs[k].n_ipv6_addrs;
>>                            l++) {
>> @@ -6189,9 +6271,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                               rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
>>                               rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
>>                               rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
>> -                        ovn_lflow_add(lflows, op->od,
>> -                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
>> -                                      ds_cstr(&match), "drop;");
>> +                        ovn_lflow_add_with_hint(lflows, op->od,
>> +                                               
>> S_SWITCH_IN_EXTERNAL_PORT, 100,
>> +                                                ds_cstr(&match),
>> "drop;",
>> +                                                &op->nbsp->header_);
>>                       }
>>                   }
>>               }
>> @@ -6326,7 +6409,8 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>            * requests only to the router port that owns the IP address.
>>            */
>>           if (!strcmp(op->nbsp->type, "router")) {
>> -            build_lswitch_rport_arp_req_flows(op->peer, op->od, op,
>> lflows);
>> +            build_lswitch_rport_arp_req_flows(op->peer, op->od, op,
>> lflows,
>> +                                              &op->nbsp->header_);
>>           }
>>             for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
>> @@ -6342,8 +6426,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                     ds_clear(&actions);
>>                   ds_put_format(&actions, "outport = %s; output;",
>> op->json_key);
>> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_L2_LKUP,
>> +                                        50, ds_cstr(&match),
>> +                                        ds_cstr(&actions),
>> +                                        &op->nbsp->header_);
>>               } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
>>                   if (lsp_is_enabled(op->nbsp)) {
>>                       ovn_multicast_add(mcgroups, &mc_unknown, op);
>> @@ -6361,8 +6447,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                     ds_clear(&actions);
>>                   ds_put_format(&actions, "outport = %s; output;",
>> op->json_key);
>> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_SWITCH_IN_L2_LKUP,
>> +                                        50, ds_cstr(&match),
>> +                                        ds_cstr(&actions),
>> +                                        &op->nbsp->header_);
>>               } else if (!strcmp(op->nbsp->addresses[i], "router")) {
>>                   if (!op->peer || !op->peer->nbrp
>>                       || !ovs_scan(op->peer->nbrp->mac,
>> @@ -6403,8 +6491,10 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                     ds_clear(&actions);
>>                   ds_put_format(&actions, "outport = %s; output;",
>> op->json_key);
>> -                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> +                                        S_SWITCH_IN_L2_LKUP, 50,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &op->nbsp->header_);
>>                     /* Add ethernet addresses specified in NAT rules on
>>                    * distributed logical routers. */
>> @@ -6426,9 +6516,11 @@ build_lswitch_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                               ds_clear(&actions);
>>                               ds_put_format(&actions, "outport = %s;
>> output;",
>>                                             op->json_key);
>> -                            ovn_lflow_add(lflows, op->od,
>> S_SWITCH_IN_L2_LKUP,
>> -                                          50, ds_cstr(&match),
>> -                                          ds_cstr(&actions));
>> +                            ovn_lflow_add_with_hint(lflows, op->od,
>> +                                                   
>> S_SWITCH_IN_L2_LKUP, 50,
>> +                                                    ds_cstr(&match),
>> +                                                    ds_cstr(&actions),
>> +                                                    &op->nbsp->header_);
>>                           }
>>                       }
>>                   }
>> @@ -6544,7 +6636,8 @@ get_outport_for_routing_policy_nexthop(struct
>> ovn_datapath *od,
>>   static void
>>   build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
>>                             struct hmap *ports,
>> -                          const struct nbrec_logical_router_policy
>> *rule)
>> +                          const struct nbrec_logical_router_policy
>> *rule,
>> +                          const struct ovsdb_idl_row *stage_hint)
>>   {
>>       struct ds match = DS_EMPTY_INITIALIZER;
>>       struct ds actions = DS_EMPTY_INITIALIZER;
>> @@ -6584,8 +6677,9 @@ build_routing_policy_flow(struct hmap *lflows,
>> struct ovn_datapath *od,
>>           ds_put_cstr(&actions, "next;");
>>       }
>>       ds_put_format(&match, "%s", rule->match);
>> -    ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +
>> +    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY,
>> rule->priority,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>       ds_destroy(&match);
>>       ds_destroy(&actions);
>>   }
>> @@ -6654,8 +6748,9 @@ add_distributed_nat_routes(struct hmap *lflows,
>> const struct ovn_port *op)
>>                         REGBIT_NAT_REDIRECT" = 0; next;",
>>                         op->od->l3dgw_port->json_key,
>>                         nat->external_mac);
>> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
>> -                      ds_cstr(&match), ds_cstr(&actions));
>> +        ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_ROUTING, 400,
>> +                                ds_cstr(&match), ds_cstr(&actions),
>> +                                &nat->header_);
>>           ds_clear(&match);
>>           ds_clear(&actions);
>>   @@ -6702,8 +6797,9 @@ add_distributed_nat_routes(struct hmap
>> *lflows, const struct ovn_port *op)
>>                             family == AF_INET ? "4" : "6",
>>                             family == AF_INET ? "" : "xx",
>>                             nat->external_ip);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_ROUTING,
>> +                                    400, ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                    &nat2->header_);
>>               ds_clear(&match);
>>               ds_clear(&actions);
>>           }
>> @@ -6715,7 +6811,8 @@ add_distributed_nat_routes(struct hmap *lflows,
>> const struct ovn_port *op)
>>   static void
>>   add_route(struct hmap *lflows, const struct ovn_port *op,
>>             const char *lrp_addr_s, const char *network_s, int plen,
>> -          const char *gateway, const char *policy)
>> +          const char *gateway, const char *policy,
>> +          const struct ovsdb_idl_row *stage_hint)
>>   {
>>       bool is_ipv4 = strchr(network_s, '.') ? true : false;
>>       struct ds match = DS_EMPTY_INITIALIZER;
>> @@ -6762,8 +6859,8 @@ add_route(struct hmap *lflows, const struct
>> ovn_port *op,
>>         /* The priority here is calculated to implement
>> longest-prefix-match
>>        * routing. */
>> -    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
>> -                  ds_cstr(&match), ds_cstr(&actions));
>> +    ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING,
>> priority,
>> +                            ds_cstr(&match), ds_cstr(&actions),
>> stage_hint);
>>       ds_destroy(&match);
>>       ds_destroy(&actions);
>>   }
>> @@ -6771,7 +6868,8 @@ add_route(struct hmap *lflows, const struct
>> ovn_port *op,
>>   static void
>>   build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
>>                           struct hmap *ports,
>> -                        const struct
>> nbrec_logical_router_static_route *route)
>> +                        const struct
>> nbrec_logical_router_static_route *route,
>> +                        const struct ovsdb_idl_row *stage_hint)
>>   {
>>       ovs_be32 nexthop;
>>       const char *lrp_addr_s = NULL;
>> @@ -6894,7 +6992,7 @@ build_static_route_flow(struct hmap *lflows,
>> struct ovn_datapath *od,
>>         char *policy = route->policy ? route->policy : "dst-ip";
>>       add_route(lflows, out_port, lrp_addr_s, prefix_s, plen,
>> route->nexthop,
>> -              policy);
>> +              policy, stage_hint);
>>     free_prefix_s:
>>       free(prefix_s);
>> @@ -6977,35 +7075,38 @@ add_router_lb_flow(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                      const char *lb_force_snat_ip, struct smap_node
>> *lb_info,
>>                      bool is_udp, int addr_family, char *ip_addr,
>>                      uint16_t l4_port, struct nbrec_load_balancer *lb,
>> -                   struct shash *meter_groups)
>> +                   struct shash *meter_groups,
>> +                   const struct ovsdb_idl_row *stage_hint)
>>   {
>>       char *backend_ips = lb_info->value;
>>         build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
>>                                 l4_port, addr_family, S_ROUTER_IN_DNAT,
>> -                              meter_groups);
>> +                              meter_groups, stage_hint);
>>         /* A match and actions for new connections. */
>>       char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
>>       if (lb_force_snat_ip) {
>>           char *new_actions = xasprintf("flags.force_snat_for_lb = 1;
>> %s",
>>                                         ds_cstr(actions));
>> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
>> -                      new_actions);
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
>> +                                new_match, new_actions, stage_hint);
>>           free(new_actions);
>>       } else {
>> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
>> -                      ds_cstr(actions));
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
>> +                                new_match, ds_cstr(actions),
>> stage_hint);
>>       }
>>         /* A match and actions for established connections. */
>>       char *est_match = xasprintf("ct.est && %s", ds_cstr(match));
>>       if (lb_force_snat_ip) {
>> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
>> -                      "flags.force_snat_for_lb = 1; ct_dnat;");
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
>> +                                est_match,
>> +                                "flags.force_snat_for_lb = 1; ct_dnat;",
>> +                                stage_hint);
>>       } else {
>> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
>> -                      "ct_dnat;");
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
>> +                                est_match, "ct_dnat;", stage_hint);
>>       }
>>         free(new_match);
>> @@ -7068,12 +7169,14 @@ add_router_lb_flow(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                    "is_chassis_resident(%s)", od->l3dgw_port->json_key,
>>                    od->l3redirect_port->json_key);
>>       if (lb_force_snat_ip) {
>> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
>> -                      ds_cstr(&undnat_match),
>> -                      "flags.force_snat_for_lb = 1; ct_dnat;");
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
>> +                                ds_cstr(&undnat_match),
>> +                                "flags.force_snat_for_lb = 1; ct_dnat;",
>> +                                stage_hint);
>>       } else {
>> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
>> -                      ds_cstr(&undnat_match), "ct_dnat;");
>> +        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
>> +                                ds_cstr(&undnat_match), "ct_dnat;",
>> +                                stage_hint);
>>       }
>>         ds_destroy(&undnat_match);
>> @@ -7219,8 +7322,8 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>             ds_clear(&match);
>>           ds_put_format(&match, "eth.mcast && inport == %s",
>> op->json_key);
>> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
>> -                      ds_cstr(&match), "next;");
>> +        ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_ADMISSION, 50,
>> +                                ds_cstr(&match), "next;",
>> &op->nbrp->header_);
>>             ds_clear(&match);
>>           ds_put_format(&match, "eth.dst == %s && inport == %s",
>> @@ -7232,8 +7335,8 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               ds_put_format(&match, " && is_chassis_resident(%s)",
>>                             op->od->l3redirect_port->json_key);
>>           }
>> -        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
>> -                      ds_cstr(&match), "next;");
>> +        ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_ADMISSION, 50,
>> +                                ds_cstr(&match), "next;",
>> &op->nbrp->header_);
>>       }
>>         /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
>> @@ -7318,10 +7421,12 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   ds_put_format(&match, " && is_chassis_resident(%s)",
>>                                 op->od->l3redirect_port->json_key);
>>               }
>> -            ovn_lflow_add(lflows, op->od,
>> S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> -                          ds_cstr(&match),
>> -                          REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> -                          "lookup_arp(inport, arp.spa, arp.sha);
>> next;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> +                                    S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> +                                    ds_cstr(&match),
>> +                                    REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> +                                    "lookup_arp(inport, arp.spa,
>> arp.sha); "
>> +                                    "next;", &op->nbrp->header_);
>>           }
>>       }
>>   @@ -7391,8 +7496,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               ds_put_cstr(&match, "ip4.src == ");
>>               op_put_v4_networks(&match, op, true);
>>               ds_put_cstr(&match, " && "REGBIT_EGRESS_LOOPBACK" == 0");
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
>> -                          ds_cstr(&match), "drop;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 100,
>> +                                    ds_cstr(&match), "drop;",
>> +                                    &op->nbrp->header_);
>>                 /* ICMP echo reply.  These flows reply to ICMP echo
>> requests
>>                * received for the router's IP address. Since packets only
>> @@ -7411,8 +7517,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   "icmp4.type = 0; "
>>                   "flags.loopback = 1; "
>>                   "next; ");
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 90,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>             /* ICMP time exceeded */
>> @@ -7433,8 +7540,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             "ip.ttl = 255; "
>>                             "next; };",
>>                             op->lrp_networks.ipv4_addrs[i].addr_s);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 40,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>             /* ARP reply.  These flows reply to ARP requests for the
>> router's own
>> @@ -7495,8 +7603,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   op->lrp_networks.ea_s,
>>                   op->lrp_networks.ipv4_addrs[i].addr_s,
>>                   op->json_key);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 90,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>             /* A set to hold all load-balancer vips that need ARP
>> responses. */
>> @@ -7740,8 +7849,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       IP_ARGS(ip),
>>                       op->json_key);
>>               }
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 90,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &nat->header_);
>>           }
>>             if (!smap_get(&op->od->nbr->options, "chassis")
>> @@ -7759,8 +7870,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                        "icmp4.type = 3; "
>>                                        "icmp4.code = 3; "
>>                                        "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
>> -                              ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        80, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>                     ds_clear(&match);
>>                   ds_put_format(&match,
>> @@ -7770,8 +7882,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                            "eth.dst <-> eth.src; "
>>                            "ip4.dst <-> ip4.src; "
>>                            "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
>> -                              ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        80, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>                     ds_clear(&match);
>>                   ds_put_format(&match,
>> @@ -7784,8 +7897,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                            "icmp4.type = 3; "
>>                            "icmp4.code = 2; "
>>                            "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
>> -                              ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        70, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>               }
>>           }
>>   @@ -7844,8 +7958,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>             if (has_drop_ips) {
>>               /* Drop IP traffic to this router. */
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
>> -                          ds_cstr(&match), "drop;");
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 60,
>> +                                    ds_cstr(&match), "drop;",
>> +                                    &op->nbrp->header_);
>>           }
>>             free(snat_ips);
>> @@ -7878,8 +7993,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                           "icmp6.type = 129; "
>>                           "flags.loopback = 1; "
>>                           "next; ");
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 90,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>             /* ND reply.  These flows reply to ND solicitations for the
>> @@ -7919,8 +8035,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             op->lrp_networks.ipv6_addrs[i].addr_s,
>>                             op->lrp_networks.ipv6_addrs[i].addr_s,
>>                             op->lrp_networks.ea_s);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 90,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>             /* UDP/TCP port unreachable */
>> @@ -7935,8 +8052,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                        "eth.dst <-> eth.src; "
>>                                        "ip6.dst <-> ip6.src; "
>>                                        "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
>> -                          ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        80, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>                     ds_clear(&match);
>>                   ds_put_format(&match,
>> @@ -7949,8 +8067,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                            "icmp6.type = 1; "
>>                            "icmp6.code = 4; "
>>                            "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
>> -                              ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        80, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>                     ds_clear(&match);
>>                   ds_put_format(&match,
>> @@ -7963,8 +8082,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                            "icmp6.type = 1; "
>>                            "icmp6.code = 3; "
>>                            "next; };";
>> -                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
>> -                              ds_cstr(&match), action);
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT,
>> +                                        70, ds_cstr(&match), action,
>> +                                        &op->nbrp->header_);
>>               }
>>           }
>>   @@ -7995,8 +8115,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             "icmp6.code = 0; /* TTL exceeded in
>> transit */ "
>>                             "next; };",
>>                             op->lrp_networks.ipv6_addrs[i].addr_s);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_IP_INPUT, 40,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>       }
>>   @@ -8135,8 +8256,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                          ds_put_cstr(&actions, "ct_snat;");
>>                       }
>>   -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
>> -                                  ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_UNSNAT,
>> +                                            90, ds_cstr(&match),
>> +                                            ds_cstr(&actions),
>> +                                            &nat->header_);
>>                   } else {
>>                       /* Distributed router. */
>>   @@ -8162,8 +8285,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                           ds_put_cstr(&actions, "ct_snat;");
>>                       }
>>   -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
>> -                                  ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_UNSNAT,
>> +                                            100, ds_cstr(&match),
>> +                                            ds_cstr(&actions),
>> +                                            &nat->header_);
>>                         /* Traffic received on other router ports must be
>>                        * redirected to the central instance of the
>> l3dgw_port
>> @@ -8172,9 +8297,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_put_format(&match, "ip && ip%s.dst == %s",
>>                                     is_v6 ? "6" : "4",
>>                                     nat->external_ip);
>> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
>> -                                  ds_cstr(&match),
>> -                                  REGBIT_NAT_REDIRECT" = 1; next;");
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_UNSNAT,
>> +                                            50, ds_cstr(&match),
>> +                                            REGBIT_NAT_REDIRECT" = 1;
>> next;",
>> +                                            &nat->header_);
>>                   }
>>               }
>>   @@ -8210,8 +8336,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         "ct_dnat(%s);", nat->logical_ip);
>>                       }
>>   -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
>> -                                  ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_DNAT, 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &nat->header_);
>>                   } else {
>>                       /* Distributed router. */
>>   @@ -8238,8 +8365,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         nat->logical_ip);
>>                       }
>>   -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
>> -                                  ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_DNAT, 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &nat->header_);
>>                         /* Traffic received on other router ports must be
>>                        * redirected to the central instance of the
>> l3dgw_port
>> @@ -8248,9 +8376,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_put_format(&match, "ip && ip%s.dst == %s",
>>                                     is_v6 ? "6" : "4",
>>                                     nat->external_ip);
>> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
>> -                                  ds_cstr(&match),
>> -                                  REGBIT_NAT_REDIRECT" = 1; next;");
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_DNAT, 50,
>> +                                            ds_cstr(&match),
>> +                                            REGBIT_NAT_REDIRECT" = 1;
>> next;",
>> +                                            &nat->header_);
>>                   }
>>               }
>>   @@ -8289,8 +8418,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_put_format(&actions, "ct_dnat;");
>>                   }
>>   -                ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_OUT_UNDNAT, 100,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &nat->header_);
>>               }
>>                 /* Egress SNAT table: Packets enter the egress
>> pipeline with
>> @@ -8317,9 +8447,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       /* The priority here is calculated such that the
>>                        * nat->logical_ip with the longest mask gets a
>> higher
>>                        * priority. */
>> -                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
>> -                                  count_1bits(ntohl(mask)) + 1,
>> -                                  ds_cstr(&match), ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_OUT_SNAT,
>> +                                            count_1bits(ntohl(mask))
>> + 1,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &nat->header_);
>>                   } else {
>>                       uint16_t priority = count_1bits(ntohl(mask)) + 1;
>>   @@ -8354,9 +8485,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       /* The priority here is calculated such that the
>>                        * nat->logical_ip with the longest mask gets a
>> higher
>>                        * priority. */
>> -                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
>> -                                  priority, ds_cstr(&match),
>> -                                  ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_OUT_SNAT,
>> +                                            priority, ds_cstr(&match),
>> +                                            ds_cstr(&actions),
>> +                                            &nat->header_);
>>                   }
>>               }
>>   @@ -8373,8 +8505,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                 ETH_ADDR_ARGS(mac),
>>                                 od->l3dgw_port->json_key,
>>                                 nat->logical_port);
>> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 50,
>> -                              ds_cstr(&match), "next;");
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_ADMISSION, 50,
>> +                                        ds_cstr(&match), "next;",
>> +                                        &nat->header_);
>>               }
>>                 /* Ingress Gateway Redirect Table: For NAT on a
>> distributed
>> @@ -8387,8 +8520,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                 is_v6 ? "6" : "4",
>>                                 nat->logical_ip,
>>                                 od->l3dgw_port->json_key);
>> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
>> -                              ds_cstr(&match), "next;");
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_GW_REDIRECT,
>> +                                        100, ds_cstr(&match), "next;",
>> +                                        &nat->header_);
>>               }
>>                 /* Egress Loopback table: For NAT on a distributed
>> router.
>> @@ -8422,16 +8556,21 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                                         "flags = 0; flags.loopback = 1; "
>>                                         REGBIT_EGRESS_LOOPBACK" = 1; "
>>                                         "next(pipeline=ingress,
>> table=0); ");
>> -                        ovn_lflow_add(lflows, od,
>> S_ROUTER_OUT_EGR_LOOP, 300,
>> -                                      ds_cstr(&match), 
>> ds_cstr(&actions));
>> +                        ovn_lflow_add_with_hint(lflows, od,
>> +                                               
>> S_ROUTER_OUT_EGR_LOOP, 300,
>> +                                                ds_cstr(&match),
>> +                                                ds_cstr(&actions),
>> +                                                &nat2->header_);
>>                             ds_clear(&match);
>>                           ds_put_format(&match,
>>                                         "ip%s.src == %s && ip%s.dst ==
>> %s",
>>                                         is_v6 ? "6" : "4",
>> nat2->external_ip,
>>                                         is_v6 ? "6" : "4",
>> nat->external_ip);
>> -                        ovn_lflow_add(lflows, od,
>> S_ROUTER_OUT_EGR_LOOP, 200,
>> -                                      ds_cstr(&match), "next;");
>> +                        ovn_lflow_add_with_hint(lflows, od,
>> +                                               
>> S_ROUTER_OUT_EGR_LOOP, 200,
>> +                                                ds_cstr(&match),
>> "next;",
>> +                                                &nat2->header_);
>>                           ds_clear(&match);
>>                       }
>>                   }
>> @@ -8451,8 +8590,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   }
>>                   ds_put_format(&actions, REGBIT_EGRESS_LOOPBACK" = 1; "
>>                                 "next(pipeline=ingress, table=0); };");
>> -                ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_OUT_EGR_LOOP, 100,
>> +                                        ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &nat->header_);
>>               }
>>           }
>>   @@ -8591,8 +8731,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                           ds_put_format(&match, "ip && ip6.dst == %s",
>>                                         ip_address);
>>                       }
>> -                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
>> -                                  100, ds_cstr(&match), "ct_next;");
>> +                    ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_DEFRAG,
>> +                                            100, ds_cstr(&match),
>> "ct_next;",
>> +                                            &lb->header_);
>>                   }
>>                     /* Higher priority rules are added for
>> load-balancing in DNAT
>> @@ -8633,7 +8774,8 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                   add_router_lb_flow(lflows, od, &match, &actions, prio,
>>                                      lb_force_snat_ip, node, is_udp,
>>                                      addr_family, ip_address, port, lb,
>> -                                   meter_groups);
>> +                                   meter_groups,
>> +                                   &lb->header_);
>>                     free(ip_address);
>>               }
>> @@ -8706,8 +8848,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>             if (add_rs_response_flow) {
>>               ds_put_cstr(&actions, "); next;");
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, 50,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> S_ROUTER_IN_ND_RA_OPTIONS,
>> +                                    50, ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>               ds_clear(&actions);
>>               ds_clear(&match);
>>               ds_put_format(&match, "inport == %s && ip6.dst ==
>> ff02::2 && "
>> @@ -8723,8 +8866,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             "outport = inport; flags.loopback = 1; "
>>                             "output;",
>>                             op->lrp_networks.ea_s, ip6_str);
>> -            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_RESPONSE,
>> 50,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, op->od,
>> +                                    S_ROUTER_IN_ND_RA_RESPONSE, 50,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>           }
>>       }
>>   @@ -8758,13 +8903,15 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>           for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>>               add_route(lflows, op,
>> op->lrp_networks.ipv4_addrs[i].addr_s,
>>                         op->lrp_networks.ipv4_addrs[i].network_s,
>> -                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL);
>> +                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL,
>> +                      &op->nbrp->header_);
>>           }
>>             for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
>>               add_route(lflows, op,
>> op->lrp_networks.ipv6_addrs[i].addr_s,
>>                         op->lrp_networks.ipv6_addrs[i].network_s,
>> -                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL);
>> +                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL,
>> +                      &op->nbrp->header_);
>>           }
>>       }
>>   @@ -8778,7 +8925,7 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               const struct nbrec_logical_router_static_route *route;
>>                 route = od->nbr->static_routes[i];
>> -            build_static_route_flow(lflows, od, ports, route);
>> +            build_static_route_flow(lflows, od, ports, route,
>> &route->header_);
>>           }
>>       }
>>   @@ -8849,7 +8996,7 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>           for (int i = 0; i < od->nbr->n_policies; i++) {
>>               const struct nbrec_logical_router_policy *rule
>>                   = od->nbr->policies[i];
>> -            build_routing_policy_flow(lflows, od, ports, rule);
>> +            build_routing_policy_flow(lflows, od, ports, rule,
>> &rule->header_);
>>           }
>>       }
>>   @@ -8900,8 +9047,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_clear(&actions);
>>                       ds_put_format(&actions, "eth.dst = %s; next;",
>>                                     op->lrp_networks.ea_s);
>> -                    ovn_lflow_add(lflows, op->peer->od,
>> S_ROUTER_IN_ARP_RESOLVE,
>> -                                  100, ds_cstr(&match),
>> ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, op->peer->od,
>> +                                            S_ROUTER_IN_ARP_RESOLVE,
>> 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &op->nbrp->header_);
>>                   }
>>                     if (op->lrp_networks.n_ipv6_addrs) {
>> @@ -8913,8 +9062,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_clear(&actions);
>>                       ds_put_format(&actions, "eth.dst = %s; next;",
>>                                     op->lrp_networks.ea_s);
>> -                    ovn_lflow_add(lflows, op->peer->od,
>> S_ROUTER_IN_ARP_RESOLVE,
>> -                                  100, ds_cstr(&match),
>> ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, op->peer->od,
>> +                                            S_ROUTER_IN_ARP_RESOLVE,
>> 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &op->nbrp->header_);
>>                   }
>>               }
>>   @@ -8935,8 +9086,11 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_clear(&actions);
>>                       ds_put_format(&actions, "eth.dst = %s; next;",
>>                                     op->lrp_networks.ea_s);
>> -                    ovn_lflow_add(lflows, op->od,
>> S_ROUTER_IN_ARP_RESOLVE,
>> -                                  50, ds_cstr(&match),
>> ds_cstr(&actions));
>> +
>> +                    ovn_lflow_add_with_hint(lflows, op->od,
>> +                                            S_ROUTER_IN_ARP_RESOLVE, 50,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &op->nbrp->header_);
>>                   }
>>               }
>>           } else if (op->od->n_router_ports && strcmp(op->nbsp->type,
>> "router")
>> @@ -8977,9 +9131,11 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             ds_clear(&actions);
>>                           ds_put_format(&actions, "eth.dst = %s;
>> next;", ea_s);
>> -                        ovn_lflow_add(lflows, peer->od,
>> -                                      S_ROUTER_IN_ARP_RESOLVE, 100,
>> -                                      ds_cstr(&match),
>> ds_cstr(&actions));
>> +                        ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                               
>> S_ROUTER_IN_ARP_RESOLVE, 100,
>> +                                                ds_cstr(&match),
>> +                                                ds_cstr(&actions),
>> +                                                &op->nbsp->header_);
>>                       }
>>                   }
>>   @@ -9011,9 +9167,11 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             ds_clear(&actions);
>>                           ds_put_format(&actions, "eth.dst = %s;
>> next;", ea_s);
>> -                        ovn_lflow_add(lflows, peer->od,
>> -                                      S_ROUTER_IN_ARP_RESOLVE, 100,
>> -                                      ds_cstr(&match),
>> ds_cstr(&actions));
>> +                        ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                               
>> S_ROUTER_IN_ARP_RESOLVE, 100,
>> +                                                ds_cstr(&match),
>> +                                                ds_cstr(&actions),
>> +                                                &op->nbsp->header_);
>>                       }
>>                   }
>>               }
>> @@ -9061,9 +9219,11 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                           ds_clear(&actions);
>>                           ds_put_format(&actions,
>>                                         "eth.dst = 00:00:00:00:00:00;
>> next;");
>> -                        ovn_lflow_add(lflows, peer->od,
>> -                                        S_ROUTER_IN_ARP_RESOLVE, 100,
>> -                                        ds_cstr(&match),
>> ds_cstr(&actions));
>> +                        ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                               
>> S_ROUTER_IN_ARP_RESOLVE, 100,
>> +                                                ds_cstr(&match),
>> +                                                ds_cstr(&actions),
>> +                                                &op->nbsp->header_);
>>                           break;
>>                       }
>>                   }
>> @@ -9104,9 +9264,11 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             ds_clear(&actions);
>>                           ds_put_format(&actions, "eth.dst = %s;
>> next;", ea_s);
>> -                        ovn_lflow_add(lflows, peer->od,
>> -                                        S_ROUTER_IN_ARP_RESOLVE, 100,
>> -                                        ds_cstr(&match),
>> ds_cstr(&actions));
>> +                        ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                               
>> S_ROUTER_IN_ARP_RESOLVE, 100,
>> +                                                ds_cstr(&match),
>> +                                                ds_cstr(&actions),
>> +                                                &op->nbsp->header_);
>>                           found_vip_network = true;
>>                           break;
>>                       }
>> @@ -9159,8 +9321,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_clear(&actions);
>>                       ds_put_format(&actions, "eth.dst = %s; next;",
>>                                                
>> router_port->lrp_networks.ea_s);
>> -                    ovn_lflow_add(lflows, peer->od,
>> S_ROUTER_IN_ARP_RESOLVE,
>> -                                  100, ds_cstr(&match),
>> ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                            S_ROUTER_IN_ARP_RESOLVE,
>> 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &op->nbsp->header_);
>>                   }
>>                     if (router_port->lrp_networks.n_ipv6_addrs) {
>> @@ -9172,8 +9336,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       ds_clear(&actions);
>>                       ds_put_format(&actions, "eth.dst = %s; next;",
>>                                     router_port->lrp_networks.ea_s);
>> -                    ovn_lflow_add(lflows, peer->od,
>> S_ROUTER_IN_ARP_RESOLVE,
>> -                                  100, ds_cstr(&match),
>> ds_cstr(&actions));
>> +                    ovn_lflow_add_with_hint(lflows, peer->od,
>> +                                            S_ROUTER_IN_ARP_RESOLVE,
>> 100,
>> +                                            ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                            &op->nbsp->header_);
>>                   }
>>               }
>>           }
>> @@ -9234,8 +9400,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               ds_put_format(&actions,
>>                             REGBIT_PKT_LARGER" = check_pkt_larger(%d);"
>>                             " next;", gw_mtu);
>> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_CHK_PKT_LEN, 50,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &od->l3dgw_port->nbrp->header_);
>>                 for (size_t i = 0; i < od->nbr->n_ports; i++) {
>>                   struct ovn_port *rp = ovn_port_find(ports,
>> @@ -9266,8 +9433,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                       rp->lrp_networks.ea_s,
>>                       rp->lrp_networks.ipv4_addrs[0].addr_s,
>>                       gw_mtu - 18);
>> -                ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 50,
>> -                              ds_cstr(&match), ds_cstr(&actions));
>> +                ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_LARGER_PKTS,
>> +                                        50, ds_cstr(&match),
>> ds_cstr(&actions),
>> +                                        &rp->nbrp->header_);
>>               }
>>           }
>>       }
>> @@ -9284,8 +9452,15 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               continue;
>>           }
>>           if (od->l3dgw_port && od->l3redirect_port) {
>> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
>> -                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
>> +            const struct ovsdb_idl_row *stage_hint = NULL;
>> +
>> +            if (od->l3dgw_port->nbrp) {
>> +                stage_hint = &od->l3dgw_port->nbrp->header_;
>> +            }
>> +
>> +            ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_GW_REDIRECT, 300,
>> +                                    REGBIT_DISTRIBUTED_NAT" == 1",
>> "next;",
>> +                                    stage_hint);
>>                 /* For traffic with outport == l3dgw_port, if the
>>                * packet did not match any higher priority redirect
>> @@ -9297,8 +9472,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               ds_clear(&actions);
>>               ds_put_format(&actions, "outport = %s; next;",
>>                             od->l3redirect_port->json_key);
>> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_GW_REDIRECT, 50,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    stage_hint);
>>                 /* If the Ethernet destination has not been resolved,
>>                * redirect to the central instance of the l3dgw_port.
>> @@ -9307,8 +9483,9 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                * table, before being redirected to the central instance.
>>                */
>>               ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
>> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +            ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_GW_REDIRECT, 150,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    stage_hint);
>>           }
>>             /* Packets are allowed by default. */
>> @@ -9357,8 +9534,10 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                             "output; "
>>                             "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
>>                             route->nexthop);
>> -            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
>> -                          ds_cstr(&match), ds_cstr(&actions));
>> +
>> +            ovn_lflow_add_with_hint(lflows, od,
>> S_ROUTER_IN_ARP_REQUEST, 200,
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &route->header_);
>>           }
>>             ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
>> @@ -9416,7 +9595,7 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>               ds_put_format(&actions, "eth.src = %s; output;",
>>                             op->lrp_networks.ea_s);
>>               ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 110,
>> -                        ds_cstr(&match), ds_cstr(&actions));
>> +                          ds_cstr(&match), ds_cstr(&actions));
>>           }
>>             ds_clear(&match);
>> diff --git a/utilities/ovn-detrace.in b/utilities/ovn-detrace.in
>> index 3bfb720..e9c3736 100755
>> --- a/utilities/ovn-detrace.in
>> +++ b/utilities/ovn-detrace.in
>> @@ -135,9 +135,74 @@ class CookieHandlerByUUUID(CookieHandler):
>>           cookie = cookie.zfill(8)
>>           return self._db.find_rows_by_partial_uuid(self._table, cookie)
>>   +class ACLHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(ACLHintHandler, self).__init__(ovnnb_db, 'ACL')
>> +
>> +    def print_record(self, acl):
>> +        output = 'ACL: %s, priority=%s, ' \
>> +                 'match=(%s), %s' % (acl.direction,
>> +                                     acl.priority,
>> +                                     acl.match.strip('"'),
>> +                                     acl.action)
>> +        if acl.log:
>> +            output += ' (log)'
>> +        print_h(output)
>> +
>> +class LSPHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(LSPHintHandler, self).__init__(ovnnb_db,
>> 'Logical_Switch_Port')
>> +
>> +    def print_record(self, lsp):
>> +        print_h('Logical Switch Port: %s type %s (addresses %s,
>> dynamic addresses %s, security %s' % (
>> +                    lsp.name, lsp.type, lsp.addresses,
>> lsp.dynamic_addresses,
>> +                    lsp.port_security))
>> +
>> +class LRPHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(LRPHintHandler, self).__init__(ovnnb_db,
>> 'Logical_Router_Port')
>> +
>> +    def print_record(self, lrp):
>> +        print_h('Logical Router Port: %s mac %s networks %s
>> ipv6_ra_configs %s' % (
>> +                    lrp.name, lrp.mac, lrp.networks,
>> lrp.ipv6_ra_configs))
>> +
>> +class LoadBalancerHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(LoadBalancerHintHandler, self).__init__(ovnnb_db,
>> 'Load_Balancer')
>> +
>> +    def print_record(self, lb):
>> +        print_h('Load Balancer: %s protocol %s vips %s
>> ip_port_mappings %s' % (
>> +                    lb.name, lb.protocol, lb.vips, lb.ip_port_mappings))
>> +
>> +class NATHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(NATHintHandler, self).__init__(ovnnb_db, 'NAT')
>> +
>> +    def print_record(self, nat):
>> +        print_h('NAT: external IP %s external_mac %s logical_ip %s
>> logical_port %s type %s' % (
>> +                    nat.external_ip, nat.external_mac, nat.logical_ip,
>> +                    nat.logical_port, nat.type))
>> +
>> +class StaticRouteHintHandler(CookieHandlerByUUUID):
>> +    def __init__(self, ovnnb_db):
>> +        super(StaticRouteHintHandler, self).__init__(ovnnb_db,
>> +                                                    
>> 'Logical_Router_Static_Route')
>> +
>> +    def print_record(self, route):
>> +        print_h('Route: %s via %s (port %s), policy=%s' % (
>> +                    route.ip_prefix, route.nexthop, route.output_port,
>> +                    route.policy))
>> +
>>   class LogicalFlowHandler(CookieHandlerByUUUID):
>> -    def __init__(self, ovnsb_db):
>> +    def __init__(self, ovnnb_db, ovnsb_db):
>>           super(LogicalFlowHandler, self).__init__(ovnsb_db,
>> 'Logical_Flow')
>> +        self._hint_handlers = [
>> +            ACLHintHandler(ovnnb_db),
>> +            LSPHintHandler(ovnnb_db),
>> +            LRPHintHandler(ovnnb_db),
>> +            LoadBalancerHintHandler(ovnnb_db),
>> +            NATHintHandler(ovnnb_db)
>> +        ]
>>         def print_record(self, lflow):
>>           print_p('Logical datapath: %s [%s]' %
>> @@ -151,24 +216,14 @@ class LogicalFlowHandler(CookieHandlerByUUUID):
>>         def print_hint(self, lflow, ovnnb_db):
>>           external_ids = lflow.external_ids
>> -        if external_ids.get('stage-name') in ['ls_in_acl',
>> -                                              'ls_out_acl']:
>> -            acl_hint = external_ids.get('stage-hint')
>> -            if not acl_hint:
>> -                return
>> -            for i, acl in enumerate(
>> -                    ovnnb_db.find_rows_by_partial_uuid('ACL',
>> acl_hint)):
>> +        hint = external_ids.get('stage-hint')
>> +        if not hint:
>> +            return
>> +        for handler in self._hint_handlers:
>> +            for i, record in enumerate(handler.get_records(hint)):
>>                   if i > 0:
>> -                    print_h('[Duplicate uuid ACL hint]')
>> -
>> -                output = 'ACL: %s, priority=%s, ' \
>> -                        'match=(%s), %s' % (acl.direction,
>> -                                            acl.priority,
>> -                                            acl.match.strip('"'),
>> -                                            acl.action)
>> -                if acl.log:
>> -                    output += ' (log)'
>> -                print_h(output)
>> +                    print_h('[Duplicate uuid hint]')
>> +                handler.print_record(record)
>>     class PortBindingHandler(CookieHandlerByUUUID):
>>       def __init__(self, ovnsb_db):
>> @@ -283,7 +338,7 @@ def main():
>>       ovsdb_ovnnb = OVSDB(ovnnb_db, 'OVN_Northbound')
>>         cookie_handlers = [
>> -        LogicalFlowHandler(ovsdb_ovnsb),
>> +        LogicalFlowHandler(ovsdb_ovnnb, ovsdb_ovnsb),
>>           PortBindingHandler(ovsdb_ovnsb),
>>           MacBindingHandler(ovsdb_ovnsb),
>>           MulticastGroupHandler(ovsdb_ovnsb),
>>
>

Patch
diff mbox series

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index b6dc809..20b8429 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -3714,6 +3714,15 @@  ovn_lflow_hash(const struct ovn_lflow *lflow)
                                  lflow->actions);
 }
 
+static char *
+ovn_lflow_hint(const struct ovsdb_idl_row *row)
+{
+    if (!row) {
+        return NULL;
+    }
+    return xasprintf("%08x", row->uuid.parts[0]);
+}
+
 static bool
 ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
 {
@@ -3744,14 +3753,14 @@  static void
 ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
                  enum ovn_stage stage, uint16_t priority,
                  const char *match, const char *actions,
-                 const char *stage_hint, const char *where)
+                 const struct ovsdb_idl_row *stage_hint, const char *where)
 {
     ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
 
     struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
     ovn_lflow_init(lflow, od, stage, priority,
                    xstrdup(match), xstrdup(actions),
-                   nullable_xstrdup(stage_hint), where);
+                   ovn_lflow_hint(stage_hint), where);
     hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
 }
 
@@ -3902,7 +3911,8 @@  build_port_security_ipv6_flow(
  *   - Priority 80 flow to drop ARP and IPv6 ND packets.
  */
 static void
-build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
+build_port_security_nd(struct ovn_port *op, struct hmap *lflows,
+                       const struct ovsdb_idl_row *stage_hint)
 {
     struct ds match = DS_EMPTY_INITIALIZER;
 
@@ -3938,8 +3948,8 @@  build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
                 ds_chomp(&match, ',');
                 ds_put_cstr(&match, "}");
             }
-            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
-                          ds_cstr(&match), "next;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
+                                    90, ds_cstr(&match), "next;", stage_hint);
         }
 
         if (ps->n_ipv6_addrs || no_ip) {
@@ -3948,15 +3958,15 @@  build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
                           op->json_key, ps->ea_s);
             build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
                                              ps->n_ipv6_addrs);
-            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
-                          ds_cstr(&match), "next;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
+                                    90, ds_cstr(&match), "next;", stage_hint);
         }
     }
 
     ds_clear(&match);
     ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
-    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
-                  ds_cstr(&match), "drop;");
+    ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
+                            ds_cstr(&match), "drop;", stage_hint);
     ds_destroy(&match);
 }
 
@@ -3977,7 +3987,8 @@  build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
  */
 static void
 build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
-                       struct hmap *lflows)
+                       struct hmap *lflows,
+                       const struct ovsdb_idl_row *stage_hint)
 {
     char *port_direction;
     enum ovn_stage stage;
@@ -4007,8 +4018,9 @@  build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                               " && ip4.dst == 255.255.255.255"
                               " && udp.src == 68 && udp.dst == 67",
                               op->json_key, ps->ea_s);
-                ovn_lflow_add(lflows, op->od, stage, 90,
-                              ds_cstr(&dhcp_match), "next;");
+                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
+                                        ds_cstr(&dhcp_match), "next;",
+                                        stage_hint);
                 ds_destroy(&dhcp_match);
                 ds_put_format(&match, "inport == %s && eth.src == %s"
                               " && ip4.src == {", op->json_key,
@@ -4047,7 +4059,9 @@  build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
             ds_chomp(&match, ' ');
             ds_chomp(&match, ',');
             ds_put_cstr(&match, "}");
-            ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match), "next;");
+            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
+                                    ds_cstr(&match), "next;",
+                                    stage_hint);
             ds_destroy(&match);
         }
 
@@ -4063,8 +4077,9 @@  build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                               " && ip6.dst == ff02::/16"
                               " && icmp6.type == {131, 135, 143}", op->json_key,
                               ps->ea_s);
-                ovn_lflow_add(lflows, op->od, stage, 90,
-                              ds_cstr(&dad_match), "next;");
+                ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
+                                        ds_cstr(&dad_match), "next;",
+                                        stage_hint);
                 ds_destroy(&dad_match);
             }
             ds_put_format(&match, "%s == %s && %s == %s",
@@ -4072,8 +4087,9 @@  build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                           pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
             build_port_security_ipv6_flow(pipeline, &match, ps->ea,
                                           ps->ipv6_addrs, ps->n_ipv6_addrs);
-            ovn_lflow_add(lflows, op->od, stage, 90,
-                          ds_cstr(&match), "next;");
+            ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
+                                    ds_cstr(&match), "next;",
+                                    stage_hint);
             ds_destroy(&match);
         }
 
@@ -4081,7 +4097,8 @@  build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                                 port_direction, op->json_key,
                                 pipeline == P_IN ? "eth.src" : "eth.dst",
                                 ps->ea_s);
-        ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
+        ovn_lflow_add_with_hint(lflows, op->od, stage, 80, match, "drop;",
+                                stage_hint);
         free(match);
     }
 
@@ -4371,12 +4388,13 @@  build_lswitch_input_port_sec(struct hmap *ports, struct hmap *datapaths,
             ds_put_format(&actions, "set_queue(%s); ", queue_id);
         }
         ds_put_cstr(&actions, "next;");
-        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
-                      ds_cstr(&match), ds_cstr(&actions));
+        ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
+                                ds_cstr(&match), ds_cstr(&actions),
+                                &op->nbsp->header_);
 
         if (op->nbsp->n_port_security) {
-            build_port_security_ip(P_IN, op, lflows);
-            build_port_security_nd(op, lflows);
+            build_port_security_ip(P_IN, op, lflows, &op->nbsp->header_);
+            build_port_security_nd(op, lflows, &op->nbsp->header_);
         }
     }
 
@@ -4436,15 +4454,17 @@  build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths,
                 }
             }
             ds_put_cstr(&actions, "output;");
-            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
-                              ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2,
+                                    50, ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbsp->header_);
         } else {
-            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
-                          ds_cstr(&match), "drop;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2,
+                                    150, ds_cstr(&match), "drop;",
+                                    &op->nbsp->header_);
         }
 
         if (op->nbsp->n_port_security) {
-            build_port_security_ip(P_OUT, op, lflows);
+            build_port_security_ip(P_OUT, op, lflows, &op->nbsp->header_);
         }
     }
 
@@ -4499,10 +4519,12 @@  build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
 
             ds_put_format(&match_in, "ip && inport == %s", op->json_key);
             ds_put_format(&match_out, "ip && outport == %s", op->json_key);
-            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
-                          ds_cstr(&match_in), "next;");
-            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
-                          ds_cstr(&match_out), "next;");
+            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
+                                    ds_cstr(&match_in), "next;",
+                                    &op->nbsp->header_);
+            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
+                                    ds_cstr(&match_out), "next;",
+                                    &op->nbsp->header_);
 
             ds_destroy(&match_in);
             ds_destroy(&match_out);
@@ -4515,10 +4537,12 @@  build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
                           od->localnet_port->json_key);
             ds_put_format(&match_out, "ip && outport == %s",
                           od->localnet_port->json_key);
-            ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
-                          ds_cstr(&match_in), "next;");
-            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
-                          ds_cstr(&match_out), "next;");
+            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
+                                    ds_cstr(&match_in), "next;",
+                                    &od->localnet_port->nbsp->header_);
+            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
+                                    ds_cstr(&match_out), "next;",
+                                    &od->localnet_port->nbsp->header_);
 
             ds_destroy(&match_in);
             ds_destroy(&match_out);
@@ -4596,7 +4620,8 @@  static void
 build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
                           struct smap_node *node, char *ip_address,
                           struct nbrec_load_balancer *lb, uint16_t port,
-                          int addr_family, int pl, struct shash *meter_groups)
+                          int addr_family, int pl, struct shash *meter_groups,
+                          const struct ovsdb_idl_row *stage_hint)
 {
     if (!controller_event_en || node->value[0]) {
         return;
@@ -4627,7 +4652,8 @@  build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
                        event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
                        meter, node->key, lb->protocol,
                        UUID_ARGS(&lb->header_.uuid));
-    ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
+    ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(&match), action,
+                            stage_hint);
     ds_destroy(&match);
     free(action);
 }
@@ -4680,7 +4706,7 @@  build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
 
             build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
                                       port, addr_family, S_SWITCH_IN_PRE_LB,
-                                      meter_groups);
+                                      meter_groups, &lb->header_);
 
             free(ip_address);
 
@@ -4772,7 +4798,8 @@  build_acl_log(struct ds *actions, const struct nbrec_acl *acl)
 static void
 build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                        enum ovn_stage stage, struct nbrec_acl *acl,
-                       struct ds *extra_match, struct ds *extra_actions)
+                       struct ds *extra_match, struct ds *extra_actions,
+                       const struct ovsdb_idl_row *stage_hint)
 {
     struct ds match = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
@@ -4788,8 +4815,9 @@  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                   "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
                   "tcp_reset { outport <-> inport; %s };",
                   ingress ? "output;" : "next(pipeline=ingress,table=0);");
-    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, od, stage,
+                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
     ds_clear(&match);
     ds_clear(&actions);
     build_acl_log(&actions, acl);
@@ -4801,8 +4829,9 @@  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                   "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
                   "tcp_reset { outport <-> inport; %s };",
                   ingress ? "output;" : "next(pipeline=ingress,table=0);");
-    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, od, stage,
+                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
 
     /* IP traffic */
     ds_clear(&match);
@@ -4819,8 +4848,9 @@  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                   "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
                   "icmp4 { outport <-> inport; %s };",
                   ingress ? "output;" : "next(pipeline=ingress,table=0);");
-    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, od, stage,
+                            acl->priority + OVN_ACL_PRI_OFFSET,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
     ds_clear(&match);
     ds_clear(&actions);
     build_acl_log(&actions, acl);
@@ -4835,8 +4865,9 @@  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                   "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
                   "outport <-> inport; %s };",
                   ingress ? "output;" : "next(pipeline=ingress,table=0);");
-    ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, od, stage,
+                            acl->priority + OVN_ACL_PRI_OFFSET,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
 
     ds_destroy(&match);
     ds_destroy(&actions);
@@ -4849,7 +4880,6 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
     bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
     enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
 
-    char *stage_hint = xasprintf("%08x", acl->header_.uuid.parts[0]);
     if (!strcmp(acl->action, "allow")
         || !strcmp(acl->action, "allow-related")) {
         /* If there are any stateful flows, we must even commit "allow"
@@ -4864,7 +4894,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
                                     acl->match, ds_cstr(&actions),
-                                    stage_hint);
+                                    &acl->header_);
             ds_destroy(&actions);
         } else {
             struct ds match = DS_EMPTY_INITIALIZER;
@@ -4893,7 +4923,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
                                     ds_cstr(&match),
                                     ds_cstr(&actions),
-                                    stage_hint);
+                                    &acl->header_);
 
             /* Match on traffic in the request direction for an established
              * connection tracking entry that has not been marked for
@@ -4913,7 +4943,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
                                     ds_cstr(&match), ds_cstr(&actions),
-                                    stage_hint);
+                                    &acl->header_);
 
             ds_destroy(&match);
             ds_destroy(&actions);
@@ -4935,14 +4965,15 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                         " || (ct.est && ct_label.blocked == 1))");
             if (!strcmp(acl->action, "reject")) {
                 build_reject_acl_rules(od, lflows, stage, acl, &match,
-                                       &actions);
+                                       &actions, &acl->header_);
             } else {
                 ds_put_format(&match, " && (%s)", acl->match);
                 build_acl_log(&actions, acl);
                 ds_put_cstr(&actions, "/* drop */");
-                ovn_lflow_add(lflows, od, stage,
-                              acl->priority + OVN_ACL_PRI_OFFSET,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, stage,
+                                        acl->priority + OVN_ACL_PRI_OFFSET,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &acl->header_);
             }
             /* For an existing connection without ct_label set, we've
              * encountered a policy change. ACLs previously allowed
@@ -4961,14 +4992,15 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
             ds_put_cstr(&actions, "ct_commit(ct_label=1/1); ");
             if (!strcmp(acl->action, "reject")) {
                 build_reject_acl_rules(od, lflows, stage, acl, &match,
-                                       &actions);
+                                       &actions, &acl->header_);
             } else {
                 ds_put_format(&match, " && (%s)", acl->match);
                 build_acl_log(&actions, acl);
                 ds_put_cstr(&actions, "/* drop */");
-                ovn_lflow_add(lflows, od, stage,
-                              acl->priority + OVN_ACL_PRI_OFFSET,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, stage,
+                                        acl->priority + OVN_ACL_PRI_OFFSET,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &acl->header_);
             }
         } else {
             /* There are no stateful ACLs in use on this datapath,
@@ -4976,19 +5008,19 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              * logical flow action in all cases. */
             if (!strcmp(acl->action, "reject")) {
                 build_reject_acl_rules(od, lflows, stage, acl, &match,
-                                       &actions);
+                                       &actions, &acl->header_);
             } else {
                 build_acl_log(&actions, acl);
                 ds_put_cstr(&actions, "/* drop */");
-                ovn_lflow_add(lflows, od, stage,
-                              acl->priority + OVN_ACL_PRI_OFFSET,
-                              acl->match, ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, stage,
+                                        acl->priority + OVN_ACL_PRI_OFFSET,
+                                        acl->match, ds_cstr(&actions),
+                                        &acl->header_);
             }
         }
         ds_destroy(&match);
         ds_destroy(&actions);
     }
-    free(stage_hint);
 }
 
 static struct ovn_port_group *
@@ -5191,9 +5223,9 @@  build_acls(struct ovn_datapath *od, struct hmap *lflows,
                               "&& ip4.src == %s && udp && udp.src == 67 "
                               "&& udp.dst == 68", od->nbs->ports[i]->name,
                               server_mac, server_id);
-                ovn_lflow_add(
+                ovn_lflow_add_with_hint(
                     lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
-                    actions);
+                    actions, &od->nbs->ports[i]->dhcpv4_options->header_);
                 ds_destroy(&match);
             }
         }
@@ -5218,9 +5250,9 @@  build_acls(struct ovn_datapath *od, struct hmap *lflows,
                               "&& ip6.src == %s && udp && udp.src == 547 "
                               "&& udp.dst == 546", od->nbs->ports[i]->name,
                               server_mac, server_ip);
-                ovn_lflow_add(
+                ovn_lflow_add_with_hint(
                     lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
-                    actions);
+                    actions, &od->nbs->ports[i]->dhcpv6_options->header_);
                 ds_destroy(&match);
             }
         }
@@ -5257,9 +5289,10 @@  build_qos(struct ovn_datapath *od, struct hmap *lflows) {
 
                 ds_put_format(&dscp_action, "ip.dscp = %"PRId64"; next;",
                               qos->value_action[j]);
-                ovn_lflow_add(lflows, od, stage,
-                              qos->priority,
-                              qos->match, ds_cstr(&dscp_action));
+                ovn_lflow_add_with_hint(lflows, od, stage,
+                                        qos->priority,
+                                        qos->match, ds_cstr(&dscp_action),
+                                        &qos->header_);
                 ds_destroy(&dscp_action);
             }
         }
@@ -5288,9 +5321,10 @@  build_qos(struct ovn_datapath *od, struct hmap *lflows) {
              *
              * We limit the bandwidth of this flow by adding a meter table.
              */
-            ovn_lflow_add(lflows, od, stage,
-                          qos->priority,
-                          qos->match, ds_cstr(&meter_action));
+            ovn_lflow_add_with_hint(lflows, od, stage,
+                                    qos->priority,
+                                    qos->match, ds_cstr(&meter_action),
+                                    &qos->header_);
             ds_destroy(&meter_action);
         }
     }
@@ -5405,11 +5439,13 @@  build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
                     ds_put_format(&match, " && tcp.dst == %d",
                                   lb_vip->vip_port);
                 }
-                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
-                              120, ds_cstr(&match), ds_cstr(&action));
+                ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 120,
+                                        ds_cstr(&match), ds_cstr(&action),
+                                        &lb->nlb->header_);
             } else {
-                ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
-                              110, ds_cstr(&match), ds_cstr(&action));
+                ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 110,
+                                        ds_cstr(&match), ds_cstr(&action),
+                                        &lb->nlb->header_);
             }
 
             ds_destroy(&match);
@@ -5574,7 +5610,8 @@  build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
                                         struct ovn_port *patch_op,
                                         struct ovn_datapath *od,
                                         uint32_t priority,
-                                        struct hmap *lflows)
+                                        struct hmap *lflows,
+                                        const struct ovsdb_idl_row *stage_hint)
 {
     struct ds match   = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
@@ -5605,8 +5642,8 @@  build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
      * in the broadcast domain.
      */
     ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
-    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
 
     ds_destroy(&match);
     ds_destroy(&actions);
@@ -5623,7 +5660,8 @@  static void
 build_lswitch_rport_arp_req_flows(struct ovn_port *op,
                                   struct ovn_datapath *sw_od,
                                   struct ovn_port *sw_op,
-                                  struct hmap *lflows)
+                                  struct hmap *lflows,
+                                  const struct ovsdb_idl_row *stage_hint)
 {
     if (!op || !op->nbrp) {
         return;
@@ -5677,11 +5715,13 @@  build_lswitch_rport_arp_req_flows(struct ovn_port *op,
 
     if (!sset_is_empty(&all_ips_v4)) {
         build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
-                                                sw_od, 75, lflows);
+                                                sw_od, 75, lflows,
+                                                stage_hint);
     }
     if (!sset_is_empty(&all_ips_v6)) {
         build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
-                                                sw_od, 75, lflows);
+                                                sw_od, 75, lflows,
+                                                stage_hint);
     }
 
     sset_destroy(&all_ips_v4);
@@ -5752,8 +5792,9 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
             (!strcmp(op->nbsp->type, "vtep"))) {
             ds_clear(&match);
             ds_put_format(&match, "inport == %s", op->json_key);
-            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
-                          ds_cstr(&match), "next;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
+                                    100, ds_cstr(&match), "next;",
+                                    &op->nbsp->header_);
         }
     }
 
@@ -5806,8 +5847,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                     "bind_vport(%s, inport); "
                     "next;",
                     op->json_key);
-                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, op->od,
+                                        S_SWITCH_IN_ARP_ND_RSP, 100,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &vp->nbsp->header_);
             }
 
             free(tokstr);
@@ -5846,8 +5889,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                         "output;",
                         op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
                         op->lsp_addrs[i].ipv4_addrs[j].addr_s);
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
-                                ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_ARP_ND_RSP, 50,
+                                            ds_cstr(&match),
+                                            ds_cstr(&actions),
+                                            &op->nbsp->header_);
 
                     /* Do not reply to an ARP request from the port that owns
                      * the address (otherwise a DHCP client that ARPs to check
@@ -5862,8 +5908,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                      * network is not working as configured, so dropping the
                      * request would frustrate that intent.) */
                     ds_put_format(&match, " && inport == %s", op->json_key);
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
-                                ds_cstr(&match), "next;");
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_ARP_ND_RSP, 100,
+                                            ds_cstr(&match), "next;",
+                                            &op->nbsp->header_);
                 }
 
                 /* For ND solicitations, we need to listen for both the
@@ -5894,14 +5942,19 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                             op->lsp_addrs[i].ipv6_addrs[j].addr_s,
                             op->lsp_addrs[i].ipv6_addrs[j].addr_s,
                             op->lsp_addrs[i].ea_s);
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
-                                ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_ARP_ND_RSP, 50,
+                                            ds_cstr(&match),
+                                            ds_cstr(&actions),
+                                            &op->nbsp->header_);
 
                     /* Do not reply to a solicitation from the port that owns
                      * the address (otherwise DAD detection will fail). */
                     ds_put_format(&match, " && inport == %s", op->json_key);
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
-                                ds_cstr(&match), "next;");
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_ARP_ND_RSP, 100,
+                                            ds_cstr(&match), "next;",
+                                            &op->nbsp->header_);
                 }
             }
         }
@@ -5949,9 +6002,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                     "output;",
                     svc_monitor_mac, svc_monitor_mac,
                     lb->vips[i].backends[j].svc_mon_src_ip);
-                ovn_lflow_add(lflows, lb->vips[i].backends[j].op->od,
-                              S_SWITCH_IN_ARP_ND_RSP, 110,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows,
+                                        lb->vips[i].backends[j].op->od,
+                                        S_SWITCH_IN_ARP_ND_RSP, 110,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &lb->nlb->header_);
             }
         }
     }
@@ -5985,6 +6040,14 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
         }
 
         for (size_t i = 0; i < op->n_lsp_addrs; i++) {
+            struct ovsdb_idl_row *stage_hint;
+
+            if (op->nbsp->dhcpv4_options) {
+                stage_hint = &op->nbsp->dhcpv4_options->header_;
+            } else {
+                stage_hint = NULL;
+            }
+
             for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
                 struct ds options_action = DS_EMPTY_INITIALIZER;
                 struct ds response_action = DS_EMPTY_INITIALIZER;
@@ -6006,9 +6069,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                                       op->json_key);
                     }
 
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
-                                  100, ds_cstr(&match),
-                                  ds_cstr(&options_action));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
+                                            ds_cstr(&match),
+                                            ds_cstr(&options_action),
+                                            stage_hint);
                     ds_clear(&match);
                     /* Allow ip4.src = OFFER_IP and
                      * ip4.dst = {SERVER_IP, 255.255.255.255} for the below
@@ -6030,9 +6095,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                                       op->json_key);
                     }
 
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
-                                  100, ds_cstr(&match),
-                                  ds_cstr(&options_action));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
+                                            ds_cstr(&match),
+                                            ds_cstr(&options_action),
+                                            stage_hint);
                     ds_clear(&match);
 
                     /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
@@ -6050,9 +6117,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                                       op->json_key);
                     }
 
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
-                                  100, ds_cstr(&match),
-                                  ds_cstr(&response_action));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_DHCP_RESPONSE, 100,
+                                            ds_cstr(&match),
+                                            ds_cstr(&response_action),
+                                            stage_hint);
                     ds_destroy(&options_action);
                     ds_destroy(&response_action);
                     ds_destroy(&ipv4_addr_match);
@@ -6060,6 +6129,12 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                 }
             }
 
+            if (op->nbsp->dhcpv6_options) {
+                stage_hint = &op->nbsp->dhcpv6_options->header_;
+            } else {
+                stage_hint = NULL;
+            }
+
             for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
                 struct ds options_action = DS_EMPTY_INITIALIZER;
                 struct ds response_action = DS_EMPTY_INITIALIZER;
@@ -6080,14 +6155,20 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                                       op->json_key);
                     }
 
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
-                                  ds_cstr(&match), ds_cstr(&options_action));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_DHCP_OPTIONS, 100,
+                                            ds_cstr(&match),
+                                            ds_cstr(&options_action),
+                                            stage_hint);
 
                     /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
                      * put_dhcpv6_opts action is successful */
                     ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
-                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100,
-                                  ds_cstr(&match), ds_cstr(&response_action));
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_SWITCH_IN_DHCP_RESPONSE, 100,
+                                            ds_cstr(&match),
+                                            ds_cstr(&response_action),
+                                            stage_hint);
                     ds_destroy(&options_action);
                     ds_destroy(&response_action);
                     break;
@@ -6172,9 +6253,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                             op->od->localnet_port->json_key,
                             op->lsp_addrs[i].ea_s, op->json_key,
                             rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
-                        ovn_lflow_add(lflows, op->od,
-                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
-                                      ds_cstr(&match), "drop;");
+                        ovn_lflow_add_with_hint(lflows, op->od,
+                                                S_SWITCH_IN_EXTERNAL_PORT,
+                                                100, ds_cstr(&match), "drop;",
+                                                &op->nbsp->header_);
                     }
                     for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs;
                          l++) {
@@ -6189,9 +6271,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                             rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
                             rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
                             rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
-                        ovn_lflow_add(lflows, op->od,
-                                      S_SWITCH_IN_EXTERNAL_PORT, 100,
-                                      ds_cstr(&match), "drop;");
+                        ovn_lflow_add_with_hint(lflows, op->od,
+                                                S_SWITCH_IN_EXTERNAL_PORT, 100,
+                                                ds_cstr(&match), "drop;",
+                                                &op->nbsp->header_);
                     }
                 }
             }
@@ -6326,7 +6409,8 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
          * requests only to the router port that owns the IP address.
          */
         if (!strcmp(op->nbsp->type, "router")) {
-            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows);
+            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
+                                              &op->nbsp->header_);
         }
 
         for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
@@ -6342,8 +6426,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
 
                 ds_clear(&actions);
                 ds_put_format(&actions, "outport = %s; output;", op->json_key);
-                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
+                                        50, ds_cstr(&match),
+                                        ds_cstr(&actions),
+                                        &op->nbsp->header_);
             } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
                 if (lsp_is_enabled(op->nbsp)) {
                     ovn_multicast_add(mcgroups, &mc_unknown, op);
@@ -6361,8 +6447,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
 
                 ds_clear(&actions);
                 ds_put_format(&actions, "outport = %s; output;", op->json_key);
-                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
+                                        50, ds_cstr(&match),
+                                        ds_cstr(&actions),
+                                        &op->nbsp->header_);
             } else if (!strcmp(op->nbsp->addresses[i], "router")) {
                 if (!op->peer || !op->peer->nbrp
                     || !ovs_scan(op->peer->nbrp->mac,
@@ -6403,8 +6491,10 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
 
                 ds_clear(&actions);
                 ds_put_format(&actions, "outport = %s; output;", op->json_key);
-                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, op->od,
+                                        S_SWITCH_IN_L2_LKUP, 50,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &op->nbsp->header_);
 
                 /* Add ethernet addresses specified in NAT rules on
                  * distributed logical routers. */
@@ -6426,9 +6516,11 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                             ds_clear(&actions);
                             ds_put_format(&actions, "outport = %s; output;",
                                           op->json_key);
-                            ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP,
-                                          50, ds_cstr(&match),
-                                          ds_cstr(&actions));
+                            ovn_lflow_add_with_hint(lflows, op->od,
+                                                    S_SWITCH_IN_L2_LKUP, 50,
+                                                    ds_cstr(&match),
+                                                    ds_cstr(&actions),
+                                                    &op->nbsp->header_);
                         }
                     }
                 }
@@ -6544,7 +6636,8 @@  get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
 static void
 build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
                           struct hmap *ports,
-                          const struct nbrec_logical_router_policy *rule)
+                          const struct nbrec_logical_router_policy *rule,
+                          const struct ovsdb_idl_row *stage_hint)
 {
     struct ds match = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
@@ -6584,8 +6677,9 @@  build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
         ds_put_cstr(&actions, "next;");
     }
     ds_put_format(&match, "%s", rule->match);
-    ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
-                  ds_cstr(&match), ds_cstr(&actions));
+
+    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
     ds_destroy(&match);
     ds_destroy(&actions);
 }
@@ -6654,8 +6748,9 @@  add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
                       REGBIT_NAT_REDIRECT" = 0; next;",
                       op->od->l3dgw_port->json_key,
                       nat->external_mac);
-        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
-                      ds_cstr(&match), ds_cstr(&actions));
+        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
+                                ds_cstr(&match), ds_cstr(&actions),
+                                &nat->header_);
         ds_clear(&match);
         ds_clear(&actions);
 
@@ -6702,8 +6797,9 @@  add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
                           family == AF_INET ? "4" : "6",
                           family == AF_INET ? "" : "xx",
                           nat->external_ip);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING,
+                                    400, ds_cstr(&match), ds_cstr(&actions),
+                                    &nat2->header_);
             ds_clear(&match);
             ds_clear(&actions);
         }
@@ -6715,7 +6811,8 @@  add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
 static void
 add_route(struct hmap *lflows, const struct ovn_port *op,
           const char *lrp_addr_s, const char *network_s, int plen,
-          const char *gateway, const char *policy)
+          const char *gateway, const char *policy,
+          const struct ovsdb_idl_row *stage_hint)
 {
     bool is_ipv4 = strchr(network_s, '.') ? true : false;
     struct ds match = DS_EMPTY_INITIALIZER;
@@ -6762,8 +6859,8 @@  add_route(struct hmap *lflows, const struct ovn_port *op,
 
     /* The priority here is calculated to implement longest-prefix-match
      * routing. */
-    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
-                  ds_cstr(&match), ds_cstr(&actions));
+    ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
+                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
     ds_destroy(&match);
     ds_destroy(&actions);
 }
@@ -6771,7 +6868,8 @@  add_route(struct hmap *lflows, const struct ovn_port *op,
 static void
 build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
                         struct hmap *ports,
-                        const struct nbrec_logical_router_static_route *route)
+                        const struct nbrec_logical_router_static_route *route,
+                        const struct ovsdb_idl_row *stage_hint)
 {
     ovs_be32 nexthop;
     const char *lrp_addr_s = NULL;
@@ -6894,7 +6992,7 @@  build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
 
     char *policy = route->policy ? route->policy : "dst-ip";
     add_route(lflows, out_port, lrp_addr_s, prefix_s, plen, route->nexthop,
-              policy);
+              policy, stage_hint);
 
 free_prefix_s:
     free(prefix_s);
@@ -6977,35 +7075,38 @@  add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
                    const char *lb_force_snat_ip, struct smap_node *lb_info,
                    bool is_udp, int addr_family, char *ip_addr,
                    uint16_t l4_port, struct nbrec_load_balancer *lb,
-                   struct shash *meter_groups)
+                   struct shash *meter_groups,
+                   const struct ovsdb_idl_row *stage_hint)
 {
     char *backend_ips = lb_info->value;
 
     build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
                               l4_port, addr_family, S_ROUTER_IN_DNAT,
-                              meter_groups);
+                              meter_groups, stage_hint);
 
     /* A match and actions for new connections. */
     char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
     if (lb_force_snat_ip) {
         char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s",
                                       ds_cstr(actions));
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
-                      new_actions);
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
+                                new_match, new_actions, stage_hint);
         free(new_actions);
     } else {
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
-                      ds_cstr(actions));
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
+                                new_match, ds_cstr(actions), stage_hint);
     }
 
     /* A match and actions for established connections. */
     char *est_match = xasprintf("ct.est && %s", ds_cstr(match));
     if (lb_force_snat_ip) {
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
-                      "flags.force_snat_for_lb = 1; ct_dnat;");
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
+                                est_match,
+                                "flags.force_snat_for_lb = 1; ct_dnat;",
+                                stage_hint);
     } else {
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
-                      "ct_dnat;");
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
+                                est_match, "ct_dnat;", stage_hint);
     }
 
     free(new_match);
@@ -7068,12 +7169,14 @@  add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
                  "is_chassis_resident(%s)", od->l3dgw_port->json_key,
                  od->l3redirect_port->json_key);
     if (lb_force_snat_ip) {
-        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
-                      ds_cstr(&undnat_match),
-                      "flags.force_snat_for_lb = 1; ct_dnat;");
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
+                                ds_cstr(&undnat_match),
+                                "flags.force_snat_for_lb = 1; ct_dnat;",
+                                stage_hint);
     } else {
-        ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
-                      ds_cstr(&undnat_match), "ct_dnat;");
+        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
+                                ds_cstr(&undnat_match), "ct_dnat;",
+                                stage_hint);
     }
 
     ds_destroy(&undnat_match);
@@ -7219,8 +7322,8 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
         ds_clear(&match);
         ds_put_format(&match, "eth.mcast && inport == %s", op->json_key);
-        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                      ds_cstr(&match), "next;");
+        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
+                                ds_cstr(&match), "next;", &op->nbrp->header_);
 
         ds_clear(&match);
         ds_put_format(&match, "eth.dst == %s && inport == %s",
@@ -7232,8 +7335,8 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_put_format(&match, " && is_chassis_resident(%s)",
                           op->od->l3redirect_port->json_key);
         }
-        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                      ds_cstr(&match), "next;");
+        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
+                                ds_cstr(&match), "next;", &op->nbrp->header_);
     }
 
     /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
@@ -7318,10 +7421,12 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 ds_put_format(&match, " && is_chassis_resident(%s)",
                               op->od->l3redirect_port->json_key);
             }
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
-                          ds_cstr(&match),
-                          REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
-                          "lookup_arp(inport, arp.spa, arp.sha); next;");
+            ovn_lflow_add_with_hint(lflows, op->od,
+                                    S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
+                                    ds_cstr(&match),
+                                    REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
+                                    "lookup_arp(inport, arp.spa, arp.sha); "
+                                    "next;", &op->nbrp->header_);
         }
     }
 
@@ -7391,8 +7496,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_put_cstr(&match, "ip4.src == ");
             op_put_v4_networks(&match, op, true);
             ds_put_cstr(&match, " && "REGBIT_EGRESS_LOOPBACK" == 0");
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
-                          ds_cstr(&match), "drop;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
+                                    ds_cstr(&match), "drop;",
+                                    &op->nbrp->header_);
 
             /* ICMP echo reply.  These flows reply to ICMP echo requests
              * received for the router's IP address. Since packets only
@@ -7411,8 +7517,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 "icmp4.type = 0; "
                 "flags.loopback = 1; "
                 "next; ");
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
 
         /* ICMP time exceeded */
@@ -7433,8 +7540,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           "ip.ttl = 255; "
                           "next; };",
                           op->lrp_networks.ipv4_addrs[i].addr_s);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
 
         /* ARP reply.  These flows reply to ARP requests for the router's own
@@ -7495,8 +7603,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 op->lrp_networks.ea_s,
                 op->lrp_networks.ipv4_addrs[i].addr_s,
                 op->json_key);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
 
         /* A set to hold all load-balancer vips that need ARP responses. */
@@ -7740,8 +7849,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     IP_ARGS(ip),
                     op->json_key);
             }
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
-                          ds_cstr(&match), ds_cstr(&actions));
+
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &nat->header_);
         }
 
         if (!smap_get(&op->od->nbr->options, "chassis")
@@ -7759,8 +7870,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                      "icmp4.type = 3; "
                                      "icmp4.code = 3; "
                                      "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
-                              ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
 
                 ds_clear(&match);
                 ds_put_format(&match,
@@ -7770,8 +7882,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                          "eth.dst <-> eth.src; "
                          "ip4.dst <-> ip4.src; "
                          "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
-                              ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
 
                 ds_clear(&match);
                 ds_put_format(&match,
@@ -7784,8 +7897,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                          "icmp4.type = 3; "
                          "icmp4.code = 2; "
                          "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
-                              ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        70, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
             }
         }
 
@@ -7844,8 +7958,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
         if (has_drop_ips) {
             /* Drop IP traffic to this router. */
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
-                          ds_cstr(&match), "drop;");
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
+                                    ds_cstr(&match), "drop;",
+                                    &op->nbrp->header_);
         }
 
         free(snat_ips);
@@ -7878,8 +7993,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         "icmp6.type = 129; "
                         "flags.loopback = 1; "
                         "next; ");
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
 
         /* ND reply.  These flows reply to ND solicitations for the
@@ -7919,8 +8035,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           op->lrp_networks.ipv6_addrs[i].addr_s,
                           op->lrp_networks.ipv6_addrs[i].addr_s,
                           op->lrp_networks.ea_s);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
 
         /* UDP/TCP port unreachable */
@@ -7935,8 +8052,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                      "eth.dst <-> eth.src; "
                                      "ip6.dst <-> ip6.src; "
                                      "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
-                          ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
 
                 ds_clear(&match);
                 ds_put_format(&match,
@@ -7949,8 +8067,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                          "icmp6.type = 1; "
                          "icmp6.code = 4; "
                          "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
-                              ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
 
                 ds_clear(&match);
                 ds_put_format(&match,
@@ -7963,8 +8082,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                          "icmp6.type = 1; "
                          "icmp6.code = 3; "
                          "next; };";
-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
-                              ds_cstr(&match), action);
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        70, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
             }
         }
 
@@ -7995,8 +8115,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           "icmp6.code = 0; /* TTL exceeded in transit */ "
                           "next; };",
                           op->lrp_networks.ipv6_addrs[i].addr_s);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
     }
 
@@ -8135,8 +8256,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                        ds_put_cstr(&actions, "ct_snat;");
                     }
 
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
-                                  ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
+                                            90, ds_cstr(&match),
+                                            ds_cstr(&actions),
+                                            &nat->header_);
                 } else {
                     /* Distributed router. */
 
@@ -8162,8 +8285,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         ds_put_cstr(&actions, "ct_snat;");
                     }
 
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
-                                  ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
+                                            100, ds_cstr(&match),
+                                            ds_cstr(&actions),
+                                            &nat->header_);
 
                     /* Traffic received on other router ports must be
                      * redirected to the central instance of the l3dgw_port
@@ -8172,9 +8297,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, "ip && ip%s.dst == %s",
                                   is_v6 ? "6" : "4",
                                   nat->external_ip);
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
-                                  ds_cstr(&match),
-                                  REGBIT_NAT_REDIRECT" = 1; next;");
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT,
+                                            50, ds_cstr(&match),
+                                            REGBIT_NAT_REDIRECT" = 1; next;",
+                                            &nat->header_);
                 }
             }
 
@@ -8210,8 +8336,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                       "ct_dnat(%s);", nat->logical_ip);
                     }
 
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
-                                  ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &nat->header_);
                 } else {
                     /* Distributed router. */
 
@@ -8238,8 +8365,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                       nat->logical_ip);
                     }
 
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
-                                  ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &nat->header_);
 
                     /* Traffic received on other router ports must be
                      * redirected to the central instance of the l3dgw_port
@@ -8248,9 +8376,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, "ip && ip%s.dst == %s",
                                   is_v6 ? "6" : "4",
                                   nat->external_ip);
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
-                                  ds_cstr(&match),
-                                  REGBIT_NAT_REDIRECT" = 1; next;");
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, 50,
+                                            ds_cstr(&match),
+                                            REGBIT_NAT_REDIRECT" = 1; next;",
+                                            &nat->header_);
                 }
             }
 
@@ -8289,8 +8418,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&actions, "ct_dnat;");
                 }
 
-                ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &nat->header_);
             }
 
             /* Egress SNAT table: Packets enter the egress pipeline with
@@ -8317,9 +8447,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     /* The priority here is calculated such that the
                      * nat->logical_ip with the longest mask gets a higher
                      * priority. */
-                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
-                                  count_1bits(ntohl(mask)) + 1,
-                                  ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_SNAT,
+                                            count_1bits(ntohl(mask)) + 1,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &nat->header_);
                 } else {
                     uint16_t priority = count_1bits(ntohl(mask)) + 1;
 
@@ -8354,9 +8485,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     /* The priority here is calculated such that the
                      * nat->logical_ip with the longest mask gets a higher
                      * priority. */
-                    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
-                                  priority, ds_cstr(&match),
-                                  ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_SNAT,
+                                            priority, ds_cstr(&match),
+                                            ds_cstr(&actions),
+                                            &nat->header_);
                 }
             }
 
@@ -8373,8 +8505,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                               ETH_ADDR_ARGS(mac),
                               od->l3dgw_port->json_key,
                               nat->logical_port);
-                ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 50,
-                              ds_cstr(&match), "next;");
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ADMISSION, 50,
+                                        ds_cstr(&match), "next;",
+                                        &nat->header_);
             }
 
             /* Ingress Gateway Redirect Table: For NAT on a distributed
@@ -8387,8 +8520,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                               is_v6 ? "6" : "4",
                               nat->logical_ip,
                               od->l3dgw_port->json_key);
-                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
-                              ds_cstr(&match), "next;");
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
+                                        100, ds_cstr(&match), "next;",
+                                        &nat->header_);
             }
 
             /* Egress Loopback table: For NAT on a distributed router.
@@ -8422,16 +8556,21 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                       "flags = 0; flags.loopback = 1; "
                                       REGBIT_EGRESS_LOOPBACK" = 1; "
                                       "next(pipeline=ingress, table=0); ");
-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300,
-                                      ds_cstr(&match),  ds_cstr(&actions));
+                        ovn_lflow_add_with_hint(lflows, od,
+                                                S_ROUTER_OUT_EGR_LOOP, 300,
+                                                ds_cstr(&match),
+                                                ds_cstr(&actions),
+                                                &nat2->header_);
 
                         ds_clear(&match);
                         ds_put_format(&match,
                                       "ip%s.src == %s && ip%s.dst == %s",
                                       is_v6 ? "6" : "4", nat2->external_ip,
                                       is_v6 ? "6" : "4", nat->external_ip);
-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
-                                      ds_cstr(&match), "next;");
+                        ovn_lflow_add_with_hint(lflows, od,
+                                                S_ROUTER_OUT_EGR_LOOP, 200,
+                                                ds_cstr(&match), "next;",
+                                                &nat2->header_);
                         ds_clear(&match);
                     }
                 }
@@ -8451,8 +8590,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 }
                 ds_put_format(&actions, REGBIT_EGRESS_LOOPBACK" = 1; "
                               "next(pipeline=ingress, table=0); };");
-                ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
+                                        ds_cstr(&match), ds_cstr(&actions),
+                                        &nat->header_);
             }
         }
 
@@ -8591,8 +8731,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         ds_put_format(&match, "ip && ip6.dst == %s",
                                       ip_address);
                     }
-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
-                                  100, ds_cstr(&match), "ct_next;");
+                    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG,
+                                            100, ds_cstr(&match), "ct_next;",
+                                            &lb->header_);
                 }
 
                 /* Higher priority rules are added for load-balancing in DNAT
@@ -8633,7 +8774,8 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 add_router_lb_flow(lflows, od, &match, &actions, prio,
                                    lb_force_snat_ip, node, is_udp,
                                    addr_family, ip_address, port, lb,
-                                   meter_groups);
+                                   meter_groups,
+                                   &lb->header_);
 
                 free(ip_address);
             }
@@ -8706,8 +8848,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
         if (add_rs_response_flow) {
             ds_put_cstr(&actions, "); next;");
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, 50,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS,
+                                    50, ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
             ds_clear(&actions);
             ds_clear(&match);
             ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && "
@@ -8723,8 +8866,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           "outport = inport; flags.loopback = 1; "
                           "output;",
                           op->lrp_networks.ea_s, ip6_str);
-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_RESPONSE, 50,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, op->od,
+                                    S_ROUTER_IN_ND_RA_RESPONSE, 50,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &op->nbrp->header_);
         }
     }
 
@@ -8758,13 +8903,15 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
         for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
             add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
                       op->lrp_networks.ipv4_addrs[i].network_s,
-                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL);
+                      op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL,
+                      &op->nbrp->header_);
         }
 
         for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
             add_route(lflows, op, op->lrp_networks.ipv6_addrs[i].addr_s,
                       op->lrp_networks.ipv6_addrs[i].network_s,
-                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL);
+                      op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL,
+                      &op->nbrp->header_);
         }
     }
 
@@ -8778,7 +8925,7 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             const struct nbrec_logical_router_static_route *route;
 
             route = od->nbr->static_routes[i];
-            build_static_route_flow(lflows, od, ports, route);
+            build_static_route_flow(lflows, od, ports, route, &route->header_);
         }
     }
 
@@ -8849,7 +8996,7 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
         for (int i = 0; i < od->nbr->n_policies; i++) {
             const struct nbrec_logical_router_policy *rule
                 = od->nbr->policies[i];
-            build_routing_policy_flow(lflows, od, ports, rule);
+            build_routing_policy_flow(lflows, od, ports, rule, &rule->header_);
         }
     }
 
@@ -8900,8 +9047,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_clear(&actions);
                     ds_put_format(&actions, "eth.dst = %s; next;",
                                   op->lrp_networks.ea_s);
-                    ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
-                                  100, ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, op->peer->od,
+                                            S_ROUTER_IN_ARP_RESOLVE, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &op->nbrp->header_);
                 }
 
                 if (op->lrp_networks.n_ipv6_addrs) {
@@ -8913,8 +9062,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_clear(&actions);
                     ds_put_format(&actions, "eth.dst = %s; next;",
                                   op->lrp_networks.ea_s);
-                    ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
-                                  100, ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, op->peer->od,
+                                            S_ROUTER_IN_ARP_RESOLVE, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &op->nbrp->header_);
                 }
             }
 
@@ -8935,8 +9086,11 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_clear(&actions);
                     ds_put_format(&actions, "eth.dst = %s; next;",
                                   op->lrp_networks.ea_s);
-                    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ARP_RESOLVE,
-                                  50, ds_cstr(&match), ds_cstr(&actions));
+
+                    ovn_lflow_add_with_hint(lflows, op->od,
+                                            S_ROUTER_IN_ARP_RESOLVE, 50,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &op->nbrp->header_);
                 }
             }
         } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")
@@ -8977,9 +9131,11 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
                         ds_clear(&actions);
                         ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
-                        ovn_lflow_add(lflows, peer->od,
-                                      S_ROUTER_IN_ARP_RESOLVE, 100,
-                                      ds_cstr(&match), ds_cstr(&actions));
+                        ovn_lflow_add_with_hint(lflows, peer->od,
+                                                S_ROUTER_IN_ARP_RESOLVE, 100,
+                                                ds_cstr(&match),
+                                                ds_cstr(&actions),
+                                                &op->nbsp->header_);
                     }
                 }
 
@@ -9011,9 +9167,11 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
                         ds_clear(&actions);
                         ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
-                        ovn_lflow_add(lflows, peer->od,
-                                      S_ROUTER_IN_ARP_RESOLVE, 100,
-                                      ds_cstr(&match), ds_cstr(&actions));
+                        ovn_lflow_add_with_hint(lflows, peer->od,
+                                                S_ROUTER_IN_ARP_RESOLVE, 100,
+                                                ds_cstr(&match),
+                                                ds_cstr(&actions),
+                                                &op->nbsp->header_);
                     }
                 }
             }
@@ -9061,9 +9219,11 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         ds_clear(&actions);
                         ds_put_format(&actions,
                                       "eth.dst = 00:00:00:00:00:00; next;");
-                        ovn_lflow_add(lflows, peer->od,
-                                        S_ROUTER_IN_ARP_RESOLVE, 100,
-                                        ds_cstr(&match), ds_cstr(&actions));
+                        ovn_lflow_add_with_hint(lflows, peer->od,
+                                                S_ROUTER_IN_ARP_RESOLVE, 100,
+                                                ds_cstr(&match),
+                                                ds_cstr(&actions),
+                                                &op->nbsp->header_);
                         break;
                     }
                 }
@@ -9104,9 +9264,11 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
                         ds_clear(&actions);
                         ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
-                        ovn_lflow_add(lflows, peer->od,
-                                        S_ROUTER_IN_ARP_RESOLVE, 100,
-                                        ds_cstr(&match), ds_cstr(&actions));
+                        ovn_lflow_add_with_hint(lflows, peer->od,
+                                                S_ROUTER_IN_ARP_RESOLVE, 100,
+                                                ds_cstr(&match),
+                                                ds_cstr(&actions),
+                                                &op->nbsp->header_);
                         found_vip_network = true;
                         break;
                     }
@@ -9159,8 +9321,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_clear(&actions);
                     ds_put_format(&actions, "eth.dst = %s; next;",
                                               router_port->lrp_networks.ea_s);
-                    ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
-                                  100, ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, peer->od,
+                                            S_ROUTER_IN_ARP_RESOLVE, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &op->nbsp->header_);
                 }
 
                 if (router_port->lrp_networks.n_ipv6_addrs) {
@@ -9172,8 +9336,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_clear(&actions);
                     ds_put_format(&actions, "eth.dst = %s; next;",
                                   router_port->lrp_networks.ea_s);
-                    ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
-                                  100, ds_cstr(&match), ds_cstr(&actions));
+                    ovn_lflow_add_with_hint(lflows, peer->od,
+                                            S_ROUTER_IN_ARP_RESOLVE, 100,
+                                            ds_cstr(&match), ds_cstr(&actions),
+                                            &op->nbsp->header_);
                 }
             }
         }
@@ -9234,8 +9400,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_put_format(&actions,
                           REGBIT_PKT_LARGER" = check_pkt_larger(%d);"
                           " next;", gw_mtu);
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &od->l3dgw_port->nbrp->header_);
 
             for (size_t i = 0; i < od->nbr->n_ports; i++) {
                 struct ovn_port *rp = ovn_port_find(ports,
@@ -9266,8 +9433,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     rp->lrp_networks.ea_s,
                     rp->lrp_networks.ipv4_addrs[0].addr_s,
                     gw_mtu - 18);
-                ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 50,
-                              ds_cstr(&match), ds_cstr(&actions));
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_LARGER_PKTS,
+                                        50, ds_cstr(&match), ds_cstr(&actions),
+                                        &rp->nbrp->header_);
             }
         }
     }
@@ -9284,8 +9452,15 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
         if (od->l3dgw_port && od->l3redirect_port) {
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
-                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
+            const struct ovsdb_idl_row *stage_hint = NULL;
+
+            if (od->l3dgw_port->nbrp) {
+                stage_hint = &od->l3dgw_port->nbrp->header_;
+            }
+
+            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
+                                    REGBIT_DISTRIBUTED_NAT" == 1", "next;",
+                                    stage_hint);
 
             /* For traffic with outport == l3dgw_port, if the
              * packet did not match any higher priority redirect
@@ -9297,8 +9472,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_clear(&actions);
             ds_put_format(&actions, "outport = %s; next;",
                           od->l3redirect_port->json_key);
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
 
             /* If the Ethernet destination has not been resolved,
              * redirect to the central instance of the l3dgw_port.
@@ -9307,8 +9483,9 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
              * table, before being redirected to the central instance.
              */
             ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
-                          ds_cstr(&match), ds_cstr(&actions));
+            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
         }
 
         /* Packets are allowed by default. */
@@ -9357,8 +9534,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           "output; "
                           "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
                           route->nexthop);
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
-                          ds_cstr(&match), ds_cstr(&actions));
+
+            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    &route->header_);
         }
 
         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
@@ -9416,7 +9595,7 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_put_format(&actions, "eth.src = %s; output;",
                           op->lrp_networks.ea_s);
             ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 110,
-                        ds_cstr(&match), ds_cstr(&actions));
+                          ds_cstr(&match), ds_cstr(&actions));
         }
 
         ds_clear(&match);
diff --git a/utilities/ovn-detrace.in b/utilities/ovn-detrace.in
index 3bfb720..e9c3736 100755
--- a/utilities/ovn-detrace.in
+++ b/utilities/ovn-detrace.in
@@ -135,9 +135,74 @@  class CookieHandlerByUUUID(CookieHandler):
         cookie = cookie.zfill(8)
         return self._db.find_rows_by_partial_uuid(self._table, cookie)
 
+class ACLHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(ACLHintHandler, self).__init__(ovnnb_db, 'ACL')
+
+    def print_record(self, acl):
+        output = 'ACL: %s, priority=%s, ' \
+                 'match=(%s), %s' % (acl.direction,
+                                     acl.priority,
+                                     acl.match.strip('"'),
+                                     acl.action)
+        if acl.log:
+            output += ' (log)'
+        print_h(output)
+
+class LSPHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(LSPHintHandler, self).__init__(ovnnb_db, 'Logical_Switch_Port')
+
+    def print_record(self, lsp):
+        print_h('Logical Switch Port: %s type %s (addresses %s, dynamic addresses %s, security %s' % (
+                    lsp.name, lsp.type, lsp.addresses, lsp.dynamic_addresses,
+                    lsp.port_security))
+
+class LRPHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(LRPHintHandler, self).__init__(ovnnb_db, 'Logical_Router_Port')
+
+    def print_record(self, lrp):
+        print_h('Logical Router Port: %s mac %s networks %s ipv6_ra_configs %s' % (
+                    lrp.name, lrp.mac, lrp.networks, lrp.ipv6_ra_configs))
+
+class LoadBalancerHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(LoadBalancerHintHandler, self).__init__(ovnnb_db, 'Load_Balancer')
+
+    def print_record(self, lb):
+        print_h('Load Balancer: %s protocol %s vips %s ip_port_mappings %s' % (
+                    lb.name, lb.protocol, lb.vips, lb.ip_port_mappings))
+
+class NATHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(NATHintHandler, self).__init__(ovnnb_db, 'NAT')
+
+    def print_record(self, nat):
+        print_h('NAT: external IP %s external_mac %s logical_ip %s logical_port %s type %s' % (
+                    nat.external_ip, nat.external_mac, nat.logical_ip,
+                    nat.logical_port, nat.type))
+
+class StaticRouteHintHandler(CookieHandlerByUUUID):
+    def __init__(self, ovnnb_db):
+        super(StaticRouteHintHandler, self).__init__(ovnnb_db,
+                                                     'Logical_Router_Static_Route')
+
+    def print_record(self, route):
+        print_h('Route: %s via %s (port %s), policy=%s' % (
+                    route.ip_prefix, route.nexthop, route.output_port,
+                    route.policy))
+
 class LogicalFlowHandler(CookieHandlerByUUUID):
-    def __init__(self, ovnsb_db):
+    def __init__(self, ovnnb_db, ovnsb_db):
         super(LogicalFlowHandler, self).__init__(ovnsb_db, 'Logical_Flow')
+        self._hint_handlers = [
+            ACLHintHandler(ovnnb_db),
+            LSPHintHandler(ovnnb_db),
+            LRPHintHandler(ovnnb_db),
+            LoadBalancerHintHandler(ovnnb_db),
+            NATHintHandler(ovnnb_db)
+        ]
 
     def print_record(self, lflow):
         print_p('Logical datapath: %s [%s]' %
@@ -151,24 +216,14 @@  class LogicalFlowHandler(CookieHandlerByUUUID):
 
     def print_hint(self, lflow, ovnnb_db):
         external_ids = lflow.external_ids
-        if external_ids.get('stage-name') in ['ls_in_acl',
-                                              'ls_out_acl']:
-            acl_hint = external_ids.get('stage-hint')
-            if not acl_hint:
-                return
-            for i, acl in enumerate(
-                    ovnnb_db.find_rows_by_partial_uuid('ACL', acl_hint)):
+        hint = external_ids.get('stage-hint')
+        if not hint:
+            return
+        for handler in self._hint_handlers:
+            for i, record in enumerate(handler.get_records(hint)):
                 if i > 0:
-                    print_h('[Duplicate uuid ACL hint]')
-
-                output = 'ACL: %s, priority=%s, ' \
-                        'match=(%s), %s' % (acl.direction,
-                                            acl.priority,
-                                            acl.match.strip('"'),
-                                            acl.action)
-                if acl.log:
-                    output += ' (log)'
-                print_h(output)
+                    print_h('[Duplicate uuid hint]')
+                handler.print_record(record)
 
 class PortBindingHandler(CookieHandlerByUUUID):
     def __init__(self, ovnsb_db):
@@ -283,7 +338,7 @@  def main():
     ovsdb_ovnnb = OVSDB(ovnnb_db, 'OVN_Northbound')
 
     cookie_handlers = [
-        LogicalFlowHandler(ovsdb_ovnsb),
+        LogicalFlowHandler(ovsdb_ovnnb, ovsdb_ovnsb),
         PortBindingHandler(ovsdb_ovnsb),
         MacBindingHandler(ovsdb_ovnsb),
         MulticastGroupHandler(ovsdb_ovnsb),