Currently there is no rate limit on tunnel hardware address ARP/ND
lookups. Furthermote, there is no indication for how frequently action
translation composes these packets.

This patch implements a limit of generating one packet per destination
per second and adds a counter for each time a lookup happens.

Fixes: a36de779d739 ("openvswitch: Userspace tunneling.")
Reported-at: https://issues.redhat.com/browse/FDP-2986
Signed-off-by: Mike Pattrick <[email protected]>
---
v2:
 - Allow the user to adjust the holdout timer
 - Corrected atomic barrier types
 - Display incomplete entries in tnl/neigh/show
v3:
 - Renamed holdout to retrans
 - Adjusted functions of appctl commands
 - Updated comments and variable names
v4:
 - Made testsuite grep more explicit
---
 NEWS                               |   3 +
 lib/tnl-neigh-cache.c              | 162 +++++++++++++++++++++++++----
 lib/tnl-neigh-cache.h              |   2 +-
 ofproto/ofproto-dpif-xlate-cache.c |   2 +-
 ofproto/ofproto-dpif-xlate.c       |  12 ++-
 tests/tunnel-push-pop-ipv6.at      |  27 ++++-
 tests/tunnel-push-pop.at           |  56 ++++++++--
 7 files changed, 229 insertions(+), 35 deletions(-)

diff --git a/NEWS b/NEWS
index d5642f985..a43109a31 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
 Post-v3.7.0
 --------------------
+   - Userspace datapath:
+     * ARP/ND lookups for native tunnel are now rate limited. The holdout
+       timer can be configured with 'ovs/neigh/retrans_time'.
 
 
 v3.7.0 - 16 Feb 2026
diff --git a/lib/tnl-neigh-cache.c b/lib/tnl-neigh-cache.c
index bdff1debc..c6ed258e5 100644
--- a/lib/tnl-neigh-cache.c
+++ b/lib/tnl-neigh-cache.c
@@ -47,6 +47,7 @@
 
 #define NEIGH_ENTRY_DEFAULT_IDLE_TIME_MS  (15 * 60 * 1000)
 #define NEIGH_ENTRY_MAX_AGING_TIME_S  3600
+#define NEIGH_ENTRY_LOOKUP_RETRANS_MS  1000
 
 struct tnl_neigh_entry {
     struct cmap_node cmap_node;
@@ -54,11 +55,13 @@ struct tnl_neigh_entry {
     struct eth_addr mac;
     atomic_llong expires;       /* Expiration time in ms. */
     char br_name[IFNAMSIZ];
+    atomic_bool complete;
 };
 
 static struct cmap table = CMAP_INITIALIZER;
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static atomic_uint32_t neigh_aging;
+static atomic_uint32_t neigh_retrans_ms;
 
 static uint32_t
 tnl_neigh_hash(const struct in6_addr *ip)
@@ -85,6 +88,24 @@ tnl_neigh_get_aging(void)
     return aging;
 }
 
+static uint32_t
+tnl_neigh_get_retrans_ms(void)
+{
+    unsigned int retrans_ms;
+
+    atomic_read_explicit(&neigh_retrans_ms, &retrans_ms, memory_order_acquire);
+    return retrans_ms;
+}
+
+static bool
+tnl_neigh_is_complete(struct tnl_neigh_entry *neigh)
+{
+    bool complete;
+
+    atomic_read_explicit(&neigh->complete, &complete, memory_order_acquire);
+    return complete;
+}
+
 static struct tnl_neigh_entry *
 tnl_neigh_lookup__(const char br_name[IFNAMSIZ], const struct in6_addr *dst)
 {
@@ -98,27 +119,62 @@ tnl_neigh_lookup__(const char br_name[IFNAMSIZ], const 
struct in6_addr *dst)
                 return NULL;
             }
 
-            atomic_store_explicit(&neigh->expires, time_msec() +
-                                  tnl_neigh_get_aging(),
-                                  memory_order_release);
+            if (tnl_neigh_is_complete(neigh)) {
+                atomic_store_explicit(&neigh->expires,
+                                      time_msec() + tnl_neigh_get_aging(),
+                                      memory_order_release);
+            }
+
             return neigh;
         }
     }
     return NULL;
 }
 
+static void
+tnl_neigh_set_partial(const char name[IFNAMSIZ], const struct in6_addr *dst)
+{
+    ovs_mutex_lock(&mutex);
+    struct tnl_neigh_entry *neigh = tnl_neigh_lookup__(name, dst);
+    if (neigh) {
+        /* Entry inserted before lock taken. */
+
+        ovs_mutex_unlock(&mutex);
+        return;
+    }
+    neigh = xmalloc(sizeof *neigh);
+
+    neigh->ip = *dst;
+    atomic_store_relaxed(&neigh->complete, false);
+    atomic_store_relaxed(&neigh->expires,
+                         time_msec() + tnl_neigh_get_retrans_ms());
+    ovs_strlcpy(neigh->br_name, name, sizeof neigh->br_name);
+    cmap_insert(&table, &neigh->cmap_node, tnl_neigh_hash(&neigh->ip));
+
+    ovs_mutex_unlock(&mutex);
+    seq_change(tnl_conf_seq);
+}
+
 int
 tnl_neigh_lookup(const char br_name[IFNAMSIZ], const struct in6_addr *dst,
-                 struct eth_addr *mac)
+                 struct eth_addr *mac, bool insert_partial)
 {
     struct tnl_neigh_entry *neigh;
     int res = ENOENT;
 
     neigh = tnl_neigh_lookup__(br_name, dst);
     if (neigh) {
-        *mac = neigh->mac;
-        res = 0;
+        if (tnl_neigh_is_complete(neigh)) {
+            *mac = neigh->mac;
+            res = 0;
+        } else {
+            res = EINPROGRESS;
+        }
+    } else if (insert_partial && tnl_neigh_get_retrans_ms()) {
+        /* Insert a partial entry only if there is a retransmit timer set. */
+        tnl_neigh_set_partial(br_name, dst);
     }
+
     return res;
 }
 
@@ -142,26 +198,40 @@ tnl_neigh_set(const char name[IFNAMSIZ], const struct 
in6_addr *dst,
 {
     ovs_mutex_lock(&mutex);
     struct tnl_neigh_entry *neigh = tnl_neigh_lookup__(name, dst);
+    bool insert = true;
+
     if (neigh) {
-        if (eth_addr_equals(neigh->mac, mac)) {
-            atomic_store_relaxed(&neigh->expires, time_msec() +
-                                 tnl_neigh_get_aging());
+        if (!tnl_neigh_is_complete(neigh)) {
+            insert = false;
+        } else if (eth_addr_equals(neigh->mac, mac)) {
+            atomic_store_relaxed(&neigh->expires,
+                                 time_msec() + tnl_neigh_get_aging());
             ovs_mutex_unlock(&mutex);
             return;
+        } else {
+            tnl_neigh_delete(neigh);
         }
-        tnl_neigh_delete(neigh);
     }
-    seq_change(tnl_conf_seq);
 
-    neigh = xmalloc(sizeof *neigh);
+    if (insert) {
+        neigh = xmalloc(sizeof *neigh);
+
+        neigh->ip = *dst;
+        ovs_strlcpy(neigh->br_name, name, sizeof neigh->br_name);
+    }
 
-    neigh->ip = *dst;
     neigh->mac = mac;
-    atomic_store_relaxed(&neigh->expires, time_msec() +
-                         tnl_neigh_get_aging());
-    ovs_strlcpy(neigh->br_name, name, sizeof neigh->br_name);
-    cmap_insert(&table, &neigh->cmap_node, tnl_neigh_hash(&neigh->ip));
+    atomic_store_explicit(&neigh->expires,
+                          time_msec() + tnl_neigh_get_aging(),
+                          memory_order_release);
+    atomic_store_explicit(&neigh->complete, true, memory_order_release);
+
+    if (insert) {
+        cmap_insert(&table, &neigh->cmap_node, tnl_neigh_hash(&neigh->ip));
+    }
+
     ovs_mutex_unlock(&mutex);
+    seq_change(tnl_conf_seq);
 }
 
 static void
@@ -308,12 +378,14 @@ tnl_neigh_cache_aging(struct unixctl_conn *conn, int argc,
     }
 
     if (!ovs_scan(argv[1], "%"SCNu32, &aging) ||
-        !aging || aging > NEIGH_ENTRY_MAX_AGING_TIME_S) {
+        !aging || aging > NEIGH_ENTRY_MAX_AGING_TIME_S ||
+        aging * 1000 < tnl_neigh_get_retrans_ms()) {
         unixctl_command_reply_error(conn, "bad aging value");
         return;
     }
 
     aging *= 1000;
+
     atomic_store_explicit(&neigh_aging, aging, memory_order_release);
     new_exp = time_msec() + aging;
 
@@ -329,6 +401,48 @@ tnl_neigh_cache_aging(struct unixctl_conn *conn, int argc,
     unixctl_command_reply(conn, "OK");
 }
 
+static void
+tnl_neigh_cache_retrans_ms(struct unixctl_conn *conn, int argc,
+                        const char *argv[], void *aux OVS_UNUSED)
+{
+    long long int new_exp, curr_exp;
+    struct tnl_neigh_entry *neigh;
+    uint32_t retrans_ms;
+
+    if (argc == 1) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+        ds_put_format(&ds, "%"PRIu32, tnl_neigh_get_retrans_ms());
+        unixctl_command_reply(conn, ds_cstr(&ds));
+        ds_destroy(&ds);
+
+        return;
+    }
+
+    /* Zero retransmit value is acceptable. */
+    if (!ovs_scan(argv[1], "%"SCNu32, &retrans_ms) ||
+        retrans_ms > tnl_neigh_get_aging()) {
+        unixctl_command_reply_error(conn, "bad retrans_ms value");
+        return;
+    }
+
+    atomic_store_explicit(&neigh_retrans_ms, retrans_ms, memory_order_release);
+    new_exp = time_msec() + retrans_ms;
+
+    CMAP_FOR_EACH (neigh, cmap_node, &table) {
+        if (tnl_neigh_is_complete(neigh)) {
+            continue;
+        }
+        atomic_read_explicit(&neigh->expires, &curr_exp,
+                             memory_order_acquire);
+        if (new_exp < curr_exp) {
+            atomic_store_explicit(&neigh->expires, new_exp,
+                                  memory_order_release);
+        }
+    }
+
+    unixctl_command_reply(conn, "OK");
+}
+
 static int
 lookup_any(const char *host_name, struct in6_addr *address)
 {
@@ -387,8 +501,13 @@ tnl_neigh_cache_show(struct unixctl_conn *conn, int argc 
OVS_UNUSED,
         need_ws = INET6_ADDRSTRLEN - (ds.length - start_len);
         ds_put_char_multiple(&ds, ' ', need_ws);
 
-        ds_put_format(&ds, ETH_ADDR_FMT"   %s",
-                      ETH_ADDR_ARGS(neigh->mac), neigh->br_name);
+        if (tnl_neigh_is_complete(neigh)) {
+            ds_put_format(&ds, ETH_ADDR_FMT"   %s",
+                          ETH_ADDR_ARGS(neigh->mac), neigh->br_name);
+        } else {
+            ds_put_format(&ds, "                    %s INCOMPLETE",
+                          neigh->br_name);
+        }
         if (tnl_neigh_expired(neigh)) {
             ds_put_format(&ds, " STALE");
         }
@@ -404,6 +523,7 @@ void
 tnl_neigh_cache_init(void)
 {
     atomic_init(&neigh_aging, NEIGH_ENTRY_DEFAULT_IDLE_TIME_MS);
+    atomic_init(&neigh_retrans_ms, NEIGH_ENTRY_LOOKUP_RETRANS_MS);
     unixctl_command_register("tnl/arp/show", "", 0, 0,
                              tnl_neigh_cache_show, NULL);
     unixctl_command_register("tnl/arp/set", "BRIDGE IP MAC", 3, 3,
@@ -420,4 +540,6 @@ tnl_neigh_cache_init(void)
                              tnl_neigh_cache_flush, NULL);
     unixctl_command_register("tnl/neigh/aging", "[SECS]", 0, 1,
                              tnl_neigh_cache_aging, NULL);
+    unixctl_command_register("tnl/neigh/retrans_ms", "[MSECS]", 0, 1,
+                             tnl_neigh_cache_retrans_ms, NULL);
 }
diff --git a/lib/tnl-neigh-cache.h b/lib/tnl-neigh-cache.h
index 877bca312..e16155b4d 100644
--- a/lib/tnl-neigh-cache.h
+++ b/lib/tnl-neigh-cache.h
@@ -36,7 +36,7 @@ int tnl_neigh_snoop(const struct flow *flow, struct 
flow_wildcards *wc,
 void tnl_neigh_set(const char name[IFNAMSIZ], const struct in6_addr *dst,
                    const struct eth_addr mac);
 int tnl_neigh_lookup(const char dev_name[IFNAMSIZ], const struct in6_addr *dst,
-                     struct eth_addr *mac);
+                     struct eth_addr *mac, bool insert_partial);
 void tnl_neigh_cache_init(void);
 void tnl_neigh_cache_run(void);
 void tnl_neigh_flush(const char dev_name[IFNAMSIZ]);
diff --git a/ofproto/ofproto-dpif-xlate-cache.c 
b/ofproto/ofproto-dpif-xlate-cache.c
index c6d935cf0..16cf812c9 100644
--- a/ofproto/ofproto-dpif-xlate-cache.c
+++ b/ofproto/ofproto-dpif-xlate-cache.c
@@ -152,7 +152,7 @@ xlate_push_stats_entry(struct xc_entry *entry,
     case XC_TNL_NEIGH:
         /* Lookup neighbor to avoid timeout. */
         tnl_neigh_lookup(entry->tnl_neigh_cache.br_name,
-                         &entry->tnl_neigh_cache.d_ipv6, &dmac);
+                         &entry->tnl_neigh_cache.d_ipv6, &dmac, false);
         break;
     case XC_TUNNEL_HEADER:
         if (entry->tunnel_hdr.operation == ADD) {
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index de82e2903..0b38dcf55 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -72,6 +72,7 @@
 COVERAGE_DEFINE(xlate_actions);
 COVERAGE_DEFINE(xlate_actions_oversize);
 COVERAGE_DEFINE(xlate_actions_too_many_output);
+COVERAGE_DEFINE(xlate_actions_neigh_sent);
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 
@@ -3931,17 +3932,26 @@ native_tunnel_output(struct xlate_ctx *ctx, const 
struct xport *xport,
         s_ip = in6_addr_get_mapped_ipv4(&s_ip6);
     }
 
-    err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &dmac);
+    err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &dmac, true);
     if (err) {
         struct in6_addr nh_s_ip6 = in6addr_any;
 
         put_cloned_drop_action(ctx->xbridge->ofproto, ctx->odp_actions,
                                XLATE_TUNNEL_NEIGH_CACHE_MISS,
                                !is_last_action);
+        if (err == EINPROGRESS) {
+            xlate_report(ctx, OFT_DETAIL,
+                         "neighbor cache miss for %s on bridge %s, "
+                         "waiting on %s request",
+                         buf_dip6, out_dev->xbridge->name,
+                         d_ip ? "ARP" : "ND");
+            return err;
+        }
         xlate_report(ctx, OFT_DETAIL,
                      "neighbor cache miss for %s on bridge %s, "
                      "sending %s request",
                      buf_dip6, out_dev->xbridge->name, d_ip ? "ARP" : "ND");
+        COVERAGE_INC(xlate_actions_neigh_sent);
 
         err = ovs_router_get_netdev_source_address(
             &d_ip6, netdev_get_name(out_dev->netdev), &nh_s_ip6);
diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
index b11387345..10585e484 100644
--- a/tests/tunnel-push-pop-ipv6.at
+++ b/tests/tunnel-push-pop-ipv6.at
@@ -344,7 +344,20 @@ AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 dnl Check Neighbour discovery.
 AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap])
 
-AT_CHECK([ovs-appctl netdev-dummy/receive int-br 
'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'])
+dnl First trace should send only two ND lookups, for each destination IP.
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(int-br),]dnl
+  [eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),]dnl
+  [ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),]dnl
+  [icmp(type=0,code=0)"], [0], [stdout])
+AT_CHECK([grep -q "sending ND request" stdout], [0])
+
+dnl Second trace should not send any ND lookups.
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(int-br),]dnl
+  [eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),]dnl
+  [ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),]dnl
+  [icmp(type=0,code=0)"], [0], [stdout])
+AT_CHECK([grep -q "waiting on ND request" stdout], [0])
+AT_CHECK([grep -qv "sending ND request" stdout], [0])
 
 dnl Wait for the two Neighbor Solicitation packets to be sent.
 dnl Sometimes the system can be slow (e.g. under valgrind)
@@ -360,6 +373,8 @@ AT_CHECK([cat p0.pcap.txt | grep 
93aa55aa55000086dd6000000000203aff2001cafe | un
 ])
 
 dnl Set the aging time to 5 seconds
+AT_CHECK([ovs-appctl tnl/neigh/retrans_ms 5000], [0], [OK
+])
 AT_CHECK([ovs-appctl tnl/neigh/aging 5], [0], [OK
 ])
 
@@ -373,6 +388,7 @@ AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::92 
aa:bb:cc:00:00:01], [0], [O
 
 AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
 2001:cafe::92                                 aa:bb:cc:00:00:01   br0
+2001:cafe::93                                                     br0 
INCOMPLETE
 ])
 
 ovs-appctl time/warp 5000
@@ -438,7 +454,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 
'in_port(200),eth(src=f8:bc:12:44:3
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+dnl Previous receive causes a flood that results in a lookup of 1.1.2.93.
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE | sort], 
[0], [dnl
 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
 ])
 
@@ -577,8 +594,10 @@ 
icmp,vlan_tci=0x0000,dl_src=be:b6:f4:e1:49:4a,dl_dst=fe:71:d8:83:72:4f,nw_src=30
 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
   port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
 ])
-AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
-recirc_id(0),tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(+key)),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no),
 packets:0, bytes:0, used:never, 
actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
+AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)' | 
strip_recirc], [0], [dnl
+[recirc_id(<recirc>),tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(+key)),]dnl
+[in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
packets:0, bytes:0, used:never, ]dnl
+[actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=<recirc>,rule_cookie=0,controller_id=0,max_len=65535))]
 ])
 
 dnl Receive VXLAN with different MAC and verify that the neigh cache gets 
updated
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index f22a37570..ff0045858 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -256,7 +256,26 @@ AT_CHECK([ovs-appctl revalidator/wait])
 dnl Check ARP request
 AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap])
 
-AT_CHECK([ovs-appctl netdev-dummy/receive int-br 
'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'])
+dnl First trace should send only two ARP lookups, for each destination IP.
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(int-br),]dnl
+  [eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),]dnl
+  [ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),]dnl
+  [icmp(type=0,code=0)"], [0], [stdout])
+AT_CHECK([grep -q "sending ARP request" stdout], [0])
+
+dnl Check partial entry installed.
+AT_CHECK([ovs-appctl tnl/arp/show | grep INCOMPLETE | sort], [0], [dnl
+1.1.2.92                                                          br0 
INCOMPLETE
+1.1.2.93                                                          br0 
INCOMPLETE
+])
+
+dnl Second trace should not send any ARP lookups.
+AT_CHECK([[ovs-appctl ofproto/trace ovs-dummy "in_port(int-br),]dnl
+  [eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),]dnl
+  [ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),]dnl
+  [icmp(type=0,code=0)"]], [0], [stdout])
+AT_CHECK([grep -q "waiting on ARP request" stdout], [0])
+AT_CHECK([grep -qv "sending ARP request" stdout], [0])
 
 dnl Wait for the two ARP requests to be sent. Sometimes the system
 dnl can be slow (e.g. under valgrind)
@@ -277,6 +296,9 @@ bad aging value
 ovs-appctl: ovs-vswitchd: server returned an error
 ])
 
+AT_CHECK([ovs-appctl tnl/neigh/retrans_ms 0], [0], [OK
+])
+
 AT_CHECK([ovs-appctl tnl/neigh/aging 3601], [2], [], [dnl
 bad aging value
 ovs-appctl: ovs-vswitchd: server returned an error
@@ -285,9 +307,24 @@ ovs-appctl: ovs-vswitchd: server returned an error
 AT_CHECK([ovs-appctl tnl/neigh/aging 1], [0], [OK
 ])
 
+dnl Retrans must be smaller than aging.
+AT_CHECK([ovs-appctl tnl/neigh/retrans_ms 2000], [2], [], [dnl
+bad retrans_ms value
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
 AT_CHECK([ovs-appctl tnl/neigh/aging 3600], [0], [OK
 ])
 
+AT_CHECK([ovs-appctl tnl/neigh/retrans_ms 5000], [0], [OK
+])
+
+dnl Reducing aging below retrans_ms will return error.
+AT_CHECK([ovs-appctl tnl/neigh/aging 4], [2], [], [dnl
+bad aging value
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
 dnl Set the aging time to 5 seconds
 AT_CHECK([ovs-appctl tnl/neigh/aging 5], [0], [OK
 ])
@@ -296,6 +333,9 @@ dnl Read the current aging time
 AT_CHECK([ovs-appctl tnl/neigh/aging], [0], [5
 ])
 
+AT_CHECK([ovs-appctl tnl/neigh/retrans_ms], [0], [5000
+])
+
 dnl Add an entry
 AT_CHECK([ovs-appctl tnl/neigh/set br0 1.1.2.92 aa:bb:cc:00:00:01], [0], [OK
 ])
@@ -324,7 +364,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:c8   br0
 ])
 
@@ -334,7 +374,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:c8   br0
 ])
 
@@ -345,7 +385,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:c8   br0
 ])
 
@@ -355,7 +395,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:b6   br0
 ])
 
@@ -366,7 +406,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 
'recirc_id(0),in_port(200),eth(src=
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:b6   br0
 ])
 
@@ -376,7 +416,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:c8   br0
 ])
 
@@ -386,7 +426,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p0 
'recirc_id(0),in_port(1),eth(src=f8
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
 
-AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | grep -v INCOMPLETE], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:b2   br0
 ])
 
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to