> This commit introduces a new `ct_lb_mark_local` action that filters backends
> based on local chassis bindings for load balancing. By forming OpenFlow groups
> for balancing only with local backends.
> In the future, this will be used to implement distributed balancers.
> 
> Suggested-by: Vladislav Odintsov <[email protected]>
> Signed-off-by: Alexandra Rukomoinikova <[email protected]>

Acked-by: Lorenzo Bianconi <[email protected]>

> ---
>  controller/lflow.c          |  17 ++++
>  controller/lflow.h          |   1 +
>  controller/ovn-controller.c |   1 +
>  include/ovn/actions.h       |  11 +++
>  lib/actions.c               | 149 ++++++++++++++++++++++++++++++------
>  lib/ovn-util.c              |   2 +-
>  tests/ovn.at                |  11 +++
>  tests/test-ovn.c            |  11 +++
>  utilities/ovn-trace.c       |   2 +
>  9 files changed, 179 insertions(+), 26 deletions(-)
> ---
> v1 --> v2: added parsing tests for new action
> ---
> diff --git a/controller/lflow.c b/controller/lflow.c
> index e84fb2486..9979508eb 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -64,6 +64,7 @@ struct lookup_port_aux {
>      const struct sbrec_logical_flow *lflow;
>      struct objdep_mgr *deps_mgr;
>      const struct hmap *chassis_tunnels;
> +    const struct shash *local_bindings;
>  };
>  
>  struct condition_aux {
> @@ -153,6 +154,20 @@ lookup_port_cb(const void *aux_, const char *port_name, 
> unsigned int *portp)
>      return false;
>  }
>  
> +/* Given the OVN port name, get true if port locates on local chassis,
> + * false otherwise. */
> +static bool
> +lookup_local_port_cb(const void *aux_, const char *port_name)
> +{
> +    const struct lookup_port_aux *aux = aux_;
> +
> +    if (local_binding_get_primary_pb(aux->local_bindings, port_name)) {
> +        return true;
> +    }
> +
> +    return false;
> +}
> +
>  /* Given the OVN port name, get its openflow port */
>  static bool
>  tunnel_ofport_cb(const void *aux_, const char *port_name, ofp_port_t *ofport)
> @@ -857,6 +872,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow 
> *lflow,
>          .lflow = lflow,
>          .deps_mgr = l_ctx_out->lflow_deps_mgr,
>          .chassis_tunnels = l_ctx_in->chassis_tunnels,
> +        .local_bindings = l_ctx_in->lbinding_lports,
>      };
>  
>      /* Parse any meter to be used if this flow should punt packets to
> @@ -871,6 +887,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow 
> *lflow,
>      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
>      struct ovnact_encode_params ep = {
>          .lookup_port = lookup_port_cb,
> +        .lookup_local_port = lookup_local_port_cb,
>          .tunnel_ofport = tunnel_ofport_cb,
>          .aux = &aux,
>          .is_switch = ldp->is_switch,
> diff --git a/controller/lflow.h b/controller/lflow.h
> index d82acc0d8..0cde1ebf4 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -138,6 +138,7 @@ struct lflow_ctx_in {
>      const struct smap *template_vars;
>      const struct flow_collector_ids *collector_ids;
>      const struct hmap *local_lbs;
> +    const struct shash *lbinding_lports;
>      bool localnet_learn_fdb;
>      bool localnet_learn_fdb_changed;
>      bool explicit_arp_ns_output;
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index c2dab41c1..74e900fc2 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -3933,6 +3933,7 @@ init_lflow_ctx(struct engine_node *node,
>      l_ctx_in->template_vars = &template_vars->local_templates;
>      l_ctx_in->collector_ids = &fo->collector_ids;
>      l_ctx_in->local_lbs = &lb_data->local_lbs;
> +    l_ctx_in->lbinding_lports = &rt_data->lbinding_data.bindings;
>  
>      l_ctx_out->flow_table = &fo->flow_table;
>      l_ctx_out->group_table = &fo->group_table;
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index d3f0bfd04..2ca8dac8f 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -75,6 +75,7 @@ struct collector_set_ids;
>      OVNACT(CT_SNAT_IN_CZONE,  ovnact_ct_nat)          \
>      OVNACT(CT_LB,             ovnact_ct_lb)           \
>      OVNACT(CT_LB_MARK,        ovnact_ct_lb)           \
> +    OVNACT(CT_LB_MARK_LOCAL,  ovnact_ct_lb)           \
>      OVNACT(SELECT,            ovnact_select)          \
>      OVNACT(CT_CLEAR,          ovnact_null)            \
>      OVNACT(CT_COMMIT_NAT,     ovnact_ct_commit_to_zone) \
> @@ -311,6 +312,12 @@ struct ovnact_ct_commit_to_zone {
>      uint8_t ltable;
>  };
>  
> +enum ovnact_ct_lb_type {
> +    OVNACT_CT_LB_TYPE_LABEL,
> +    OVNACT_CT_LB_TYPE_MARK,
> +    OVNACT_CT_LB_LOCAL_TYPE_MARK,
> +};
> +
>  enum ovnact_ct_lb_flag {
>      OVNACT_CT_LB_FLAG_NONE,
>      OVNACT_CT_LB_FLAG_SKIP_SNAT,
> @@ -324,6 +331,7 @@ struct ovnact_ct_lb_dst {
>          ovs_be32 ipv4;
>      };
>      uint16_t port;
> +    char *port_name;
>  };
>  
>  /* OVNACT_CT_LB/OVNACT_CT_LB_MARK. */
> @@ -891,6 +899,9 @@ struct ovnact_encode_params {
>      bool (*lookup_port)(const void *aux, const char *port_name,
>                          unsigned int *portp);
>  
> +    /* Check if the logical port is bound to this chassis. */
> +    bool (*lookup_local_port)(const void *aux, const char *port_name);
> +
>      /* Looks up tunnel port to a chassis by its port name.  If found, stores
>       * its openflow port number in '*ofport' and returns true;
>       * otherwise, returns false. */
> diff --git a/lib/actions.c b/lib/actions.c
> index 53f4d20a9..ee508fd85 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -1187,8 +1187,25 @@ ovnact_ct_commit_to_zone_free(struct 
> ovnact_ct_commit_to_zone *cn OVS_UNUSED)
>  {
>  }
>  
> +
> +static bool
> +parse_ct_lb_logical_port_name(struct action_context *ctx,
> +                              struct ovnact_ct_lb_dst *dst)
> +{
> +    if (ctx->lexer->token.type != LEX_T_STRING) {
> +        return false;
> +    }
> +
> +    dst->port_name = xstrdup(ctx->lexer->token.s);
> +
> +    lexer_get(ctx->lexer);
> +
> +    return true;
> +}
> +
>  static void
> -parse_ct_lb_action(struct action_context *ctx, bool ct_lb_mark)
> +parse_ct_lb_action(struct action_context *ctx,
> +                   enum ovnact_ct_lb_type type)
>  {
>      if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
>          lexer_error(ctx->lexer, "\"ct_lb\" action not allowed in last 
> table.");
> @@ -1211,7 +1228,19 @@ parse_ct_lb_action(struct action_context *ctx, bool 
> ct_lb_mark)
>  
>          while (!lexer_match(ctx->lexer, LEX_T_SEMICOLON) &&
>                 !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
> -            struct ovnact_ct_lb_dst dst;
> +            struct ovnact_ct_lb_dst dst = {0};
> +
> +            if (type == OVNACT_CT_LB_LOCAL_TYPE_MARK) {
> +                if (!parse_ct_lb_logical_port_name(ctx, &dst)) {
> +                    vector_destroy(&dsts);
> +                    lexer_syntax_error(ctx->lexer,
> +                                       "expecting logical port name "
> +                                       "for distributed load balancer");
> +                    return;
> +                }
> +                lexer_get(ctx->lexer);
> +            }
> +
>              if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) {
>                  /* IPv6 address and port */
>                  if (ctx->lexer->token.type != LEX_T_INTEGER
> @@ -1298,8 +1327,19 @@ parse_ct_lb_action(struct action_context *ctx, bool 
> ct_lb_mark)
>          }
>      }
>  
> -    struct ovnact_ct_lb *cl = ct_lb_mark ? 
> ovnact_put_CT_LB_MARK(ctx->ovnacts)
> -                                         : ovnact_put_CT_LB(ctx->ovnacts);
> +    struct ovnact_ct_lb *cl;
> +    switch (type) {
> +        case OVNACT_CT_LB_TYPE_LABEL:
> +            cl = ovnact_put_CT_LB(ctx->ovnacts);
> +            break;
> +        case OVNACT_CT_LB_TYPE_MARK:
> +            cl = ovnact_put_CT_LB_MARK(ctx->ovnacts);
> +            break;
> +        case OVNACT_CT_LB_LOCAL_TYPE_MARK:
> +            cl = ovnact_put_CT_LB_MARK_LOCAL(ctx->ovnacts);
> +            break;
> +    }
> +
>      cl->ltable = ctx->pp->cur_ltable + 1;
>      cl->n_dsts = vector_len(&dsts);
>      cl->dsts = vector_steal_array(&dsts);
> @@ -1308,21 +1348,26 @@ parse_ct_lb_action(struct action_context *ctx, bool 
> ct_lb_mark)
>  }
>  
>  static void
> -format_ct_lb(const struct ovnact_ct_lb *cl, struct ds *s, bool ct_lb_mark)
> +format_ct_lb(const struct ovnact_ct_lb *cl, struct ds *s,
> +             enum ovnact_ct_lb_type type)
>  {
> -    if (ct_lb_mark) {
> -        ds_put_cstr(s, "ct_lb_mark");
> -    } else {
> -        ds_put_cstr(s, "ct_lb");
> -    }
> +    static const char *const lb_action_strings[] = {
> +        [OVNACT_CT_LB_TYPE_LABEL] = "ct_lb",
> +        [OVNACT_CT_LB_TYPE_MARK] = "ct_lb_mark",
> +        [OVNACT_CT_LB_LOCAL_TYPE_MARK] = "ct_lb_mark_local",
> +    };
> +    ds_put_cstr(s, lb_action_strings[type]);
> +
>      if (cl->n_dsts) {
>          ds_put_cstr(s, "(backends=");
>          for (size_t i = 0; i < cl->n_dsts; i++) {
>              if (i) {
>                  ds_put_char(s, ',');
>              }
> -
>              const struct ovnact_ct_lb_dst *dst = &cl->dsts[i];
> +            if (type == OVNACT_CT_LB_LOCAL_TYPE_MARK) {
> +                ds_put_format(s, "\"%s\":", dst->port_name);
> +            }
>              if (dst->family == AF_INET) {
>                  ds_put_format(s, IP_FMT, IP_ARGS(dst->ipv4));
>                  if (dst->port) {
> @@ -1363,20 +1408,36 @@ format_ct_lb(const struct ovnact_ct_lb *cl, struct ds 
> *s, bool ct_lb_mark)
>  static void
>  format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
>  {
> -    format_ct_lb(cl, s, false);
> +    format_ct_lb(cl, s, OVNACT_CT_LB_TYPE_LABEL);
>  }
>  
>  static void
>  format_CT_LB_MARK(const struct ovnact_ct_lb *cl, struct ds *s)
>  {
> -    format_ct_lb(cl, s, true);
> +    format_ct_lb(cl, s, OVNACT_CT_LB_TYPE_MARK);
> +}
> +
> +static void
> +format_CT_LB_MARK_LOCAL(const struct ovnact_ct_lb *cl, struct ds *s)
> +{
> +    format_ct_lb(cl, s, OVNACT_CT_LB_LOCAL_TYPE_MARK);
> +}
> +
> +static inline void
> +append_nat_destination(struct ds *ds, const char *ip_addr,
> +                       bool needs_brackets)
> +{
> +    ds_put_format(ds, "ct(nat(dst=%s%s%s",
> +                  needs_brackets ? "[" : "",
> +                  ip_addr,
> +                  needs_brackets ? "]" : "");
>  }
>  
>  static void
>  encode_ct_lb(const struct ovnact_ct_lb *cl,
>               const struct ovnact_encode_params *ep,
>               struct ofpbuf *ofpacts,
> -             bool ct_lb_mark)
> +             enum ovnact_ct_lb_type type)
>  {
>      uint8_t recirc_table = cl->ltable + first_ptable(ep, ep->pipeline);
>      if (!cl->n_dsts) {
> @@ -1408,7 +1469,8 @@ encode_ct_lb(const struct ovnact_ct_lb *cl,
>      struct ofpact_group *og;
>      uint32_t zone_reg = ep->is_switch ? MFF_LOG_CT_ZONE - MFF_REG0
>                              : MFF_LOG_DNAT_ZONE - MFF_REG0;
> -    const char *flag_reg = ct_lb_mark ? "ct_mark" : "ct_label";
> +    const char *flag_reg = (type == OVNACT_CT_LB_TYPE_LABEL)
> +                            ? "ct_label" : "ct_mark";
>  
>      const char *ct_flag_value;
>      switch (cl->ct_flag) {
> @@ -1435,32 +1497,50 @@ encode_ct_lb(const struct ovnact_ct_lb *cl,
>      BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS);
>      BUILD_ASSERT(MFF_LOG_DNAT_ZONE >= MFF_REG0);
>      BUILD_ASSERT(MFF_LOG_DNAT_ZONE < MFF_REG0 + FLOW_N_REGS);
> +
> +    size_t n_active_backends = 0;
>      for (size_t bucket_id = 0; bucket_id < cl->n_dsts; bucket_id++) {
>          const struct ovnact_ct_lb_dst *dst = &cl->dsts[bucket_id];
>          char ip_addr[INET6_ADDRSTRLEN];
> +
>          if (dst->family == AF_INET) {
>              inet_ntop(AF_INET, &dst->ipv4, ip_addr, sizeof ip_addr);
>          } else {
>              inet_ntop(AF_INET6, &dst->ipv6, ip_addr, sizeof ip_addr);
>          }
> -        ds_put_format(&ds, 
> ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions="
> -                      "ct(nat(dst=%s%s%s", bucket_id,
> -                      dst->family == AF_INET6 && dst->port ? "[" : "",
> -                      ip_addr,
> -                      dst->family == AF_INET6 && dst->port ? "]" : "");
> +
> +        if (type == OVNACT_CT_LB_LOCAL_TYPE_MARK
> +            && !ep->lookup_local_port(ep->aux, dst->port_name)) {
> +            continue;
> +        }
> +
> +        ds_put_format(&ds, 
> ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions=",
> +                      bucket_id);
> +
> +        bool needs_brackets = (dst->family == AF_INET6 && dst->port);
> +        append_nat_destination(&ds, ip_addr, needs_brackets);
> +
>          if (dst->port) {
>              ds_put_format(&ds, ":%"PRIu16, dst->port);
>          }
> +
>          ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15],"
>                        "exec(set_field:"
>                          OVN_CT_MASKED_STR(OVN_CT_NATTED)
>                        "->%s",
>                        recirc_table, zone_reg, flag_reg);
> +
>          if (ct_flag_value) {
>              ds_put_format(&ds, ",set_field:%s->%s", ct_flag_value, flag_reg);
>          }
>  
>          ds_put_cstr(&ds, "))");
> +
> +        n_active_backends++;
> +    }
> +
> +    if (!n_active_backends) {
> +        return;
>      }
>  
>      table_id = ovn_extend_table_assign_id(ep->group_table, ds_cstr(&ds),
> @@ -1480,7 +1560,7 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
>               const struct ovnact_encode_params *ep,
>               struct ofpbuf *ofpacts)
>  {
> -    encode_ct_lb(cl, ep, ofpacts, false);
> +    encode_ct_lb(cl, ep, ofpacts, OVNACT_CT_LB_TYPE_LABEL);
>  }
>  
>  static void
> @@ -1488,13 +1568,30 @@ encode_CT_LB_MARK(const struct ovnact_ct_lb *cl,
>                    const struct ovnact_encode_params *ep,
>                    struct ofpbuf *ofpacts)
>  {
> -    encode_ct_lb(cl, ep, ofpacts, true);
> +    encode_ct_lb(cl, ep, ofpacts, OVNACT_CT_LB_TYPE_MARK);
>  }
>  
>  static void
> -ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb)
> +encode_CT_LB_MARK_LOCAL(const struct ovnact_ct_lb *cl,
> +                        const struct ovnact_encode_params *ep,
> +                        struct ofpbuf *ofpacts)
> +{
> +    encode_ct_lb(cl, ep, ofpacts, OVNACT_CT_LB_LOCAL_TYPE_MARK);
> +}
> +
> +static void
> +ovnact_ct_lb_free_dsts(struct ovnact_ct_lb *ct_lb)
>  {
> +    for (size_t i = 0; i < ct_lb->n_dsts; i++) {
> +        free(ct_lb->dsts[i].port_name);
> +    }
>      free(ct_lb->dsts);
> +}
> +
> +static void
> +ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb)
> +{
> +    ovnact_ct_lb_free_dsts(ct_lb);
>      free(ct_lb->hash_fields);
>  }
>  
> @@ -5904,9 +6001,11 @@ parse_action(struct action_context *ctx)
>      } else if (lexer_match_id(ctx->lexer, "ct_lb")) {
>          VLOG_WARN_RL(&rl, "The \"ct_lb\" action is deprecated please "
>                            "consider using a different action.");
> -        parse_ct_lb_action(ctx, false);
> +        parse_ct_lb_action(ctx, OVNACT_CT_LB_TYPE_LABEL);
>      } else if (lexer_match_id(ctx->lexer, "ct_lb_mark")) {
> -        parse_ct_lb_action(ctx, true);
> +        parse_ct_lb_action(ctx, OVNACT_CT_LB_TYPE_MARK);
> +    } else if (lexer_match_id(ctx->lexer, "ct_lb_mark_local")) {
> +        parse_ct_lb_action(ctx, OVNACT_CT_LB_LOCAL_TYPE_MARK);
>      } else if (lexer_match_id(ctx->lexer, "ct_clear")) {
>          ovnact_put_CT_CLEAR(ctx->ovnacts);
>      } else if (lexer_match_id(ctx->lexer, "ct_commit_nat")) {
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index d27983d1e..e020cb81d 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -921,7 +921,7 @@ ip_address_and_port_from_lb_key(const char *key, char 
> **ip_address,
>   *
>   * NOTE: If OVN_NORTHD_PIPELINE_CSUM is updated make sure to double check
>   * whether an update of OVN_INTERNAL_MINOR_VER is required. */
> -#define OVN_NORTHD_PIPELINE_CSUM "2164662054 10958"
> +#define OVN_NORTHD_PIPELINE_CSUM "4239189964 11014"
>  #define OVN_INTERNAL_MINOR_VER 11
>  
>  /* Returns the OVN version. The caller must free the returned value. */
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 0a3671368..e163c14e3 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2375,6 +2375,17 @@ mirror("lsp1");
>  mirror(lsp1);
>      Syntax error at `lsp1' expecting port name string.
>  
> +ct_lb_mark_local(backends="lsp1":192.168.0.101:10880);
> +    encodes as group:25
> +    uses group: id(25), 
> name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.0.101:10880),commit,table=oflow_in_table,zone=NXM_NX_REG13[[0..15]],exec(set_field:2/2->ct_mark)))
> +    has prereqs ip
> +
> +ct_lb_mark_local(backends=192.168.0.101:80);
> +    Syntax error at `192.168.0.101' expecting logical port name for 
> distributed load balancer.
> +
> +ct_lb_mark_local(backends=lsp1:192.168.0.101:10880);
> +    Syntax error at `lsp1' expecting logical port name for distributed load 
> balancer.
> +
>  # Miscellaneous negative tests.
>  ;
>      Syntax error at `;'.
> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> index fae7e7bd5..1132714f6 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -265,6 +265,16 @@ lookup_port_cb(const void *ports_, const char 
> *port_name, unsigned int *portp)
>      return true;
>  }
>  
> +static bool
> +lookup_local_port_cb(const void *ports_, const char *port_name)
> +{
> +    const struct simap *ports = ports_;
> +    const struct simap_node *node = simap_find(ports, port_name);
> +
> +
> +    return node ? true : false;
> +}
> +
>  static bool
>  lookup_tunnel_ofport(const void *ports_, const char *port_name,
>                       ofp_port_t *ofport)
> @@ -1361,6 +1371,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx 
> OVS_UNUSED)
>              /* Encode the actions into OpenFlow and print. */
>              const struct ovnact_encode_params ep = {
>                  .lookup_port = lookup_port_cb,
> +                .lookup_local_port = lookup_local_port_cb,
>                  .tunnel_ofport = lookup_tunnel_ofport,
>                  .aux = &ports,
>                  .is_switch = true,
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index 9d9f915da..f288c8a8f 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -3590,6 +3590,8 @@ trace_actions(const struct ovnact *ovnacts, size_t 
> ovnacts_len,
>          case OVNACT_CT_STATE_SAVE:
>              execute_ct_save_state(ovnact_get_CT_STATE_SAVE(a), uflow, super);
>              break;
> +        case OVNACT_CT_LB_MARK_LOCAL:
> +            break;
>          }
>      }
>      ofpbuf_uninit(&stack);
> -- 
> 2.48.1
> 
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to