@@ -3,6 +3,8 @@ OVN v21.12.3 - xx xxx xxxx
- A new ACL option, "log-related" has been added that allows for reply and
related traffic to be logged for an ACL in addition to the traffic that
directly matches the ACL.
+ - Add "garp-max-timeout-sec" config option to vswitchd external-ids to
+ cap the time between when ovn-controller sends gARP packets.
OVN v21.12.2 - 03 Jun 2022
--------------------------
@@ -338,6 +338,17 @@
of how many entries there are in the cache. By default this is set to
30000 (30 seconds).
</dd>
+ <dt><code>external_ids:garp-max-timeout-sec</code></dt>
+ <dd>
+ When used, this configuration value specifies the maximum timeout
+ (in seconds) between two consecutive GARP packets sent by
+ <code>ovn-controller</code>.
+ <code>ovn-controller</code> by default sends just 4 GARP packets
+ with an exponential backoff timeout.
+ Setting <code>external_ids:garp-max-timeout-sec</code> allows to
+ cap for the exponential backoff used by <code>ovn-controller</code>
+ to send GARPs packets.
+ </dd>
</dl>
<p>
@@ -3888,7 +3888,9 @@ main(int argc, char *argv[])
&runtime_data->local_datapaths,
&runtime_data->active_tunnels,
&runtime_data->local_active_ports_ipv6_pd,
- &runtime_data->local_active_ports_ras);
+ &runtime_data->local_active_ports_ras,
+ ovsrec_open_vswitch_table_get(
+ ovs_idl_loop.idl));
stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME,
time_msec());
/* Updating monitor conditions if runtime data or
@@ -164,6 +164,10 @@ static struct ovs_mutex pinctrl_mutex = OVS_MUTEX_INITIALIZER;
static struct seq *pinctrl_handler_seq;
static struct seq *pinctrl_main_seq;
+#define GARP_RARP_DEF_MAX_TIMEOUT 16000
+static long long int garp_rarp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
+static bool garp_rarp_continuous;
+
static void *pinctrl_handler(void *arg);
struct pinctrl {
@@ -209,7 +213,8 @@ static void send_garp_rarp_prepare(
const struct ovsrec_bridge *,
const struct sbrec_chassis *,
const struct hmap *local_datapaths,
- const struct sset *active_tunnels)
+ const struct sset *active_tunnels,
+ const struct ovsrec_open_vswitch_table *ovs_table)
OVS_REQUIRES(pinctrl_mutex);
static void send_garp_rarp_run(struct rconn *swconn,
long long int *send_garp_rarp_time)
@@ -3475,7 +3480,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
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 shash *local_active_ports_ras,
+ const struct ovsrec_open_vswitch_table *ovs_table)
{
ovs_mutex_lock(&pinctrl_mutex);
pinctrl_set_br_int_name_(br_int->name);
@@ -3487,7 +3493,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
send_garp_rarp_prepare(ovnsb_idl_txn, sbrec_port_binding_by_datapath,
sbrec_port_binding_by_name,
sbrec_mac_binding_by_lport_ip, br_int, chassis,
- local_datapaths, active_tunnels);
+ local_datapaths, active_tunnels, 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,
@@ -4330,7 +4336,8 @@ 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 for the next announcement. */
+ 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. */
};
@@ -4359,7 +4366,7 @@ add_garp_rarp(const char *name, const struct eth_addr ea, ovs_be32 ip,
garp_rarp->ea = ea;
garp_rarp->ipv4 = ip;
garp_rarp->announce_time = time_msec() + 1000;
- garp_rarp->backoff = 1;
+ 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);
@@ -4375,7 +4382,9 @@ 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)
+ struct shash *nat_addresses,
+ long long int garp_max_timeout,
+ bool garp_continuous)
{
volatile struct garp_rarp_data *garp_rarp = NULL;
@@ -4401,6 +4410,12 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
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,
laddrs->ipv4_addrs[i].addr,
@@ -4425,6 +4440,12 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
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,
@@ -4444,6 +4465,12 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
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;
}
@@ -4529,13 +4556,15 @@ send_garp_rarp(struct rconn *swconn, struct garp_rarp_data *garp_rarp,
ofpbuf_uninit(&ofpacts);
/* Set the next announcement. At most 5 announcements are sent for a
- * vif. */
- if (garp_rarp->backoff < 16) {
- garp_rarp->backoff *= 2;
- garp_rarp->announce_time = current_time + garp_rarp->backoff * 1000;
+ * vif if garp_rarp_max_timeout is not specified otherwise cap the max
+ * timeout to garp_rarp_max_timeout. */
+ if (garp_rarp_continuous || garp_rarp->backoff < garp_rarp_max_timeout) {
+ garp_rarp->announce_time = current_time + garp_rarp->backoff;
} else {
garp_rarp->announce_time = LLONG_MAX;
}
+ garp_rarp->backoff = MIN(garp_rarp_max_timeout, garp_rarp->backoff * 2);
+
return garp_rarp->announce_time;
}
@@ -5778,13 +5807,26 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
const struct ovsrec_bridge *br_int,
const struct sbrec_chassis *chassis,
const struct hmap *local_datapaths,
- const struct sset *active_tunnels)
+ 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;
+ bool garp_continuous = false;
+ const struct ovsrec_open_vswitch *cfg =
+ ovsrec_open_vswitch_table_first(ovs_table);
+ if (cfg) {
+ garp_max_timeout = smap_get_ullong(
+ &cfg->external_ids, "garp-max-timeout-sec", 0) * 1000;
+ garp_continuous = !!garp_max_timeout;
+ if (!garp_max_timeout) {
+ garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
+ }
+ }
shash_init(&nat_addresses);
@@ -5813,8 +5855,10 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
const struct sbrec_port_binding *pb = lport_lookup_by_name(
sbrec_port_binding_by_name, iface_id);
if (pb) {
- send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
- local_datapaths, pb, &nat_addresses);
+ send_garp_rarp_update(ovnsb_idl_txn,
+ sbrec_mac_binding_by_lport_ip,
+ local_datapaths, pb, &nat_addresses,
+ garp_max_timeout, garp_continuous);
}
}
@@ -5825,7 +5869,8 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
= lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
if (pb) {
send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
- local_datapaths, pb, &nat_addresses);
+ local_datapaths, pb, &nat_addresses,
+ garp_max_timeout, garp_continuous);
}
}
@@ -5843,6 +5888,9 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
shash_destroy(&nat_addresses);
sset_destroy(&nat_ip_keys);
+
+ garp_rarp_max_timeout = garp_max_timeout;
+ garp_rarp_continuous = garp_continuous;
}
static bool
@@ -28,6 +28,7 @@ struct lport_index;
struct ovsdb_idl_index;
struct ovsdb_idl_txn;
struct ovsrec_bridge;
+struct ovsrec_open_vswitch_table;
struct sbrec_chassis;
struct sbrec_dns_table;
struct sbrec_controller_event_table;
@@ -52,7 +53,8 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
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 shash *local_active_ports_ras,
+ const struct ovsrec_open_vswitch_table *ovs_table);
void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
void pinctrl_destroy(void);
void pinctrl_set_br_int_name(char *br_int_name);
@@ -30762,3 +30762,108 @@ check test "$current_id" = "$prev_id"
OVN_CLEANUP([hv1],[hv2])
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([send gratuitous arp for l3gateway only on selected chassis])
+AT_SKIP_IF([test $HAVE_TCPDUMP = no])
+ovn_start
+
+# Create logical switch
+ovn-nbctl ls-add ls0
+# Create gateway router
+ovn-nbctl lr-add lr0
+# Add router port to gateway router
+ovn-nbctl lrp-add lr0 lr0-ls0 f0:00:00:00:00:01 192.168.0.1/24
+ovn-nbctl lsp-add ls0 ls0-lr0 -- set Logical_Switch_Port ls0-lr0 \
+ type=router options:router-port=lr0-ls0 addresses='"f0:00:00:00:00:01"'
+
+# Create a localnet port.
+ovn-nbctl lsp-add ls0 ln_port
+ovn-nbctl lsp-set-addresses ln_port unknown
+ovn-nbctl lsp-set-type ln_port localnet
+ovn-nbctl --wait=hv lsp-set-options ln_port network_name=physnet1
+
+# Prepare packets
+touch empty_expected
+echo "fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" > arp_expected
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl \
+ -- add-br br-phys \
+ -- add-br br-eth0
+
+ovn_attach n1 br-phys 192.168.0.10
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
+AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
+
+sim_add hv2
+as hv2
+ovs-vsctl \
+ -- add-br br-phys \
+ -- add-br br-eth0
+
+ovn_attach n1 br-phys 192.168.0.20
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
+#AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv2/snoopvif-tx.pcap options:rxq_pcap=hv2/snoopvif-rx.pcap])
+
+ovn-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
+
+# Wait until the patch ports are created in hv1 and hv2 to connect br-int to br-eth0
+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv1])
+OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l`])
+
+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv2])
+OVS_WAIT_UNTIL([test 1 = `as hv2 ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l`])
+
+# Temporarily remove lr0 chassis
+AT_CHECK([ovn-nbctl remove logical_router lr0 options chassis])
+
+reset_pcap_file() {
+ local hv=$1
+ local iface=$2
+ local pcap_file=$3
+ as $hv
+ ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+ rm -f ${pcap_file}*.pcap
+ ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+reset_pcap_file hv1 snoopvif hv1/snoopvif
+reset_pcap_file hv2 snoopvif hv2/snoopvif
+
+hv1_uuid=$(ovn-sbctl --bare --columns _uuid list chassis hv1)
+AT_CHECK([ovn-nbctl set logical_router lr0 options:chassis=hv1])
+OVS_WAIT_UNTIL([
+ ls0_lr0=$(ovn-sbctl --bare --columns chassis list port_binding ls0-lr0)
+ test "$ls0_lr0" = $hv1_uuid
+])
+
+sleep 2
+OVN_CHECK_PACKETS_CONTAIN([hv1/snoopvif-tx.pcap], [arp_expected])
+OVN_CHECK_PACKETS([hv2/snoopvif-tx.pcap], [empty_expected])
+
+# Temporarily remove lr0 chassis
+AT_CHECK([ovn-nbctl remove logical_router lr0 options chassis])
+as hv1 reset_pcap_file snoopvif hv1/snoopvif
+as hv2 reset_pcap_file snoopvif hv2/snoopvif
+
+AT_CHECK([ovn-nbctl --wait=hv set logical_router lr0 options:chassis=hv1])
+# set garp max timeout to 2s
+AT_CHECK([as hv1 ovs-vsctl set Open_vSwitch . external-ids:garp-max-timeout-sec=2])
+
+OVS_WAIT_UNTIL([
+n_arp=$(tcpdump -c 10 -ner hv1/snoopvif-tx.pcap arp | wc -l)
+test "$n_arp" = 10
+])
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+])