On Mon, Feb 24, 2025 at 2:57 PM Lorenzo Bianconi < lorenzo.bianc...@redhat.com> wrote:
> Introduce the capability to actively refresh mac_binding entries available > in OFTABLE_MAC_BINDING table when they are inactive for more than the > configured threshold. Add the OFTABLE_MAC_BINDING table stats > monitoring. This feature avoids possible unnecessary mac_entry removals > if the destination is still alive but inactive for more than the configured > value. > > Reported-at: https://issues.redhat.com/browse/FDP-1135 > Signed-off-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com> > --- > Hi Lorenzo, thank you for the follow up, I have a couple of questions down below. controller/mac-cache.c | 112 ++++++++++++++++++- > controller/mac-cache.h | 26 ++++- > controller/ovn-controller.c | 40 +++++-- > controller/pinctrl.c | 8 +- > controller/pinctrl.h | 7 ++ > controller/statctrl.c | 25 ++++- > tests/ovn.at | 216 +++++++++++++++++++++++++++++++++++- > 7 files changed, 405 insertions(+), 29 deletions(-) > > diff --git a/controller/mac-cache.c b/controller/mac-cache.c > index 9d05c1950..72333b17e 100644 > --- a/controller/mac-cache.c > +++ b/controller/mac-cache.c > @@ -16,6 +16,7 @@ > #include <config.h> > #include <stdbool.h> > > +#include "lflow.h" > #include "lib/mac-binding-index.h" > #include "local_data.h" > #include "lport.h" > @@ -24,6 +25,7 @@ > #include "openvswitch/vlog.h" > #include "ovn/logical-fields.h" > #include "ovn-sb-idl.h" > +#include "pinctrl.h" > > VLOG_DEFINE_THIS_MODULE(mac_cache); > > @@ -160,17 +162,25 @@ mac_cache_thresholds_clear(struct mac_cache_data > *data) > /* MAC binding. */ > void > mac_binding_add(struct hmap *map, struct mac_binding_data mb_data, > - const struct sbrec_mac_binding *smb, long long timestamp) > + const struct sbrec_mac_binding *smb, > + const struct sbrec_port_binding *pb, > + long long timestamp) > { > struct mac_binding *mb = mac_binding_find(map, &mb_data); > if (!mb) { > - mb = xmalloc(sizeof *mb); > + mb = xzalloc(sizeof *mb); > hmap_insert(map, &mb->hmap_node, mac_binding_data_hash(&mb_data)); > } > > mb->data = mb_data; > mb->sbrec = smb; > mb->timestamp = timestamp; > + if (pb) { > + destroy_lport_addresses(&mb->laddr); > + if (extract_lsp_addresses(pb->mac[0], &mb->laddr)) { > + mb->tunnel_key = pb->tunnel_key; > + } > + } > mac_binding_update_log("Added", &mb_data, false, NULL, 0, 0); > } > > @@ -179,6 +189,7 @@ mac_binding_remove(struct hmap *map, struct > mac_binding *mb) > { > mac_binding_update_log("Removed", &mb->data, false, NULL, 0, 0); > hmap_remove(map, &mb->hmap_node); > + destroy_lport_addresses(&mb->laddr); > free(mb); > } > > @@ -237,6 +248,7 @@ mac_bindings_clear(struct hmap *map) > { > struct mac_binding *mb; > HMAP_FOR_EACH_POP (mb, hmap_node, map) { > + destroy_lport_addresses(&mb->laddr); > free(mb); > } > } > @@ -336,6 +348,7 @@ struct mac_cache_stats { > struct ovs_list list_node; > > int64_t idle_age_ms; > + uint64_t packet_count; > nit: This isn't used anymore, we can remove it. > union { > /* Common data to identify MAC binding. */ > @@ -399,7 +412,8 @@ mac_binding_update_log(const char *action, > } > > void > -mac_binding_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, > +mac_binding_stats_run(struct rconn *swconn OVS_UNUSED, > + struct ovs_list *stats_list, uint64_t *req_delay, > void *data) > { > struct mac_cache_data *cache_data = data; > @@ -495,8 +509,9 @@ fdb_update_log(const char *action, > } > > void > -fdb_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, > - void *data) > +fdb_stats_run(struct rconn *swconn OVS_UNUSED, > + struct ovs_list *stats_list, > + uint64_t *req_delay, void *data) > { > struct mac_cache_data *cache_data = data; > long long timewall_now = time_wall_msec(); > @@ -847,3 +862,90 @@ buffered_packets_db_lookup(struct buffered_packets > *bp, struct ds *ip, > > eth_addr_from_string(smb->mac, mac); > } > + > +void > +mac_binding_probe_stats_process_flow_stats( > + struct ovs_list *stats_list, > + struct ofputil_flow_stats *ofp_stats) > +{ > + struct mac_cache_stats *stats = xmalloc(sizeof *stats); > + > + stats->idle_age_ms = ofp_stats->idle_age * 1000; > + stats->packet_count = ofp_stats->packet_count; > + stats->data.mb = (struct mac_binding_data) { > + .cookie = ntohll(ofp_stats->cookie), > + /* The port_key must be zero to match > mac_binding_data_from_sbrec. */ > + .port_key = 0, > + .dp_key = ntohll(ofp_stats->match.flow.metadata), > + }; > + > + if (ofp_stats->match.flow.regs[0]) { > + stats->data.mb.ip = > + in6_addr_mapped_ipv4(htonl(ofp_stats->match.flow.regs[0])); > + } else { > + ovs_be128 ip6 = hton128(flow_get_xxreg(&ofp_stats->match.flow, > 1)); > + memcpy(&stats->data.mb.ip, &ip6, sizeof stats->data.mb.ip); > + } > + > + ovs_list_push_back(stats_list, &stats->list_node); > +} > + > +void > +mac_binding_probe_stats_run( > + struct rconn *swconn, > + struct ovs_list *stats_list, > + uint64_t *req_delay, void *data) > +{ > + struct mac_cache_data *cache_data = data; > + > + struct mac_cache_stats *stats; > + LIST_FOR_EACH_POP (stats, list_node, stats_list) { > + struct mac_binding *mb = > mac_binding_find(&cache_data->mac_bindings, > + &stats->data.mb); > + if (!mb) { > + mac_binding_update_log("Probe: not found in the cache:", > + &stats->data.mb, false, NULL, 0, 0); > + free(stats); > + continue; > + } > + > + struct mac_cache_threshold *threshold = > + mac_cache_threshold_find(cache_data, mb->data.dp_key); > + if (stats->idle_age_ms < threshold->value || > + stats->idle_age_ms > 2 * threshold->value) { > I'm not sure I understand this restriction. It shouldn't be possible for the idle age to be bigger than 2 * threshold, that entry would be aged out by that point. And the other part is that we will skip if the idle_age is smaller than threshold, I'm not sure why? That's actually indication that the entry is active, I think this condition should be changed to "stats->idle_age_ms >= threshold->value" unless I'm missing something. + free(stats); > + continue; > + } > + > + const struct sbrec_mac_binding *sbrec = mb->sbrec; > + if (!sbrec->datapath) { > This condition cannot happen. The MAC binding cannot exist without a datapath. + free(stats); > + continue; > + } > + > + if (!mb->laddr.n_ipv4_addrs && !mb->laddr.n_ipv6_addrs) { > + free(stats); > + continue; > + } > + > + mac_binding_update_log("Probe: trying to refresh", &mb->data, > true, > + threshold, stats->idle_age_ms, > + time_wall_msec() - mb->sbrec->timestamp); > + > + struct in6_addr local = mb->laddr.n_ipv4_addrs > + ? in6_addr_mapped_ipv4(mb->laddr.ipv4_addrs[0].addr) > + : mb->laddr.ipv6_addrs[0].addr; > + send_self_originated_neigh_packet(swconn, > + sbrec->datapath->tunnel_key, > + mb->tunnel_key, mb->laddr.ea, > + &local, &mb->data.ip, > + OFTABLE_LOCAL_OUTPUT); > + free(stats); > + } > + > + mac_cache_update_req_delay(&cache_data->thresholds, req_delay); > + *req_delay = *req_delay / 2; > + if (*req_delay) { > + VLOG_DBG("MAC probe binding statistics delay: %"PRIu64, > *req_delay); > + } > +} > diff --git a/controller/mac-cache.h b/controller/mac-cache.h > index 40d7b9c25..eb2ad7b55 100644 > --- a/controller/mac-cache.h > +++ b/controller/mac-cache.h > @@ -70,6 +70,10 @@ struct mac_binding { > const struct sbrec_mac_binding *sbrec; > /* User specified timestamp (in ms) */ > long long timestamp; > + /* SB MAC binding logical port address. */ > + struct lport_addresses laddr; > + /* SB Port binding tunnel key. */ > + int64_t tunnel_key; > }; > > struct fdb_data { > @@ -147,7 +151,9 @@ mac_binding_data_init(struct mac_binding_data *data, > } > > void mac_binding_add(struct hmap *map, struct mac_binding_data mb_data, > - const struct sbrec_mac_binding *, long long > timestamp); > + const struct sbrec_mac_binding *smb, > + const struct sbrec_port_binding *pb, > + long long timestamp); > > void mac_binding_remove(struct hmap *map, struct mac_binding *mb); > > @@ -180,15 +186,17 @@ void > mac_binding_stats_process_flow_stats(struct ovs_list *stats_list, > struct ofputil_flow_stats > *ofp_stats); > > -void mac_binding_stats_run(struct ovs_list *stats_list, uint64_t > *req_delay, > - void *data); > +void mac_binding_stats_run(struct rconn *swconn OVS_UNUSED, > + struct ovs_list *stats_list, > + uint64_t *req_delay, void *data); > > /* FDB stat processing. */ > void fdb_stats_process_flow_stats(struct ovs_list *stats_list, > struct ofputil_flow_stats *ofp_stats); > > -void fdb_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, > - void *data); > +void fdb_stats_run(struct rconn *swconn OVS_UNUSED, > + struct ovs_list *stats_list, > + uint64_t *req_delay, void *data); > > void mac_cache_stats_destroy(struct ovs_list *stats_list); > > @@ -221,4 +229,12 @@ bool buffered_packets_ctx_is_ready_to_send(struct > buffered_packets_ctx *ctx); > > bool buffered_packets_ctx_has_packets(struct buffered_packets_ctx *ctx); > > +void mac_binding_probe_stats_process_flow_stats( > + struct ovs_list *stats_list, > + struct ofputil_flow_stats *ofp_stats); > + > +void mac_binding_probe_stats_run(struct rconn *swconn, > + struct ovs_list *stats_list, > + uint64_t *req_delay, void *data); > + > #endif /* controller/mac-cache.h */ > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index 081411cba..5749f316b 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -3054,14 +3054,17 @@ en_lb_data_cleanup(void *data) > > static void > mac_binding_add_sb(struct mac_cache_data *data, > - const struct sbrec_mac_binding *smb) > + const struct sbrec_mac_binding *smb, > + struct ovsdb_idl_index *sbrec_port_binding_by_name) > { > struct mac_binding_data mb_data; > if (!mac_binding_data_from_sbrec(&mb_data, smb)) { > return; > } > > - mac_binding_add(&data->mac_bindings, mb_data, smb, 0); > + const struct sbrec_port_binding *pb = lport_lookup_by_name( > + sbrec_port_binding_by_name, smb->logical_port); > + mac_binding_add(&data->mac_bindings, mb_data, smb, pb, 0); > } > > static void > @@ -3113,7 +3116,8 @@ fdb_remove_sb(struct mac_cache_data *data, const > struct sbrec_fdb *sfdb) > static void > mac_cache_mb_handle_for_datapath(struct mac_cache_data *data, > const struct sbrec_datapath_binding *dp, > - struct ovsdb_idl_index *sbrec_mb_by_dp) > + struct ovsdb_idl_index *sbrec_mb_by_dp, > + struct ovsdb_idl_index *sbrec_pb_by_name) > { > bool has_threshold = mac_cache_threshold_find(data, dp->tunnel_key); > > @@ -3124,7 +3128,7 @@ mac_cache_mb_handle_for_datapath(struct > mac_cache_data *data, > const struct sbrec_mac_binding *mb; > SBREC_MAC_BINDING_FOR_EACH_EQUAL (mb, mb_index_row, sbrec_mb_by_dp) { > if (has_threshold) { > - mac_binding_add_sb(data, mb); > + mac_binding_add_sb(data, mb, sbrec_pb_by_name); > } else { > mac_binding_remove_sb(data, mb); > } > @@ -3185,6 +3189,10 @@ en_mac_cache_run(struct engine_node *node, void > *data) > engine_ovsdb_node_get_index( > engine_get_input("SB_fdb", node), > "dp_key"); > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_port_binding", node), > + "name"); > > mac_cache_thresholds_clear(cache_data); > mac_bindings_clear(&cache_data->mac_bindings); > @@ -3199,7 +3207,8 @@ en_mac_cache_run(struct engine_node *node, void > *data) > > mac_cache_threshold_add(cache_data, sbrec_dp); > mac_cache_mb_handle_for_datapath(cache_data, sbrec_dp, > - sbrec_mb_by_dp); > + sbrec_mb_by_dp, > + sbrec_port_binding_by_name); > mac_cache_fdb_handle_for_datapath(cache_data, sbrec_dp, > sbrec_fdb_by_dp_key); > } > @@ -3216,6 +3225,10 @@ mac_cache_sb_mac_binding_handler(struct engine_node > *node, void *data) > const struct sbrec_mac_binding_table *mb_table = > EN_OVSDB_GET(engine_get_input("SB_mac_binding", node)); > size_t previous_size = hmap_count(&cache_data->mac_bindings); > + 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_mac_binding *sbrec_mb; > SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (sbrec_mb, mb_table) { > @@ -3231,7 +3244,8 @@ mac_cache_sb_mac_binding_handler(struct engine_node > *node, void *data) > > if (mac_cache_threshold_find(cache_data, > sbrec_mb->datapath->tunnel_key)) { > - mac_binding_add_sb(cache_data, sbrec_mb); > + mac_binding_add_sb(cache_data, sbrec_mb, > + sbrec_port_binding_by_name); > } > } > > @@ -3292,6 +3306,10 @@ mac_cache_runtime_data_handler(struct engine_node > *node, void *data OVS_UNUSED) > engine_ovsdb_node_get_index( > engine_get_input("SB_fdb", node), > "dp_key"); > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_port_binding", node), > + "name"); > > /* There are no tracked data. Fall back to full recompute. */ > if (!rt_data->tracked) { > @@ -3312,7 +3330,8 @@ mac_cache_runtime_data_handler(struct engine_node > *node, void *data OVS_UNUSED) > > HMAP_FOR_EACH (tdp, node, &rt_data->tracked_dp_bindings) { > mac_cache_mb_handle_for_datapath(cache_data, tdp->dp, > - sbrec_mb_by_dp); > + sbrec_mb_by_dp, > + sbrec_port_binding_by_name); > > mac_cache_fdb_handle_for_datapath(cache_data, tdp->dp, > sbrec_fdb_by_dp_key); > @@ -3342,6 +3361,10 @@ mac_cache_sb_datapath_binding_handler(struct > engine_node *node, void *data) > engine_ovsdb_node_get_index( > engine_get_input("SB_fdb", node), > "dp_key"); > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_port_binding", node), > + "name"); > > size_t previous_mb_size = hmap_count(&cache_data->mac_bindings); > size_t previous_fdb_size = hmap_count(&cache_data->fdbs); > @@ -3365,7 +3388,8 @@ mac_cache_sb_datapath_binding_handler(struct > engine_node *node, void *data) > > SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (sbrec_dp, dp_table) { > mac_cache_mb_handle_for_datapath(cache_data, sbrec_dp, > - sbrec_mb_by_dp); > + sbrec_mb_by_dp, > + sbrec_port_binding_by_name); > > mac_cache_fdb_handle_for_datapath(cache_data, sbrec_dp, > sbrec_fdb_by_dp_key); > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index 47c4bf78b..1957ff771 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -4718,7 +4718,7 @@ pinctrl_handle_put_mac_binding(const struct flow *md, > ? random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1 > : 0; > long long timestamp = time_msec() + delay; > - mac_binding_add(&put_mac_bindings, mb_data, NULL, timestamp); > + mac_binding_add(&put_mac_bindings, mb_data, NULL, NULL, timestamp); > > /* We can send the buffered packet once the main ovn-controller > * thread calls pinctrl_run() and it writes the mac_bindings stored > @@ -4924,7 +4924,7 @@ run_buffered_binding(const struct > sbrec_mac_binding_table *mac_binding_table, > continue; > } > > - mac_binding_add(&recent_mbs, mb_data, smb, 0); > + mac_binding_add(&recent_mbs, mb_data, smb, pb, 0); > nit: The pb here isn't relevant, it can be NULL. > > const char *redirect_port = > smap_get(&pb->options, "chassis-redirect-port"); > @@ -4941,7 +4941,7 @@ run_buffered_binding(const struct > sbrec_mac_binding_table *mac_binding_table, > /* Add the same entry also for chassisredirect port as the > buffered > * traffic might be buffered on the cr port. */ > mb_data.port_key = pb->tunnel_key; > - mac_binding_add(&recent_mbs, mb_data, smb, 0); > + mac_binding_add(&recent_mbs, mb_data, smb, NULL, 0); > } > > buffered_packets_ctx_run(&buffered_packets_ctx, &recent_mbs, > @@ -5303,7 +5303,7 @@ send_garp_rarp_delete(const char *lport) > notify_pinctrl_handler(); > } > > -static void > +void > send_self_originated_neigh_packet(struct rconn *swconn, > uint32_t dp_key, uint32_t port_key, > struct eth_addr eth, > diff --git a/controller/pinctrl.h b/controller/pinctrl.h > index 8459f4f53..e33d7cbf5 100644 > --- a/controller/pinctrl.h > +++ b/controller/pinctrl.h > @@ -31,6 +31,7 @@ struct ovsdb_idl_index; > struct ovsdb_idl_txn; > struct ovsrec_bridge; > struct ovsrec_open_vswitch_table; > +struct rconn; > struct sbrec_chassis; > struct sbrec_dns_table; > struct sbrec_controller_event_table; > @@ -77,4 +78,10 @@ struct activated_port { > void tag_port_as_activated_in_engine(struct activated_port *ap); > struct ovs_list *get_ports_to_activate_in_engine(void); > bool pinctrl_is_port_activated(int64_t dp_key, int64_t port_key); > +void send_self_originated_neigh_packet(struct rconn *swconn, > + uint32_t dp_key, uint32_t port_key, > + struct eth_addr eth, > + struct in6_addr *local, > + struct in6_addr *target, > + uint8_t table_id); > #endif /* controller/pinctrl.h */ > diff --git a/controller/statctrl.c b/controller/statctrl.c > index d3c70ccba..6052d2884 100644 > --- a/controller/statctrl.c > +++ b/controller/statctrl.c > @@ -39,6 +39,7 @@ VLOG_DEFINE_THIS_MODULE(statctrl); > enum stat_type { > STATS_MAC_BINDING = 0, > STATS_FDB, > + STATS_MAC_BINDING_PROBE, > STATS_MAX, > }; > > @@ -62,7 +63,8 @@ struct stats_node { > struct ofputil_flow_stats *ofp_stats); > /* Function to process the parsed stats. > * This function runs in main thread locked behind mutex. */ > - void (*run)(struct ovs_list *stats_list, uint64_t *req_delay, void > *data); > + void (*run)(struct rconn *swconn, struct ovs_list *stats_list, > + uint64_t *req_delay, void *data); > }; > > #define STATS_NODE(NAME, REQUEST, DESTROY, PROCESS, RUN) > \ > @@ -145,6 +147,18 @@ statctrl_init(void) > STATS_NODE(FDB, fdb_request, mac_cache_stats_destroy, > fdb_stats_process_flow_stats, fdb_stats_run); > > + struct ofputil_flow_stats_request mac_binding_probe_request = { > + .cookie = htonll(0), > + .cookie_mask = htonll(0), > + .out_port = OFPP_ANY, > + .out_group = OFPG_ANY, > + .table_id = OFTABLE_MAC_BINDING, > + }; > + STATS_NODE(MAC_BINDING_PROBE, mac_binding_probe_request, > + mac_cache_stats_destroy, > + mac_binding_probe_stats_process_flow_stats, > + mac_binding_probe_stats_run); > + > statctrl_ctx.thread = ovs_thread_create("ovn_statctrl", > statctrl_thread_handler, > &statctrl_ctx); > @@ -158,7 +172,11 @@ statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, > return; > } > > - void *node_data[STATS_MAX] = {mac_cache_data, mac_cache_data}; > + void *node_data[STATS_MAX] = { > + mac_cache_data, > + mac_cache_data, > + mac_cache_data > + }; > > bool schedule_updated = false; > long long now = time_msec(); > @@ -168,7 +186,8 @@ statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, > struct stats_node *node = &statctrl_ctx.nodes[i]; > uint64_t prev_delay = node->request_delay; > > - node->run(&node->stats_list, &node->request_delay, node_data[i]); > + node->run(statctrl_ctx.swconn, &node->stats_list, > + &node->request_delay, node_data[i]); > > schedule_updated |= > statctrl_update_next_request_timestamp(node, now, > prev_delay); > diff --git a/tests/ovn.at b/tests/ovn.at > index 509a0c6ce..fac5a55a1 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -36070,10 +36070,218 @@ done > uuid2=$(fetch_column mac_binding _uuid ip="192.168.10.10" > logical_port="lr-ls1") > check test "$uuid" = "$uuid2" > > -# The other entry must have expired by now. > -check_row_count mac_binding 0 ip="192.168.20.10" logical_port="lr-ls2" > -# Wait for the alive one to expire as well. > -wait_row_count mac_binding 0 ip="192.168.10.10" logical_port="lr-ls1" > +wait_row_count mac_binding 0 > This is slightly changing the test. It should be kept as is. + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > +]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([MAC binding aging - probing]) > +AT_SKIP_IF([test $HAVE_SCAPY = no]) > +ovn_start > + > +send_udp() { > + local hv=$1 dev=$2 hdst=$3 hsrc=$4 idst=$5 isrc=$6 > + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ > + IP(dst='${idst}', src='${isrc}')/UDP()") > + as $hv ovs-appctl netdev-dummy/receive $dev $packet > +} > + > +dump_udp() { > + local hdst=$1 hsrc=$2 idst=$3 isrc=$4 ttl=$5 > + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ > + IP(dst='${idst}', src='${isrc}', ttl=${ttl})/ > \ > + UDP()") > + echo $packet > +} > + > +dump_arp() { > + local op=$1 eth_src=$2 eth_dst=$3 spa=$4 tpa=$5 hwdst=$6 > + > + local packet=$(fmt_pkt "Ether(dst='${eth_dst}', src='${eth_src}')/ \ > + ARP(op=$op, hwsrc='${eth_src}', hwdst='${hwdst}', \ > + psrc='${spa}', pdst='${tpa}')") > + echo $packet > +} > + > +net_add n1 > +sim_add hv1 > +as hv1 > +check ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > +ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg > + > +check ovn-nbctl \ > + -- ls-add ls1 \ > + -- ls-add ls2 \ > + -- lr-add lr \ > + -- set logical_router lr options:mac_binding_age_threshold=15 \ > + -- lrp-add lr lr-ls1 00:00:00:00:10:00 192.168.10.1/24 \ > + -- lrp-add lr lr-ls2 00:00:00:00:20:00 192.168.20.1/24 \ > + -- lsp-add ls1 ls1-lr \ > + -- lsp-set-type ls1-lr router \ > + -- lsp-set-addresses ls1-lr router \ > + -- lsp-set-options ls1-lr router-port=lr-ls1 \ > + -- lsp-add ls1 vif1 \ > + -- lsp-set-addresses vif1 "unknown" \ > + -- lsp-add ls2 ls2-lr \ > + -- lsp-set-type ls2-lr router \ > + -- lsp-set-addresses ls2-lr router \ > + -- lsp-set-options ls2-lr router-port=lr-ls2 \ > + -- lsp-add ls2 vif2 \ > + -- lsp-set-addresses vif2 "unknown" > + > +check ovs-vsctl > \ > + -- add-port br-int vif1 > \ > + -- set interface vif1 external-ids:iface-id=vif1 > \ > + options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap > \ > + -- add-port br-int vif2 > \ > + -- set interface vif2 external-ids:iface-id=vif2 > \ > + options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap > + > +OVN_POPULATE_ARP > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > + > +dnl Wait for pinctrl thread to be connected. > +OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected]) > + > +send_garp hv1 vif1 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 192.168.10.100 > 192.168.10.100 > +wait_row_count mac_binding 1 ip="192.168.10.100" logical_port="lr-ls1" > + > +send_garp hv1 vif2 2 00:00:00:00:10:1b ff:ff:ff:ff:ff:ff 192.168.20.100 > 192.168.20.100 > +wait_row_count mac_binding 1 ip="192.168.20.100" logical_port="lr-ls2" > + > +# Send UDP traffic to create entries in OFTABLE_MAC_BINDING. > +# Please note we are using a different src mac address with respect to > the GARP > +# one. > +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 192.168.20.100 > 192.168.10.100 > +dump_udp 00:00:00:00:10:1b 00:00:00:00:20:00 192.168.20.100 > 192.168.10.100 63 > expected1 > +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected1]) > + > +send_udp hv1 vif2 00:00:00:00:20:00 00:00:00:00:10:2b 192.168.10.100 > 192.168.20.100 > +dump_udp 00:00:00:00:10:1a 00:00:00:00:10:00 192.168.10.100 > 192.168.20.100 63 > expected2 > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected2]) > + > +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > \ > + sed > 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ > + grep -q > "reg0=0xc0a80a64,reg15=<cleared>,metadata=<cleared> > actions=mod_dl_dst:00:00:00:00:10:1a")]) > +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > \ > + sed > 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ > + grep -q > "reg0=0xc0a81464,reg15=<cleared>,metadata=<cleared> > actions=mod_dl_dst:00:00:00:00:10:1b")]) > + > +ts0=$(fetch_column Mac_Binding timestamp ip=192.168.10.100) > +# Wait for ARP requests to be generated. > +dump_arp 1 00:00:00:00:10:00 ff:ff:ff:ff:ff:ff 192.168.10.1 > 192.168.10.100 00:00:00:00:00:00 >> expected2 > +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected2]) > +# Check MAC_Binding timestamp is updated receiving the ARP replay. > +send_garp hv1 vif1 2 00:00:00:00:10:1a 00:00:00:00:10:00 192.168.10.100 > 192.168.10.1 > +OVS_WAIT_UNTIL([test $(fetch_column Mac_Binding timestamp > ip=192.168.10.100) -gt $ts0]) > +# Refresh OFTABLE_MAC_BINDING entires. > +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 192.168.20.100 > 192.168.10.100 > +send_udp hv1 vif2 00:00:00:00:20:00 00:00:00:00:10:2b 192.168.10.100 > 192.168.20.100 > +# Check OVN tries to refresh MAC Binding entries. > +dump_arp 1 00:00:00:00:10:00 ff:ff:ff:ff:ff:ff 192.168.10.1 > 192.168.10.100 00:00:00:00:00:00 >> expected2 > +wait_row_count mac_binding 0 ip="192.168.10.100" logical_port="lr-ls1" > +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected2]) > + > +dump_arp 1 00:00:00:00:20:00 ff:ff:ff:ff:ff:ff 192.168.20.1 > 192.168.20.100 00:00:00:00:00:00 >> expected1 > +wait_row_count mac_binding 0 ip="192.168.20.100" logical_port="lr-ls2" > +OVN_CHECK_PACKETS_CONTAIN([hv1/vif2-tx.pcap], [expected1]) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > +]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([MAC binding aging - probing GW router Dynamic Neigh]) > +AT_SKIP_IF([test $HAVE_SCAPY = no]) > +ovn_start > + > +send_imcp_echo_req() { > + local hv=$1 dev=$2 hdst=$3 hsrc=$4 idst=$5 isrc=$6 > + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ > + IP(dst='${idst}', src='${isrc}')/ICMP()") > + as $hv ovs-appctl netdev-dummy/receive $dev $packet > +} > + > +dump_icmp() { > + local hdst=$1 hsrc=$2 idst=$3 isrc=$4 ttl=$5 type=$6 chksum=$7 > + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ > + IP(dst='${idst}', src='${isrc}', ttl=${ttl})/\ > + ICMP(type=${type}, chksum=${chksum})") > + > + echo $packet > +} > + > +net_add n1 > +sim_add hv1 > +as hv1 > +check ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > +ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg > + > +check ovn-nbctl \ > + -- ls-add public \ > + -- ls-add join \ > + -- lr-add lr \ > + -- lr-add gw \ > + -- set Logical_Router gw options:chassis="hv1" \ > + -- set logical_router gw options:mac_binding_age_threshold=15 \ > + -- set logical_router gw options:dynamic_neigh_routers=true \ > + -- lrp-add gw gw-public 00:00:00:00:10:00 192.168.10.1/24 \ > + -- lrp-add gw gw-join 00:00:00:00:20:00 192.168.20.1/24 \ > + -- lsp-add public public-gw \ > + -- lsp-set-type public-gw router \ > + -- lsp-set-addresses public-gw router \ > + -- lsp-set-options public-gw router-port=gw-public \ > + -- lsp-add public public \ > + -- lsp-set-addresses public "unknown" \ > + -- lsp-add join join-gw \ > + -- lsp-set-type join-gw router \ > + -- lsp-set-addresses join-gw router \ > + -- lsp-set-options join-gw router-port=gw-join \ > + -- lrp-add lr lr-join 00:00:00:00:30:00 192.168.20.2/24 \ > + -- lsp-add join join-lr \ > + -- lsp-set-type join-lr router \ > + -- lsp-set-addresses join-lr router \ > + -- lsp-set-options join-lr router-port=lr-join \ > + -- lr-route-add lr 0.0.0.0/0 192.168.20.1 > + > +check ovs-vsctl \ > + -- add-port br-int public \ > + -- set interface public external-ids:iface-id=public \ > + options:tx_pcap=hv1/public-tx.pcap options:rxq_pcap=hv1/public-rx.pcap > + > +OVN_POPULATE_ARP > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > + > +dnl Wait for pinctrl thread to be connected. > +OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected]) > + > +# Send a GARP for an external device to populate MAC Binding table. > +send_garp hv1 public 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 192.168.10.100 > 192.168.10.100 > +wait_row_count mac_binding 1 ip="192.168.10.100" logical_port="gw-public" > + > +# Send ICMP echo request to create entries in OFTABLE_MAC_BINDING. > +send_imcp_echo_req hv1 public 00:00:00:00:10:00 00:00:00:00:10:1a > 192.168.20.2 192.168.10.100 > +dump_icmp 00:00:00:00:10:1a 00:00:00:00:10:00 192.168.10.100 192.168.20.2 > 253 0 0 > expected > +wait_row_count mac_binding 1 ip="192.168.20.2" logical_port="gw-join" > +ts0=$(fetch_column Mac_Binding timestamp ip=192.168.20.2) > +OVN_CHECK_PACKETS_CONTAIN([hv1/public-tx.pcap], [expected]) > +n_arp=$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE |awk > '/arp_spa=192.168.20.2/{print <http://192.168.20.2/%7Bprint> > substr($4,11,1)}') > + > +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > \ > + sed > 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ > + grep -q > "reg0=0xc0a80a64,reg15=<cleared>,metadata=<cleared> > actions=mod_dl_dst:00:00:00:00:10:1a")]) > +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > \ > + sed > 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ > + grep -q > "reg0=0xc0a81402,reg15=<cleared>,metadata=<cleared> > actions=mod_dl_dst:00:00:00:00:30:00")]) > + > +# Check GW router refreshes LR entry. > +OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int > table=OFTABLE_MAC_CACHE_USE |awk '/arp_spa=192.168.20.2/{print > <http://192.168.20.2/%7Bprint> substr($4,11,1)}') -ge $((n_arp+2))]) > > OVN_CLEANUP([hv1]) > AT_CLEANUP > -- > 2.48.1 > > Thanks, Ales _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev