On Tue, Apr 08, 2025 at 07:58:40AM +0200, Ales Musil via dev wrote: > On Mon, Apr 7, 2025 at 2:00 PM Felix Huettner <felix.huettner@stackit.cloud> > wrote: > > > On Thu, Apr 03, 2025 at 01:25:10PM +0200, Ales Musil wrote: > > > On Thu, Mar 20, 2025 at 1:02 PM Felix Huettner via dev < > > > ovs-dev@openvswitch.org> wrote: > > > > > > > Previously on each call to pinctrl_run all addresses that need to be > > > > announced via garps or rarps where computed newly. On larger external > > > > networks that might take significant time. > > > > However the content of the data can only change in a run of the I+P > > > > engine. So we move the whole logic to an engine node and only handle > > the > > > > minimal needed set in pinctrl_run. > > > > > > > > Acked-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com> > > > > Co-Authored-by: Max Lamprecht <max.lamprecht@mail.schwarz> > > > > Signed-off-by: Max Lamprecht <max.lamprecht@mail.schwarz> > > > > Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud> > > > > --- > > > > > > > > > > Hi Felix, > > > > > > thank you for the patch. I have a few comments down below. > > > > Hi Ales, > > > > Hi Felix, > > thank you for working on this.
No problem, it eats a lot of performance for us. > > > > > > > > v6->v7: added ack > > > > > > > > controller/automake.mk | 4 +- > > > > controller/garp_rarp.c | 626 ++++++++++++++++++++++++++++++++++++ > > > > controller/garp_rarp.h | 61 ++++ > > > > controller/ovn-controller.c | 243 +++++++++++++- > > > > controller/pinctrl.c | 559 ++------------------------------ > > > > controller/pinctrl.h | 16 +- > > > > 6 files changed, 967 insertions(+), 542 deletions(-) > > > > create mode 100644 controller/garp_rarp.c > > > > create mode 100644 controller/garp_rarp.h > > > > > > > > diff --git a/controller/automake.mk b/controller/automake.mk > > > > index 067c2e6e4..f0638ea97 100644 > > > > --- a/controller/automake.mk > > > > +++ b/controller/automake.mk > > > > @@ -59,7 +59,9 @@ controller_ovn_controller_SOURCES = \ > > > > controller/route-exchange.h \ > > > > controller/route-table-notify.h \ > > > > controller/route.h \ > > > > - controller/route.c > > > > + controller/route.c \ > > > > + controller/garp_rarp.h \ > > > > + controller/garp_rarp.c > > > > > > > > if HAVE_NETLINK > > > > controller_ovn_controller_SOURCES += \ > > > > diff --git a/controller/garp_rarp.c b/controller/garp_rarp.c > > > > new file mode 100644 > > > > index 000000000..916ff896a > > > > --- /dev/null > > > > +++ b/controller/garp_rarp.c > > > > @@ -0,0 +1,626 @@ > > > > +/* > > > > + * Copyright (c) 2025, STACKIT GmbH & Co. KG > > > > + * > > > > + * 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 <net/if.h> > > > > +#include <stdbool.h> > > > > +#include <stddef.h> > > > > + > > > > +#include "controller/encaps.h" > > > > +#include "controller/local_data.h" > > > > +#include "controller/lport.h" > > > > +#include "hash.h" > > > > +#include "mac-binding-index.h" > > > > +#include "openvswitch/hmap.h" > > > > +#include "openvswitch/vlog.h" > > > > +#include "ovn/lex.h" > > > > +#include "packets.h" > > > > +#include "smap.h" > > > > +#include "sset.h" > > > > +#include "openvswitch/shash.h" > > > > +#include "garp_rarp.h" > > > > +#include "ovn-sb-idl.h" > > > > +#include "vswitch-idl.h" > > > > + > > > > +VLOG_DEFINE_THIS_MODULE(garp_rarp); > > > > +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); > > > > + > > > > +/* Get localnet vifs, local l3gw ports and ofport for localnet patch > > > > ports. */ > > > > +static void > > > > +get_localnet_vifs_l3gwports( > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_name, > > > > + const struct ovsrec_bridge *br_int, > > > > + const struct sbrec_chassis *chassis, > > > > + const struct hmap *local_datapaths, > > > > + struct sset *localnet_vifs, > > > > + struct sset *local_l3gw_ports) > > > > +{ > > > > + for (int i = 0; i < br_int->n_ports; i++) { > > > > + const struct ovsrec_port *port_rec = br_int->ports[i]; > > > > + if (!strcmp(port_rec->name, br_int->name)) { > > > > + continue; > > > > + } > > > > + const char *tunnel_id = smap_get(&port_rec->external_ids, > > > > + "ovn-chassis-id"); > > > > + if (tunnel_id && > > > > + encaps_tunnel_id_match(tunnel_id, chassis->name, NULL, > > > > NULL)) { > > > > + continue; > > > > + } > > > > + const char *localnet = smap_get(&port_rec->external_ids, > > > > + "ovn-localnet-port"); > > > > + if (localnet) { > > > > + continue; > > > > + } > > > > + for (int j = 0; j < port_rec->n_interfaces; j++) { > > > > + const struct ovsrec_interface *iface_rec = > > > > port_rec->interfaces[j]; > > > > + if (!iface_rec->n_ofport) { > > > > + continue; > > > > + } > > > > + /* Get localnet vif. */ > > > > + const char *iface_id = smap_get(&iface_rec->external_ids, > > > > + "iface-id"); > > > > + if (!iface_id) { > > > > + continue; > > > > + } > > > > + const struct sbrec_port_binding *pb > > > > + = lport_lookup_by_name(sbrec_port_binding_by_name, > > > > iface_id); > > > > + if (!pb || pb->chassis != chassis) { > > > > + continue; > > > > + } > > > > + if (!iface_rec->link_state || > > > > + strcmp(iface_rec->link_state, "up")) { > > > > + continue; > > > > + } > > > > + struct local_datapath *ld > > > > + = get_local_datapath(local_datapaths, > > > > + pb->datapath->tunnel_key); > > > > + if (ld && ld->localnet_port) { > > > > + sset_add(localnet_vifs, iface_id); > > > > + } > > > > + } > > > > + } > > > > > > > > > > I know that this is old code but it makes me wonder if there's a better > > > way to find local vifs that are connected to localnet port. I think > > > it should be doable through bindings. > > > > Actually it seems like we can get this easily in the local_datapaths > > loop below. I will update this in the next version. It is so simple that > > i am unsure if i missed something. > > > This part of pinctrl is really old at this point, back then there > probably wasn't anything like local_datapaths. And by using them > we can actually remove the dependency on "en_ovs_open_vswitch" and > "en_ovs_bridge" which is a good thing. Less dependencies that can > cause recomputes is better. Yep "en_ovs_bridge" can be removed. We still need "en_ovs_open_vswitch" to get the chassis record. Maybe we should rework this as well, it seems strange to have nearly all engine nodes get it. > > > > > > > > > > + > > > > + struct sbrec_port_binding *target = > > sbrec_port_binding_index_init_row( > > > > + sbrec_port_binding_by_datapath); > > > > + > > > > + const struct local_datapath *ld; > > > > + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { > > > > + const struct sbrec_port_binding *pb; > > > > + > > > > + if (!ld->localnet_port) { > > > > + continue; > > > > + } > > > > + > > > > + /* Get l3gw ports. Consider port bindings with type > > "l3gateway" > > > > + * that connect to gateway routers (if local), and consider > > port > > > > + * bindings of type "patch" since they might connect to > > > > + * distributed gateway ports with NAT addresses. */ > > > > + > > > > + sbrec_port_binding_index_set_datapath(target, ld->datapath); > > > > + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, > > > > + > > > > sbrec_port_binding_by_datapath) { > > > > + if ((!strcmp(pb->type, "l3gateway") && pb->chassis == > > chassis) > > > > + || !strcmp(pb->type, "patch")) { > > > > + sset_add(local_l3gw_ports, pb->logical_port); > > > > + } > > > > + } > > > > + } > > > > + sbrec_port_binding_index_destroy_row(target); > > > > +} > > > > + > > > > + > > > > +/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from > > > > + * 'addresses' which should be of the format 'MAC [IP1 IP2 ..] > > > > + * [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid > > IPv4 > > > > + * or IPv6 address, and stores them in the 'ipv4_addrs' and > > 'ipv6_addrs' > > > > + * fields of 'laddrs'. The logical port name is stored in 'lport'. > > > > + * > > > > + * Returns true if at least 'MAC' is found in 'address', false > > otherwise. > > > > + * > > > > + * The caller must call destroy_lport_addresses() and free(*lport). */ > > > > +static bool > > > > +extract_addresses_with_port(const char *addresses, > > > > + struct lport_addresses *laddrs, > > > > + char **lport) > > > > +{ > > > > + int ofs; > > > > + if (!extract_addresses(addresses, laddrs, &ofs)) { > > > > + return false; > > > > + } else if (!addresses[ofs]) { > > > > + return true; > > > > + } > > > > + > > > > + struct lexer lexer; > > > > + lexer_init(&lexer, addresses + ofs); > > > > + lexer_get(&lexer); > > > > + > > > > + if (lexer.error || lexer.token.type != LEX_T_ID > > > > + || !lexer_match_id(&lexer, "is_chassis_resident")) { > > > > + VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", > > addresses); > > > > + lexer_destroy(&lexer); > > > > + return true; > > > > + } > > > > + > > > > + if (!lexer_match(&lexer, LEX_T_LPAREN)) { > > > > + VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after " > > > > + "'is_chassis_resident' in address '%s'", > > > > addresses); > > > > + lexer_destroy(&lexer); > > > > + return false; > > > > + } > > > > + > > > > + if (lexer.token.type != LEX_T_STRING) { > > > > + VLOG_INFO_RL(&rl, > > > > + "Syntax error: expecting quoted string after " > > > > + "'is_chassis_resident' in address '%s'", > > addresses); > > > > + lexer_destroy(&lexer); > > > > + return false; > > > > + } > > > > + > > > > + *lport = xstrdup(lexer.token.s); > > > > + > > > > + lexer_get(&lexer); > > > > + if (!lexer_match(&lexer, LEX_T_RPAREN)) { > > > > + VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted > > > > string in " > > > > + "'is_chassis_resident()' in address '%s'", > > > > + addresses); > > > > + lexer_destroy(&lexer); > > > > + return false; > > > > + } > > > > + > > > > + lexer_destroy(&lexer); > > > > + return true; > > > > +} > > > > + > > > > +static void > > > > +consider_nat_address(struct ovsdb_idl_index > > *sbrec_port_binding_by_name, > > > > + const char *nat_address, > > > > + const struct sbrec_port_binding *pb, > > > > + struct sset *nat_address_keys, > > > > + const struct sbrec_chassis *chassis, > > > > + const struct sset *active_tunnels, > > > > + struct shash *nat_addresses, > > > > + struct sset *non_local_lports, > > > > + struct sset *local_lports) > > > > +{ > > > > + struct lport_addresses *laddrs = xmalloc(sizeof *laddrs); > > > > + char *lport = NULL; > > > > + if (!extract_addresses_with_port(nat_address, laddrs, &lport) > > > > + || (!lport && !strcmp(pb->type, "patch"))) { > > > > + destroy_lport_addresses(laddrs); > > > > + free(laddrs); > > > > + free(lport); > > > > + return; > > > > + } > > > > + if (lport) { > > > > + if (!lport_is_chassis_resident(sbrec_port_binding_by_name, > > > > + chassis, active_tunnels, lport)) { > > > > + sset_add(non_local_lports, lport); > > > > + destroy_lport_addresses(laddrs); > > > > + free(laddrs); > > > > + free(lport); > > > > + return; > > > > + } else { > > > > + sset_add(local_lports, lport); > > > > + } > > > > + } > > > > + free(lport); > > > > + > > > > + int i; > > > > + for (i = 0; i < laddrs->n_ipv4_addrs; i++) { > > > > + char *name = xasprintf("%s-%s", pb->logical_port, > > > > + laddrs->ipv4_addrs[i].addr_s); > > > > + sset_add(nat_address_keys, name); > > > > + free(name); > > > > + } > > > > + if (laddrs->n_ipv4_addrs == 0) { > > > > + char *name = xasprintf("%s-noip", pb->logical_port); > > > > + sset_add(nat_address_keys, name); > > > > + free(name); > > > > + } > > > > + shash_add(nat_addresses, pb->logical_port, laddrs); > > > > +} > > > > + > > > > +static void > > > > +get_nat_addresses_and_keys(struct ovsdb_idl_index > > > > *sbrec_port_binding_by_name, > > > > + struct sset *nat_address_keys, > > > > + struct sset *local_l3gw_ports, > > > > + const struct sbrec_chassis *chassis, > > > > + const struct sset *active_tunnels, > > > > + struct shash *nat_addresses, > > > > + struct sset *non_local_lports, > > > > + struct sset *local_lports) > > > > +{ > > > > + const char *gw_port; > > > > + SSET_FOR_EACH (gw_port, local_l3gw_ports) { > > > > + const struct sbrec_port_binding *pb; > > > > + > > > > + pb = lport_lookup_by_name(sbrec_port_binding_by_name, > > gw_port); > > > > + if (!pb) { > > > > + continue; > > > > + } > > > > + > > > > + if (pb->n_nat_addresses) { > > > > + for (int i = 0; i < pb->n_nat_addresses; i++) { > > > > + consider_nat_address(sbrec_port_binding_by_name, > > > > + pb->nat_addresses[i], pb, > > > > + nat_address_keys, chassis, > > > > + active_tunnels, > > > > + nat_addresses, > > > > + non_local_lports, > > > > + local_lports); > > > > + } > > > > + } else { > > > > + /* Continue to support options:nat-addresses for version > > > > + * upgrade. */ > > > > + const char *nat_addresses_options = smap_get(&pb->options, > > > > + > > "nat-addresses"); > > > > + if (nat_addresses_options) { > > > > + consider_nat_address(sbrec_port_binding_by_name, > > > > + nat_addresses_options, pb, > > > > + nat_address_keys, chassis, > > > > + active_tunnels, > > > > + nat_addresses, > > > > + non_local_lports, > > > > + local_lports); > > > > + } > > > > + } > > > > + } > > > > +} > > > > + > > > > +static uint32_t > > > > +garp_rarp_node_hash(const struct eth_addr *ea, uint32_t dp_key, > > > > + uint32_t port_key) > > > > +{ > > > > + return hash_bytes(ea, sizeof *ea, hash_2words(dp_key, port_key)); > > > > +} > > > > + > > > > +static uint32_t > > > > +garp_rarp_node_hash_struct(const struct garp_rarp_node *n) > > > > +{ > > > > + return garp_rarp_node_hash(&n->ea, n->dp_key, n->port_key); > > > > +} > > > > + > > > > +static void > > > > +garp_rarp_node_add(struct hmap *map, const struct eth_addr ea, > > ovs_be32 > > > > ip, > > > > + uint32_t dp_key, uint32_t port_key) > > > > +{ > > > > + struct garp_rarp_node *grn = xmalloc(sizeof *grn); > > > > + grn->ea = ea; > > > > + grn->ipv4 = ip; > > > > + grn->announce_time = time_msec() + 1000; > > > > + grn->backoff = 1000; /* msec. */ > > > > + grn->dp_key = dp_key; > > > > + grn->port_key = port_key; > > > > + hmap_insert(map, &grn->hmap_node, > > garp_rarp_node_hash_struct(grn)); > > > > +} > > > > + > > > > +/* Searches for a given garp_rarp_node in a hmap. Ignores the > > > > announce_time > > > > + * and backoff field since they might be different based on runtime. > > */ > > > > +static struct garp_rarp_node* > > > > +garp_rarp_lookup(const struct hmap *map, const struct garp_rarp_node > > > > *search) > > > > +{ > > > > + struct garp_rarp_node *n; > > > > + uint32_t hash = garp_rarp_node_hash_struct(search); > > > > + HMAP_FOR_EACH_WITH_HASH (n, hmap_node, hash, map) { > > > > + if (!eth_addr_equals(search->ea, n->ea)) { > > > > + continue; > > > > + } > > > > + > > > > + if (search->ipv4 != n->ipv4) { > > > > + continue; > > > > + } > > > > + > > > > + if (search->dp_key != n->dp_key) { > > > > + continue; > > > > + } > > > > + > > > > + if (search->port_key != n->port_key) { > > > > + continue; > > > > + } > > > > + > > > > + return n; > > > > + } > > > > + return NULL; > > > > +} > > > > + > > > > +/* Update or add an IP-MAC binding for 'logical_port'. > > > > + * Caller should make sure that 'ovnsb_idl_txn' is valid. */ > > > > +static void > > > > +mac_binding_add_to_sb(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > + struct ovsdb_idl_index > > > > *sbrec_mac_binding_by_lport_ip, > > > > + const char *logical_port, > > > > + const struct sbrec_datapath_binding *dp, > > > > + struct eth_addr ea, const char *ip, > > > > + bool update_only) > > > > +{ > > > > + /* Convert ethernet argument to string form for database. */ > > > > + char mac_string[ETH_ADDR_STRLEN + 1]; > > > > + snprintf(mac_string, sizeof mac_string, ETH_ADDR_FMT, > > > > ETH_ADDR_ARGS(ea)); > > > > + > > > > + const struct sbrec_mac_binding *b = > > > > + mac_binding_lookup(sbrec_mac_binding_by_lport_ip, > > > > + logical_port, ip); > > > > + if (!b) { > > > > + if (update_only) { > > > > + return; > > > > + } > > > > + b = sbrec_mac_binding_insert(ovnsb_idl_txn); > > > > + sbrec_mac_binding_set_logical_port(b, logical_port); > > > > + sbrec_mac_binding_set_ip(b, ip); > > > > + sbrec_mac_binding_set_datapath(b, dp); > > > > + } > > > > + > > > > + if (strcmp(b->mac, mac_string)) { > > > > + sbrec_mac_binding_set_mac(b, mac_string); > > > > + > > > > + /* For backward compatibility check if timestamp column is > > > > available > > > > + * in SB DB. */ > > > > + if (sbrec_server_has_mac_binding_table_col_timestamp( > > > > + ovsdb_idl_txn_get_idl(ovnsb_idl_txn))) { > > > > + VLOG_DBG("Setting MAC binding timestamp for " > > > > + "ip:%s mac:%s port:%s to %lld", > > > > + b->ip, b->mac, logical_port, time_wall_msec()); > > > > + sbrec_mac_binding_set_timestamp(b, time_wall_msec()); > > > > + } > > > > + } > > > > +} > > > > > > > > > > Unless I've missed something this is duplicated from pinctrl.c, how about > > > moving > > > this function to a module where it could be used by both? > > > > Sorry for missing that. Will be centralized in the next version. > > > > > > > > > > > > + > > > > +/* Simulate the effect of a GARP on local datapaths, i.e., create > > > > MAC_Bindings > > > > + * on peer router datapaths. > > > > + */ > > > > +static void > > > > +send_garp_locally(const struct garp_rarp_ctx_in *r_ctx_in, > > > > + const struct sbrec_port_binding *in_pb, > > > > + struct eth_addr ea, ovs_be32 ip) > > > > +{ > > > > + if (!r_ctx_in->ovnsb_idl_txn) { > > > > + return; > > > > + } > > > > + > > > > + const struct local_datapath *ldp = > > > > + get_local_datapath(r_ctx_in->local_datapaths, > > > > + in_pb->datapath->tunnel_key); > > > > + > > > > + ovs_assert(ldp); > > > > + for (size_t i = 0; i < ldp->n_peer_ports; i++) { > > > > + const struct sbrec_port_binding *local = > > ldp->peer_ports[i].local; > > > > + const struct sbrec_port_binding *remote = > > > > ldp->peer_ports[i].remote; > > > > + > > > > + /* Skip "ingress" port. */ > > > > + if (local == in_pb) { > > > > + continue; > > > > + } > > > > + > > > > + bool update_only = > > !smap_get_bool(&remote->datapath->external_ids, > > > > + > > "always_learn_from_arp_request", > > > > + true); > > > > + > > > > + struct ds ip_s = DS_EMPTY_INITIALIZER; > > > > + > > > > + ip_format_masked(ip, OVS_BE32_MAX, &ip_s); > > > > + mac_binding_add_to_sb(r_ctx_in->ovnsb_idl_txn, > > > > + r_ctx_in->sbrec_mac_binding_by_lport_ip, > > > > + remote->logical_port, remote->datapath, > > > > + ea, ds_cstr(&ip_s), update_only); > > > > + ds_destroy(&ip_s); > > > > + } > > > > +} > > > > + > > > > +/* Add or update a vif for which GARPs need to be announced. */ > > > > +static void > > > > +send_garp_rarp_update(const struct garp_rarp_ctx_in *r_ctx_in, > > > > + const struct sbrec_port_binding *binding_rec, > > > > + struct shash *nat_addresses, > > > > + struct hmap *garp_rarp_data) > > > > +{ > > > > + /* Skip localports as they don't need to be announced */ > > > > + if (!strcmp(binding_rec->type, "localport")) { > > > > + return; > > > > + } > > > > + > > > > + /* Update GARP for NAT IP if it exists. Consider port bindings > > with > > > > type > > > > + * "l3gateway" for logical switch ports attached to gateway > > routers, > > > > and > > > > + * port bindings with type "patch" for logical switch ports > > attached > > > > to > > > > + * distributed gateway ports. */ > > > > + if (!strcmp(binding_rec->type, "l3gateway") > > > > + || !strcmp(binding_rec->type, "patch")) { > > > > + struct lport_addresses *laddrs = NULL; > > > > + while ((laddrs = shash_find_and_delete(nat_addresses, > > > > + > > > > binding_rec->logical_port))) { > > > > + int i; > > > > + for (i = 0; i < laddrs->n_ipv4_addrs; i++) { > > > > + garp_rarp_node_add(garp_rarp_data, laddrs->ea, > > > > + laddrs->ipv4_addrs[i].addr, > > > > + binding_rec->datapath->tunnel_key, > > > > + binding_rec->tunnel_key); > > > > + send_garp_locally(r_ctx_in, binding_rec, laddrs->ea, > > > > + laddrs->ipv4_addrs[i].addr); > > > > + } > > > > + /* > > > > + * Send RARPs even if we do not have a ipv4 address as it > > e.g. > > > > + * happens on ipv6 only ports. > > > > + */ > > > > + if (laddrs->n_ipv4_addrs == 0) { > > > > + garp_rarp_node_add(garp_rarp_data, laddrs->ea, > > > > + 0, binding_rec->datapath->tunnel_key, > > > > + binding_rec->tunnel_key); > > > > + } > > > > + destroy_lport_addresses(laddrs); > > > > + free(laddrs); > > > > + } > > > > + return; > > > > + } > > > > + > > > > + /* Add GARP for new vif. */ > > > > + int i; > > > > + for (i = 0; i < binding_rec->n_mac; i++) { > > > > + struct lport_addresses laddrs; > > > > + ovs_be32 ip = 0; > > > > + if (!extract_lsp_addresses(binding_rec->mac[i], &laddrs)) { > > > > + continue; > > > > + } > > > > + > > > > + if (laddrs.n_ipv4_addrs) { > > > > + ip = laddrs.ipv4_addrs[0].addr; > > > > + } > > > > + > > > > + garp_rarp_node_add(garp_rarp_data, > > > > + laddrs.ea, ip, > > > > + binding_rec->datapath->tunnel_key, > > > > + binding_rec->tunnel_key); > > > > + if (ip) { > > > > + send_garp_locally(r_ctx_in, binding_rec, laddrs.ea, ip); > > > > + } > > > > + > > > > + destroy_lport_addresses(&laddrs); > > > > + break; > > > > + } > > > > +} > > > > + > > > > +static struct garp_rarp_node* > > > > +garp_rarp_node_clone(const struct garp_rarp_node *src) > > > > +{ > > > > + struct garp_rarp_node *dst = xzalloc(sizeof *dst); > > > > + dst->ea = src->ea; > > > > + dst->ipv4 = src->ipv4; > > > > + dst->announce_time = time_msec() + 1000; > > > > + dst->backoff = 1000; > > > > + dst->dp_key = src->dp_key; > > > > + dst->port_key = src->port_key; > > > > + return dst; > > > > +} > > > > + > > > > +static void > > > > +garp_rarp_clear(struct garp_rarp_ctx_in *r_ctx_in, > > > > + struct garp_rarp_ctx_out *r_ctx_out) > > > > +{ > > > > + struct garp_rarp_node *grn; > > > > + HMAP_FOR_EACH_POP (grn, hmap_node, r_ctx_out->garp_rarp_data) { > > > > + garp_rarp_node_free(grn); > > > > + } > > > > + hmap_clear(r_ctx_out->garp_rarp_data); > > > > + sset_clear(r_ctx_in->non_local_lports); > > > > + sset_clear(r_ctx_in->local_lports); > > > > +} > > > > + > > > > +void > > > > +garp_rarp_run(struct garp_rarp_ctx_in *r_ctx_in, > > > > + struct garp_rarp_ctx_out *r_ctx_out) > > > > +{ > > > > + garp_rarp_clear(r_ctx_in, r_ctx_out); > > > > + > > > > + struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs); > > > > + struct sset local_l3gw_ports = > > SSET_INITIALIZER(&local_l3gw_ports); > > > > + struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys); > > > > + struct shash nat_addresses; > > > > + > > > > + shash_init(&nat_addresses); > > > > + > > > > + > > get_localnet_vifs_l3gwports(r_ctx_in->sbrec_port_binding_by_datapath, > > > > + r_ctx_in->sbrec_port_binding_by_name, > > > > + r_ctx_in->br_int, r_ctx_in->chassis, > > > > + r_ctx_in->local_datapaths, > > > > + &localnet_vifs, &local_l3gw_ports); > > > > + > > > > + get_nat_addresses_and_keys(r_ctx_in->sbrec_port_binding_by_name, > > > > + &nat_ip_keys, &local_l3gw_ports, > > > > + r_ctx_in->chassis, > > > > r_ctx_in->active_tunnels, > > > > + &nat_addresses, > > r_ctx_in->non_local_lports, > > > > + r_ctx_in->local_lports); > > > > + > > > > + /* Update send_garp_rarp_data. */ > > > > + const char *iface_id; > > > > + SSET_FOR_EACH (iface_id, &localnet_vifs) { > > > > + const struct sbrec_port_binding *pb = lport_lookup_by_name( > > > > + r_ctx_in->sbrec_port_binding_by_name, iface_id); > > > > + if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", > > > > false)) { > > > > + send_garp_rarp_update(r_ctx_in, pb, &nat_addresses, > > > > + r_ctx_out->garp_rarp_data); > > > > + } > > > > + } > > > > + > > > > + /* Update send_garp_rarp_data for nat-addresses. */ > > > > + const char *gw_port; > > > > + SSET_FOR_EACH (gw_port, &local_l3gw_ports) { > > > > + const struct sbrec_port_binding *pb = lport_lookup_by_name( > > > > + r_ctx_in->sbrec_port_binding_by_name, gw_port); > > > > + if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", > > > > false)) { > > > > + send_garp_rarp_update(r_ctx_in, pb, &nat_addresses, > > > > + r_ctx_out->garp_rarp_data); > > > > + } > > > > + } > > > > + > > > > + sset_destroy(&localnet_vifs); > > > > + sset_destroy(&local_l3gw_ports); > > > > + > > > > + struct shash_node *iter; > > > > + SHASH_FOR_EACH_SAFE (iter, &nat_addresses) { > > > > + struct lport_addresses *laddrs = iter->data; > > > > + destroy_lport_addresses(laddrs); > > > > + shash_delete(&nat_addresses, iter); > > > > + free(laddrs); > > > > + } > > > > + shash_destroy(&nat_addresses); > > > > + > > > > + sset_destroy(&nat_ip_keys); > > > > +} > > > > + > > > > +/* Syncs a hmap of garp_rarp_node to another hmap. The elements in the > > > > + * destination hmap are initialized with valid announce times based > > on the > > > > + * current timestamp. > > > > + * If source is NULL then destination will at most change the timer if > > > > + * reset_timers is set. > > > > + * If reset_timers is true then the timers of all entries in the > > > > destination > > > > + * hmap will be reset to default values. > > > > + * Returns true if the destination hmap was chnaged. */ > > > > +bool > > > > +garp_rarp_sync(const struct hmap *source, struct hmap *dest, bool > > > > reset_timers) > > > > +{ > > > > + struct garp_rarp_node *grn; > > > > + bool changed = false; > > > > + > > > > + HMAP_FOR_EACH_SAFE (grn, hmap_node, dest) { > > > > + if (source && !garp_rarp_lookup(source, grn)) { > > > > + hmap_remove(dest, &grn->hmap_node); > > > > + garp_rarp_node_free(grn); > > > > + changed = true; > > > > + } else if (reset_timers) { > > > > + grn->announce_time = time_msec() + 1000; > > > > + grn->backoff = 1000; > > > > + changed = true; > > > > + } > > > > + } > > > > + > > > > + if (source) { > > > > + HMAP_FOR_EACH (grn, hmap_node, source) { > > > > + if (!garp_rarp_lookup(dest, grn)) { > > > > + uint32_t hash = garp_rarp_node_hash_struct(grn); > > > > + hmap_insert(dest, > > &garp_rarp_node_clone(grn)->hmap_node, > > > > hash); > > > > + changed = true; > > > > + } > > > > + } > > > > + } > > > > + > > > > + return changed; > > > > +} > > > > > > > > > > That's a lot of copying between the hmaps, why can't we indicate that the > > > announcement was sent for the last time and use just the status instead > > > of copying parts of the hmap? > > > > The idea was that any state that needs to be persistently kept is part > > of the pinctrl module. The engine node is then free to discard the hmap > > and generate it again from scratch. > > > > Also the data is needed in the pinctrl thread separately from the main > > thread. So copying seemed like the best solution to avoid needing to > > lock the data between pinctrl and the engine node. > > > > Yes the locking might be a problem, how about using CMAP instead? > That should get rid of the locking problem and we could avoid the > copies. I'll leave it up to you if you would like to do it as part > of this patch or as follow up. I'll include it in these changes. It seems to be cleaner when i rework it in one go. Thanks a lot, Felix > > > > > > > > > > > > > > + > > > > +void > > > > +garp_rarp_node_free(struct garp_rarp_node *garp_rarp) > > > > +{ > > > > + free(garp_rarp); > > > > +} > > > > diff --git a/controller/garp_rarp.h b/controller/garp_rarp.h > > > > new file mode 100644 > > > > index 000000000..b85e75a7c > > > > --- /dev/null > > > > +++ b/controller/garp_rarp.h > > > > @@ -0,0 +1,61 @@ > > > > +/* > > > > + * Copyright (c) 2025, STACKIT GmbH & Co. KG > > > > + * > > > > + * 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 GARP_RARP_H > > > > +#define GARP_RARP_H 1 > > > > + > > > > +#include "openvswitch/hmap.h" > > > > +#include "openvswitch/types.h" > > > > + > > > > +/* Contains a single mac and ip address that should be announced. */ > > > > +struct garp_rarp_node { > > > > + struct hmap_node hmap_node; > > > > + struct eth_addr ea; /* Ethernet address of port. */ > > > > + ovs_be32 ipv4; /* Ipv4 address of port. */ > > > > + long long int announce_time; /* Next announcement in ms. > > > > + * If LLONG_MAX there should be no > > > > + * annoucement. */ > > > > + int backoff; /* Backoff timeout for the next > > > > + * announcement (in msecs). */ > > > > + uint32_t dp_key; /* Datapath used to output this > > GARP. */ > > > > + uint32_t port_key; /* Port to inject the GARP into. */ > > > > +}; > > > > + > > > > +struct garp_rarp_ctx_in { > > > > + struct ovsdb_idl_txn *ovnsb_idl_txn; > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_datapath; > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_name; > > > > + struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip; > > > > + const struct sbrec_ecmp_nexthop_table *ecmp_nh_table; > > > > + const struct ovsrec_bridge *br_int; > > > > + const struct sbrec_chassis *chassis; > > > > + const struct hmap *local_datapaths; > > > > + const struct sset *active_tunnels; > > > > + struct sset *non_local_lports; > > > > + struct sset *local_lports; > > > > +}; > > > > + > > > > +struct garp_rarp_ctx_out { > > > > + /* Contains struct garp_rarp_node. */ > > > > + struct hmap *garp_rarp_data; > > > > +}; > > > > + > > > > +void garp_rarp_run(struct garp_rarp_ctx_in *, struct > > garp_rarp_ctx_out *); > > > > +bool garp_rarp_sync(const struct hmap *source, struct hmap *dest, > > > > + bool reset_timers); > > > > +void garp_rarp_node_free(struct garp_rarp_node *); > > > > + > > > > +#endif /* GARP_RARP_H */ > > > > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > > > > index cfed5282c..6f1b27876 100644 > > > > --- a/controller/ovn-controller.c > > > > +++ b/controller/ovn-controller.c > > > > @@ -92,6 +92,7 @@ > > > > #include "route.h" > > > > #include "route-exchange.h" > > > > #include "route-table-notify.h" > > > > +#include "garp_rarp.h" > > > > > > > > VLOG_DEFINE_THIS_MODULE(main); > > > > > > > > @@ -4952,6 +4953,14 @@ controller_output_route_exchange_handler(struct > > > > engine_node *node, > > > > return true; > > > > } > > > > > > > > +static bool > > > > +controller_output_garp_rarp_handler(struct engine_node *node, > > > > + void *data OVS_UNUSED) > > > > +{ > > > > + engine_set_node_state(node, EN_UPDATED); > > > > + return true; > > > > +} > > > > + > > > > /* Handles sbrec_chassis changes. > > > > * If a new chassis is added or removed return false, so that > > > > * flows are recomputed. For any updates, there is no need for > > > > @@ -5305,6 +5314,216 @@ en_route_table_notify_cleanup(void *data > > > > OVS_UNUSED) > > > > { > > > > } > > > > > > > > +struct ed_type_garp_rarp { > > > > + struct hmap garp_rarp_data; > > > > + > > > > + /* non_local_lports and local_lports are used in the incremental > > > > handlers > > > > + * to trigger updates if such a port changes. */ > > > > + struct sset non_local_lports; /* lports that we did not consider > > > > because > > > > + they where not local. */ > > > > + struct sset local_lports; /* lports where we did consider the > > > > addresses > > > > + because they where local. */ > > > > +}; > > > > + > > > > +static void > > > > +en_garp_rarp_run(struct engine_node *node, void *data_) > > > > +{ > > > > + struct ed_type_garp_rarp *data = data_; > > > > + > > > > + const struct ovsrec_open_vswitch_table *ovs_table = > > > > + EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node)); > > > > + const char *chassis_id = get_ovs_chassis_id(ovs_table); > > > > + ovs_assert(chassis_id); > > > > + > > > > + struct ovsdb_idl_index *sbrec_chassis_by_name = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_chassis", node), > > > > + "name"); > > > > + const struct sbrec_chassis *chassis > > > > + = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id); > > > > + ovs_assert(chassis); > > > > + > > > > + const struct ovsrec_bridge_table *bridge_table = > > > > + EN_OVSDB_GET(engine_get_input("OVS_bridge", node)); > > > > + const struct ovsrec_bridge *br_int = get_br_int(bridge_table, > > > > ovs_table); > > > > + > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_datapath = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_port_binding", node), > > > > + "datapath"); > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_port_binding", node), > > > > + "name"); > > > > + struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_mac_binding", node), > > > > + "lport_ip"); > > > > + > > > > + struct ovsdb_idl_txn *ovnsb_idl_txn = > > > > engine_get_context()->ovnsb_idl_txn; > > > > + > > > > + const struct sbrec_ecmp_nexthop_table *ecmp_nh_table = > > > > + > > > > sbrec_ecmp_nexthop_table_get(ovsdb_idl_txn_get_idl(ovnsb_idl_txn)); > > > > + > > > > + struct ed_type_runtime_data *rt_data = > > > > + engine_get_input_data("runtime_data", node); > > > > + > > > > + struct garp_rarp_ctx_in r_ctx_in = { > > > > + .ovnsb_idl_txn = ovnsb_idl_txn, > > > > + .sbrec_port_binding_by_datapath = > > sbrec_port_binding_by_datapath, > > > > + .sbrec_port_binding_by_name = sbrec_port_binding_by_name, > > > > + .sbrec_mac_binding_by_lport_ip = > > sbrec_mac_binding_by_lport_ip, > > > > + .ecmp_nh_table = ecmp_nh_table, > > > > + .br_int = br_int, > > > > + .chassis = chassis, > > > > + .active_tunnels = &rt_data->active_tunnels, > > > > + .local_datapaths = &rt_data->local_datapaths, > > > > + .non_local_lports = &data->non_local_lports, > > > > + .local_lports = &data->local_lports, > > > > + }; > > > > + > > > > + struct garp_rarp_ctx_out r_ctx_out = { > > > > + .garp_rarp_data = &data->garp_rarp_data, > > > > + }; > > > > + > > > > + garp_rarp_run(&r_ctx_in, &r_ctx_out); > > > > +} > > > > + > > > > + > > > > +static void * > > > > +en_garp_rarp_init(struct engine_node *node OVS_UNUSED, > > > > + struct engine_arg *arg OVS_UNUSED) > > > > +{ > > > > + struct ed_type_garp_rarp *gr = xmalloc(sizeof *gr); > > > > + hmap_init(&gr->garp_rarp_data); > > > > + sset_init(&gr->non_local_lports); > > > > + sset_init(&gr->local_lports); > > > > + return gr; > > > > +} > > > > + > > > > +static void > > > > +en_garp_rarp_cleanup(void *data_) > > > > +{ > > > > + struct ed_type_garp_rarp *data = data_; > > > > + struct garp_rarp_node *grn; > > > > + HMAP_FOR_EACH_POP (grn, hmap_node, &data->garp_rarp_data) { > > > > + garp_rarp_node_free(grn); > > > > + } > > > > + hmap_destroy(&data->garp_rarp_data); > > > > + sset_destroy(&data->non_local_lports); > > > > + sset_destroy(&data->local_lports); > > > > +} > > > > + > > > > +static bool > > > > +garp_rarp_sb_port_binding_handler(struct engine_node *node, > > > > + void *data_) > > > > +{ > > > > + /* We need to handle a change if there was change on a datapath > > with > > > > + * a localnet port. > > > > + * Also the ha_chassis status of a port binding might change. */ > > > > + struct ed_type_garp_rarp *data = data_; > > > > + > > > > + const struct ovsrec_open_vswitch_table *ovs_table = > > > > + EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node)); > > > > + const char *chassis_id = get_ovs_chassis_id(ovs_table); > > > > + ovs_assert(chassis_id); > > > > + > > > > + struct ovsdb_idl_index *sbrec_chassis_by_name = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_chassis", node), > > > > + "name"); > > > > + const struct sbrec_chassis *chassis > > > > + = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id); > > > > + ovs_assert(chassis); > > > > + > > > > + struct ed_type_runtime_data *rt_data = > > > > + engine_get_input_data("runtime_data", node); > > > > + const struct sbrec_port_binding_table *port_binding_table = > > > > + EN_OVSDB_GET(engine_get_input("SB_port_binding", node)); > > > > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > > > > + engine_ovsdb_node_get_index( > > > > + engine_get_input("SB_port_binding", node), > > > > + "name"); > > > > + > > > > + const struct sbrec_port_binding *pb; > > > > + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb, > > port_binding_table) { > > > > + struct local_datapath *ld = get_local_datapath( > > > > + &rt_data->local_datapaths, pb->datapath->tunnel_key); > > > > + > > > > + if (!ld || ld->localnet_port) { > > > > + /* XXX: actually handle this incrementally. */ > > > > + return false; > > > > + } > > > > + > > > > + if (sset_contains(&data->non_local_lports, pb->logical_port) > > && > > > > + lport_is_chassis_resident(sbrec_port_binding_by_name, > > chassis, > > > > + &rt_data->active_tunnels, > > > > + pb->logical_port)) { > > > > + /* XXX: actually handle this incrementally. */ > > > > + return false; > > > > + } > > > > + > > > > + if (sset_contains(&data->local_lports, pb->logical_port) && > > > > + !lport_is_chassis_resident(sbrec_port_binding_by_name, > > > > chassis, > > > > + &rt_data->active_tunnels, > > > > + pb->logical_port)) { > > > > + /* XXX: actually handle this incrementally. */ > > > > + return false; > > > > + } > > > > + } > > > > + > > > > + return true; > > > > +} > > > > + > > > > +static bool > > > > +garp_rarp_runtime_data_handler(struct engine_node *node, void *data > > > > OVS_UNUSED) > > > > +{ > > > > + /* We use two elements from rt_data: > > > > + * 1. active_tunnels: There is currently not incremental > > processing > > > > for > > > > + * this in runtime_data. So we just fall back to a recompute. > > > > + * 2. local_datapaths: This has incremental processing on the > > > > runtime_data > > > > + * side. We are only interested in datapaths with a localnet > > port > > > > so > > > > + * we just recompute if there is one in there. Otherwise the > > > > change is > > > > + * irrelevant for us. */ > > > > + > > > > + struct ed_type_runtime_data *rt_data = > > > > + engine_get_input_data("runtime_data", node); > > > > + > > > > + /* There are no tracked data. Fall back to full recompute. */ > > > > + if (!rt_data->tracked) { > > > > + return false; > > > > + } > > > > + > > > > + struct tracked_datapath *tdp; > > > > + HMAP_FOR_EACH (tdp, node, &rt_data->tracked_dp_bindings) { > > > > + if (tdp->tracked_type == TRACKED_RESOURCE_REMOVED) { > > > > + /* This is currently not handled incrementally in > > runtime_data > > > > + * so it should never happen. Recompute just in case. */ > > > > + return false; > > > > + } > > > > + > > > > + struct local_datapath *ld = get_local_datapath( > > > > + &rt_data->local_datapaths, tdp->dp->tunnel_key); > > > > + > > > > + if (!ld || ld->localnet_port) { > > > > + /* XXX: actually handle this incrementally. */ > > > > + return false; > > > > + } > > > > + > > > > + /* The localnet port might also have been removed. */ > > > > + struct tracked_lport *tlp; > > > > + struct shash_node *sn; > > > > + SHASH_FOR_EACH (sn, &tdp->lports) { > > > > + tlp = sn->data; > > > > + if (!strcmp(tlp->pb->type, "localnet")) { > > > > + return false; > > > > + } > > > > + } > > > > + } > > > > + > > > > + return true; > > > > +} > > > > + > > > > /* Returns false if the northd internal version stored in SB_Global > > > > * and ovn-controller internal version don't match. > > > > */ > > > > @@ -5614,6 +5833,7 @@ main(int argc, char *argv[]) > > > > ENGINE_NODE(route, "route"); > > > > ENGINE_NODE(route_table_notify, "route_table_notify"); > > > > ENGINE_NODE(route_exchange, "route_exchange"); > > > > + ENGINE_NODE(garp_rarp, "garp_rarp"); > > > > > > > > #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR); > > > > SB_NODES > > > > @@ -5826,6 +6046,15 @@ main(int argc, char *argv[]) > > > > engine_add_input(&en_dns_cache, &en_sb_dns, > > > > dns_cache_sb_dns_handler); > > > > > > > > + engine_add_input(&en_garp_rarp, &en_ovs_open_vswitch, NULL); > > > > + engine_add_input(&en_garp_rarp, &en_ovs_bridge, NULL); > > > > + engine_add_input(&en_garp_rarp, &en_sb_chassis, NULL); > > > > + engine_add_input(&en_garp_rarp, &en_sb_port_binding, > > > > + garp_rarp_sb_port_binding_handler); > > > > + engine_add_input(&en_garp_rarp, &en_sb_mac_binding, NULL); > > > > > > > > > > This dependency leads to recompute every time we insert MB locally. > > > It seems that we need this only for index, so it should be noop handler. > > > > Yes, thanks. > > > > > > > > > > > > + engine_add_input(&en_garp_rarp, &en_runtime_data, > > > > + garp_rarp_runtime_data_handler); > > > > + > > > > engine_add_input(&en_controller_output, &en_dns_cache, > > > > NULL); > > > > engine_add_input(&en_controller_output, &en_lflow_output, > > > > @@ -5838,6 +6067,8 @@ main(int argc, char *argv[]) > > > > controller_output_bfd_chassis_handler); > > > > engine_add_input(&en_controller_output, &en_route_exchange, > > > > controller_output_route_exchange_handler); > > > > + engine_add_input(&en_controller_output, &en_garp_rarp, > > > > + controller_output_garp_rarp_handler); > > > > > > > > engine_add_input(&en_acl_id, &en_sb_acl_id, NULL); > > > > engine_add_input(&en_controller_output, &en_acl_id, > > > > @@ -5874,6 +6105,8 @@ main(int argc, char *argv[]) > > > > > > > > sbrec_chassis_template_var_index_by_chassis); > > > > engine_ovsdb_node_add_index(&en_sb_learned_route, "datapath", > > > > > > sbrec_learned_route_index_by_datapath); > > > > + engine_ovsdb_node_add_index(&en_sb_mac_binding, "lport_ip", > > > > + sbrec_mac_binding_by_lport_ip); > > > > engine_ovsdb_node_add_index(&en_ovs_flow_sample_collector_set, > > "id", > > > > > > ovsrec_flow_sample_collector_set_by_id); > > > > engine_ovsdb_node_add_index(&en_ovs_port, "qos", > > ovsrec_port_by_qos); > > > > @@ -6290,9 +6523,12 @@ main(int argc, char *argv[]) > > > > stopwatch_start(PINCTRL_RUN_STOPWATCH_NAME, > > > > time_msec()); > > > > pinctrl_update(ovnsb_idl_loop.idl); > > > > + const struct ed_type_garp_rarp *grd = > > > > engine_get_data( > > > > + &en_garp_rarp); > > > > + const struct hmap *garp_rarp_data = > > > > + grd ? &grd->garp_rarp_data : NULL; > > > > pinctrl_run(ovnsb_idl_txn, > > > > sbrec_datapath_binding_by_key, > > > > - sbrec_port_binding_by_datapath, > > > > sbrec_port_binding_by_key, > > > > sbrec_port_binding_by_name, > > > > sbrec_mac_binding_by_lport_ip, > > > > @@ -6308,13 +6544,14 @@ main(int argc, char *argv[]) > > > > > > > > sbrec_bfd_table_get(ovnsb_idl_loop.idl), > > > > sbrec_ecmp_nexthop_table_get( > > > > ovnsb_idl_loop.idl), > > > > - br_int, chassis, > > > > + chassis, > > > > &runtime_data->local_datapaths, > > > > &runtime_data->active_tunnels, > > > > > > > > &runtime_data->local_active_ports_ipv6_pd, > > > > > > &runtime_data->local_active_ports_ras, > > > > ovsrec_open_vswitch_table_get( > > > > - ovs_idl_loop.idl)); > > > > + ovs_idl_loop.idl), > > > > + garp_rarp_data); > > > > stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME, > > > > time_msec()); > > > > mirror_run(ovs_idl_txn, > > > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > > > > index 1481b2547..6b4a4a520 100644 > > > > --- a/controller/pinctrl.c > > > > +++ b/controller/pinctrl.c > > > > @@ -64,6 +64,7 @@ > > > > #include "ip-mcast.h" > > > > #include "ovn-sb-idl.h" > > > > #include "ovn-dns.h" > > > > +#include "garp_rarp.h" > > > > > > > > VLOG_DEFINE_THIS_MODULE(pinctrl); > > > > > > > > @@ -233,15 +234,9 @@ static void destroy_send_arps_nds(void); > > > > static void send_garp_rarp_wait(long long int send_garp_rarp_time); > > > > static void send_arp_nd_wait(long long int send_arp_nd_time); > > > > static void send_garp_rarp_prepare( > > > > - struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > - struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > > > > - struct ovsdb_idl_index *sbrec_port_binding_by_name, > > > > - struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, > > > > + const struct hmap *garp_rarp_data, > > > > const struct sbrec_ecmp_nexthop_table *ecmp_nh_table, > > > > - const struct ovsrec_bridge *, > > > > - const struct sbrec_chassis *, > > > > - const struct hmap *local_datapaths, > > > > - const struct sset *active_tunnels, > > > > + const struct sbrec_chassis *chassis, > > > > const struct ovsrec_open_vswitch_table *ovs_table) > > > > OVS_REQUIRES(pinctrl_mutex); > > > > static void send_garp_rarp_run(struct rconn *swconn, > > > > @@ -4066,7 +4061,6 @@ pinctrl_update(const struct ovsdb_idl *idl) > > > > void > > > > pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > struct ovsdb_idl_index *sbrec_datapath_binding_by_key, > > > > - struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > > > > struct ovsdb_idl_index *sbrec_port_binding_by_key, > > > > struct ovsdb_idl_index *sbrec_port_binding_by_name, > > > > struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, > > > > @@ -4078,13 +4072,13 @@ pinctrl_run(struct ovsdb_idl_txn > > *ovnsb_idl_txn, > > > > const struct sbrec_mac_binding_table *mac_binding_table, > > > > const struct sbrec_bfd_table *bfd_table, > > > > const struct sbrec_ecmp_nexthop_table *ecmp_nh_table, > > > > - const struct ovsrec_bridge *br_int, > > > > const struct sbrec_chassis *chassis, > > > > const struct hmap *local_datapaths, > > > > const struct sset *active_tunnels, > > > > const struct shash *local_active_ports_ipv6_pd, > > > > const struct shash *local_active_ports_ras, > > > > - const struct ovsrec_open_vswitch_table *ovs_table) > > > > + const struct ovsrec_open_vswitch_table *ovs_table, > > > > + const struct hmap *garp_rarp_data) > > > > { > > > > ovs_mutex_lock(&pinctrl_mutex); > > > > run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key, > > > > @@ -4092,11 +4086,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > sbrec_mac_binding_by_lport_ip); > > > > run_put_vport_bindings(ovnsb_idl_txn, > > sbrec_datapath_binding_by_key, > > > > sbrec_port_binding_by_key, chassis); > > > > - send_garp_rarp_prepare(ovnsb_idl_txn, > > sbrec_port_binding_by_datapath, > > > > - sbrec_port_binding_by_name, > > > > - sbrec_mac_binding_by_lport_ip, > > ecmp_nh_table, > > > > - br_int, chassis, local_datapaths, > > > > active_tunnels, > > > > - ovs_table); > > > > + send_garp_rarp_prepare(garp_rarp_data, ecmp_nh_table, > > > > + chassis, ovs_table); > > > > prepare_ipv6_ras(local_active_ports_ras, > > sbrec_port_binding_by_name); > > > > prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name, > > > > local_active_ports_ipv6_pd, chassis, > > > > @@ -4785,47 +4776,6 @@ mac_binding_add_to_sb(struct ovsdb_idl_txn > > > > *ovnsb_idl_txn, > > > > } > > > > } > > > > > > > > -/* Simulate the effect of a GARP on local datapaths, i.e., create > > > > MAC_Bindings > > > > - * on peer router datapaths. > > > > - */ > > > > -static void > > > > -send_garp_locally(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > - struct ovsdb_idl_index > > *sbrec_mac_binding_by_lport_ip, > > > > - const struct hmap *local_datapaths, > > > > - const struct sbrec_port_binding *in_pb, > > > > - struct eth_addr ea, ovs_be32 ip) > > > > -{ > > > > - if (!ovnsb_idl_txn) { > > > > - return; > > > > - } > > > > - > > > > - const struct local_datapath *ldp = > > > > - get_local_datapath(local_datapaths, > > in_pb->datapath->tunnel_key); > > > > - > > > > - ovs_assert(ldp); > > > > - for (size_t i = 0; i < ldp->n_peer_ports; i++) { > > > > - const struct sbrec_port_binding *local = > > ldp->peer_ports[i].local; > > > > - const struct sbrec_port_binding *remote = > > > > ldp->peer_ports[i].remote; > > > > - > > > > - /* Skip "ingress" port. */ > > > > - if (local == in_pb) { > > > > - continue; > > > > - } > > > > - > > > > - bool update_only = > > !smap_get_bool(&remote->datapath->external_ids, > > > > - > > "always_learn_from_arp_request", > > > > - true); > > > > - > > > > - struct ds ip_s = DS_EMPTY_INITIALIZER; > > > > - > > > > - ip_format_masked(ip, OVS_BE32_MAX, &ip_s); > > > > - mac_binding_add_to_sb(ovnsb_idl_txn, > > > > sbrec_mac_binding_by_lport_ip, > > > > - remote->logical_port, remote->datapath, > > > > - ea, ds_cstr(&ip_s), update_only); > > > > - ds_destroy(&ip_s); > > > > - } > > > > -} > > > > - > > > > static void > > > > run_put_mac_binding(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > struct ovsdb_idl_index > > *sbrec_datapath_binding_by_key, > > > > @@ -4977,173 +4927,25 @@ wait_put_mac_bindings(struct ovsdb_idl_txn > > > > *ovnsb_idl_txn) > > > > * are needed for switches and routers on the broadcast segment to > > update > > > > * their port-mac and ARP tables. > > > > */ > > > > -struct garp_rarp_data { > > > > - struct eth_addr ea; /* Ethernet address of port. */ > > > > - ovs_be32 ipv4; /* Ipv4 address of port. */ > > > > - long long int announce_time; /* Next announcement in ms. */ > > > > - int backoff; /* Backoff timeout for the next > > > > - * announcement (in msecs). */ > > > > - uint32_t dp_key; /* Datapath used to output this > > GARP. */ > > > > - uint32_t port_key; /* Port to inject the GARP into. */ > > > > -}; > > > > > > > > -/* Contains GARPs/RARPs to be sent. Protected by pinctrl_mutex*/ > > > > -static struct shash send_garp_rarp_data; > > > > +/* Contains GARPs/RARPs to be sent in struct garp_rarp_node. > > > > + * Protected by pinctrl_mutex. */ > > > > +static struct hmap send_garp_rarp_data; > > > > > > > > static void > > > > init_send_garps_rarps(void) > > > > { > > > > - shash_init(&send_garp_rarp_data); > > > > + hmap_init(&send_garp_rarp_data); > > > > } > > > > > > > > static void > > > > destroy_send_garps_rarps(void) > > > > { > > > > - shash_destroy_free_data(&send_garp_rarp_data); > > > > -} > > > > - > > > > -/* Runs with in the main ovn-controller thread context. */ > > > > -static void > > > > -add_garp_rarp(const char *name, const struct eth_addr ea, ovs_be32 ip, > > > > - uint32_t dp_key, uint32_t port_key) > > > > -{ > > > > - struct garp_rarp_data *garp_rarp = xmalloc(sizeof *garp_rarp); > > > > - garp_rarp->ea = ea; > > > > - garp_rarp->ipv4 = ip; > > > > - garp_rarp->announce_time = time_msec() + 1000; > > > > - garp_rarp->backoff = 1000; /* msec. */ > > > > - garp_rarp->dp_key = dp_key; > > > > - garp_rarp->port_key = port_key; > > > > - shash_add(&send_garp_rarp_data, name, garp_rarp); > > > > - > > > > - /* Notify pinctrl_handler so that it can wakeup and process > > > > - * these GARP/RARP requests. */ > > > > - notify_pinctrl_handler(); > > > > -} > > > > - > > > > -/* Add or update a vif for which GARPs need to be announced. */ > > > > -static void > > > > -send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > - struct ovsdb_idl_index > > > > *sbrec_mac_binding_by_lport_ip, > > > > - const struct hmap *local_datapaths, > > > > - const struct sbrec_port_binding *binding_rec, > > > > - struct shash *nat_addresses, > > > > - long long int garp_max_timeout, > > > > - bool garp_continuous) > > > > -{ > > > > - volatile struct garp_rarp_data *garp_rarp = NULL; > > > > - > > > > - /* Skip localports as they don't need to be announced */ > > > > - if (!strcmp(binding_rec->type, "localport")) { > > > > - return; > > > > - } > > > > - > > > > - /* Update GARP for NAT IP if it exists. Consider port bindings > > with > > > > type > > > > - * "l3gateway" for logical switch ports attached to gateway > > routers, > > > > and > > > > - * port bindings with type "patch" for logical switch ports > > attached > > > > to > > > > - * distributed gateway ports. */ > > > > - if (!strcmp(binding_rec->type, "l3gateway") > > > > - || !strcmp(binding_rec->type, "patch")) { > > > > - struct lport_addresses *laddrs = NULL; > > > > - while ((laddrs = shash_find_and_delete(nat_addresses, > > > > - > > > > binding_rec->logical_port))) { > > > > - int i; > > > > - for (i = 0; i < laddrs->n_ipv4_addrs; i++) { > > > > - char *name = xasprintf("%s-%s", > > binding_rec->logical_port, > > > > - > > > > laddrs->ipv4_addrs[i].addr_s); > > > > - garp_rarp = shash_find_data(&send_garp_rarp_data, > > name); > > > > - if (garp_rarp) { > > > > - garp_rarp->dp_key = > > binding_rec->datapath->tunnel_key; > > > > - garp_rarp->port_key = binding_rec->tunnel_key; > > > > - if (garp_max_timeout != garp_rarp_max_timeout || > > > > - garp_continuous != garp_rarp_continuous) { > > > > - /* reset backoff */ > > > > - garp_rarp->announce_time = time_msec() + 1000; > > > > - garp_rarp->backoff = 1000; /* msec. */ > > > > - } > > > > - } else if (ovnsb_idl_txn) { > > > > - add_garp_rarp(name, laddrs->ea, > > > > - laddrs->ipv4_addrs[i].addr, > > > > - binding_rec->datapath->tunnel_key, > > > > - binding_rec->tunnel_key); > > > > - send_garp_locally(ovnsb_idl_txn, > > > > - sbrec_mac_binding_by_lport_ip, > > > > - local_datapaths, binding_rec, > > > > laddrs->ea, > > > > - laddrs->ipv4_addrs[i].addr); > > > > - > > > > - } > > > > - free(name); > > > > - } > > > > - /* > > > > - * Send RARPs even if we do not have a ipv4 address as it > > e.g. > > > > - * happens on ipv6 only ports. > > > > - */ > > > > - if (laddrs->n_ipv4_addrs == 0) { > > > > - char *name = xasprintf("%s-noip", > > > > - binding_rec->logical_port); > > > > - garp_rarp = shash_find_data(&send_garp_rarp_data, > > > > name); > > > > - if (garp_rarp) { > > > > - garp_rarp->dp_key = > > > > binding_rec->datapath->tunnel_key; > > > > - garp_rarp->port_key = binding_rec->tunnel_key; > > > > - if (garp_max_timeout != garp_rarp_max_timeout > > || > > > > - garp_continuous != garp_rarp_continuous) { > > > > - /* reset backoff */ > > > > - garp_rarp->announce_time = time_msec() + > > 1000; > > > > - garp_rarp->backoff = 1000; /* msec. */ > > > > - } > > > > - } else { > > > > - add_garp_rarp(name, laddrs->ea, > > > > - 0, > > > > binding_rec->datapath->tunnel_key, > > > > - binding_rec->tunnel_key); > > > > - } > > > > - free(name); > > > > - } > > > > - destroy_lport_addresses(laddrs); > > > > - free(laddrs); > > > > - } > > > > - return; > > > > - } > > > > - > > > > - /* Update GARP for vif if it exists. */ > > > > - garp_rarp = shash_find_data(&send_garp_rarp_data, > > > > - binding_rec->logical_port); > > > > - if (garp_rarp) { > > > > - garp_rarp->dp_key = binding_rec->datapath->tunnel_key; > > > > - garp_rarp->port_key = binding_rec->tunnel_key; > > > > - if (garp_max_timeout != garp_rarp_max_timeout || > > > > - garp_continuous != garp_rarp_continuous) { > > > > - /* reset backoff */ > > > > - garp_rarp->announce_time = time_msec() + 1000; > > > > - garp_rarp->backoff = 1000; /* msec. */ > > > > - } > > > > - return; > > > > - } > > > > - > > > > - /* Add GARP for new vif. */ > > > > - int i; > > > > - for (i = 0; i < binding_rec->n_mac; i++) { > > > > - struct lport_addresses laddrs; > > > > - ovs_be32 ip = 0; > > > > - if (!extract_lsp_addresses(binding_rec->mac[i], &laddrs)) { > > > > - continue; > > > > - } > > > > - > > > > - if (laddrs.n_ipv4_addrs) { > > > > - ip = laddrs.ipv4_addrs[0].addr; > > > > - } > > > > - > > > > - add_garp_rarp(binding_rec->logical_port, > > > > - laddrs.ea, ip, > > > > - binding_rec->datapath->tunnel_key, > > > > - binding_rec->tunnel_key); > > > > - if (ip) { > > > > - send_garp_locally(ovnsb_idl_txn, > > > > sbrec_mac_binding_by_lport_ip, > > > > - local_datapaths, binding_rec, laddrs.ea, > > > > ip); > > > > - } > > > > - > > > > - destroy_lport_addresses(&laddrs); > > > > - break; > > > > + struct garp_rarp_node *garp_rarp; > > > > + HMAP_FOR_EACH_POP (garp_rarp, hmap_node, &send_garp_rarp_data) { > > > > + garp_rarp_node_free(garp_rarp); > > > > } > > > > + hmap_destroy(&send_garp_rarp_data); > > > > } > > > > > > > > struct arp_nd_data { > > > > @@ -5289,16 +5091,6 @@ send_arp_nd_update(const struct > > sbrec_port_binding > > > > *pb, const char *nexthop, > > > > } > > > > } > > > > > > > > -/* Remove a vif from GARP announcements. */ > > > > -static void > > > > -send_garp_rarp_delete(const char *lport) > > > > -{ > > > > - struct garp_rarp_data *garp_rarp = shash_find_and_delete > > > > - (&send_garp_rarp_data, lport); > > > > - free(garp_rarp); > > > > - notify_pinctrl_handler(); > > > > -} > > > > - > > > > void > > > > send_self_originated_neigh_packet(struct rconn *swconn, > > > > uint32_t dp_key, uint32_t port_key, > > > > @@ -5350,7 +5142,7 @@ send_self_originated_neigh_packet(struct rconn > > > > *swconn, > > > > > > > > /* Called with in the pinctrl_handler thread context. */ > > > > static long long int > > > > -send_garp_rarp(struct rconn *swconn, struct garp_rarp_data *garp_rarp, > > > > +send_garp_rarp(struct rconn *swconn, struct garp_rarp_node *garp_rarp, > > > > long long int current_time) > > > > OVS_REQUIRES(pinctrl_mutex) > > > > { > > > > @@ -6419,242 +6211,12 @@ ip_mcast_querier_wait(long long int > > query_time) > > > > } > > > > } > > > > > > > > -/* Get localnet vifs, local l3gw ports and ofport for localnet patch > > > > ports. */ > > > > -static void > > > > -get_localnet_vifs_l3gwports( > > > > - struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > > > > - struct ovsdb_idl_index *sbrec_port_binding_by_name, > > > > - const struct ovsrec_bridge *br_int, > > > > - const struct sbrec_chassis *chassis, > > > > - const struct hmap *local_datapaths, > > > > - struct sset *localnet_vifs, > > > > - struct sset *local_l3gw_ports) > > > > -{ > > > > - for (int i = 0; i < br_int->n_ports; i++) { > > > > - const struct ovsrec_port *port_rec = br_int->ports[i]; > > > > - if (!strcmp(port_rec->name, br_int->name)) { > > > > - continue; > > > > - } > > > > - const char *tunnel_id = smap_get(&port_rec->external_ids, > > > > - "ovn-chassis-id"); > > > > - if (tunnel_id && > > > > - encaps_tunnel_id_match(tunnel_id, chassis->name, NULL, > > > > NULL)) { > > > > - continue; > > > > - } > > > > - const char *localnet = smap_get(&port_rec->external_ids, > > > > - "ovn-localnet-port"); > > > > - if (localnet) { > > > > - continue; > > > > - } > > > > - for (int j = 0; j < port_rec->n_interfaces; j++) { > > > > - const struct ovsrec_interface *iface_rec = > > > > port_rec->interfaces[j]; > > > > - if (!iface_rec->n_ofport) { > > > > - continue; > > > > - } > > > > - /* Get localnet vif. */ > > > > - const char *iface_id = smap_get(&iface_rec->external_ids, > > > > - "iface-id"); > > > > - if (!iface_id) { > > > > - continue; > > > > - } > > > > - const struct sbrec_port_binding *pb > > > > - = lport_lookup_by_name(sbrec_port_binding_by_name, > > > > iface_id); > > > > - if (!pb || pb->chassis != chassis) { > > > > - continue; > > > > - } > > > > - if (!iface_rec->link_state || > > > > - strcmp(iface_rec->link_state, "up")) { > > > > - continue; > > > > - } > > > > - struct local_datapath *ld > > > > - = get_local_datapath(local_datapaths, > > > > - pb->datapath->tunnel_key); > > > > - if (ld && ld->localnet_port) { > > > > - sset_add(localnet_vifs, iface_id); > > > > - } > > > > - } > > > > - } > > > > - > > > > - struct sbrec_port_binding *target = > > sbrec_port_binding_index_init_row( > > > > - sbrec_port_binding_by_datapath); > > > > - > > > > - const struct local_datapath *ld; > > > > - HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { > > > > - const struct sbrec_port_binding *pb; > > > > - > > > > - if (!ld->localnet_port) { > > > > - continue; > > > > - } > > > > - > > > > - /* Get l3gw ports. Consider port bindings with type > > "l3gateway" > > > > - * that connect to gateway routers (if local), and consider > > port > > > > - * bindings of type "patch" since they might connect to > > > > - * distributed gateway ports with NAT addresses. */ > > > > - > > > > - sbrec_port_binding_index_set_datapath(target, ld->datapath); > > > > - SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, > > > > - > > > > sbrec_port_binding_by_datapath) { > > > > - if ((!strcmp(pb->type, "l3gateway") && pb->chassis == > > chassis) > > > > - || !strcmp(pb->type, "patch")) { > > > > - sset_add(local_l3gw_ports, pb->logical_port); > > > > - } > > > > - } > > > > - } > > > > - sbrec_port_binding_index_destroy_row(target); > > > > -} > > > > - > > > > - > > > > -/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from > > > > - * 'addresses' which should be of the format 'MAC [IP1 IP2 ..] > > > > - * [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid > > IPv4 > > > > - * or IPv6 address, and stores them in the 'ipv4_addrs' and > > 'ipv6_addrs' > > > > - * fields of 'laddrs'. The logical port name is stored in 'lport'. > > > > - * > > > > - * Returns true if at least 'MAC' is found in 'address', false > > otherwise. > > > > - * > > > > - * The caller must call destroy_lport_addresses() and free(*lport). */ > > > > -static bool > > > > -extract_addresses_with_port(const char *addresses, > > > > - struct lport_addresses *laddrs, > > > > - char **lport) > > > > -{ > > > > - int ofs; > > > > - if (!extract_addresses(addresses, laddrs, &ofs)) { > > > > - return false; > > > > - } else if (!addresses[ofs]) { > > > > - return true; > > > > - } > > > > - > > > > - struct lexer lexer; > > > > - lexer_init(&lexer, addresses + ofs); > > > > - lexer_get(&lexer); > > > > - > > > > - if (lexer.error || lexer.token.type != LEX_T_ID > > > > - || !lexer_match_id(&lexer, "is_chassis_resident")) { > > > > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > > > > - VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", > > addresses); > > > > - lexer_destroy(&lexer); > > > > - return true; > > > > - } > > > > - > > > > - if (!lexer_match(&lexer, LEX_T_LPAREN)) { > > > > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > > > > - VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after " > > > > - "'is_chassis_resident' in address '%s'", > > > > addresses); > > > > - lexer_destroy(&lexer); > > > > - return false; > > > > - } > > > > - > > > > - if (lexer.token.type != LEX_T_STRING) { > > > > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > > > > - VLOG_INFO_RL(&rl, > > > > - "Syntax error: expecting quoted string after " > > > > - "'is_chassis_resident' in address '%s'", > > addresses); > > > > - lexer_destroy(&lexer); > > > > - return false; > > > > - } > > > > - > > > > - *lport = xstrdup(lexer.token.s); > > > > - > > > > - lexer_get(&lexer); > > > > - if (!lexer_match(&lexer, LEX_T_RPAREN)) { > > > > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > > > > - VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted > > > > string in " > > > > - "'is_chassis_resident()' in address '%s'", > > > > - addresses); > > > > - lexer_destroy(&lexer); > > > > - return false; > > > > - } > > > > - > > > > - lexer_destroy(&lexer); > > > > - return true; > > > > -} > > > > - > > > > -static void > > > > -consider_nat_address(struct ovsdb_idl_index > > *sbrec_port_binding_by_name, > > > > - const char *nat_address, > > > > - const struct sbrec_port_binding *pb, > > > > - struct sset *nat_address_keys, > > > > - const struct sbrec_chassis *chassis, > > > > - const struct sset *active_tunnels, > > > > - struct shash *nat_addresses) > > > > -{ > > > > - struct lport_addresses *laddrs = xmalloc(sizeof *laddrs); > > > > - char *lport = NULL; > > > > - if (!extract_addresses_with_port(nat_address, laddrs, &lport) > > > > - || (!lport && !strcmp(pb->type, "patch")) > > > > - || (lport && !lport_is_chassis_resident( > > > > - sbrec_port_binding_by_name, chassis, > > > > - active_tunnels, lport))) { > > > > - destroy_lport_addresses(laddrs); > > > > - free(laddrs); > > > > - free(lport); > > > > - return; > > > > - } > > > > - free(lport); > > > > - > > > > - int i; > > > > - for (i = 0; i < laddrs->n_ipv4_addrs; i++) { > > > > - char *name = xasprintf("%s-%s", pb->logical_port, > > > > - laddrs->ipv4_addrs[i].addr_s); > > > > - sset_add(nat_address_keys, name); > > > > - free(name); > > > > - } > > > > - if (laddrs->n_ipv4_addrs == 0) { > > > > - char *name = xasprintf("%s-noip", pb->logical_port); > > > > - sset_add(nat_address_keys, name); > > > > - free(name); > > > > - } > > > > - shash_add(nat_addresses, pb->logical_port, laddrs); > > > > -} > > > > - > > > > -static void > > > > -get_nat_addresses_and_keys(struct ovsdb_idl_index > > > > *sbrec_port_binding_by_name, > > > > - struct sset *nat_address_keys, > > > > - struct sset *local_l3gw_ports, > > > > - const struct sbrec_chassis *chassis, > > > > - const struct sset *active_tunnels, > > > > - struct shash *nat_addresses) > > > > -{ > > > > - const char *gw_port; > > > > - SSET_FOR_EACH(gw_port, local_l3gw_ports) { > > > > - const struct sbrec_port_binding *pb; > > > > - > > > > - pb = lport_lookup_by_name(sbrec_port_binding_by_name, > > gw_port); > > > > - if (!pb) { > > > > - continue; > > > > - } > > > > - > > > > - if (pb->n_nat_addresses) { > > > > - for (int i = 0; i < pb->n_nat_addresses; i++) { > > > > - consider_nat_address(sbrec_port_binding_by_name, > > > > - pb->nat_addresses[i], pb, > > > > - nat_address_keys, chassis, > > > > - active_tunnels, > > > > - nat_addresses); > > > > - } > > > > - } else { > > > > - /* Continue to support options:nat-addresses for version > > > > - * upgrade. */ > > > > - const char *nat_addresses_options = smap_get(&pb->options, > > > > - > > "nat-addresses"); > > > > - if (nat_addresses_options) { > > > > - consider_nat_address(sbrec_port_binding_by_name, > > > > - nat_addresses_options, pb, > > > > - nat_address_keys, chassis, > > > > - active_tunnels, > > > > - nat_addresses); > > > > - } > > > > - } > > > > - } > > > > -} > > > > - > > > > static void > > > > send_garp_rarp_wait(long long int send_garp_rarp_time) > > > > { > > > > /* Set the poll timer for next garp/rarp only if there is data to > > > > * be sent. */ > > > > - if (!shash_is_empty(&send_garp_rarp_data)) { > > > > + if (!hmap_is_empty(&send_garp_rarp_data)) { > > > > poll_timer_wait_until(send_garp_rarp_time); > > > > } > > > > } > > > > @@ -6674,16 +6236,16 @@ static void > > > > send_garp_rarp_run(struct rconn *swconn, long long int > > > > *send_garp_rarp_time) > > > > OVS_REQUIRES(pinctrl_mutex) > > > > { > > > > - if (shash_is_empty(&send_garp_rarp_data)) { > > > > + if (hmap_is_empty(&send_garp_rarp_data)) { > > > > return; > > > > } > > > > > > > > /* Send GARPs, and update the next announcement. */ > > > > - struct shash_node *iter; > > > > long long int current_time = time_msec(); > > > > *send_garp_rarp_time = LLONG_MAX; > > > > - SHASH_FOR_EACH (iter, &send_garp_rarp_data) { > > > > - long long int next_announce = send_garp_rarp(swconn, > > iter->data, > > > > + struct garp_rarp_node *garp; > > > > + HMAP_FOR_EACH (garp, hmap_node, &send_garp_rarp_data) { > > > > + long long int next_announce = send_garp_rarp(swconn, garp, > > > > current_time); > > > > if (*send_garp_rarp_time > next_announce) { > > > > *send_garp_rarp_time = next_announce; > > > > @@ -6743,22 +6305,12 @@ send_arp_nd_run(struct rconn *swconn, long long > > > > int *send_arp_nd_time) > > > > /* Called by pinctrl_run(). Runs with in the main ovn-controller > > > > * thread context. */ > > > > static void > > > > -send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > - struct ovsdb_idl_index > > > > *sbrec_port_binding_by_datapath, > > > > - struct ovsdb_idl_index > > *sbrec_port_binding_by_name, > > > > - struct ovsdb_idl_index > > > > *sbrec_mac_binding_by_lport_ip, > > > > +send_garp_rarp_prepare(const struct hmap *garp_rarp_data, > > > > const struct sbrec_ecmp_nexthop_table > > > > *ecmp_nh_table, > > > > - const struct ovsrec_bridge *br_int, > > > > const struct sbrec_chassis *chassis, > > > > - const struct hmap *local_datapaths, > > > > - const struct sset *active_tunnels, > > > > const struct ovsrec_open_vswitch_table > > *ovs_table) > > > > OVS_REQUIRES(pinctrl_mutex) > > > > { > > > > - struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs); > > > > - struct sset local_l3gw_ports = > > SSET_INITIALIZER(&local_l3gw_ports); > > > > - struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys); > > > > - struct shash nat_addresses; > > > > unsigned long long garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT; > > > > unsigned long long max_arp_nd_timeout = GARP_RARP_DEF_MAX_TIMEOUT; > > > > bool garp_continuous = false, continuous_arp_nd = true; > > > > @@ -6778,51 +6330,13 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn > > > > *ovnsb_idl_txn, > > > > continuous_arp_nd = !!max_arp_nd_timeout; > > > > } > > > > > > > > - shash_init(&nat_addresses); > > > > + bool reset_timers = (garp_max_timeout != garp_rarp_max_timeout || > > > > + garp_continuous != garp_rarp_continuous); > > > > > > > > - get_localnet_vifs_l3gwports(sbrec_port_binding_by_datapath, > > > > - sbrec_port_binding_by_name, > > > > - br_int, chassis, local_datapaths, > > > > - &localnet_vifs, &local_l3gw_ports); > > > > - > > > > - get_nat_addresses_and_keys(sbrec_port_binding_by_name, > > > > - &nat_ip_keys, &local_l3gw_ports, > > > > - chassis, active_tunnels, > > > > - &nat_addresses); > > > > - > > > > - /* For deleted ports and deleted nat ips, remove from > > > > - * send_garp_rarp_data. */ > > > > - struct shash_node *iter; > > > > - SHASH_FOR_EACH_SAFE (iter, &send_garp_rarp_data) { > > > > - if (!sset_contains(&localnet_vifs, iter->name) && > > > > - !sset_contains(&nat_ip_keys, iter->name)) { > > > > - send_garp_rarp_delete(iter->name); > > > > - } > > > > - } > > > > - > > > > - /* Update send_garp_rarp_data. */ > > > > - const char *iface_id; > > > > - SSET_FOR_EACH (iface_id, &localnet_vifs) { > > > > - const struct sbrec_port_binding *pb = lport_lookup_by_name( > > > > - sbrec_port_binding_by_name, iface_id); > > > > - if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", > > > > false)) { > > > > - send_garp_rarp_update(ovnsb_idl_txn, > > > > - sbrec_mac_binding_by_lport_ip, > > > > - local_datapaths, pb, &nat_addresses, > > > > - garp_max_timeout, garp_continuous); > > > > - } > > > > - } > > > > - > > > > - /* Update send_garp_rarp_data for nat-addresses. */ > > > > - const char *gw_port; > > > > - SSET_FOR_EACH (gw_port, &local_l3gw_ports) { > > > > - const struct sbrec_port_binding *pb > > > > - = lport_lookup_by_name(sbrec_port_binding_by_name, > > gw_port); > > > > - if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", > > > > false)) { > > > > - send_garp_rarp_update(ovnsb_idl_txn, > > > > sbrec_mac_binding_by_lport_ip, > > > > - local_datapaths, pb, &nat_addresses, > > > > - garp_max_timeout, garp_continuous); > > > > - } > > > > + if (garp_rarp_sync(garp_rarp_data, &send_garp_rarp_data, > > > > reset_timers)) { > > > > + /* Notify pinctrl_handler so that it can wakeup and process > > > > + * these GARP/RARP requests. */ > > > > + notify_pinctrl_handler(); > > > > } > > > > > > > > arp_nd_sync_data(ecmp_nh_table); > > > > @@ -6836,21 +6350,6 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn > > > > *ovnsb_idl_txn, > > > > } > > > > } > > > > > > > > - /* pinctrl_handler thread will send the GARPs. */ > > > > - > > > > - sset_destroy(&localnet_vifs); > > > > - sset_destroy(&local_l3gw_ports); > > > > - > > > > - SHASH_FOR_EACH_SAFE (iter, &nat_addresses) { > > > > - struct lport_addresses *laddrs = iter->data; > > > > - destroy_lport_addresses(laddrs); > > > > - shash_delete(&nat_addresses, iter); > > > > - free(laddrs); > > > > - } > > > > - shash_destroy(&nat_addresses); > > > > - > > > > - sset_destroy(&nat_ip_keys); > > > > - > > > > garp_rarp_max_timeout = garp_max_timeout; > > > > garp_rarp_continuous = garp_continuous; > > > > > > > > @@ -6862,7 +6361,7 @@ static bool > > > > may_inject_pkts(void) > > > > { > > > > return (!shash_is_empty(&ipv6_ras) || > > > > - !shash_is_empty(&send_garp_rarp_data) || > > > > + !hmap_is_empty(&send_garp_rarp_data) || > > > > ipv6_prefixd_should_inject() || > > > > !ovs_list_is_empty(&mcast_query_list) || > > > > > > buffered_packets_ctx_is_ready_to_send(&buffered_packets_ctx) > > > > || > > > > diff --git a/controller/pinctrl.h b/controller/pinctrl.h > > > > index e33d7cbf5..f57f5c306 100644 > > > > --- a/controller/pinctrl.h > > > > +++ b/controller/pinctrl.h > > > > @@ -44,24 +44,24 @@ struct sbrec_mac_binding_table; > > > > void pinctrl_init(void); > > > > void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > struct ovsdb_idl_index > > *sbrec_datapath_binding_by_key, > > > > - struct ovsdb_idl_index > > *sbrec_port_binding_by_datapath, > > > > struct ovsdb_idl_index *sbrec_port_binding_by_key, > > > > struct ovsdb_idl_index *sbrec_port_binding_by_name, > > > > struct ovsdb_idl_index > > *sbrec_mac_binding_by_lport_ip, > > > > struct ovsdb_idl_index *sbrec_igmp_groups, > > > > struct ovsdb_idl_index *sbrec_ip_multicast_opts, > > > > struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac, > > > > - const struct sbrec_controller_event_table *, > > > > - const struct sbrec_service_monitor_table *, > > > > - const struct sbrec_mac_binding_table *, > > > > - const struct sbrec_bfd_table *, > > > > - const struct sbrec_ecmp_nexthop_table *, > > > > - const struct ovsrec_bridge *, const struct > > sbrec_chassis > > > > *, > > > > + const struct sbrec_controller_event_table *ce_table, > > > > + const struct sbrec_service_monitor_table > > *svc_mon_table, > > > > + const struct sbrec_mac_binding_table > > *mac_binding_table, > > > > + const struct sbrec_bfd_table *bfd_table, > > > > + const struct sbrec_ecmp_nexthop_table *ecmp_nh_table, > > > > + const struct sbrec_chassis *chassis, > > > > > > > > > > nit: Unrelated change. > > > > will be cleaned up. > > > > Thanks a lot, > > Felix > > > > > > > > const struct hmap *local_datapaths, > > > > const struct sset *active_tunnels, > > > > const struct shash *local_active_ports_ipv6_pd, > > > > const struct shash *local_active_ports_ras, > > > > - const struct ovsrec_open_vswitch_table *ovs_table); > > > > + const struct ovsrec_open_vswitch_table *ovs_table, > > > > + const struct hmap *garp_rarp_data); > > > > void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn); > > > > void pinctrl_destroy(void); > > > > > > > > -- > > > > 2.43.0 > > > > > > > > > > > > _______________________________________________ > > > > dev mailing list > > > > d...@openvswitch.org > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > > > > > > > > > > Thanks, > > > Ales > > > > > Thanks, > Ales > _______________________________________________ > 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