On Sun, Sep 19, 2021 at 5:24 PM Vladislav Odintsov <[email protected]> wrote: > > Previously support for multiple routing tables was added > to northd code. > This commit expands support for multiple routing tables > by adding support of advertising and learning routes with > their routing table information. > > To utilize such feature, user must: > 1. create Logical Router in each AZ; > 2. create IC transit switch for each routing table, that > he/she needs; > 3. connect each TS with this LR; > 4. assign routing table for TS's LRP > (ovn-nbctl lrp-set-options <lrp> route_table=<>); > 5. enable routes sync (turn on learning and advertising > routes in NB_Global table); > 6. create LRPs for subnets in LR, create static routes > with supplying route_table parameter. > > Note 1: routes for directly-connected networks will be > learned to global routing table and if Logical Routers > have more than one Transit Switch, which interconnects > them, directly-connected routes will be added via each > transit switch port and configured as ECMP routes. > > Note 2: static routes within route tables will be advertised > and learned only if interconnecting transit switch's LRPs > will have options:route_table same value as route's route_table > value. > > Signed-off-by: Vladislav Odintsov <[email protected]>
The patch LGTM. I'm not comfortable giving Acked-by just because I'm not too familiar with the code base. I'll defer it to Han for that. Reviewed-by: Numan Siddique <[email protected]> Numan > --- > NEWS | 4 + > ic/ovn-ic.c | 534 ++++++++++++++++++++++++++++---------------- > ovn-ic-sb.ovsschema | 5 +- > ovn-ic-sb.xml | 18 ++ > tests/ovn-ic.at | 440 ++++++++++++++++++++++++++++++++++++ > 5 files changed, 808 insertions(+), 193 deletions(-) > > diff --git a/NEWS b/NEWS > index 8a21c029e..3855f0d48 100644 > --- a/NEWS > +++ b/NEWS > @@ -12,6 +12,10 @@ OVN v21.09.0 - xx xxx xxxx > - Allow static routes without nexthops. > - Enabled logical dp groups as a default. CMS should disable it if not > desired. > + - Added support for multiple routing tables in Logical Router Static Routes > + and LRPs. OVN Interconnection supports routes' route tables as well. > + This requires to update schemas for OVN_Northdbound and OVN_IC_Southbound > + DBs. > > OVN v21.06.0 - 18 Jun 2021 > ------------------------- > diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c > index 92c83d730..94f123f70 100644 > --- a/ic/ovn-ic.c > +++ b/ic/ovn-ic.c > @@ -63,9 +63,11 @@ struct ic_context { > struct ovsdb_idl_txn *ovninb_txn; > struct ovsdb_idl_txn *ovnisb_txn; > struct ovsdb_idl_index *nbrec_ls_by_name; > + struct ovsdb_idl_index *nbrec_lrp_by_name; > struct ovsdb_idl_index *nbrec_port_by_name; > struct ovsdb_idl_index *sbrec_chassis_by_name; > struct ovsdb_idl_index *sbrec_port_binding_by_name; > + struct ovsdb_idl_index *icnbrec_transit_switch_by_name; > struct ovsdb_idl_index *icsbrec_port_binding_by_az; > struct ovsdb_idl_index *icsbrec_port_binding_by_ts; > struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az; > @@ -773,7 +775,7 @@ port_binding_run(struct ic_context *ctx, > icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name); > > ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, > - ctx->icsbrec_port_binding_by_ts) > { > + > ctx->icsbrec_port_binding_by_ts) { > if (isb_pb->availability_zone == az) { > shash_add(&local_pbs, isb_pb->logical_port, isb_pb); > shash_find_and_delete(&isb_all_local_pbs, > @@ -844,7 +846,9 @@ port_binding_run(struct ic_context *ctx, > struct ic_router_info { > struct hmap_node node; > const struct nbrec_logical_router *lr; /* key of hmap */ > - const struct icsbrec_port_binding *isb_pb; > + const struct icsbrec_port_binding **isb_pbs; > + size_t n_isb_pbs; > + size_t n_allocated_isb_pbs; > struct hmap routes_learned; > }; > > @@ -854,6 +858,7 @@ struct ic_route_info { > struct in6_addr prefix; > unsigned int plen; > struct in6_addr nexthop; > + const char *route_table; > > /* Either nb_route or nb_lrp is set and the other one must be NULL. > * - For a route that is learned from IC-SB, or a static route that is > @@ -875,13 +880,15 @@ ic_route_hash(const struct in6_addr *prefix, unsigned > int plen, > > static struct ic_route_info * > ic_route_find(struct hmap *routes, const struct in6_addr *prefix, > - unsigned int plen, const struct in6_addr *nexthop) > + unsigned int plen, const struct in6_addr *nexthop, > + char *route_table) > { > struct ic_route_info *r; > uint32_t hash = ic_route_hash(prefix, plen, nexthop); > HMAP_FOR_EACH_WITH_HASH (r, node, hash, routes) { > if (ipv6_addr_equals(&r->prefix, prefix) && > r->plen == plen && > + !strcmp(r->route_table ? r->route_table : "", route_table) && > ipv6_addr_equals(&r->nexthop, nexthop)) { > return r; > } > @@ -926,11 +933,19 @@ add_to_routes_learned(struct hmap *routes_learned, > &prefix, &plen, &nexthop)) { > return false; > } > + > + if (ic_route_find(routes_learned, &prefix, plen, &nexthop, > + nb_route->route_table)) { > + /* Route is already added to learned in 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->route_table = nb_route->route_table; > hmap_insert(routes_learned, &ic_route->node, > ic_route_hash(&prefix, plen, &nexthop)); > return true; > @@ -1069,8 +1084,17 @@ static void > add_to_routes_ad(struct hmap *routes_ad, > const struct nbrec_logical_router_static_route *nb_route, > const struct lport_addresses *nexthop_addresses, > - const struct smap *nb_options) > + const struct smap *nb_options, const char *route_table) > { > + if (strcmp(route_table, nb_route->route_table)) { > + if (VLOG_IS_DBG_ENABLED()) { > + VLOG_DBG("Skip advertising route %s -> %s as its route table %s > !=" > + " %s of TS port", nb_route->ip_prefix, > nb_route->nexthop, > + nb_route->route_table, route_table); > + } > + return; > + } > + > struct in6_addr prefix, nexthop; > unsigned int plen; > if (!parse_route(nb_route->ip_prefix, nb_route->nexthop, > @@ -1088,11 +1112,33 @@ add_to_routes_ad(struct hmap *routes_ad, > 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", > strlen(nb_route->route_table) > + ? nb_route->route_table > + : "global"); > + > + VLOG_DBG("%s", ds_cstr(&msg)); > + ds_destroy(&msg); > + } > + > 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->route_table = nb_route->route_table; > hmap_insert(routes_ad, &ic_route->node, > ic_route_hash(&prefix, plen, &nexthop)); > } > @@ -1124,8 +1170,8 @@ add_network_to_routes_ad(struct hmap *routes_ad, const > char *network, > if (VLOG_IS_DBG_ENABLED()) { > struct ds msg = DS_EMPTY_INITIALIZER; > > - ds_put_format(&msg, "Route ad: direct network %s of lrp %s, nexthop > ", > - network, nb_lrp->name); > + ds_put_format(&msg, "Adding direct network route to global routing " > + "table: %s of lrp %s, nexthop ", network, > nb_lrp->name); > > if (IN6_IS_ADDR_V4MAPPED(&nexthop)) { > ds_put_format(&msg, IP_FMT, > @@ -1143,13 +1189,15 @@ add_network_to_routes_ad(struct hmap *routes_ad, > const char *network, > ic_route->plen = plen; > ic_route->nexthop = nexthop; > ic_route->nb_lrp = nb_lrp; > + > + /* directly-connected routes go to global route table */ > + ic_route->route_table = NULL; > hmap_insert(routes_ad, &ic_route->node, > ic_route_hash(&prefix, plen, &nexthop)); > } > > static bool > -route_need_learn(struct in6_addr *prefix, > - unsigned int plen, > +route_need_learn(struct in6_addr *prefix, unsigned int plen, > const struct smap *nb_options) > { > if (!smap_get_bool(nb_options, "ic-route-learn", false)) { > @@ -1172,70 +1220,147 @@ route_need_learn(struct in6_addr *prefix, > return true; > } > > +static const char * > +get_lrp_name_by_ts_port_name(struct ic_context *ctx, const char > *ts_port_name) > +{ > + const struct nbrec_logical_switch_port *nb_lsp; > + const struct nbrec_logical_switch_port *nb_lsp_key = > + nbrec_logical_switch_port_index_init_row(ctx->nbrec_port_by_name); > + nbrec_logical_switch_port_index_set_name(nb_lsp_key, ts_port_name); > + nb_lsp = nbrec_logical_switch_port_index_find(ctx->nbrec_port_by_name, > + nb_lsp_key); > + nbrec_logical_switch_port_index_destroy_row(nb_lsp_key); > + > + if (!nb_lsp) { > + return NULL; > + } > + > + return smap_get(&nb_lsp->options, "router-port"); > +} > + > +static const char * > +get_route_table_by_lrp_name(struct ic_context *ctx, 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(ctx->nbrec_lrp_by_name); > + nbrec_logical_router_port_index_set_name(lrp_key, lrp_name); > + lrp = nbrec_logical_router_port_index_find(ctx->nbrec_lrp_by_name, > + lrp_key); > + nbrec_logical_router_port_index_destroy_row(lrp_key); > + > + if (lrp) { > + return smap_get_def(&lrp->options, "route_table", ""); > + } > + return ""; /* Global route table */ > +} > + > +static bool > +lrp_is_ts_port(struct ic_context *ctx, struct ic_router_info *ic_lr, > + const char *lrp_name) > +{ > + const struct icsbrec_port_binding *isb_pb; > + const char *ts_lrp_name; > + for (int i = 0; i < ic_lr->n_isb_pbs; i++) { > + isb_pb = ic_lr->isb_pbs[i]; > + ts_lrp_name = get_lrp_name_by_ts_port_name(ctx, > isb_pb->logical_port); > + if (!strcmp(ts_lrp_name, lrp_name)) { > + return true; > + } > + } > + return false; > +} > + > static void > -sync_learned_route(struct ic_context *ctx, > - const struct icsbrec_availability_zone *az, > - struct ic_router_info *ic_lr) > +sync_learned_routes(struct ic_context *ctx, > + const struct icsbrec_availability_zone *az, > + struct ic_router_info *ic_lr) > { > ovs_assert(ctx->ovnnb_txn); > const struct icsbrec_route *isb_route; > const struct icsbrec_route *isb_route_key = > icsbrec_route_index_init_row(ctx->icsbrec_route_by_ts); > > - icsbrec_route_index_set_transit_switch(isb_route_key, > - ic_lr->isb_pb->transit_switch); > + const struct nbrec_nb_global *nb_global = > + nbrec_nb_global_first(ctx->ovnnb_idl); > + ovs_assert(nb_global); > > - ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, > - ctx->icsbrec_route_by_ts) { > - if (isb_route->availability_zone == az) { > - 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; > - } > - const struct nbrec_nb_global *nb_global = > - nbrec_nb_global_first(ctx->ovnnb_idl); > - ovs_assert(nb_global); > - if (!route_need_learn(&prefix, plen, &nb_global->options)) { > - continue; > - } > - struct ic_route_info *route_learned > - = ic_route_find(&ic_lr->routes_learned, &prefix, plen, &nexthop); > - 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)) { > + const char *lrp_name, *ts_route_table; > + const struct icsbrec_port_binding *isb_pb; > + for (int i = 0; i < ic_lr->n_isb_pbs; i++) { > + isb_pb = ic_lr->isb_pbs[i]; > + lrp_name = get_lrp_name_by_ts_port_name(ctx, isb_pb->logical_port); > + ts_route_table = get_route_table_by_lrp_name(ctx, lrp_name); > + > + icsbrec_route_index_set_transit_switch(isb_route_key, > + isb_pb->transit_switch); > + > + ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key, > + ctx->icsbrec_route_by_ts) { > + if (isb_route->availability_zone == az) { > + continue; > + } > + > + if (strlen(isb_route->route_table) && > + 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(&prefix, plen, &nb_global->options)) { > + continue; > + } > + struct ic_route_info *route_learned > + = ic_route_find(&ic_lr->routes_learned, &prefix, plen, > + &nexthop, isb_route->route_table); > + 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_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( > - route_learned->nb_route, "ic-learned-route", uuid_s); > + nb_route, "ic-learned-route", uuid_s); > free(uuid_s); > + nbrec_logical_router_update_static_routes_addvalue(ic_lr->lr, > + nb_route); > } > - 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_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_update_external_ids_setkey( > - nb_route, "ic-learned-route", uuid_s); > - free(uuid_s); > - nbrec_logical_router_update_static_routes_addvalue( > - ic_lr->lr, nb_route); > } > } > icsbrec_route_index_destroy_row(isb_route_key); > @@ -1271,10 +1396,10 @@ ad_route_sync_external_ids(const struct ic_route_info > *route_adv, > > /* Sync routes from routes_ad to IC-SB. */ > static void > -advertise_route(struct ic_context *ctx, > - const struct icsbrec_availability_zone *az, > - const char *ts_name, > - struct hmap *routes_ad) > +advertise_routes(struct ic_context *ctx, > + const struct icsbrec_availability_zone *az, > + const char *ts_name, > + struct hmap *routes_ad) > { > ovs_assert(ctx->ovnisb_txn); > const struct icsbrec_route *isb_route; > @@ -1298,7 +1423,8 @@ advertise_route(struct ic_context *ctx, > continue; > } > struct ic_route_info *route_adv = > - ic_route_find(routes_ad, &prefix, plen, &nexthop); > + ic_route_find(routes_ad, &prefix, plen, &nexthop, > + isb_route->route_table); > if (!route_adv) { > /* Delete the extra route from IC-SB. */ > VLOG_DBG("Delete route %s -> %s from IC-SB, which is not found" > @@ -1338,6 +1464,9 @@ advertise_route(struct ic_context *ctx, > } > icsbrec_route_set_ip_prefix(isb_route, prefix_s); > icsbrec_route_set_nexthop(isb_route, nexthop_s); > + icsbrec_route_set_route_table(isb_route, route_adv->route_table > + ? route_adv->route_table > + : ""); > free(prefix_s); > free(nexthop_s); > > @@ -1348,23 +1477,97 @@ advertise_route(struct ic_context *ctx, > } > } > > -static const char * > -get_lrp_name_by_ts_port_name(struct ic_context *ctx, > - const char *ts_port_name) > +static void > +build_ts_routes_to_adv(struct ic_context *ctx, > + 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 struct nbrec_logical_switch_port *nb_lsp; > - const struct nbrec_logical_switch_port *nb_lsp_key = > - nbrec_logical_switch_port_index_init_row(ctx->nbrec_port_by_name); > - nbrec_logical_switch_port_index_set_name(nb_lsp_key, ts_port_name); > - nb_lsp = nbrec_logical_switch_port_index_find(ctx->nbrec_port_by_name, > - nb_lsp_key); > - nbrec_logical_switch_port_index_destroy_row(nb_lsp_key); > + 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)) { > + 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 { > + /* It may be a route to be advertised */ > + add_to_routes_ad(routes_ad, nb_route, ts_port_addrs, > + &nb_global->options, ts_route_table); > + } > + } > > - if (!nb_lsp) { > - return NULL; > + /* 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(ctx, 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); > + } > + } else { > + /* The router port of the TS port is ignored. */ > + VLOG_DBG("Skip advertising direct route of lrp %s (TS port)", > + lrp->name); > + } > } > +} > > - return smap_get(&nb_lsp->options, "router-port"); > +static void > +advertise_lr_routes(struct ic_context *ctx, > + const struct icsbrec_availability_zone *az, > + struct ic_router_info *ic_lr) > +{ > + const struct nbrec_nb_global *nb_global = > + nbrec_nb_global_first(ctx->ovnnb_idl); > + ovs_assert(nb_global); > + > + const struct icsbrec_port_binding *isb_pb; > + const char *lrp_name, *route_table; > + struct lport_addresses ts_port_addrs; > + const struct nbrec_logical_router *lr = ic_lr->lr; > + const struct icnbrec_transit_switch *ts, *key = > + icnbrec_transit_switch_index_init_row( > + ctx->icnbrec_transit_switch_by_name); > + > + struct hmap routes_ad = HMAP_INITIALIZER(&routes_ad); > + for (int i = 0; i < ic_lr->n_isb_pbs; i++) { > + isb_pb = ic_lr->isb_pbs[i]; > + icnbrec_transit_switch_index_set_name(key, isb_pb->transit_switch); > + ts = icnbrec_transit_switch_index_find( > + ctx->icnbrec_transit_switch_by_name, key); > + > + 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, > + lr->name); > + continue; > + } > + lrp_name = get_lrp_name_by_ts_port_name(ctx, isb_pb->logical_port); > + route_table = get_route_table_by_lrp_name(ctx, lrp_name); > + build_ts_routes_to_adv(ctx, ic_lr, &routes_ad, &ts_port_addrs, > + nb_global, route_table); > + advertise_routes(ctx, az, ts->name, &routes_ad); > + destroy_lport_addresses(&ts_port_addrs); > + } > + hmap_destroy(&routes_ad); > + icnbrec_transit_switch_index_destroy_row(key); > } > > static void > @@ -1375,131 +1578,70 @@ route_run(struct ic_context *ctx, > return; > } > > - const struct nbrec_nb_global *nb_global = > - nbrec_nb_global_first(ctx->ovnnb_idl); > - ovs_assert(nb_global); > - > - const struct icnbrec_transit_switch *ts; > - ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) { > - struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs); > - struct hmap routes_ad = HMAP_INITIALIZER(&routes_ad); > - > - const struct icsbrec_port_binding *isb_pb; > - const struct icsbrec_port_binding *isb_pb_key = > - icsbrec_port_binding_index_init_row( > - ctx->icsbrec_port_binding_by_ts_az); > - icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name); > - icsbrec_port_binding_index_set_availability_zone(isb_pb_key, 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. */ > - ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, > - > ctx->icsbrec_port_binding_by_ts_az) > - { > - const char *ts_lrp_name = > - get_lrp_name_by_ts_port_name(ctx, 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.", > - isb_pb->logical_port, ts->name); > - continue; > - } > + 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(ctx->icsbrec_port_binding_by_az); > + icsbrec_port_binding_index_set_availability_zone(isb_pb_key, az); > > - 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_get_for_uuid(ctx->ovnnb_idl, > &lr_uuid); > - if (!lr) { > - continue; > - } > + /* 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, > + ctx->icsbrec_port_binding_by_az) > + { > + const char *ts_lrp_name = > + get_lrp_name_by_ts_port_name(ctx, 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; > + } > > - if (ic_router_find(&ic_lrs, lr)) { > - 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 router has another port " > - "connected to same ts.", isb_pb->logical_port, > - ts->name, lr->name); > - 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; > + } > > - struct lport_addresses ts_port_addrs; > - 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, ts->name, lr->name); > - continue; > - } > + const struct nbrec_logical_router *lr > + = nbrec_logical_router_get_for_uuid(ctx->ovnnb_idl, &lr_uuid); > + if (!lr) { > + continue; > + } > > - struct ic_router_info *ic_lr = xzalloc(sizeof *ic_lr); > + 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_pb = isb_pb; > hmap_init(&ic_lr->routes_learned); > hmap_insert(&ic_lrs, &ic_lr->node, uuid_hash(&lr->header_.uuid)); > - > - /* 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)) { > - 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 { > - /* It may be a route to be advertised */ > - add_to_routes_ad(&routes_ad, nb_route, &ts_port_addrs, > - &nb_global->options); > - } > - } > - > - /* Check direct-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 (!strcmp(lrp->name, ts_lrp_name)) { > - /* The router port of the TS port is ignored. */ > - VLOG_DBG("Route ad: skip lrp %s (TS port: %s)", > - lrp->name, isb_pb->logical_port); > - continue; > - } > - > - 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); > - } > - } > - > - destroy_lport_addresses(&ts_port_addrs); > } > - icsbrec_port_binding_index_destroy_row(isb_pb_key); > - > - advertise_route(ctx, az, ts->name, &routes_ad); > - hmap_destroy(&routes_ad); > > - struct ic_router_info *ic_lr, *next; > - HMAP_FOR_EACH_SAFE (ic_lr, next, node, &ic_lrs) { > - sync_learned_route(ctx, az, ic_lr); > - hmap_destroy(&ic_lr->routes_learned); > - hmap_remove(&ic_lrs, &ic_lr->node); > - free(ic_lr); > + if (ic_lr->n_isb_pbs == ic_lr->n_allocated_isb_pbs) { > + ic_lr->isb_pbs = x2nrealloc(ic_lr->isb_pbs, > + &ic_lr->n_allocated_isb_pbs, > + sizeof *ic_lr->isb_pbs); > } > - hmap_destroy(&ic_lrs); > + ic_lr->isb_pbs[ic_lr->n_isb_pbs++] = isb_pb; > } > + icsbrec_port_binding_index_destroy_row(isb_pb_key); > + > + struct ic_router_info *ic_lr, *next; > + HMAP_FOR_EACH_SAFE (ic_lr, next, node, &ic_lrs) { > + advertise_lr_routes(ctx, az, ic_lr); > + sync_learned_routes(ctx, az, ic_lr); > + free(ic_lr->isb_pbs); > + hmap_destroy(&ic_lr->routes_learned); > + hmap_remove(&ic_lrs, &ic_lr->node); > + free(ic_lr); > + } > + hmap_destroy(&ic_lrs); > } > > static void > @@ -1697,6 +1839,9 @@ main(int argc, char *argv[]) > struct ovsdb_idl_index *nbrec_port_by_name > = ovsdb_idl_index_create1(ovnnb_idl_loop.idl, > &nbrec_logical_switch_port_col_name); > + struct ovsdb_idl_index *nbrec_lrp_by_name > + = ovsdb_idl_index_create1(ovnnb_idl_loop.idl, > + &nbrec_logical_router_port_col_name); > struct ovsdb_idl_index *sbrec_port_binding_by_name > = ovsdb_idl_index_create1(ovnsb_idl_loop.idl, > &sbrec_port_binding_col_logical_port); > @@ -1704,6 +1849,10 @@ main(int argc, char *argv[]) > = ovsdb_idl_index_create1(ovnsb_idl_loop.idl, > &sbrec_chassis_col_name); > > + struct ovsdb_idl_index *icnbrec_transit_switch_by_name > + = ovsdb_idl_index_create1(ovninb_idl_loop.idl, > + &icnbrec_transit_switch_col_name); > + > struct ovsdb_idl_index *icsbrec_port_binding_by_az > = ovsdb_idl_index_create1(ovnisb_idl_loop.idl, > > &icsbrec_port_binding_col_availability_zone); > @@ -1762,9 +1911,12 @@ main(int argc, char *argv[]) > .ovnisb_idl = ovnisb_idl_loop.idl, > .ovnisb_txn = ovsdb_idl_loop_run(&ovnisb_idl_loop), > .nbrec_ls_by_name = nbrec_ls_by_name, > + .nbrec_lrp_by_name = nbrec_lrp_by_name, > .nbrec_port_by_name = nbrec_port_by_name, > .sbrec_port_binding_by_name = sbrec_port_binding_by_name, > .sbrec_chassis_by_name = sbrec_chassis_by_name, > + .icnbrec_transit_switch_by_name = > + icnbrec_transit_switch_by_name, > .icsbrec_port_binding_by_az = icsbrec_port_binding_by_az, > .icsbrec_port_binding_by_ts = icsbrec_port_binding_by_ts, > .icsbrec_port_binding_by_ts_az = > icsbrec_port_binding_by_ts_az, > diff --git a/ovn-ic-sb.ovsschema b/ovn-ic-sb.ovsschema > index 5364b21b4..140ced149 100644 > --- a/ovn-ic-sb.ovsschema > +++ b/ovn-ic-sb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_IC_Southbound", > - "version": "1.0.0", > - "cksum": "108951192 6585", > + "version": "1.1.0", > + "cksum": "3076915724 6636", > "tables": { > "IC_SB_Global": { > "columns": { > @@ -92,6 +92,7 @@ > "transit_switch": {"type": "string"}, > "availability_zone": {"type": {"key": {"type": "uuid", > "refTable": "Availability_Zone"}}}, > + "route_table": {"type": "string"}, > "ip_prefix": {"type": "string"}, > "nexthop": {"type": "string"}, > "external_ids": { > diff --git a/ovn-ic-sb.xml b/ovn-ic-sb.xml > index 3582cff47..2f2ecf952 100644 > --- a/ovn-ic-sb.xml > +++ b/ovn-ic-sb.xml > @@ -306,6 +306,24 @@ > The availability zone that has advertised the route. > </column> > > + <column name="route_table"> > + Route table within which this route was created. > + Empty value means "global" routing table. > + <p> > + Routes for directly-connected networks will be > + learned to global routing table and if Logical Routers > + have more than one Transit Switch, which interconnects > + them, directly-connected routes will be added via each > + transit switch port and configured as ECMP routes. > + </p> > + <p> > + Static routes within route tables will be advertised > + and learned only if interconnecting transit switch's LRPs > + will have <code>options:route_table</code> same value as > + route's <code>route_table</code> value. > + </p> > + </column> > + > <column name="ip_prefix"> > IP prefix of this route (e.g. 192.168.100.0/24). > </column> > diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at > index 3aab54362..5803f76e9 100644 > --- a/tests/ovn-ic.at > +++ b/tests/ovn-ic.at > @@ -430,3 +430,443 @@ OVN_CLEANUP_IC([az1], [az2]) > > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn-ic -- route sync -- route tables]) > + > +ovn_init_ic_db > +ovn-ic-nbctl ts-add ts1 > + > +for i in 1 2; do > + ovn_start az$i > + ovn_as az$i > + > + # Enable route learning at AZ level > + ovn-nbctl set nb_global . options:ic-route-learn=true > + # Enable route advertising at AZ level > + ovn-nbctl set nb_global . options:ic-route-adv=true > + > + # Create LRP and connect to TS > + ovn-nbctl lr-add lr$i > + ovn-nbctl lrp-add lr$i lrp-lr$i-ts1 aa:aa:aa:aa:aa:0$i 169.254.100.$i/24 > + ovn-nbctl lsp-add ts1 lsp-ts1-lr$i \ > + -- lsp-set-addresses lsp-ts1-lr$i router \ > + -- lsp-set-type lsp-ts1-lr$i router \ > + -- lsp-set-options lsp-ts1-lr$i router-port=lrp-lr$i-ts1 > + > + # Create static routes > + ovn-nbctl lr-route-add lr$i 10.11.$i.0/24 169.254.0.1 > + > + # Create a src-ip route, which shouldn't be synced > + ovn-nbctl --policy=src-ip --route-table=rtb1 lr-route-add lr$i > 10.22.$i.0/24 169.254.0.2 > +done > + > +for i in 1 2; do > + OVS_WAIT_UNTIL([ovn_as az$i ovn-nbctl lr-route-list lr$i | grep learned]) > +done > + > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl > +IPv4 Routes > +Route Table global: > + 10.11.1.0/24 169.254.0.1 dst-ip > + 10.11.2.0/24 169.254.100.2 dst-ip (learned) > + > +Route Table rtb1: > + 10.22.1.0/24 169.254.0.2 src-ip > +]) > + > +# move routes from global route table to rtb1 > +for i in 1 2; do > + ovn_as az$i ovn-nbctl lr-route-del lr$i 10.11.$i.0/24 169.254.0.1 > + ovn_as az$i ovn-nbctl --route-table=rtb1 lr-route-add lr$i 10.11.$i.0/24 > 169.254.0.1 > +done > + > +for i in 1 2; do > + OVS_WAIT_WHILE([ovn_as az$i ovn-nbctl lr-route-list lr$i | grep learned]) > +done > + > +# ensure route from rtb1 is not learned to any route table as route table is > +# not set to TS port > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.1.0/24 169.254.0.1 dst-ip > + 10.22.1.0/24 169.254.0.2 src-ip > +]) > + > +# assign route table rtb1 to TS port on AZ2 and check routes are advertised > to IC SB DB > +check ovn_as az2 ovn-nbctl lrp-set-options lrp-lr2-ts1 route_table=rtb1 > +OVS_WAIT_UNTIL([ovn-ic-sbctl find route route_table=rtb1 | grep > 10.11.2.0/24]) > + > +# ensure route was not learned as on AZ1 TS port's LRP was not set to route > table rtb1 > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.1.0/24 169.254.0.1 dst-ip > + 10.22.1.0/24 169.254.0.2 src-ip > +]) > + > +# set TS port's LRP to route table rtb1 to learn routes from AZ2 from rtb1 > +check ovn_as az1 ovn-nbctl lrp-set-options lrp-lr1-ts1 route_table=rtb1 > + > +OVS_WAIT_UNTIL([ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-list lr1 | > grep learned]) > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.1.0/24 169.254.0.1 dst-ip > + 10.11.2.0/24 169.254.100.2 dst-ip (learned) > + 10.22.1.0/24 169.254.0.2 src-ip > +]) > + > +# Delete route in AZ1, AZ2's learned route should be deleted. > +ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-del lr1 10.11.1.0/24 > +OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl --route-table=rtb1 lr-route-list lr2 | > grep learned]) > + > +# Add the route back > +ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-add lr1 10.11.1.0/24 > 169.254.0.1 > +OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned]) > + > +# Disable route-learning for AZ1 > +ovn_as az1 ovn-nbctl set nb_global . options:ic-route-learn=false > +OVS_WAIT_WHILE([ovn_as az1 ovn-nbctl lr-route-list lr1 | grep learned]) > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.1.0/24 169.254.0.1 dst-ip > + 10.22.1.0/24 169.254.0.2 src-ip > +]) > + > +# AZ1 should still advertise and AZ2 should still learn the route > +AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned], [0], > [ignore]) > +AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.1.0/24 169.254.100.1 dst-ip (learned) > + 10.11.2.0/24 169.254.0.1 dst-ip > + 10.22.2.0/24 169.254.0.2 src-ip > +]) > + > +# Disable route-advertising for AZ1 > +ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv=false > + > +# AZ2 shouldn't have the route learned, because AZ1 have stopped advertising. > +OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned]) > + > +# Add default route in AZ1 > +ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-add lr1 0.0.0.0/0 > 169.254.0.3 > + > +# Re-enable router-advertising & learn for AZ1 > +ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv=true > +ovn_as az1 ovn-nbctl set nb_global . options:ic-route-learn=true > + > +for i in 1 2; do > + OVS_WAIT_UNTIL([ovn_as az$i ovn-nbctl lr-route-list lr$i | grep learned]) > +done > + > +# Default route should NOT get advertised or learned, by default. > +AT_CHECK([ovn-ic-sbctl find route ip_prefix="0.0.0.0/0"], [0], []) > + > +# Enable default route advertising in AZ1, ensure it advertised, but not > learned > +ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv-default=true > +OVS_WAIT_UNTIL([ovn-ic-sbctl find route ip_prefix="0.0.0.0/0" > route_table=rtb1 | grep 0.0.0.0]) > +OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl --route-table=rtb1 lr-route-list lr2 | > grep learned | grep 0.0.0.0]) > + > +# Enable default route learning in AZ2 > +ovn_as az2 ovn-nbctl set nb_global . options:ic-route-learn-default=true > +OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl --route-table=rtb1 lr-route-list lr2 | > grep learned | grep 0.0.0.0]) > + > +# Test directly connected subnet route advertising. Route should go to > global route table. > +ovn_as az1 ovn-nbctl lrp-add lr1 lrp-lr1-ls1 aa:aa:aa:aa:bb:01 > "192.168.0.1/24" > +OVS_WAIT_UNTIL([ovn-ic-sbctl find route ip_prefix="192.168.0.1/24" > route_table="\"\"" | grep 192.168.0.1/24]) > +OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned | grep > 192.168]) > +AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl > +IPv4 Routes > +Route Table global: > + 192.168.0.0/24 169.254.100.1 dst-ip (learned) > + > +Route Table rtb1: > + 10.11.1.0/24 169.254.100.1 dst-ip (learned) > + 10.11.2.0/24 169.254.0.1 dst-ip > + 10.22.2.0/24 169.254.0.2 src-ip > + 0.0.0.0/0 169.254.100.1 dst-ip (learned) > +]) > + > +# Delete the directly connected subnet from AZ1, learned route should be > +# removed from AZ2. > +ovn_as az1 ovn-nbctl lrp-del lrp-lr1-ls1 > +OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned | grep > 192.168]) > + > +# Test blacklist routes > +# Add back the directly connected 192.168 route. > +ovn_as az1 ovn-nbctl lrp-add lr1 lrp-lr1-ls1 aa:aa:aa:aa:bb:01 > "192.168.0.1/24" > +OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned | grep > 192.168]) > +# Now add 10.11.0.0/16 and 192.168.0.0/16 to blacklist in AZ2. > +check ovn_as az2 ovn-nbctl set nb_global . > options:ic-route-blacklist="10.11.0.0/16,192.168.0.0/16" > +# AZ2 shouldn't learn 192.168 route any more. > +OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned | grep > 192.168]) > +# AZ1 shouldn't learn 10.11 any more. > +OVS_WAIT_WHILE([ovn_as az1 ovn-nbctl lr-route-list lr1 | grep learned | grep > 10.11]) > +AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.11.2.0/24 169.254.0.1 dst-ip > + 10.22.2.0/24 169.254.0.2 src-ip > + 0.0.0.0/0 169.254.100.1 dst-ip (learned) > +]) > + > +OVN_CLEANUP_IC([az1], [az2]) > + > +AT_CLEANUP > +]) > + > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn-ic -- route sync -- multiple route tables]) > + > +ovn_init_ic_db > +ovn-ic-nbctl ts-add ts1 > + > +for i in 1 2; do > + ovn_start az$i > + ovn_as az$i > + > + # Enable route learning at AZ level > + ovn-nbctl set nb_global . options:ic-route-learn=true > + # Enable route advertising at AZ level > + ovn-nbctl set nb_global . options:ic-route-adv=true > +done > + > +# Create new transit switches and LRs. Test topology is next: > +# VPC1: > +# / transit switch (ts11) \ > +# logical router (lr11) - transit switch (ts12) - logical router (lr12) > +# \ transit switch (ts13) / > +# > +# VPC2: > +# / transit switch (ts21) \ > +# logical router (lr21) logical router (lr22) > +# \ transit switch (ts22) / > +# > +# each LR has one connected subnet except TS port > + > + > +# VPC1 > +# create lr11, lr12, ts11, ts12, ts13 and connect them > +# assign route tables rtb1, rtb2, rtb3 to ts ports > +for i in 1 2; do > + ovn_as az$i > + > + lr=lr1$i > + ovn-nbctl lr-add $lr > + > + for j in 1 2 3; do > + ts=ts1$j > + ovn-ic-nbctl --may-exist ts-add $ts > + > + lrp=lrp-$lr-$ts > + lsp=lsp-$ts-$lr > + # Create LRP and connect to TS > + ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:a$j:0$i 169.254.10$j.$i/24 > + ovn-nbctl lrp-set-options $lrp route_table=rtb$j > + ovn-nbctl lsp-add $ts $lsp \ > + -- lsp-set-addresses $lsp router \ > + -- lsp-set-type $lsp router \ > + -- lsp-set-options $lsp router-port=$lrp > + done > +done > + > +# VPC2 > +# create lr21, lr22, ts21, ts22 and connect them > +# assign route tables rtb1, rtb2, rtb3 to ts ports > +for i in 1 2; do > + ovn_as az$i > + > + lr=lr2$i > + ovn-nbctl lr-add $lr > + > + for j in 1 2; do > + ts=ts2$j > + ovn-ic-nbctl --may-exist ts-add $ts > + > + lrp=lrp-$lr-$ts > + lsp=lsp-$ts-$lr > + # Create LRP and connect to TS > + ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:a$j:0$i 169.254.10$j.$i/24 > + ovn-nbctl lrp-set-options $lrp route_table=rtb$j > + ovn-nbctl lsp-add $ts $lsp \ > + -- lsp-set-addresses $lsp router \ > + -- lsp-set-type $lsp router \ > + -- lsp-set-options $lsp router-port=$lrp > + done > +done > + > +# Create directly-connected and static routes in VPC1 > +ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12 aa:aa:aa:aa:bb:01 "192.168.0.1/24" > +ovn_as az2 ovn-nbctl --route-table=rtb1 lr-route-add lr12 10.10.10.0/24 > 192.168.0.10 > +ovn_as az2 ovn-nbctl --route-table=rtb2 lr-route-add lr12 10.10.10.0/24 > 192.168.0.11 > +ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 10.10.10.0/24 > 192.168.0.12 > + > +# Create directly-connected route in VPC2 > +ovn_as az2 ovn-nbctl lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bb:01 "192.168.0.1/24" > + > +# Test direct routes from lr12 were learned to lr11 > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 | > + grep learned | awk '{print $1, $2, $5}' | sort ], [0], [dnl > +192.168.0.0/24 169.254.101.2 ecmp > +192.168.0.0/24 169.254.102.2 ecmp > +192.168.0.0/24 169.254.103.2 ecmp > +]) > + > +# Test static routes from lr12 rtbs rtb1,rtb2,rtb3 were learned to lr11 > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-list lr11], [0], > [dnl > +IPv4 Routes > +Route Table rtb1: > + 10.10.10.0/24 169.254.101.2 dst-ip (learned) > +]) > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb2 lr-route-list lr11], [0], > [dnl > +IPv4 Routes > +Route Table rtb2: > + 10.10.10.0/24 169.254.102.2 dst-ip (learned) > +]) > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb3 lr-route-list lr11], [0], > [dnl > +IPv4 Routes > +Route Table rtb3: > + 10.10.10.0/24 169.254.103.2 dst-ip (learned) > +]) > + > +# Test routes from lr12 didn't leak as learned to lr21 > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr21 | grep 192.168 | sort], > [0], [dnl > + 192.168.0.0/24 169.254.101.2 dst-ip (learned) ecmp > + 192.168.0.0/24 169.254.102.2 dst-ip (learned) ecmp > +]) > + > +OVN_CLEANUP_IC([az1], [az2]) > + > +AT_CLEANUP > +]) > + > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn-ic -- route sync -- multiple route tables IPv6]) > + > +ovn_init_ic_db > +ovn-ic-nbctl ts-add ts1 > + > +for i in 1 2; do > + ovn_start az$i > + ovn_as az$i > + > + # Enable route learning at AZ level > + ovn-nbctl set nb_global . options:ic-route-learn=true > + # Enable route advertising at AZ level > + ovn-nbctl set nb_global . options:ic-route-adv=true > +done > + > +# Create new transit switches and LRs. Test topology is next: > +# VPC1: > +# / transit switch (ts11) \ > +# logical router (lr11) - transit switch (ts12) - logical router (lr12) > +# \ transit switch (ts13) / > +# > +# VPC2: > +# / transit switch (ts21) \ > +# logical router (lr21) logical router (lr22) > +# \ transit switch (ts22) / > +# > +# each LR has one connected subnet except TS port > + > + > +# VPC1 > +# create lr11, lr12, ts11, ts12, ts13 and connect them > +# assign route tables rtb1, rtb2, rtb3 to ts ports > +for i in 1 2; do > + ovn_as az$i > + > + lr=lr1$i > + ovn-nbctl lr-add $lr > + > + for j in 1 2 3; do > + ts=ts1$j > + ovn-ic-nbctl --may-exist ts-add $ts > + > + lrp=lrp-$lr-$ts > + lsp=lsp-$ts-$lr > + # Create LRP and connect to TS > + ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:a$j:0$i 2001:db8:$j::$i/64 > + ovn-nbctl lrp-set-options $lrp route_table=rtb$j > + ovn-nbctl lsp-add $ts $lsp \ > + -- lsp-set-addresses $lsp router \ > + -- lsp-set-type $lsp router \ > + -- lsp-set-options $lsp router-port=$lrp > + done > +done > + > +# VPC2 > +# create lr21, lr22, ts21, ts22 and connect them > +# assign route tables rtb1, rtb2, rtb3 to ts ports > +for i in 1 2; do > + ovn_as az$i > + > + lr=lr2$i > + ovn-nbctl lr-add $lr > + > + for j in 1 2; do > + ts=ts2$j > + ovn-ic-nbctl --may-exist ts-add $ts > + > + lrp=lrp-$lr-$ts > + lsp=lsp-$ts-$lr > + # Create LRP and connect to TS > + ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:a$j:0$i 2001:db8:$j::$i/64 > + ovn-nbctl lrp-set-options $lrp route_table=rtb$j > + ovn-nbctl lsp-add $ts $lsp \ > + -- lsp-set-addresses $lsp router \ > + -- lsp-set-type $lsp router \ > + -- lsp-set-options $lsp router-port=$lrp > + done > +done > + > +# Create directly-connected and static routes in VPC1 > +ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12 aa:aa:aa:aa:bb:01 > "2001:db8:200::1/64" > +ovn_as az2 ovn-nbctl --route-table=rtb1 lr-route-add lr12 2001:db8:aaaa::/64 > 2001:db8:200::10 > +ovn_as az2 ovn-nbctl --route-table=rtb2 lr-route-add lr12 2001:db8:aaaa::/64 > 2001:db8:200::11 > +ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 2001:db8:aaaa::/64 > 2001:db8:200::12 > + > +# Create directly-connected route in VPC2 > +ovn_as az2 ovn-nbctl lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bb:01 > "2001:db8:200::1/64" > + > +# Test direct routes from lr12 were learned to lr11 > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001:db8:200 | > + grep learned | awk '{print $1, $2, $5}' | sort], [0], [dnl > +2001:db8:200::/64 2001:db8:1::2 ecmp > +2001:db8:200::/64 2001:db8:2::2 ecmp > +2001:db8:200::/64 2001:db8:3::2 ecmp > +]) > + > +# Test static routes from lr12 rtbs rtb1,rtb2,rtb3 were learned to lr11 > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb1 lr-route-list lr11], [0], > [dnl > +IPv6 Routes > +Route Table rtb1: > + 2001:db8:aaaa::/64 2001:db8:1::2 dst-ip (learned) > +]) > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb2 lr-route-list lr11], [0], > [dnl > +IPv6 Routes > +Route Table rtb2: > + 2001:db8:aaaa::/64 2001:db8:2::2 dst-ip (learned) > +]) > +AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb3 lr-route-list lr11], [0], > [dnl > +IPv6 Routes > +Route Table rtb3: > + 2001:db8:aaaa::/64 2001:db8:3::2 dst-ip (learned) > +]) > + > +# Test routes from lr12 didn't leak as learned to lr21 > +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr21 | grep 2001 | sort], [0], > [dnl > + 2001:db8:200::/64 2001:db8:1::2 dst-ip (learned) ecmp > + 2001:db8:200::/64 2001:db8:2::2 dst-ip (learned) ecmp > +]) > + > +OVN_CLEANUP_IC([az1], [az2]) > + > +AT_CLEANUP > +]) > -- > 2.30.0 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
