@@ -1,5 +1,7 @@
bin_PROGRAMS += ovn/controller/ovn-controller
ovn_controller_ovn_controller_SOURCES = \
+ ovn/controller/bfd.c \
+ ovn/controller/bfd.h \
ovn/controller/binding.c \
ovn/controller/binding.h \
ovn/controller/chassis.c \
new file mode 100644
@@ -0,0 +1,201 @@
+/* Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "bfd.h"
+#include "gchassis.h"
+#include "lport.h"
+#include "ovn-controller.h"
+
+#include "lib/hash.h"
+#include "lib/sset.h"
+#include "lib/util.h"
+#include "lib/vswitch-idl.h"
+#include "openvswitch/vlog.h"
+#include "ovn/lib/ovn-sb-idl.h"
+#include "ovn-controller.h"
+
+VLOG_DEFINE_THIS_MODULE(ovn_bfd);
+
+void
+bfd_register_ovs_idl(struct ovsdb_idl *ovs_idl)
+{
+ /* NOTE: this assumes that binding.c has added the
+ * ovsrec_interface table */
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
+}
+
+
+static void
+interface_set_bfd(const struct ovsrec_interface *iface, bool bfd_setting)
+{
+ const char *new_setting = bfd_setting ? "true":"false";
+ const char *current_setting = smap_get(&iface->bfd, "enable");
+ if (current_setting && !strcmp(current_setting, new_setting)) {
+ /* If already set to true we skip setting it again
+ * to avoid flapping to bfd initialization state */
+ return;
+ }
+ const struct smap bfd = SMAP_CONST1(&bfd, "enable", new_setting);
+ ovsrec_interface_verify_bfd(iface);
+ ovsrec_interface_set_bfd(iface, &bfd);
+ VLOG_INFO("%s BFD on interface %s", bfd_setting?"Enabled":"Disabled",
+ iface->name);
+}
+
+void
+bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
+ struct sset *active_tunnels)
+{
+ int i;
+
+ for (i = 0; i < br_int->n_ports; i++) {
+ const struct ovsrec_port *port_rec = br_int->ports[i];
+
+ if (!strcmp(port_rec->name, br_int->name)) {
+ continue;
+ }
+
+ int j;
+ for (j = 0; j < port_rec->n_interfaces; j++) {
+ const struct ovsrec_interface *iface_rec;
+ iface_rec = port_rec->interfaces[j];
+
+ /* Check if this is a tunnel interface. */
+ if (smap_get(&iface_rec->options, "remote_ip")) {
+ /* Add ovn-chassis-id if the bfd_status of the tunnel
+ * is active */
+ const char *bfd = smap_get(&iface_rec->bfd, "enable");
+ if (bfd && !strcmp(bfd, "true")) {
+ const char *status = smap_get(&iface_rec->bfd_status,
+ "state");
+ if (status && !strcmp(status, "up")) {
+ const char *id = smap_get(&port_rec->external_ids,
+ "ovn-chassis-id");
+ if (id) {
+ sset_add(active_tunnels, id);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+bfd_calculate_chassis(const struct sbrec_chassis *our_chassis,
+ struct hmap *local_datapaths,
+ const struct chassis_index *chassis_index,
+ struct sset *bfd_chassis)
+{
+ /* Identify all chassis nodes to which we need to enable bfd.
+ * 1) Any chassis hosting the chassisredirect ports for known
+ * router datapaths.
+ * 2) Chassis hosting peer datapaths (with ports) connected
+ * to a router datapath when our chassis is hosting a router
+ * with a chassis redirect port. */
+ struct local_datapath *dp;
+ HMAP_FOR_EACH (dp, hmap_node, local_datapaths) {
+ const char *is_router = smap_get(&dp->datapath->external_ids,
+ "logical-router");
+ bool our_chassis_is_gw_for_dp = false;
+ if (is_router) {
+ for (size_t j = 0; j < dp->ldatapath->n_lports; j++) {
+ const struct sbrec_port_binding *pb = dp->ldatapath->lports[j];
+ if (!strcmp(pb->type, "chassisredirect")) {
+ struct ovs_list *gateway_chassis = NULL;
+ gateway_chassis =
+ gateway_chassis_get_ordered(pb, chassis_index);
+ /* we don't need BFD for non-HA chassisredirect */
+ if (!gateway_chassis ||
+ ovs_list_is_short(gateway_chassis)) {
+ continue;
+ }
+ our_chassis_is_gw_for_dp = gateway_chassis_contains(
+ gateway_chassis, our_chassis);
+ struct gateway_chassis *gwc;
+ LIST_FOR_EACH (gwc, node, gateway_chassis) {
+ if (gwc->db->chassis) {
+ sset_add(bfd_chassis, gwc->db->chassis->name);
+ }
+ }
+ gateway_chassis_destroy(gateway_chassis);
+ break;
+ }
+ }
+ }
+ if (our_chassis_is_gw_for_dp) {
+ for (size_t i = 0; i < dp->n_peer_dps; i++) {
+ const struct ldatapath *pdp = dp->peer_dps[i];
+ if (!pdp) {
+ continue;
+ }
+ for (size_t j = 0; j < pdp->n_lports; j++) {
+ const struct sbrec_port_binding *pb = pdp->lports[j];
+ if (pb->chassis) {
+ /* Gateway node has to enable bfd to all nodes hosting
+ * connected network ports */
+ const char *chassis_name = pb->chassis->name;
+ if (chassis_name) {
+ sset_add(bfd_chassis, chassis_name);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+bfd_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
+ const struct sbrec_chassis *chassis_rec, struct hmap *local_datapaths,
+ const struct chassis_index *chassis_index)
+{
+
+ if (!chassis_rec) {
+ return;
+ }
+ struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
+ bfd_calculate_chassis(chassis_rec, local_datapaths, chassis_index,
+ &bfd_chassis);
+ /* Identify tunnels ports(connected to remote chassis id) to enable bfd */
+ struct sset tunnels = SSET_INITIALIZER(&tunnels);
+ struct sset bfd_ifaces = SSET_INITIALIZER(&bfd_ifaces);
+ for (size_t k = 0; k < br_int->n_ports; k++) {
+ const char *chassis_id = smap_get(&br_int->ports[k]->external_ids,
+ "ovn-chassis-id");
+ if (chassis_id) {
+ char *port_name = br_int->ports[k]->name;
+ sset_add(&tunnels, port_name);
+ if (sset_contains(&bfd_chassis, chassis_id)) {
+ sset_add(&bfd_ifaces, port_name);
+ }
+ }
+ }
+
+ /* Enable or disable bfd */
+ const struct ovsrec_interface *iface;
+ OVSREC_INTERFACE_FOR_EACH (iface, ctx->ovs_idl) {
+ if (sset_contains(&tunnels, iface->name)) {
+ interface_set_bfd(
+ iface, sset_contains(&bfd_ifaces, iface->name));
+ }
+ }
+
+ sset_destroy(&tunnels);
+ sset_destroy(&bfd_ifaces);
+ sset_destroy(&bfd_chassis);
+}
new file mode 100644
@@ -0,0 +1,34 @@
+/* Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVN_BFD_H
+#define OVN_BFD_H 1
+
+struct chassis_index;
+struct controller_ctx;
+struct hmap;
+struct ovsdb_idl;
+struct ovsrec_bridge;
+struct sbrec_chassis;
+struct sset;
+
+void bfd_register_ovs_idl(struct ovsdb_idl *);
+void bfd_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
+ const struct sbrec_chassis *chassis_rec,
+ struct hmap *local_datapaths, const struct chassis_index *);
+void bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
+ struct sset *active_tunnels);
+
+#endif
@@ -60,6 +60,8 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status);
ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
@@ -149,6 +151,12 @@ add_local_datapath__(const struct ldatapath_index *ldatapaths,
if (peer && peer->datapath) {
add_local_datapath__(ldatapaths, lports, peer->datapath,
false, depth + 1, local_datapaths);
+ ld->n_peer_dps++;
+ ld->peer_dps = xrealloc(
+ ld->peer_dps,
+ ld->n_peer_dps * sizeof *ld->peer_dps);
+ ld->peer_dps[ld->n_peer_dps - 1] = ldatapath_lookup_by_key(
+ ldatapaths, peer->datapath->tunnel_key);
}
}
}
@@ -359,6 +367,8 @@ static void
consider_local_datapath(struct controller_ctx *ctx,
const struct ldatapath_index *ldatapaths,
const struct lport_index *lports,
+ const struct chassis_index *chassis_index,
+ struct sset *active_tunnels,
const struct sbrec_chassis *chassis_rec,
const struct sbrec_port_binding *binding_rec,
struct hmap *qos_map,
@@ -368,6 +378,7 @@ consider_local_datapath(struct controller_ctx *ctx,
{
const struct ovsrec_interface *iface_rec
= shash_find_data(lport_to_iface, binding_rec->logical_port);
+ struct ovs_list *gateway_chassis = NULL;
bool our_chassis = false;
if (iface_rec
@@ -396,16 +407,28 @@ consider_local_datapath(struct controller_ctx *ctx,
false, local_datapaths);
}
} else if (!strcmp(binding_rec->type, "chassisredirect")) {
- if (gateway_chassis_in_pb_contains(binding_rec, chassis_rec)) {
+ gateway_chassis = gateway_chassis_get_ordered(binding_rec,
+ chassis_index);
+ if (gateway_chassis &&
+ gateway_chassis_contains(gateway_chassis, chassis_rec)) {
+ struct gateway_chassis *gwc;
+ LIST_FOR_EACH (gwc, node, gateway_chassis) {
+ if (!gwc->db->chassis) {
+ continue;
+ }
+ if (!strcmp(gwc->db->chassis->name, chassis_rec->name)) {
+ /* sb_rec_port_binding->chassis should reflect master */
+ our_chassis = true;
+ break;
+ }
+ if (sset_contains(active_tunnels, gwc->db->chassis->name)) {
+ break;
+ }
+ }
add_local_datapath(ldatapaths, lports, binding_rec->datapath,
false, local_datapaths);
- /* XXX this should only be set to true if our chassis
- * (chassis_rec) is the master for this chassisredirect port
- * but for now we'll bind it only when not bound, this is
- * handled in subsequent patches */
- our_chassis = !binding_rec->chassis ||
- chassis_rec == binding_rec->chassis;
}
+ gateway_chassis_destroy(gateway_chassis);
} else if (!strcmp(binding_rec->type, "l3gateway")) {
const char *chassis_id = smap_get(&binding_rec->options,
"l3gateway-chassis");
@@ -475,8 +498,10 @@ void
binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
const struct sbrec_chassis *chassis_rec,
const struct ldatapath_index *ldatapaths,
- const struct lport_index *lports, struct hmap *local_datapaths,
- struct sset *local_lports)
+ const struct lport_index *lports,
+ const struct chassis_index *chassis_index,
+ struct sset *active_tunnels,
+ struct hmap *local_datapaths, struct sset *local_lports)
{
if (!chassis_rec) {
return;
@@ -497,8 +522,8 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
* chassis and update the binding accordingly. This includes both
* directly connected logical ports and children of those ports. */
SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
- consider_local_datapath(ctx, ldatapaths, lports,
- chassis_rec, binding_rec,
+ consider_local_datapath(ctx, ldatapaths, lports, chassis_index,
+ active_tunnels, chassis_rec, binding_rec,
sset_is_empty(&egress_ifaces) ? NULL :
&qos_map, local_datapaths, &lport_to_iface,
local_lports);
@@ -32,7 +32,8 @@ struct sset;
void binding_register_ovs_idl(struct ovsdb_idl *);
void binding_run(struct controller_ctx *, const struct ovsrec_bridge *br_int,
const struct sbrec_chassis *, const struct ldatapath_index *,
- const struct lport_index *, struct hmap *local_datapaths,
+ const struct lport_index *, const struct chassis_index *,
+ struct sset *active_tunnels, struct hmap *local_datapaths,
struct sset *all_lports);
bool binding_cleanup(struct controller_ctx *, const struct sbrec_chassis *);
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <string.h>
+#include "bfd.h"
#include "binding.h"
#include "chassis.h"
#include "command-line.h"
@@ -502,6 +503,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
+ ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_ofport);
@@ -523,6 +526,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
chassis_register_ovs_idl(ovs_idl);
encaps_register_ovs_idl(ovs_idl);
binding_register_ovs_idl(ovs_idl);
+ bfd_register_ovs_idl(ovs_idl);
physical_register_ovs_idl(ovs_idl);
}
@@ -621,6 +625,7 @@ main(int argc, char *argv[])
* l2gateway ports for which options:l2gateway-chassis designates the
* local hypervisor, and localnet ports. */
struct sset local_lports = SSET_INITIALIZER(&local_lports);
+ struct sset active_tunnels = SSET_INITIALIZER(&active_tunnels);
const struct ovsrec_bridge *br_int = get_br_int(&ctx);
const char *chassis_id = get_chassis_id(ctx.ovs_idl);
@@ -639,8 +644,10 @@ main(int argc, char *argv[])
if (chassis_id) {
chassis = chassis_run(&ctx, chassis_id, br_int);
encaps_run(&ctx, br_int, chassis_id);
+ bfd_calculate_active_tunnels(br_int, &active_tunnels);
binding_run(&ctx, br_int, chassis, &ldatapaths, &lports,
- &local_datapaths, &local_lports);
+ &chassis_index, &active_tunnels, &local_datapaths,
+ &local_lports);
}
if (br_int && chassis) {
@@ -664,6 +671,10 @@ main(int argc, char *argv[])
&local_datapaths, &group_table,
&addr_sets, &flow_table);
+ if (chassis_id) {
+ bfd_run(&ctx, br_int, chassis, &local_datapaths,
+ &chassis_index);
+ }
physical_run(&ctx, mff_ovn_geneve,
br_int, chassis, &ct_zones, &lports,
&flow_table, &local_datapaths, &local_lports,
@@ -717,9 +728,11 @@ main(int argc, char *argv[])
chassis_index_destroy(&chassis_index);
sset_destroy(&local_lports);
+ sset_destroy(&active_tunnels);
struct local_datapath *cur_node, *next_node;
HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, &local_datapaths) {
+ free(cur_node->peer_dps);
hmap_remove(&local_datapaths, &cur_node->hmap_node);
free(cur_node);
}
@@ -23,6 +23,8 @@
/* Linux supports a maximum of 64K zones, which seems like a fine default. */
#define MAX_CT_ZONES 65535
+struct sset;
+
struct controller_ctx {
struct ovsdb_idl *ovnsb_idl;
struct ovsdb_idl_txn *ovnsb_idl_txn;
@@ -66,6 +68,8 @@ struct local_datapath {
/* True if this datapath contains an l3gateway port located on this
* hypervisor. */
bool has_local_l3gateway;
+ const struct ldatapath **peer_dps;
+ size_t n_peer_dps;
};
struct local_datapath *get_local_datapath(const struct hmap *,
@@ -7787,6 +7787,22 @@ echo "------ Port_Binding chassisredirect -------"
ovn-sbctl find Port_Binding type=chassisredirect
echo "-------------------------------------------"
+for chassis in gw1 gw2 hv1 hv2; do
+ as $chassis
+ echo "------ $chassis dump ----------"
+ ovs-ofctl show br-int
+ ovs-ofctl dump-flows br-int
+ echo ""
+ echo "BFD (from $chassis):"
+ # dump BFD config and status to the other chassis
+ for chassis2 in gw1 gw2 hv1 hv2; do
+ if [[ "$chassis" != "$chassis2" ]]; then
+ echo " -> $chassis2:"
+ echo " $(ovs-vsctl --bare --columns bfd,bfd_status find Interface name=ovn-$chassis2-0)"
+ fi
+ done
+ echo "--------------------------"
+done
hv1_gw1_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0)
hv1_gw2_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0)
@@ -7828,6 +7844,52 @@ AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | gre
])
+# check BFD enablement on tunnel ports from gw1 #########
+as gw1
+for chassis in gw2 hv1 hv2; do
+ echo "checking gw1 -> $chassis"
+ AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
+ [[enable=true
+]])
+done
+
+
+# check BFD enablement on tunnel ports from gw2 ##########
+as gw2
+for chassis in gw1 hv1 hv2; do
+ echo "checking gw2 -> $chassis"
+ AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
+ [[enable=true
+]])
+done
+
+# check BFD enablement on tunnel ports from hv1 ###########
+as hv1
+for chassis in gw1 gw2; do
+ echo "checking hv1 -> $chassis"
+ AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
+ [[enable=true
+]])
+done
+# make sure BFD is not enabled to hv2, we don't need it
+AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv2-0],[0],
+ [[enable=false
+]])
+
+
+# check BFD enablement on tunnel ports from hv2 ##########
+as hv2
+for chassis in gw1 gw2; do
+ echo "checking hv2 -> $chassis"
+ AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
+ [[enable=true
+]])
+done
+# make sure BFD is not enabled to hv1, we don't need it
+AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv1-0],[0],
+ [[enable=false
+]])
+
OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
AT_CLEANUP