> previously connected routes (so routes based on the networks of the > LRPs) where handled separately from static routes. > > In order to later announce routes from ovn to the fabric we need to have > a central overview over all routes we want to announce. This includes > routes based on Logical_Router_Static_Routes as well as the networks on > Logical_Router_Port. > > With this change the "routes" engine node will output a complete list of > all routes in the system. This also serves as a future integration point > to integrate routes learned from outside of OVN.
Hi Felix, This patch needs a respin. Some nits inline. Regards, Lorenzo > > Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud> > --- > northd/northd.c | 270 ++++++++++++++++++++++++++++++------------------ > northd/northd.h | 11 +- > 2 files changed, 178 insertions(+), 103 deletions(-) > > diff --git a/northd/northd.c b/northd/northd.c > index 1265e3c81..90ddc2acb 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -11071,6 +11071,20 @@ parsed_route_lookup(struct hmap *routes, size_t hash, > continue; > } > > + if (pr->source != new_pr->source) { > + continue; > + } > + > + if (!pr->nexthop != !new_pr->nexthop) { > + continue; why not if (pr->nexthop != new_pr->nexthop) ? > + } > + > + if (pr->nexthop && > + memcmp(pr->nexthop, new_pr->nexthop, > + sizeof(struct in6_addr))) { I guess we can use ipv6_addr_equals() here. > + continue; > + } > + > if (memcmp(&pr->prefix, &new_pr->prefix, sizeof(struct in6_addr))) { > continue; > } > @@ -11111,6 +11125,9 @@ parsed_route_lookup(struct hmap *routes, size_t hash, > > static void > parsed_route_free(struct parsed_route *pr) { > + if (pr->nexthop) { you can remove if () here > + free(pr->nexthop); > + } > if (pr->lrp_addr_s) { > free(pr->lrp_addr_s); > } > @@ -11119,31 +11136,77 @@ parsed_route_free(struct parsed_route *pr) { > } > > static void > -parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports, > +parsed_route_add(const struct ovn_datapath *od, > + struct in6_addr *nexthop, > + const struct in6_addr prefix, > + unsigned int plen, > + bool is_discard_route, > + const char *lrp_addr_s, > + const struct ovn_port *out_port, > + const struct nbrec_logical_router_static_route *route, > + uint32_t route_table_id, > + bool is_src_route, > + bool ecmp_symmetric_reply, > + enum route_source source, > + struct hmap *routes) > +{ > + > + struct parsed_route *new_pr = xzalloc(sizeof *new_pr); > + new_pr->prefix = prefix; > + new_pr->plen = plen; > + new_pr->nexthop = nexthop; > + new_pr->route_table_id = route_table_id; > + new_pr->is_src_route = is_src_route; > + new_pr->hash = route_hash(new_pr); > + new_pr->nbr = od->nbr; > + new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply; > + new_pr->is_discard_route = is_discard_route; > + if (!is_discard_route) { > + new_pr->lrp_addr_s = xstrdup(lrp_addr_s); > + } > + new_pr->out_port = out_port; > + new_pr->source = source; > + new_pr->route = route; > + > + size_t hash = uuid_hash(&od->key); > + struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr); > + if (!pr) { > + hmap_insert(routes, &new_pr->key_node, hash); > + } else { > + pr->stale = false; > + parsed_route_free(new_pr); > + } > +} > + > +static void > +parsed_routes_add_static(struct ovn_datapath *od, const struct hmap > *lr_ports, > const struct nbrec_logical_router_static_route *route, > const struct hmap *bfd_connections, > struct hmap *routes, struct simap *route_tables, > struct hmap *bfd_active_connections) > { > /* Verify that the next hop is an IP address with an all-ones mask. */ > - struct in6_addr nexthop; > + struct in6_addr *nexthop = NULL; if we allow parsed_route_add() to make a private copy of it we can avoid a pointer for nexthop here and make the patch more readable (e.g. remove all free(nexthop)) > unsigned int plen; > bool is_discard_route = !strcmp(route->nexthop, "discard"); > bool valid_nexthop = route->nexthop[0] && !is_discard_route; > if (valid_nexthop) { > - if (!ip46_parse_cidr(route->nexthop, &nexthop, &plen)) { > + nexthop = xmalloc(sizeof(*nexthop)); > + if (!ip46_parse_cidr(route->nexthop, nexthop, &plen)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > VLOG_WARN_RL(&rl, "bad 'nexthop' %s in static route " > UUID_FMT, route->nexthop, > UUID_ARGS(&route->header_.uuid)); > + free(nexthop); > return; > } > - if ((IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 32) || > - (!IN6_IS_ADDR_V4MAPPED(&nexthop) && plen != 128)) { > + if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) || > + (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > VLOG_WARN_RL(&rl, "bad next hop mask %s in static route " > UUID_FMT, route->nexthop, > UUID_ARGS(&route->header_.uuid)); > + free(nexthop); > return; > } > } > @@ -11155,17 +11218,19 @@ parsed_routes_add(struct ovn_datapath *od, const > struct hmap *lr_ports, > VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in static route " > UUID_FMT, route->ip_prefix, > UUID_ARGS(&route->header_.uuid)); > + free(nexthop); > return; > } > > /* Verify that ip_prefix and nexthop have same address familiy. */ > if (valid_nexthop) { > - if (IN6_IS_ADDR_V4MAPPED(&prefix) != IN6_IS_ADDR_V4MAPPED(&nexthop)) > { > + if (IN6_IS_ADDR_V4MAPPED(&prefix) != IN6_IS_ADDR_V4MAPPED(nexthop)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > VLOG_WARN_RL(&rl, "Address family doesn't match between > 'ip_prefix'" > " %s and 'nexthop' %s in static route "UUID_FMT, > route->ip_prefix, route->nexthop, > UUID_ARGS(&route->header_.uuid)); > + free(nexthop); > return; > } > } > @@ -11177,6 +11242,7 @@ parsed_routes_add(struct ovn_datapath *od, const > struct hmap *lr_ports, > !find_static_route_outport(od, lr_ports, route, > IN6_IS_ADDR_V4MAPPED(&prefix), > &lrp_addr_s, &out_port)) { > + free(nexthop); > return; > } > > @@ -11186,6 +11252,7 @@ parsed_routes_add(struct ovn_datapath *od, const > struct hmap *lr_ports, > nb_bt->logical_port, > nb_bt->dst_ip); > if (!bfd_e) { > + free(nexthop); > return; > } > > @@ -11205,36 +11272,58 @@ parsed_routes_add(struct ovn_datapath *od, const > struct hmap *lr_ports, > > > if (!strcmp(bfd_sr->status, "down")) { > + free(nexthop); > return; > } > } > > - struct parsed_route *new_pr = xzalloc(sizeof *new_pr); > - new_pr->prefix = prefix; > - new_pr->plen = plen; > - new_pr->route_table_id = get_route_table_id(route_tables, > - route->route_table); > - new_pr->is_src_route = (route->policy && > - !strcmp(route->policy, "src-ip")); > - new_pr->hash = route_hash(new_pr); > - new_pr->route = route; > - new_pr->nbr = od->nbr; > - new_pr->ecmp_symmetric_reply = smap_get_bool(&route->options, > - "ecmp_symmetric_reply", > - false); > - new_pr->is_discard_route = is_discard_route; > - if (!is_discard_route) { > - new_pr->lrp_addr_s = xstrdup(lrp_addr_s); > - } > - new_pr->out_port = out_port; > + uint32_t route_table_id = get_route_table_id(route_tables, > + route->route_table); > + bool is_src_route = (route->policy && !strcmp(route->policy, "src-ip")); > + bool ecmp_symmetric_reply = smap_get_bool(&route->options, > + "ecmp_symmetric_reply", > + false); > > - size_t hash = uuid_hash(&od->key); > - struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr); > - if (!pr) { > - hmap_insert(routes, &new_pr->key_node, hash); > + enum route_source source; > + if (!strcmp(smap_get_def(&route->options, "origin", ""), > + ROUTE_ORIGIN_CONNECTED)) { > + source = ROUTE_SOURCE_CONNECTED; > } else { > - pr->stale = false; > - parsed_route_free(new_pr); > + source = ROUTE_SOURCE_STATIC; > + } > + > + parsed_route_add(od, nexthop, prefix, plen, is_discard_route, lrp_addr_s, > + out_port, route, route_table_id, is_src_route, > + ecmp_symmetric_reply, source, > + routes); > +} > + > +static void > +parsed_routes_add_connected(struct ovn_datapath *od, const struct ovn_port > *op, > + struct hmap *routes) > +{ > + for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { > + const struct ipv4_netaddr *addr = &op->lrp_networks.ipv4_addrs[i]; > + struct in6_addr prefix; > + ip46_parse(addr->network_s, &prefix); > + > + parsed_route_add(od, NULL, prefix, addr->plen, > + false, addr->addr_s, op, > + NULL, 0, false, > + false, ROUTE_SOURCE_CONNECTED, > + routes); > + } > + > + for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { > + const struct ipv6_netaddr *addr = &op->lrp_networks.ipv6_addrs[i]; > + struct in6_addr prefix; > + ip46_parse(addr->network_s, &prefix); > + > + parsed_route_add(od, NULL, prefix, addr->plen, > + false, addr->addr_s, op, > + NULL, 0, false, > + false, ROUTE_SOURCE_CONNECTED, > + routes); > } > } > > @@ -11252,9 +11341,14 @@ build_parsed_routes(struct ovn_datapath *od, const > struct hmap *lr_ports, > } > > for (int i = 0; i < od->nbr->n_static_routes; i++) { > - parsed_routes_add(od, lr_ports, od->nbr->static_routes[i], > - bfd_connections, routes, route_tables, > - bfd_active_connections); > + parsed_routes_add_static(od, lr_ports, od->nbr->static_routes[i], > + bfd_connections, routes, route_tables, > + bfd_active_connections); > + } > + > + const struct ovn_port *op; > + HMAP_FOR_EACH (op, dp_node, &od->ports) { > + parsed_routes_add_connected(od, op, routes); > } > > HMAP_FOR_EACH_SAFE (pr, key_node, routes) { > @@ -11279,7 +11373,7 @@ struct ecmp_groups_node { > struct in6_addr prefix; > unsigned int plen; > bool is_src_route; > - const char *origin; > + enum route_source source; > uint32_t route_table_id; > uint16_t route_count; > struct ovs_list route_list; /* Contains ecmp_route_list_node */ > @@ -11318,7 +11412,7 @@ ecmp_groups_add(struct hmap *ecmp_groups, > eg->prefix = route->prefix; > eg->plen = route->plen; > eg->is_src_route = route->is_src_route; > - eg->origin = smap_get_def(&route->route->options, "origin", ""); > + eg->source = route->source; > eg->route_table_id = route->route_table_id; > ovs_list_init(&eg->route_list); > ecmp_groups_add_route(eg, route); > @@ -11382,6 +11476,7 @@ unique_routes_remove(struct hmap *unique_routes, > if (ipv6_addr_equals(&route->prefix, &ur->route->prefix) && > route->plen == ur->route->plen && > route->is_src_route == ur->route->is_src_route && > + route->source == ur->route->source && > route->route_table_id == ur->route->route_table_id) { > hmap_remove(unique_routes, &ur->hmap_node); > const struct parsed_route *existed_route = ur->route; > @@ -11515,7 +11610,7 @@ static void > add_ecmp_symmetric_reply_flows(struct lflow_table *lflows, > struct ovn_datapath *od, > const char *port_ip, > - struct ovn_port *out_port, > + const struct ovn_port *out_port, > const struct parsed_route *route, > struct ds *route_match, > struct lflow_ref *lflow_ref) > @@ -11609,6 +11704,19 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > *lflows, > ds_destroy(&ecmp_reply); > } > > +static int > +route_source_to_offset(enum route_source source) > +{ > + switch (source) { > + case ROUTE_SOURCE_CONNECTED: > + return ROUTE_PRIO_OFFSET_CONNECTED; > + case ROUTE_SOURCE_STATIC: > + return ROUTE_PRIO_OFFSET_STATIC; > + default: > + OVS_NOT_REACHED(); > + } > +} > + > static void > build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, > struct ecmp_groups_node *eg, struct lflow_ref > *lflow_ref) > @@ -11620,8 +11728,7 @@ build_ecmp_route_flow(struct lflow_table *lflows, > struct ovn_datapath *od, > struct ds route_match = DS_EMPTY_INITIALIZER; > > char *prefix_s = build_route_prefix_s(&eg->prefix, eg->plen); > - int ofs = !strcmp(eg->origin, ROUTE_ORIGIN_CONNECTED) ? > - ROUTE_PRIO_OFFSET_CONNECTED: ROUTE_PRIO_OFFSET_STATIC; > + int ofs = route_source_to_offset(eg->source); > build_route_match(NULL, eg->route_table_id, prefix_s, eg->plen, > eg->is_src_route, is_ipv4, &route_match, &priority, > ofs); > free(prefix_s); > @@ -11675,13 +11782,14 @@ build_ecmp_route_flow(struct lflow_table *lflows, > struct ovn_datapath *od, > REG_ECMP_MEMBER_ID" == %"PRIu16, > eg->id, er->id); > ds_clear(&actions); > - ds_put_format(&actions, "%s = %s; " > + ds_put_format(&actions, "%s = ", > + is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); > + ipv6_format_mapped(route_->nexthop, &actions); > + ds_put_format(&actions, "; " > "%s = %s; " > "eth.src = %s; " > "outport = %s; " > "next;", > - is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6, > - route->nexthop, > is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, > route_->lrp_addr_s, > route_->out_port->lrp_networks.ea_s, > @@ -11699,17 +11807,19 @@ build_ecmp_route_flow(struct lflow_table *lflows, > struct ovn_datapath *od, > static void > add_route(struct lflow_table *lflows, struct ovn_datapath *od, > const struct ovn_port *op, const char *lrp_addr_s, > - const char *network_s, int plen, const char *gateway, > + const char *network_s, int plen, const struct in6_addr *gateway, > bool is_src_route, const uint32_t rtb_id, > const struct sset *bfd_ports, > const struct ovsdb_idl_row *stage_hint, bool is_discard_route, > - int ofs, struct lflow_ref *lflow_ref) > + enum route_source source, struct lflow_ref *lflow_ref) > { > bool is_ipv4 = strchr(network_s, '.') ? true : false; > struct ds match = DS_EMPTY_INITIALIZER; > uint16_t priority; > const struct ovn_port *op_inport = NULL; > > + int ofs = route_source_to_offset(source); > + > /* IPv6 link-local addresses must be scoped to the local router port. */ > if (!is_ipv4) { > struct in6_addr network; > @@ -11728,8 +11838,8 @@ add_route(struct lflow_table *lflows, struct > ovn_datapath *od, > } else { > ds_put_format(&common_actions, REG_ECMP_GROUP_ID" = 0; %s = ", > is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); > - if (gateway && gateway[0]) { > - ds_put_cstr(&common_actions, gateway); > + if (gateway) { > + ipv6_format_mapped(gateway, &common_actions); > } else { > ds_put_format(&common_actions, "ip%s.dst", is_ipv4 ? "4" : "6"); > } > @@ -11764,23 +11874,20 @@ add_route(struct lflow_table *lflows, struct > ovn_datapath *od, > } > > static void > -build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, > +build_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, > const struct parsed_route *route_, > const struct sset *bfd_ports, > struct lflow_ref *lflow_ref) > { > const struct nbrec_logical_router_static_route *route = route_->route; > > - int ofs = !strcmp(smap_get_def(&route->options, "origin", ""), > - ROUTE_ORIGIN_CONNECTED) ? ROUTE_PRIO_OFFSET_CONNECTED > - : ROUTE_PRIO_OFFSET_STATIC; > - > char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen); > add_route(lflows, route_->is_discard_route ? od : route_->out_port->od, > route_->out_port, route_->lrp_addr_s, prefix_s, > - route_->plen, route->nexthop, route_->is_src_route, > - route_->route_table_id, bfd_ports, &route->header_, > - route_->is_discard_route, ofs, lflow_ref); > + route_->plen, route_->nexthop, route_->is_src_route, > + route_->route_table_id, bfd_ports, > + route ? &route->header_ : &route_->out_port->nbrp->header_, > + route_->is_discard_route, route_->source, lflow_ref); > > free(prefix_s); > } > @@ -13509,51 +13616,8 @@ build_ip_routing_pre_flows_for_lrouter(struct > ovn_datapath *od, > REG_ROUTE_TABLE_ID" = 0; next;", lflow_ref); > } > > -/* Logical router ingress table IP_ROUTING : IP Routing. > - * > - * A packet that arrives at this table is an IP packet that should be > - * routed to the address in 'ip[46].dst'. > - * > - * For regular routes without ECMP, table IP_ROUTING sets outport to the > - * correct output port, eth.src to the output port's MAC address, and > - * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 to the next-hop IP address > - * (leaving 'ip[46].dst', the packet’s final destination, unchanged), and > - * advances to the next table. > - * > - * For ECMP routes, i.e. multiple routes with same policy and prefix, table > - * IP_ROUTING remembers ECMP group id and selects a member id, and advances > - * to table IP_ROUTING_ECMP, which sets outport, eth.src and > - * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 for the selected ECMP member. > - * > - * This function adds routes for directly connected subnets configured on the > - * LRP 'op'. > - */ > static void > -build_ip_routing_flows_for_lrp(struct ovn_port *op, > - const struct sset *bfd_ports, > - struct lflow_table *lflows, > - struct lflow_ref *lflow_ref) > -{ > - ovs_assert(op->nbrp); > - for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { > - add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s, > - op->lrp_networks.ipv4_addrs[i].network_s, > - op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0, > - bfd_ports, &op->nbrp->header_, false, > - ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref); > - } > - > - for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { > - add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s, > - op->lrp_networks.ipv6_addrs[i].network_s, > - op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0, > - bfd_ports, &op->nbrp->header_, false, > - ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref); > - } > -} > - > -static void > -build_static_route_flows_for_lrouter( > +build_route_flows_for_lrouter( > struct ovn_datapath *od, struct lflow_table *lflows, > struct hmap *parsed_routes, > struct simap *route_tables, const struct sset *bfd_ports, > @@ -13580,6 +13644,10 @@ build_static_route_flows_for_lrouter( > struct parsed_route *route; > HMAP_FOR_EACH_WITH_HASH (route, key_node, uuid_hash(&od->key), > parsed_routes) { > + if (route->source == ROUTE_SOURCE_CONNECTED) { > + unique_routes_add(&unique_routes, route); > + continue; > + } > group = ecmp_groups_find(&ecmp_groups, route); > if (group) { > ecmp_groups_add_route(group, route); > @@ -13608,7 +13676,7 @@ build_static_route_flows_for_lrouter( > } > const struct unique_routes_node *ur; > HMAP_FOR_EACH (ur, hmap_node, &unique_routes) { > - build_static_route_flow(lflows, od, ur->route, > + build_route_flow(lflows, od, ur->route, > bfd_ports, lflow_ref); > } > ecmp_groups_destroy(&ecmp_groups); > @@ -16852,7 +16920,7 @@ build_routable_flows_for_router_port( > laddrs->ipv4_addrs[k].network_s, > laddrs->ipv4_addrs[k].plen, NULL, false, 0, > bfd_ports, &router_port->nbrp->header_, > - false, ROUTE_PRIO_OFFSET_CONNECTED, > + false, ROUTE_SOURCE_CONNECTED, > lrp->stateful_lflow_ref); > } > } > @@ -17094,7 +17162,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct > ovn_datapath *od, > lsi->meter_groups, NULL); > build_ND_RA_flows_for_lrouter(od, lsi->lflows, NULL); > build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL); > - build_static_route_flows_for_lrouter(od, lsi->lflows, > + build_route_flows_for_lrouter(od, lsi->lflows, > lsi->parsed_routes, > lsi->route_tables, > lsi->bfd_ports, NULL); > build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match, > @@ -17167,8 +17235,6 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct > ovn_port *op, > &lsi->actions, op->lflow_ref); > build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, > &lsi->actions, > op->lflow_ref); > - build_ip_routing_flows_for_lrp(op, lsi->bfd_ports, > - lsi->lflows, op->lflow_ref); > build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, > &lsi->actions, lsi->meter_groups, > op->lflow_ref); > diff --git a/northd/northd.h b/northd/northd.h > index f701abd45..eb669f734 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -696,10 +696,18 @@ struct ovn_port { > struct lflow_ref *stateful_lflow_ref; > }; > > +enum route_source { > + /* the route is directly connected to the logical router */ > + ROUTE_SOURCE_CONNECTED, I know naming is hard but maybe ROUTE_SOURCE_LOCAL? > + /* the route is derived from a northbound static route entry */ > + ROUTE_SOURCE_STATIC, > +}; > + > struct parsed_route { > struct hmap_node key_node; > struct in6_addr prefix; > unsigned int plen; > + struct in6_addr *nexthop; /* NULL for ROUTE_SOURCE_CONNECTED */ > bool is_src_route; > uint32_t route_table_id; > uint32_t hash; > @@ -708,8 +716,9 @@ struct parsed_route { > bool is_discard_route; > const struct nbrec_logical_router *nbr; > bool stale; > + enum route_source source; > char *lrp_addr_s; > - struct ovn_port *out_port; > + const struct ovn_port *out_port; can you add this in the patch where you added out_port pointer? > }; > > void ovnnb_db_run(struct northd_input *input_data, > -- > 2.47.0 > > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev