diff mbox series

[ovs-dev,RFC,3/3] controller: bfd: introduce BFD state machine

Message ID 0d6d52452333ebdfd601a799c60e63d47b18f4f7.1605196277.git.lorenzo.bianconi@redhat.com
State Superseded
Headers show
Series introduce BFD support in ovn-controller | expand

Commit Message

Lorenzo Bianconi Nov. 12, 2020, 4:10 p.m. UTC
Introduce BFD state machine according to RFC880
https://tools.ietf.org/html/rfc5880

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 controller/pinctrl.c | 103 ++++++++++++++++++++++++++++++++++++++++++-
 northd/ovn-northd.c  |   9 ++++
 2 files changed, 111 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index b6d68e2a5..e88193d77 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -6317,6 +6317,28 @@  sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
 
 }
 
+enum bfd_state {
+    BFD_STATE_ADMIN_DOWN,
+    BFD_STATE_DOWN,
+    BFD_STATE_INIT,
+    BFD_STATE_UP,
+};
+
+static char *
+bfd_get_status(enum bfd_state state)
+{
+    switch (state) {
+    case BFD_STATE_ADMIN_DOWN:
+        return "admin_down";
+    case BFD_STATE_DOWN:
+        return "down";
+    case BFD_STATE_INIT:
+        return "init";
+    case BFD_STATE_UP:
+        return "up";
+    }
+}
+
 static struct hmap bfd_monitor_map;
 
 struct bfd_entry {
@@ -6341,7 +6363,12 @@  struct bfd_entry {
     int64_t port_key;
     int64_t metadata;
 
+    enum bfd_state state;
+    bool change_state;
+
+    uint32_t detection_timeout;
     long long int last_update;
+    long long int last_rx;
     long long int next_tx;
 };
 
@@ -6369,6 +6396,18 @@  pinctrl_find_bfd_monitor_entry_by_port(uint16_t port)
     return NULL;
 }
 
+static struct bfd_entry *
+pinctrl_find_bfd_monitor_entry_by_disc(ovs_be32 disc)
+{
+    struct bfd_entry *entry;
+    HMAP_FOR_EACH (entry, node, &bfd_monitor_map) {
+        if (entry->disc == disc) {
+            return entry;
+        }
+    }
+    return NULL;
+}
+
 static bool
 bfd_monitor_should_inject(void)
 {
@@ -6419,9 +6458,11 @@  bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet)
     udp->udp_dst = htons(BFD_DEST_PORT);
     udp->udp_len = htons(sizeof *udp + sizeof *msg);
 
-    msg = dp_packet_put_uninit(packet, sizeof *msg);
+    msg = dp_packet_put_zeros(packet, sizeof *msg);
     msg->vers_diag = (BFD_VERSION << 5);
     msg->length = BFD_PACKET_LEN;
+    msg->flags = entry->state << 6;
+    msg->my_disc = entry->disc;
 }
 
 static void
@@ -6432,6 +6473,10 @@  bfd_monitor_send_msg(struct rconn *swconn, long long int *bfd_time)
     struct bfd_entry *entry;
 
     HMAP_FOR_EACH (entry, node, &bfd_monitor_map) {
+        if (cur_time > entry->last_rx + entry->detection_timeout) {
+            entry->state = BFD_STATE_DOWN;
+        }
+
         if (cur_time < entry->next_tx) {
             goto next;
         }
@@ -6483,6 +6528,56 @@  pinctrl_handle_bfd_msg(struct rconn *swconn, const struct flow *ip_flow,
                        struct dp_packet *pkt_in, const struct match *md)
     OVS_REQUIRES(pinctrl_mutex)
 {
+    /* XXX add sanity checks here */
+    const struct bfd_msg *msg = dp_packet_get_udp_payload(pkt_in);
+    struct bfd_entry *entry = pinctrl_find_bfd_monitor_entry_by_disc(
+            msg->your_disc);
+    if (!entry) {
+        return;
+    }
+
+    enum bfd_state peer_state = msg->flags >> 6;
+    /* bfd state machine */
+    switch (entry->state) {
+    case BFD_STATE_DOWN:
+        if (peer_state == BFD_STATE_DOWN) {
+            entry->state = BFD_STATE_INIT;
+            entry->change_state = true;
+        }
+        if (peer_state == BFD_STATE_INIT) {
+            entry->state = BFD_STATE_UP;
+            entry->change_state = true;
+        }
+        entry->last_rx = time_msec();
+        break;
+    case BFD_STATE_INIT:
+        if (peer_state == BFD_STATE_INIT ||
+            peer_state == BFD_STATE_UP) {
+            entry->state = BFD_STATE_UP;
+            entry->change_state = true;
+        }
+        if (peer_state == BFD_STATE_ADMIN_DOWN) {
+            entry->state = BFD_STATE_DOWN;
+            entry->change_state = true;
+        }
+        entry->last_rx = time_msec();
+        break;
+    case BFD_STATE_UP:
+        if (peer_state == BFD_STATE_ADMIN_DOWN ||
+            peer_state == BFD_STATE_DOWN) {
+            entry->state = BFD_STATE_DOWN;
+            entry->change_state = true;
+        }
+        entry->last_rx = time_msec();
+        break;
+    case BFD_STATE_ADMIN_DOWN:
+    default:
+        break;
+    }
+
+    if (entry->change_state) {
+        notify_pinctrl_main();
+    }
 }
 
 #define BFD_MONITOR_STALE_TIMEOUT  180000LL
@@ -6564,12 +6659,18 @@  bfd_monitor_run(const struct sbrec_bfd_table *bfd_table,
             entry->udp_src = bt->src_port;
             entry->disc = htonl(bt->disc);
             entry->next_tx = cur_time;
+            entry->last_rx = cur_time;
+            entry->detection_timeout = 30000; /* XXX */
             entry->metadata = pb->datapath->tunnel_key;
             entry->port_key = pb->tunnel_key;
+            entry->state = BFD_STATE_DOWN;
 
             uint32_t hash = hash_string(bt->dst_ip, 0);
             hmap_insert(&bfd_monitor_map, &entry->node, hash);
             changed = true;
+        } else if (entry->change_state) {
+            sbrec_bfd_set_status(bt, bfd_get_status(entry->state));
+            entry->change_state = false;
         }
         entry->last_update = cur_time;
     }
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 6470d1670..91dff5979 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -91,6 +91,8 @@  static bool controller_event_en;
 
 static bool check_lsp_is_up;
 
+static bool bfd_en;
+
 /* MAC allocated for service monitor usage. Just one mac is allocated
  * for this purpose and ovn-controller's on each chassis will make use
  * of this mac when sending out the packets to monitor the services
@@ -9267,6 +9269,12 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
+        /* BFD msg handling */
+        if (bfd_en) {
+            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100,
+                          "udp.dst == 3784", "handle_bfd_msg;");
+        }
+
         /* Packets are allowed by default. */
         ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1", "next;");
         ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 0, "1", "next;");
@@ -12330,6 +12338,7 @@  ovnnb_db_run(struct northd_context *ctx,
 
     controller_event_en = smap_get_bool(&nb->options,
                                         "controller_event", false);
+    bfd_en = smap_get_bool(&nb->options, "bfd", false);
     check_lsp_is_up = !smap_get_bool(&nb->options,
                                      "ignore_lsp_down", false);