This new engine now maintains the route related data for ovn-ic daemon which was earlier maintained by the ic engine node invoked the route_run() function. The inputs to this engine node are: en_nb_nb_global; en_nb_logical_switch; en_nb_logical_router; en_icnb_transit_switch; en_icsb_port_binding; en_icsb_route; en_nb_logical_router_static_route; In order to achieve this, we refactor in the following way: * Introduce route_init() which initializes this data. * Introduce route_destroy() which clears this data for a new iteration. * Introduce route_run() which invokes the full recompute of the engine.
This engine node becomes an input to 'ic' node. Signed-off-by: Paulo Guilherme Silva <[email protected]> --- ic/automake.mk | 2 + ic/en-ic.c | 18 - ic/en-route.c | 1554 +++++++++++++++++++++++++++++++++++++++++ ic/en-route.h | 70 ++ ic/inc-proc-ic.c | 15 +- ic/ovn-ic.c | 1300 +--------------------------------- ic/ovn-ic.h | 8 +- lib/stopwatch-names.h | 1 + 8 files changed, 1646 insertions(+), 1322 deletions(-) create mode 100644 ic/en-route.c create mode 100644 ic/en-route.h diff --git a/ic/automake.mk b/ic/automake.mk index 91783df0f..24807a360 100644 --- a/ic/automake.mk +++ b/ic/automake.mk @@ -8,6 +8,8 @@ ic_ovn_ic_SOURCES = ic/ovn-ic.c \ ic/en-enum-datapaths.h \ ic/en-port-binding.c \ ic/en-port-binding.h \ + ic/en-route.c \ + ic/en-route.h \ ic/inc-proc-ic.c \ ic/inc-proc-ic.h ic_ovn_ic_LDADD = \ diff --git a/ic/en-ic.c b/ic/en-ic.c index e7c7ab71b..e0956bdee 100644 --- a/ic/en-ic.c +++ b/ic/en-ic.c @@ -44,8 +44,6 @@ ic_get_input_data(struct engine_node *node, struct ic_input *input_data) { /* Table references */ - input_data->nbrec_nb_global_table = - EN_OVSDB_GET(engine_get_input("NB_nb_global", node)); input_data->nbrec_logical_switch_table = EN_OVSDB_GET(engine_get_input("NB_logical_switch", node)); input_data->nbrec_logical_router_table = @@ -112,22 +110,6 @@ ic_get_input_data(struct engine_node *node, engine_ovsdb_node_get_index( engine_get_input("ICNB_transit_switch", node), "icnbrec_transit_switch_by_name"); - input_data->icsbrec_port_binding_by_az = - engine_ovsdb_node_get_index( - engine_get_input("ICSB_port_binding", node), - "icsbrec_port_binding_by_az"); - input_data->icsbrec_route_by_az = - engine_ovsdb_node_get_index( - engine_get_input("ICSB_route", node), - "icsbrec_route_by_az"); - input_data->icsbrec_route_by_ts = - engine_ovsdb_node_get_index( - engine_get_input("ICSB_route", node), - "icsbrec_route_by_ts"); - input_data->icsbrec_route_by_ts_az = - engine_ovsdb_node_get_index( - engine_get_input("ICSB_route", node), - "icsbrec_route_by_ts_az"); input_data->icsbrec_service_monitor_by_source_az = engine_ovsdb_node_get_index( engine_get_input("ICSB_service_monitor", node), diff --git a/ic/en-route.c b/ic/en-route.c new file mode 100644 index 000000000..f46e303e3 --- /dev/null +++ b/ic/en-route.c @@ -0,0 +1,1554 @@ +/* + * 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 <config.h> + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include "vec.h" + +/* OVS includes. */ +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "ovn-ic.h" +#include "en-route.h" +#include "inc-proc-ic.h" +#include "lib/inc-proc-eng.h" +#include "lib/ovn-nb-idl.h" +#include "lib/ovn-ic-nb-idl.h" +#include "lib/ovn-ic-sb-idl.h" +#include "lib/ovn-util.h" +#include "lib/stopwatch-names.h" +#include "coverage.h" +#include "stopwatch.h" +#include "stopwatch-names.h" + +VLOG_DEFINE_THIS_MODULE(en_route); +COVERAGE_DEFINE(route_run); + +static void +route_run(const struct engine_context *eng_ctx, + struct route_input *route_input, + struct ed_type_route *route_data, + const struct nbrec_logical_router_table *nb_lr_table, + const struct nbrec_nb_global_table *nb_global_table); +static void route_init(struct ed_type_route *data); +static void route_destroy(struct ed_type_route *data); +static void route_clear(struct ed_type_route *data); +static void route_get_input_data(struct engine_node *node, + struct route_input *input_data); + +static uint32_t + ic_route_hash(const struct in6_addr *prefix, unsigned int plen, + const struct in6_addr *nexthop, const char *origin, + const char *route_table); +static struct ic_route_info * + ic_route_find(struct hmap *routes, const struct in6_addr *prefix, + unsigned int plen, const struct in6_addr *nexthop, + const char *origin, const char *route_table, uint32_t hash); +static struct ic_router_info * + ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr); +static bool + parse_route(const char *s_prefix, const char *s_nexthop, + struct in6_addr *prefix, unsigned int *plen, + struct in6_addr *nexthop); +static bool + add_to_routes_learned(struct hmap *routes_learned, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr); +static bool + get_nexthop_from_lport_addresses(bool is_v4, + const struct lport_addresses *laddr, + struct in6_addr *nexthop); +static bool + prefix_is_filtered(struct in6_addr *prefix, + unsigned int plen, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp, + bool is_advertisement); +static bool + prefix_is_deny_filtered(struct in6_addr *prefix, + unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp, + bool is_advertisement); +static bool + route_need_advertise(const char *policy, + struct in6_addr *prefix, + unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp); +static void + add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix, + unsigned int plen, const struct in6_addr nexthop, + const char *origin, const char *route_table, + const struct nbrec_logical_router_port *nb_lrp, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_load_balancer *nb_lb, + const char *route_tag); +static void + add_static_to_routes_ad(struct hmap *routes_ad, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp); +static void + add_network_to_routes_ad(struct hmap *routes_ad, const char *network, + const struct nbrec_logical_router_port *nb_lrp, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp); +static void + add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key, + const struct nbrec_load_balancer *nb_lb, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp); +static bool + route_has_local_gw(const struct nbrec_logical_router *lr, + const char *route_table, const char *ip_prefix); +static bool + lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp, + struct in6_addr *nexthop); +static bool + route_matches_local_lb(const struct nbrec_load_balancer *nb_lb, + const char *ip_prefix); +static bool + route_need_learn(const struct nbrec_logical_router *lr, + const struct icsbrec_route *isb_route, + struct in6_addr *prefix, unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router_port *ts_lrp, + struct in6_addr *nexthop); +static const char * + get_lrp_name_by_ts_port_name(struct route_input *ic, + const char *ts_port_name); +static const struct nbrec_logical_router_port * + find_lrp_of_nexthop(struct route_input *ic, + const struct icsbrec_route *isb_route); +static bool + lrp_is_ts_port(struct route_input *ic, struct ic_router_info *ic_lr, + const char *lrp_name); +static void + sync_learned_routes(const struct engine_context *ctx, + struct route_input *ic, struct ic_router_info *ic_lr, + const struct nbrec_nb_global_table *nb_global_table); +static void + ad_route_sync_external_ids(const struct ic_route_info *route_adv, + const struct icsbrec_route *isb_route); +static void + advertise_routes(const struct engine_context *ctx, + struct route_input *ic, + const struct icsbrec_availability_zone *az, + const char *ts_name, struct hmap *routes_ad); +static void + build_ts_routes_to_adv(struct route_input *ic, + struct ic_router_info *ic_lr, + struct hmap *routes_ad, + struct lport_addresses *ts_port_addrs, + const struct nbrec_nb_global *nb_global, + const char *ts_route_table, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp); +static void + collect_lr_routes(struct route_input *ic, + struct ic_router_info *ic_lr, + struct shash *routes_ad_by_ts, + const struct nbrec_nb_global_table *nb_global_table); +static void + delete_orphan_ic_routes(struct route_input *ic, + const struct icsbrec_availability_zone *az); + +static void +route_get_input_data(struct engine_node *node, + struct route_input *input_data) +{ + /* Indexes */ + input_data->nbrec_ls_by_name = + engine_ovsdb_node_get_index( + engine_get_input("NB_logical_switch", node), + "nbrec_ls_by_name"); + input_data->nbrec_port_by_name = + engine_ovsdb_node_get_index( + engine_get_input("NB_logical_switch", node), + "nbrec_port_by_name"); + input_data->nbrec_lrp_by_name = + engine_ovsdb_node_get_index( + engine_get_input("NB_logical_router", node), + "nbrec_lrp_by_name"); + input_data->icnbrec_transit_switch_by_name = + engine_ovsdb_node_get_index( + engine_get_input("ICNB_transit_switch", node), + "icnbrec_transit_switch_by_name"); + input_data->icsbrec_port_binding_by_az = + engine_ovsdb_node_get_index( + engine_get_input("ICSB_port_binding", node), + "icsbrec_port_binding_by_az"); + input_data->icsbrec_route_by_az = + engine_ovsdb_node_get_index( + engine_get_input("ICSB_route", node), + "icsbrec_route_by_az"); + input_data->icsbrec_route_by_ts = + engine_ovsdb_node_get_index( + engine_get_input("ICSB_route", node), + "icsbrec_route_by_ts"); + input_data->icsbrec_route_by_ts_az = + engine_ovsdb_node_get_index( + engine_get_input("ICSB_route", node), + "icsbrec_route_by_ts_az"); +} + +enum engine_node_state +en_route_run(struct engine_node *node, void *data) +{ + const struct engine_context *eng_ctx = engine_get_context(); + struct ed_type_route *route_data = data; + struct route_input route_input; + + route_clear(route_data); + + const struct nbrec_logical_router_table *nb_lr_table = + EN_OVSDB_GET(engine_get_input("NB_logical_router", node)); + const struct nbrec_nb_global_table *nb_global_table = + EN_OVSDB_GET(engine_get_input("NB_nb_global", node)); + + route_get_input_data(node, &route_input); + route_input.runned_az = eng_ctx->client_ctx; + + COVERAGE_INC(route_run); + stopwatch_start(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, time_usec()); + route_run(eng_ctx, &route_input, route_data, nb_lr_table, nb_global_table); + stopwatch_stop(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, time_usec()); + + return EN_UPDATED; +} + +void * +en_route_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + struct ed_type_route *data = xzalloc(sizeof *data); + route_init(data); + return data; +} + +void +en_route_cleanup(void *data) +{ + route_destroy(data); +} + +static void +route_init(struct ed_type_route *data) +{ + hmap_init(&data->pb_tnlids); + shash_init(&data->switch_all_local_pbs); + shash_init(&data->router_all_local_pbs); +} + +static void +route_destroy(struct ed_type_route *data) +{ + route_clear(data); + ovn_destroy_tnlids(&data->pb_tnlids); + + shash_destroy(&data->switch_all_local_pbs); + shash_destroy(&data->router_all_local_pbs); +} + +static void +route_clear(struct ed_type_route *data) +{ + ovn_destroy_tnlids(&data->pb_tnlids); + hmap_init(&data->pb_tnlids); + + shash_clear(&data->switch_all_local_pbs); + shash_clear(&data->router_all_local_pbs); +} + +static void +route_run(const struct engine_context *eng_ctx, + struct route_input *route_input, + struct ed_type_route *route_data OVS_UNUSED, + const struct nbrec_logical_router_table *nb_lr_table, + const struct nbrec_nb_global_table *nb_global_table) +{ + if (!eng_ctx->ovnisb_idl_txn || !eng_ctx->ovnnb_idl_txn) { + return; + } + + delete_orphan_ic_routes(route_input, route_input->runned_az); + + struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs); + const struct icsbrec_port_binding *isb_pb; + const struct icsbrec_port_binding *isb_pb_key = + icsbrec_port_binding_index_init_row( + route_input->icsbrec_port_binding_by_az); + icsbrec_port_binding_index_set_availability_zone(isb_pb_key, + route_input->runned_az); + + /* Each port on TS maps to a logical router, which is stored in the + * external_ids:router-id of the IC SB port_binding record. + * Here we build info for interconnected Logical Router: + * collect IC Port Binding to process routes sync later on. */ + ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, + route_input->icsbrec_port_binding_by_az) + { + if (ic_pb_get_type(isb_pb) == IC_ROUTER_PORT) { + continue; + } + const struct nbrec_logical_switch_port *nb_lsp; + + nb_lsp = get_lsp_by_ts_port_name(route_input->nbrec_port_by_name, + isb_pb->logical_port); + if (!strcmp(nb_lsp->type, "switch")) { + VLOG_DBG("IC-SB Port_Binding '%s' on ts '%s' corresponds to a " + "switch port, not considering for route collection.", + isb_pb->logical_port, isb_pb->transit_switch); + continue; + } + + const char *ts_lrp_name = + get_lrp_name_by_ts_port_name(route_input, isb_pb->logical_port); + if (!ts_lrp_name) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Route sync ignores port %s on ts %s because " + "logical router port is not found in NB. Deleting it", + isb_pb->logical_port, isb_pb->transit_switch); + icsbrec_port_binding_delete(isb_pb); + continue; + } + + struct uuid lr_uuid; + if (!smap_get_uuid(&isb_pb->external_ids, "router-id", &lr_uuid)) { + VLOG_DBG("IC-SB Port_Binding %s doesn't have " + "external_ids:router-id set.", isb_pb->logical_port); + continue; + } + + const struct nbrec_logical_router *lr + = nbrec_logical_router_table_get_for_uuid(nb_lr_table, &lr_uuid); + if (!lr) { + continue; + } + + struct ic_router_info *ic_lr = ic_router_find(&ic_lrs, lr); + if (!ic_lr) { + ic_lr = xzalloc(sizeof *ic_lr); + ic_lr->lr = lr; + ic_lr->isb_pbs = + VECTOR_EMPTY_INITIALIZER(const struct icsbrec_port_binding *); + hmap_init(&ic_lr->routes_learned); + hmap_insert(&ic_lrs, &ic_lr->node, uuid_hash(&lr->header_.uuid)); + } + vector_push(&ic_lr->isb_pbs, &isb_pb); + } + icsbrec_port_binding_index_destroy_row(isb_pb_key); + + struct ic_router_info *ic_lr; + struct shash routes_ad_by_ts = SHASH_INITIALIZER(&routes_ad_by_ts); + HMAP_FOR_EACH_SAFE (ic_lr, node, &ic_lrs) { + collect_lr_routes(route_input, ic_lr, &routes_ad_by_ts, + nb_global_table); + sync_learned_routes(eng_ctx, route_input, ic_lr, nb_global_table); + vector_destroy(&ic_lr->isb_pbs); + hmap_destroy(&ic_lr->routes_learned); + hmap_remove(&ic_lrs, &ic_lr->node); + free(ic_lr); + } + struct shash_node *node; + SHASH_FOR_EACH (node, &routes_ad_by_ts) { + advertise_routes(eng_ctx, route_input, route_input->runned_az, + node->name, node->data); + hmap_destroy(node->data); + } + shash_destroy_free_data(&routes_ad_by_ts); + hmap_destroy(&ic_lrs); +} + +static uint32_t +ic_route_hash(const struct in6_addr *prefix, unsigned int plen, + const struct in6_addr *nexthop, const char *origin, + const char *route_table) +{ + uint32_t basis = hash_bytes(prefix, sizeof *prefix, (uint32_t) plen); + basis = hash_string(origin, basis); + basis = hash_string(route_table, basis); + return hash_bytes(nexthop, sizeof *nexthop, basis); +} + +static struct ic_route_info * +ic_route_find(struct hmap *routes, const struct in6_addr *prefix, + unsigned int plen, const struct in6_addr *nexthop, + const char *origin, const char *route_table, uint32_t hash) +{ + struct ic_route_info *r; + if (!hash) { + hash = ic_route_hash(prefix, plen, nexthop, origin, route_table); + } + HMAP_FOR_EACH_WITH_HASH (r, node, hash, routes) { + if (ipv6_addr_equals(&r->prefix, prefix) && + r->plen == plen && + ipv6_addr_equals(&r->nexthop, nexthop) && + !strcmp(r->origin, origin) && + !strcmp(r->route_table ? r->route_table : "", route_table)) { + return r; + } + } + return NULL; +} + +static struct ic_router_info * +ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr) +{ + struct ic_router_info *ic_lr; + HMAP_FOR_EACH_WITH_HASH (ic_lr, node, uuid_hash(&lr->header_.uuid), + ic_lrs) { + if (ic_lr->lr == lr) { + return ic_lr; + } + } + return NULL; +} + +static bool +parse_route(const char *s_prefix, const char *s_nexthop, + struct in6_addr *prefix, unsigned int *plen, + struct in6_addr *nexthop) +{ + if (!ip46_parse_cidr(s_prefix, prefix, plen)) { + return false; + } + + unsigned int nlen; + if (strcmp(s_nexthop, "discard") && + !ip46_parse_cidr(s_nexthop, nexthop, &nlen)) { + return false; + } + + /* Do not learn routes with link-local next hop. */ + return !in6_is_lla(nexthop); +} + +/* Return false if can't be added due to bad format. */ +static bool +add_to_routes_learned(struct hmap *routes_learned, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr) +{ + struct in6_addr prefix, nexthop; + unsigned int plen; + if (!parse_route(nb_route->ip_prefix, nb_route->nexthop, + &prefix, &plen, &nexthop)) { + return false; + } + const char *origin = smap_get_def(&nb_route->options, "origin", ""); + if (ic_route_find(routes_learned, &prefix, plen, &nexthop, origin, + nb_route->route_table, 0)) { + /* Route was added to learned on previous iteration. */ + return true; + } + + struct ic_route_info *ic_route = xzalloc(sizeof *ic_route); + ic_route->prefix = prefix; + ic_route->plen = plen; + ic_route->nexthop = nexthop; + ic_route->nb_route = nb_route; + ic_route->origin = origin; + ic_route->route_table = nb_route->route_table; + ic_route->nb_lr = nb_lr; + hmap_insert(routes_learned, &ic_route->node, + ic_route_hash(&prefix, plen, &nexthop, origin, + nb_route->route_table)); + return true; +} + +static bool +get_nexthop_from_lport_addresses(bool is_v4, + const struct lport_addresses *laddr, + struct in6_addr *nexthop) +{ + if (is_v4) { + if (!laddr->n_ipv4_addrs) { + return false; + } + in6_addr_set_mapped_ipv4(nexthop, laddr->ipv4_addrs[0].addr); + return true; + } + + /* ipv6 */ + if (laddr->n_ipv6_addrs) { + *nexthop = laddr->ipv6_addrs[0].addr; + return true; + } + + /* ipv6 link local */ + in6_generate_lla(laddr->ea, nexthop); + return true; +} + +static bool +prefix_is_filtered(struct in6_addr *prefix, + unsigned int plen, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp, + bool is_advertisement) +{ + struct ds filter_list = DS_EMPTY_INITIALIZER; + const char *filter_direction = is_advertisement ? "ic-route-filter-adv" : + "ic-route-filter-learn"; + if (ts_lrp) { + const char *lrp_route_filter = smap_get(&ts_lrp->options, + filter_direction); + if (lrp_route_filter) { + ds_put_format(&filter_list, "%s,", lrp_route_filter); + } + } + const char *lr_route_filter = smap_get(&nb_lr->options, + filter_direction); + if (lr_route_filter) { + ds_put_format(&filter_list, "%s,", lr_route_filter); + } + + struct sset prefix_set = SSET_INITIALIZER(&prefix_set); + sset_from_delimited_string(&prefix_set, ds_cstr(&filter_list), ","); + + bool matched = true; + if (!sset_is_empty(&prefix_set)) { + matched = find_prefix_in_set(prefix, plen, &prefix_set, + filter_direction); + } + + ds_destroy(&filter_list); + sset_destroy(&prefix_set); + return matched; +} + +static bool +prefix_is_deny_filtered(struct in6_addr *prefix, + unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp, + bool is_advertisement) +{ + struct ds deny_list = DS_EMPTY_INITIALIZER; + const char *deny_key = is_advertisement ? "ic-route-deny-adv" : + "ic-route-deny-learn"; + + if (ts_lrp) { + const char *lrp_deny_filter = smap_get(&ts_lrp->options, deny_key); + if (lrp_deny_filter) { + ds_put_format(&deny_list, "%s,", lrp_deny_filter); + } + } + + if (nb_lr) { + const char *lr_deny_filter = smap_get(&nb_lr->options, deny_key); + if (lr_deny_filter) { + ds_put_format(&deny_list, "%s,", lr_deny_filter); + } + } + + if (nb_options) { + const char *global_deny = smap_get(nb_options, "ic-route-denylist"); + if (!global_deny || !global_deny[0]) { + global_deny = smap_get(nb_options, "ic-route-blacklist"); + } + if (global_deny && global_deny[0]) { + ds_put_format(&deny_list, "%s,", global_deny); + } + } + + struct sset prefix_set = SSET_INITIALIZER(&prefix_set); + sset_from_delimited_string(&prefix_set, ds_cstr(&deny_list), ","); + + bool denied = false; + if (!sset_is_empty(&prefix_set)) { + denied = find_prefix_in_set(prefix, plen, &prefix_set, deny_key); + } + + ds_destroy(&deny_list); + sset_destroy(&prefix_set); + return denied; +} + +static bool +route_need_advertise(const char *policy, + struct in6_addr *prefix, + unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_logical_router_port *ts_lrp) +{ + if (!smap_get_bool(nb_options, "ic-route-adv", false)) { + return false; + } + + if (plen == 0 && + !smap_get_bool(nb_options, "ic-route-adv-default", false)) { + return false; + } + + if (policy && !strcmp(policy, "src-ip")) { + return false; + } + + if (prefix_is_link_local(prefix, plen)) { + return false; + } + + if (prefix_is_deny_filtered(prefix, plen, nb_options, + nb_lr, ts_lrp, true)) { + return false; + } + + if (!prefix_is_filtered(prefix, plen, nb_lr, ts_lrp, true)) { + return false; + } + + return true; +} + +static void +add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix, + unsigned int plen, const struct in6_addr nexthop, + const char *origin, const char *route_table, + const struct nbrec_logical_router_port *nb_lrp, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr, + const struct nbrec_load_balancer *nb_lb, + const char *route_tag) +{ + ovs_assert(nb_route || nb_lrp || nb_lb); + + if (route_table == NULL) { + route_table = ""; + } + + uint hash = ic_route_hash(&prefix, plen, &nexthop, origin, route_table); + + if (!ic_route_find(routes_ad, &prefix, plen, &nexthop, origin, + route_table, hash)) { + struct ic_route_info *ic_route = xzalloc(sizeof *ic_route); + ic_route->prefix = prefix; + ic_route->plen = plen; + ic_route->nexthop = nexthop; + ic_route->nb_route = nb_route; + ic_route->origin = origin; + ic_route->route_table = route_table; + ic_route->nb_lrp = nb_lrp; + ic_route->nb_lr = nb_lr; + ic_route->nb_lb = nb_lb; + ic_route->route_tag = route_tag; + hmap_insert(routes_ad, &ic_route->node, hash); + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + const char *msg_fmt = "Duplicate %s route advertisement was " + "suppressed! NB %s uuid: "UUID_FMT; + if (nb_route) { + VLOG_WARN_RL(&rl, msg_fmt, origin, "route", + UUID_ARGS(&nb_route->header_.uuid)); + } else if (nb_lb) { + VLOG_WARN_RL(&rl, msg_fmt, origin, "loadbalancer", + UUID_ARGS(&nb_lb->header_.uuid)); + } else { + VLOG_WARN_RL(&rl, msg_fmt, origin, "lrp", + UUID_ARGS(&nb_lrp->header_.uuid)); + } + } +} + +static void +add_static_to_routes_ad( + struct hmap *routes_ad, + const struct nbrec_logical_router_static_route *nb_route, + const struct nbrec_logical_router *nb_lr, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp) +{ + struct in6_addr prefix, nexthop; + unsigned int plen; + if (!parse_route(nb_route->ip_prefix, nb_route->nexthop, + &prefix, &plen, &nexthop)) { + return; + } + + if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options, + nb_lr, ts_lrp)) { + return; + } + + if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix), + nexthop_addresses, + &nexthop)) { + return; + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds msg = DS_EMPTY_INITIALIZER; + + ds_put_format(&msg, "Advertising static route: %s -> %s, ic nexthop: ", + nb_route->ip_prefix, nb_route->nexthop); + + if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { + ds_put_format(&msg, IP_FMT, + IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); + } else { + ipv6_format_addr(&nexthop, &msg); + } + + ds_put_format(&msg, ", route_table: %s", nb_route->route_table[0] + ? nb_route->route_table + : "<main>"); + + VLOG_DBG("%s", ds_cstr(&msg)); + ds_destroy(&msg); + } + + add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_STATIC, + nb_route->route_table, NULL, nb_route, nb_lr, + NULL, route_tag); +} + +static void +add_network_to_routes_ad(struct hmap *routes_ad, const char *network, + const struct nbrec_logical_router_port *nb_lrp, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp) +{ + struct in6_addr prefix, nexthop; + unsigned int plen; + if (!ip46_parse_cidr(network, &prefix, &plen)) { + return; + } + + if (!route_need_advertise(NULL, &prefix, plen, nb_options, + nb_lr, ts_lrp)) { + VLOG_DBG("Route ad: skip network %s of lrp %s.", + network, nb_lrp->name); + return; + } + + if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix), + nexthop_addresses, + &nexthop)) { + return; + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds msg = DS_EMPTY_INITIALIZER; + + ds_put_format(&msg, "Adding direct network route to <main> routing " + "table: %s of lrp %s, nexthop ", network, nb_lrp->name); + + if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { + ds_put_format(&msg, IP_FMT, + IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); + } else { + ipv6_format_addr(&nexthop, &msg); + } + + VLOG_DBG("%s", ds_cstr(&msg)); + ds_destroy(&msg); + } + + /* directly-connected routes go to <main> route table */ + add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_CONNECTED, + NULL, nb_lrp, NULL, nb_lr, NULL, route_tag); +} + +static void +add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key, + const struct nbrec_load_balancer *nb_lb, + const struct lport_addresses *nexthop_addresses, + const struct smap *nb_options, + const struct nbrec_logical_router *nb_lr, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp) +{ + char *vip_str = NULL; + struct in6_addr vip_ip, nexthop; + uint16_t vip_port; + int addr_family; + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + + if (!ip_address_and_port_from_lb_key(vip_key, &vip_str, &vip_ip, + &vip_port, &addr_family)) { + VLOG_WARN_RL(&rl, "Route ad: Parsing failed for lb vip %s", vip_key); + return; + } + if (vip_str == NULL) { + return; + } + unsigned int plen = (addr_family == AF_INET) ? 32 : 128; + if (!route_need_advertise(NULL, &vip_ip, plen, nb_options, + nb_lr, ts_lrp)) { + VLOG_DBG("Route ad: skip lb vip %s.", vip_key); + goto out; + } + if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&vip_ip), + nexthop_addresses, + &nexthop)) { + VLOG_WARN_RL(&rl, "Route ad: failed to get nexthop for lb vip"); + goto out; + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds msg = DS_EMPTY_INITIALIZER; + + ds_put_format(&msg, "Adding lb vip route to <main> routing " + "table: %s, nexthop ", vip_str); + + if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { + ds_put_format(&msg, IP_FMT, + IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); + } else { + ipv6_format_addr(&nexthop, &msg); + } + + VLOG_DBG("%s", ds_cstr(&msg)); + ds_destroy(&msg); + } + + /* Lb vip routes go to <main> route table */ + add_to_routes_ad(routes_ad, vip_ip, plen, nexthop, ROUTE_ORIGIN_LB, + NULL, NULL, NULL, nb_lr, nb_lb, route_tag); +out: + free(vip_str); +} + +static bool +route_has_local_gw(const struct nbrec_logical_router *lr, + const char *route_table, const char *ip_prefix) { + + const struct nbrec_logical_router_static_route *route; + for (int i = 0; i < lr->n_static_routes; i++) { + route = lr->static_routes[i]; + if (!smap_get(&route->external_ids, "ic-learned-route") && + !strcmp(route->route_table, route_table) && + !strcmp(route->ip_prefix, ip_prefix)) { + return true; + } + } + return false; +} + +static bool +lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp, + struct in6_addr *nexthop) +{ + if (!lrp || !nexthop) { + return false; + } + + struct lport_addresses lrp_networks; + if (!extract_lrp_networks(lrp, &lrp_networks)) { + destroy_lport_addresses(&lrp_networks); + return false; + } + + if (IN6_IS_ADDR_V4MAPPED(nexthop)) { + ovs_be32 neigh_prefix_v4 = in6_addr_get_mapped_ipv4(nexthop); + for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) { + struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i]; + if (address.network == (neigh_prefix_v4 & address.mask)) { + destroy_lport_addresses(&lrp_networks); + return true; + } + } + } else { + for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) { + struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i]; + struct in6_addr neigh_prefix = ipv6_addr_bitand(nexthop, + &address.mask); + if (ipv6_addr_equals(&address.network, &neigh_prefix)) { + destroy_lport_addresses(&lrp_networks); + return true; + } + } + } + + destroy_lport_addresses(&lrp_networks); + return false; +} + +static bool +route_matches_local_lb(const struct nbrec_load_balancer *nb_lb, + const char *ip_prefix) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + struct in6_addr prefix; + unsigned int plen; + + if (!ip46_parse_cidr(ip_prefix, &prefix, &plen)) { + return false; + } + + struct smap_node *node; + SMAP_FOR_EACH (node, &nb_lb->vips) { + char *vip_str = NULL; + struct in6_addr vip_ip; + uint16_t vip_port; + int addr_family; + if (ip_address_and_port_from_lb_key(node->key, &vip_str, + &vip_ip, &vip_port, + &addr_family)) { + if (IN6_IS_ADDR_V4MAPPED(&prefix) && addr_family == AF_INET) { + ovs_be32 vip = in6_addr_get_mapped_ipv4(&vip_ip); + ovs_be32 mask = be32_prefix_mask(plen); + + if ((vip & mask) == in6_addr_get_mapped_ipv4(&prefix)) { + free(vip_str); + return true; + } + } else if (!IN6_IS_ADDR_V4MAPPED(&prefix) + && addr_family == AF_INET6) { + struct in6_addr mask = ipv6_create_mask(plen); + struct in6_addr vip_prefix = ipv6_addr_bitand(&vip_ip, &mask); + if (ipv6_addr_equals(&prefix, &vip_prefix)) { + free(vip_str); + return true; + } + } + free(vip_str); + } else { + VLOG_WARN_RL(&rl, + "Route learn: Parsing failed for local lb vip %s", + node->key); + } + } + return false; +} + +static bool +route_need_learn(const struct nbrec_logical_router *lr, + const struct icsbrec_route *isb_route, + struct in6_addr *prefix, unsigned int plen, + const struct smap *nb_options, + const struct nbrec_logical_router_port *ts_lrp, + struct in6_addr *nexthop) +{ + if (!smap_get_bool(nb_options, "ic-route-learn", false)) { + return false; + } + + if (plen == 0 && + !smap_get_bool(nb_options, "ic-route-learn-default", false)) { + return false; + } + + if (!strcmp(isb_route->origin, ROUTE_ORIGIN_LB) && + !smap_get_bool(nb_options, "ic-route-learn-lb", false)) { + return false; + } + + if (!lrouter_is_enabled(lr)) { + return false; + } + + if (prefix_is_link_local(prefix, plen)) { + return false; + } + + if (prefix_is_deny_filtered(prefix, plen, nb_options, lr, ts_lrp, false)) { + return false; + } + + if (!prefix_is_filtered(prefix, plen, lr, ts_lrp, false)) { + return false; + } + + if (route_has_local_gw(lr, isb_route->route_table, isb_route->ip_prefix)) { + VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got one with " + "local GW", isb_route->ip_prefix, isb_route->route_table); + return false; + } + + if (!lrp_has_neighbor_in_ts(ts_lrp, nexthop)) { + return false; + } + + for (size_t i = 0; i < lr->n_load_balancer; i++) { + if (route_matches_local_lb(lr->load_balancer[i], + isb_route->ip_prefix)) { + VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got local" + " LB with matching VIP", isb_route->ip_prefix, + isb_route->route_table); + return false; + } + } + for (size_t i = 0; i < lr->n_load_balancer_group; i++) { + const struct nbrec_load_balancer_group *nb_lbg = + lr->load_balancer_group[i]; + for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) { + if (route_matches_local_lb(nb_lbg->load_balancer[j], + isb_route->ip_prefix)) { + VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got local" + " LB with matching VIP", isb_route->ip_prefix, + isb_route->route_table); + return false; + } + } + } + + return true; +} + +static const char * +get_lrp_name_by_ts_port_name(struct route_input *ic, const char *ts_port_name) +{ + const struct nbrec_logical_switch_port *nb_lsp; + + nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name, ts_port_name); + if (!nb_lsp) { + return NULL; + } + + return smap_get(&nb_lsp->options, "router-port"); +} + +static const struct nbrec_logical_router_port * +find_lrp_of_nexthop(struct route_input *ic, + const struct icsbrec_route *isb_route) +{ + const struct nbrec_logical_router_port *lrp; + const struct nbrec_logical_switch *ls; + ls = find_ts_in_nb(ic->nbrec_ls_by_name, isb_route->transit_switch); + if (!ls) { + return NULL; + } + + struct in6_addr nexthop; + if (!ip46_parse(isb_route->nexthop, &nexthop)) { + return NULL; + } + + for (size_t i = 0; i < ls->n_ports; i++) { + char *lsp_name = ls->ports[i]->name; + const char *lrp_name = get_lrp_name_by_ts_port_name(ic, + lsp_name); + if (!lrp_name) { + continue; + } + + lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name); + if (!lrp) { + continue; + } + + struct lport_addresses lrp_networks; + if (!extract_lrp_networks(lrp, &lrp_networks)) { + destroy_lport_addresses(&lrp_networks); + continue; + } + + if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { + ovs_be32 nexthop_v4 = in6_addr_get_mapped_ipv4(&nexthop); + for (size_t i_v4 = 0; i_v4 < lrp_networks.n_ipv4_addrs; i_v4++) { + struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i_v4]; + if (address.addr == nexthop_v4) { + destroy_lport_addresses(&lrp_networks); + return lrp; + } + } + } else { + for (size_t i_v6 = 0; i_v6 < lrp_networks.n_ipv6_addrs; i_v6++) { + struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i_v6]; + struct in6_addr nexthop_v6 = ipv6_addr_bitand(&nexthop, + &address.mask); + if (ipv6_addr_equals(&address.network, &nexthop_v6)) { + destroy_lport_addresses(&lrp_networks); + return lrp; + } + } + } + destroy_lport_addresses(&lrp_networks); + } + + return NULL; +} + +static bool +lrp_is_ts_port(struct route_input *ic, struct ic_router_info *ic_lr, + const char *lrp_name) +{ + const struct icsbrec_port_binding *isb_pb; + const char *ts_lrp_name; + VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { + ts_lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); + if (!strcmp(ts_lrp_name, lrp_name)) { + return true; + } + } + return false; +} + +static void +sync_learned_routes(const struct engine_context *ctx, + struct route_input *ic, + struct ic_router_info *ic_lr, + const struct nbrec_nb_global_table *nb_global_table) +{ + ovs_assert(ctx->ovnnb_idl_txn); + const struct icsbrec_route *isb_route, *isb_route_key; + + const struct nbrec_nb_global *nb_global = + nbrec_nb_global_table_first(nb_global_table); + ovs_assert(nb_global); + + const char *lrp_name, *ts_route_table, *route_filter_tag; + const struct icsbrec_port_binding *isb_pb; + const struct nbrec_logical_router_port *lrp; + VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { + if (!strcmp(isb_pb->address, "")) { + continue; + } + lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); + lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name); + if (lrp) { + ts_route_table = smap_get_def(&lrp->options, "route_table", ""); + route_filter_tag = smap_get_def(&lrp->options, + "ic-route-filter-tag", ""); + } else { + ts_route_table = ""; + route_filter_tag = ""; + } + + isb_route_key = icsbrec_route_index_init_row(ic->icsbrec_route_by_ts); + icsbrec_route_index_set_transit_switch(isb_route_key, + isb_pb->transit_switch); + + ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, + ic->icsbrec_route_by_ts) { + /* Filters ICSB routes, skipping those that either belong to + * current logical router or are legacy routes from the current + * availability zone (withoud lr-id). + */ + const char *lr_id = smap_get(&isb_route->external_ids, "lr-id"); + struct uuid lr_uuid; + if (lr_id) { + if (!uuid_from_string(&lr_uuid, lr_id) + || uuid_equals(&ic_lr->lr->header_.uuid, &lr_uuid)) { + continue; + } + } else if (isb_route->availability_zone == ic->runned_az) { + continue; + } + + const char *isb_route_tag = smap_get(&isb_route->external_ids, + "ic-route-tag"); + if (isb_route_tag && !strcmp(isb_route_tag, route_filter_tag)) { + VLOG_DBG("Skip learning route %s -> %s as its route tag " + "[%s] is filtered by the filter tag [%s] of TS LRP ", + isb_route->ip_prefix, isb_route->nexthop, + isb_route_tag, route_filter_tag); + continue; + } + + if (isb_route->route_table[0] && + strcmp(isb_route->route_table, ts_route_table)) { + if (VLOG_IS_DBG_ENABLED()) { + VLOG_DBG("Skip learning static route %s -> %s as either " + "its route table %s != %s of TS port or ", + isb_route->ip_prefix, isb_route->nexthop, + isb_route->route_table, ts_route_table); + } + continue; + } + + struct in6_addr prefix, nexthop; + unsigned int plen; + if (!parse_route(isb_route->ip_prefix, isb_route->nexthop, + &prefix, &plen, &nexthop)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Bad route format in IC-SB: %s -> %s. " + "Ignored.", isb_route->ip_prefix, + isb_route->nexthop); + continue; + } + if (!route_need_learn(ic_lr->lr, isb_route, &prefix, plen, + &nb_global->options, lrp, &nexthop)) { + continue; + } + + struct ic_route_info *route_learned + = ic_route_find(&ic_lr->routes_learned, &prefix, plen, + &nexthop, isb_route->origin, + isb_route->route_table, 0); + if (route_learned) { + /* Sync external-ids */ + struct uuid ext_id; + smap_get_uuid(&route_learned->nb_route->external_ids, + "ic-learned-route", &ext_id); + if (!uuid_equals(&ext_id, &isb_route->header_.uuid)) { + char *uuid_s = + xasprintf(UUID_FMT, + UUID_ARGS(&isb_route->header_.uuid)); + nbrec_logical_router_static_route_update_external_ids_setkey( + route_learned->nb_route, "ic-learned-route", uuid_s); + free(uuid_s); + } + hmap_remove(&ic_lr->routes_learned, &route_learned->node); + free(route_learned); + } else { + /* Create the missing route in NB. */ + const struct nbrec_logical_router_static_route *nb_route = + nbrec_logical_router_static_route_insert( + ctx->ovnnb_idl_txn); + nbrec_logical_router_static_route_set_ip_prefix(nb_route, + isb_route->ip_prefix); + nbrec_logical_router_static_route_set_nexthop(nb_route, + isb_route->nexthop); + char *uuid_s = xasprintf(UUID_FMT, + UUID_ARGS(&isb_route->header_.uuid)); + nbrec_logical_router_static_route_set_route_table(nb_route, + isb_route->route_table); + nbrec_logical_router_static_route_update_external_ids_setkey( + nb_route, "ic-learned-route", uuid_s); + nbrec_logical_router_static_route_update_options_setkey( + nb_route, "origin", isb_route->origin); + free(uuid_s); + nbrec_logical_router_update_static_routes_addvalue(ic_lr->lr, + nb_route); + } + } + icsbrec_route_index_destroy_row(isb_route_key); + } + + /* Delete extra learned routes. */ + struct ic_route_info *route_learned; + HMAP_FOR_EACH_SAFE (route_learned, node, &ic_lr->routes_learned) { + VLOG_DBG("Delete route %s -> %s that is not in IC-SB from NB.", + route_learned->nb_route->ip_prefix, + route_learned->nb_route->nexthop); + nbrec_logical_router_update_static_routes_delvalue( + ic_lr->lr, route_learned->nb_route); + hmap_remove(&ic_lr->routes_learned, &route_learned->node); + free(route_learned); + } +} + +static void +ad_route_sync_external_ids(const struct ic_route_info *route_adv, + const struct icsbrec_route *isb_route) +{ + struct uuid isb_ext_id, nb_id, isb_ext_lr_id, lr_id; + const char *route_tag; + smap_get_uuid(&isb_route->external_ids, "nb-id", &isb_ext_id); + smap_get_uuid(&isb_route->external_ids, "lr-id", &isb_ext_lr_id); + nb_id = route_adv->nb_lb ? route_adv->nb_lb->header_.uuid : + route_adv->nb_route ? route_adv->nb_route->header_.uuid : + route_adv->nb_lrp->header_.uuid; + + lr_id = route_adv->nb_lr->header_.uuid; + if (!uuid_equals(&isb_ext_id, &nb_id)) { + char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&nb_id)); + icsbrec_route_update_external_ids_setkey(isb_route, "nb-id", + uuid_s); + free(uuid_s); + } + if (!uuid_equals(&isb_ext_lr_id, &lr_id)) { + char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_id)); + icsbrec_route_update_external_ids_setkey(isb_route, "lr-id", + uuid_s); + free(uuid_s); + } + if (strcmp(route_adv->route_tag, "")) { + icsbrec_route_update_external_ids_setkey(isb_route, "ic-route-tag", + route_adv->route_tag); + } else { + route_tag = smap_get(&isb_route->external_ids, "ic-route-tag"); + if (route_tag) { + icsbrec_route_update_external_ids_delkey(isb_route, + "ic-route-tag"); + } + } +} + +/* Sync routes from routes_ad to IC-SB. */ +static void +advertise_routes(const struct engine_context *ctx, + struct route_input *ic, + const struct icsbrec_availability_zone *az, + const char *ts_name, struct hmap *routes_ad) +{ + ovs_assert(ctx->ovnisb_idl_txn); + const struct icsbrec_route *isb_route; + const struct icsbrec_route *isb_route_key = + icsbrec_route_index_init_row(ic->icsbrec_route_by_ts_az); + icsbrec_route_index_set_transit_switch(isb_route_key, ts_name); + icsbrec_route_index_set_availability_zone(isb_route_key, az); + + ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, + ic->icsbrec_route_by_ts_az) { + struct in6_addr prefix, nexthop; + unsigned int plen; + + if (!parse_route(isb_route->ip_prefix, isb_route->nexthop, + &prefix, &plen, &nexthop)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Bad route format in IC-SB: %s -> %s. " + "Delete it.", + isb_route->ip_prefix, isb_route->nexthop); + icsbrec_route_delete(isb_route); + continue; + } + struct ic_route_info *route_adv = + ic_route_find(routes_ad, &prefix, plen, &nexthop, + isb_route->origin, isb_route->route_table, 0); + if (!route_adv) { + /* Delete the extra route from IC-SB. */ + VLOG_DBG("Delete route %s -> %s from IC-SB, which is not found" + " in local routes to be advertised.", + isb_route->ip_prefix, isb_route->nexthop); + icsbrec_route_delete(isb_route); + } else { + ad_route_sync_external_ids(route_adv, isb_route); + + hmap_remove(routes_ad, &route_adv->node); + free(route_adv); + } + } + icsbrec_route_index_destroy_row(isb_route_key); + + /* Create the missing routes in IC-SB */ + struct ic_route_info *route_adv; + HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) { + isb_route = icsbrec_route_insert(ctx->ovnisb_idl_txn); + icsbrec_route_set_transit_switch(isb_route, ts_name); + icsbrec_route_set_availability_zone(isb_route, az); + + char *prefix_s, *nexthop_s; + if (IN6_IS_ADDR_V4MAPPED(&route_adv->prefix)) { + ovs_be32 ipv4 = in6_addr_get_mapped_ipv4(&route_adv->prefix); + ovs_be32 nh = in6_addr_get_mapped_ipv4(&route_adv->nexthop); + prefix_s = xasprintf(IP_FMT "/%d", IP_ARGS(ipv4), route_adv->plen); + nexthop_s = xasprintf(IP_FMT, IP_ARGS(nh)); + } else { + char network_s[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &route_adv->prefix, network_s, + INET6_ADDRSTRLEN); + prefix_s = xasprintf("%s/%d", network_s, route_adv->plen); + inet_ntop(AF_INET6, &route_adv->nexthop, network_s, + INET6_ADDRSTRLEN); + nexthop_s = xstrdup(network_s); + } + icsbrec_route_set_ip_prefix(isb_route, prefix_s); + icsbrec_route_set_nexthop(isb_route, nexthop_s); + icsbrec_route_set_origin(isb_route, route_adv->origin); + icsbrec_route_set_route_table(isb_route, route_adv->route_table + ? route_adv->route_table + : ""); + free(prefix_s); + free(nexthop_s); + + ad_route_sync_external_ids(route_adv, isb_route); + + hmap_remove(routes_ad, &route_adv->node); + free(route_adv); + } +} + +static void +build_ts_routes_to_adv(struct route_input *ic, + struct ic_router_info *ic_lr, + struct hmap *routes_ad, + struct lport_addresses *ts_port_addrs, + const struct nbrec_nb_global *nb_global, + const char *ts_route_table, + const char *route_tag, + const struct nbrec_logical_router_port *ts_lrp) +{ + const struct nbrec_logical_router *lr = ic_lr->lr; + + /* Check static routes of the LR */ + for (int i = 0; i < lr->n_static_routes; i++) { + const struct nbrec_logical_router_static_route *nb_route + = lr->static_routes[i]; + struct uuid isb_uuid; + if (smap_get_uuid(&nb_route->external_ids, "ic-learned-route", + &isb_uuid)) { + /* It is a learned route */ + if (!add_to_routes_learned(&ic_lr->routes_learned, nb_route, lr)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Bad format of learned route in NB: " + "%s -> %s. Delete it.", nb_route->ip_prefix, + nb_route->nexthop); + nbrec_logical_router_update_static_routes_delvalue(lr, + nb_route); + } + } else if (!strcmp(ts_route_table, nb_route->route_table)) { + /* It may be a route to be advertised */ + add_static_to_routes_ad(routes_ad, nb_route, lr, ts_port_addrs, + &nb_global->options, route_tag, ts_lrp); + } + } + + /* Check directly-connected subnets of the LR */ + for (int i = 0; i < lr->n_ports; i++) { + const struct nbrec_logical_router_port *lrp = lr->ports[i]; + if (!lrp_is_ts_port(ic, ic_lr, lrp->name)) { + for (int j = 0; j < lrp->n_networks; j++) { + add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp, + ts_port_addrs, + &nb_global->options, + lr, route_tag, ts_lrp); + } + } else { + /* The router port of the TS port is ignored. */ + VLOG_DBG("Skip advertising direct route of lrp %s (TS port)", + lrp->name); + } + } + + /* Check loadbalancers associated with the LR */ + if (smap_get_bool(&nb_global->options, "ic-route-adv-lb", false)) { + for (size_t i = 0; i < lr->n_load_balancer; i++) { + const struct nbrec_load_balancer *nb_lb = lr->load_balancer[i]; + struct smap_node *node; + SMAP_FOR_EACH (node, &nb_lb->vips) { + add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb, + ts_port_addrs, + &nb_global->options, + lr, route_tag, ts_lrp); + } + } + + for (size_t i = 0; i < lr->n_load_balancer_group; i++) { + const struct nbrec_load_balancer_group *nb_lbg = + lr->load_balancer_group[i]; + for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) { + const struct nbrec_load_balancer *nb_lb = + nb_lbg->load_balancer[j]; + struct smap_node *node; + SMAP_FOR_EACH (node, &nb_lb->vips) { + add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb, + ts_port_addrs, + &nb_global->options, + lr, route_tag, ts_lrp); + } + } + } + } +} + +static void +collect_lr_routes(struct route_input *ic, + struct ic_router_info *ic_lr, + struct shash *routes_ad_by_ts, + const struct nbrec_nb_global_table *nb_global_table) +{ + const struct nbrec_nb_global *nb_global = + nbrec_nb_global_table_first(nb_global_table); + + ovs_assert(nb_global); + + const struct icsbrec_port_binding *isb_pb; + const char *lrp_name, *ts_name, *route_table, *route_tag; + struct lport_addresses ts_port_addrs; + const struct icnbrec_transit_switch *key; + const struct nbrec_logical_router_port *lrp; + + struct hmap *routes_ad; + const struct icnbrec_transit_switch *t_sw; + VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { + key = icnbrec_transit_switch_index_init_row( + ic->icnbrec_transit_switch_by_name); + icnbrec_transit_switch_index_set_name(key, isb_pb->transit_switch); + t_sw = icnbrec_transit_switch_index_find( + ic->icnbrec_transit_switch_by_name, key); + icnbrec_transit_switch_index_destroy_row(key); + if (!t_sw) { + continue; + } + ts_name = t_sw->name; + routes_ad = shash_find_data(routes_ad_by_ts, ts_name); + if (!routes_ad) { + routes_ad = xzalloc(sizeof *routes_ad); + hmap_init(routes_ad); + shash_add(routes_ad_by_ts, ts_name, routes_ad); + } + + if (!extract_lsp_addresses(isb_pb->address, &ts_port_addrs)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_INFO_RL(&rl, "Route sync ignores port %s on ts %s for router" + " %s because the addresses are invalid.", + isb_pb->logical_port, isb_pb->transit_switch, + ic_lr->lr->name); + continue; + } + lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); + lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name); + if (lrp) { + route_table = smap_get_def(&lrp->options, "route_table", ""); + route_tag = smap_get_def(&lrp->options, "ic-route-tag", ""); + } else { + route_table = ""; + route_tag = ""; + } + build_ts_routes_to_adv(ic, ic_lr, routes_ad, &ts_port_addrs, + nb_global, route_table, route_tag, lrp); + destroy_lport_addresses(&ts_port_addrs); + } +} + +static void +delete_orphan_ic_routes(struct route_input *ic, + const struct icsbrec_availability_zone *az) +{ + const struct icsbrec_route *isb_route, *isb_route_key = + icsbrec_route_index_init_row(ic->icsbrec_route_by_az); + icsbrec_route_index_set_availability_zone(isb_route_key, az); + + const struct icnbrec_transit_switch *t_sw, *t_sw_key; + + ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, + ic->icsbrec_route_by_az) + { + t_sw_key = icnbrec_transit_switch_index_init_row( + ic->icnbrec_transit_switch_by_name); + icnbrec_transit_switch_index_set_name(t_sw_key, + isb_route->transit_switch); + t_sw = icnbrec_transit_switch_index_find( + ic->icnbrec_transit_switch_by_name, t_sw_key); + icnbrec_transit_switch_index_destroy_row(t_sw_key); + + if (!t_sw || !find_lrp_of_nexthop(ic, isb_route)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_INFO_RL(&rl, "Deleting orphan ICDB:Route: %s->%s (%s, rtb:%s," + " transit switch: %s)", isb_route->ip_prefix, + isb_route->nexthop, isb_route->origin, + isb_route->route_table, isb_route->transit_switch); + icsbrec_route_delete(isb_route); + } + } + icsbrec_route_index_destroy_row(isb_route_key); +} diff --git a/ic/en-route.h b/ic/en-route.h new file mode 100644 index 000000000..2365edcbf --- /dev/null +++ b/ic/en-route.h @@ -0,0 +1,70 @@ +#ifndef EN_IC_ROUTE_H +#define EN_IC_ROUTE_H 1 + +#include <config.h> + +#include <stdbool.h> +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include "vec.h" + +/* OVN includes. */ +#include "lib/inc-proc-eng.h" + +struct ed_type_route { + struct hmap pb_tnlids; + struct shash switch_all_local_pbs; + struct shash router_all_local_pbs; +}; + +struct ic_router_info { + struct hmap_node node; + const struct nbrec_logical_router *lr; /* key of hmap */ + struct vector isb_pbs; /* Vector of const struct icsbrec_port_binding *. */ + struct hmap routes_learned; +}; + +/* Represents an interconnection route entry. */ +struct ic_route_info { + struct hmap_node node; + struct in6_addr prefix; + unsigned int plen; + struct in6_addr nexthop; + const char *origin; + const char *route_table; + const char *route_tag; + + const struct nbrec_logical_router *nb_lr; + + /* One of nb_route, nb_lrp, nb_lb is set and the other ones must be NULL. + * - For a route that is learned from IC-SB, or a static route that is + * generated from a route that is configured in NB, the "nb_route" + * is set. + * - For a route that is generated from a direct-connect subnet of + * a logical router port, the "nb_lrp" is set. + * - For a route that is generated from a load-balancer vip of + * a logical router, the "nb_lb" is set. */ + const struct nbrec_logical_router_static_route *nb_route; + const struct nbrec_logical_router_port *nb_lrp; + const struct nbrec_load_balancer *nb_lb; +}; + +struct route_input { + /* Indexes */ + const struct icsbrec_availability_zone *runned_az; + struct ovsdb_idl_index *nbrec_ls_by_name; + struct ovsdb_idl_index *nbrec_port_by_name; + struct ovsdb_idl_index *nbrec_lrp_by_name; + struct ovsdb_idl_index *icsbrec_route_by_az; + struct ovsdb_idl_index *icsbrec_route_by_ts; + struct ovsdb_idl_index *icsbrec_route_by_ts_az; + struct ovsdb_idl_index *icsbrec_port_binding_by_az; + struct ovsdb_idl_index *icnbrec_transit_switch_by_name; +}; + +void *en_route_init(struct engine_node *, struct engine_arg *); +enum engine_node_state en_route_run(struct engine_node *, void *data); +void en_route_cleanup(void *data); + +#endif diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c index ac360fb9f..7399dbd57 100644 --- a/ic/inc-proc-ic.c +++ b/ic/inc-proc-ic.c @@ -29,6 +29,7 @@ #include "en-ic.h" #include "en-enum-datapaths.h" #include "en-port-binding.h" +#include "en-route.h" #include "unixctl.h" #include "util.h" @@ -162,6 +163,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_ic); static ENGINE_NODE(ic, SB_WRITE); static ENGINE_NODE(enum_datapaths); static ENGINE_NODE(port_binding, SB_WRITE); +static ENGINE_NODE(route); void inc_proc_ic_init(struct ovsdb_idl_loop *nb, struct ovsdb_idl_loop *sb, @@ -181,10 +183,18 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_port_binding, &en_nb_logical_router, NULL); engine_add_input(&en_port_binding, &en_sb_chassis, NULL); + engine_add_input(&en_route, &en_nb_nb_global, NULL); + engine_add_input(&en_route, &en_nb_logical_switch, NULL); + engine_add_input(&en_route, &en_nb_logical_router, NULL); + engine_add_input(&en_route, &en_icnb_transit_switch, NULL); + engine_add_input(&en_route, &en_icsb_port_binding, NULL); + engine_add_input(&en_route, &en_icsb_route, NULL); + engine_add_input(&en_route, &en_nb_logical_router_static_route, NULL); + engine_add_input(&en_ic, &en_enum_datapaths, NULL); engine_add_input(&en_ic, &en_port_binding, NULL); - engine_add_input(&en_ic, &en_nb_nb_global, NULL); - engine_add_input(&en_ic, &en_nb_logical_router_static_route, NULL); + engine_add_input(&en_ic, &en_route, NULL); + engine_add_input(&en_ic, &en_nb_logical_router, NULL); engine_add_input(&en_ic, &en_nb_logical_router_port, NULL); engine_add_input(&en_ic, &en_nb_logical_switch, NULL); @@ -210,7 +220,6 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_ic, &en_icsb_encap, NULL); engine_add_input(&en_ic, &en_icsb_service_monitor, NULL); engine_add_input(&en_ic, &en_icsb_gateway, NULL); - engine_add_input(&en_ic, &en_icsb_route, NULL); engine_add_input(&en_ic, &en_icsb_datapath_binding, NULL); struct engine_arg engine_arg = { diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c index 7bde85854..c880244e2 100644 --- a/ic/ovn-ic.c +++ b/ic/ovn-ic.c @@ -577,14 +577,15 @@ gateway_run(struct engine_context *ctx, } const struct nbrec_logical_router_port * -get_lrp_by_lrp_name(struct ic_input *ic, const char *lrp_name) +get_lrp_by_lrp_name(struct ovsdb_idl_index *nbrec_lrp_by_name, + const char *lrp_name) { const struct nbrec_logical_router_port *lrp; const struct nbrec_logical_router_port *lrp_key = - nbrec_logical_router_port_index_init_row(ic->nbrec_lrp_by_name); + nbrec_logical_router_port_index_init_row(nbrec_lrp_by_name); nbrec_logical_router_port_index_set_name(lrp_key, lrp_name); lrp = - nbrec_logical_router_port_index_find(ic->nbrec_lrp_by_name, lrp_key); + nbrec_logical_router_port_index_find(nbrec_lrp_by_name, lrp_key); nbrec_logical_router_port_index_destroy_row(lrp_key); return lrp; @@ -643,1297 +644,6 @@ find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name, return pb; } -struct ic_router_info { - struct hmap_node node; - const struct nbrec_logical_router *lr; /* key of hmap */ - struct vector isb_pbs; /* Vector of const struct icsbrec_port_binding *. */ - struct hmap routes_learned; -}; - -/* Represents an interconnection route entry. */ -struct ic_route_info { - struct hmap_node node; - struct in6_addr prefix; - unsigned int plen; - struct in6_addr nexthop; - const char *origin; - const char *route_table; - const char *route_tag; - - const struct nbrec_logical_router *nb_lr; - - /* One of nb_route, nb_lrp, nb_lb is set and the other ones must be NULL. - * - For a route that is learned from IC-SB, or a static route that is - * generated from a route that is configured in NB, the "nb_route" - * is set. - * - For a route that is generated from a direct-connect subnet of - * a logical router port, the "nb_lrp" is set. - * - For a route that is generated from a load-balancer vip of - * a logical router, the "nb_lb" is set. */ - const struct nbrec_logical_router_static_route *nb_route; - const struct nbrec_logical_router_port *nb_lrp; - const struct nbrec_load_balancer *nb_lb; -}; - -static uint32_t -ic_route_hash(const struct in6_addr *prefix, unsigned int plen, - const struct in6_addr *nexthop, const char *origin, - const char *route_table) -{ - uint32_t basis = hash_bytes(prefix, sizeof *prefix, (uint32_t)plen); - basis = hash_string(origin, basis); - basis = hash_string(route_table, basis); - return hash_bytes(nexthop, sizeof *nexthop, basis); -} - -static struct ic_route_info * -ic_route_find(struct hmap *routes, const struct in6_addr *prefix, - unsigned int plen, const struct in6_addr *nexthop, - const char *origin, const char *route_table, uint32_t hash) -{ - struct ic_route_info *r; - if (!hash) { - hash = ic_route_hash(prefix, plen, nexthop, origin, route_table); - } - HMAP_FOR_EACH_WITH_HASH (r, node, hash, routes) { - if (ipv6_addr_equals(&r->prefix, prefix) && - r->plen == plen && - ipv6_addr_equals(&r->nexthop, nexthop) && - !strcmp(r->origin, origin) && - !strcmp(r->route_table ? r->route_table : "", route_table)) { - return r; - } - } - return NULL; -} - -static struct ic_router_info * -ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr) -{ - struct ic_router_info *ic_lr; - HMAP_FOR_EACH_WITH_HASH (ic_lr, node, uuid_hash(&lr->header_.uuid), - ic_lrs) { - if (ic_lr->lr == lr) { - return ic_lr; - } - } - return NULL; -} - -static bool -parse_route(const char *s_prefix, const char *s_nexthop, - struct in6_addr *prefix, unsigned int *plen, - struct in6_addr *nexthop) -{ - if (!ip46_parse_cidr(s_prefix, prefix, plen)) { - return false; - } - - unsigned int nlen; - if (strcmp(s_nexthop, "discard") && - !ip46_parse_cidr(s_nexthop, nexthop, &nlen)) { - return false; - } - - /* Do not learn routes with link-local next hop. */ - return !in6_is_lla(nexthop); -} - -/* Return false if can't be added due to bad format. */ -static bool -add_to_routes_learned(struct hmap *routes_learned, - const struct nbrec_logical_router_static_route *nb_route, - const struct nbrec_logical_router *nb_lr) -{ - struct in6_addr prefix, nexthop; - unsigned int plen; - if (!parse_route(nb_route->ip_prefix, nb_route->nexthop, - &prefix, &plen, &nexthop)) { - return false; - } - const char *origin = smap_get_def(&nb_route->options, "origin", ""); - if (ic_route_find(routes_learned, &prefix, plen, &nexthop, origin, - nb_route->route_table, 0)) { - /* Route was added to learned on previous iteration. */ - return true; - } - - struct ic_route_info *ic_route = xzalloc(sizeof *ic_route); - ic_route->prefix = prefix; - ic_route->plen = plen; - ic_route->nexthop = nexthop; - ic_route->nb_route = nb_route; - ic_route->origin = origin; - ic_route->route_table = nb_route->route_table; - ic_route->nb_lr = nb_lr; - hmap_insert(routes_learned, &ic_route->node, - ic_route_hash(&prefix, plen, &nexthop, origin, - nb_route->route_table)); - return true; -} - -static bool -get_nexthop_from_lport_addresses(bool is_v4, - const struct lport_addresses *laddr, - struct in6_addr *nexthop) -{ - if (is_v4) { - if (!laddr->n_ipv4_addrs) { - return false; - } - in6_addr_set_mapped_ipv4(nexthop, laddr->ipv4_addrs[0].addr); - return true; - } - - /* ipv6 */ - if (laddr->n_ipv6_addrs) { - *nexthop = laddr->ipv6_addrs[0].addr; - return true; - } - - /* ipv6 link local */ - in6_generate_lla(laddr->ea, nexthop); - return true; -} - -static bool -prefix_is_filtered(struct in6_addr *prefix, - unsigned int plen, - const struct nbrec_logical_router *nb_lr, - const struct nbrec_logical_router_port *ts_lrp, - bool is_advertisement) -{ - struct ds filter_list = DS_EMPTY_INITIALIZER; - const char *filter_direction = is_advertisement ? "ic-route-filter-adv" : - "ic-route-filter-learn"; - if (ts_lrp) { - const char *lrp_route_filter = smap_get(&ts_lrp->options, - filter_direction); - if (lrp_route_filter) { - ds_put_format(&filter_list, "%s,", lrp_route_filter); - } - } - const char *lr_route_filter = smap_get(&nb_lr->options, - filter_direction); - if (lr_route_filter) { - ds_put_format(&filter_list, "%s,", lr_route_filter); - } - - struct sset prefix_set = SSET_INITIALIZER(&prefix_set); - sset_from_delimited_string(&prefix_set, ds_cstr(&filter_list), ","); - - bool matched = true; - if (!sset_is_empty(&prefix_set)) { - matched = find_prefix_in_set(prefix, plen, &prefix_set, - filter_direction); - } - - ds_destroy(&filter_list); - sset_destroy(&prefix_set); - return matched; -} - -static bool -prefix_is_deny_filtered(struct in6_addr *prefix, - unsigned int plen, - const struct smap *nb_options, - const struct nbrec_logical_router *nb_lr, - const struct nbrec_logical_router_port *ts_lrp, - bool is_advertisement) -{ - struct ds deny_list = DS_EMPTY_INITIALIZER; - const char *deny_key = is_advertisement ? "ic-route-deny-adv" : - "ic-route-deny-learn"; - - if (ts_lrp) { - const char *lrp_deny_filter = smap_get(&ts_lrp->options, deny_key); - if (lrp_deny_filter) { - ds_put_format(&deny_list, "%s,", lrp_deny_filter); - } - } - - if (nb_lr) { - const char *lr_deny_filter = smap_get(&nb_lr->options, deny_key); - if (lr_deny_filter) { - ds_put_format(&deny_list, "%s,", lr_deny_filter); - } - } - - if (nb_options) { - const char *global_deny = smap_get(nb_options, "ic-route-denylist"); - if (!global_deny || !global_deny[0]) { - global_deny = smap_get(nb_options, "ic-route-blacklist"); - } - if (global_deny && global_deny[0]) { - ds_put_format(&deny_list, "%s,", global_deny); - } - } - - struct sset prefix_set = SSET_INITIALIZER(&prefix_set); - sset_from_delimited_string(&prefix_set, ds_cstr(&deny_list), ","); - - bool denied = false; - if (!sset_is_empty(&prefix_set)) { - denied = find_prefix_in_set(prefix, plen, &prefix_set, deny_key); - } - - ds_destroy(&deny_list); - sset_destroy(&prefix_set); - return denied; -} - -static bool -route_need_advertise(const char *policy, - struct in6_addr *prefix, - unsigned int plen, - const struct smap *nb_options, - const struct nbrec_logical_router *nb_lr, - const struct nbrec_logical_router_port *ts_lrp) -{ - if (!smap_get_bool(nb_options, "ic-route-adv", false)) { - return false; - } - - if (plen == 0 && - !smap_get_bool(nb_options, "ic-route-adv-default", false)) { - return false; - } - - if (policy && !strcmp(policy, "src-ip")) { - return false; - } - - if (prefix_is_link_local(prefix, plen)) { - return false; - } - - if (prefix_is_deny_filtered(prefix, plen, nb_options, - nb_lr, ts_lrp, true)) { - return false; - } - - if (!prefix_is_filtered(prefix, plen, nb_lr, ts_lrp, true)) { - return false; - } - - return true; -} - -static void -add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix, - unsigned int plen, const struct in6_addr nexthop, - const char *origin, const char *route_table, - const struct nbrec_logical_router_port *nb_lrp, - const struct nbrec_logical_router_static_route *nb_route, - const struct nbrec_logical_router *nb_lr, - const struct nbrec_load_balancer *nb_lb, - const char *route_tag) -{ - ovs_assert(nb_route || nb_lrp || nb_lb); - - if (route_table == NULL) { - route_table = ""; - } - - uint hash = ic_route_hash(&prefix, plen, &nexthop, origin, route_table); - - if (!ic_route_find(routes_ad, &prefix, plen, &nexthop, origin, - route_table, hash)) { - struct ic_route_info *ic_route = xzalloc(sizeof *ic_route); - ic_route->prefix = prefix; - ic_route->plen = plen; - ic_route->nexthop = nexthop; - ic_route->nb_route = nb_route; - ic_route->origin = origin; - ic_route->route_table = route_table; - ic_route->nb_lrp = nb_lrp; - ic_route->nb_lr = nb_lr; - ic_route->nb_lb = nb_lb; - ic_route->route_tag = route_tag; - hmap_insert(routes_ad, &ic_route->node, hash); - } else { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - const char *msg_fmt = "Duplicate %s route advertisement was " - "suppressed! NB %s uuid: "UUID_FMT; - if (nb_route) { - VLOG_WARN_RL(&rl, msg_fmt, origin, "route", - UUID_ARGS(&nb_route->header_.uuid)); - } else if (nb_lb) { - VLOG_WARN_RL(&rl, msg_fmt, origin, "loadbalancer", - UUID_ARGS(&nb_lb->header_.uuid)); - } else { - VLOG_WARN_RL(&rl, msg_fmt, origin, "lrp", - UUID_ARGS(&nb_lrp->header_.uuid)); - } - } -} - -static void -add_static_to_routes_ad( - struct hmap *routes_ad, - const struct nbrec_logical_router_static_route *nb_route, - const struct nbrec_logical_router *nb_lr, - const struct lport_addresses *nexthop_addresses, - const struct smap *nb_options, - const char *route_tag, - const struct nbrec_logical_router_port *ts_lrp) -{ - struct in6_addr prefix, nexthop; - unsigned int plen; - if (!parse_route(nb_route->ip_prefix, nb_route->nexthop, - &prefix, &plen, &nexthop)) { - return; - } - - if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options, - nb_lr, ts_lrp)) { - return; - } - - if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix), - nexthop_addresses, - &nexthop)) { - return; - } - - if (VLOG_IS_DBG_ENABLED()) { - struct ds msg = DS_EMPTY_INITIALIZER; - - ds_put_format(&msg, "Advertising static route: %s -> %s, ic nexthop: ", - nb_route->ip_prefix, nb_route->nexthop); - - if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { - ds_put_format(&msg, IP_FMT, - IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); - } else { - ipv6_format_addr(&nexthop, &msg); - } - - ds_put_format(&msg, ", route_table: %s", nb_route->route_table[0] - ? nb_route->route_table - : "<main>"); - - VLOG_DBG("%s", ds_cstr(&msg)); - ds_destroy(&msg); - } - - add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_STATIC, - nb_route->route_table, NULL, nb_route, nb_lr, - NULL, route_tag); -} - -static void -add_network_to_routes_ad(struct hmap *routes_ad, const char *network, - const struct nbrec_logical_router_port *nb_lrp, - const struct lport_addresses *nexthop_addresses, - const struct smap *nb_options, - const struct nbrec_logical_router *nb_lr, - const char *route_tag, - const struct nbrec_logical_router_port *ts_lrp) -{ - struct in6_addr prefix, nexthop; - unsigned int plen; - if (!ip46_parse_cidr(network, &prefix, &plen)) { - return; - } - - if (!route_need_advertise(NULL, &prefix, plen, nb_options, - nb_lr, ts_lrp)) { - VLOG_DBG("Route ad: skip network %s of lrp %s.", - network, nb_lrp->name); - return; - } - - if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix), - nexthop_addresses, - &nexthop)) { - return; - } - - if (VLOG_IS_DBG_ENABLED()) { - struct ds msg = DS_EMPTY_INITIALIZER; - - ds_put_format(&msg, "Adding direct network route to <main> routing " - "table: %s of lrp %s, nexthop ", network, nb_lrp->name); - - if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { - ds_put_format(&msg, IP_FMT, - IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); - } else { - ipv6_format_addr(&nexthop, &msg); - } - - VLOG_DBG("%s", ds_cstr(&msg)); - ds_destroy(&msg); - } - - /* directly-connected routes go to <main> route table */ - add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_CONNECTED, - NULL, nb_lrp, NULL, nb_lr, NULL, route_tag); -} - -static void -add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key, - const struct nbrec_load_balancer *nb_lb, - const struct lport_addresses *nexthop_addresses, - const struct smap *nb_options, - const struct nbrec_logical_router *nb_lr, - const char *route_tag, - const struct nbrec_logical_router_port *ts_lrp) -{ - char *vip_str = NULL; - struct in6_addr vip_ip, nexthop; - uint16_t vip_port; - int addr_family; - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - - if (!ip_address_and_port_from_lb_key(vip_key, &vip_str, &vip_ip, - &vip_port, &addr_family)) { - VLOG_WARN_RL(&rl, "Route ad: Parsing failed for lb vip %s", vip_key); - return; - } - if (vip_str == NULL) { - return; - } - unsigned int plen = (addr_family == AF_INET) ? 32 : 128; - if (!route_need_advertise(NULL, &vip_ip, plen, nb_options, - nb_lr, ts_lrp)) { - VLOG_DBG("Route ad: skip lb vip %s.", vip_key); - goto out; - } - if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&vip_ip), - nexthop_addresses, - &nexthop)) { - VLOG_WARN_RL(&rl, "Route ad: failed to get nexthop for lb vip"); - goto out; - } - - if (VLOG_IS_DBG_ENABLED()) { - struct ds msg = DS_EMPTY_INITIALIZER; - - ds_put_format(&msg, "Adding lb vip route to <main> routing " - "table: %s, nexthop ", vip_str); - - if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { - ds_put_format(&msg, IP_FMT, - IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop))); - } else { - ipv6_format_addr(&nexthop, &msg); - } - - VLOG_DBG("%s", ds_cstr(&msg)); - ds_destroy(&msg); - } - - /* Lb vip routes go to <main> route table */ - add_to_routes_ad(routes_ad, vip_ip, plen, nexthop, ROUTE_ORIGIN_LB, - NULL, NULL, NULL, nb_lr, nb_lb, route_tag); -out: - free(vip_str); -} - -static bool -route_has_local_gw(const struct nbrec_logical_router *lr, - const char *route_table, const char *ip_prefix) { - - const struct nbrec_logical_router_static_route *route; - for (int i = 0; i < lr->n_static_routes; i++) { - route = lr->static_routes[i]; - if (!smap_get(&route->external_ids, "ic-learned-route") && - !strcmp(route->route_table, route_table) && - !strcmp(route->ip_prefix, ip_prefix)) { - return true; - } - } - return false; -} - -static bool -lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp, - struct in6_addr *nexthop) -{ - if (!lrp || !nexthop) { - return false; - } - - struct lport_addresses lrp_networks; - if (!extract_lrp_networks(lrp, &lrp_networks)) { - destroy_lport_addresses(&lrp_networks); - return false; - } - - if (IN6_IS_ADDR_V4MAPPED(nexthop)) { - ovs_be32 neigh_prefix_v4 = in6_addr_get_mapped_ipv4(nexthop); - for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) { - struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i]; - if (address.network == (neigh_prefix_v4 & address.mask)) { - destroy_lport_addresses(&lrp_networks); - return true; - } - } - } else { - for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) { - struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i]; - struct in6_addr neigh_prefix = ipv6_addr_bitand(nexthop, - &address.mask); - if (ipv6_addr_equals(&address.network, &neigh_prefix)) { - destroy_lport_addresses(&lrp_networks); - return true; - } - } - } - - destroy_lport_addresses(&lrp_networks); - return false; -} - -static bool -route_matches_local_lb(const struct nbrec_load_balancer *nb_lb, - const char *ip_prefix) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - struct in6_addr prefix; - unsigned int plen; - - if (!ip46_parse_cidr(ip_prefix, &prefix, &plen)) { - return false; - } - - struct smap_node *node; - SMAP_FOR_EACH (node, &nb_lb->vips) { - char *vip_str = NULL; - struct in6_addr vip_ip; - uint16_t vip_port; - int addr_family; - if (ip_address_and_port_from_lb_key(node->key, &vip_str, - &vip_ip, &vip_port, - &addr_family)) { - if (IN6_IS_ADDR_V4MAPPED(&prefix) && addr_family == AF_INET) { - ovs_be32 vip = in6_addr_get_mapped_ipv4(&vip_ip); - ovs_be32 mask = be32_prefix_mask(plen); - - if ((vip & mask) == in6_addr_get_mapped_ipv4(&prefix)) { - free(vip_str); - return true; - } - } else if (!IN6_IS_ADDR_V4MAPPED(&prefix) - && addr_family == AF_INET6) { - struct in6_addr mask = ipv6_create_mask(plen); - struct in6_addr vip_prefix = ipv6_addr_bitand(&vip_ip, &mask); - if (ipv6_addr_equals(&prefix, &vip_prefix)) { - free(vip_str); - return true; - } - } - free(vip_str); - } else { - VLOG_WARN_RL(&rl, - "Route learn: Parsing failed for local lb vip %s", - node->key); - } - } - return false; -} - -static bool -route_need_learn(const struct nbrec_logical_router *lr, - const struct icsbrec_route *isb_route, - struct in6_addr *prefix, unsigned int plen, - const struct smap *nb_options, - const struct nbrec_logical_router_port *ts_lrp, - struct in6_addr *nexthop) -{ - if (!smap_get_bool(nb_options, "ic-route-learn", false)) { - return false; - } - - if (plen == 0 && - !smap_get_bool(nb_options, "ic-route-learn-default", false)) { - return false; - } - - if (!strcmp(isb_route->origin, ROUTE_ORIGIN_LB) && - !smap_get_bool(nb_options, "ic-route-learn-lb", false)) { - return false; - } - - if (!lrouter_is_enabled(lr)) { - return false; - } - - if (prefix_is_link_local(prefix, plen)) { - return false; - } - - if (prefix_is_deny_filtered(prefix, plen, nb_options, lr, ts_lrp, false)) { - return false; - } - - if (!prefix_is_filtered(prefix, plen, lr, ts_lrp, false)) { - return false; - } - - if (route_has_local_gw(lr, isb_route->route_table, isb_route->ip_prefix)) { - VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got one with " - "local GW", isb_route->ip_prefix, isb_route->route_table); - return false; - } - - if (!lrp_has_neighbor_in_ts(ts_lrp, nexthop)) { - return false; - } - - for (size_t i = 0; i < lr->n_load_balancer; i++) { - if (route_matches_local_lb(lr->load_balancer[i], - isb_route->ip_prefix)) { - VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got local" - " LB with matching VIP", isb_route->ip_prefix, - isb_route->route_table); - return false; - } - } - for (size_t i = 0; i < lr->n_load_balancer_group; i++) { - const struct nbrec_load_balancer_group *nb_lbg = - lr->load_balancer_group[i]; - for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) { - if (route_matches_local_lb(nb_lbg->load_balancer[j], - isb_route->ip_prefix)) { - VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got local" - " LB with matching VIP", isb_route->ip_prefix, - isb_route->route_table); - return false; - } - } - } - - return true; -} - -static const char * -get_lrp_name_by_ts_port_name(struct ic_input *ic, const char *ts_port_name) -{ - const struct nbrec_logical_switch_port *nb_lsp; - - nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name, ts_port_name); - if (!nb_lsp) { - return NULL; - } - - return smap_get(&nb_lsp->options, "router-port"); -} - -static const struct nbrec_logical_router_port * -find_lrp_of_nexthop(struct ic_input *ic, - const struct icsbrec_route *isb_route) -{ - const struct nbrec_logical_router_port *lrp; - const struct nbrec_logical_switch *ls; - ls = find_ts_in_nb(ic->nbrec_ls_by_name, isb_route->transit_switch); - if (!ls) { - return NULL; - } - - struct in6_addr nexthop; - if (!ip46_parse(isb_route->nexthop, &nexthop)) { - return NULL; - } - - for (size_t i = 0; i < ls->n_ports; i++) { - char *lsp_name = ls->ports[i]->name; - const char *lrp_name = get_lrp_name_by_ts_port_name(ic, - lsp_name); - if (!lrp_name) { - continue; - } - - lrp = get_lrp_by_lrp_name(ic, lrp_name); - if (!lrp) { - continue; - } - - struct lport_addresses lrp_networks; - if (!extract_lrp_networks(lrp, &lrp_networks)) { - destroy_lport_addresses(&lrp_networks); - continue; - } - - if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { - ovs_be32 nexthop_v4 = in6_addr_get_mapped_ipv4(&nexthop); - for (size_t i_v4 = 0; i_v4 < lrp_networks.n_ipv4_addrs; i_v4++) { - struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i_v4]; - if (address.addr == nexthop_v4) { - destroy_lport_addresses(&lrp_networks); - return lrp; - } - } - } else { - for (size_t i_v6 = 0; i_v6 < lrp_networks.n_ipv6_addrs; i_v6++) { - struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i_v6]; - struct in6_addr nexthop_v6 = ipv6_addr_bitand(&nexthop, - &address.mask); - if (ipv6_addr_equals(&address.network, &nexthop_v6)) { - destroy_lport_addresses(&lrp_networks); - return lrp; - } - } - } - destroy_lport_addresses(&lrp_networks); - } - - return NULL; -} - -static bool -lrp_is_ts_port(struct ic_input *ic, struct ic_router_info *ic_lr, - const char *lrp_name) -{ - const struct icsbrec_port_binding *isb_pb; - const char *ts_lrp_name; - VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { - ts_lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); - if (!strcmp(ts_lrp_name, lrp_name)) { - return true; - } - } - return false; -} - -static void -sync_learned_routes(struct engine_context *ctx, - struct ic_input *ic, - struct ic_router_info *ic_lr) -{ - ovs_assert(ctx->ovnnb_idl_txn); - const struct icsbrec_route *isb_route, *isb_route_key; - - const struct nbrec_nb_global *nb_global = - nbrec_nb_global_table_first(ic->nbrec_nb_global_table); - ovs_assert(nb_global); - - const char *lrp_name, *ts_route_table, *route_filter_tag; - const struct icsbrec_port_binding *isb_pb; - const struct nbrec_logical_router_port *lrp; - VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { - if (!strcmp(isb_pb->address, "")) { - continue; - } - lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); - lrp = get_lrp_by_lrp_name(ic, lrp_name); - if (lrp) { - ts_route_table = smap_get_def(&lrp->options, "route_table", ""); - route_filter_tag = smap_get_def(&lrp->options, - "ic-route-filter-tag", ""); - } else { - ts_route_table = ""; - route_filter_tag = ""; - } - - isb_route_key = icsbrec_route_index_init_row(ic->icsbrec_route_by_ts); - icsbrec_route_index_set_transit_switch(isb_route_key, - isb_pb->transit_switch); - - ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, - ic->icsbrec_route_by_ts) { - /* Filters ICSB routes, skipping those that either belong to - * current logical router or are legacy routes from the current - * availability zone (withoud lr-id). - */ - const char *lr_id = smap_get(&isb_route->external_ids, "lr-id"); - struct uuid lr_uuid; - if (lr_id) { - if (!uuid_from_string(&lr_uuid, lr_id) - || uuid_equals(&ic_lr->lr->header_.uuid, &lr_uuid)) { - continue; - } - } else if (isb_route->availability_zone == ic->runned_az) { - continue; - } - - const char *isb_route_tag = smap_get(&isb_route->external_ids, - "ic-route-tag"); - if (isb_route_tag && !strcmp(isb_route_tag, route_filter_tag)) { - VLOG_DBG("Skip learning route %s -> %s as its route tag " - "[%s] is filtered by the filter tag [%s] of TS LRP ", - isb_route->ip_prefix, isb_route->nexthop, - isb_route_tag, route_filter_tag); - continue; - } - - if (isb_route->route_table[0] && - strcmp(isb_route->route_table, ts_route_table)) { - if (VLOG_IS_DBG_ENABLED()) { - VLOG_DBG("Skip learning static route %s -> %s as either " - "its route table %s != %s of TS port or ", - isb_route->ip_prefix, isb_route->nexthop, - isb_route->route_table, ts_route_table); - } - continue; - } - - struct in6_addr prefix, nexthop; - unsigned int plen; - if (!parse_route(isb_route->ip_prefix, isb_route->nexthop, - &prefix, &plen, &nexthop)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Bad route format in IC-SB: %s -> %s. " - "Ignored.", isb_route->ip_prefix, - isb_route->nexthop); - continue; - } - if (!route_need_learn(ic_lr->lr, isb_route, &prefix, plen, - &nb_global->options, lrp, &nexthop)) { - continue; - } - - struct ic_route_info *route_learned - = ic_route_find(&ic_lr->routes_learned, &prefix, plen, - &nexthop, isb_route->origin, - isb_route->route_table, 0); - if (route_learned) { - /* Sync external-ids */ - struct uuid ext_id; - smap_get_uuid(&route_learned->nb_route->external_ids, - "ic-learned-route", &ext_id); - if (!uuid_equals(&ext_id, &isb_route->header_.uuid)) { - char *uuid_s = - xasprintf(UUID_FMT, - UUID_ARGS(&isb_route->header_.uuid)); - nbrec_logical_router_static_route_update_external_ids_setkey( - route_learned->nb_route, "ic-learned-route", uuid_s); - free(uuid_s); - } - hmap_remove(&ic_lr->routes_learned, &route_learned->node); - free(route_learned); - } else { - /* Create the missing route in NB. */ - const struct nbrec_logical_router_static_route *nb_route = - nbrec_logical_router_static_route_insert( - ctx->ovnnb_idl_txn); - nbrec_logical_router_static_route_set_ip_prefix(nb_route, - isb_route->ip_prefix); - nbrec_logical_router_static_route_set_nexthop(nb_route, - isb_route->nexthop); - char *uuid_s = xasprintf(UUID_FMT, - UUID_ARGS(&isb_route->header_.uuid)); - nbrec_logical_router_static_route_set_route_table(nb_route, - isb_route->route_table); - nbrec_logical_router_static_route_update_external_ids_setkey( - nb_route, "ic-learned-route", uuid_s); - nbrec_logical_router_static_route_update_options_setkey( - nb_route, "origin", isb_route->origin); - free(uuid_s); - nbrec_logical_router_update_static_routes_addvalue(ic_lr->lr, - nb_route); - } - } - icsbrec_route_index_destroy_row(isb_route_key); - } - - /* Delete extra learned routes. */ - struct ic_route_info *route_learned; - HMAP_FOR_EACH_SAFE (route_learned, node, &ic_lr->routes_learned) { - VLOG_DBG("Delete route %s -> %s that is not in IC-SB from NB.", - route_learned->nb_route->ip_prefix, - route_learned->nb_route->nexthop); - nbrec_logical_router_update_static_routes_delvalue( - ic_lr->lr, route_learned->nb_route); - hmap_remove(&ic_lr->routes_learned, &route_learned->node); - free(route_learned); - } -} - -static void -ad_route_sync_external_ids(const struct ic_route_info *route_adv, - const struct icsbrec_route *isb_route) -{ - struct uuid isb_ext_id, nb_id, isb_ext_lr_id, lr_id; - const char *route_tag; - smap_get_uuid(&isb_route->external_ids, "nb-id", &isb_ext_id); - smap_get_uuid(&isb_route->external_ids, "lr-id", &isb_ext_lr_id); - nb_id = route_adv->nb_lb ? route_adv->nb_lb->header_.uuid : - route_adv->nb_route ? route_adv->nb_route->header_.uuid : - route_adv->nb_lrp->header_.uuid; - - lr_id = route_adv->nb_lr->header_.uuid; - if (!uuid_equals(&isb_ext_id, &nb_id)) { - char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&nb_id)); - icsbrec_route_update_external_ids_setkey(isb_route, "nb-id", - uuid_s); - free(uuid_s); - } - if (!uuid_equals(&isb_ext_lr_id, &lr_id)) { - char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_id)); - icsbrec_route_update_external_ids_setkey(isb_route, "lr-id", - uuid_s); - free(uuid_s); - } - if (strcmp(route_adv->route_tag, "")) { - icsbrec_route_update_external_ids_setkey(isb_route, "ic-route-tag", - route_adv->route_tag); - } else { - route_tag = smap_get(&isb_route->external_ids, "ic-route-tag"); - if (route_tag) { - icsbrec_route_update_external_ids_delkey(isb_route, - "ic-route-tag"); - } - } -} - -/* Sync routes from routes_ad to IC-SB. */ -static void -advertise_routes(struct engine_context *ctx, - struct ic_input *ic, - const struct icsbrec_availability_zone *az, - const char *ts_name, - struct hmap *routes_ad) -{ - ovs_assert(ctx->ovnisb_idl_txn); - const struct icsbrec_route *isb_route; - const struct icsbrec_route *isb_route_key = - icsbrec_route_index_init_row(ic->icsbrec_route_by_ts_az); - icsbrec_route_index_set_transit_switch(isb_route_key, ts_name); - icsbrec_route_index_set_availability_zone(isb_route_key, az); - - ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, - ic->icsbrec_route_by_ts_az) { - struct in6_addr prefix, nexthop; - unsigned int plen; - - if (!parse_route(isb_route->ip_prefix, isb_route->nexthop, - &prefix, &plen, &nexthop)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Bad route format in IC-SB: %s -> %s. " - "Delete it.", - isb_route->ip_prefix, isb_route->nexthop); - icsbrec_route_delete(isb_route); - continue; - } - struct ic_route_info *route_adv = - ic_route_find(routes_ad, &prefix, plen, &nexthop, - isb_route->origin, isb_route->route_table, 0); - if (!route_adv) { - /* Delete the extra route from IC-SB. */ - VLOG_DBG("Delete route %s -> %s from IC-SB, which is not found" - " in local routes to be advertised.", - isb_route->ip_prefix, isb_route->nexthop); - icsbrec_route_delete(isb_route); - } else { - ad_route_sync_external_ids(route_adv, isb_route); - - hmap_remove(routes_ad, &route_adv->node); - free(route_adv); - } - } - icsbrec_route_index_destroy_row(isb_route_key); - - /* Create the missing routes in IC-SB */ - struct ic_route_info *route_adv; - HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) { - isb_route = icsbrec_route_insert(ctx->ovnisb_idl_txn); - icsbrec_route_set_transit_switch(isb_route, ts_name); - icsbrec_route_set_availability_zone(isb_route, az); - - char *prefix_s, *nexthop_s; - if (IN6_IS_ADDR_V4MAPPED(&route_adv->prefix)) { - ovs_be32 ipv4 = in6_addr_get_mapped_ipv4(&route_adv->prefix); - ovs_be32 nh = in6_addr_get_mapped_ipv4(&route_adv->nexthop); - prefix_s = xasprintf(IP_FMT "/%d", IP_ARGS(ipv4), route_adv->plen); - nexthop_s = xasprintf(IP_FMT, IP_ARGS(nh)); - } else { - char network_s[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &route_adv->prefix, network_s, - INET6_ADDRSTRLEN); - prefix_s = xasprintf("%s/%d", network_s, route_adv->plen); - inet_ntop(AF_INET6, &route_adv->nexthop, network_s, - INET6_ADDRSTRLEN); - nexthop_s = xstrdup(network_s); - } - icsbrec_route_set_ip_prefix(isb_route, prefix_s); - icsbrec_route_set_nexthop(isb_route, nexthop_s); - icsbrec_route_set_origin(isb_route, route_adv->origin); - icsbrec_route_set_route_table(isb_route, route_adv->route_table - ? route_adv->route_table - : ""); - free(prefix_s); - free(nexthop_s); - - ad_route_sync_external_ids(route_adv, isb_route); - - hmap_remove(routes_ad, &route_adv->node); - free(route_adv); - } -} - -static void -build_ts_routes_to_adv(struct ic_input *ic, - struct ic_router_info *ic_lr, - struct hmap *routes_ad, - struct lport_addresses *ts_port_addrs, - const struct nbrec_nb_global *nb_global, - const char *ts_route_table, - const char *route_tag, - const struct nbrec_logical_router_port *ts_lrp) -{ - const struct nbrec_logical_router *lr = ic_lr->lr; - - /* Check static routes of the LR */ - for (int i = 0; i < lr->n_static_routes; i++) { - const struct nbrec_logical_router_static_route *nb_route - = lr->static_routes[i]; - struct uuid isb_uuid; - if (smap_get_uuid(&nb_route->external_ids, "ic-learned-route", - &isb_uuid)) { - /* It is a learned route */ - if (!add_to_routes_learned(&ic_lr->routes_learned, nb_route, lr)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Bad format of learned route in NB: " - "%s -> %s. Delete it.", nb_route->ip_prefix, - nb_route->nexthop); - nbrec_logical_router_update_static_routes_delvalue(lr, - nb_route); - } - } else if (!strcmp(ts_route_table, nb_route->route_table)) { - /* It may be a route to be advertised */ - add_static_to_routes_ad(routes_ad, nb_route, lr, ts_port_addrs, - &nb_global->options, route_tag, ts_lrp); - } - } - - /* Check directly-connected subnets of the LR */ - for (int i = 0; i < lr->n_ports; i++) { - const struct nbrec_logical_router_port *lrp = lr->ports[i]; - if (!lrp_is_ts_port(ic, ic_lr, lrp->name)) { - for (int j = 0; j < lrp->n_networks; j++) { - add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp, - ts_port_addrs, - &nb_global->options, - lr, route_tag, ts_lrp); - } - } else { - /* The router port of the TS port is ignored. */ - VLOG_DBG("Skip advertising direct route of lrp %s (TS port)", - lrp->name); - } - } - - /* Check loadbalancers associated with the LR */ - if (smap_get_bool(&nb_global->options, "ic-route-adv-lb", false)) { - for (size_t i = 0; i < lr->n_load_balancer; i++) { - const struct nbrec_load_balancer *nb_lb = lr->load_balancer[i]; - struct smap_node *node; - SMAP_FOR_EACH (node, &nb_lb->vips) { - add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb, - ts_port_addrs, - &nb_global->options, - lr, route_tag, ts_lrp); - } - } - - for (size_t i = 0; i < lr->n_load_balancer_group; i++) { - const struct nbrec_load_balancer_group *nb_lbg = - lr->load_balancer_group[i]; - for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) { - const struct nbrec_load_balancer *nb_lb = - nb_lbg->load_balancer[j]; - struct smap_node *node; - SMAP_FOR_EACH (node, &nb_lb->vips) { - add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb, - ts_port_addrs, - &nb_global->options, - lr, route_tag, ts_lrp); - } - } - } - } -} - -static void -collect_lr_routes(struct ic_input *ic, - struct ic_router_info *ic_lr, - struct shash *routes_ad_by_ts) -{ - const struct nbrec_nb_global *nb_global = - nbrec_nb_global_table_first(ic->nbrec_nb_global_table); - - ovs_assert(nb_global); - - const struct icsbrec_port_binding *isb_pb; - const char *lrp_name, *ts_name, *route_table, *route_tag; - struct lport_addresses ts_port_addrs; - const struct icnbrec_transit_switch *key; - const struct nbrec_logical_router_port *lrp; - - struct hmap *routes_ad; - const struct icnbrec_transit_switch *t_sw; - VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) { - key = icnbrec_transit_switch_index_init_row( - ic->icnbrec_transit_switch_by_name); - icnbrec_transit_switch_index_set_name(key, isb_pb->transit_switch); - t_sw = icnbrec_transit_switch_index_find( - ic->icnbrec_transit_switch_by_name, key); - icnbrec_transit_switch_index_destroy_row(key); - if (!t_sw) { - continue; - } - ts_name = t_sw->name; - routes_ad = shash_find_data(routes_ad_by_ts, ts_name); - if (!routes_ad) { - routes_ad = xzalloc(sizeof *routes_ad); - hmap_init(routes_ad); - shash_add(routes_ad_by_ts, ts_name, routes_ad); - } - - if (!extract_lsp_addresses(isb_pb->address, &ts_port_addrs)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_INFO_RL(&rl, "Route sync ignores port %s on ts %s for router" - " %s because the addresses are invalid.", - isb_pb->logical_port, isb_pb->transit_switch, - ic_lr->lr->name); - continue; - } - lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); - lrp = get_lrp_by_lrp_name(ic, lrp_name); - if (lrp) { - route_table = smap_get_def(&lrp->options, "route_table", ""); - route_tag = smap_get_def(&lrp->options, "ic-route-tag", ""); - } else { - route_table = ""; - route_tag = ""; - } - build_ts_routes_to_adv(ic, ic_lr, routes_ad, &ts_port_addrs, - nb_global, route_table, route_tag, lrp); - destroy_lport_addresses(&ts_port_addrs); - } -} - -static void -delete_orphan_ic_routes(struct ic_input *ic, - const struct icsbrec_availability_zone *az) -{ - const struct icsbrec_route *isb_route, *isb_route_key = - icsbrec_route_index_init_row(ic->icsbrec_route_by_az); - icsbrec_route_index_set_availability_zone(isb_route_key, az); - - const struct icnbrec_transit_switch *t_sw, *t_sw_key; - - ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, - ic->icsbrec_route_by_az) - { - t_sw_key = icnbrec_transit_switch_index_init_row( - ic->icnbrec_transit_switch_by_name); - icnbrec_transit_switch_index_set_name(t_sw_key, - isb_route->transit_switch); - t_sw = icnbrec_transit_switch_index_find( - ic->icnbrec_transit_switch_by_name, t_sw_key); - icnbrec_transit_switch_index_destroy_row(t_sw_key); - - if (!t_sw || !find_lrp_of_nexthop(ic, isb_route)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_INFO_RL(&rl, "Deleting orphan ICDB:Route: %s->%s (%s, rtb:%s," - " transit switch: %s)", isb_route->ip_prefix, - isb_route->nexthop, isb_route->origin, - isb_route->route_table, isb_route->transit_switch); - icsbrec_route_delete(isb_route); - } - } - icsbrec_route_index_destroy_row(isb_route_key); -} - -static void -route_run(struct engine_context *ctx, - struct ic_input *ic) -{ - if (!ctx->ovnisb_idl_txn || !ctx->ovnnb_idl_txn) { - return; - } - - delete_orphan_ic_routes(ic, ic->runned_az); - - struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs); - const struct icsbrec_port_binding *isb_pb; - const struct icsbrec_port_binding *isb_pb_key = - icsbrec_port_binding_index_init_row(ic->icsbrec_port_binding_by_az); - icsbrec_port_binding_index_set_availability_zone(isb_pb_key, - ic->runned_az); - - /* Each port on TS maps to a logical router, which is stored in the - * external_ids:router-id of the IC SB port_binding record. - * Here we build info for interconnected Logical Router: - * collect IC Port Binding to process routes sync later on. */ - ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, - ic->icsbrec_port_binding_by_az) - { - if (ic_pb_get_type(isb_pb) == IC_ROUTER_PORT) { - continue; - } - const struct nbrec_logical_switch_port *nb_lsp; - - nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name, - isb_pb->logical_port); - if (!strcmp(nb_lsp->type, "switch")) { - VLOG_DBG("IC-SB Port_Binding '%s' on ts '%s' corresponds to a " - "switch port, not considering for route collection.", - isb_pb->logical_port, isb_pb->transit_switch); - continue; - } - - const char *ts_lrp_name = - get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port); - if (!ts_lrp_name) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Route sync ignores port %s on ts %s because " - "logical router port is not found in NB. Deleting it", - isb_pb->logical_port, isb_pb->transit_switch); - icsbrec_port_binding_delete(isb_pb); - continue; - } - - struct uuid lr_uuid; - if (!smap_get_uuid(&isb_pb->external_ids, "router-id", &lr_uuid)) { - VLOG_DBG("IC-SB Port_Binding %s doesn't have " - "external_ids:router-id set.", isb_pb->logical_port); - continue; - } - - const struct nbrec_logical_router *lr - = nbrec_logical_router_table_get_for_uuid( - ic->nbrec_logical_router_table, &lr_uuid); - if (!lr) { - continue; - } - - struct ic_router_info *ic_lr = ic_router_find(&ic_lrs, lr); - if (!ic_lr) { - ic_lr = xzalloc(sizeof *ic_lr); - ic_lr->lr = lr; - ic_lr->isb_pbs = - VECTOR_EMPTY_INITIALIZER(const struct icsbrec_port_binding *); - hmap_init(&ic_lr->routes_learned); - hmap_insert(&ic_lrs, &ic_lr->node, uuid_hash(&lr->header_.uuid)); - } - vector_push(&ic_lr->isb_pbs, &isb_pb); - } - icsbrec_port_binding_index_destroy_row(isb_pb_key); - - struct ic_router_info *ic_lr; - struct shash routes_ad_by_ts = SHASH_INITIALIZER(&routes_ad_by_ts); - HMAP_FOR_EACH_SAFE (ic_lr, node, &ic_lrs) { - collect_lr_routes(ic, ic_lr, &routes_ad_by_ts); - sync_learned_routes(ctx, ic, ic_lr); - vector_destroy(&ic_lr->isb_pbs); - hmap_destroy(&ic_lr->routes_learned); - hmap_remove(&ic_lrs, &ic_lr->node); - free(ic_lr); - } - struct shash_node *node; - SHASH_FOR_EACH (node, &routes_ad_by_ts) { - advertise_routes(ctx, ic, ic->runned_az, node->name, node->data); - hmap_destroy(node->data); - } - shash_destroy_free_data(&routes_ad_by_ts); - hmap_destroy(&ic_lrs); -} - /* * Data structures and functions related to * synchronize health checks for load balancers @@ -2485,7 +1195,6 @@ ovn_db_run(struct ic_input *input_data, gateway_run(eng_ctx, input_data); ts_run(eng_ctx, input_data, ic_data->dp_tnlids, ic_data->isb_ts_dps); tr_run(eng_ctx, input_data, ic_data->dp_tnlids, ic_data->isb_tr_dps); - route_run(eng_ctx, input_data); sync_service_monitor(eng_ctx, input_data); } @@ -2891,6 +1600,7 @@ main(int argc, char *argv[]) stopwatch_create(IC_OVN_DB_RUN_STOPWATCH_NAME, SW_MS); stopwatch_create(OVN_IC_ENUM_DATAPATHS_RUN_STOPWATCH_NAME, SW_MS); stopwatch_create(OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME, SW_MS); + stopwatch_create(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, SW_MS); /* Initialize incremental processing engine for ovn-northd */ inc_proc_ic_init(&ovnnb_idl_loop, &ovnsb_idl_loop, diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h index ff6317786..bc4a1e6e0 100644 --- a/ic/ovn-ic.h +++ b/ic/ovn-ic.h @@ -22,7 +22,6 @@ struct ic_input { /* Northbound table references */ const struct nbrec_logical_switch_table *nbrec_logical_switch_table; const struct nbrec_logical_router_table *nbrec_logical_router_table; - const struct nbrec_nb_global_table *nbrec_nb_global_table; /* Southbound table references */ const struct sbrec_chassis_table *sbrec_chassis_table; @@ -54,10 +53,6 @@ struct ic_input { struct ovsdb_idl_index *sbrec_service_monitor_by_ic_learned; struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type_logical_port; struct ovsdb_idl_index *icnbrec_transit_switch_by_name; - struct ovsdb_idl_index *icsbrec_port_binding_by_az; - struct ovsdb_idl_index *icsbrec_route_by_az; - struct ovsdb_idl_index *icsbrec_route_by_ts; - struct ovsdb_idl_index *icsbrec_route_by_ts_az; struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az; struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az; struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az_logical_port; @@ -80,7 +75,8 @@ enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX }; enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX }; const struct nbrec_logical_router_port * - get_lrp_by_lrp_name(struct ic_input *ic, const char *lrp_name); +get_lrp_by_lrp_name(struct ovsdb_idl_index *nbrec_lrp_by_name, + const char *lrp_name); const struct sbrec_port_binding * find_sb_pb_by_name( struct ovsdb_idl_index *sbrec_port_binding_by_name, const char *name); const struct nbrec_logical_switch * diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h index bf5da699c..c277d4056 100644 --- a/lib/stopwatch-names.h +++ b/lib/stopwatch-names.h @@ -44,5 +44,6 @@ #define IC_OVN_DB_RUN_STOPWATCH_NAME "ovn_db_run" #define OVN_IC_ENUM_DATAPATHS_RUN_STOPWATCH_NAME "enum_datapaths_run" #define OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME "port_binding_run" +#define OVN_IC_ROUTE_RUN_STOPWATCH_NAME "route_run" #endif -- 2.34.1 -- _'Esta mensagem é direcionada apenas para os endereços constantes no cabeçalho inicial. Se você não está listado nos endereços constantes no cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão imediatamente anuladas e proibidas'._ * **'Apesar do Magazine Luiza tomar todas as precauções razoáveis para assegurar que nenhum vírus esteja presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por quaisquer perdas ou danos causados por esse e-mail ou por seus anexos'.* _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
