From patchwork Tue Jan 30 21:22:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1893120 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TPdSG74xTz1yQ0 for ; Wed, 31 Jan 2024 08:23:10 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 8002461406; Tue, 30 Jan 2024 21:23:08 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 8002461406 X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id OWLQretcJZ9c; Tue, 30 Jan 2024 21:23:05 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id 7798661467; Tue, 30 Jan 2024 21:23:04 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 7798661467 Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 3616FC0DD4; Tue, 30 Jan 2024 21:23:04 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 4258DC0037 for ; Tue, 30 Jan 2024 21:23:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 0233A83F48 for ; Tue, 30 Jan 2024 21:22:37 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 0233A83F48 X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id pUhT8bKIpcNf for ; Tue, 30 Jan 2024 21:22:32 +0000 (UTC) Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::221]) by smtp1.osuosl.org (Postfix) with ESMTPS id DE42883E16 for ; Tue, 30 Jan 2024 21:22:31 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org DE42883E16 Received: by mail.gandi.net (Postfix) with ESMTPSA id E873E240004; Tue, 30 Jan 2024 21:22:28 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Tue, 30 Jan 2024 16:22:18 -0500 Message-ID: <20240130212218.1483304-1-numans@ovn.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240130212028.1482153-1-numans@ovn.org> References: <20240130212028.1482153-1-numans@ovn.org> MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Cc: Dumitru Ceara Subject: [ovs-dev] [PATCH ovn v6 07/13] northd: Move ovn_lb_datapaths from lib to northd module. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique It also moves the ovn-controller specific code from lib/lb.c and lib/lb.h to controller/lb.c and controller/lb.h. Acked-by: Han Zhou Co-authored-by: Dumitru Ceara Signed-off-by: Dumitru Ceara Signed-off-by: Numan Siddique --- controller/automake.mk | 2 + controller/lb.c | 146 ++++++++ controller/lb.h | 55 +++ controller/lflow.c | 1 + lib/lb.c | 771 +--------------------------------------- lib/lb.h | 199 +---------- northd/automake.mk | 4 +- northd/en-lb-data.c | 1 + northd/en-lr-stateful.c | 1 + northd/en-sync-sb.c | 1 + northd/lb.c | 651 +++++++++++++++++++++++++++++++++ northd/lb.h | 189 ++++++++++ northd/northd.c | 1 + 13 files changed, 1068 insertions(+), 954 deletions(-) create mode 100644 controller/lb.c create mode 100644 controller/lb.h create mode 100644 northd/lb.c create mode 100644 northd/lb.h diff --git a/controller/automake.mk b/controller/automake.mk index 0dbbd5d26b..a17ff0d60b 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -14,6 +14,8 @@ controller_ovn_controller_SOURCES = \ controller/if-status.h \ controller/ip-mcast.c \ controller/ip-mcast.h \ + controller/lb.c \ + controller/lb.h \ controller/lflow.c \ controller/lflow.h \ controller/lflow-cache.c \ diff --git a/controller/lb.c b/controller/lb.c new file mode 100644 index 0000000000..8f9f20ed54 --- /dev/null +++ b/controller/lb.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* OpenvSwitch lib includes. */ +#include "openvswitch/vlog.h" +#include "lib/smap.h" + +/* OVN includes */ +#include "lb.h" +#include "lib/ovn-sb-idl.h" +#include "ovn/lex.h" + +VLOG_DEFINE_THIS_MODULE(controller_lb); + +static void +ovn_lb_get_hairpin_snat_ip(const struct uuid *lb_uuid, + const struct smap *lb_options, + struct lport_addresses *hairpin_addrs) +{ + const char *addresses = smap_get(lb_options, "hairpin_snat_ip"); + + if (!addresses) { + return; + } + + if (!extract_ip_address(addresses, hairpin_addrs)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad hairpin_snat_ip %s in load balancer "UUID_FMT, + addresses, UUID_ARGS(lb_uuid)); + } +} + +struct ovn_controller_lb * +ovn_controller_lb_create(const struct sbrec_load_balancer *sbrec_lb, + const struct smap *template_vars, + struct sset *template_vars_ref) +{ + struct ovn_controller_lb *lb = xzalloc(sizeof *lb); + bool template = smap_get_bool(&sbrec_lb->options, "template", false); + + lb->slb = sbrec_lb; + lb->n_vips = smap_count(&sbrec_lb->vips); + lb->vips = xcalloc(lb->n_vips, sizeof *lb->vips); + + struct smap_node *node; + size_t n_vips = 0; + + SMAP_FOR_EACH (node, &sbrec_lb->vips) { + struct ovn_lb_vip *lb_vip = &lb->vips[n_vips]; + + struct lex_str key_s = template + ? lexer_parse_template_string(node->key, + template_vars, + template_vars_ref) + : lex_str_use(node->key); + struct lex_str value_s = template + ? lexer_parse_template_string(node->value, + template_vars, + template_vars_ref) + : lex_str_use(node->value); + char *error = ovn_lb_vip_init_explicit(lb_vip, + lex_str_get(&key_s), + lex_str_get(&value_s)); + if (error) { + free(error); + } else { + n_vips++; + } + lex_str_free(&key_s); + lex_str_free(&value_s); + } + + lb->proto = IPPROTO_TCP; + if (sbrec_lb->protocol && sbrec_lb->protocol[0]) { + if (!strcmp(sbrec_lb->protocol, "udp")) { + lb->proto = IPPROTO_UDP; + } else if (!strcmp(sbrec_lb->protocol, "sctp")) { + lb->proto = IPPROTO_SCTP; + } + } + + /* It's possible that parsing VIPs fails. Update the lb->n_vips to the + * correct value. + */ + lb->n_vips = n_vips; + + lb->hairpin_orig_tuple = smap_get_bool(&sbrec_lb->options, + "hairpin_orig_tuple", + false); + lb->ct_flush = smap_get_bool(&sbrec_lb->options, "ct_flush", false); + ovn_lb_get_hairpin_snat_ip(&sbrec_lb->header_.uuid, &sbrec_lb->options, + &lb->hairpin_snat_ips); + return lb; +} + +void +ovn_controller_lb_destroy(struct ovn_controller_lb *lb) +{ + for (size_t i = 0; i < lb->n_vips; i++) { + ovn_lb_vip_destroy(&lb->vips[i]); + } + free(lb->vips); + destroy_lport_addresses(&lb->hairpin_snat_ips); + free(lb); +} + +void +ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs) +{ + struct ovn_controller_lb *lb; + HMAP_FOR_EACH_POP (lb, hmap_node, ovn_controller_lbs) { + ovn_controller_lb_destroy(lb); + } + + hmap_destroy(ovn_controller_lbs); +} + +struct ovn_controller_lb * +ovn_controller_lb_find(const struct hmap *ovn_controller_lbs, + const struct uuid *uuid) +{ + struct ovn_controller_lb *lb; + size_t hash = uuid_hash(uuid); + HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, ovn_controller_lbs) { + if (uuid_equals(&lb->slb->header_.uuid, uuid)) { + return lb; + } + } + return NULL; +} + diff --git a/controller/lb.h b/controller/lb.h new file mode 100644 index 0000000000..84d51c3329 --- /dev/null +++ b/controller/lb.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_CONTROLLER_LB_H +#define OVN_CONTROLLER_LB_H 1 + +#include "lib/lb.h" + +struct sbrec_load_balancer; + +struct ovn_controller_lb { + struct hmap_node hmap_node; + + const struct sbrec_load_balancer *slb; /* May be NULL. */ + + uint8_t proto; + + struct ovn_lb_vip *vips; + size_t n_vips; + bool hairpin_orig_tuple; /* True if ovn-northd stores the original + * destination tuple in registers. + */ + bool ct_flush; /* True if we should flush CT after backend removal. */ + + struct lport_addresses hairpin_snat_ips; /* IP (v4 and/or v6) to be used + * as source for hairpinned + * traffic. + */ +}; + +struct ovn_controller_lb *ovn_controller_lb_create( + const struct sbrec_load_balancer *, + const struct smap *template_vars, + struct sset *template_vars_ref); +void ovn_controller_lb_destroy(struct ovn_controller_lb *); +void ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs); +struct ovn_controller_lb *ovn_controller_lb_find( + const struct hmap *ovn_controller_lbs, + const struct uuid *uuid); + +#endif /* OVN_CONTROLLER_LB_H */ + diff --git a/controller/lflow.c b/controller/lflow.c index c0cf0aa106..895d17d193 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -18,6 +18,7 @@ #include "lflow.h" #include "coverage.h" #include "ha-chassis.h" +#include "lb.h" #include "lflow-cache.h" #include "local_data.h" #include "lport.h" diff --git a/lib/lb.c b/lib/lb.c index d0d562b6fb..e67a5fcfd0 100644 --- a/lib/lb.c +++ b/lib/lb.c @@ -16,76 +16,16 @@ #include #include "lb.h" -#include "lib/ovn-nb-idl.h" -#include "lib/ovn-sb-idl.h" #include "lib/ovn-util.h" -#include "northd/northd.h" #include "ovn/lex.h" /* OpenvSwitch lib includes. */ #include "openvswitch/vlog.h" -#include "lib/bitmap.h" -#include "lib/smap.h" -#include "socket-util.h" VLOG_DEFINE_THIS_MODULE(lb); -static const char *lb_neighbor_responder_mode_names[] = { - [LB_NEIGH_RESPOND_REACHABLE] = "reachable", - [LB_NEIGH_RESPOND_ALL] = "all", - [LB_NEIGH_RESPOND_NONE] = "none", -}; - -static struct nbrec_load_balancer_health_check * -ovn_lb_get_health_check(const struct nbrec_load_balancer *nbrec_lb, - const char *vip_port_str, bool template); static void ovn_lb_backends_clear(struct ovn_lb_vip *vip); -struct ovn_lb_ip_set * -ovn_lb_ip_set_create(void) -{ - struct ovn_lb_ip_set *lb_ip_set = xzalloc(sizeof *lb_ip_set); - - sset_init(&lb_ip_set->ips_v4); - sset_init(&lb_ip_set->ips_v4_routable); - sset_init(&lb_ip_set->ips_v4_reachable); - sset_init(&lb_ip_set->ips_v6); - sset_init(&lb_ip_set->ips_v6_routable); - sset_init(&lb_ip_set->ips_v6_reachable); - - return lb_ip_set; -} - -void -ovn_lb_ip_set_destroy(struct ovn_lb_ip_set *lb_ip_set) -{ - if (!lb_ip_set) { - return; - } - sset_destroy(&lb_ip_set->ips_v4); - sset_destroy(&lb_ip_set->ips_v4_routable); - sset_destroy(&lb_ip_set->ips_v4_reachable); - sset_destroy(&lb_ip_set->ips_v6); - sset_destroy(&lb_ip_set->ips_v6_routable); - sset_destroy(&lb_ip_set->ips_v6_reachable); - free(lb_ip_set); -} - -struct ovn_lb_ip_set * -ovn_lb_ip_set_clone(struct ovn_lb_ip_set *lb_ip_set) -{ - struct ovn_lb_ip_set *clone = ovn_lb_ip_set_create(); - - sset_clone(&clone->ips_v4, &lb_ip_set->ips_v4); - sset_clone(&clone->ips_v4_routable, &lb_ip_set->ips_v4_routable); - sset_clone(&clone->ips_v4_reachable, &lb_ip_set->ips_v4_reachable); - sset_clone(&clone->ips_v6, &lb_ip_set->ips_v6); - sset_clone(&clone->ips_v6_routable, &lb_ip_set->ips_v6_routable); - sset_clone(&clone->ips_v6_reachable, &lb_ip_set->ips_v6_reachable); - - return clone; -} - /* Format for backend ips: "IP1:port1,IP2:port2,...". */ static char * ovn_lb_backends_init_explicit(struct ovn_lb_vip *lb_vip, const char *value) @@ -160,9 +100,9 @@ ovn_lb_backends_init_explicit(struct ovn_lb_vip *lb_vip, const char *value) return NULL; } -static -char *ovn_lb_vip_init_explicit(struct ovn_lb_vip *lb_vip, const char *lb_key, - const char *lb_value) +char * +ovn_lb_vip_init_explicit(struct ovn_lb_vip *lb_vip, const char *lb_key, + const char *lb_value) { if (!ip_address_and_port_from_lb_key(lb_key, &lb_vip->vip_str, &lb_vip->vip, &lb_vip->vip_port, @@ -369,22 +309,6 @@ ovn_lb_vip_format__(const struct ovn_lb_vip *vip, struct ds *s, } } -/* Formats the VIP in the way the ovn-controller expects it, that is, - * template IPv6 variables need to be between brackets too. - */ -static char * -ovn_lb_vip6_template_format_internal(const struct ovn_lb_vip *vip) -{ - struct ds s = DS_EMPTY_INITIALIZER; - - if (vip->vip_str && *vip->vip_str == LEX_TEMPLATE_PREFIX) { - ovn_lb_vip_format__(vip, &s, true); - } else { - ovn_lb_vip_format(vip, &s, !!vip->port_str); - } - return ds_steal_cstr(&s); -} - void ovn_lb_vip_format(const struct ovn_lb_vip *vip, struct ds *s, bool template) { @@ -417,540 +341,20 @@ ovn_lb_vip_backends_format(const struct ovn_lb_vip *vip, struct ds *s) } } -static -void ovn_northd_lb_vip_init(struct ovn_northd_lb_vip *lb_vip_nb, - const struct ovn_lb_vip *lb_vip, - const struct nbrec_load_balancer *nbrec_lb, - const char *vip_port_str, const char *backend_ips, - bool template) -{ - lb_vip_nb->backend_ips = xstrdup(backend_ips); - lb_vip_nb->n_backends = lb_vip->n_backends; - lb_vip_nb->backends_nb = xcalloc(lb_vip_nb->n_backends, - sizeof *lb_vip_nb->backends_nb); - lb_vip_nb->lb_health_check = - ovn_lb_get_health_check(nbrec_lb, vip_port_str, template); -} - -static void -ovn_lb_vip_backends_health_check_init(const struct ovn_northd_lb *lb, - const struct ovn_lb_vip *lb_vip, - struct ovn_northd_lb_vip *lb_vip_nb) -{ - struct ds key = DS_EMPTY_INITIALIZER; - - for (size_t j = 0; j < lb_vip->n_backends; j++) { - struct ovn_lb_backend *backend = &lb_vip->backends[j]; - ds_clear(&key); - ds_put_format(&key, IN6_IS_ADDR_V4MAPPED(&lb_vip->vip) - ? "%s" : "[%s]", backend->ip_str); - - const char *s = smap_get(&lb->nlb->ip_port_mappings, ds_cstr(&key)); - if (!s) { - continue; - } - - char *svc_mon_src_ip = NULL; - char *port_name = xstrdup(s); - char *p = strstr(port_name, ":"); - if (p) { - *p = 0; - p++; - struct sockaddr_storage svc_mon_src_addr; - if (!inet_parse_address(p, &svc_mon_src_addr)) { - static struct vlog_rate_limit rl = - VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", p); - } else { - struct ds src_ip_s = DS_EMPTY_INITIALIZER; - ss_format_address_nobracks(&svc_mon_src_addr, - &src_ip_s); - svc_mon_src_ip = ds_steal_cstr(&src_ip_s); - } - } - - if (svc_mon_src_ip) { - struct ovn_northd_lb_backend *backend_nb = - &lb_vip_nb->backends_nb[j]; - backend_nb->health_check = true; - backend_nb->logical_port = xstrdup(port_name); - backend_nb->svc_mon_src_ip = svc_mon_src_ip; - } - free(port_name); - } - - ds_destroy(&key); -} - -static -void ovn_northd_lb_vip_destroy(struct ovn_northd_lb_vip *vip) -{ - free(vip->backend_ips); - for (size_t i = 0; i < vip->n_backends; i++) { - free(vip->backends_nb[i].logical_port); - free(vip->backends_nb[i].svc_mon_src_ip); - } - free(vip->backends_nb); -} - -static void -ovn_lb_get_hairpin_snat_ip(const struct uuid *lb_uuid, - const struct smap *lb_options, - struct lport_addresses *hairpin_addrs) -{ - const char *addresses = smap_get(lb_options, "hairpin_snat_ip"); - - if (!addresses) { - return; - } - - if (!extract_ip_address(addresses, hairpin_addrs)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad hairpin_snat_ip %s in load balancer "UUID_FMT, - addresses, UUID_ARGS(lb_uuid)); - } -} - -static bool -ovn_lb_get_routable_mode(const struct nbrec_load_balancer *nbrec_lb, - bool routable, bool template) -{ - if (template && routable) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_WARN_RL(&rl, "Template load balancer "UUID_FMT" does not suport " - "option 'add_route'. Forcing it to disabled.", - UUID_ARGS(&nbrec_lb->header_.uuid)); - return false; - } - return routable; -} - -static bool -ovn_lb_neigh_mode_is_valid(enum lb_neighbor_responder_mode mode, bool template) -{ - if (!template) { - return true; - } - - switch (mode) { - case LB_NEIGH_RESPOND_REACHABLE: - return false; - case LB_NEIGH_RESPOND_ALL: - case LB_NEIGH_RESPOND_NONE: - return true; - } - return false; -} - -static enum lb_neighbor_responder_mode -ovn_lb_get_neigh_mode(const struct nbrec_load_balancer *nbrec_lb, - const char *mode, bool template) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - enum lb_neighbor_responder_mode default_mode = - template ? LB_NEIGH_RESPOND_NONE : LB_NEIGH_RESPOND_REACHABLE; - - if (!mode) { - mode = lb_neighbor_responder_mode_names[default_mode]; - } - - for (size_t i = 0; i < ARRAY_SIZE(lb_neighbor_responder_mode_names); i++) { - if (!strcmp(mode, lb_neighbor_responder_mode_names[i])) { - if (ovn_lb_neigh_mode_is_valid(i, template)) { - return i; - } - break; - } - } - - VLOG_WARN_RL(&rl, "Invalid neighbor responder mode %s for load balancer " - UUID_FMT", forcing it to %s", - mode, UUID_ARGS(&nbrec_lb->header_.uuid), - lb_neighbor_responder_mode_names[default_mode]); - return default_mode; -} - -static struct nbrec_load_balancer_health_check * -ovn_lb_get_health_check(const struct nbrec_load_balancer *nbrec_lb, - const char *vip_port_str, bool template) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - - if (!nbrec_lb->n_health_check) { - return NULL; - } - - if (nbrec_lb->protocol && !strcmp(nbrec_lb->protocol, "sctp")) { - VLOG_WARN_RL(&rl, - "SCTP load balancers do not currently support " - "health checks. Not creating health checks for " - "load balancer " UUID_FMT, - UUID_ARGS(&nbrec_lb->header_.uuid)); - return NULL; - } - - if (template) { - VLOG_WARN_RL(&rl, - "Template load balancers do not currently support " - "health checks. Not creating health checks for " - "load balancer " UUID_FMT, - UUID_ARGS(&nbrec_lb->header_.uuid)); - return NULL; - } - - for (size_t i = 0; i < nbrec_lb->n_health_check; i++) { - if (!strcmp(nbrec_lb->health_check[i]->vip, vip_port_str)) { - return nbrec_lb->health_check[i]; - } - } - return NULL; -} - -static void -ovn_northd_lb_init(struct ovn_northd_lb *lb, - const struct nbrec_load_balancer *nbrec_lb) -{ - bool template = smap_get_bool(&nbrec_lb->options, "template", false); - bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp"); - bool is_sctp = nullable_string_is_equal(nbrec_lb->protocol, "sctp"); - int address_family = !strcmp(smap_get_def(&nbrec_lb->options, - "address-family", "ipv4"), - "ipv4") - ? AF_INET - : AF_INET6; - - lb->nlb = nbrec_lb; - lb->proto = is_udp ? "udp" : is_sctp ? "sctp" : "tcp"; - lb->n_vips = smap_count(&nbrec_lb->vips); - lb->vips = xcalloc(lb->n_vips, sizeof *lb->vips); - lb->vips_nb = xcalloc(lb->n_vips, sizeof *lb->vips_nb); - smap_init(&lb->template_vips); - lb->controller_event = smap_get_bool(&nbrec_lb->options, "event", false); - - bool routable = smap_get_bool(&nbrec_lb->options, "add_route", false); - lb->routable = ovn_lb_get_routable_mode(nbrec_lb, routable, template); - - lb->skip_snat = smap_get_bool(&nbrec_lb->options, "skip_snat", false); - lb->template = template; - - const char *mode = smap_get(&nbrec_lb->options, "neighbor_responder"); - lb->neigh_mode = ovn_lb_get_neigh_mode(nbrec_lb, mode, template); - - uint32_t affinity_timeout = - smap_get_uint(&nbrec_lb->options, "affinity_timeout", 0); - if (affinity_timeout > UINT16_MAX) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_WARN_RL(&rl, "max affinity_timeout timeout value is %u", - UINT16_MAX); - affinity_timeout = UINT16_MAX; - } - lb->affinity_timeout = affinity_timeout; - - sset_init(&lb->ips_v4); - sset_init(&lb->ips_v6); - struct smap_node *node; - size_t n_vips = 0; - - SMAP_FOR_EACH (node, &nbrec_lb->vips) { - struct ovn_lb_vip *lb_vip = &lb->vips[n_vips]; - struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[n_vips]; - - char *error = ovn_lb_vip_init(lb_vip, node->key, node->value, - template, address_family); - if (error) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Failed to initialize LB VIP: %s", error); - ovn_lb_vip_destroy(lb_vip); - free(error); - continue; - } - lb_vip->empty_backend_rej = smap_get_bool(&nbrec_lb->options, - "reject", false); - ovn_northd_lb_vip_init(lb_vip_nb, lb_vip, nbrec_lb, - node->key, node->value, template); - if (lb_vip_nb->lb_health_check) { - lb->health_checks = true; - } - - if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) { - sset_add(&lb->ips_v4, lb_vip->vip_str); - } else { - sset_add(&lb->ips_v6, lb_vip->vip_str); - } - - if (lb->template && address_family == AF_INET6) { - smap_add_nocopy(&lb->template_vips, - ovn_lb_vip6_template_format_internal(lb_vip), - xstrdup(node->value)); - } - n_vips++; - - if (lb_vip_nb->lb_health_check) { - ovn_lb_vip_backends_health_check_init(lb, lb_vip, lb_vip_nb); - } - } - - /* It's possible that parsing VIPs fails. Update the lb->n_vips to the - * correct value. - */ - lb->n_vips = n_vips; - - if (nbrec_lb->n_selection_fields) { - char *proto = NULL; - if (nbrec_lb->protocol && nbrec_lb->protocol[0]) { - proto = nbrec_lb->protocol; - } - - struct ds sel_fields = DS_EMPTY_INITIALIZER; - for (size_t i = 0; i < lb->nlb->n_selection_fields; i++) { - char *field = lb->nlb->selection_fields[i]; - if (!strcmp(field, "tp_src") && proto) { - ds_put_format(&sel_fields, "%s_src,", proto); - } else if (!strcmp(field, "tp_dst") && proto) { - ds_put_format(&sel_fields, "%s_dst,", proto); - } else { - ds_put_format(&sel_fields, "%s,", field); - } - } - ds_chomp(&sel_fields, ','); - lb->selection_fields = ds_steal_cstr(&sel_fields); - } -} - -struct ovn_northd_lb * -ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb) -{ - struct ovn_northd_lb *lb = xzalloc(sizeof *lb); - ovn_northd_lb_init(lb, nbrec_lb); - return lb; -} - -struct ovn_northd_lb * -ovn_northd_lb_find(const struct hmap *lbs, const struct uuid *uuid) -{ - struct ovn_northd_lb *lb; - size_t hash = uuid_hash(uuid); - HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, lbs) { - if (uuid_equals(&lb->nlb->header_.uuid, uuid)) { - return lb; - } - } - return NULL; -} - -const struct smap * -ovn_northd_lb_get_vips(const struct ovn_northd_lb *lb) -{ - if (!smap_is_empty(&lb->template_vips)) { - return &lb->template_vips; - } - return &lb->nlb->vips; -} - -static void -ovn_northd_lb_cleanup(struct ovn_northd_lb *lb) -{ - for (size_t i = 0; i < lb->n_vips; i++) { - ovn_lb_vip_destroy(&lb->vips[i]); - ovn_northd_lb_vip_destroy(&lb->vips_nb[i]); - } - free(lb->vips); - free(lb->vips_nb); - lb->vips = NULL; - lb->vips_nb = NULL; - smap_destroy(&lb->template_vips); - sset_destroy(&lb->ips_v4); - sset_destroy(&lb->ips_v6); - free(lb->selection_fields); - lb->selection_fields = NULL; - lb->health_checks = false; -} - -void -ovn_northd_lb_destroy(struct ovn_northd_lb *lb) -{ - ovn_northd_lb_cleanup(lb); - free(lb); -} - -void -ovn_northd_lb_reinit(struct ovn_northd_lb *lb, - const struct nbrec_load_balancer *nbrec_lb) -{ - ovn_northd_lb_cleanup(lb); - ovn_northd_lb_init(lb, nbrec_lb); -} - -static void -ovn_lb_group_init(struct ovn_lb_group *lb_group, - const struct nbrec_load_balancer_group *nbrec_lb_group, - const struct hmap *lbs) -{ - lb_group->n_lbs = nbrec_lb_group->n_load_balancer; - lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs); - lb_group->lb_ips = ovn_lb_ip_set_create(); - - for (size_t i = 0; i < nbrec_lb_group->n_load_balancer; i++) { - const struct uuid *lb_uuid = - &nbrec_lb_group->load_balancer[i]->header_.uuid; - lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid); - lb_group->has_routable_lb |= lb_group->lbs[i]->routable; - } -} - -/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group record - * and an array of 'struct ovn_northd_lb' objects for its associated - * load balancers. */ -struct ovn_lb_group * -ovn_lb_group_create(const struct nbrec_load_balancer_group *nbrec_lb_group, - const struct hmap *lbs) -{ - struct ovn_lb_group *lb_group = xzalloc(sizeof *lb_group); - lb_group->uuid = nbrec_lb_group->header_.uuid; - ovn_lb_group_init(lb_group, nbrec_lb_group, lbs); - return lb_group; -} - -static void -ovn_lb_group_cleanup(struct ovn_lb_group *lb_group) -{ - ovn_lb_ip_set_destroy(lb_group->lb_ips); - lb_group->lb_ips = NULL; - lb_group->has_routable_lb = false; - free(lb_group->lbs); -} - -void -ovn_lb_group_destroy(struct ovn_lb_group *lb_group) -{ - if (!lb_group) { - return; - } - - ovn_lb_group_cleanup(lb_group); - free(lb_group); -} - -void -ovn_lb_group_reinit(struct ovn_lb_group *lb_group, - const struct nbrec_load_balancer_group *nbrec_lb_group, - const struct hmap *lbs) -{ - ovn_lb_group_cleanup(lb_group); - ovn_lb_group_init(lb_group, nbrec_lb_group, lbs); -} - -struct ovn_lb_group * -ovn_lb_group_find(const struct hmap *lb_groups, const struct uuid *uuid) -{ - struct ovn_lb_group *lb_group; - size_t hash = uuid_hash(uuid); - - HMAP_FOR_EACH_WITH_HASH (lb_group, hmap_node, hash, lb_groups) { - if (uuid_equals(&lb_group->uuid, uuid)) { - return lb_group; - } - } - return NULL; -} - -struct ovn_controller_lb * -ovn_controller_lb_create(const struct sbrec_load_balancer *sbrec_lb, - const struct smap *template_vars, - struct sset *template_vars_ref) -{ - struct ovn_controller_lb *lb = xzalloc(sizeof *lb); - bool template = smap_get_bool(&sbrec_lb->options, "template", false); - - lb->slb = sbrec_lb; - lb->n_vips = smap_count(&sbrec_lb->vips); - lb->vips = xcalloc(lb->n_vips, sizeof *lb->vips); - - struct smap_node *node; - size_t n_vips = 0; - - SMAP_FOR_EACH (node, &sbrec_lb->vips) { - struct ovn_lb_vip *lb_vip = &lb->vips[n_vips]; - - struct lex_str key_s = template - ? lexer_parse_template_string(node->key, - template_vars, - template_vars_ref) - : lex_str_use(node->key); - struct lex_str value_s = template - ? lexer_parse_template_string(node->value, - template_vars, - template_vars_ref) - : lex_str_use(node->value); - char *error = ovn_lb_vip_init_explicit(lb_vip, - lex_str_get(&key_s), - lex_str_get(&value_s)); - if (error) { - free(error); - } else { - n_vips++; - } - lex_str_free(&key_s); - lex_str_free(&value_s); - } - - lb->proto = IPPROTO_TCP; - if (sbrec_lb->protocol && sbrec_lb->protocol[0]) { - if (!strcmp(sbrec_lb->protocol, "udp")) { - lb->proto = IPPROTO_UDP; - } else if (!strcmp(sbrec_lb->protocol, "sctp")) { - lb->proto = IPPROTO_SCTP; - } - } - - /* It's possible that parsing VIPs fails. Update the lb->n_vips to the - * correct value. - */ - lb->n_vips = n_vips; - - lb->hairpin_orig_tuple = smap_get_bool(&sbrec_lb->options, - "hairpin_orig_tuple", - false); - lb->ct_flush = smap_get_bool(&sbrec_lb->options, "ct_flush", false); - ovn_lb_get_hairpin_snat_ip(&sbrec_lb->header_.uuid, &sbrec_lb->options, - &lb->hairpin_snat_ips); - return lb; -} - -void -ovn_controller_lb_destroy(struct ovn_controller_lb *lb) -{ - for (size_t i = 0; i < lb->n_vips; i++) { - ovn_lb_vip_destroy(&lb->vips[i]); - } - free(lb->vips); - destroy_lport_addresses(&lb->hairpin_snat_ips); - free(lb); -} - -void -ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs) +/* Formats the VIP in the way the ovn-controller expects it, that is, + * template IPv6 variables need to be between brackets too. + */ +char * +ovn_lb_vip6_template_format_internal(const struct ovn_lb_vip *vip) { - struct ovn_controller_lb *lb; - HMAP_FOR_EACH_POP (lb, hmap_node, ovn_controller_lbs) { - ovn_controller_lb_destroy(lb); - } - - hmap_destroy(ovn_controller_lbs); -} + struct ds s = DS_EMPTY_INITIALIZER; -struct ovn_controller_lb * -ovn_controller_lb_find(const struct hmap *ovn_controller_lbs, - const struct uuid *uuid) -{ - struct ovn_controller_lb *lb; - size_t hash = uuid_hash(uuid); - HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, ovn_controller_lbs) { - if (uuid_equals(&lb->slb->header_.uuid, uuid)) { - return lb; - } + if (vip->vip_str && *vip->vip_str == LEX_TEMPLATE_PREFIX) { + ovn_lb_vip_format__(vip, &s, true); + } else { + ovn_lb_vip_format(vip, &s, !!vip->port_str); } - return NULL; + return ds_steal_cstr(&s); } static uint32_t @@ -1020,150 +424,3 @@ ovn_lb_5tuples_destroy(struct hmap *tuples) hmap_destroy(tuples); } - -void -build_lrouter_lb_ips(struct ovn_lb_ip_set *lb_ips, - const struct ovn_northd_lb *lb) -{ - add_ips_to_lb_ip_set(lb_ips, lb->routable, &lb->ips_v4, &lb->ips_v6); -} - -void -add_ips_to_lb_ip_set(struct ovn_lb_ip_set *lb_ips, - bool is_routable, - const struct sset *lb_ips_v4, - const struct sset *lb_ips_v6) -{ - const char *ip_address; - - SSET_FOR_EACH (ip_address, lb_ips_v4) { - sset_add(&lb_ips->ips_v4, ip_address); - if (is_routable) { - sset_add(&lb_ips->ips_v4_routable, ip_address); - } - } - SSET_FOR_EACH (ip_address, lb_ips_v6) { - sset_add(&lb_ips->ips_v6, ip_address); - if (is_routable) { - sset_add(&lb_ips->ips_v6_routable, ip_address); - } - } -} - -void -remove_ips_from_lb_ip_set(struct ovn_lb_ip_set *lb_ips, - bool is_routable, - const struct sset *lb_ips_v4, - const struct sset *lb_ips_v6) -{ - const char *ip_address; - - SSET_FOR_EACH (ip_address, lb_ips_v4) { - sset_find_and_delete(&lb_ips->ips_v4, ip_address); - if (is_routable) { - sset_find_and_delete(&lb_ips->ips_v4_routable, ip_address); - } - } - SSET_FOR_EACH (ip_address, lb_ips_v6) { - sset_find_and_delete(&lb_ips->ips_v6, ip_address); - if (is_routable) { - sset_find_and_delete(&lb_ips->ips_v6_routable, ip_address); - } - } -} - -/* lb datapaths functions */ -struct ovn_lb_datapaths * -ovn_lb_datapaths_create(const struct ovn_northd_lb *lb, size_t n_ls_datapaths, - size_t n_lr_datapaths) -{ - struct ovn_lb_datapaths *lb_dps = xzalloc(sizeof *lb_dps); - lb_dps->lb = lb; - lb_dps->nb_ls_map = bitmap_allocate(n_ls_datapaths); - lb_dps->nb_lr_map = bitmap_allocate(n_lr_datapaths); - - return lb_dps; -} - -struct ovn_lb_datapaths * -ovn_lb_datapaths_find(const struct hmap *lb_dps_map, - const struct uuid *lb_uuid) -{ - struct ovn_lb_datapaths *lb_dps; - size_t hash = uuid_hash(lb_uuid); - HMAP_FOR_EACH_WITH_HASH (lb_dps, hmap_node, hash, lb_dps_map) { - if (uuid_equals(&lb_dps->lb->nlb->header_.uuid, lb_uuid)) { - return lb_dps; - } - } - return NULL; -} - -void -ovn_lb_datapaths_destroy(struct ovn_lb_datapaths *lb_dps) -{ - bitmap_free(lb_dps->nb_lr_map); - bitmap_free(lb_dps->nb_ls_map); - free(lb_dps); -} - -void -ovn_lb_datapaths_add_lr(struct ovn_lb_datapaths *lb_dps, size_t n, - struct ovn_datapath **ods) -{ - for (size_t i = 0; i < n; i++) { - if (!bitmap_is_set(lb_dps->nb_lr_map, ods[i]->index)) { - bitmap_set1(lb_dps->nb_lr_map, ods[i]->index); - lb_dps->n_nb_lr++; - } - } -} - -void -ovn_lb_datapaths_add_ls(struct ovn_lb_datapaths *lb_dps, size_t n, - struct ovn_datapath **ods) -{ - for (size_t i = 0; i < n; i++) { - if (!bitmap_is_set(lb_dps->nb_ls_map, ods[i]->index)) { - bitmap_set1(lb_dps->nb_ls_map, ods[i]->index); - lb_dps->n_nb_ls++; - } - } -} - -struct ovn_lb_group_datapaths * -ovn_lb_group_datapaths_create(const struct ovn_lb_group *lb_group, - size_t max_ls_datapaths, - size_t max_lr_datapaths) -{ - struct ovn_lb_group_datapaths *lb_group_dps = - xzalloc(sizeof *lb_group_dps); - lb_group_dps->lb_group = lb_group; - lb_group_dps->ls = xmalloc(max_ls_datapaths * sizeof *lb_group_dps->ls); - lb_group_dps->lr = xmalloc(max_lr_datapaths * sizeof *lb_group_dps->lr); - - return lb_group_dps; -} - -void -ovn_lb_group_datapaths_destroy(struct ovn_lb_group_datapaths *lb_group_dps) -{ - free(lb_group_dps->ls); - free(lb_group_dps->lr); - free(lb_group_dps); -} - -struct ovn_lb_group_datapaths * -ovn_lb_group_datapaths_find(const struct hmap *lb_group_dps_map, - const struct uuid *lb_group_uuid) -{ - struct ovn_lb_group_datapaths *lb_group_dps; - size_t hash = uuid_hash(lb_group_uuid); - - HMAP_FOR_EACH_WITH_HASH (lb_group_dps, hmap_node, hash, lb_group_dps_map) { - if (uuid_equals(&lb_group_dps->lb_group->uuid, lb_group_uuid)) { - return lb_group_dps; - } - } - return NULL; -} diff --git a/lib/lb.h b/lib/lb.h index b8e3c1e8fb..bcc677f82e 100644 --- a/lib/lb.h +++ b/lib/lb.h @@ -25,63 +25,8 @@ #include "sset.h" #include "uuid.h" -struct nbrec_load_balancer; -struct nbrec_load_balancer_group; -struct sbrec_load_balancer; -struct sbrec_datapath_binding; -struct ovn_datapath; -struct ovn_dp_group; -struct ovn_port; struct uuid; -enum lb_neighbor_responder_mode { - LB_NEIGH_RESPOND_REACHABLE, - LB_NEIGH_RESPOND_ALL, - LB_NEIGH_RESPOND_NONE, -}; - -/* The "routable" ssets are subsets of the load balancer IPs for which IP - * routes and ARP resolution flows are automatically added. */ -struct ovn_lb_ip_set { - struct sset ips_v4; - struct sset ips_v4_routable; - struct sset ips_v4_reachable; - struct sset ips_v6; - struct sset ips_v6_routable; - struct sset ips_v6_reachable; -}; - -struct ovn_lb_ip_set *ovn_lb_ip_set_create(void); -void ovn_lb_ip_set_destroy(struct ovn_lb_ip_set *); -struct ovn_lb_ip_set *ovn_lb_ip_set_clone(struct ovn_lb_ip_set *); - -struct ovn_northd_lb { - struct hmap_node hmap_node; - - const struct nbrec_load_balancer *nlb; /* May be NULL. */ - const char *proto; - char *selection_fields; - struct ovn_lb_vip *vips; - struct ovn_northd_lb_vip *vips_nb; - struct smap template_vips; /* Slightly changed template VIPs, populated - * if needed. Until now it's only required - * for IPv6 template load balancers. */ - size_t n_vips; - - enum lb_neighbor_responder_mode neigh_mode; - bool controller_event; - bool routable; - bool skip_snat; - bool template; - uint16_t affinity_timeout; - - struct sset ips_v4; - struct sset ips_v6; - - /* Indicates if the load balancer has health checks configured. */ - bool health_checks; -}; - struct ovn_lb_vip { struct in6_addr vip; /* Only used in ovn-controller. */ char *vip_str; /* Actual VIP string representation (without port). @@ -113,153 +58,15 @@ struct ovn_lb_backend { */ }; -/* ovn-northd specific backend information. */ -struct ovn_northd_lb_vip { - char *backend_ips; - struct ovn_northd_lb_backend *backends_nb; - size_t n_backends; - - struct nbrec_load_balancer_health_check *lb_health_check; -}; - -struct ovn_northd_lb_backend { - bool health_check; - char *logical_port; /* Logical port to which the ip belong to. */ - char *svc_mon_src_ip; /* Source IP to use for monitoring. */ -}; - -struct ovn_northd_lb *ovn_northd_lb_create(const struct nbrec_load_balancer *); -struct ovn_northd_lb *ovn_northd_lb_find(const struct hmap *, - const struct uuid *); -const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb *); -void ovn_northd_lb_destroy(struct ovn_northd_lb *); -void ovn_northd_lb_reinit(struct ovn_northd_lb *, - const struct nbrec_load_balancer *); - -void build_lrouter_lb_ips(struct ovn_lb_ip_set *, - const struct ovn_northd_lb *); -void add_ips_to_lb_ip_set(struct ovn_lb_ip_set *lb_ips, - bool is_routable, - const struct sset *lb_ips_v4, - const struct sset *lb_ips_v6); -void remove_ips_from_lb_ip_set(struct ovn_lb_ip_set *lb_ips, - bool is_routable, - const struct sset *lb_ips_v4, - const struct sset *lb_ips_v6); - -struct ovn_lb_group { - struct hmap_node hmap_node; - struct uuid uuid; - size_t n_lbs; - struct ovn_northd_lb **lbs; - struct ovn_lb_ip_set *lb_ips; - bool has_routable_lb; -}; - -struct ovn_lb_group *ovn_lb_group_create( - const struct nbrec_load_balancer_group *, - const struct hmap *lbs); -void ovn_lb_group_destroy(struct ovn_lb_group *lb_group); -struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups, - const struct uuid *); -void ovn_lb_group_reinit( - struct ovn_lb_group *lb_group, - const struct nbrec_load_balancer_group *, - const struct hmap *lbs); - -struct ovn_lb_datapaths { - struct hmap_node hmap_node; - - const struct ovn_northd_lb *lb; - size_t n_nb_ls; - unsigned long *nb_ls_map; - - size_t n_nb_lr; - unsigned long *nb_lr_map; -}; - -struct ovn_lb_datapaths *ovn_lb_datapaths_create(const struct ovn_northd_lb *, - size_t n_ls_datapaths, - size_t n_lr_datapaths); -struct ovn_lb_datapaths *ovn_lb_datapaths_find(const struct hmap *, - const struct uuid *); -void ovn_lb_datapaths_destroy(struct ovn_lb_datapaths *); -void ovn_lb_datapaths_add_lr(struct ovn_lb_datapaths *, size_t n, - struct ovn_datapath **); -void ovn_lb_datapaths_add_ls(struct ovn_lb_datapaths *, size_t n, - struct ovn_datapath **); - -struct ovn_lb_group_datapaths { - struct hmap_node hmap_node; - - const struct ovn_lb_group *lb_group; - - /* Datapaths to which 'lb_group' is applied. */ - size_t n_ls; - struct ovn_datapath **ls; - size_t n_lr; - struct ovn_datapath **lr; -}; - -struct ovn_lb_group_datapaths *ovn_lb_group_datapaths_create( - const struct ovn_lb_group *, size_t max_ls_datapaths, - size_t max_lr_datapaths); - -void ovn_lb_group_datapaths_destroy(struct ovn_lb_group_datapaths *); -struct ovn_lb_group_datapaths *ovn_lb_group_datapaths_find( - const struct hmap *lb_group_dps, const struct uuid *); - -static inline void -ovn_lb_group_datapaths_add_ls(struct ovn_lb_group_datapaths *lbg_dps, size_t n, - struct ovn_datapath **ods) -{ - memcpy(&lbg_dps->ls[lbg_dps->n_ls], ods, n * sizeof *ods); - lbg_dps->n_ls += n; -} - -static inline void -ovn_lb_group_datapaths_add_lr(struct ovn_lb_group_datapaths *lbg_dps, - struct ovn_datapath *lr) -{ - lbg_dps->lr[lbg_dps->n_lr++] = lr; -} - -struct ovn_controller_lb { - struct hmap_node hmap_node; - - const struct sbrec_load_balancer *slb; /* May be NULL. */ - - uint8_t proto; - - struct ovn_lb_vip *vips; - size_t n_vips; - bool hairpin_orig_tuple; /* True if ovn-northd stores the original - * destination tuple in registers. - */ - bool ct_flush; /* True if we should flush CT after backend removal. */ - - struct lport_addresses hairpin_snat_ips; /* IP (v4 and/or v6) to be used - * as source for hairpinned - * traffic. - */ -}; - -struct ovn_controller_lb *ovn_controller_lb_create( - const struct sbrec_load_balancer *, - const struct smap *template_vars, - struct sset *template_vars_ref); -void ovn_controller_lb_destroy(struct ovn_controller_lb *); -void ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs); -struct ovn_controller_lb *ovn_controller_lb_find( - const struct hmap *ovn_controller_lbs, - const struct uuid *uuid); - char *ovn_lb_vip_init(struct ovn_lb_vip *lb_vip, const char *lb_key, const char *lb_value, bool template, int address_family); +char *ovn_lb_vip_init_explicit(struct ovn_lb_vip *lb_vip, const char *lb_key, + const char *lb_value); void ovn_lb_vip_destroy(struct ovn_lb_vip *vip); void ovn_lb_vip_format(const struct ovn_lb_vip *vip, struct ds *s, bool template); void ovn_lb_vip_backends_format(const struct ovn_lb_vip *vip, struct ds *s); +char *ovn_lb_vip6_template_format_internal(const struct ovn_lb_vip *vip); struct ovn_lb_5tuple { struct hmap_node hmap_node; diff --git a/northd/automake.mk b/northd/automake.mk index 7c6d56a4ff..19abb0dece 100644 --- a/northd/automake.mk +++ b/northd/automake.mk @@ -35,7 +35,9 @@ northd_ovn_northd_SOURCES = \ northd/ipam.c \ northd/ipam.h \ northd/lflow-mgr.c \ - northd/lflow-mgr.h + northd/lflow-mgr.h \ + northd/lb.c \ + northd/lb.h northd_ovn_northd_LDADD = \ lib/libovn.la \ $(OVSDB_LIBDIR)/libovsdb.la \ diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c index d06f46a54b..6ad3fbb35f 100644 --- a/northd/en-lb-data.c +++ b/northd/en-lb-data.c @@ -25,6 +25,7 @@ /* OVN includes */ #include "en-lb-data.h" +#include "lb.h" #include "lib/inc-proc-eng.h" #include "lib/lb.h" #include "lib/ovn-nb-idl.h" diff --git a/northd/en-lr-stateful.c b/northd/en-lr-stateful.c index 8665b3c791..3e2a6c254e 100644 --- a/northd/en-lr-stateful.c +++ b/northd/en-lr-stateful.c @@ -33,6 +33,7 @@ #include "en-lb-data.h" #include "en-lr-nat.h" #include "en-lr-stateful.h" +#include "lb.h" #include "lib/inc-proc-eng.h" #include "lib/lb.h" #include "lib/ovn-nb-idl.h" diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c index 53f687f220..9ca59d4338 100644 --- a/northd/en-sync-sb.c +++ b/northd/en-sync-sb.c @@ -24,6 +24,7 @@ #include "en-lr-nat.h" #include "en-lr-stateful.h" #include "en-sync-sb.h" +#include "lb.h" #include "lib/inc-proc-eng.h" #include "lib/lb.h" #include "lib/ovn-nb-idl.h" diff --git a/northd/lb.c b/northd/lb.c new file mode 100644 index 0000000000..e35569cb70 --- /dev/null +++ b/northd/lb.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2024, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* OVS includes */ +#include "lib/bitmap.h" +#include "openvswitch/vlog.h" +#include "socket-util.h" + +/* OVN includes */ +#include "lb.h" +#include "lib/ovn-nb-idl.h" +#include "northd.h" +#include "ovn/lex.h" + +VLOG_DEFINE_THIS_MODULE(northd_lb); + +static const char *lb_neighbor_responder_mode_names[] = { + [LB_NEIGH_RESPOND_REACHABLE] = "reachable", + [LB_NEIGH_RESPOND_ALL] = "all", + [LB_NEIGH_RESPOND_NONE] = "none", +}; + +static struct nbrec_load_balancer_health_check * +ovn_lb_get_health_check(const struct nbrec_load_balancer *nbrec_lb, + const char *vip_port_str, bool template); + +struct ovn_lb_ip_set * +ovn_lb_ip_set_create(void) +{ + struct ovn_lb_ip_set *lb_ip_set = xzalloc(sizeof *lb_ip_set); + + sset_init(&lb_ip_set->ips_v4); + sset_init(&lb_ip_set->ips_v4_routable); + sset_init(&lb_ip_set->ips_v4_reachable); + sset_init(&lb_ip_set->ips_v6); + sset_init(&lb_ip_set->ips_v6_routable); + sset_init(&lb_ip_set->ips_v6_reachable); + + return lb_ip_set; +} + +void +ovn_lb_ip_set_destroy(struct ovn_lb_ip_set *lb_ip_set) +{ + if (!lb_ip_set) { + return; + } + sset_destroy(&lb_ip_set->ips_v4); + sset_destroy(&lb_ip_set->ips_v4_routable); + sset_destroy(&lb_ip_set->ips_v4_reachable); + sset_destroy(&lb_ip_set->ips_v6); + sset_destroy(&lb_ip_set->ips_v6_routable); + sset_destroy(&lb_ip_set->ips_v6_reachable); + free(lb_ip_set); +} + +struct ovn_lb_ip_set * +ovn_lb_ip_set_clone(struct ovn_lb_ip_set *lb_ip_set) +{ + struct ovn_lb_ip_set *clone = ovn_lb_ip_set_create(); + + sset_clone(&clone->ips_v4, &lb_ip_set->ips_v4); + sset_clone(&clone->ips_v4_routable, &lb_ip_set->ips_v4_routable); + sset_clone(&clone->ips_v4_reachable, &lb_ip_set->ips_v4_reachable); + sset_clone(&clone->ips_v6, &lb_ip_set->ips_v6); + sset_clone(&clone->ips_v6_routable, &lb_ip_set->ips_v6_routable); + sset_clone(&clone->ips_v6_reachable, &lb_ip_set->ips_v6_reachable); + + return clone; +} + +static +void ovn_northd_lb_vip_init(struct ovn_northd_lb_vip *lb_vip_nb, + const struct ovn_lb_vip *lb_vip, + const struct nbrec_load_balancer *nbrec_lb, + const char *vip_port_str, const char *backend_ips, + bool template) +{ + lb_vip_nb->backend_ips = xstrdup(backend_ips); + lb_vip_nb->n_backends = lb_vip->n_backends; + lb_vip_nb->backends_nb = xcalloc(lb_vip_nb->n_backends, + sizeof *lb_vip_nb->backends_nb); + lb_vip_nb->lb_health_check = + ovn_lb_get_health_check(nbrec_lb, vip_port_str, template); +} + +static void +ovn_lb_vip_backends_health_check_init(const struct ovn_northd_lb *lb, + const struct ovn_lb_vip *lb_vip, + struct ovn_northd_lb_vip *lb_vip_nb) +{ + struct ds key = DS_EMPTY_INITIALIZER; + + for (size_t j = 0; j < lb_vip->n_backends; j++) { + struct ovn_lb_backend *backend = &lb_vip->backends[j]; + ds_clear(&key); + ds_put_format(&key, IN6_IS_ADDR_V4MAPPED(&lb_vip->vip) + ? "%s" : "[%s]", backend->ip_str); + + const char *s = smap_get(&lb->nlb->ip_port_mappings, ds_cstr(&key)); + if (!s) { + continue; + } + + char *svc_mon_src_ip = NULL; + char *port_name = xstrdup(s); + char *p = strstr(port_name, ":"); + if (p) { + *p = 0; + p++; + struct sockaddr_storage svc_mon_src_addr; + if (!inet_parse_address(p, &svc_mon_src_addr)) { + static struct vlog_rate_limit rl = + VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", p); + } else { + struct ds src_ip_s = DS_EMPTY_INITIALIZER; + ss_format_address_nobracks(&svc_mon_src_addr, + &src_ip_s); + svc_mon_src_ip = ds_steal_cstr(&src_ip_s); + } + } + + if (svc_mon_src_ip) { + struct ovn_northd_lb_backend *backend_nb = + &lb_vip_nb->backends_nb[j]; + backend_nb->health_check = true; + backend_nb->logical_port = xstrdup(port_name); + backend_nb->svc_mon_src_ip = svc_mon_src_ip; + } + free(port_name); + } + + ds_destroy(&key); +} + +static +void ovn_northd_lb_vip_destroy(struct ovn_northd_lb_vip *vip) +{ + free(vip->backend_ips); + for (size_t i = 0; i < vip->n_backends; i++) { + free(vip->backends_nb[i].logical_port); + free(vip->backends_nb[i].svc_mon_src_ip); + } + free(vip->backends_nb); +} + +static bool +ovn_lb_get_routable_mode(const struct nbrec_load_balancer *nbrec_lb, + bool routable, bool template) +{ + if (template && routable) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "Template load balancer "UUID_FMT" does not suport " + "option 'add_route'. Forcing it to disabled.", + UUID_ARGS(&nbrec_lb->header_.uuid)); + return false; + } + return routable; +} + +static bool +ovn_lb_neigh_mode_is_valid(enum lb_neighbor_responder_mode mode, bool template) +{ + if (!template) { + return true; + } + + switch (mode) { + case LB_NEIGH_RESPOND_REACHABLE: + return false; + case LB_NEIGH_RESPOND_ALL: + case LB_NEIGH_RESPOND_NONE: + return true; + } + return false; +} + +static enum lb_neighbor_responder_mode +ovn_lb_get_neigh_mode(const struct nbrec_load_balancer *nbrec_lb, + const char *mode, bool template) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + enum lb_neighbor_responder_mode default_mode = + template ? LB_NEIGH_RESPOND_NONE : LB_NEIGH_RESPOND_REACHABLE; + + if (!mode) { + mode = lb_neighbor_responder_mode_names[default_mode]; + } + + for (size_t i = 0; i < ARRAY_SIZE(lb_neighbor_responder_mode_names); i++) { + if (!strcmp(mode, lb_neighbor_responder_mode_names[i])) { + if (ovn_lb_neigh_mode_is_valid(i, template)) { + return i; + } + break; + } + } + + VLOG_WARN_RL(&rl, "Invalid neighbor responder mode %s for load balancer " + UUID_FMT", forcing it to %s", + mode, UUID_ARGS(&nbrec_lb->header_.uuid), + lb_neighbor_responder_mode_names[default_mode]); + return default_mode; +} + +static struct nbrec_load_balancer_health_check * +ovn_lb_get_health_check(const struct nbrec_load_balancer *nbrec_lb, + const char *vip_port_str, bool template) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + if (!nbrec_lb->n_health_check) { + return NULL; + } + + if (nbrec_lb->protocol && !strcmp(nbrec_lb->protocol, "sctp")) { + VLOG_WARN_RL(&rl, + "SCTP load balancers do not currently support " + "health checks. Not creating health checks for " + "load balancer " UUID_FMT, + UUID_ARGS(&nbrec_lb->header_.uuid)); + return NULL; + } + + if (template) { + VLOG_WARN_RL(&rl, + "Template load balancers do not currently support " + "health checks. Not creating health checks for " + "load balancer " UUID_FMT, + UUID_ARGS(&nbrec_lb->header_.uuid)); + return NULL; + } + + for (size_t i = 0; i < nbrec_lb->n_health_check; i++) { + if (!strcmp(nbrec_lb->health_check[i]->vip, vip_port_str)) { + return nbrec_lb->health_check[i]; + } + } + return NULL; +} + +static void +ovn_northd_lb_init(struct ovn_northd_lb *lb, + const struct nbrec_load_balancer *nbrec_lb) +{ + bool template = smap_get_bool(&nbrec_lb->options, "template", false); + bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp"); + bool is_sctp = nullable_string_is_equal(nbrec_lb->protocol, "sctp"); + int address_family = !strcmp(smap_get_def(&nbrec_lb->options, + "address-family", "ipv4"), + "ipv4") + ? AF_INET + : AF_INET6; + + lb->nlb = nbrec_lb; + lb->proto = is_udp ? "udp" : is_sctp ? "sctp" : "tcp"; + lb->n_vips = smap_count(&nbrec_lb->vips); + lb->vips = xcalloc(lb->n_vips, sizeof *lb->vips); + lb->vips_nb = xcalloc(lb->n_vips, sizeof *lb->vips_nb); + smap_init(&lb->template_vips); + lb->controller_event = smap_get_bool(&nbrec_lb->options, "event", false); + + bool routable = smap_get_bool(&nbrec_lb->options, "add_route", false); + lb->routable = ovn_lb_get_routable_mode(nbrec_lb, routable, template); + + lb->skip_snat = smap_get_bool(&nbrec_lb->options, "skip_snat", false); + lb->template = template; + + const char *mode = smap_get(&nbrec_lb->options, "neighbor_responder"); + lb->neigh_mode = ovn_lb_get_neigh_mode(nbrec_lb, mode, template); + + uint32_t affinity_timeout = + smap_get_uint(&nbrec_lb->options, "affinity_timeout", 0); + if (affinity_timeout > UINT16_MAX) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "max affinity_timeout timeout value is %u", + UINT16_MAX); + affinity_timeout = UINT16_MAX; + } + lb->affinity_timeout = affinity_timeout; + + sset_init(&lb->ips_v4); + sset_init(&lb->ips_v6); + struct smap_node *node; + size_t n_vips = 0; + + SMAP_FOR_EACH (node, &nbrec_lb->vips) { + struct ovn_lb_vip *lb_vip = &lb->vips[n_vips]; + struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[n_vips]; + + char *error = ovn_lb_vip_init(lb_vip, node->key, node->value, + template, address_family); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Failed to initialize LB VIP: %s", error); + ovn_lb_vip_destroy(lb_vip); + free(error); + continue; + } + lb_vip->empty_backend_rej = smap_get_bool(&nbrec_lb->options, + "reject", false); + ovn_northd_lb_vip_init(lb_vip_nb, lb_vip, nbrec_lb, + node->key, node->value, template); + if (lb_vip_nb->lb_health_check) { + lb->health_checks = true; + } + + if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) { + sset_add(&lb->ips_v4, lb_vip->vip_str); + } else { + sset_add(&lb->ips_v6, lb_vip->vip_str); + } + + if (lb->template && address_family == AF_INET6) { + smap_add_nocopy(&lb->template_vips, + ovn_lb_vip6_template_format_internal(lb_vip), + xstrdup(node->value)); + } + n_vips++; + + if (lb_vip_nb->lb_health_check) { + ovn_lb_vip_backends_health_check_init(lb, lb_vip, lb_vip_nb); + } + } + + /* It's possible that parsing VIPs fails. Update the lb->n_vips to the + * correct value. + */ + lb->n_vips = n_vips; + + if (nbrec_lb->n_selection_fields) { + char *proto = NULL; + if (nbrec_lb->protocol && nbrec_lb->protocol[0]) { + proto = nbrec_lb->protocol; + } + + struct ds sel_fields = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < lb->nlb->n_selection_fields; i++) { + char *field = lb->nlb->selection_fields[i]; + if (!strcmp(field, "tp_src") && proto) { + ds_put_format(&sel_fields, "%s_src,", proto); + } else if (!strcmp(field, "tp_dst") && proto) { + ds_put_format(&sel_fields, "%s_dst,", proto); + } else { + ds_put_format(&sel_fields, "%s,", field); + } + } + ds_chomp(&sel_fields, ','); + lb->selection_fields = ds_steal_cstr(&sel_fields); + } +} + +struct ovn_northd_lb * +ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb) +{ + struct ovn_northd_lb *lb = xzalloc(sizeof *lb); + ovn_northd_lb_init(lb, nbrec_lb); + return lb; +} + +struct ovn_northd_lb * +ovn_northd_lb_find(const struct hmap *lbs, const struct uuid *uuid) +{ + struct ovn_northd_lb *lb; + size_t hash = uuid_hash(uuid); + HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, lbs) { + if (uuid_equals(&lb->nlb->header_.uuid, uuid)) { + return lb; + } + } + return NULL; +} + +const struct smap * +ovn_northd_lb_get_vips(const struct ovn_northd_lb *lb) +{ + if (!smap_is_empty(&lb->template_vips)) { + return &lb->template_vips; + } + return &lb->nlb->vips; +} + +static void +ovn_northd_lb_cleanup(struct ovn_northd_lb *lb) +{ + for (size_t i = 0; i < lb->n_vips; i++) { + ovn_lb_vip_destroy(&lb->vips[i]); + ovn_northd_lb_vip_destroy(&lb->vips_nb[i]); + } + free(lb->vips); + free(lb->vips_nb); + lb->vips = NULL; + lb->vips_nb = NULL; + smap_destroy(&lb->template_vips); + sset_destroy(&lb->ips_v4); + sset_destroy(&lb->ips_v6); + free(lb->selection_fields); + lb->selection_fields = NULL; + lb->health_checks = false; +} + +void +ovn_northd_lb_destroy(struct ovn_northd_lb *lb) +{ + ovn_northd_lb_cleanup(lb); + free(lb); +} + +void +ovn_northd_lb_reinit(struct ovn_northd_lb *lb, + const struct nbrec_load_balancer *nbrec_lb) +{ + ovn_northd_lb_cleanup(lb); + ovn_northd_lb_init(lb, nbrec_lb); +} + +static void +ovn_lb_group_init(struct ovn_lb_group *lb_group, + const struct nbrec_load_balancer_group *nbrec_lb_group, + const struct hmap *lbs) +{ + lb_group->n_lbs = nbrec_lb_group->n_load_balancer; + lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs); + lb_group->lb_ips = ovn_lb_ip_set_create(); + + for (size_t i = 0; i < nbrec_lb_group->n_load_balancer; i++) { + const struct uuid *lb_uuid = + &nbrec_lb_group->load_balancer[i]->header_.uuid; + lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid); + lb_group->has_routable_lb |= lb_group->lbs[i]->routable; + } +} + +/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group record + * and an array of 'struct ovn_northd_lb' objects for its associated + * load balancers. */ +struct ovn_lb_group * +ovn_lb_group_create(const struct nbrec_load_balancer_group *nbrec_lb_group, + const struct hmap *lbs) +{ + struct ovn_lb_group *lb_group = xzalloc(sizeof *lb_group); + lb_group->uuid = nbrec_lb_group->header_.uuid; + ovn_lb_group_init(lb_group, nbrec_lb_group, lbs); + return lb_group; +} + +static void +ovn_lb_group_cleanup(struct ovn_lb_group *lb_group) +{ + ovn_lb_ip_set_destroy(lb_group->lb_ips); + lb_group->lb_ips = NULL; + lb_group->has_routable_lb = false; + free(lb_group->lbs); +} + +void +ovn_lb_group_destroy(struct ovn_lb_group *lb_group) +{ + if (!lb_group) { + return; + } + + ovn_lb_group_cleanup(lb_group); + free(lb_group); +} + +void +ovn_lb_group_reinit(struct ovn_lb_group *lb_group, + const struct nbrec_load_balancer_group *nbrec_lb_group, + const struct hmap *lbs) +{ + ovn_lb_group_cleanup(lb_group); + ovn_lb_group_init(lb_group, nbrec_lb_group, lbs); +} + +struct ovn_lb_group * +ovn_lb_group_find(const struct hmap *lb_groups, const struct uuid *uuid) +{ + struct ovn_lb_group *lb_group; + size_t hash = uuid_hash(uuid); + + HMAP_FOR_EACH_WITH_HASH (lb_group, hmap_node, hash, lb_groups) { + if (uuid_equals(&lb_group->uuid, uuid)) { + return lb_group; + } + } + return NULL; +} + +void +build_lrouter_lb_ips(struct ovn_lb_ip_set *lb_ips, + const struct ovn_northd_lb *lb) +{ + add_ips_to_lb_ip_set(lb_ips, lb->routable, &lb->ips_v4, &lb->ips_v6); +} + +void +add_ips_to_lb_ip_set(struct ovn_lb_ip_set *lb_ips, + bool is_routable, + const struct sset *lb_ips_v4, + const struct sset *lb_ips_v6) +{ + const char *ip_address; + + SSET_FOR_EACH (ip_address, lb_ips_v4) { + sset_add(&lb_ips->ips_v4, ip_address); + if (is_routable) { + sset_add(&lb_ips->ips_v4_routable, ip_address); + } + } + SSET_FOR_EACH (ip_address, lb_ips_v6) { + sset_add(&lb_ips->ips_v6, ip_address); + if (is_routable) { + sset_add(&lb_ips->ips_v6_routable, ip_address); + } + } +} + +void +remove_ips_from_lb_ip_set(struct ovn_lb_ip_set *lb_ips, + bool is_routable, + const struct sset *lb_ips_v4, + const struct sset *lb_ips_v6) +{ + const char *ip_address; + + SSET_FOR_EACH (ip_address, lb_ips_v4) { + sset_find_and_delete(&lb_ips->ips_v4, ip_address); + if (is_routable) { + sset_find_and_delete(&lb_ips->ips_v4_routable, ip_address); + } + } + SSET_FOR_EACH (ip_address, lb_ips_v6) { + sset_find_and_delete(&lb_ips->ips_v6, ip_address); + if (is_routable) { + sset_find_and_delete(&lb_ips->ips_v6_routable, ip_address); + } + } +} + +/* lb datapaths functions */ +struct ovn_lb_datapaths * +ovn_lb_datapaths_create(const struct ovn_northd_lb *lb, size_t n_ls_datapaths, + size_t n_lr_datapaths) +{ + struct ovn_lb_datapaths *lb_dps = xzalloc(sizeof *lb_dps); + lb_dps->lb = lb; + lb_dps->nb_ls_map = bitmap_allocate(n_ls_datapaths); + lb_dps->nb_lr_map = bitmap_allocate(n_lr_datapaths); + + return lb_dps; +} + +void +ovn_lb_datapaths_destroy(struct ovn_lb_datapaths *lb_dps) +{ + bitmap_free(lb_dps->nb_lr_map); + bitmap_free(lb_dps->nb_ls_map); + free(lb_dps); +} + +void +ovn_lb_datapaths_add_lr(struct ovn_lb_datapaths *lb_dps, size_t n, + struct ovn_datapath **ods) +{ + for (size_t i = 0; i < n; i++) { + if (!bitmap_is_set(lb_dps->nb_lr_map, ods[i]->index)) { + bitmap_set1(lb_dps->nb_lr_map, ods[i]->index); + lb_dps->n_nb_lr++; + } + } +} + +void +ovn_lb_datapaths_add_ls(struct ovn_lb_datapaths *lb_dps, size_t n, + struct ovn_datapath **ods) +{ + for (size_t i = 0; i < n; i++) { + if (!bitmap_is_set(lb_dps->nb_ls_map, ods[i]->index)) { + bitmap_set1(lb_dps->nb_ls_map, ods[i]->index); + lb_dps->n_nb_ls++; + } + } +} + +struct ovn_lb_datapaths * +ovn_lb_datapaths_find(const struct hmap *lb_dps_map, + const struct uuid *lb_uuid) +{ + struct ovn_lb_datapaths *lb_dps; + size_t hash = uuid_hash(lb_uuid); + HMAP_FOR_EACH_WITH_HASH (lb_dps, hmap_node, hash, lb_dps_map) { + if (uuid_equals(&lb_dps->lb->nlb->header_.uuid, lb_uuid)) { + return lb_dps; + } + } + return NULL; +} + +struct ovn_lb_group_datapaths * +ovn_lb_group_datapaths_create(const struct ovn_lb_group *lb_group, + size_t max_ls_datapaths, + size_t max_lr_datapaths) +{ + struct ovn_lb_group_datapaths *lb_group_dps = + xzalloc(sizeof *lb_group_dps); + lb_group_dps->lb_group = lb_group; + lb_group_dps->ls = xmalloc(max_ls_datapaths * sizeof *lb_group_dps->ls); + lb_group_dps->lr = xmalloc(max_lr_datapaths * sizeof *lb_group_dps->lr); + + return lb_group_dps; +} + +void +ovn_lb_group_datapaths_destroy(struct ovn_lb_group_datapaths *lb_group_dps) +{ + free(lb_group_dps->ls); + free(lb_group_dps->lr); + free(lb_group_dps); +} + +struct ovn_lb_group_datapaths * +ovn_lb_group_datapaths_find(const struct hmap *lb_group_dps_map, + const struct uuid *lb_group_uuid) +{ + struct ovn_lb_group_datapaths *lb_group_dps; + size_t hash = uuid_hash(lb_group_uuid); + + HMAP_FOR_EACH_WITH_HASH (lb_group_dps, hmap_node, hash, lb_group_dps_map) { + if (uuid_equals(&lb_group_dps->lb_group->uuid, lb_group_uuid)) { + return lb_group_dps; + } + } + return NULL; +} diff --git a/northd/lb.h b/northd/lb.h new file mode 100644 index 0000000000..00f81c3533 --- /dev/null +++ b/northd/lb.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_NORTHD_LB_H +#define OVN_NORTHD_LB_H 1 + +#include "openvswitch/hmap.h" +#include "uuid.h" + +#include "lib/lb.h" + +struct nbrec_load_balancer; +struct nbrec_load_balancer_group; +struct ovn_datapath; + +enum lb_neighbor_responder_mode { + LB_NEIGH_RESPOND_REACHABLE, + LB_NEIGH_RESPOND_ALL, + LB_NEIGH_RESPOND_NONE, +}; + +/* The "routable" ssets are subsets of the load balancer IPs for which IP + * routes and ARP resolution flows are automatically added. */ +struct ovn_lb_ip_set { + struct sset ips_v4; + struct sset ips_v4_routable; + struct sset ips_v4_reachable; + struct sset ips_v6; + struct sset ips_v6_routable; + struct sset ips_v6_reachable; +}; + +struct ovn_lb_ip_set *ovn_lb_ip_set_create(void); +void ovn_lb_ip_set_destroy(struct ovn_lb_ip_set *); +struct ovn_lb_ip_set *ovn_lb_ip_set_clone(struct ovn_lb_ip_set *); + +struct ovn_northd_lb { + struct hmap_node hmap_node; + + const struct nbrec_load_balancer *nlb; /* May be NULL. */ + const char *proto; + char *selection_fields; + struct ovn_lb_vip *vips; + struct ovn_northd_lb_vip *vips_nb; + struct smap template_vips; /* Slightly changed template VIPs, populated + * if needed. Until now it's only required + * for IPv6 template load balancers. */ + size_t n_vips; + + enum lb_neighbor_responder_mode neigh_mode; + bool controller_event; + bool routable; + bool skip_snat; + bool template; + uint16_t affinity_timeout; + + struct sset ips_v4; + struct sset ips_v6; + + /* Indicates if the load balancer has health checks configured. */ + bool health_checks; +}; + +/* ovn-northd specific backend information. */ +struct ovn_northd_lb_vip { + char *backend_ips; + struct ovn_northd_lb_backend *backends_nb; + size_t n_backends; + + struct nbrec_load_balancer_health_check *lb_health_check; +}; + +struct ovn_northd_lb_backend { + bool health_check; + char *logical_port; /* Logical port to which the ip belong to. */ + char *svc_mon_src_ip; /* Source IP to use for monitoring. */ +}; + +struct ovn_northd_lb *ovn_northd_lb_create(const struct nbrec_load_balancer *); +struct ovn_northd_lb *ovn_northd_lb_find(const struct hmap *, + const struct uuid *); +const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb *); +void ovn_northd_lb_destroy(struct ovn_northd_lb *); +void ovn_northd_lb_reinit(struct ovn_northd_lb *, + const struct nbrec_load_balancer *); + +void build_lrouter_lb_ips(struct ovn_lb_ip_set *, + const struct ovn_northd_lb *); +void add_ips_to_lb_ip_set(struct ovn_lb_ip_set *lb_ips, + bool is_routable, + const struct sset *lb_ips_v4, + const struct sset *lb_ips_v6); +void remove_ips_from_lb_ip_set(struct ovn_lb_ip_set *lb_ips, + bool is_routable, + const struct sset *lb_ips_v4, + const struct sset *lb_ips_v6); + +struct ovn_lb_group { + struct hmap_node hmap_node; + struct uuid uuid; + size_t n_lbs; + struct ovn_northd_lb **lbs; + struct ovn_lb_ip_set *lb_ips; + bool has_routable_lb; +}; + +struct ovn_lb_group *ovn_lb_group_create( + const struct nbrec_load_balancer_group *, + const struct hmap *lbs); +void ovn_lb_group_destroy(struct ovn_lb_group *lb_group); +struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups, + const struct uuid *); +void ovn_lb_group_reinit( + struct ovn_lb_group *lb_group, + const struct nbrec_load_balancer_group *, + const struct hmap *lbs); + +struct ovn_lb_datapaths { + struct hmap_node hmap_node; + + const struct ovn_northd_lb *lb; + size_t n_nb_ls; + unsigned long *nb_ls_map; + + size_t n_nb_lr; + unsigned long *nb_lr_map; +}; + +struct ovn_lb_datapaths *ovn_lb_datapaths_create(const struct ovn_northd_lb *, + size_t n_ls_datapaths, + size_t n_lr_datapaths); +struct ovn_lb_datapaths *ovn_lb_datapaths_find(const struct hmap *, + const struct uuid *); +void ovn_lb_datapaths_destroy(struct ovn_lb_datapaths *); + +void ovn_lb_datapaths_add_lr(struct ovn_lb_datapaths *, size_t n, + struct ovn_datapath **); +void ovn_lb_datapaths_add_ls(struct ovn_lb_datapaths *, size_t n, + struct ovn_datapath **); + +struct ovn_lb_group_datapaths { + struct hmap_node hmap_node; + + const struct ovn_lb_group *lb_group; + + /* Datapaths to which 'lb_group' is applied. */ + size_t n_ls; + struct ovn_datapath **ls; + size_t n_lr; + struct ovn_datapath **lr; +}; + +struct ovn_lb_group_datapaths *ovn_lb_group_datapaths_create( + const struct ovn_lb_group *, size_t max_ls_datapaths, + size_t max_lr_datapaths); + +void ovn_lb_group_datapaths_destroy(struct ovn_lb_group_datapaths *); +struct ovn_lb_group_datapaths *ovn_lb_group_datapaths_find( + const struct hmap *lb_group_dps, const struct uuid *); + +static inline void +ovn_lb_group_datapaths_add_ls(struct ovn_lb_group_datapaths *lbg_dps, size_t n, + struct ovn_datapath **ods) +{ + memcpy(&lbg_dps->ls[lbg_dps->n_ls], ods, n * sizeof *ods); + lbg_dps->n_ls += n; +} + +static inline void +ovn_lb_group_datapaths_add_lr(struct ovn_lb_group_datapaths *lbg_dps, + struct ovn_datapath *lr) +{ + lbg_dps->lr[lbg_dps->n_lr++] = lr; +} + +#endif /* OVN_NORTHD_LB_H */ diff --git a/northd/northd.c b/northd/northd.c index cb53ec9716..00899e3d1a 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -31,6 +31,7 @@ #include "openvswitch/hmap.h" #include "openvswitch/json.h" #include "ovn/lex.h" +#include "lb.h" #include "lib/chassis-index.h" #include "lib/ip-mcast-index.h" #include "lib/static-mac-binding-index.h"