When we stop ovn-controller without immediately restarting it we now cleanup routes. This allows the routing agents to stop advertising this chassis to the fabric.
Acked-by: Dumitru Ceara <dce...@redhat.com> Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud> --- v5->v6: * addressed review comments * fixed testcase to cleanup all vrfs v3->v4: - addressed review comments. controller/route-exchange-netlink.c | 32 ++++++++--- controller/route-exchange-netlink.h | 2 + controller/route-exchange.c | 67 ++++++++++++++++++++++- tests/system-ovn.at | 84 +++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 8 deletions(-) diff --git a/controller/route-exchange-netlink.c b/controller/route-exchange-netlink.c index 0cb07e39f..ad0c25989 100644 --- a/controller/route-exchange-netlink.c +++ b/controller/route-exchange-netlink.c @@ -214,6 +214,9 @@ handle_route_msg(const struct route_table_msg *msg, void *data) /* This route is not from us, so we learn it. */ if (rd->rtm_protocol != RTPROT_OVN) { + if (!handle_data->learned_routes) { + return; + } if (prefix_is_link_local(&rd->rta_dst, rd->rtm_dst_len)) { return; } @@ -236,13 +239,15 @@ handle_route_msg(const struct route_table_msg *msg, void *data) return; } - uint32_t hash = advertise_route_hash(&rd->rta_dst, rd->rtm_dst_len); - HMAP_FOR_EACH_WITH_HASH (ar, node, hash, handle_data->routes) { - if (ipv6_addr_equals(&ar->addr, &rd->rta_dst) - && ar->plen == rd->rtm_dst_len - && ar->priority == rd->rta_priority) { - hmapx_find_and_delete(handle_data->routes_to_advertise, ar); - return; + if (handle_data->routes_to_advertise) { + uint32_t hash = advertise_route_hash(&rd->rta_dst, rd->rtm_dst_len); + HMAP_FOR_EACH_WITH_HASH (ar, node, hash, handle_data->routes) { + if (ipv6_addr_equals(&ar->addr, &rd->rta_dst) + && ar->plen == rd->rtm_dst_len + && ar->priority == rd->rta_priority) { + hmapx_find_and_delete(handle_data->routes_to_advertise, ar); + return; + } } } err = re_nl_delete_route(rd->rta_table_id, &rd->rta_dst, @@ -302,3 +307,16 @@ re_nl_sync_routes(uint32_t table_id, const struct hmap *routes, } hmapx_destroy(&routes_to_advertise); } + +void +re_nl_cleanup_routes(uint32_t table_id) +{ + /* Remove routes from the system that are not in the host_routes hmap and + * remove entries from host_routes hmap that match routes already installed + * in the system. */ + struct route_msg_handle_data data = { + .routes_to_advertise = NULL, + .learned_routes = NULL, + }; + route_table_dump_one_table(table_id, handle_route_msg, &data); +} diff --git a/controller/route-exchange-netlink.h b/controller/route-exchange-netlink.h index 08102edcf..1eadde491 100644 --- a/controller/route-exchange-netlink.h +++ b/controller/route-exchange-netlink.h @@ -57,4 +57,6 @@ void re_nl_sync_routes(uint32_t table_id, const struct hmap *routes, struct ovs_list *learned_routes, const struct sbrec_datapath_binding *db); +void re_nl_cleanup_routes(uint32_t table_id); + #endif /* route-exchange-netlink.h */ diff --git a/controller/route-exchange.c b/controller/route-exchange.c index 8f740fa91..3c49c60c0 100644 --- a/controller/route-exchange.c +++ b/controller/route-exchange.c @@ -19,6 +19,7 @@ #include <errno.h> #include <net/if.h> +#include <stdbool.h> #include "openvswitch/vlog.h" #include "openvswitch/list.h" @@ -36,6 +37,13 @@ VLOG_DEFINE_THIS_MODULE(route_exchange); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); +struct maintained_route_table_entry { + struct hmap_node node; + uint32_t table_id; +}; + +static struct hmap _maintained_route_tables = + HMAP_INITIALIZER(&_maintained_route_tables); static struct sset _maintained_vrfs = SSET_INITIALIZER(&_maintained_vrfs); struct route_entry { @@ -44,6 +52,38 @@ struct route_entry { const struct sbrec_learned_route *sb_route; }; +static uint32_t +maintained_route_table_hash(uint32_t table_id) +{ + return hash_int(table_id, 0); +} + +static bool +maintained_route_table_contains(uint32_t table_id) +{ + uint32_t hash = maintained_route_table_hash(table_id); + struct maintained_route_table_entry *mrt; + HMAP_FOR_EACH_WITH_HASH (mrt, node, hash, + &_maintained_route_tables) { + if (mrt->table_id == table_id) { + return true; + } + } + return false; +} + +static void +maintained_route_table_add(uint32_t table_id) +{ + if (maintained_route_table_contains(table_id)) { + return; + } + uint32_t hash = maintained_route_table_hash(table_id); + struct maintained_route_table_entry *mrt = xmalloc(sizeof *mrt); + mrt->table_id = table_id; + hmap_insert(&_maintained_route_tables, &mrt->node, hash); +} + static void route_add_entry(struct hmap *routes, const struct sbrec_learned_route *sb_route) @@ -171,6 +211,9 @@ route_exchange_run(struct route_exchange_ctx_in *r_ctx_in, { struct sset old_maintained_vrfs = SSET_INITIALIZER(&old_maintained_vrfs); sset_swap(&_maintained_vrfs, &old_maintained_vrfs); + struct hmap old_maintained_route_table = + HMAP_INITIALIZER(&old_maintained_route_table); + hmap_swap(&_maintained_route_tables, &old_maintained_route_table); const struct advertise_datapath_entry *ad; HMAP_FOR_EACH (ad, node, r_ctx_in->announce_routes) { @@ -198,9 +241,10 @@ route_exchange_run(struct route_exchange_ctx_in *r_ctx_in, sset_find_and_delete(&old_maintained_vrfs, vrf_name); } + maintained_route_table_add(table_id); + struct ovs_list received_routes = OVS_LIST_INITIALIZER( &received_routes); - re_nl_sync_routes(ad->db->tunnel_key, &ad->routes, &received_routes, ad->db); @@ -215,6 +259,16 @@ route_exchange_run(struct route_exchange_ctx_in *r_ctx_in, re_nl_learned_routes_destroy(&received_routes); } + /* Remove routes in tables previously maintained by us. */ + struct maintained_route_table_entry *mrt; + HMAP_FOR_EACH_POP (mrt, node, &old_maintained_route_table) { + if (!maintained_route_table_contains(mrt->table_id)) { + re_nl_cleanup_routes(mrt->table_id); + } + free(mrt); + } + hmap_destroy(&old_maintained_route_table); + /* Remove VRFs previously maintained by us not found in the above loop. */ const char *vrf_name; SSET_FOR_EACH_SAFE (vrf_name, &old_maintained_vrfs) { @@ -229,6 +283,11 @@ route_exchange_run(struct route_exchange_ctx_in *r_ctx_in, void route_exchange_cleanup_vrfs(void) { + struct maintained_route_table_entry *mrt; + HMAP_FOR_EACH (mrt, node, &_maintained_route_tables) { + re_nl_cleanup_routes(mrt->table_id); + } + const char *vrf_name; SSET_FOR_EACH (vrf_name, &_maintained_vrfs) { re_nl_delete_vrf(vrf_name); @@ -238,10 +297,16 @@ route_exchange_cleanup_vrfs(void) void route_exchange_destroy(void) { + struct maintained_route_table_entry *mrt; + HMAP_FOR_EACH_POP (mrt, node, &_maintained_route_tables) { + free(mrt); + } + const char *vrf_name; SSET_FOR_EACH_SAFE (vrf_name, &_maintained_vrfs) { sset_delete(&_maintained_vrfs, SSET_NODE_FROM_NAME(vrf_name)); } sset_destroy(&_maintained_vrfs); + hmap_destroy(&_maintained_route_tables); } diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 10fbcc8e5..62fd77cdb 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -16172,7 +16172,49 @@ check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvr check ovn-nbctl --wait=hv sync check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20 +# Stopping the ovn-controller will clean up the route entries created by it. +# We first need to unset dynamic-routing-maintain-vrf as otherwise it will +# delete the whole vrf. +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \ + options:dynamic-routing-maintain-vrf=false OVS_APP_EXIT_AND_WAIT([ovn-controller]) +AT_CHECK([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [0], [dnl +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink +]) + +# Starting it again will add the routes again. +start_daemon ovn-controller +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" == "running"]) +OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [dnl +blackhole 192.0.2.1 proto 84 metric 1000 +blackhole 192.0.2.2 proto 84 metric 100 +blackhole 192.0.2.3 proto 84 metric 100 +blackhole 192.0.2.10 proto 84 metric 100 +blackhole 198.51.100.0/24 proto 84 metric 1000 +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink]) + +# Stoping with --restart will not touch the routes. +check ovn-appctl -t ovn-controller exit --restart +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" != "running"]) +OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [dnl +blackhole 192.0.2.1 proto 84 metric 1000 +blackhole 192.0.2.2 proto 84 metric 100 +blackhole 192.0.2.3 proto 84 metric 100 +blackhole 192.0.2.10 proto 84 metric 100 +blackhole 198.51.100.0/24 proto 84 metric 1000 +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink]) + +# Now we set maintain-vrf again and stop the ovn-controller. +# It will then remove the VRF. +start_daemon ovn-controller +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" == "running"]) +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \ + options:dynamic-routing-maintain-vrf=true +OVS_APP_EXIT_AND_WAIT([ovn-controller]) +AT_CHECK([ip vrf | grep -q ovnvrf1337], [1], []) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) @@ -16446,7 +16488,49 @@ check ip route add 233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink vrf ovnvr check ovn-nbctl --wait=hv sync check_row_count Learned_Route 1 ip_prefix=233.253.0.0/24 nexthop=192.168.20.20 +# Stopping the ovn-controller will clean up the route entries created by it. +# We first need to unset dynamic-routing-maintain-vrf as otherwise it will +# delete the whole vrf +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \ + options:dynamic-routing-maintain-vrf=false OVS_APP_EXIT_AND_WAIT([ovn-controller]) +AT_CHECK([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [0], [dnl +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink +]) + +# Starting it again will add the routes again. +start_daemon ovn-controller +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" == "running"]) +OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [dnl +blackhole 192.0.2.1 proto 84 metric 100 +blackhole 192.0.2.2 proto 84 metric 100 +blackhole 192.0.2.3 proto 84 metric 100 +blackhole 192.0.2.10 proto 84 metric 100 +blackhole 198.51.100.0/24 proto 84 metric 1000 +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink]) + +# Stoping with --restart will not touch the routes. +check ovn-appctl -t ovn-controller exit --restart +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" != "running"]) +OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf1337 | awk '{$1=$1};1'], [dnl +blackhole 192.0.2.1 proto 84 metric 100 +blackhole 192.0.2.2 proto 84 metric 100 +blackhole 192.0.2.3 proto 84 metric 100 +blackhole 192.0.2.10 proto 84 metric 100 +blackhole 198.51.100.0/24 proto 84 metric 1000 +233.252.0.0/24 via 192.168.10.10 dev lo onlink +233.253.0.0/24 via 192.168.20.20 dev hv1-mll onlink]) + +# Now we set maintain-vrf again and stop the ovn-controller. +# It will then remove the VRF. +start_daemon ovn-controller +OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" == "running"]) +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \ + options:dynamic-routing-maintain-vrf=true +OVS_APP_EXIT_AND_WAIT([ovn-controller]) +AT_CHECK([ip vrf | grep -q ovnvrf1337], [1], []) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) -- 2.47.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev