From: Dumitru Ceara <dce...@redhat.com> For each (Linux) interface that's interesting for the OVN control plane, watch for neighbor (fdb/arp) changes. This allows us to reconcile on changed neighbor entries from outside routing agents (e.g., FRR).
This is the neighbor equivalent of the support added for route tables in 673d90f1173f ("controller: Watch for route changes."). Signed-off-by: Dumitru Ceara <dce...@redhat.com> --- controller/automake.mk | 5 +- controller/neighbor-table-notify-stub.c | 57 ++++++ controller/neighbor-table-notify.c | 244 ++++++++++++++++++++++++ controller/neighbor-table-notify.h | 45 +++++ 4 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 controller/neighbor-table-notify-stub.c create mode 100644 controller/neighbor-table-notify.c create mode 100644 controller/neighbor-table-notify.h diff --git a/controller/automake.mk b/controller/automake.mk index 3eb45475c..6af6ee2a9 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -63,18 +63,21 @@ controller_ovn_controller_SOURCES = \ controller/route.h \ controller/route.c \ controller/garp_rarp.h \ - controller/garp_rarp.c + controller/garp_rarp.c \ + controller/neighbor-table-notify.h if HAVE_NETLINK controller_ovn_controller_SOURCES += \ controller/neighbor-exchange-netlink.h \ controller/neighbor-exchange-netlink.c \ + controller/neighbor-table-notify.c \ controller/route-exchange-netlink.h \ controller/route-exchange-netlink.c \ controller/route-exchange.c \ controller/route-table-notify.c else controller_ovn_controller_SOURCES += \ + controller/neighbor-table-notify-stub.c \ controller/route-exchange-stub.c \ controller/route-table-notify-stub.c endif diff --git a/controller/neighbor-table-notify-stub.c b/controller/neighbor-table-notify-stub.c new file mode 100644 index 000000000..bb4fe5991 --- /dev/null +++ b/controller/neighbor-table-notify-stub.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include <stdbool.h> + +#include "openvswitch/compiler.h" +#include "neighbor-table-notify.h" + +bool +neighbor_table_notify_run(void) +{ + return false; +} + +void +neighbor_table_notify_wait(void) +{ +} + +void +neighbor_table_add_watch_request( + struct hmap *neighbor_table_watches OVS_UNUSED, + int32_t if_index OVS_UNUSED, + const char *if_name OVS_UNUSED) +{ +} + +void +neighbor_table_watch_request_cleanup( + struct hmap *neighbor_table_watches OVS_UNUSED) +{ +} + +void +neighbor_table_notify_update_watches( + const struct hmap *neighbor_table_watches OVS_UNUSED) +{ +} + +void +neighbor_table_notify_destroy(void) +{ +} diff --git a/controller/neighbor-table-notify.c b/controller/neighbor-table-notify.c new file mode 100644 index 000000000..19b975e39 --- /dev/null +++ b/controller/neighbor-table-notify.c @@ -0,0 +1,244 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include <linux/rtnetlink.h> +#include <net/if.h> + +#include "hash.h" +#include "hmapx.h" +#include "lib/util.h" +#include "netlink-notifier.h" +#include "openvswitch/vlog.h" + +#include "neighbor-exchange-netlink.h" +#include "neighbor-table-notify.h" + +VLOG_DEFINE_THIS_MODULE(neighbor_table_notify); + +struct neighbor_table_watch_request { + struct hmap_node node; + int32_t if_index; + char if_name[IFNAMSIZ + 1]; +}; + +struct neighbor_table_watch_entry { + struct hmap_node node; + int32_t if_index; + char if_name[IFNAMSIZ + 1]; +}; + +static struct hmap watches = HMAP_INITIALIZER(&watches); +static bool any_neighbor_table_changed; +static struct ne_table_msg nln_nmsg_change; + +static struct nln *nl_neighbor_handle; +static struct nln_notifier *nl_neighbor_notifier; + +static void fdb_table_change(const void *change_, void *aux); + +static void +neighbor_table_register_notifiers(void) +{ + VLOG_INFO("Adding neighbor table watchers."); + ovs_assert(!nl_neighbor_handle); + + nl_neighbor_handle = nln_create(NETLINK_ROUTE, ne_table_parse, + &nln_nmsg_change); + ovs_assert(nl_neighbor_handle); + + nl_neighbor_notifier = + nln_notifier_create(nl_neighbor_handle, RTNLGRP_NEIGH, + fdb_table_change, NULL); + if (!nl_neighbor_notifier) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Failed to create neighbor table watcher."); + } +} + +static void +neighbor_table_deregister_notifiers(void) +{ + VLOG_INFO("Removing neighbor table watchers."); + ovs_assert(nl_neighbor_handle); + + nln_notifier_destroy(nl_neighbor_notifier); + nln_destroy(nl_neighbor_handle); + nl_neighbor_notifier = NULL; + nl_neighbor_handle = NULL; +} + +static uint32_t +neighbor_table_notify_hash_watch(int32_t if_index) +{ + /* To allow lookups triggered by netlink messages, don't include the + * if_name in the hash. The netlink updates only include if_index. */ + return hash_int(if_index, 0); +} + +static void +add_watch_entry(int32_t if_index, const char *if_name) +{ + VLOG_INFO("Registering new neighbor table watcher " + "for if_index %s (%"PRId32").", + if_name, if_index); + + struct neighbor_table_watch_entry *we; + uint32_t hash = neighbor_table_notify_hash_watch(if_index); + we = xzalloc(sizeof *we); + we->if_index = if_index; + ovs_strzcpy(we->if_name, if_name, IFNAMSIZ + 1); + hmap_insert(&watches, &we->node, hash); + + if (!nl_neighbor_handle) { + neighbor_table_register_notifiers(); + } +} + +static void +remove_watch_entry(struct neighbor_table_watch_entry *we) +{ + VLOG_INFO("Removing neighbor table watcher for table %s (%"PRId32").", + we->if_name, we->if_index); + hmap_remove(&watches, &we->node); + free(we); + + if (hmap_is_empty(&watches)) { + neighbor_table_deregister_notifiers(); + } +} + +bool +neighbor_table_notify_run(void) +{ + any_neighbor_table_changed = false; + + if (nl_neighbor_handle) { + nln_run(nl_neighbor_handle); + } + + return any_neighbor_table_changed; +} + +void +neighbor_table_notify_wait(void) +{ + if (nl_neighbor_handle) { + nln_wait(nl_neighbor_handle); + } +} + +void +neighbor_table_add_watch_request(struct hmap *neighbor_table_watches, + int32_t if_index, const char *if_name) +{ + struct neighbor_table_watch_request *wr = xzalloc(sizeof *wr); + + wr->if_index = if_index; + ovs_strzcpy(wr->if_name, if_name, IFNAMSIZ + 1); + hmap_insert(neighbor_table_watches, &wr->node, + neighbor_table_notify_hash_watch(wr->if_index)); +} + +void +neighbor_table_watch_request_cleanup(struct hmap *neighbor_table_watches) +{ + struct neighbor_table_watch_request *wr; + HMAP_FOR_EACH_POP (wr, node, neighbor_table_watches) { + free(wr); + } +} + +static struct neighbor_table_watch_entry * +find_watch_entry(int32_t if_index, const char *if_name) +{ + struct neighbor_table_watch_entry *we; + uint32_t hash = neighbor_table_notify_hash_watch(if_index); + HMAP_FOR_EACH_WITH_HASH (we, node, hash, &watches) { + if (if_index == we->if_index && !strcmp(if_name, we->if_name)) { + return we; + } + } + return NULL; +} + +static struct neighbor_table_watch_entry * +find_watch_entry_by_if_index(int32_t if_index) +{ + struct neighbor_table_watch_entry *we; + uint32_t hash = neighbor_table_notify_hash_watch(if_index); + HMAP_FOR_EACH_WITH_HASH (we, node, hash, &watches) { + if (if_index == we->if_index) { + return we; + } + } + return NULL; +} + +void +neighbor_table_notify_update_watches(const struct hmap *neighbor_table_watches) +{ + struct hmapx sync_watches = HMAPX_INITIALIZER(&sync_watches); + struct neighbor_table_watch_entry *we; + HMAP_FOR_EACH (we, node, &watches) { + hmapx_add(&sync_watches, we); + } + + struct neighbor_table_watch_request *wr; + HMAP_FOR_EACH (wr, node, neighbor_table_watches) { + we = find_watch_entry(wr->if_index, wr->if_name); + if (we) { + hmapx_find_and_delete(&sync_watches, we); + } else { + add_watch_entry(wr->if_index, wr->if_name); + } + } + + struct hmapx_node *node; + HMAPX_FOR_EACH (node, &sync_watches) { + remove_watch_entry(node->data); + } + + hmapx_destroy(&sync_watches); +} + +void +neighbor_table_notify_destroy(void) +{ + struct neighbor_table_watch_entry *we; + HMAP_FOR_EACH_SAFE (we, node, &watches) { + remove_watch_entry(we); + } +} + +static void +fdb_table_change(const void *change_, void *aux OVS_UNUSED) +{ + /* We currently track whether at least one recent neighbor table change + * was detected. If that's the case already there's no need to + * continue. */ + if (any_neighbor_table_changed) { + return; + } + + const struct ne_table_msg *change = change_; + + if (change && !ne_is_ovn_owned(&change->nd)) { + if (find_watch_entry_by_if_index(change->nd.if_index)) { + any_neighbor_table_changed = true; + } + } +} diff --git a/controller/neighbor-table-notify.h b/controller/neighbor-table-notify.h new file mode 100644 index 000000000..9f21271cc --- /dev/null +++ b/controller/neighbor-table-notify.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NEIGHBOR_TABLE_NOTIFY_H +#define NEIGHBOR_TABLE_NOTIFY_H 1 + +#include <stdbool.h> +#include "openvswitch/hmap.h" + +/* Returns true if any neighbor table has changed enough that we need + * to learn new neighbor entries. */ +bool neighbor_table_notify_run(void); +void neighbor_table_notify_wait(void); + +/* Add a watch request to the hmap. The hmap should later be passed to + * neighbor_table_notify_update_watches*/ +void neighbor_table_add_watch_request(struct hmap *neighbor_table_watches, + int32_t if_index, const char *if_name); + +/* Cleanup all watch request in the provided hmap that where added using + * neighbor_table_add_watch_request. */ +void neighbor_table_watch_request_cleanup( + struct hmap *neighbor_table_watches); + +/* Updates the list of neighbor table watches that are currently active. + * hmap should contain struct neighbor_table_watch_request */ +void neighbor_table_notify_update_watches( + const struct hmap *neighbor_table_watches); + +/* Cleans up all neighbor table watches. */ +void neighbor_table_notify_destroy(void); + +#endif /* NEIGHBOR_TABLE_NOTIFY_H */ -- 2.50.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev