On 11/7/24 21:59, [email protected] wrote:
> From: Numan Siddique <[email protected]>
> 
> If we have a UDP load balancer - 10.0.0.10:80 = 10.0.0.3:8080, in order to
> determine if the load balanced traffic needs to be hairpinned, the
> vip - 10.0.0.10 and the vip port - 80 are stored in the registers before
> the packet is load balanced using the below logical flow -
> 
> table=6 (ls_in_pre_stateful ), priority=120  ,
>   match=(reg0[2] == 1 && ip4.dst == 10.0.0.10 && tcp.dst == 80),
>   action=(reg1 = 10.0.0.10; reg2[0..15] = 80; ct_lb_mark;)
> 
> These registers are used in the later stages to check if the load balanced
> packet needs to be hairpinned or not.
> 
> However, if the packet is fragmented we may not be able to match on the
> L4 fields (tcp, udp or sctp dest port) and this breaks the hairpin
> traffic.
> 
> This patch addressed this issue by making use of ct_nw_dst/ct_ip6_dst and
> ct_tp_dst conntrack fields to determine the hairpin load balanced
> traffic.
> 
> In order to not break hardware offload on certain smart nics, care is taken
> to match on these fields only for fragmented packets.
> 
> Note: Relying on conntrack to reassemble packets is not exactly correct, it
> only accidentaly works with the kernel datapath.  In our internal bug
> tracking system we have this issue to track this incorrect assumption:
> https://issues.redhat.com/browse/FDP-913
> 
> Reported-at: https://issues.redhat.com/browse/FDP-905
> Fixes: 1139b655c996 ("Don't blindly save original dst IP and Port to avoid 
> megaflow unwildcarding.")
> CC: Han Zhou <[email protected]>
> Suggested-by: Dumitru Ceara <[email protected]>
> Signed-off-by: Numan Siddique <[email protected]>
> ---
> 
> v1 -> v2
> ------
>   * Addressed review comments from Dumitru.

Hi Numan,

>From my perspective this looks good.  I did trigger a recheck of the
ovn-kubernetes CI jobs (one was timing out but I think that's just an
unrelated flake).

I left a few small comments below which can be addressed at merge time.
With them addressed:

Acked-by: Dumitru Ceara <[email protected]>

Thanks,
Dumitru

> 
>  controller/lflow.c           |   3 +
>  controller/lflow.h           |   4 +
>  controller/physical.c        |  37 +++++++++
>  include/ovn/actions.h        |  14 +++-
>  include/ovn/logical-fields.h |   4 +
>  lib/actions.c                | 138 +++++++++++++++++++++++++++++----
>  northd/northd.c              |  57 +++++++++++++-
>  ovn-sb.xml                   |  27 +++++++
>  tests/ovn-macros.at          |   3 +
>  tests/ovn-northd.at          |  70 ++++++++++-------
>  tests/ovn.at                 |  56 +++++++++++++-
>  tests/system-ovn-kmod.at     | 143 +++++++++++++++++++++++++++++++++++
>  tests/test-ovn.c             |   3 +
>  utilities/ovn-trace.c        |   6 ++
>  14 files changed, 514 insertions(+), 51 deletions(-)
> 
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 13c3a0d73e..76b4952fd4 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -886,6 +886,9 @@ add_matches_to_flow_table(const struct sbrec_logical_flow 
> *lflow,
>          .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
>          .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
>          .mac_cache_use_table = OFTABLE_MAC_CACHE_USE,
> +        .ct_nw_dst_load_table = OFTABLE_CT_ORIG_NW_DST_LOAD,
> +        .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD,
> +        .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD,
>          .ctrl_meter_id = ctrl_meter_id,
>          .common_nat_ct_zone = get_common_nat_zone(ldp),
>      };
> diff --git a/controller/lflow.h b/controller/lflow.h
> index e95a016501..58a12ee0fe 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -95,6 +95,10 @@ struct uuid;
>  #define OFTABLE_CHK_LB_AFFINITY          78
>  #define OFTABLE_MAC_CACHE_USE            79
>  #define OFTABLE_CT_ZONE_LOOKUP           80
> +#define OFTABLE_CT_ORIG_NW_DST_LOAD      81
> +#define OFTABLE_CT_ORIG_IP6_DST_LOAD     82
> +#define OFTABLE_CT_ORIG_TP_DST_LOAD      83
> +
>  
>  struct lflow_ctx_in {
>      struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
> diff --git a/controller/physical.c b/controller/physical.c
> index c6db4f376b..3ca4e07835 100644
> --- a/controller/physical.c
> +++ b/controller/physical.c
> @@ -2769,5 +2769,42 @@ physical_run(struct physical_ctx *p_ctx,
>       */
>      add_default_drop_flow(p_ctx, OFTABLE_LOG_TO_PHY, flow_table);
>  
> +    /* Table 81, 82 and 83
> +     * Match on ct.trk and ct.est and store the ct_nw_dst, ct_ip6_dst and
> +     * ct_tp_dst in the registers. */
> +    uint32_t ct_state = OVS_CS_F_TRACKED | OVS_CS_F_ESTABLISHED;
> +    match_init_catchall(&match);
> +    ofpbuf_clear(&ofpacts);
> +
> +    /* Add the flow:
> +     * match = (ct.trk && ct.est), action = (reg8 = ct_tp_dst)
> +     * table = 83
> +     */
> +    match_set_ct_state_masked(&match, ct_state, ct_state);
> +    put_move(MFF_CT_TP_DST, 0,  MFF_LOG_CT_ORIG_TP_DST_PORT, 0, 16, 
> &ofpacts);
> +    ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_TP_DST_LOAD, 100, 0, &match,
> +                    &ofpacts, hc_uuid);
> +
> +    /* Add the flow:
> +     * match = (ct.trk && ct.est && ip4), action = (reg4 = ct_nw_dst)
> +     * table = 81
> +     */
> +    ofpbuf_clear(&ofpacts);
> +    match_set_dl_type(&match, htons(ETH_TYPE_IP));
> +    put_move(MFF_CT_NW_DST, 0,  MFF_LOG_CT_ORIG_NW_DST_ADDR, 0, 32, 
> &ofpacts);
> +    ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_NW_DST_LOAD, 100, 0, &match,
> +                    &ofpacts, hc_uuid);
> +
> +    /* Add the flow:
> +     * match = (ct.trk && ct.est && ip6), action = (xxreg0 = ct_ip6_dst)
> +     * table = 82
> +     */
> +    ofpbuf_clear(&ofpacts);
> +    match_set_dl_type(&match, htons(ETH_TYPE_IPV6));
> +    put_move(MFF_CT_IPV6_DST, 0,  MFF_LOG_CT_ORIG_IP6_DST_ADDR, 0,
> +             128, &ofpacts);
> +    ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_IP6_DST_LOAD, 100, 0, &match,
> +                    &ofpacts, hc_uuid);
> +
>      ofpbuf_uninit(&ofpacts);
>  }
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index 63a12a8821..91a6139b00 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -131,6 +131,9 @@ struct collector_set_ids;
>      OVNACT(CHK_LB_AFF,        ovnact_result)          \
>      OVNACT(SAMPLE,            ovnact_sample)          \
>      OVNACT(MAC_CACHE_USE,     ovnact_null)            \
> +    OVNACT(CT_ORIG_NW_DST,    ovnact_result)          \
> +    OVNACT(CT_ORIG_IP6_DST,   ovnact_result)          \
> +    OVNACT(CT_ORIG_TP_DST,    ovnact_result)          \
>  
>  /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
>  enum OVS_PACKED_ENUM ovnact_type {
> @@ -416,10 +419,11 @@ struct ovnact_set_queue {
>      uint16_t queue_id;
>  };
>  
> -/* OVNACT_DNS_LOOKUP, OVNACT_CHK_LB_HAIRPIN, OVNACT_CHK_LB_HAIRPIN_REPLY. */
> +/* OVNACT_DNS_LOOKUP, OVNACT_CHK_LB_HAIRPIN, OVNACT_CHK_LB_HAIRPIN_REPLY,
> + * OVNACT_CT_ORIG_NW_DST, CT_ORIG_IP6_DST, CT_ORIG_TP_DST */
>  struct ovnact_result {
>      struct ovnact ovnact;
> -    struct expr_field dst;      /* 1-bit destination field. */
> +    struct expr_field dst;      /* destination field. */
>  };
>  
>  /* OVNACT_LOG. */
> @@ -935,6 +939,12 @@ struct ovnact_encode_params {
>                                      this determines which CT zone to use */
>      uint32_t mac_cache_use_table; /* OpenFlow table for 'mac_cache_use'
>                                     * to resubmit. */
> +    uint32_t ct_nw_dst_load_table; /* OpenFlow table for 'ct_nw_dst'
> +                                   *  to resubmit. */
> +    uint32_t ct_ip6_dst_load_table; /* OpenFlow table for 'ct_ip6_dst'
> +                                   *  to resubmit. */
> +    uint32_t ct_tp_dst_load_table; /* OpenFlow table for 'ct_tp_dst'
> +                                   *  to resubmit. */

Nit: the indentation of the multiline comments is a bit off.

>  };
>  
>  void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
> diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> index d6c4a9b6b3..d563e044cb 100644
> --- a/include/ovn/logical-fields.h
> +++ b/include/ovn/logical-fields.h
> @@ -60,6 +60,10 @@ enum ovn_controller_event {
>  #define MFF_LOG_LB_AFF_MATCH_LR_IP6_ADDR    MFF_XXREG1
>  #define MFF_LOG_LB_AFF_MATCH_PORT           MFF_REG8
>  
> +#define MFF_LOG_CT_ORIG_NW_DST_ADDR         MFF_REG4
> +#define MFF_LOG_CT_ORIG_IP6_DST_ADDR        MFF_XXREG0
> +#define MFF_LOG_CT_ORIG_TP_DST_PORT         MFF_REG8
> +
>  void ovn_init_symtab(struct shash *symtab);
>  
>  /* MFF_LOG_FLAGS_REG bit assignments */
> diff --git a/lib/actions.c b/lib/actions.c
> index c5bde996b7..d5fc30b27a 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -3280,9 +3280,10 @@ ovnact_set_queue_free(struct ovnact_set_queue *a 
> OVS_UNUSED)
>  }
>  
>  static void
> -parse_ovnact_result(struct action_context *ctx, const char *name,
> -                    const char *prereq, const struct expr_field *dst,
> -                    struct ovnact_result *res)
> +parse_ovnact_result__(struct action_context *ctx, const char *name,
> +                      const char *prereq, const struct expr_field *dst,
> +                      struct ovnact_result *res,
> +                      int n_bits)
>  {
>      lexer_get(ctx->lexer); /* Skip action name. */
>      lexer_get(ctx->lexer); /* Skip '('. */
> @@ -3290,8 +3291,8 @@ parse_ovnact_result(struct action_context *ctx, const 
> char *name,
>          lexer_error(ctx->lexer, "%s doesn't take any parameters", name);
>          return;
>      }
> -    /* Validate that the destination is a 1-bit, modifiable field. */
> -    char *error = expr_type_check(dst, 1, true, ctx->scope);
> +    /* Validate that the destination is n_bits, modifiable field. */
> +    char *error = expr_type_check(dst, n_bits, true, ctx->scope);
>      if (error) {
>          lexer_error(ctx->lexer, "%s", error);
>          free(error);
> @@ -3304,6 +3305,14 @@ parse_ovnact_result(struct action_context *ctx, const 
> char *name,
>      }
>  }
>  
> +static void
> +parse_ovnact_result(struct action_context *ctx, const char *name,
> +                    const char *prereq, const struct expr_field *dst,
> +                    struct ovnact_result *res)
> +{
> +    parse_ovnact_result__(ctx, name, prereq, dst, res, 1);
> +}
> +
>  static void
>  parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst,
>                   struct ovnact_result *dl)
> @@ -4299,22 +4308,40 @@ format_CHK_LB_HAIRPIN_REPLY(const struct 
> ovnact_result *res, struct ds *s)
>      ds_put_cstr(s, " = chk_lb_hairpin_reply();");
>  }
>  
> +static void
> +encode_result_action___(const struct ovnact_result *res,
> +                        uint8_t resubmit_table,
> +                        enum mf_field_id dst,
> +                        int ofs, int n_bits,
> +                        struct ofpbuf *ofpacts)
> +{
> +    ovs_assert(n_bits <= 128);
> +
> +    struct mf_subfield res_dst = expr_resolve_field(&res->dst);
> +    ovs_assert(res_dst.field);
> +
> +    put_load(0, dst, ofs, n_bits < 64 ? n_bits : 64, ofpacts);
> +    if (n_bits > 64) {
> +        put_load(0, dst, ofs + 64, n_bits - 64, ofpacts);
> +    }
> +
> +    emit_resubmit(ofpacts, resubmit_table);
> +
> +    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
> +    orm->dst = res_dst;
> +    orm->src.field = mf_from_id(dst);
> +    orm->src.ofs = ofs;
> +    orm->src.n_bits = n_bits;
> +}
> +
>  static void
>  encode_result_action__(const struct ovnact_result *res,
>                         uint8_t resubmit_table,
>                         int log_flags_result_bit,
>                         struct ofpbuf *ofpacts)
>  {
> -    struct mf_subfield dst = expr_resolve_field(&res->dst);
> -    ovs_assert(dst.field);
> -    put_load(0, MFF_LOG_FLAGS, log_flags_result_bit, 1, ofpacts);
> -    emit_resubmit(ofpacts, resubmit_table);
> -
> -    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
> -    orm->dst = dst;
> -    orm->src.field = mf_from_id(MFF_LOG_FLAGS);
> -    orm->src.ofs = log_flags_result_bit;
> -    orm->src.n_bits = 1;
> +    encode_result_action___(res, resubmit_table, MFF_LOG_FLAGS,
> +                            log_flags_result_bit, 1, ofpacts);
>  }
>  
>  static void
> @@ -5435,6 +5462,75 @@ encode_MAC_CACHE_USE(const struct ovnact_null *null 
> OVS_UNUSED,
>      emit_resubmit(ofpacts, ep->mac_cache_use_table);
>  }
>  
> +static void
> +encode_CT_ORIG_NW_DST(const struct ovnact_result *res,
> +                      const struct ovnact_encode_params *ep,
> +                      struct ofpbuf *ofpacts)
> +{
> +    encode_result_action___(res, ep->ct_nw_dst_load_table,
> +                            MFF_LOG_CT_ORIG_NW_DST_ADDR, 0, 32, ofpacts);
> +}
> +
> +static void
> +parse_CT_ORIG_NW_DST(struct action_context *ctx, const struct expr_field 
> *dst,
> +                     struct ovnact_result *res)
> +{
> +    parse_ovnact_result__(ctx, "ct_nw_dst", NULL, dst, res, 32);
> +}
> +
> +static void
> +format_CT_ORIG_NW_DST(const struct ovnact_result *res, struct ds *s)
> +{
> +    expr_field_format(&res->dst, s);
> +    ds_put_cstr(s, " = ct_nw_dst();");
> +}
> +
> +static void
> +encode_CT_ORIG_IP6_DST(const struct ovnact_result *res,
> +                       const struct ovnact_encode_params *ep,
> +                       struct ofpbuf *ofpacts)
> +{
> +    encode_result_action___(res, ep->ct_ip6_dst_load_table,
> +                            MFF_LOG_CT_ORIG_IP6_DST_ADDR, 0, 128, ofpacts);
> +}
> +
> +static void
> +parse_CT_ORIG_IP6_DST(struct action_context *ctx, const struct expr_field 
> *dst,
> +                     struct ovnact_result *res)
> +{
> +    parse_ovnact_result__(ctx, "ct_ip6_dst", NULL, dst, res, 128);
> +}
> +
> +static void
> +format_CT_ORIG_IP6_DST(const struct ovnact_result *res, struct ds *s)
> +{
> +    expr_field_format(&res->dst, s);
> +    ds_put_cstr(s, " = ct_ip6_dst();");
> +}
> +
> +static void
> +encode_CT_ORIG_TP_DST(const struct ovnact_result *res,
> +                      const struct ovnact_encode_params *ep OVS_UNUSED,
> +                      struct ofpbuf *ofpacts)
> +{
> +    encode_result_action___(res, ep->ct_tp_dst_load_table,
> +                            MFF_LOG_CT_ORIG_TP_DST_PORT, 0, 16, ofpacts);
> +}
> +
> +static void
> +parse_CT_ORIG_TP_DST(struct action_context *ctx, const struct expr_field 
> *dst,
> +                     struct ovnact_result *res)
> +{
> +    parse_ovnact_result__(ctx, "ct_tp_dst", NULL, dst, res, 16);
> +}
> +
> +static void
> +format_CT_ORIG_TP_DST(const struct ovnact_result *res, struct ds *s)
> +{
> +    expr_field_format(&res->dst, s);
> +    ds_put_cstr(s, " = ct_tp_dst();");
> +}
> +
>  /* Parses an assignment or exchange or put_dhcp_opts action. */
>  static void
>  parse_set_action(struct action_context *ctx)
> @@ -5529,6 +5625,18 @@ parse_set_action(struct action_context *ctx)
>          } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_chk")) {
>              parse_dhcp_relay_chk(
>                  ctx, &lhs, ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
> +        } else if (!strcmp(ctx->lexer->token.s, "ct_nw_dst") &&
> +                   lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> +            parse_CT_ORIG_NW_DST(ctx, &lhs,
> +                                 ovnact_put_CT_ORIG_NW_DST(ctx->ovnacts));
> +        } else if (!strcmp(ctx->lexer->token.s, "ct_ip6_dst") &&
> +                   lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> +            parse_CT_ORIG_IP6_DST(ctx, &lhs,
> +                                  ovnact_put_CT_ORIG_IP6_DST(ctx->ovnacts));
> +        } else if (!strcmp(ctx->lexer->token.s, "ct_tp_dst") &&
> +                   lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> +            parse_CT_ORIG_TP_DST(ctx, &lhs,
> +                                 ovnact_put_CT_ORIG_TP_DST(ctx->ovnacts));
>          } else {
>              parse_assignment_action(ctx, false, &lhs);
>          }
> diff --git a/northd/northd.c b/northd/northd.c
> index 3037ce0b55..985283a98e 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -124,6 +124,7 @@ static bool vxlan_mode;
>  #define REGBIT_ACL_STATELESS      "reg0[16]"
>  #define REGBIT_ACL_HINT_ALLOW_REL "reg0[17]"
>  #define REGBIT_FROM_ROUTER_PORT   "reg0[18]"
> +#define REGBIT_IP_FRAG            "reg0[19]"
>  
>  #define REG_ORIG_DIP_IPV4         "reg1"
>  #define REG_ORIG_DIP_IPV6         "xxreg1"
> @@ -6398,6 +6399,13 @@ build_pre_stateful(struct ovn_datapath *od,
>  
>      /* Note: priority-120 flows are added in build_lb_rules_pre_stateful(). 
> */
>  
> +    /* If the packet is fragmented, set the REGBIT_IP_FRAG reg bit to 1
> +     * as ip.is_frag will not be preserved after conntrack recirculation. */
> +    ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 115,
> +                  REGBIT_CONNTRACK_NAT" == 1 && ip.is_frag",
> +                  REGBIT_IP_FRAG" = 1; ct_lb_mark;",
> +                  lflow_ref);
> +
>      ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 110,
>                    REGBIT_CONNTRACK_NAT" == 1", "ct_lb_mark;",
>                    lflow_ref);
> @@ -8240,13 +8248,32 @@ build_lb_rules(struct lflow_table *lflows, struct 
> ovn_lb_datapaths *lb_dps,
>          struct ovn_lb_vip *lb_vip = &lb->vips[i];
>          struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i];
>          const char *ip_match = NULL;
> +
> +        ds_clear(action);
> +
> +        /* Store the original destination IP to be used when generating
> +         * hairpin flows.
> +         * If the packet is fragmented, then the flow which saves the
> +         * original destination IP (and port) in the "ls_in_pre_stateful"
> +         * stage will not be hit.
> +         */
>          if (lb_vip->address_family == AF_INET) {
>              ip_match = "ip4";
> +            ds_put_format(action, REG_ORIG_DIP_IPV4 " = %s; ",
> +                          lb_vip->vip_str);
>          } else {
>              ip_match = "ip6";
> +            ds_put_format(action, REG_ORIG_DIP_IPV6 " = %s; ",
> +                          lb_vip->vip_str);
>          }
>  
> -        ds_clear(action);
> +        if (lb_vip->port_str) {
> +            /* Store the original destination port to be used when generating
> +             * hairpin flows.
> +             */
> +            ds_put_format(action, REG_ORIG_TP_DPORT " = %s; ",
> +                          lb_vip->port_str);
> +        }
>          ds_clear(match);
>  
>          /* New connections in Ingress table. */
> @@ -8378,8 +8405,32 @@ build_lb_hairpin(const struct ls_stateful_record 
> *ls_stateful_rec,
>                    lflow_ref);
>  
>      if (ls_stateful_rec->has_lb_vip) {
> -        /* Check if the packet needs to be hairpinned.
> -         * Set REGBIT_HAIRPIN in the original direction and
> +        /* Check if the packet needs to be hairpinned. */
> +
> +        /* In order to check if the fragmented packets needs to be
> +         * hairpinned we need to save the ct tuple original IPv4/v6
> +         * destination and L4 destination port in the registers after
> +         * the conntrack recirculation.
> +         *
> +         * Note: We are assuming that sending the packets to conntrack
> +         * will reassemble the packet and L4 fields will be available.
> +         * It is a risky assumption as ovs-vswitchd doesn't guarantee it
> +         * and userspace datapath doesn't reassemble the fragmented packets
> +         * after conntrack.  It is the kernel datapath conntrack behavior.
> +         * We need to find a better way to handle the fragmented packets.
> +         * */
> +        ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 110,
> +                      "ct.trk && !ct.rpl && "REGBIT_IP_FRAG" == 1 && ip4",
> +                      REG_ORIG_DIP_IPV4 " = ct_nw_dst(); "
> +                      REG_ORIG_TP_DPORT " = ct_tp_dst(); next;",
> +                      lflow_ref);
> +        ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 110,
> +                      "ct.trk && !ct.rpl && "REGBIT_IP_FRAG" == 1 && ip6",
> +                      REG_ORIG_DIP_IPV6 " = ct_ip6_dst(); "
> +                      REG_ORIG_TP_DPORT " = ct_tp_dst(); next;",
> +                      lflow_ref);
> +
> +        /* Set REGBIT_HAIRPIN in the original direction and
>           * REGBIT_HAIRPIN_REPLY in the reply direction.
>           */
>          ovn_lflow_add_with_hint(
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index 479d3e2851..ea4adc1c34 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -2766,6 +2766,33 @@ tcp.flags = RST;
>            </p>
>          </dd>
>  
> +        <dt><code><var>R</var> = ct_nw_dst();</code></dt>
> +        <dd>
> +          <p>
> +            This action checks if the packet is tracked and stores the
> +            conntrack original destination IPv4 address in the register
> +            <var>R</var> of 32-bit size.
> +          </p>
> +        </dd>
> +
> +        <dt><code><var>R</var> = ct_ip6_dst();</code></dt>
> +        <dd>
> +          <p>
> +            This action checks if the packet is tracked and stores the
> +            conntrack original destination IPv6 address in the register
> +            <var>R</var> of 128-bit size.
> +          </p>
> +        </dd>
> +
> +        <dt><code><var>R</var> = ct_tp_dst();</code></dt>
> +        <dd>
> +          <p>
> +            This action checks if the packet is tracked and stores the
> +            conntrack original L4 destination port in the register
> +            <var>R</var> of 16-bit size.
> +          </p>
> +        </dd>
> +
>          <dt><code>sample(probability=<var>packets</var>, ...)</code></dt>
>          <dd>
>            <p>
> diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
> index 7d11663ece..f419b66121 100644
> --- a/tests/ovn-macros.at
> +++ b/tests/ovn-macros.at
> @@ -1226,5 +1226,8 @@ m4_define([OFTABLE_ECMP_NH], [77])
>  m4_define([OFTABLE_CHK_LB_AFFINITY], [78])
>  m4_define([OFTABLE_MAC_CACHE_USE], [79])
>  m4_define([OFTABLE_CT_ZONE_LOOKUP], [80])
> +m4_define([OFTABLE_CT_ORIG_NW_DST_LOAD], [81])
> +m4_define([OFTABLE_CT_ORIG_IP6_DST_LOAD], [82])
> +m4_define([OFTABLE_CT_ORIG_TP_DST_LOAD], [83])
>  
>  m4_define([OFTABLE_SAVE_INPORT_HEX], [m4_eval(OFTABLE_SAVE_INPORT, 16)])
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index d1f7f105c0..e1386323a7 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -1415,7 +1415,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
>  AT_CAPTURE_FILE([sbflows])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | 
> ovn_strip_lflows], 0, [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  
>  # disabled LSPs should not be a backend of Load Balancer
> @@ -1424,7 +1424,7 @@ check ovn-nbctl lsp-set-enabled sw0-p1 disabled
>  AT_CAPTURE_FILE([sbflows])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | 
> ovn_strip_lflows], 0, [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=20.0.0.3:80);)
>  ])
>  wait_row_count Service_Monitor 1
>  
> @@ -1433,7 +1433,7 @@ check ovn-nbctl lsp-set-enabled sw0-p1 enabled
>  AT_CAPTURE_FILE([sbflows])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | 
> ovn_strip_lflows], 0, [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  wait_row_count Service_Monitor 2
>  
> @@ -1444,7 +1444,7 @@ wait_row_count Service_Monitor 0
>  AT_CAPTURE_FILE([sbflows2])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*backends' | 
> ovn_strip_lflows], [0],
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Create the Load_Balancer_Health_Check again.])
> @@ -1456,7 +1456,7 @@ check ovn-nbctl --wait=sb sync
>  
>  ovn-sbctl dump-flows sw0 | grep backends | grep priority=120 > lflows.txt
>  AT_CHECK([cat lflows.txt | ovn_strip_lflows], [0], [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Get the uuid of both the service_monitor])
> @@ -1466,7 +1466,7 @@ sm_sw1_p1=$(fetch_column Service_Monitor _uuid 
> logical_port=sw1-p1)
>  AT_CAPTURE_FILE([sbflows3])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*backends' 
> | ovn_strip_lflows], [0],
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Set the service monitor for sw1-p1 to offline])
> @@ -1477,7 +1477,7 @@ check ovn-nbctl --wait=sb sync
>  AT_CAPTURE_FILE([sbflows4])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*backends' | 
> ovn_strip_lflows], [0],
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80);)
>  ])
>  
>  AS_BOX([Set the service monitor for sw0-p1 to offline])
> @@ -1506,7 +1506,7 @@ check ovn-nbctl --wait=sb sync
>  AT_CAPTURE_FILE([sbflows7])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep backends | grep 
> priority=120 | ovn_strip_lflows], 0,
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Set the service monitor for sw1-p1 to error])
> @@ -1517,7 +1517,7 @@ check ovn-nbctl --wait=sb sync
>  ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
>  | grep priority=120 > lflows.txt
>  AT_CHECK([cat lflows.txt | grep ls_in_lb | ovn_strip_lflows], [0], [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80);)
>  ])
>  
>  AS_BOX([Add one more vip to lb1])
> @@ -1543,8 +1543,8 @@ AT_CAPTURE_FILE([sbflows9])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep backends | grep 
> priority=120 | ovn_strip_lflows],
>    0,
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), action=(ct_lb_mark(backends=10.0.0.3:1000);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 
> 1000; ct_lb_mark(backends=10.0.0.3:1000);)
>  ])
>  
>  AS_BOX([Set the service monitor for sw1-p1 to online])
> @@ -1557,8 +1557,8 @@ AT_CAPTURE_FILE([sbflows10])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep backends | grep 
> priority=120 | ovn_strip_lflows],
>    0,
> -[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), 
> action=(ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
> +[  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst 
> == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 
> 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Associate lb1 to sw1])
> @@ -1567,8 +1567,8 @@ AT_CAPTURE_FILE([sbflows11])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep backends | grep 
> priority=120 | ovn_strip_lflows],
>    0, [dnl
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), 
> action=(ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 
> 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);)
>  ])
>  
>  AS_BOX([Now create lb2 same as lb1 but udp protocol.])
> @@ -4653,14 +4653,17 @@ check_stateful_flows() {
>    table=??(ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
>    table=??(ls_in_pre_stateful ), priority=100  , match=(reg0[[0]] == 1), 
> action=(ct_next;)
>    table=??(ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), 
> action=(ct_lb_mark;)
> +  table=??(ls_in_pre_stateful ), priority=115  , match=(reg0[[2]] == 1 && 
> ip.is_frag), action=(reg0[[19]] = 1; ct_lb_mark;)
>    table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
> ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; 
> reg2[[0..15]] = 80; ct_lb_mark;)
>    table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
> ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg1 = 10.0.0.20; 
> reg2[[0..15]] = 80; ct_lb_mark;)
>  ])
>  
>      AT_CHECK([grep "ls_in_lb " sw0flows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.4:8080);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.20 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.40:8080);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.4:8080);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.20 && tcp.dst == 80), action=(reg1 = 10.0.0.20; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.40:8080);)
>  ])
>  
>      AT_CHECK([grep "ls_in_stateful" sw0flows | ovn_strip_lflows], [0], [dnl
> @@ -4724,6 +4727,7 @@ AT_CHECK([grep "ls_in_pre_stateful" sw0flows | 
> ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
>    table=??(ls_in_pre_stateful ), priority=100  , match=(reg0[[0]] == 1), 
> action=(ct_next;)
>    table=??(ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), 
> action=(ct_lb_mark;)
> +  table=??(ls_in_pre_stateful ), priority=115  , match=(reg0[[2]] == 1 && 
> ip.is_frag), action=(reg0[[19]] = 1; ct_lb_mark;)
>  ])
>  
>  AT_CHECK([grep "ls_in_lb " sw0flows | ovn_strip_lflows], [0], [dnl
> @@ -4764,6 +4768,8 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
>  AT_CHECK([ovn-sbctl dump-flows sw0 | grep "ls_in_lb " | ovn_strip_lflows ], 
> [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
>    table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.20), action=(drop;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
>  ])
>  
>  AT_CLEANUP
> @@ -7763,7 +7769,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" 
> lsflows | ovn_strip_lflo
>  
>  AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
>  ])
>  
>  AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl
> @@ -7818,7 +7826,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" 
> lsflows | ovn_strip_lflo
>  
>  AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
>  ])
>  
>  AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl
> @@ -7873,7 +7883,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" 
> lsflows | ovn_strip_lflo
>  
>  AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
>  ])
>  
>  AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl
> @@ -9258,13 +9270,13 @@ AT_CAPTURE_FILE([S1flows])
>  
>  AT_CHECK([grep "ls_in_lb " S0flows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.2:80);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.11 && tcp.dst == 8080), action=(ct_lb_mark(backends=10.0.0.2:8080);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = 
> 80; ct_lb_mark(backends=10.0.0.2:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.11 && tcp.dst == 8080), action=(reg1 = 172.16.0.11; reg2[[0..15]] = 
> 8080; ct_lb_mark(backends=10.0.0.2:8080);)
>  ])
>  AT_CHECK([grep "ls_in_lb " S1flows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.2:80);)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.11 && tcp.dst == 8080), action=(ct_lb_mark(backends=10.0.0.2:8080);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = 
> 80; ct_lb_mark(backends=10.0.0.2:80);)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.11 && tcp.dst == 8080), action=(reg1 = 172.16.0.11; reg2[[0..15]] = 
> 8080; ct_lb_mark(backends=10.0.0.2:8080);)
>  ])
>  
>  ovn-sbctl get datapath S0 _uuid > dp_uuids
> @@ -9394,7 +9406,9 @@ AT_CHECK([grep "ls_in_lb_aff_check" S0flows | 
> ovn_strip_lflows], [0], [dnl
>  ])
>  AT_CHECK([grep "ls_in_lb " S0flows | ovn_strip_lflows], [0], [dnl
>    table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
> -  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.trk && !ct.rpl && 
> reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = 
> ct_tp_dst(); next;)
> +  table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = 
> 80; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>    table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && 
> ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), 
> action=(reg1 = 172.16.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.2:80);)
>    table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && 
> ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), 
> action=(reg1 = 172.16.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=20.0.0.2:80);)
>  ])
> @@ -13976,7 +13990,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | 
> ovn_strip_lflows | grep "30.0.0.1"
>    table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
> ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;)
>  ])
>  AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep "30.0.0.1"], 
> [0], [dnl
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 30.0.0.1), 
> action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 30.0.0.1), action=(reg1 = 30.0.0.1; 
> ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>  ])
>  
>  # Associate load balancer to lr1 with DGP
> @@ -14090,7 +14104,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | 
> ovn_strip_lflows | grep "2001:db8:
>    table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
> ip6.dst == 2001:db8:cccc::1), action=(xxreg1 = 2001:db8:cccc::1; ct_lb_mark;)
>  ])
>  AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep 
> "2001:db8:cccc::1"], [0], [dnl
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip6.dst == 
> 2001:db8:cccc::1), 
> action=(ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip6.dst == 
> 2001:db8:cccc::1), action=(xxreg1 = 2001:db8:cccc::1; 
> ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>  ])
>  
>  # Associate load balancer to lr1 with DGP
> @@ -14201,7 +14215,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | 
> ovn_strip_lflows | grep "30.0.0.1"
>    table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
> ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;)
>  ])
>  AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep "30.0.0.1"], 
> [0], [dnl
> -  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 30.0.0.1), 
> action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
> +  table=??(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
> 30.0.0.1), action=(reg1 = 30.0.0.1; 
> ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>  ])
>  
>  # Associate load balancer to lr1 with DGP
> diff --git a/tests/ovn.at b/tests/ovn.at
> index a8eb5b0a60..15b78f4c37 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2418,6 +2418,54 @@ sample(probability=10, obs_point=ct_label);
>  mac_cache_use;
>      encodes as resubmit(,OFTABLE_MAC_CACHE_USE)
>  
> +# ct_nw_dst()
> +reg1 = ct_nw_dst();
> +    encodes as 
> set_field:0->reg4,resubmit(,OFTABLE_CT_ORIG_NW_DST_LOAD),move:NXM_NX_REG4[[]]->NXM_NX_XXREG0[[64..95]]
> +
> +xreg1[[3..34]] = ct_nw_dst();
> +    encodes as 
> set_field:0->reg4,resubmit(,OFTABLE_CT_ORIG_NW_DST_LOAD),move:NXM_NX_REG4[[]]->NXM_NX_XXREG0[[3..34]]
> +
> +reg1[[3..34]] = ct_nw_dst();
> +    Cannot select bits 3 to 34 of 32-bit field reg1.
> +
> +reg1[[3..35]] = ct_nw_dst();
> +    Cannot select bits 3 to 35 of 32-bit field reg1.
> +
> +reg1[[1]] = ct_nw_dst();
> +    Cannot use 1-bit field reg1[[1..1]] where 32-bit field is required.
> +
> +ct_nw_dst;
> +    Syntax error at `ct_nw_dst' expecting action.
> +
> +ct_nw_dst();
> +    Syntax error at `ct_nw_dst' expecting action.
> +
> +# ct_ip6_dst()
> +xxreg1 = ct_ip6_dst();
> +    encodes as 
> set_field:0/0xffffffffffffffff->xxreg0,set_field:0/0xffffffffffffffff0000000000000000->xxreg0,resubmit(,OFTABLE_CT_ORIG_IP6_DST_LOAD),move:NXM_NX_XXREG0[[]]->NXM_NX_XXREG1[[]]
> +
> +reg1 = ct_ip6_dst();
> +    Cannot use 32-bit field reg1[[0..31]] where 128-bit field is required.
> +
> +ct_ip6_dst;
> +    Syntax error at `ct_ip6_dst' expecting action.
> +
> +ct_ip6_dst();
> +    Syntax error at `ct_ip6_dst' expecting action.
> +
> +# ct_tp_dst()
> +reg1[[0..15]] = ct_tp_dst();
> +    encodes as 
> set_field:0/0xffff->reg8,resubmit(,OFTABLE_CT_ORIG_TP_DST_LOAD),move:NXM_NX_REG8[[0..15]]->NXM_NX_XXREG0[[64..79]]
> +
> +reg1 = ct_tp_dst();
> +    Cannot use 32-bit field reg1[[0..31]] where 16-bit field is required.
> +
> +ct_tp_dst;
> +    Syntax error at `ct_tp_dst' expecting action.
> +
> +ct_tp_dst();
> +    Syntax error at `ct_tp_dst' expecting action.
> +
>  # Miscellaneous negative tests.
>  ;
>      Syntax error at `;'.
> @@ -25632,7 +25680,7 @@ OVS_WAIT_FOR_OUTPUT(
>     ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed 
> 's/table=..//'], 0,
>    [dnl
>    (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark;)
> -  (ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), 
> action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; 
> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
> +  (ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 
> 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; 
> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>  ])
>  
>  AT_CAPTURE_FILE([sbflows2])
> @@ -25831,7 +25879,7 @@ OVS_WAIT_FOR_OUTPUT(
>     ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed 
> 's/table=..//'], 0,
>    [dnl
>    (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6.dst == 
> 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; 
> ct_lb_mark;)
> -  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a 
> && tcp.dst == 80), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; 
> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
> +  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a 
> && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; 
> ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; 
> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>  ])
>  
>  AT_CAPTURE_FILE([sbflows2])
> @@ -35773,7 +35821,9 @@ check_default_flows() {
>      for table in $(grep -oP "table=\K\d*, " oflows | tr -d ',' | sort -n | 
> uniq); do
>          # Tables 68 and 70 are part of the chk_lb_hairpin and ct_snat_to_vip 
> actions
>          # respectively and it's OK if they don't have a default action.
> -        if test ${table} -eq 68 -o ${table} -eq 70; then
> +        # Tables 81, 82 and 83 are part of ct_nw_dst(), ct_ip6_dst() and 
> ct_tp_dst()
> +        # actions respectively and its OK for them to not have default flows.
> +        if test ${table} -eq 68 -o ${table} -eq 70 -o ${table} -eq 81 -o 
> ${table} -eq 82 -o ${table} -eq 83; then
>              continue;
>          fi
>          AT_CHECK([grep -qe "table=$table.* priority=0\(,metadata=0x\w*\)\? 
> actions" oflows], [0], [ignore], [ignore], [echo "Table $table does not 
> contain a default action"])
> diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
> index 75ecdadebe..6a09ccdb3d 100644
> --- a/tests/system-ovn-kmod.at
> +++ b/tests/system-ovn-kmod.at
> @@ -1027,3 +1027,146 @@ OVS_TRAFFIC_VSWITCHD_STOP(["
>  "])
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([Load Balancer LS hairpin IPv4 UDP - larger than MTU])
> +AT_SKIP_IF([test $HAVE_NC = no])
> +AT_KEYWORDS([lb])
> +
> +ovn_start
> +
> +OVS_TRAFFIC_VSWITCHD_START()
> +ADD_BR([br-int])
> +
> +# Set external-ids in br-int needed for ovn-controller
> +ovs-vsctl \
> +        -- set Open_vSwitch . external-ids:system-id=hv1 \
> +        -- set Open_vSwitch . 
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> +        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> +        -- set bridge br-int fail-mode=secure 
> other-config:disable-in-band=true
> +
> +# Start ovn-controller
> +start_daemon ovn-controller
> +
> +# Logical network:
> +# One logical switch with IPv4 load balancers that hairpin the traffic.
> +check ovn-nbctl ls-add sw
> +check ovn-nbctl lsp-add sw lsp -- lsp-set-addresses lsp 00:00:00:00:00:01
> +check ovn-nbctl lb-add lb-ipv4-udp     88.88.88.88:4040 42.42.42.1:2021 udp
> +check ovn-nbctl ls-lb-add sw lb-ipv4-udp
> +
> +check ovn-nbctl lr-add rtr
> +check ovn-nbctl lrp-add rtr rtr-sw 00:00:00:00:01:00 42.42.42.254/24
> +check ovn-nbctl lsp-add sw sw-rtr                       \
> +    -- lsp-set-type sw-rtr router                 \
> +    -- lsp-set-addresses sw-rtr 00:00:00:00:01:00 \
> +    -- lsp-set-options sw-rtr router-port=rtr-sw
> +
> +ADD_NAMESPACES(lsp)
> +ADD_VETH(lsp, lsp, br-int, "42.42.42.1/24", "00:00:00:00:00:01", \
> +         "42.42.42.254")
> +
> +ovn-nbctl --wait=hv sync

Nit: missing check.

> +
> +printf %8000s > datafile
> +printf %32000s > frag_test_srv.expected
> +printf %16000s > frag_test_client.expected
> +
> +# Start IPv4 UDP server on lsp.

Nit: either we add a comment about the client or we remove this one.

> +
> +NETNS_DAEMONIZE([lsp], [nc -e /bin/cat -u -v -l  42.42.42.1 2021 -o 
> udp_frag_test_srv.rcvd], [lsp-nc.pid])

Nit: extra whitespace after -l.

> +NS_CHECK_EXEC([lsp], [(cat datafile; sleep 3) | nc -u 88.88.88.88 4040 -p 
> 20000 -o udp_frag_test_c1.recvd], [0], [ignore], [ignore])
> +NS_CHECK_EXEC([lsp], [(cat datafile; sleep 3) | nc -u 88.88.88.88 4040 -p 
> 20000 -o udp_frag_test_c2.recvd], [0], [ignore], [ignore])

Maybe we need a small comment above these two lines mentioning that
we're reusing the same port (from the server perspective it's the same
session).

> +
> +check cmp frag_test_srv.expected udp_frag_test_srv.rcvd
> +check cmp frag_test_client.expected udp_frag_test_c1.recvd
> +check cmp frag_test_client.expected udp_frag_test_c2.recvd
> +
> +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> +/connection dropped.*/d"])
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([Load Balancer LS hairpin IPv6 UDP - larger than MTU])
> +AT_SKIP_IF([test $HAVE_NC = no])
> +AT_KEYWORDS([lb])
> +
> +ovn_start
> +
> +OVS_TRAFFIC_VSWITCHD_START()
> +ADD_BR([br-int])
> +
> +# Set external-ids in br-int needed for ovn-controller
> +ovs-vsctl \
> +        -- set Open_vSwitch . external-ids:system-id=hv1 \
> +        -- set Open_vSwitch . 
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> +        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> +        -- set bridge br-int fail-mode=secure 
> other-config:disable-in-band=true
> +
> +# Start ovn-controller
> +start_daemon ovn-controller
> +
> +# Logical network:
> +# One logical switch with IPv6 load balancers that hairpin the traffic.
> +check ovn-nbctl ls-add sw
> +check ovn-nbctl lsp-add sw lsp -- lsp-set-addresses lsp 00:00:00:00:00:01
> +check ovn-nbctl lb-add lb-ipv6-udp     [[8800::0088]]:4040 [[4200::1]]:2021 
> udp
> +check ovn-nbctl ls-lb-add sw lb-ipv6-udp
> +
> +check ovn-nbctl lr-add rtr
> +check ovn-nbctl lrp-add rtr rtr-sw 00:00:00:00:01:00 4200::00ff/64
> +check ovn-nbctl lsp-add sw sw-rtr                       \
> +    -- lsp-set-type sw-rtr router                 \
> +    -- lsp-set-addresses sw-rtr 00:00:00:00:01:00 \
> +    -- lsp-set-options sw-rtr router-port=rtr-sw
> +
> +ovn-nbctl --wait=hv sync

Nit: missing check.

> +
> +ADD_NAMESPACES(lsp)
> +ADD_VETH(lsp, lsp, br-int, "4200::1/64", "00:00:00:00:00:01", "4200::00ff", 
> "nodad")
> +
> +printf %8000s > datafile
> +printf %32000s > frag_test_srv.expected
> +printf %16000s > frag_test_client.expected
> +
> +# Start IPv6 UDP server on lsp.
> +NETNS_DAEMONIZE([lsp], [nc -e /bin/cat -u -v -l 4200::1 2021 -o 
> udp_frag_test_srv.rcvd], [lsp-nc.pid])
> +
> +NS_CHECK_EXEC([lsp], [(cat datafile; sleep 3) | nc -6 -u 8800::0088 4040 -p 
> 20000 -o udp_frag_test_c1.recvd], [0], [ignore], [ignore])
> +NS_CHECK_EXEC([lsp], [(cat datafile; sleep 3) | nc -6 -u 8800::0088 4040 -p 
> 20000 -o udp_frag_test_c2.recvd], [0], [ignore], [ignore])
> +

Here too.

> +check cmp frag_test_srv.expected udp_frag_test_srv.rcvd
> +check cmp frag_test_client.expected udp_frag_test_c1.recvd
> +check cmp frag_test_client.expected udp_frag_test_c2.recvd
> +
> +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> +/connection dropped.*/d"])
> +AT_CLEANUP
> +])
> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> index 2ea68f2124..7954bb98ac 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -1376,6 +1376,9 @@ test_parse_actions(struct ovs_cmdl_context *ctx 
> OVS_UNUSED)
>                  .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
>                  .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
>                  .mac_cache_use_table = OFTABLE_MAC_CACHE_USE,
> +                .ct_nw_dst_load_table = OFTABLE_CT_ORIG_NW_DST_LOAD,
> +                .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD,
> +                .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD,
>                  .lflow_uuid.parts =
>                      { 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd},
>                  .dp_key = 0xabcdef,
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index f9cc2463b7..806bdf3d9a 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -3447,6 +3447,12 @@ trace_actions(const struct ovnact *ovnacts, size_t 
> ovnacts_len,
>              break;
>          case OVNACT_MAC_CACHE_USE:
>              break;
> +        case OVNACT_CT_ORIG_NW_DST:
> +            break;
> +        case OVNACT_CT_ORIG_IP6_DST:
> +            break;
> +        case OVNACT_CT_ORIG_TP_DST:
> +            break;
>          }
>      }
>      ofpbuf_uninit(&stack);

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to