Skip to content

Commit

Permalink
controller: make garp_max_timeout configurable
Browse files Browse the repository at this point in the history
When using VLAN backed networks and OVN routers leveraging the
'ovn-chassis-mac-mappings' option for east-west traffic, the eth.src field is
replaced by the chassis mac address in order to not expose the router mac
address from different nodes and confuse the TOR switch. However doing so
the TOR switch is not able to learn the port/mac bindings for routed E/W
traffic and it is force to always flood it. Fix this issue adding the
capability to configure a given timeout for garp sent by ovn-controller
and not disable it after the exponential backoff in order to keep
refreshing the entries in TOR swtich fdb table.
More into about the issue can be found here [0].

[0] https://mail.openvswitch.org/pipermail/ovs-discuss/2020-September/050678.html
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2087779

Signed-off-by: Lorenzo Bianconi <[email protected]>
Acked-by: Ales Musil <[email protected]>
Signed-off-by: Mark Michelson <[email protected]>
(cherry picked from commit 0d02121)
Signed-off-by: Dumitru Ceara <[email protected]>
  • Loading branch information
LorenzoBianconi authored and dceara committed Dec 7, 2023
1 parent c452d96 commit 00f5227
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 15 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
OVN v22.09.4 - xx xxx xxxx
--------------------------
- Add "garp-max-timeout-sec" config option to vswitchd external-ids to
cap the time between when ovn-controller sends gARP packets.

OVN v22.09.3 - 15 Sep 2023
--------------------------
Expand Down
11 changes: 11 additions & 0 deletions controller/ovn-controller.8.xml
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@
heplful to pin source outer IP for the tunnel when multiple interfaces
are used on the host for overlay traffic.
</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>
Expand Down
4 changes: 3 additions & 1 deletion controller/ovn-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -4421,7 +4421,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
Expand Down
73 changes: 60 additions & 13 deletions controller/pinctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,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 {
Expand Down Expand Up @@ -224,7 +228,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)
Expand Down Expand Up @@ -3588,7 +3593,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);
run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
Expand All @@ -3599,7 +3605,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,
Expand Down Expand Up @@ -4461,7 +4467,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. */
};
Expand Down Expand Up @@ -4490,7 +4497,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);
Expand All @@ -4506,7 +4513,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;

Expand All @@ -4532,6 +4541,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,
Expand All @@ -4556,6 +4571,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,
Expand All @@ -4575,6 +4596,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;
}

Expand Down Expand Up @@ -4660,13 +4687,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;
}

Expand Down Expand Up @@ -5966,13 +5995,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);

Expand Down Expand Up @@ -6003,7 +6045,8 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
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);
}
}

Expand All @@ -6014,7 +6057,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);
}
}

Expand All @@ -6032,6 +6076,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
Expand Down
4 changes: 3 additions & 1 deletion controller/pinctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct ovsdb_idl;
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;
Expand All @@ -55,7 +56,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(const char *br_int_name);
Expand Down
16 changes: 16 additions & 0 deletions tests/ovn.at
Original file line number Diff line number Diff line change
Expand Up @@ -8851,6 +8851,7 @@ 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
Expand Down Expand Up @@ -8940,6 +8941,21 @@ sleep 2
OVN_CHECK_PACKETS_CONTAIN([hv2/snoopvif-tx.pcap], [arp_expected])
OVN_CHECK_PACKETS([hv1/snoopvif-tx.pcap], [empty_expected])

# Temporarily remove lr0 chassis
AT_CHECK([ovn-nbctl --wait=hv 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
Expand Down

0 comments on commit 00f5227

Please sign in to comment.