This is a continuation of previous commit. It's separate for ease of review. It will be squshed together for the final version.
Signed-off-by: Martin Kalcok <martin.kal...@canonical.com> --- northd/en-advertised-route-sync.c | 20 ++- northd/en-learned-route-sync.c | 3 +- northd/northd.c | 217 ++++++++++++++++++++++++++++-- northd/northd.h | 11 +- 4 files changed, 231 insertions(+), 20 deletions(-) diff --git a/northd/en-advertised-route-sync.c b/northd/en-advertised-route-sync.c index 9d4fb308d..e81c86afa 100644 --- a/northd/en-advertised-route-sync.c +++ b/northd/en-advertised-route-sync.c @@ -25,7 +25,6 @@ #include "openvswitch/hmap.h" #include "ovn-util.h" - static void advertised_route_table_sync( struct ovsdb_idl_txn *ovnsb_txn, @@ -205,9 +204,13 @@ en_dynamic_routes_run(struct engine_node *node, void *data) if (!od->dynamic_routing) { continue; } - build_lb_nat_parsed_routes(od, lr_stateful_rec->lrnat_rec, - &dynamic_routes_data->parsed_routes); + build_nat_parsed_routes(od, lr_stateful_rec->lrnat_rec, + northd_data, + &dynamic_routes_data->parsed_routes); + build_lb_parsed_routes(od, lr_stateful_rec->lb_ips, + northd_data, + &dynamic_routes_data->parsed_routes); } engine_set_node_state(node, EN_UPDATED); } @@ -442,10 +445,19 @@ advertised_route_table_sync_route_add( if (route->source == ROUTE_SOURCE_NAT && (drr & DRRM_NAT) == 0) { return; } + if (route->source == ROUTE_SOURCE_LB && (drr & DRRM_LB) == 0) { + return; + } + /* XXX: I'm not sure if normalize prefix is the best call here. It doesn't + * include "/plen" for host routes, so they get announced without it. */ char *ip_prefix = normalize_v46_prefix(&route->prefix, route->plen); + const struct sbrec_port_binding *tracked_port = NULL; + if (route->tracked_port) { + tracked_port = route->tracked_port->sb; + } ar_add_entry(sync_routes, route->od->sb, route->out_port->sb, - ip_prefix, NULL); + ip_prefix, tracked_port); } static void diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c index 406f1551f..4e87b3265 100644 --- a/northd/en-learned-route-sync.c +++ b/northd/en-learned-route-sync.c @@ -181,7 +181,8 @@ parse_route_from_sbrec_route(struct hmap *parsed_routes_out, parsed_route_add(od, nexthop, &prefix, plen, false, lrp_addr_s, out_port, 0, false, false, NULL, - ROUTE_SOURCE_LEARNED, &route->header_, parsed_routes_out); + ROUTE_SOURCE_LEARNED, &route->header_, NULL, + parsed_routes_out); } static void diff --git a/northd/northd.c b/northd/northd.c index 4b5708059..c0953028a 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -10992,6 +10992,7 @@ parsed_route_init(const struct ovn_datapath *od, bool ecmp_symmetric_reply, const struct sset *ecmp_selection_fields, enum route_source source, + const struct ovn_port *tracked_port, const struct ovsdb_idl_row *source_hint) { @@ -11007,6 +11008,7 @@ parsed_route_init(const struct ovn_datapath *od, new_pr->is_discard_route = is_discard_route; new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s); new_pr->out_port = out_port; + new_pr->tracked_port = tracked_port; new_pr->source = source; if (ecmp_selection_fields) { sset_clone(&new_pr->ecmp_selection_fields, ecmp_selection_fields); @@ -11030,7 +11032,7 @@ parsed_route_clone(const struct parsed_route *pr) pr->od, nexthop, pr->prefix, pr->plen, pr->is_discard_route, pr->lrp_addr_s, pr->out_port, pr->route_table_id, pr->is_src_route, pr->ecmp_symmetric_reply, &pr->ecmp_selection_fields, pr->source, - pr->source_hint); + pr->tracked_port, pr->source_hint); new_pr->hash = pr->hash; return new_pr; @@ -11069,13 +11071,14 @@ parsed_route_add(const struct ovn_datapath *od, const struct sset *ecmp_selection_fields, enum route_source source, const struct ovsdb_idl_row *source_hint, + const struct ovn_port *tracked_port, struct hmap *routes) { struct parsed_route *new_pr = parsed_route_init( od, nexthop, *prefix, plen, is_discard_route, lrp_addr_s, out_port, route_table_id, is_src_route, ecmp_symmetric_reply, - ecmp_selection_fields, source, source_hint); + ecmp_selection_fields, source, tracked_port, source_hint); new_pr->hash = route_hash(new_pr); @@ -11212,7 +11215,7 @@ parsed_routes_add_static(const struct ovn_datapath *od, parsed_route_add(od, nexthop, &prefix, plen, is_discard_route, lrp_addr_s, out_port, route_table_id, is_src_route, ecmp_symmetric_reply, &ecmp_selection_fields, source, - &route->header_, routes); + &route->header_, NULL, routes); sset_destroy(&ecmp_selection_fields); } @@ -11230,7 +11233,7 @@ parsed_routes_add_connected(const struct ovn_datapath *od, false, addr->addr_s, op, 0, false, false, NULL, ROUTE_SOURCE_CONNECTED, - &op->nbrp->header_, routes); + &op->nbrp->header_, NULL, routes); } for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { @@ -11242,7 +11245,7 @@ parsed_routes_add_connected(const struct ovn_datapath *od, false, addr->addr_s, op, 0, false, false, NULL, ROUTE_SOURCE_CONNECTED, - &op->nbrp->header_, routes); + &op->nbrp->header_, NULL, routes); } } @@ -11280,10 +11283,153 @@ build_parsed_routes(const struct ovn_datapath *od, const struct hmap *lr_ports, } } +static int +lrouter_check_nat_entry(const struct ovn_datapath *od, + const struct ovn_nat *nat_entry, + const struct hmap *lr_ports, ovs_be32 *mask, + bool *is_v6, int *cidr_bits, struct eth_addr *mac, + bool *distributed, struct ovn_port **nat_l3dgw_port); + +static const struct ovn_port * +get_nat_route_tracked_port(const struct ovn_port *op, + const struct ovn_nat *nat, + const struct northd_data *northd_data) +{ + /* Ports on GW routers don't need tracked_port because all resources + * are located on the same chassis.*/ + if (op->od->is_gw_router) { + return NULL; + } + struct eth_addr mac = eth_addr_broadcast; + bool is_v6, distributed_nat; + ovs_be32 mask; + int cidr_bits; + struct ovn_port *l3dgw_port; + + if (lrouter_check_nat_entry(op->od, nat, &northd_data->lr_ports, &mask, &is_v6, + &cidr_bits, + &mac, &distributed_nat, &l3dgw_port) < 0) { + return NULL; + } + + /* For distributed NAT rules, we find ovn_port with name that matches + * logical_port value of the NAT entry, and use that as tracked_port. */ + if (distributed_nat) { + return ovn_port_find(&northd_data->ls_ports, nat->nb->logical_port); + /* For centralized NAT rules, we use designated DGP as a tracked port. */ + } else { + return l3dgw_port; + } + + return NULL; +} + +static void +lport_addrs_to_parsed_routes(const struct ovn_port *out_port, + const struct ovn_port *tracked_port, + const struct lport_addresses *laddrs, + enum route_source route_type, + struct hmap *routes) +{ + for (int i = 0; i < laddrs->n_ipv4_addrs; i++) { + struct ipv4_netaddr *addr = &laddrs->ipv4_addrs[i]; + struct in6_addr prefix; + ip46_parse(addr->network_s, &prefix); + parsed_route_add(out_port->od, NULL, &prefix, addr->plen, + false, addr->addr_s, out_port, + 0, false, + false, NULL, route_type, + &out_port->nbrp->header_, tracked_port, routes); + } + for (int i = 0; i < laddrs->n_ipv6_addrs; i++) { + struct ipv6_netaddr *addr = &laddrs->ipv6_addrs[i]; + parsed_route_add(out_port->od, NULL, &addr->addr, addr->plen, + false, addr->addr_s, out_port, + 0, false, + false, NULL, route_type, + &out_port->nbrp->header_, tracked_port, routes); + } +} + +/* XXX: This function is, in nature, very similar to how + * publish_host_routes_lrp looks up neighboring host routes. I really wanted + * to reuse it, but it's designed to work with already existing parsed_routes + * when creating Advertised_Route records. And that doesn't work in following + * scenario: + * + * R1 --- ls_int --- R2 + * + * If R1 and R2 don't have IPv4/6 configured on their LRPs plugged to + * ls_int, and you set "connected-as-host", no parsed_routes will be generated + * (makes sense). But as a consequence, publish_host_routes_lrp is never + * executed. + * We would very much like this scenario to work. i.e. no explicit IP + * configuration on ls_int, but NATs/LBs on R2 are reachable from R1 over + * R2s IPv6 LLA. This function aims to solve it by generating + * advertised_routes for NATs that are on op's neighbors (either directly + * connected LRs or LRs connected to same LS as op). I tried to keep the + * overhead to minimum. + */ +static void +build_connected_nat_routes(const struct ovn_port *op, struct hmap *routes) +{ + if (!op->peer) { + return; + } + struct ovn_datapath *peer_od = op->peer->od; + if (!peer_od->nbs && !peer_od->nbr) { + return; + } + + const struct ovn_port *peer_port = NULL; + /* This is directly connected LR peer. */ + if (peer_od->nbr) { + peer_port = op->peer; + size_t n_nats = 0; + char **nats = NULL; + nats = get_nat_addresses(peer_port, &n_nats, false, false, NULL, true); + for (size_t i = 0; i < n_nats; i++) { + /* XXX: This block should be probably squshed to a function. It's + * bit repetitive with the one bellow. */ + struct lport_addresses laddrs; + int ofs = 0; + extract_addresses(nats[i], &laddrs, &ofs); + lport_addrs_to_parsed_routes(op, peer_port, &laddrs, ROUTE_SOURCE_NAT, routes); + free(nats[i]); + destroy_lport_addresses(&laddrs); + } + free(nats); + return; + } + + /* This peer is LSP, we need to check all connected router ports for NAT.*/ + for (size_t i = 0; i < peer_od->n_router_ports; i++) { + peer_port = peer_od->router_ports[i]->peer; + if (peer_port == op) { + /* no need to check NAT addresses on ovn_port that initiated this + * function.*/ + continue; + } + size_t n_nats = 0; + char **nats = NULL; + nats = get_nat_addresses(peer_port, &n_nats, false, false, NULL, true); + for (size_t j = 0; j < n_nats; j++) { + struct lport_addresses laddrs; + int ofs = 0; + extract_addresses(nats[j], &laddrs, &ofs); + lport_addrs_to_parsed_routes(op, peer_port, &laddrs, ROUTE_SOURCE_NAT, routes); + free(nats[j]); + destroy_lport_addresses(&laddrs); + } + free(nats); + } +} + void -build_lb_nat_parsed_routes(const struct ovn_datapath *od, - const struct lr_nat_record *lr_nat, - struct hmap *routes) +build_nat_parsed_routes(const struct ovn_datapath *od, + const struct lr_nat_record *lr_nat, + const struct northd_data *northd_data, + struct hmap *routes) { const struct ovn_port *op; HMAP_FOR_EACH (op, dp_node, &od->ports) { @@ -11291,19 +11437,62 @@ build_lb_nat_parsed_routes(const struct ovn_datapath *od, if (!(drrm & DRRM_NAT)) { continue; } - /* I'm thinking of extending parsed_route struct with "tracked_port". - * Since we are already parsing/iterating NATs here, it feels more - * efficinet to figure out the tracked_port here, rather than - * re-parsing NATs down the line in the advertised_route_table_sync - * function before calling "ar_add_entry".*/ + for (size_t i = 0; i < lr_nat->n_nat_entries; i++) { const struct ovn_nat *nat = &lr_nat->nat_entries[i]; int plen = nat_entry_is_v6(nat) ? 128 : 32; struct in6_addr prefix; ip46_parse(nat->nb->external_ip, &prefix); + const struct ovn_port *tracked_port = + get_nat_route_tracked_port(op, nat, northd_data); parsed_route_add(od, NULL, &prefix, plen, false, nat->nb->external_ip, op, 0, false, false, - NULL, ROUTE_SOURCE_NAT, &op->nbrp->header_, routes); + NULL, ROUTE_SOURCE_NAT, &op->nbrp->header_, + tracked_port, routes); + } + + /* XXX: This could be made optional by adding "nat-connected" option + * to dynamic-routing-redistribute. Similar to how connected and + * connected-as-host work.*/ + build_connected_nat_routes(op, routes); + } + +} + +void +build_lb_parsed_routes(const struct ovn_datapath *od, + const struct ovn_lb_ip_set *lb_ips, + const struct northd_data *northd_data, + struct hmap *routes) +{ + const struct ovn_port *op; + HMAP_FOR_EACH (op, dp_node, &od->ports) { + enum dynamic_routing_redistribute_mode drrm = op->dynamic_routing_redistribute; + if (!(drrm & DRRM_LB)) { + continue; + } + + const char *ip_address; + SSET_FOR_EACH (ip_address, &lb_ips->ips_v4) { + struct in6_addr prefix; + ip46_parse(ip_address, &prefix); + /* XXX: Need to figure out tracked_port for LB without re-parsing + * ovn_datapath. lr_stateful_record doens't have as much data + * about LBs as it does about NATs. */ + const struct ovn_port *tracked_port = NULL; + parsed_route_add(od, NULL, &prefix, 32, false, + ip_address, op, 0, false, false, + NULL, ROUTE_SOURCE_LB, &op->nbrp->header_, + tracked_port, routes); + } + SSET_FOR_EACH (ip_address, &lb_ips->ips_v6) { + struct in6_addr prefix; + ip46_parse(ip_address, &prefix); + const struct ovn_port *tracked_port = NULL; + parsed_route_add(od, NULL, &prefix, 128, false, + ip_address, op, 0, false, false, + NULL, ROUTE_SOURCE_LB, &op->nbrp->header_, + tracked_port, routes); } } diff --git a/northd/northd.h b/northd/northd.h index 95f2fe010..5c0f7bc52 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -761,6 +761,7 @@ struct parsed_route { const struct ovsdb_idl_row *source_hint; char *lrp_addr_s; const struct ovn_port *out_port; + const struct ovn_port *tracked_port; /* May be NULL. */ }; /* Returns an independent clone of the provided parsed_route. The returned @@ -789,6 +790,7 @@ void parsed_route_add(const struct ovn_datapath *od, const struct sset *ecmp_selection_fields, enum route_source source, const struct ovsdb_idl_row *source_hint, + const struct ovn_port *tracked_port, struct hmap *routes); bool @@ -823,7 +825,14 @@ void route_policies_destroy(struct route_policies_data *); void build_parsed_routes(const struct ovn_datapath *, const struct hmap *, const struct hmap *, struct hmap *, struct simap *, struct hmap *); -void build_lb_nat_parsed_routes(const struct ovn_datapath *, const struct lr_nat_record *, struct hmap *); +void build_nat_parsed_routes(const struct ovn_datapath *, + const struct lr_nat_record *, + const struct northd_data *, + struct hmap *); +void build_lb_parsed_routes(const struct ovn_datapath *, + const struct ovn_lb_ip_set *, + const struct northd_data *, + struct hmap *); uint32_t get_route_table_id(struct simap *, const char *); void routes_init(struct routes_data *); void routes_destroy(struct routes_data *); -- 2.43.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev