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

Reply via email to