This commit adds new 'ct_lb_mark_local' action to enable per‑chassis load balancing, action filters backends by local chassis bindings and builds OpenFlow groups containing only local backends. This mechanism will later support fully distributed load balancers.
Suggested-by: Vladislav Odintsov <[email protected]> Signed-off-by: Alexandra Rukomoinikova <[email protected]> Acked-by: Lorenzo Bianconi <[email protected]> --- v2 --> v3: added Acked-by --- 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(-) 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 30c6dbebd..cd714ffef 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 93cd5cec5..0a507fbe4 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 cec029e42..eefa868d4 100644 --- a/lib/ovn-util.c +++ b/lib/ovn-util.c @@ -934,7 +934,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 6ea0d1de5..2411ab958 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
