On Tue, Jan 24, 2023 at 12:28 PM Ales Musil <amu...@redhat.com> wrote:

> To make it easier to add flows to this stage, refactor the function.
> This also has the benefit that we should see fewer allocations due to
> rearranging how we create flows and how we manipulate the match string.
>
> Signed-off-by: Ales Musil <amu...@redhat.com>
> ---
> v5: Rebase on top of current main.
>     Add missing ");" if ct_lb_related feature is not supported.
> v6: Rebase on top of current main.
>     Fix wrong enclosing for reject action and add missing test case.
> ---
>  northd/northd.c     | 483 ++++++++++++++++++++------------------------
>  tests/ovn-northd.at |  69 ++++++-
>  2 files changed, 280 insertions(+), 272 deletions(-)
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 0944a7b56..4b0d37375 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3878,10 +3878,13 @@ static bool
>  build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
>                       struct ovn_northd_lb_vip *lb_vip_nb,
>                       struct ds *action, char *selection_fields,
> -                     bool ls_dp, bool ct_lb_mark)
> +                     struct ds *skip_snat_action, struct ds
> *force_snat_action,
> +                     bool ls_dp, const struct chassis_features *features)
>  {
> -    const char *ct_lb_action = ct_lb_mark ? "ct_lb_mark" : "ct_lb";
> -    bool skip_hash_fields = false, reject = false;
> +    const char *ct_lb_action =
> +        features->ct_no_masked_label ? "ct_lb_mark" : "ct_lb";
> +    bool reject = !lb_vip->n_backends && lb_vip->empty_backend_rej;
> +    bool drop = false;
>
>      if (lb_vip_nb->lb_health_check) {
>          ds_put_format(action, "%s(backends=", ct_lb_action);
> @@ -3902,23 +3905,12 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
>              ds_put_format(action, "%s:%"PRIu16",",
>                            backend->ip_str, backend->port);
>          }
> +        ds_chomp(action, ',');
>
> -        if (!n_active_backends) {
> -            if (!lb_vip->empty_backend_rej) {
> -                ds_clear(action);
> -                ds_put_cstr(action, debug_drop_action());
> -                skip_hash_fields = true;
> -            } else {
> -                reject = true;
> -            }
> -        } else {
> -            ds_chomp(action, ',');
> -            ds_put_cstr(action, ");");
> -        }
> -    } else if (lb_vip->empty_backend_rej && !lb_vip->n_backends) {
> -        reject = true;
> +        drop = !n_active_backends && !lb_vip->empty_backend_rej;
> +        reject = !n_active_backends && lb_vip->empty_backend_rej;
>      } else {
> -        ds_put_format(action, "%s(backends=%s);", ct_lb_action,
> +        ds_put_format(action, "%s(backends=%s", ct_lb_action,
>                        lb_vip_nb->backend_ips);
>      }
>
> @@ -3927,12 +3919,29 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
>                            : ovn_stage_get_table(S_ROUTER_OUT_SNAT);
>          ds_clear(action);
>          ds_put_format(action, "reg0 = 0; reject { outport <-> inport; "
> -                      "next(pipeline=egress,table=%d);};", stage);
> -    } else if (!skip_hash_fields && selection_fields &&
> selection_fields[0]) {
> -        ds_chomp(action, ';');
> -        ds_chomp(action, ')');
> -        ds_put_format(action, "; hash_fields=\"%s\");", selection_fields);
> +                              "next(pipeline=egress,table=%d);};", stage);
> +    } else if (drop) {
> +        ds_clear(action);
> +        ds_put_cstr(action, debug_drop_action());
> +    } else if (selection_fields && selection_fields[0]) {
> +        ds_put_format(action, "; hash_fields=\"%s\"", selection_fields);
> +    }
> +
> +    bool is_lb_action = !(reject || drop);
> +    const char *enclose = is_lb_action ? ");" : "";
> +
> +    if (!ls_dp) {
> +        bool flag_supported = is_lb_action && features->ct_lb_related;
> +        ds_put_format(skip_snat_action, "flags.skip_snat_for_lb = 1;
> %s%s",
> +                      ds_cstr(action),
> +                      flag_supported ? "; skip_snat);" : enclose);
> +        ds_put_format(force_snat_action, "flags.force_snat_for_lb = 1;
> %s%s",
> +                      ds_cstr(action),
> +                      flag_supported ? "; force_snat);" : enclose);
>      }
> +
> +    ds_put_cstr(action, enclose);
> +
>      return reject;
>  }
>
> @@ -7502,9 +7511,9 @@ build_lb_affinity_default_flows(struct ovn_datapath
> *od, struct hmap *lflows)
>  }
>
>  static void
> -build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, bool
> ct_lb_mark,
> -               struct ds *match, struct ds *action,
> -               const struct shash *meter_groups)
> +build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb,
> +               const struct chassis_features *features, struct ds *match,
> +               struct ds *action, const struct shash *meter_groups)
>  {
>      for (size_t i = 0; i < lb->n_vips; i++) {
>          struct ovn_lb_vip *lb_vip = &lb->vips[i];
> @@ -7527,8 +7536,8 @@ build_lb_rules(struct hmap *lflows, struct
> ovn_northd_lb *lb, bool ct_lb_mark,
>          /* New connections in Ingress table. */
>          const char *meter = NULL;
>          bool reject = build_lb_vip_actions(lb_vip, lb_vip_nb, action,
> -                                           lb->selection_fields, true,
> -                                           ct_lb_mark);
> +                                           lb->selection_fields, NULL,
> +                                           NULL, true, features);
>
>          ds_put_format(match, "ct.new && %s.dst == %s", ip_match,
>                        lb_vip->vip_str);
> @@ -10465,49 +10474,123 @@ get_force_snat_ip(struct ovn_datapath *od,
> const char *key_type,
>      return true;
>  }
>
> +#define LROUTER_NAT_LB_FLOW_INIT(MATCH, ACTION, PRIO) \
> +        (struct lrouter_nat_lb_flow)                  \
> +        { .action = (ACTION), .lflow_ref = NULL,      \
> +          .hash = ovn_logical_flow_hash(              \
> +          ovn_stage_get_table(S_ROUTER_IN_DNAT),      \
> +          ovn_stage_get_pipeline(S_ROUTER_IN_DNAT),   \
> +          (PRIO), ds_cstr(MATCH), (ACTION)) }
> +
> +enum lrouter_nat_lb_flow_type {
> +    LROUTER_NAT_LB_FLOW_NORMAL = 0,
> +    LROUTER_NAT_LB_FLOW_SKIP_SNAT,
> +    LROUTER_NAT_LB_FLOW_FORCE_SNAT,
> +    LROUTER_NAT_LB_FLOW_MAX,
> +};
> +
> +struct lrouter_nat_lb_flow {
> +    char *action;
> +    struct ovn_lflow *lflow_ref;
> +
> +    uint32_t hash;
> +};
> +
> +struct lrouter_nat_lb_flows_ctx {
> +    struct lrouter_nat_lb_flow new[LROUTER_NAT_LB_FLOW_MAX];
> +    struct lrouter_nat_lb_flow est[LROUTER_NAT_LB_FLOW_MAX];
> +
> +    struct ds *new_match;
> +    struct ds *est_match;
> +    struct ds *undnat_match;
> +
> +    struct ovn_lb_vip *lb_vip;
> +    struct ovn_northd_lb *lb;
> +    bool reject;
> +
> +    int prio;
> +
> +    struct hmap *lflows;
> +    const struct shash *meter_groups;
> +};
> +
>  static void
> -build_gw_lrouter_nat_flows_for_lb(struct ovn_northd_lb *lb,
> -                                  struct ovn_datapath **dplist, int
> n_dplist,
> -                                  bool reject, char *new_match,
> -                                  char *new_action, char *est_match,
> -                                  char *est_action, struct hmap *lflows,
> -                                  int prio, const struct shash
> *meter_groups)
> -{
> -    if (!n_dplist) {
> +build_distr_lrouter_nat_flows_for_lb(struct lrouter_nat_lb_flows_ctx *ctx,
> +                                     enum lrouter_nat_lb_flow_type type,
> +                                     struct ovn_datapath *od)
> +{
> +    char *gw_action = od->is_gw_router ? "ct_dnat;" : "ct_dnat_in_czone;";
> +    /* Store the match lengths, so we can reuse the ds buffer. */
> +    size_t new_match_len = ctx->new_match->length;
> +    size_t est_match_len = ctx->est_match->length;
> +    size_t undnat_match_len = ctx->undnat_match->length;
> +
> +
> +    const char *meter = NULL;
> +
> +    if (ctx->reject) {
> +        meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
> ctx->meter_groups);
> +    }
> +
> +    if (ctx->lb_vip->n_backends || !ctx->lb_vip->empty_backend_rej) {
> +        ds_put_format(ctx->new_match, " && is_chassis_resident(%s)",
> +                      od->l3dgw_ports[0]->cr_port->json_key);
> +        ds_put_format(ctx->est_match, " && is_chassis_resident(%s)",
> +                      od->l3dgw_ports[0]->cr_port->json_key);
> +    }
> +
> +    ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
> ctx->prio,
> +                              ds_cstr(ctx->new_match),
> ctx->new[type].action,
> +                              NULL, meter, &ctx->lb->nlb->header_);
> +    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_IN_DNAT, ctx->prio,
> +                            ds_cstr(ctx->est_match),
> ctx->est[type].action,
> +                            &ctx->lb->nlb->header_);
> +
> +    ds_truncate(ctx->new_match, new_match_len);
> +    ds_truncate(ctx->est_match, est_match_len);
> +
> +    if (!ctx->lb_vip->n_backends) {
>          return;
>      }
>
> -    struct ovn_lflow *lflow_ref_new = NULL, *lflow_ref_est = NULL;
> -    uint32_t hash_new = ovn_logical_flow_hash(
> -            ovn_stage_get_table(S_ROUTER_IN_DNAT),
> -            ovn_stage_get_pipeline(S_ROUTER_IN_DNAT),
> -            prio, new_match, new_action);
> -    uint32_t hash_est = ovn_logical_flow_hash(
> -            ovn_stage_get_table(S_ROUTER_IN_DNAT),
> -            ovn_stage_get_pipeline(S_ROUTER_IN_DNAT),
> -            prio, est_match, est_action);
> +    char *action = type == LROUTER_NAT_LB_FLOW_NORMAL
> +                   ? gw_action : ctx->est[type].action;
>
> -    for (size_t i = 0; i < n_dplist; i++) {
> -        struct ovn_datapath *od = dplist[i];
> -        const char *meter = NULL;
> +    ds_put_format(ctx->undnat_match,
> +                  ") && outport == %s && is_chassis_resident(%s)",
> +                  od->l3dgw_ports[0]->json_key,
> +                  od->l3dgw_ports[0]->cr_port->json_key);
> +    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> +                            ds_cstr(ctx->undnat_match), action,
> +                            &ctx->lb->nlb->header_);
> +    ds_truncate(ctx->undnat_match, undnat_match_len);
> +}
>
> -        if (reject) {
> -            meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
> meter_groups);
> -        }
> -        if (meter || !ovn_dp_group_add_with_reference(lflow_ref_new, od))
> {
> -            struct ovn_lflow *lflow = ovn_lflow_add_at_with_hash(lflows,
> od,
> -                    S_ROUTER_IN_DNAT, prio, new_match, new_action,
> -                    NULL, meter, &lb->nlb->header_, OVS_SOURCE_LOCATOR,
> -                    hash_new);
> -            lflow_ref_new = meter ? NULL : lflow;
> -        }
> +static void
> +build_gw_lrouter_nat_flows_for_lb(struct lrouter_nat_lb_flows_ctx *ctx,
> +                                  enum lrouter_nat_lb_flow_type type,
> +                                  struct ovn_datapath *od)
> +{
> +    const char *meter = NULL;
> +    struct lrouter_nat_lb_flow *new_flow = &ctx->new[type];
> +    struct lrouter_nat_lb_flow *est_flow = &ctx->est[type];
>
> -        if (!ovn_dp_group_add_with_reference(lflow_ref_est, od)) {
> -            lflow_ref_est = ovn_lflow_add_at_with_hash(lflows, od,
> -                    S_ROUTER_IN_DNAT, prio, est_match, est_action,
> -                    NULL, NULL, &lb->nlb->header_,
> -                    OVS_SOURCE_LOCATOR, hash_est);
> -        }
> +    if (ctx->reject) {
> +        meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
> ctx->meter_groups);
> +    }
> +    if (meter || !ovn_dp_group_add_with_reference(new_flow->lflow_ref,
> od)) {
> +        struct ovn_lflow *lflow = ovn_lflow_add_at_with_hash(ctx->lflows,
> od,
> +                S_ROUTER_IN_DNAT, ctx->prio, ds_cstr(ctx->new_match),
> +                new_flow->action, NULL, meter, &ctx->lb->nlb->header_,
> +                OVS_SOURCE_LOCATOR, new_flow->hash);
> +        new_flow->lflow_ref = meter ? NULL : lflow;
> +    }
> +
> +    if (!ovn_dp_group_add_with_reference(est_flow->lflow_ref, od)) {
> +        est_flow->lflow_ref = ovn_lflow_add_at_with_hash(ctx->lflows, od,
> +                S_ROUTER_IN_DNAT, ctx->prio, ds_cstr(ctx->est_match),
> +                est_flow->action, NULL, NULL, &ctx->lb->nlb->header_,
> +                OVS_SOURCE_LOCATOR, est_flow->hash);
>      }
>  }
>
> @@ -10523,22 +10606,25 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
>      const char *ct_natted = features->ct_no_masked_label
>                              ? "ct_mark.natted"
>                              : "ct_label.natted";
> -    char *skip_snat_new_action = NULL;
> -    char *skip_snat_est_action = NULL;
> -    char *new_match;
> -    char *est_match;
> +
> +    bool ipv4 = lb_vip->address_family == AF_INET;
> +    const char *ip_match = ipv4 ? "ip4" : "ip6";
> +    const char *ip_reg = ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6;
> +
> +    int prio = 110;
> +
> +    struct ds skip_snat_act = DS_EMPTY_INITIALIZER;
> +    struct ds force_snat_act = DS_EMPTY_INITIALIZER;
> +    struct ds est_match = DS_EMPTY_INITIALIZER;
> +    struct ds undnat_match = DS_EMPTY_INITIALIZER;
> +    struct ds unsnat_match = DS_EMPTY_INITIALIZER;
>
>      ds_clear(match);
>      ds_clear(action);
>
>      bool reject = build_lb_vip_actions(lb_vip, vips_nb, action,
> -                                       lb->selection_fields, false,
> -                                       features->ct_no_masked_label);
> -    bool drop = !!strncmp(ds_cstr(action), "ct_lb", strlen("ct_lb"));
> -    if (!drop) {
> -        /* Remove the trailing ");". */
> -        ds_truncate(action, action->length - 2);
> -    }
> +                                       lb->selection_fields,
> &skip_snat_act,
> +                                       &force_snat_act, false, features);
>
>      /* Higher priority rules are added for load-balancing in DNAT
>       * table.  For every match (on a VIP[:port]), we add two flows.
> @@ -10546,52 +10632,23 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
>       * of "ct_lb_mark($targets);". The other flow is for ct.est with
>       * an action of "next;".
>       */
> -    if (lb_vip->address_family == AF_INET) {
> -        ds_put_format(match, "ip4 && "REG_NEXT_HOP_IPV4" == %s",
> -                      lb_vip->vip_str);
> -    } else {
> -        ds_put_format(match, "ip6 && "REG_NEXT_HOP_IPV6" == %s",
> -                      lb_vip->vip_str);
> -    }
> -
> -    if (lb->skip_snat) {
> -        const char *skip_snat = features->ct_lb_related && !drop
> -                                ? "; skip_snat);"
> -                                : "";
> -        skip_snat_new_action = xasprintf("flags.skip_snat_for_lb = 1;
> %s%s",
> -                                         ds_cstr(action), skip_snat);
> -        skip_snat_est_action = xasprintf("flags.skip_snat_for_lb = 1; "
> -                                         "next;");
> -    }
> -
> -    int prio = 110;
> -    if (lb_vip->port_str) {
> +    ds_put_format(match, "ct.new && !ct.rel && %s && %s == %s",
> +                  ip_match, ip_reg, lb_vip->vip_str);
> +    if (lb_vip->vip_port) {
>          prio = 120;
> -        new_match = xasprintf("ct.new && !ct.rel && %s && %s && "
> -                              REG_ORIG_TP_DPORT_ROUTER" == %s",
> -                              ds_cstr(match), lb->proto,
> lb_vip->port_str);
> -        est_match = xasprintf("ct.est && !ct.rel && %s && %s && "
> -                              REG_ORIG_TP_DPORT_ROUTER" == %s && %s == 1",
> -                              ds_cstr(match), lb->proto, lb_vip->port_str,
> -                              ct_natted);
> -    } else {
> -        new_match = xasprintf("ct.new && !ct.rel && %s", ds_cstr(match));
> -        est_match = xasprintf("ct.est && !ct.rel && %s && %s == 1",
> -                          ds_cstr(match), ct_natted);
> +        ds_put_format(match, " && %s && "REG_ORIG_TP_DPORT_ROUTER" == %d",
> +                      lb->proto, lb_vip->vip_port);
>      }
>
> -    const char *ip_match = NULL;
> -    if (lb_vip->address_family == AF_INET) {
> -        ip_match = "ip4";
> -    } else {
> -        ip_match = "ip6";
> -    }
> +    ds_put_cstr(&est_match, "ct.est");
> +    /* Clone the match after initial "ct.new" (6 bytes). */
> +    ds_put_cstr(&est_match, ds_cstr(match) + 6);
> +    ds_put_format(&est_match, " && %s == 1", ct_natted);
>
>      /* Add logical flows to UNDNAT the load balanced reverse traffic in
>       * the router egress pipleine stage - S_ROUTER_OUT_UNDNAT if the
> logical
>       * router has a gateway router port associated.
>       */
> -    struct ds undnat_match = DS_EMPTY_INITIALIZER;
>      ds_put_format(&undnat_match, "%s && (", ip_match);
>
>      for (size_t i = 0; i < lb_vip->n_backends; i++) {
> @@ -10606,12 +10663,9 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
>              ds_put_cstr(&undnat_match, ") || ");
>          }
>      }
> -    ds_chomp(&undnat_match, ' ');
> -    ds_chomp(&undnat_match, '|');
> -    ds_chomp(&undnat_match, '|');
> -    ds_chomp(&undnat_match, ' ');
> +    /* Remove the trailing " || ". */
> +    ds_truncate(&undnat_match, undnat_match.length - 4);
>
> -    struct ds unsnat_match = DS_EMPTY_INITIALIZER;
>      ds_put_format(&unsnat_match, "%s && %s.dst == %s && %s",
>                    ip_match, ip_match, lb_vip->vip_str, lb->proto);
>      if (lb_vip->port_str) {
> @@ -10619,21 +10673,34 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
>                        lb_vip->port_str);
>      }
>
> -    struct ovn_datapath **gw_router_skip_snat =
> -        xcalloc(lb->n_nb_lr, sizeof *gw_router_skip_snat);
> -    int n_gw_router_skip_snat = 0;
> +    struct lrouter_nat_lb_flows_ctx ctx = {
> +        .lb_vip = lb_vip,
> +        .lb = lb,
> +        .reject = reject,
> +        .prio = prio,
> +        .lflows = lflows,
> +        .meter_groups = meter_groups,
> +        .new_match = match,
> +        .est_match = &est_match,
> +        .undnat_match = &undnat_match
> +    };
>
> -    struct ovn_datapath **gw_router_force_snat =
> -        xcalloc(lb->n_nb_lr, sizeof *gw_router_force_snat);
> -    int n_gw_router_force_snat = 0;
> +    ctx.new[LROUTER_NAT_LB_FLOW_NORMAL] =
> +        LROUTER_NAT_LB_FLOW_INIT(match, ds_cstr(action), prio);
> +    ctx.est[LROUTER_NAT_LB_FLOW_NORMAL] =
> +        LROUTER_NAT_LB_FLOW_INIT(&est_match, "next;", prio);
>
> -    struct ovn_datapath **gw_router =
> -        xcalloc(lb->n_nb_lr, sizeof *gw_router);
> -    int n_gw_router = 0;
> +    ctx.new[LROUTER_NAT_LB_FLOW_SKIP_SNAT] =
> +        LROUTER_NAT_LB_FLOW_INIT(match, ds_cstr(&skip_snat_act), prio);
> +    ctx.est[LROUTER_NAT_LB_FLOW_SKIP_SNAT] =
> +        LROUTER_NAT_LB_FLOW_INIT(&est_match,
> +                                 "flags.skip_snat_for_lb = 1; next;",
> prio);
>
> -    struct ovn_datapath **distributed_router =
> -        xcalloc(lb->n_nb_lr, sizeof *distributed_router);
> -    int n_distributed_router = 0;
> +    ctx.new[LROUTER_NAT_LB_FLOW_FORCE_SNAT] =
> +        LROUTER_NAT_LB_FLOW_INIT(match, ds_cstr(&force_snat_act), prio);
> +    ctx.est[LROUTER_NAT_LB_FLOW_FORCE_SNAT] =
> +        LROUTER_NAT_LB_FLOW_INIT(&est_match,
> +                                 "flags.force_snat_for_lb = 1; next;",
> prio);
>
>      struct ovn_datapath **lb_aff_force_snat_router =
>          xcalloc(lb->n_nb_lr, sizeof *lb_aff_force_snat_router);
> @@ -10647,18 +10714,22 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
>       * lflow generation for them.
>       */
>      for (size_t i = 0; i < lb->n_nb_lr; i++) {
> +        enum lrouter_nat_lb_flow_type type;
>          struct ovn_datapath *od = lb->nb_lr[i];
> +
> +        if (lb->skip_snat) {
> +            type = LROUTER_NAT_LB_FLOW_SKIP_SNAT;
> +        } else if (!lport_addresses_is_empty(&od->lb_force_snat_addrs) ||
> +                   od->lb_force_snat_router_ip) {
> +            type = LROUTER_NAT_LB_FLOW_FORCE_SNAT;
> +        } else {
> +            type = LROUTER_NAT_LB_FLOW_NORMAL;
> +        }
> +
>          if (!od->n_l3dgw_ports) {
> -            if (lb->skip_snat) {
> -                gw_router_skip_snat[n_gw_router_skip_snat++] = od;
> -            } else if
> (!lport_addresses_is_empty(&od->lb_force_snat_addrs) ||
> -                       od->lb_force_snat_router_ip) {
> -                gw_router_force_snat[n_gw_router_force_snat++] = od;
> -            } else {
> -                gw_router[n_gw_router++] = od;
> -            }
> +            build_gw_lrouter_nat_flows_for_lb(&ctx, type, od);
>          } else {
> -            distributed_router[n_distributed_router++] = od;
> +            build_distr_lrouter_nat_flows_for_lb(&ctx, type, od);
>          }
>
>          if (!lport_addresses_is_empty(&od->lb_force_snat_addrs) ||
> @@ -10680,149 +10751,36 @@ build_lrouter_nat_flows_for_lb(struct
> ovn_lb_vip *lb_vip,
>               * unsnat stage, the conntrack flags are not set properly, and
>               * it doesn't hit the established state flows in
>               * S_ROUTER_IN_DNAT stage. */
> -             ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT, 120,
> -                                     ds_cstr(&unsnat_match), "next;",
> -                                     &lb->nlb->header_);
> +            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT, 120,
> +                                    ds_cstr(&unsnat_match), "next;",
> +                                    &lb->nlb->header_);
>          }
>      }
>
> -    /* GW router logic */
> -    build_gw_lrouter_nat_flows_for_lb(lb, gw_router_skip_snat,
> -            n_gw_router_skip_snat, reject, new_match,
> -            skip_snat_new_action, est_match,
> -            skip_snat_est_action, lflows, prio, meter_groups);
> -
> -    const char *force_snat = features->ct_lb_related && !drop
> -                             ? "; force_snat);"
> -                             : "";
> -    char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s%s",
> -                                  ds_cstr(action), force_snat);
> -    build_gw_lrouter_nat_flows_for_lb(lb, gw_router_force_snat,
> -            n_gw_router_force_snat, reject, new_match,
> -            new_actions, est_match,
> -            "flags.force_snat_for_lb = 1; next;",
> -            lflows, prio, meter_groups);
> -
>      /* LB affinity flows for datapaths where CMS has specified
>       * force_snat_for_lb floag option.
>       */
> -    build_lb_affinity_lr_flows(lflows, lb, lb_vip, new_match,
> +    build_lb_affinity_lr_flows(lflows, lb, lb_vip, ds_cstr(match),
>                                 "flags.force_snat_for_lb = 1; ",
>                                 lb_aff_force_snat_router,
>                                 n_lb_aff_force_snat_router);
>
> -    if (!drop) {
> -        ds_put_cstr(action, ");");
> -    }
> -
> -    build_gw_lrouter_nat_flows_for_lb(lb, gw_router, n_gw_router,
> -            reject, new_match, ds_cstr(action), est_match,
> -            "next;", lflows, prio, meter_groups);
> -
>      /* LB affinity flows for datapaths where CMS has specified
>       * skip_snat_for_lb floag option or regular datapaths.
>       */
>      char *lb_aff_action =
>          lb->skip_snat ? "flags.skip_snat_for_lb = 1; " : NULL;
> -    build_lb_affinity_lr_flows(lflows, lb, lb_vip, new_match,
> lb_aff_action,
> -                               lb_aff_router, n_lb_aff_router);
> -
> -    /* Distributed router logic */
> -    for (size_t i = 0; i < n_distributed_router; i++) {
> -        struct ovn_datapath *od = distributed_router[i];
> -        char *new_match_p = new_match;
> -        char *est_match_p = est_match;
> -        const char *meter = NULL;
> -        bool is_dp_lb_force_snat =
> -            !lport_addresses_is_empty(&od->lb_force_snat_addrs) ||
> -            od->lb_force_snat_router_ip;
> -
> -        if (reject) {
> -            meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
> meter_groups);
> -        }
> -
> -        if (lb_vip->n_backends || !lb_vip->empty_backend_rej) {
> -            new_match_p = xasprintf("%s && is_chassis_resident(%s)",
> -                                    new_match,
> -
> od->l3dgw_ports[0]->cr_port->json_key);
> -            est_match_p = xasprintf("%s && is_chassis_resident(%s)",
> -                                    est_match,
> -
> od->l3dgw_ports[0]->cr_port->json_key);
> -        }
> -
> -        if (lb->skip_snat) {
> -            ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                      new_match_p, skip_snat_new_action,
> -                                      NULL, meter, &lb->nlb->header_);
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                    est_match_p, skip_snat_est_action,
> -                                    &lb->nlb->header_);
> -        } else if (is_dp_lb_force_snat) {
> -            ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                      new_match_p, new_actions, NULL,
> -                                      meter, &lb->nlb->header_);
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                    est_match_p,
> -                                    "flags.force_snat_for_lb = 1; next;",
> -                                    &lb->nlb->header_);
> -        } else {
> -            ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                      new_match_p, ds_cstr(action), NULL,
> -                                      meter, &lb->nlb->header_);
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio,
> -                                    est_match_p, "next;",
> -                                    &lb->nlb->header_);
> -        }
> -
> -        if (new_match_p != new_match) {
> -            free(new_match_p);
> -        }
> -        if (est_match_p != est_match) {
> -            free(est_match_p);
> -        }
> -
> -        if (!lb_vip->n_backends) {
> -            continue;
> -        }
> -
> -        char *undnat_match_p = xasprintf(
> -            "%s) && outport == %s && is_chassis_resident(%s)",
> -            ds_cstr(&undnat_match),
> -            od->l3dgw_ports[0]->json_key,
> -            od->l3dgw_ports[0]->cr_port->json_key);
> -        if (lb->skip_snat) {
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> -                                    undnat_match_p, skip_snat_est_action,
> -                                    &lb->nlb->header_);
> -        } else if (is_dp_lb_force_snat) {
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
> -                                    undnat_match_p,
> -                                    "flags.force_snat_for_lb = 1; next;",
> -                                    &lb->nlb->header_);
> -        } else {
> -            ovn_lflow_add_with_hint(
> -                lflows, od, S_ROUTER_OUT_UNDNAT, 120, undnat_match_p,
> -                od->is_gw_router ? "ct_dnat;" : "ct_dnat_in_czone;",
> -                &lb->nlb->header_);
> -        }
> -        free(undnat_match_p);
> -    }
> +    build_lb_affinity_lr_flows(lflows, lb, lb_vip, ds_cstr(match),
> +                               lb_aff_action, lb_aff_router,
> n_lb_aff_router);
>
>      ds_destroy(&unsnat_match);
>      ds_destroy(&undnat_match);
> +    ds_destroy(&est_match);
> +    ds_destroy(&skip_snat_act);
> +    ds_destroy(&force_snat_act);
>
> -    free(skip_snat_new_action);
> -    free(skip_snat_est_action);
> -    free(est_match);
> -    free(new_match);
> -    free(new_actions);
> -
> -    free(gw_router_force_snat);
> -    free(gw_router_skip_snat);
> -    free(distributed_router);
>      free(lb_aff_force_snat_router);
>      free(lb_aff_router);
> -    free(gw_router);
>  }
>
>  static void
> @@ -10867,8 +10825,7 @@ build_lswitch_flows_for_lb(struct ovn_northd_lb
> *lb, struct hmap *lflows,
>       * REGBIT_CONNTRACK_COMMIT. */
>      build_lb_rules_pre_stateful(lflows, lb, features->ct_no_masked_label,
>                                  match, action);
> -    build_lb_rules(lflows, lb, features->ct_no_masked_label,
> -                   match, action, meter_groups);
> +    build_lb_rules(lflows, lb, features, match, action, meter_groups);
>  }
>
>  /* If there are any load balancing rules, we should send the packet to
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 5d5c32ee3..3fa02d2b3 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -3773,15 +3773,6 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0],
> [dnl
>    table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
> ct_commit_nat;)
>  ])
>
> -AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' |
> sort], [0], [dnl
> -  table=? (lr_out_undnat      ), priority=0    , match=(1), action=(next;)
> -  table=? (lr_out_undnat      ), priority=50   , match=(ip),
> action=(flags.loopback = 1; ct_dnat;)
> -])
> -
> -AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' |
> sort], [0], [dnl
> -  table=? (lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=? (lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> -])
>
>  check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="20.0.0.4 aef0::4"
>
> @@ -5616,6 +5607,66 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed
> 's/table=./table=?/' | sort], [0], [
>    table=? (lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>  ])
>
> +# LB with reject configured
> +check ovn-nbctl --wait=sb remove logical_router lr0 options
> lb_force_snat_ip
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lr-lb-del lr0
> +check ovn-nbctl lsp-add sw0 vip1
> +check ovn-nbctl lsp-add sw0 vip2
> +check ovn-nbctl --reject lb-add lb5 172.168.10.10 10.0.20.10,10.0.20.20
> +check ovn-nbctl --wait=sb set load_balancer lb5
> ip_port_mappings:10.0.20.10=vip1:10.0.0.2
> +check ovn-nbctl --wait=sb set load_balancer lb5
> ip_port_mappings:10.0.20.20=vip2:20.0.0.2
> +
> +check ovn-nbctl --wait=sb lr-lb-add lr0 lb5
> +AT_CHECK([ovn-nbctl --wait=sb -- --id=@hc create \
> +Load_Balancer_Health_Check vip="172.168.10.10" -- add Load_Balancer lb5 \
> +health_check @hc | uuidfilt], [0], [<0>
> +])
> +wait_row_count Service_Monitor 2
> +
> +# Set the service monitor for vip1 and vip2 to offline
> +sm_vip1=$(fetch_column Service_Monitor _uuid logical_port=vip1)
> +sm_vip2=$(fetch_column Service_Monitor _uuid logical_port=vip2)
> +
> +ovn-sbctl set service_monitor $sm_vip1 status=offline
> +ovn-sbctl set service_monitor $sm_vip2 status=offline
> +
> +AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
> +  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
> && ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1), action=(next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && reg0 == 172.168.10.10), action=(reg0 = 0; reject { outport <->
> inport; next(pipeline=egress,table=3);};)
> +  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
> 1; ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
> ct_commit_nat;)
> +])
> +
> +# LB with reject and skip_snat
> +check ovn-nbctl --wait=sb set load_balancer lb5 options:skip_snat=true
> +
> +AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
> +  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
> && ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1),
> action=(flags.skip_snat_for_lb = 1; next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && reg0 == 172.168.10.10), action=(flags.skip_snat_for_lb = 1; reg0
> = 0; reject { outport <-> inport; next(pipeline=egress,table=3);};)
> +  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
> 1; ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
> ct_commit_nat;)
> +])
> +
> +check ovn-nbctl --wait=sb remove load_balancer lb5 options skip_snat
> +
> +# LB with reject and force_snat
> +check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="router_ip"
> +
> +AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
> +  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
> && ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1),
> action=(flags.force_snat_for_lb = 1; next;)
> +  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && reg0 == 172.168.10.10), action=(flags.force_snat_for_lb = 1; reg0
> = 0; reject { outport <-> inport; next(pipeline=egress,table=3);};)
> +  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
> 1; ct_commit_nat;)
> +  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
> && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
> ct_commit_nat;)
> +])
> +
>  AT_CLEANUP
>  ])
>
> --
> 2.39.1
>
>
The diff between v5 and v6:

diff --git a/northd/northd.c b/northd/northd.c
index d22316bd3..4b0d37375 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -3928,20 +3928,20 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
     }

     bool is_lb_action = !(reject || drop);
+    const char *enclose = is_lb_action ? ");" : "";

     if (!ls_dp) {
         bool flag_supported = is_lb_action && features->ct_lb_related;
         ds_put_format(skip_snat_action, "flags.skip_snat_for_lb = 1; %s%s",
                       ds_cstr(action),
-                      flag_supported ? "; skip_snat);" : ");");
+                      flag_supported ? "; skip_snat);" : enclose);
         ds_put_format(force_snat_action, "flags.force_snat_for_lb = 1;
%s%s",
                       ds_cstr(action),
-                      flag_supported ? "; force_snat);" : ");");
-    }
-    if (is_lb_action) {
-        ds_put_cstr(action, ");");
+                      flag_supported ? "; force_snat);" : enclose);
     }

+    ds_put_cstr(action, enclose);
+
     return reject;
 }

diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index dfa064d53..3fa02d2b3 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5607,6 +5607,66 @@ AT_CHECK([grep "lr_out_snat" lr0flows | sed
's/table=./table=?/' | sort], [0], [
   table=? (lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
 ])

+# LB with reject configured
+check ovn-nbctl --wait=sb remove logical_router lr0 options
lb_force_snat_ip
+check ovn-nbctl --wait=sb sync
+
+check ovn-nbctl lr-lb-del lr0
+check ovn-nbctl lsp-add sw0 vip1
+check ovn-nbctl lsp-add sw0 vip2
+check ovn-nbctl --reject lb-add lb5 172.168.10.10 10.0.20.10,10.0.20.20
+check ovn-nbctl --wait=sb set load_balancer lb5
ip_port_mappings:10.0.20.10=vip1:10.0.0.2
+check ovn-nbctl --wait=sb set load_balancer lb5
ip_port_mappings:10.0.20.20=vip2:20.0.0.2
+
+check ovn-nbctl --wait=sb lr-lb-add lr0 lb5
+AT_CHECK([ovn-nbctl --wait=sb -- --id=@hc create \
+Load_Balancer_Health_Check vip="172.168.10.10" -- add Load_Balancer lb5 \
+health_check @hc | uuidfilt], [0], [<0>
+])
+wait_row_count Service_Monitor 2
+
+# Set the service monitor for vip1 and vip2 to offline
+sm_vip1=$(fetch_column Service_Monitor _uuid logical_port=vip1)
+sm_vip2=$(fetch_column Service_Monitor _uuid logical_port=vip2)
+
+ovn-sbctl set service_monitor $sm_vip1 status=offline
+ovn-sbctl set service_monitor $sm_vip2 status=offline
+
+AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
+  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
&& ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1), action=(next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
&& ip4 && reg0 == 172.168.10.10), action=(reg0 = 0; reject { outport <->
inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
&& !ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
1; ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
ct_commit_nat;)
+])
+
+# LB with reject and skip_snat
+check ovn-nbctl --wait=sb set load_balancer lb5 options:skip_snat=true
+
+AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
+  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
&& ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1),
action=(flags.skip_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
&& ip4 && reg0 == 172.168.10.10), action=(flags.skip_snat_for_lb = 1; reg0
= 0; reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
&& !ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
1; ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
ct_commit_nat;)
+])
+
+check ovn-nbctl --wait=sb remove load_balancer lb5 options skip_snat
+
+# LB with reject and force_snat
+check ovn-nbctl --wait=sb set logical_router lr0
options:lb_force_snat_ip="router_ip"
+
+AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
+  table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel
&& ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1),
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
&& ip4 && reg0 == 172.168.10.10), action=(flags.force_snat_for_lb = 1; reg0
= 0; reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
&& !ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb =
1; ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est
&& !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1;
ct_commit_nat;)
+])
+
 AT_CLEANUP
 ])

-- 

Ales Musil

Senior Software Engineer - OVN Core

Red Hat EMEA <https://www.redhat.com>

amu...@redhat.com    IM: amusil
<https://red.ht/sig>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to