[ovs-dev] [RFC] bfd: Replace bfd.{c, h} with generic implementation.

Alex Wang alexw at nicira.com
Tue Mar 4 01:46:41 UTC 2014


This commit replaces the current bfd.{c,h} with a generic library-like
implementation.  Wrappers (in bfd_ts.{c,h}) are then added to guarantee
the thread-safety and compatibility with current invocation pattern.

Signed-off-by: Alex Wang <alexw at nicira.com>
---
 build-aux/thread-safety-blacklist |    1 -
 lib/automake.mk                   |    2 +
 lib/bfd.c                         | 1327 ++++++++++---------------------------
 lib/bfd.h                         |  229 ++++++-
 lib/bfd_ts.c                      |  620 +++++++++++++++++
 lib/bfd_ts.h                      |   48 ++
 ofproto/ofproto-dpif-monitor.c    |   16 +-
 ofproto/ofproto-dpif-xlate.c      |   22 +-
 ofproto/ofproto-dpif.c            |   19 +-
 9 files changed, 1241 insertions(+), 1043 deletions(-)
 create mode 100644 lib/bfd_ts.c
 create mode 100644 lib/bfd_ts.h

diff --git a/build-aux/thread-safety-blacklist b/build-aux/thread-safety-blacklist
index 42560df..4db5a84 100644
--- a/build-aux/thread-safety-blacklist
+++ b/build-aux/thread-safety-blacklist
@@ -70,7 +70,6 @@
 \bputchar_unlocked(
 \bputenv(
 \bpututxline(
-\brand(
 \bsetenv(
 \bsetgrent(
 \bsetkey(
diff --git a/lib/automake.mk b/lib/automake.mk
index 9c345e7..b712410 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -18,6 +18,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/backtrace.h \
 	lib/bfd.c \
 	lib/bfd.h \
+	lib/bfd_ts.c \
+	lib/bfd_ts.h \
 	lib/bitmap.c \
 	lib/bitmap.h \
 	lib/bundle.c \
diff --git a/lib/bfd.c b/lib/bfd.c
index 5413105..566214b 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -13,363 +13,200 @@
  * limitations under the License. */
 
 #include <config.h>
+
 #include "bfd.h"
 
-#include <sys/types.h>
 #include <arpa/inet.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-
-#include "byte-order.h"
-#include "connectivity.h"
-#include "csum.h"
-#include "dpif.h"
-#include "dynamic-string.h"
-#include "flow.h"
-#include "hash.h"
-#include "hmap.h"
-#include "list.h"
-#include "netdev.h"
-#include "netlink.h"
-#include "odp-util.h"
-#include "ofpbuf.h"
-#include "ovs-thread.h"
-#include "openvswitch/types.h"
-#include "packets.h"
-#include "poll-loop.h"
-#include "random.h"
-#include "seq.h"
-#include "smap.h"
-#include "timeval.h"
-#include "unaligned.h"
-#include "unixctl.h"
-#include "util.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(bfd);
-
-/* XXX Finish BFD.
- *
- * The goal of this module is to replace CFM with something both more flexible
- * and standards compliant.  In service of this goal, the following needs to be
- * done.
- *
- * - Compliance
- *   * Implement Demand mode.
- *   * Go through the RFC line by line and verify we comply.
- *   * Test against a hardware implementation.  Preferably a popular one.
- *   * Delete BFD packets with nw_ttl != 255 in the datapath to prevent DOS
- *     attacks.
- *
- * - Unit tests.
- *
- * - Set TOS/PCP on the outer tunnel header when encapped.
- *
- * - Sending BFD messages should be in its own thread/process.
- *
- * - Scale testing.  How does it operate when there are large number of bfd
- *   sessions?  Do we ever have random flaps?  What's the CPU utilization?
- *
- * - Rely on data traffic for liveness by using BFD demand mode.
- *   If we're receiving traffic on a port, we can safely assume it's up (modulo
- *   unidrectional failures).  BFD has a demand mode in which it can stay quiet
- *   unless it feels the need to check the status of the port.  Using this, we
- *   can implement a strategy in which BFD only sends control messages on dark
- *   interfaces.
- *
- * - Depending on how one interprets the spec, it appears that a BFD session
- *   can never change bfd.LocalDiag to "No Diagnostic".  We should verify that
- *   this is what hardware implementations actually do.  Seems like "No
- *   Diagnostic" should be set once a BFD session state goes UP. */
-
-#define BFD_VERSION 1
-
-enum flags {
-    FLAG_MULTIPOINT = 1 << 0,
-    FLAG_DEMAND = 1 << 1,
-    FLAG_AUTH = 1 << 2,
-    FLAG_CTL = 1 << 3,
-    FLAG_FINAL = 1 << 4,
-    FLAG_POLL = 1 << 5
-};
-
-enum state {
-    STATE_ADMIN_DOWN = 0 << 6,
-    STATE_DOWN = 1 << 6,
-    STATE_INIT = 2 << 6,
-    STATE_UP = 3 << 6
-};
-
-enum diag {
-    DIAG_NONE = 0,                /* No Diagnostic. */
-    DIAG_EXPIRED = 1,             /* Control Detection Time Expired. */
-    DIAG_ECHO_FAILED = 2,         /* Echo Function Failed. */
-    DIAG_RMT_DOWN = 3,            /* Neighbor Signaled Session Down. */
-    DIAG_FWD_RESET = 4,           /* Forwarding Plane Reset. */
-    DIAG_PATH_DOWN = 5,           /* Path Down. */
-    DIAG_CPATH_DOWN = 6,          /* Concatenated Path Down. */
-    DIAG_ADMIN_DOWN = 7,          /* Administratively Down. */
-    DIAG_RCPATH_DOWN = 8          /* Reverse Concatenated Path Down. */
-};
-
-/* RFC 5880 Section 4.1
- *  0                   1                   2                   3
- *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |Vers |  Diag   |Sta|P|F|C|A|D|M|  Detect Mult  |    Length     |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |                       My Discriminator                        |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |                      Your Discriminator                       |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |                    Desired Min TX Interval                    |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |                   Required Min RX Interval                    |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |                 Required Min Echo RX Interval                 |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
-struct msg {
-    uint8_t vers_diag;    /* Version and diagnostic. */
-    uint8_t flags;        /* 2bit State field followed by flags. */
-    uint8_t mult;         /* Fault detection multiplier. */
-    uint8_t length;       /* Length of this BFD message. */
-    ovs_be32 my_disc;     /* My discriminator. */
-    ovs_be32 your_disc;   /* Your discriminator. */
-    ovs_be32 min_tx;      /* Desired minimum tx interval. */
-    ovs_be32 min_rx;      /* Required minimum rx interval. */
-    ovs_be32 min_rx_echo; /* Required minimum echo rx interval. */
-};
-BUILD_ASSERT_DECL(BFD_PACKET_LEN == sizeof(struct msg));
-
-#define DIAG_MASK 0x1f
-#define VERS_SHIFT 5
-#define STATE_MASK 0xC0
-#define FLAGS_MASK 0x3f
-
-struct bfd {
-    struct hmap_node node;        /* In 'all_bfds'. */
-    uint32_t disc;                /* bfd.LocalDiscr. Key in 'all_bfds' hmap. */
-
-    char *name;                   /* Name used for logging. */
-
-    bool cpath_down;              /* Concatenated Path Down. */
-    uint8_t mult;                 /* bfd.DetectMult. */
-
-    struct netdev *netdev;
-    uint64_t rx_packets;          /* Packets received by 'netdev'. */
-
-    enum state state;             /* bfd.SessionState. */
-    enum state rmt_state;         /* bfd.RemoteSessionState. */
-
-    enum diag diag;               /* bfd.LocalDiag. */
-    enum diag rmt_diag;           /* Remote diagnostic. */
-
-    enum flags flags;             /* Flags sent on messages. */
-    enum flags rmt_flags;         /* Flags last received. */
-
-    uint32_t rmt_disc;            /* bfd.RemoteDiscr. */
-
-    uint8_t eth_dst[ETH_ADDR_LEN];/* Ethernet destination address. */
-    bool eth_dst_set;             /* 'eth_dst' set through database. */
-
-    ovs_be32 ip_src;              /* IPv4 source address. */
-    ovs_be32 ip_dst;              /* IPv4 destination address. */
-
-    uint16_t udp_src;             /* UDP source port. */
-
-    /* All timers in milliseconds. */
-    long long int rmt_min_rx;     /* bfd.RemoteMinRxInterval. */
-    long long int rmt_min_tx;     /* Remote minimum TX interval. */
-
-    long long int cfg_min_tx;     /* Configured minimum TX rate. */
-    long long int cfg_min_rx;     /* Configured required minimum RX rate. */
-    long long int poll_min_tx;    /* Min TX negotating in a poll sequence. */
-    long long int poll_min_rx;    /* Min RX negotating in a poll sequence. */
-    long long int min_tx;         /* bfd.DesiredMinTxInterval. */
-    long long int min_rx;         /* bfd.RequiredMinRxInterval. */
-
-    long long int last_tx;        /* Last TX time. */
-    long long int next_tx;        /* Next TX time. */
-    long long int detect_time;    /* RFC 5880 6.8.4 Detection time. */
-
-    bool last_forwarding;         /* Last calculation of forwarding flag. */
-    int forwarding_override;      /* Manual override of 'forwarding' status. */
-
-    atomic_bool check_tnl_key;    /* Verify tunnel key of inbound packets? */
-    struct ovs_refcount ref_cnt;
-
-    /* When forward_if_rx is true, bfd_forwarding() will return
-     * true as long as there are incoming packets received.
-     * Note, forwarding_override still has higher priority. */
-    bool forwarding_if_rx;
-    long long int forwarding_if_rx_detect_time;
-
-    /* BFD decay related variables. */
-    bool in_decay;                /* True when bfd is in decay. */
-    int decay_min_rx;             /* min_rx is set to decay_min_rx when */
-                                  /* in decay. */
-    int decay_rx_ctl;             /* Count bfd packets received within decay */
-                                  /* detect interval. */
-    uint64_t decay_rx_packets;    /* Packets received by 'netdev'. */
-    long long int decay_detect_time; /* Decay detection time. */
-
-    uint64_t flap_count;          /* Counts bfd forwarding flaps. */
-};
-
-static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
-static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
-static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
-
-static bool bfd_lookup_ip(const char *host_name, struct in_addr *)
-    OVS_REQUIRES(mutex);
-static bool bfd_forwarding__(struct bfd *) OVS_REQUIRES(mutex);
-static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex);
-static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex);
-static const char *bfd_diag_str(enum diag) OVS_REQUIRES(mutex);
-static const char *bfd_state_str(enum state) OVS_REQUIRES(mutex);
-static long long int bfd_min_tx(const struct bfd *) OVS_REQUIRES(mutex);
-static long long int bfd_tx_interval(const struct bfd *)
-    OVS_REQUIRES(mutex);
-static long long int bfd_rx_interval(const struct bfd *)
-    OVS_REQUIRES(mutex);
-static void bfd_set_next_tx(struct bfd *) OVS_REQUIRES(mutex);
-static void bfd_set_state(struct bfd *, enum state, enum diag)
-    OVS_REQUIRES(mutex);
-static uint32_t generate_discriminator(void) OVS_REQUIRES(mutex);
-static void bfd_put_details(struct ds *, const struct bfd *)
-    OVS_REQUIRES(mutex);
-static uint64_t bfd_rx_packets(const struct bfd *) OVS_REQUIRES(mutex);
-static void bfd_try_decay(struct bfd *) OVS_REQUIRES(mutex);
-static void bfd_decay_update(struct bfd *) OVS_REQUIRES(mutex);
-
-static void bfd_forwarding_if_rx_update(struct bfd *) OVS_REQUIRES(mutex);
-static void bfd_unixctl_show(struct unixctl_conn *, int argc,
-                             const char *argv[], void *aux OVS_UNUSED);
-static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
-                                                int argc, const char *argv[],
-                                                void *aux OVS_UNUSED);
-static void log_msg(enum vlog_level, const struct msg *, const char *message,
-                    const struct bfd *) OVS_REQUIRES(mutex);
-
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20);
-
-/* Returns true if the interface on which 'bfd' is running may be used to
- * forward traffic according to the BFD session state. */
-bool
-bfd_forwarding(struct bfd *bfd) OVS_EXCLUDED(mutex)
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+
+static long long int
+bfd_min_tx(const struct bfd *bfd)
 {
-    bool ret;
+    /* RFC 5880 Section 6.8.3
+     * When bfd.SessionState is not Up, the system MUST set
+     * bfd.DesiredMinTxInterval to a value of not less than one second
+     * (1,000,000 microseconds).  This is intended to ensure that the
+     * bandwidth consumed by BFD sessions that are not Up is negligible,
+     * particularly in the case where a neighbor may not be running BFD. */
+    return (bfd->state == STATE_UP ? bfd->min_tx
+            : MAX(bfd->min_tx, 1000));
+}
 
-    ovs_mutex_lock(&mutex);
-    ret = bfd_forwarding__(bfd);
-    ovs_mutex_unlock(&mutex);
-    return ret;
+static long long int
+bfd_tx_interval(const struct bfd *bfd)
+{
+    long long int interval = bfd_min_tx(bfd);
+    return MAX(interval, bfd->rmt_min_rx);
 }
 
-/* When forwarding_if_rx is enabled, if there are packets received,
- * updates forwarding_if_rx_detect_time. */
-void
-bfd_account_rx(struct bfd *bfd, const struct dpif_flow_stats *stats)
+static long long int
+bfd_rx_interval(const struct bfd *bfd)
+{
+    return MAX(bfd->min_rx, bfd->rmt_min_tx);
+}
+
+static void
+bfd_set_next_tx(struct bfd *bfd)
+{
+    long long int interval = bfd_tx_interval(bfd);
+    interval -= interval * (rand() % 26) / 100;
+    bfd->next_tx = bfd->last_tx + interval;
+}
+
+
+static bool
+bfd_forwarding__(const struct bfd *bfd, long long int now)
+{
+    bool should_forward = false;
+
+    if (bfd->forwarding_override) {
+        return bfd->forwarding_override == 1;
+    }
+
+    if (bfd->forward_if_rx_interval) {
+        /* The first time after forward_if_rx is enabled. */
+        if (!bfd->forward_if_rx_detect_time) {
+            should_forward = bfd->state == STATE_UP ? true : false;
+        } else {
+            should_forward = bfd->forward_if_rx_detect_time > now;
+        }
+    }
+
+    return (bfd->state == STATE_UP
+            || (bfd->forward_if_rx_interval && should_forward))
+           && bfd->rmt_diag != DIAG_PATH_DOWN
+           && bfd->rmt_diag != DIAG_CPATH_DOWN
+           && bfd->rmt_diag != DIAG_RCPATH_DOWN;
+}
+
+/* If there is packet received, sets the 'forward_if_rx_detect_time'
+ * to MAX('forward_if_rx_interval', 'rx_int') away from now. */
+static void
+bfd_forward_if_rx(struct bfd *bfd, long long int now)
 {
-    if (stats->n_packets && bfd->forwarding_if_rx) {
-        ovs_mutex_lock(&mutex);
-        bfd_forwarding__(bfd);
-        bfd_forwarding_if_rx_update(bfd);
-        bfd_forwarding__(bfd);
-        ovs_mutex_unlock(&mutex);
+    if (bfd->forward_if_rx_data && bfd->forward_if_rx_interval) {
+        uint32_t rx_int = bfd->mult * bfd_rx_interval(bfd);
+
+        bfd->forward_if_rx_detect_time = MAX(bfd->forward_if_rx_interval,
+                                             rx_int) + now;
+        bfd->forward_if_rx_data = false;
     }
 }
 
-/* Returns a 'smap' of key value pairs representing the status of 'bfd'
- * intended for the OVS database. */
-void
-bfd_get_status(const struct bfd *bfd, struct smap *smap)
-    OVS_EXCLUDED(mutex)
+/* Increments the 'flap_count' if there is a change in the
+ * forwarding flag value. */
+static void
+bfd_check_forwarding_flap(struct bfd *bfd, long long int now)
 {
-    ovs_mutex_lock(&mutex);
-    smap_add(smap, "forwarding",
-             bfd_forwarding__(CONST_CAST(struct bfd *, bfd))
-             ? "true" : "false");
-    smap_add(smap, "state", bfd_state_str(bfd->state));
-    smap_add(smap, "diagnostic", bfd_diag_str(bfd->diag));
-    smap_add_format(smap, "flap_count", "%"PRIu64, bfd->flap_count);
-
-    if (bfd->state != STATE_DOWN) {
-        smap_add(smap, "remote_state", bfd_state_str(bfd->rmt_state));
-        smap_add(smap, "remote_diagnostic", bfd_diag_str(bfd->rmt_diag));
+    bool last_forwarding = bfd->last_forwarding;
+
+    bfd->last_forwarding = bfd_forwarding__(bfd, now);
+    if (bfd->last_forwarding != last_forwarding) {
+        bfd->flap_count++;
     }
-    ovs_mutex_unlock(&mutex);
 }
 
-/* Initializes, destroys, or reconfigures the BFD session 'bfd' (named 'name'),
- * according to the database configuration contained in 'cfg'.  Takes ownership
- * of 'bfd', which may be NULL.  Returns a BFD object which may be used as a
- * handle for the session, or NULL if BFD is not enabled according to 'cfg'.
- * Also returns NULL if cfg is NULL. */
-struct bfd *
-bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
-              struct netdev *netdev) OVS_EXCLUDED(mutex)
+/* Decays the 'bfd->min_rx' to 'bfd->decay_min_rx' when number of packets
+ * received during the 'decay_min_rx' interval is less than two time
+ * number of BFD control packets expected to be received. */
+static void
+bfd_try_decay(struct bfd *bfd, long long int now)
 {
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-    static atomic_uint16_t udp_src = ATOMIC_VAR_INIT(0);
+    if (bfd->state == STATE_UP && bfd->decay_min_rx
+        && now >= bfd->decay_detect_time
+        && bfd->decay_min_rx > bfd->cfg_min_rx) {
+        uint32_t expect_rx = 2 * (bfd->decay_min_rx / bfd->min_rx + 1);
 
-    int decay_min_rx;
-    long long int min_tx, min_rx;
-    bool need_poll = false;
-    bool cfg_min_rx_changed = false;
-    bool cpath_down, forwarding_if_rx;
-    const char *hwaddr, *ip_src, *ip_dst;
-    struct in_addr in_addr;
-    uint8_t ea[ETH_ADDR_LEN];
-
-    if (ovsthread_once_start(&once)) {
-        unixctl_command_register("bfd/show", "[interface]", 0, 1,
-                                 bfd_unixctl_show, NULL);
-        unixctl_command_register("bfd/set-forwarding",
-                                 "[interface] normal|false|true", 1, 2,
-                                 bfd_unixctl_set_forwarding_override, NULL);
-        ovsthread_once_done(&once);
+        bfd->in_decay = bfd->decay_rx_count < expect_rx;
+        bfd->decay_detect_time = bfd->decay_min_rx + now;
+        bfd->decay_rx_count = 0;
     }
+}
 
-    if (!cfg || !smap_get_bool(cfg, "enable", false)) {
-        bfd_unref(bfd);
-        return NULL;
+static void
+bfd_set_state(struct bfd *bfd, enum bfd_state state, enum bfd_diag diag,
+              long long int now)
+{
+    if (bfd->cpath_down) {
+        diag = DIAG_CPATH_DOWN;
     }
 
-    ovs_mutex_lock(&mutex);
-    if (!bfd) {
-        bfd = xzalloc(sizeof *bfd);
-        bfd->name = xstrdup(name);
-        bfd->forwarding_override = -1;
-        bfd->disc = generate_discriminator();
-        hmap_insert(all_bfds, &bfd->node, bfd->disc);
-
-        bfd->diag = DIAG_NONE;
-        bfd->min_tx = 1000;
-        bfd->mult = 3;
-        ovs_refcount_init(&bfd->ref_cnt);
-        bfd->netdev = netdev_ref(netdev);
-        bfd->rx_packets = bfd_rx_packets(bfd);
-        bfd->in_decay = false;
-        bfd->flap_count = 0;
+    if (bfd->state != state || bfd->diag != diag) {
+        bfd->state = state;
+        bfd->diag = diag;
+
+        if (bfd->state <= STATE_DOWN) {
+            bfd->rmt_state = STATE_DOWN;
+            bfd->rmt_diag = DIAG_NONE;
+            bfd->rmt_min_rx = 1;
+            bfd->rmt_flags = 0;
+            bfd->rmt_disc = 0;
+            bfd->rmt_min_tx = 0;
+        }
+
+        if (bfd->state != STATE_UP && bfd->decay_min_rx) {
+            bfd->min_rx = bfd->cfg_min_rx;
+            bfd->in_decay = false;
+            bfd->decay_rx_count = UINT32_MAX;
+        }
+    }
+
+    bfd_check_forwarding_flap(bfd, now);
+}
+
+static bool
+bfd_in_poll(const struct bfd *bfd)
+{
+    return (bfd->flags & FLAG_POLL) != 0;
+}
 
-        /* RFC 5881 section 4
-         * The source port MUST be in the range 49152 through 65535.  The same
-         * UDP source port number MUST be used for all BFD Control packets
-         * associated with a particular session.  The source port number SHOULD
-         * be unique among all BFD sessions on the system. */
-        atomic_add(&udp_src, 1, &bfd->udp_src);
-        bfd->udp_src = (bfd->udp_src % 16384) + 49152;
+static void
+bfd_poll(struct bfd *bfd)
+{
+    if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd)
+        && !(bfd->flags & FLAG_FINAL)) {
+        bfd->poll_min_tx = bfd->cfg_min_tx;
+        bfd->poll_min_rx = bfd->in_decay ? bfd->decay_min_rx : bfd->cfg_min_rx;
+        bfd->flags |= FLAG_POLL;
+        bfd->next_tx = 0;
+    }
+}
 
-        bfd_set_state(bfd, STATE_DOWN, DIAG_NONE);
+
+/* Configures 'bfd' using the 'setting'.  Returns 0 if successful, a positive
+ * error number otherwise. */
+enum bfd_error
+bfd_configure(struct bfd *bfd, const struct bfd_setting *setting)
+{
+    uint32_t min_tx, min_rx;
+    uint8_t mult;
+    bool min_rx_changed = false;
+    bool need_poll = false;
+
+    if (!bfd || !setting) {
+        return BFD_EINVAL;
+    }
 
-        memcpy(bfd->eth_dst, eth_addr_bfd, ETH_ADDR_LEN);
+    if (bfd->state == STATE_ADMIN_DOWN) {
+        bfd_set_state(bfd, STATE_DOWN, DIAG_NONE, 0);
     }
 
-    atomic_store(&bfd->check_tnl_key,
-                 smap_get_bool(cfg, "check_tnl_key", false));
-    min_tx = smap_get_int(cfg, "min_tx", 100);
-    min_tx = MAX(min_tx, 100);
+    if (bfd->disc != setting->disc) {
+        bfd->disc = setting->disc;
+    }
+
+    mult = MAX(setting->mult, 3);
+    if (bfd->mult != mult) {
+        bfd->mult = mult;
+    }
+
+    min_tx = MAX(setting->min_tx, 100);
     if (bfd->cfg_min_tx != min_tx) {
         bfd->cfg_min_tx = min_tx;
         if (bfd->state != STATE_UP
@@ -379,220 +216,187 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
         need_poll = true;
     }
 
-    min_rx = smap_get_int(cfg, "min_rx", 1000);
-    min_rx = MAX(min_rx, 100);
+    min_rx = MAX(setting->min_rx, 100);
     if (bfd->cfg_min_rx != min_rx) {
         bfd->cfg_min_rx = min_rx;
         if (bfd->state != STATE_UP
             || (!bfd_in_poll(bfd) && bfd->cfg_min_rx > bfd->min_rx)) {
             bfd->min_rx = bfd->cfg_min_rx;
         }
-        cfg_min_rx_changed = true;
+        min_rx_changed = true;
         need_poll = true;
     }
 
-    decay_min_rx = smap_get_int(cfg, "decay_min_rx", 0);
-    if (bfd->decay_min_rx != decay_min_rx || cfg_min_rx_changed) {
-        if (decay_min_rx > 0 && decay_min_rx < bfd->cfg_min_rx) {
-            VLOG_WARN("%s: decay_min_rx cannot be less than %lld ms",
-                      bfd->name, bfd->cfg_min_rx);
-            bfd->decay_min_rx = 0;
-        } else {
-            bfd->decay_min_rx = decay_min_rx;
-        }
-        /* Resets decay. */
-        bfd->in_decay = false;
-        bfd_decay_update(bfd);
-        need_poll = true;
-    }
-
-    cpath_down = smap_get_bool(cfg, "cpath_down", false);
-    if (bfd->cpath_down != cpath_down) {
-        bfd->cpath_down = cpath_down;
-        bfd_set_state(bfd, bfd->state, DIAG_NONE);
+    if (bfd->cpath_down != setting->cpath_down) {
+        bfd->cpath_down = setting->cpath_down;
+        bfd_set_state(bfd, bfd->state, DIAG_NONE, LLONG_MAX);
         need_poll = true;
     }
 
-    hwaddr = smap_get(cfg, "bfd_dst_mac");
-    if (hwaddr && eth_addr_from_string(hwaddr, ea) && !eth_addr_is_zero(ea)) {
-        memcpy(bfd->eth_dst, ea, ETH_ADDR_LEN);
-        bfd->eth_dst_set = true;
-    } else if (bfd->eth_dst_set) {
-        memcpy(bfd->eth_dst, eth_addr_bfd, ETH_ADDR_LEN);
-        bfd->eth_dst_set = false;
+    if (bfd->forwarding_override != setting->forwarding_override) {
+        bfd->forwarding_override = setting->forwarding_override;
     }
 
-    ip_src = smap_get(cfg, "bfd_src_ip");
-    if (ip_src && bfd_lookup_ip(ip_src, &in_addr)) {
-        memcpy(&bfd->ip_src, &in_addr, sizeof in_addr);
-    } else {
-        bfd->ip_src = htonl(0xA9FE0100); /* 169.254.1.0. */
+    if (bfd->forward_if_rx_interval != setting->forward_if_rx_interval) {
+        bfd->forward_if_rx_interval = setting->forward_if_rx_interval;
+        bfd->forward_if_rx_detect_time = 0;
     }
 
-    ip_dst = smap_get(cfg, "bfd_dst_ip");
-    if (ip_dst && bfd_lookup_ip(ip_dst, &in_addr)) {
-        memcpy(&bfd->ip_dst, &in_addr, sizeof in_addr);
-    } else {
-        bfd->ip_dst = htonl(0xA9FE0101); /* 169.254.1.1. */
-    }
-
-    forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false);
-    if (bfd->forwarding_if_rx != forwarding_if_rx) {
-        bfd->forwarding_if_rx = forwarding_if_rx;
-        if (bfd->state == STATE_UP && bfd->forwarding_if_rx) {
-            bfd_forwarding_if_rx_update(bfd);
-        } else {
-            bfd->forwarding_if_rx_detect_time = 0;
-        }
+    if (bfd->decay_min_rx != setting->decay_min_rx || min_rx_changed) {
+        bfd->decay_min_rx = setting->decay_min_rx;
+        bfd->in_decay = false;
+        bfd->decay_rx_count = UINT32_MAX;
+        bfd->decay_detect_time = 0;
+        need_poll = true;
     }
 
     if (need_poll) {
         bfd_poll(bfd);
     }
-    ovs_mutex_unlock(&mutex);
-    return bfd;
-}
-
-struct bfd *
-bfd_ref(const struct bfd *bfd_)
-{
-    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
-    if (bfd) {
-        ovs_refcount_ref(&bfd->ref_cnt);
-    }
-    return bfd;
-}
-
-void
-bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex)
-{
-    if (bfd && ovs_refcount_unref(&bfd->ref_cnt) == 1) {
-        ovs_mutex_lock(&mutex);
-        hmap_remove(all_bfds, &bfd->node);
-        netdev_close(bfd->netdev);
-        ovs_refcount_destroy(&bfd->ref_cnt);
-        free(bfd->name);
-        free(bfd);
-        ovs_mutex_unlock(&mutex);
-    }
-}
 
-void
-bfd_wait(const struct bfd *bfd) OVS_EXCLUDED(mutex)
-{
-    poll_timer_wait_until(bfd_wake_time(bfd));
+    return BFD_PASS;
 }
 
-/* Returns the next wake up time. */
+/* Returns the wakeup time of the BFD session. */
 long long int
-bfd_wake_time(const struct bfd *bfd) OVS_EXCLUDED(mutex)
+bfd_wait(const struct bfd *bfd)
 {
-    long long int retval;
+    long long int ret;
 
     if (!bfd) {
         return LLONG_MAX;
     }
 
-    ovs_mutex_lock(&mutex);
     if (bfd->flags & FLAG_FINAL) {
-        retval = 0;
+        ret = 0;
     } else {
-        retval = bfd->next_tx;
+        ret = bfd->next_tx;
         if (bfd->state > STATE_DOWN) {
-            retval = MIN(bfd->detect_time, retval);
+            ret = MIN(bfd->detect_time, ret);
+        }
+        if (bfd->state == STATE_UP && bfd->decay_min_rx) {
+            ret = MIN(bfd->decay_detect_time, ret);
         }
     }
-    ovs_mutex_unlock(&mutex);
-    return retval;
+
+    return ret;
 }
 
+/* Updates the BFD sessions status.  Checks decay and forward_if_rx.
+ * Initiates the POLL sequence if needed. */
 void
-bfd_run(struct bfd *bfd) OVS_EXCLUDED(mutex)
+bfd_run(struct bfd *bfd, long long int now)
 {
-    long long int now;
-    bool old_in_decay;
+    bool old;
 
-    ovs_mutex_lock(&mutex);
-    now = time_msec();
-    old_in_decay = bfd->in_decay;
+    if (!bfd) {
+        return;
+    }
 
     if (bfd->state > STATE_DOWN && now >= bfd->detect_time) {
-        bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED);
+        bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED, now);
     }
-    bfd_forwarding__(bfd);
 
-    /* Decay may only happen when state is STATE_UP, bfd->decay_min_rx is
-     * configured, and decay_detect_time is reached. */
-    if (bfd->state == STATE_UP && bfd->decay_min_rx > 0
-        && now >= bfd->decay_detect_time) {
-        bfd_try_decay(bfd);
-    }
+    old = bfd->in_decay;
+    bfd_try_decay(bfd, now);
+
+    bfd_forward_if_rx(bfd, now);
+    bfd_check_forwarding_flap(bfd, now);
 
     if (bfd->min_tx != bfd->cfg_min_tx
-        || (bfd->min_rx != bfd->cfg_min_rx && bfd->min_rx != bfd->decay_min_rx)
-        || bfd->in_decay != old_in_decay) {
+        || (!bfd->in_decay && bfd->min_rx != bfd->cfg_min_rx)
+        || (bfd->in_decay && bfd->min_rx != bfd->decay_min_rx)
+        || bfd->in_decay != old) {
         bfd_poll(bfd);
     }
-    ovs_mutex_unlock(&mutex);
 }
 
+/* Queries the 'bfd''s status, the function will fill in the
+ * 'struct bfd_status'. */
+void
+bfd_get_status(const struct bfd *bfd, struct bfd_status *s)
+{
+    if (!bfd || !s) {
+        return;
+    }
+
+    s->forwarding = bfd->last_forwarding;
+    s->mult = bfd->mult;
+    s->cpath_down = bfd->cpath_down;
+    s->tx_interval = bfd_tx_interval(bfd);
+    s->rx_interval = bfd_rx_interval(bfd);
+
+    s->local_disc = bfd->disc;
+    s->local_min_tx = bfd_min_tx(bfd);
+    s->local_min_rx = bfd->min_rx;
+    s->local_flags = bfd->flags;
+    s->local_state = bfd->state;
+    s->local_diag = bfd->diag;
+    s->detect_time = bfd->detect_time;
+    s->next_tx = bfd->next_tx;
+    s->last_tx = bfd->last_tx;
+
+    s->rmt_disc = bfd->rmt_disc;
+    s->rmt_min_tx = bfd->rmt_min_tx;
+    s->rmt_min_rx = bfd->rmt_min_rx;
+    s->rmt_flags = bfd->rmt_flags;
+    s->rmt_state = bfd->rmt_state;
+    s->rmt_diag = bfd->rmt_diag;
+
+    s->flap_count = bfd->flap_count;
+}
+
+/* Returns true if the interface on which BFD is running may be used to
+ * forward traffic according to the BFD session state.  'now' is the
+ * current time in milliseconds. */
 bool
-bfd_should_send_packet(const struct bfd *bfd) OVS_EXCLUDED(mutex)
+bfd_forwarding(const struct bfd *bfd, long long int now)
 {
-    bool ret;
-    ovs_mutex_lock(&mutex);
-    ret = bfd->flags & FLAG_FINAL || time_msec() >= bfd->next_tx;
-    ovs_mutex_unlock(&mutex);
-    return ret;
+    return bfd_forwarding__(bfd, now);
 }
 
+/* Sets the corresponding flags to indicate that packet
+ * is received from this monitored interface. */
 void
-bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
-               uint8_t eth_src[ETH_ADDR_LEN]) OVS_EXCLUDED(mutex)
+bfd_account_rx(struct bfd *bfd, uint32_t n_pkt)
+{
+    if (bfd->forward_if_rx_interval && n_pkt) {
+        bfd->forward_if_rx_data = true;
+    }
+
+    if (bfd->decay_min_rx && bfd->decay_rx_count != UINT32_MAX) {
+        bfd->decay_rx_count += n_pkt;
+    }
+}
+
+/* For send/recv BFD control packets. */
+/* Returns true if the BFD control packet should be sent for this BFD
+ * session.  e.g. tx timeout or POLL flag is on. */
+bool
+bfd_should_send_packet(const struct bfd *bfd, long long int now)
+{
+    return bfd->flags & FLAG_FINAL || now >= bfd->next_tx;
+}
+
+/* Constructs the BFD control packet in payload.  This function assumes that
+ * the payload is properly aligned. */
+enum bfd_error
+bfd_put_packet(struct bfd *bfd, void *p, size_t len, long long int now)
 {
     long long int min_tx, min_rx;
-    struct udp_header *udp;
-    struct eth_header *eth;
-    struct ip_header *ip;
-    struct msg *msg;
-
-    ovs_mutex_lock(&mutex);
-    if (bfd->next_tx) {
-        long long int delay = time_msec() - bfd->next_tx;
-        long long int interval = bfd_tx_interval(bfd);
-        if (delay > interval * 3 / 2) {
-            VLOG_INFO("%s: long delay of %lldms (expected %lldms) sending BFD"
-                      " control message", bfd->name, delay, interval);
-        }
+    struct bfd_msg *msg = p;
+
+    if (!bfd || !p || len < BFD_PACKET_LEN) {
+        return BFD_EINVAL;
     }
 
     /* RFC 5880 Section 6.5
      * A BFD Control packet MUST NOT have both the Poll (P) and Final (F) bits
      * set. */
-    ovs_assert(!(bfd->flags & FLAG_POLL) || !(bfd->flags & FLAG_FINAL));
-
-    ofpbuf_reserve(p, 2); /* Properly align after the ethernet header. */
-    eth = ofpbuf_put_uninit(p, sizeof *eth);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
-    memcpy(eth->eth_dst, bfd->eth_dst, ETH_ADDR_LEN);
-    eth->eth_type = htons(ETH_TYPE_IP);
-
-    ip = ofpbuf_put_zeros(p, sizeof *ip);
-    ip->ip_ihl_ver = IP_IHL_VER(5, 4);
-    ip->ip_tot_len = htons(sizeof *ip + sizeof *udp + sizeof *msg);
-    ip->ip_ttl = MAXTTL;
-    ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
-    ip->ip_proto = IPPROTO_UDP;
-    put_16aligned_be32(&ip->ip_src, bfd->ip_src);
-    put_16aligned_be32(&ip->ip_dst, bfd->ip_dst);
-    ip->ip_csum = csum(ip, sizeof *ip);
-
-    udp = ofpbuf_put_zeros(p, sizeof *udp);
-    udp->udp_src = htons(bfd->udp_src);
-    udp->udp_dst = htons(BFD_DEST_PORT);
-    udp->udp_len = htons(sizeof *udp + sizeof *msg);
-
-    msg = ofpbuf_put_uninit(p, sizeof *msg);
+    if ((bfd->flags & FLAG_POLL) && (bfd->flags & FLAG_FINAL)) {
+        return BFD_EPOLL;
+    }
+
     msg->vers_diag = (BFD_VERSION << 5) | bfd->diag;
     msg->flags = (bfd->state & STATE_MASK) | bfd->flags;
 
@@ -615,69 +419,39 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
 
     bfd->flags &= ~FLAG_FINAL;
 
-    log_msg(VLL_DBG, msg, "Sending BFD Message", bfd);
-
-    bfd->last_tx = time_msec();
+    bfd->last_tx = now;
     bfd_set_next_tx(bfd);
-    ovs_mutex_unlock(&mutex);
+
+    return BFD_PASS;
 }
 
+/* Given the packet header entries, check if the packet is BFD control
+ * packet. */
 bool
-bfd_should_process_flow(const struct bfd *bfd_, const struct flow *flow,
-                        struct flow_wildcards *wc)
+bfd_should_process_packet(const __be16 eth_type, const uint8_t ip_proto,
+                          const __be16 udp_dst)
 {
-    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
-    bool check_tnl_key;
-
-    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
-    if (bfd->eth_dst_set && memcmp(bfd->eth_dst, flow->dl_dst, ETH_ADDR_LEN)) {
-        return false;
-    }
-
-    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
-    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
-
-    atomic_read(&bfd->check_tnl_key, &check_tnl_key);
-    if (check_tnl_key) {
-        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
-    }
-    return (flow->dl_type == htons(ETH_TYPE_IP)
-            && flow->nw_proto == IPPROTO_UDP
-            && flow->tp_dst == htons(BFD_DEST_PORT)
-            && (!check_tnl_key || flow->tunnel.tun_id == htonll(0)));
+    return (eth_type == htons(0x0800) /* IP. */
+            && ip_proto == 17         /* UDP. */
+            && udp_dst == htons(BFD_DEST_PORT));
 }
 
-void
-bfd_process_packet(struct bfd *bfd, const struct flow *flow,
-                   const struct ofpbuf *p) OVS_EXCLUDED(mutex)
+/* Processes the BFD control packet in payload 'p'.  The payload length is
+ * provided. */
+enum bfd_error
+bfd_process_packet(struct bfd *bfd, void *p, size_t len, long long int now)
 {
     uint32_t rmt_min_rx, pkt_your_disc;
-    enum state rmt_state;
-    enum flags flags;
+    enum bfd_state rmt_state;
+    enum bfd_flags flags;
     uint8_t version;
-    struct msg *msg;
+    struct bfd_msg *msg = p;
 
-    /* This function is designed to follow section RFC 5880 6.8.6 closely. */
-
-    ovs_mutex_lock(&mutex);
-    /* Increments the decay rx counter. */
-    bfd->decay_rx_ctl++;
-
-    bfd_forwarding__(bfd);
-
-    if (flow->nw_ttl != 255) {
-        /* XXX Should drop in the kernel to prevent DOS. */
-        goto out;
+    if (!bfd || !p || len < BFD_PACKET_LEN) {
+        return BFD_EINVAL;
     }
 
-    msg = ofpbuf_at(p, (uint8_t *)p->l7 - (uint8_t *)p->data, BFD_PACKET_LEN);
-    if (!msg) {
-        VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
-                     "%"PRIdPTR" bytes long, at least %d required).",
-                     bfd->name, (uint8_t *) ofpbuf_tail(p) - (uint8_t *) p->l7,
-                     BFD_PACKET_LEN);
-        goto out;
-    }
+    /* This function is designed to follow section RFC 5880 6.8.6 closely. */
 
     /* RFC 5880 Section 6.8.6
      * If the Length field is greater than the payload of the encapsulating
@@ -692,61 +466,46 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
     rmt_state = msg->flags & STATE_MASK;
     version = msg->vers_diag >> VERS_SHIFT;
 
-    log_msg(VLL_DBG, msg, "Received BFD control message", bfd);
-
     if (version != BFD_VERSION) {
-        log_msg(VLL_WARN, msg, "Incorrect version", bfd);
-        goto out;
+        goto err;
     }
 
     /* Technically this should happen after the length check. We don't support
      * authentication however, so it's simpler to do the check first. */
     if (flags & FLAG_AUTH) {
-        log_msg(VLL_WARN, msg, "Authenticated control message with"
-                   " authentication disabled", bfd);
-        goto out;
+        goto err;
     }
 
     if (msg->length != BFD_PACKET_LEN) {
-        log_msg(VLL_WARN, msg, "Unexpected length", bfd);
         if (msg->length < BFD_PACKET_LEN) {
-            goto out;
+            goto err;
         }
     }
 
     if (!msg->mult) {
-        log_msg(VLL_WARN, msg, "Zero multiplier", bfd);
-        goto out;
+        goto err;
     }
 
     if (flags & FLAG_MULTIPOINT) {
-        log_msg(VLL_WARN, msg, "Unsupported multipoint flag", bfd);
-        goto out;
+        goto err;
     }
 
     if (!msg->my_disc) {
-        log_msg(VLL_WARN, msg, "NULL my_disc", bfd);
-        goto out;
+        goto err;
     }
 
     pkt_your_disc = ntohl(msg->your_disc);
     if (pkt_your_disc) {
         /* Technically, we should use the your discriminator field to figure
          * out which 'struct bfd' this packet is destined towards.  That way a
-         * bfd session could migrate from one interface to another
+         * BFD session could migrate from one interface to another
          * transparently.  This doesn't fit in with the OVS structure very
          * well, so in this respect, we are not compliant. */
        if (pkt_your_disc != bfd->disc) {
-           log_msg(VLL_WARN, msg, "Incorrect your_disc", bfd);
-           goto out;
+           goto err;
        }
     } else if (rmt_state > STATE_DOWN) {
-        log_msg(VLL_WARN, msg, "Null your_disc", bfd);
-        goto out;
-    }
-
-    if (bfd->rmt_state != rmt_state) {
-        seq_change(connectivity_seq_get());
+        goto err;
     }
 
     bfd->rmt_disc = ntohl(msg->my_disc);
@@ -758,7 +517,6 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
         bfd->min_tx = bfd->poll_min_tx;
         bfd->min_rx = bfd->poll_min_rx;
         bfd->flags &= ~FLAG_POLL;
-        log_msg(VLL_INFO, msg, "Poll sequence terminated", bfd);
     }
 
     if (flags & FLAG_POLL) {
@@ -777,209 +535,107 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
         if (bfd->next_tx) {
             bfd_set_next_tx(bfd);
         }
-        log_msg(VLL_INFO, msg, "New remote min_rx", bfd);
     }
 
     bfd->rmt_min_tx = MAX(ntohl(msg->min_tx) / 1000, 1);
-    bfd->detect_time = bfd_rx_interval(bfd) * bfd->mult + time_msec();
+    bfd->detect_time = bfd_rx_interval(bfd) * bfd->mult + now;
 
     if (bfd->state == STATE_ADMIN_DOWN) {
-        VLOG_DBG_RL(&rl, "Administratively down, dropping control message.");
         goto out;
     }
 
     if (rmt_state == STATE_ADMIN_DOWN) {
         if (bfd->state != STATE_DOWN) {
-            bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN);
+            bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN, now);
         }
     } else {
         switch (bfd->state) {
         case STATE_DOWN:
             if (rmt_state == STATE_DOWN) {
-                bfd_set_state(bfd, STATE_INIT, bfd->diag);
+                bfd_set_state(bfd, STATE_INIT, bfd->diag, now);
             } else if (rmt_state == STATE_INIT) {
-                bfd_set_state(bfd, STATE_UP, bfd->diag);
+                bfd_set_state(bfd, STATE_UP, bfd->diag, now);
             }
             break;
         case STATE_INIT:
             if (rmt_state > STATE_DOWN) {
-                bfd_set_state(bfd, STATE_UP, bfd->diag);
+                bfd_set_state(bfd, STATE_UP, bfd->diag, now);
             }
             break;
         case STATE_UP:
             if (rmt_state <= STATE_DOWN) {
-                bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN);
-                log_msg(VLL_INFO, msg, "Remote signaled STATE_DOWN", bfd);
+                bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN, now);
             }
             break;
         case STATE_ADMIN_DOWN:
         default:
-            OVS_NOT_REACHED();
+            break;
         }
     }
-    /* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */
 
 out:
-    bfd_forwarding__(bfd);
-    ovs_mutex_unlock(&mutex);
-}
+    return BFD_PASS;
 
-/* Must be called when the netdev owned by 'bfd' should change. */
-void
-bfd_set_netdev(struct bfd *bfd, const struct netdev *netdev)
-    OVS_EXCLUDED(mutex)
-{
-    ovs_mutex_lock(&mutex);
-    if (bfd->netdev != netdev) {
-        netdev_close(bfd->netdev);
-        bfd->netdev = netdev_ref(netdev);
-        if (bfd->decay_min_rx && bfd->state == STATE_UP) {
-            bfd_decay_update(bfd);
-        }
-        if (bfd->forwarding_if_rx && bfd->state == STATE_UP) {
-            bfd_forwarding_if_rx_update(bfd);
-        }
-        bfd->rx_packets = bfd_rx_packets(bfd);
-    }
-    ovs_mutex_unlock(&mutex);
-}
-
-
-/* Updates the forwarding flag.  If override is not configured and
- * the forwarding flag value changes, increments the flap count.
- *
- * Note this function may be called multiple times in a function
- * (e.g. bfd_account_rx) before and after the bfd state or status
- * change.  This is to capture any forwarding flag flap. */
-static bool
-bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    long long int time;
-    bool last_forwarding = bfd->last_forwarding;
-
-    if (bfd->forwarding_override != -1) {
-        return bfd->forwarding_override == 1;
-    }
-
-    time = bfd->forwarding_if_rx_detect_time;
-    bfd->last_forwarding = (bfd->state == STATE_UP
-                            || (bfd->forwarding_if_rx && time > time_msec()))
-                            && bfd->rmt_diag != DIAG_PATH_DOWN
-                            && bfd->rmt_diag != DIAG_CPATH_DOWN
-                            && bfd->rmt_diag != DIAG_RCPATH_DOWN;
-    if (bfd->last_forwarding != last_forwarding) {
-        bfd->flap_count++;
-        seq_change(connectivity_seq_get());
-    }
-    return bfd->last_forwarding;
+err:
+    return BFD_EMSG;
 }
 
 /* Helpers. */
-static bool
-bfd_lookup_ip(const char *host_name, struct in_addr *addr)
-{
-    if (!inet_pton(AF_INET, host_name, addr)) {
-        VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
-        return false;
-    }
-    return true;
-}
-
-static bool
-bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    return (bfd->flags & FLAG_POLL) != 0;
-}
-
-static void
-bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex)
+/* Converts the BFD error code to string. */
+const char *
+bfd_error_to_str(enum bfd_error error)
 {
-    if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd)
-        && !(bfd->flags & FLAG_FINAL)) {
-        bfd->poll_min_tx = bfd->cfg_min_tx;
-        bfd->poll_min_rx = bfd->in_decay ? bfd->decay_min_rx : bfd->cfg_min_rx;
-        bfd->flags |= FLAG_POLL;
-        bfd->next_tx = 0;
-        VLOG_INFO_RL(&rl, "%s: Initiating poll sequence", bfd->name);
+    switch (error) {
+    case BFD_PASS: return "No Error";
+    case BFD_EINVAL: return "Invalid Arguments";
+    case BFD_EPOLL: return "Both POLL And FINAL Set";
+    case BFD_EMSG: return "Bad Control Packet";
+    default: return "Not An Error Code";
     }
 }
 
-static long long int
-bfd_min_tx(const struct bfd *bfd) OVS_REQUIRES(mutex)
+/* Converts the BFD flags to string. */
+const char *
+bfd_flag_to_str(enum bfd_flags flags)
 {
-    /* RFC 5880 Section 6.8.3
-     * When bfd.SessionState is not Up, the system MUST set
-     * bfd.DesiredMinTxInterval to a value of not less than one second
-     * (1,000,000 microseconds).  This is intended to ensure that the
-     * bandwidth consumed by BFD sessions that are not Up is negligible,
-     * particularly in the case where a neighbor may not be running BFD. */
-    return (bfd->state == STATE_UP ? bfd->min_tx : MAX(bfd->min_tx, 1000));
-}
-
-static long long int
-bfd_tx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    long long int interval = bfd_min_tx(bfd);
-    return MAX(interval, bfd->rmt_min_rx);
-}
-
-static long long int
-bfd_rx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    return MAX(bfd->min_rx, bfd->rmt_min_tx);
-}
-
-static void
-bfd_set_next_tx(struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    long long int interval = bfd_tx_interval(bfd);
-    interval -= interval * random_range(26) / 100;
-    bfd->next_tx = bfd->last_tx + interval;
-}
-
-static const char *
-bfd_flag_str(enum flags flags)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
     static char flag_str[128];
 
     if (!flags) {
         return "none";
     }
 
+    memset(flag_str, 0, sizeof *flag_str);
+
     if (flags & FLAG_MULTIPOINT) {
-        ds_put_cstr(&ds, "multipoint ");
+        strcat(flag_str, "multipoint ");
     }
 
     if (flags & FLAG_DEMAND) {
-        ds_put_cstr(&ds, "demand ");
+        strcat(flag_str, "demand ");
     }
 
     if (flags & FLAG_AUTH) {
-        ds_put_cstr(&ds, "auth ");
+        strcat(flag_str, "auth");
     }
 
     if (flags & FLAG_CTL) {
-        ds_put_cstr(&ds, "ctl ");
+        strcat(flag_str, "ctl");
     }
 
     if (flags & FLAG_FINAL) {
-        ds_put_cstr(&ds, "final ");
+        strcat(flag_str, "final");
     }
 
     if (flags & FLAG_POLL) {
-        ds_put_cstr(&ds, "poll ");
+        strcat(flag_str, "poll");
     }
 
-    /* Do not copy the trailing whitespace. */
-    ds_chomp(&ds, ' ');
-    ovs_strlcpy(flag_str, ds_cstr(&ds), sizeof flag_str);
-    ds_destroy(&ds);
     return flag_str;
 }
 
-static const char *
-bfd_state_str(enum state state)
+/* Converts the BFD state code to string. */
+const char *
+bfd_state_to_str(enum bfd_state state)
 {
     switch (state) {
     case STATE_ADMIN_DOWN: return "admin_down";
@@ -990,8 +646,9 @@ bfd_state_str(enum state state)
     }
 }
 
-static const char *
-bfd_diag_str(enum diag diag) {
+/* Converts the BFD diag to string. */
+const char *
+bfd_diag_to_str(enum bfd_diag diag) {
     switch (diag) {
     case DIAG_NONE: return "No Diagnostic";
     case DIAG_EXPIRED: return "Control Detection Time Expired";
@@ -1004,286 +661,4 @@ bfd_diag_str(enum diag diag) {
     case DIAG_RCPATH_DOWN: return "Reverse Concatenated Path Down";
     default: return "Invalid Diagnostic";
     }
-};
-
-static void
-log_msg(enum vlog_level level, const struct msg *p, const char *message,
-        const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
-
-    if (vlog_should_drop(THIS_MODULE, level, &rl)) {
-        return;
-    }
-
-    ds_put_format(&ds,
-                  "%s: %s."
-                  "\n\tvers:%"PRIu8" diag:\"%s\" state:%s mult:%"PRIu8
-                  " length:%"PRIu8
-                  "\n\tflags: %s"
-                  "\n\tmy_disc:0x%"PRIx32" your_disc:0x%"PRIx32
-                  "\n\tmin_tx:%"PRIu32"us (%"PRIu32"ms)"
-                  "\n\tmin_rx:%"PRIu32"us (%"PRIu32"ms)"
-                  "\n\tmin_rx_echo:%"PRIu32"us (%"PRIu32"ms)",
-                  bfd->name, message, p->vers_diag >> VERS_SHIFT,
-                  bfd_diag_str(p->vers_diag & DIAG_MASK),
-                  bfd_state_str(p->flags & STATE_MASK),
-                  p->mult, p->length, bfd_flag_str(p->flags & FLAGS_MASK),
-                  ntohl(p->my_disc), ntohl(p->your_disc),
-                  ntohl(p->min_tx), ntohl(p->min_tx) / 1000,
-                  ntohl(p->min_rx), ntohl(p->min_rx) / 1000,
-                  ntohl(p->min_rx_echo), ntohl(p->min_rx_echo) / 1000);
-    bfd_put_details(&ds, bfd);
-    VLOG(level, "%s", ds_cstr(&ds));
-    ds_destroy(&ds);
-}
-
-static void
-bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
-    OVS_REQUIRES(mutex)
-{
-    if (bfd->cpath_down) {
-        diag = DIAG_CPATH_DOWN;
-    }
-
-    if (bfd->state != state || bfd->diag != diag) {
-        if (!VLOG_DROP_INFO(&rl)) {
-            struct ds ds = DS_EMPTY_INITIALIZER;
-
-            ds_put_format(&ds, "%s: BFD state change: %s->%s"
-                          " \"%s\"->\"%s\".\n",
-                          bfd->name, bfd_state_str(bfd->state),
-                          bfd_state_str(state), bfd_diag_str(bfd->diag),
-                          bfd_diag_str(diag));
-            bfd_put_details(&ds, bfd);
-            VLOG_INFO("%s", ds_cstr(&ds));
-            ds_destroy(&ds);
-        }
-
-        bfd->state = state;
-        bfd->diag = diag;
-
-        if (bfd->state <= STATE_DOWN) {
-            bfd->rmt_state = STATE_DOWN;
-            bfd->rmt_diag = DIAG_NONE;
-            bfd->rmt_min_rx = 1;
-            bfd->rmt_flags = 0;
-            bfd->rmt_disc = 0;
-            bfd->rmt_min_tx = 0;
-            /* Resets the min_rx if in_decay. */
-            if (bfd->in_decay) {
-                bfd->min_rx = bfd->cfg_min_rx;
-                bfd->in_decay = false;
-            }
-        }
-        /* Resets the decay when state changes to STATE_UP
-         * and decay_min_rx is configured. */
-        if (bfd->state == STATE_UP && bfd->decay_min_rx) {
-            bfd_decay_update(bfd);
-        }
-
-        seq_change(connectivity_seq_get());
-    }
-}
-
-static uint64_t
-bfd_rx_packets(const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    struct netdev_stats stats;
-
-    if (!netdev_get_stats(bfd->netdev, &stats)) {
-        return stats.rx_packets;
-    } else {
-        return 0;
-    }
-}
-
-/* Decays the bfd->min_rx to bfd->decay_min_rx when 'diff' is less than
- * the 'expect' value. */
-static void
-bfd_try_decay(struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    int64_t diff, expect;
-
-    /* The 'diff' is the difference between current interface rx_packets
-     * stats and last-time check.  The 'expect' is the recorded number of
-     * bfd control packets received within an approximately decay_min_rx
-     * (2000 ms if decay_min_rx is less than 2000 ms) interval.
-     *
-     * Since the update of rx_packets stats at interface happens
-     * asynchronously to the bfd_rx_packets() function, the 'diff' value
-     * can be jittered.  Thusly, we double the decay_rx_ctl to provide
-     * more wiggle room. */
-    diff = bfd_rx_packets(bfd) - bfd->decay_rx_packets;
-    expect = 2 * MAX(bfd->decay_rx_ctl, 1);
-    bfd->in_decay = diff <= expect ? true : false;
-    bfd_decay_update(bfd);
-}
-
-/* Updates the rx_packets, decay_rx_ctl and decay_detect_time. */
-static void
-bfd_decay_update(struct bfd * bfd) OVS_REQUIRES(mutex)
-{
-    bfd->decay_rx_packets = bfd_rx_packets(bfd);
-    bfd->decay_rx_ctl = 0;
-    bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec();
-}
-
-static void
-bfd_forwarding_if_rx_update(struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    int64_t incr = bfd_rx_interval(bfd) * bfd->mult;
-    bfd->forwarding_if_rx_detect_time = MAX(incr, 2000) + time_msec();
-}
-
-static uint32_t
-generate_discriminator(void)
-{
-    uint32_t disc = 0;
-
-    /* RFC 5880 Section 6.8.1
-     * It SHOULD be set to a random (but still unique) value to improve
-     * security.  The value is otherwise outside the scope of this
-     * specification. */
-
-    while (!disc) {
-        struct bfd *bfd;
-
-        /* 'disc' is by definition random, so there's no reason to waste time
-         * hashing it. */
-        disc = random_uint32();
-        HMAP_FOR_EACH_IN_BUCKET (bfd, node, disc, all_bfds) {
-            if (bfd->disc == disc) {
-                disc = 0;
-                break;
-            }
-        }
-    }
-
-    return disc;
-}
-
-static struct bfd *
-bfd_find_by_name(const char *name) OVS_REQUIRES(mutex)
-{
-    struct bfd *bfd;
-
-    HMAP_FOR_EACH (bfd, node, all_bfds) {
-        if (!strcmp(bfd->name, name)) {
-            return bfd;
-        }
-    }
-    return NULL;
-}
-
-static void
-bfd_put_details(struct ds *ds, const struct bfd *bfd) OVS_REQUIRES(mutex)
-{
-    ds_put_format(ds, "\tForwarding: %s\n",
-                  bfd_forwarding__(CONST_CAST(struct bfd *, bfd))
-                  ? "true" : "false");
-    ds_put_format(ds, "\tDetect Multiplier: %d\n", bfd->mult);
-    ds_put_format(ds, "\tConcatenated Path Down: %s\n",
-                  bfd->cpath_down ? "true" : "false");
-    ds_put_format(ds, "\tTX Interval: Approx %lldms\n", bfd_tx_interval(bfd));
-    ds_put_format(ds, "\tRX Interval: Approx %lldms\n", bfd_rx_interval(bfd));
-    ds_put_format(ds, "\tDetect Time: now %+lldms\n",
-                  time_msec() - bfd->detect_time);
-    ds_put_format(ds, "\tNext TX Time: now %+lldms\n",
-                  time_msec() - bfd->next_tx);
-    ds_put_format(ds, "\tLast TX Time: now %+lldms\n",
-                  time_msec() - bfd->last_tx);
-
-    ds_put_cstr(ds, "\n");
-
-    ds_put_format(ds, "\tLocal Flags: %s\n", bfd_flag_str(bfd->flags));
-    ds_put_format(ds, "\tLocal Session State: %s\n",
-                  bfd_state_str(bfd->state));
-    ds_put_format(ds, "\tLocal Diagnostic: %s\n", bfd_diag_str(bfd->diag));
-    ds_put_format(ds, "\tLocal Discriminator: 0x%"PRIx32"\n", bfd->disc);
-    ds_put_format(ds, "\tLocal Minimum TX Interval: %lldms\n",
-                  bfd_min_tx(bfd));
-    ds_put_format(ds, "\tLocal Minimum RX Interval: %lldms\n", bfd->min_rx);
-
-    ds_put_cstr(ds, "\n");
-
-    ds_put_format(ds, "\tRemote Flags: %s\n", bfd_flag_str(bfd->rmt_flags));
-    ds_put_format(ds, "\tRemote Session State: %s\n",
-                  bfd_state_str(bfd->rmt_state));
-    ds_put_format(ds, "\tRemote Diagnostic: %s\n",
-                  bfd_diag_str(bfd->rmt_diag));
-    ds_put_format(ds, "\tRemote Discriminator: 0x%"PRIx32"\n", bfd->rmt_disc);
-    ds_put_format(ds, "\tRemote Minimum TX Interval: %lldms\n",
-                  bfd->rmt_min_tx);
-    ds_put_format(ds, "\tRemote Minimum RX Interval: %lldms\n",
-                  bfd->rmt_min_rx);
-}
-
-static void
-bfd_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
-                 void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
-    struct bfd *bfd;
-
-    ovs_mutex_lock(&mutex);
-    if (argc > 1) {
-        bfd = bfd_find_by_name(argv[1]);
-        if (!bfd) {
-            unixctl_command_reply_error(conn, "no such bfd object");
-            goto out;
-        }
-        bfd_put_details(&ds, bfd);
-    } else {
-        HMAP_FOR_EACH (bfd, node, all_bfds) {
-            ds_put_format(&ds, "---- %s ----\n", bfd->name);
-            bfd_put_details(&ds, bfd);
-        }
-    }
-    unixctl_command_reply(conn, ds_cstr(&ds));
-    ds_destroy(&ds);
-
-out:
-    ovs_mutex_unlock(&mutex);
-}
-
-
-static void
-bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc,
-                                    const char *argv[], void *aux OVS_UNUSED)
-    OVS_EXCLUDED(mutex)
-{
-    const char *forward_str = argv[argc - 1];
-    int forwarding_override;
-    struct bfd *bfd;
-
-    ovs_mutex_lock(&mutex);
-    if (!strcasecmp("true", forward_str)) {
-        forwarding_override = 1;
-    } else if (!strcasecmp("false", forward_str)) {
-        forwarding_override = 0;
-    } else if (!strcasecmp("normal", forward_str)) {
-        forwarding_override = -1;
-    } else {
-        unixctl_command_reply_error(conn, "unknown fault string");
-        goto out;
-    }
-
-    if (argc > 2) {
-        bfd = bfd_find_by_name(argv[1]);
-        if (!bfd) {
-            unixctl_command_reply_error(conn, "no such BFD object");
-            goto out;
-        }
-        bfd->forwarding_override = forwarding_override;
-    } else {
-        HMAP_FOR_EACH (bfd, node, all_bfds) {
-            bfd->forwarding_override = forwarding_override;
-        }
-    }
-
-    unixctl_command_reply(conn, "OK");
-
-out:
-    ovs_mutex_unlock(&mutex);
 }
diff --git a/lib/bfd.h b/lib/bfd.h
index 4e7d4cb..99f7006 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 Nicira, Inc.
+/* Copyright (c) 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -10,46 +10,203 @@
  * 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.  */
+ * limitations under the License. */
 
 #ifndef BFD_H
 #define BFD_H 1
 
+#include <linux/types.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define BFD_VERSION 1
 #define BFD_PACKET_LEN 24
 #define BFD_DEST_PORT 3784
 
-#include <stdbool.h>
-#include <inttypes.h>
-
-struct bfd;
-struct dpif_flow_stats;
-struct flow;
-struct flow_wildcards;
-struct netdev;
-struct ofpbuf;
-struct smap;
-
-void bfd_wait(const struct bfd *);
-void bfd_run(struct bfd *);
-
-bool bfd_should_send_packet(const struct bfd *);
-void bfd_put_packet(struct bfd *bfd, struct ofpbuf *packet,
-                    uint8_t eth_src[6]);
-
-bool bfd_should_process_flow(const struct bfd *, const struct flow *,
-                             struct flow_wildcards *);
-void bfd_process_packet(struct bfd *, const struct flow *,
-                        const struct ofpbuf *);
-
-struct bfd *bfd_configure(struct bfd *, const char *name,
-                          const struct smap *smap,
-                          struct netdev *netdev);
-struct bfd *bfd_ref(const struct bfd *);
-void bfd_unref(struct bfd *);
-
-void bfd_account_rx(struct bfd *, const struct dpif_flow_stats *);
-bool bfd_forwarding(struct bfd *);
-void bfd_get_status(const struct bfd *, struct smap *);
-void bfd_set_netdev(struct bfd *, const struct netdev *);
-long long int bfd_wake_time(const struct bfd *);
+#define VERS_SHIFT 5
+#define DIAG_MASK 0x1f
+#define STATE_MASK 0xC0
+#define FLAGS_MASK 0x3f
+
+/* RFC 5880 Section 4.1
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Vers |  Diag   |Sta|P|F|C|A|D|M|  Detect Mult  |    Length     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                       My Discriminator                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      Your Discriminator                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                    Desired Min TX Interval                    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                   Required Min RX Interval                    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                 Required Min Echo RX Interval                 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+struct bfd_msg {
+    uint8_t vers_diag;    /* Version and diagnostic. */
+    uint8_t flags;        /* 2bit State field followed by flags. */
+    uint8_t mult;         /* Fault detection multiplier. */
+    uint8_t length;       /* Length of this BFD message. */
+    __be32 my_disc;       /* My discriminator. */
+    __be32 your_disc;     /* Your discriminator. */
+    __be32 min_tx;        /* Desired minimum tx interval. */
+    __be32 min_rx;        /* Required minimum rx interval. */
+    __be32 min_rx_echo;   /* Required minimum echo rx interval. */
+};
+
+enum bfd_flags {
+    FLAG_MULTIPOINT = 1 << 0,
+    FLAG_DEMAND = 1 << 1,
+    FLAG_AUTH = 1 << 2,
+    FLAG_CTL = 1 << 3,
+    FLAG_FINAL = 1 << 4,
+    FLAG_POLL = 1 << 5
+};
+
+enum bfd_state {
+    STATE_ADMIN_DOWN = 0 << 6,
+    STATE_DOWN = 1 << 6,
+    STATE_INIT = 2 << 6,
+    STATE_UP = 3 << 6
+};
+
+enum bfd_diag {
+    DIAG_NONE = 0,                /* No Diagnostic. */
+    DIAG_EXPIRED = 1,             /* Control Detection Time Expired. */
+    DIAG_ECHO_FAILED = 2,         /* Echo Function Failed. */
+    DIAG_RMT_DOWN = 3,            /* Neighbor Signaled Session Down. */
+    DIAG_FWD_RESET = 4,           /* Forwarding Plane Reset. */
+    DIAG_PATH_DOWN = 5,           /* Path Down. */
+    DIAG_CPATH_DOWN = 6,          /* Concatenated Path Down. */
+    DIAG_ADMIN_DOWN = 7,          /* Administratively Down. */
+    DIAG_RCPATH_DOWN = 8          /* Reverse Concatenated Path Down. */
+};
+
+enum bfd_error {
+    BFD_PASS = 0,                 /* No error. */
+    BFD_EINVAL = 1,               /* Invalid arguments. */
+    BFD_EPOLL = 2,                /* bfd poll and final flags are both on. */
+    BFD_EMSG = 3                  /* bfd control packet error. */
+};
+
+/* Used to configure a BFD session. */
+struct bfd_setting {
+    /* Local state variables. */
+    uint32_t disc;                /* bfd.LocalDiscr. */
+    uint8_t mult;                 /* bfd.DetectMult. */
+    uint32_t min_tx;              /* bfd.DesiredMinTxInterval. */
+    uint32_t min_rx;              /* bfd.RequiredMinRxInterval. */
+
+    /* Open Vswitch specific settings. */
+    bool cpath_down;              /* Set Concatenated Path Down. */
+    bool forwarding_override;     /* Manual override of 'forwarding' status. */
+    int forward_if_rx_interval;   /* How often to detect forward_if_rx. */
+    int decay_min_rx;             /* bfd.min_rx is set to decay_min_rx when */
+                                  /* in decay. */
+};
+
+/* BFD status. */
+struct bfd_status {
+    bool forwarding;              /* The liveness of bfd session. */
+    uint8_t mult;                 /* bfd.DetectMult. */
+    bool cpath_down;              /* If cpath_down enabled. */
+    uint32_t tx_interval;         /* tx interval in use. */
+    uint32_t rx_interval;         /* rx interval in use. */
+
+    uint32_t local_disc;          /* bfd.LocalDiscr. */
+    uint32_t local_min_tx;        /* bfd.DesiredMinTxInterval */
+    uint32_t local_min_rx;        /* bfd.DesiredMinRxInterval*/
+    enum bfd_flags local_flags;   /* Flags sent on messages. */
+    enum bfd_state local_state;   /* bfd.SessionState. */
+    enum bfd_diag local_diag;     /* bfd.LocalDiag. */
+    long long int detect_time;    /* RFC 5880 6.8.4 Detection time. */
+    long long int last_tx;        /* Last TX time. */
+    long long int next_tx;        /* Next TX time. */
+
+    uint32_t rmt_disc;            /* bfd.RemoteDiscr. */
+    uint32_t rmt_min_tx;          /* bfd.RemoteMinTxInterval */
+    uint32_t rmt_min_rx;          /* bfd.RemoteMinRxInterval*/
+    enum bfd_flags rmt_flags;     /* Flags last received. */
+    enum bfd_state rmt_state;     /* bfd.RemoteSessionState. */
+    enum bfd_diag rmt_diag;       /* Remote diagnostic. */
+
+    uint64_t flap_count;          /* Flap count of forwarding. */
+};
+
+/* A BFD session.  Users are not permitted to directly access the variable
+ * of this struct.  For BFD configuration, use the bfd_configure().  For
+ * BFD status extraction, use the bfd_get_status(). */
+struct bfd {
+    /* Local state variables. */
+    uint32_t disc;                /* bfd.LocalDiscr. */
+    uint8_t mult;                 /* bfd.DetectMult. */
+    enum bfd_state state;         /* bfd.SessionState. */
+    enum bfd_diag diag;           /* bfd.LocalDiag. */
+    enum bfd_flags flags;         /* Flags sent on messages. */
+    uint32_t min_tx;              /* bfd.DesiredMinTxInterval. */
+    uint32_t min_rx;              /* bfd.RequiredMinRxInterval. */
+    uint32_t cfg_min_tx;          /* Configured minimum TX rate. */
+    uint32_t cfg_min_rx;          /* Configured required minimum RX rate. */
+    long long int detect_time;    /* RFC 5880 6.8.4 Detection time. */
+    long long int last_tx;        /* Last TX time. */
+    long long int next_tx;        /* Next TX time. */
+
+    /* Remote side state variables. */
+    uint32_t rmt_disc;            /* bfd.RemoteDiscr. */
+    enum bfd_state rmt_state;     /* bfd.RemoteSessionState. */
+    enum bfd_diag rmt_diag;       /* Remote diagnostic. */
+    enum bfd_flags rmt_flags;     /* Flags last received. */
+    long long int rmt_min_rx;     /* bfd.RemoteMinRxInterval. */
+    long long int rmt_min_tx;     /* Remote minimum TX interval. */
+
+    /* POLL sequence. */
+    uint32_t poll_min_tx;         /* min_tx in POLL sequence. */
+    uint32_t poll_min_rx;         /* min_rx in POLL sequence. */
+
+    /* Open Vswitch specific features. */
+    bool cpath_down;              /* Set Concatenated Path Down. */
+
+    int forwarding_override;      /* Manual override of 'forwarding' status. */
+
+    /* Equivalent to bfd demand mode. */
+    bool last_forwarding;         /* Last calculation of forwarding flag. */
+    int forward_if_rx_interval;   /* How often to detect forward_if_rx. */
+    long long int forward_if_rx_detect_time;
+    bool forward_if_rx_data;      /* Data packet received in last interval. */
+
+    /* BFD decay feature is for reducing the */
+    bool in_decay;                /* True when bfd is in decay. */
+    int decay_min_rx;             /* bfd.min_rx is set to decay_min_rx when */
+                                  /* in decay. */
+    long long int decay_detect_time; /* Next decay detect time. */
+    uint32_t decay_rx_count;      /* Count of data packets received. */
+
+    uint64_t flap_count;          /* Counts bfd forwarding flaps. */
+};
+
+enum bfd_error bfd_configure(struct bfd *, const struct bfd_setting *);
+long long int bfd_wait(const struct bfd *);
+
+void bfd_run(struct bfd *, long long int now);
+void bfd_get_status(const struct bfd *, struct bfd_status *);
+bool bfd_forwarding(const struct bfd *, long long int now);
+void bfd_account_rx(struct bfd *, uint32_t n_pkt);
+
+bool bfd_should_send_packet(const struct bfd *, long long int now);
+enum bfd_error bfd_put_packet(struct bfd *, void *, size_t len,
+                              long long int now);
+bool bfd_should_process_packet(const __be16 eth_type, const uint8_t ip_proto,
+                               const __be16 udp_dst);
+enum bfd_error bfd_process_packet(struct bfd *, void *, size_t len,
+                                  long long now);
+
+/* Helpers. */
+const char * bfd_error_to_str(enum bfd_error error);
+const char * bfd_flag_to_str(enum bfd_flags flags);
+const char * bfd_state_to_str(enum bfd_state state);
+const char * bfd_diag_to_str(enum bfd_diag diag);
+
 #endif /* bfd.h */
diff --git a/lib/bfd_ts.c b/lib/bfd_ts.c
new file mode 100644
index 0000000..f05e0b0
--- /dev/null
+++ b/lib/bfd_ts.c
@@ -0,0 +1,620 @@
+/* Copyright (c) 2014 Nicira, 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 "bfd_ts.h"
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <errno.h>
+
+#include "byte-order.h"
+#include "connectivity.h"
+#include "csum.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "flow.h"
+#include "hmap.h"
+#include "odp-util.h"
+#include "ofpbuf.h"
+#include "ovs-thread.h"
+#include "openvswitch/types.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "random.h"
+#include "seq.h"
+#include "smap.h"
+#include "timeval.h"
+#include "unaligned.h"
+#include "unixctl.h"
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(bfd_ts);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20);
+
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
+static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
+
+/* Thread-safe wrapper for bfd.{c,h}.
+ * The 'struct bfd_ts' is the thread-safe wrapper for 'struct bfd'.  It
+ * contains the additional information (e.g. monitored interface name)
+ * and header fields needed for construction and parse of the BFD control
+ * packet.  Each 'struct bfd' should be created by creating the
+ * 'struct bfd_ts'.  Thusly, the argument 'struct bfd *' can be converted to
+ * the corresponding 'struct bfd_ts *'. */
+struct bfd_ts {
+    struct hmap_node node;        /* In 'all_bfds'. */
+    struct bfd bfd;               /* The BFD session. */
+    char *name;                   /* Name of the monitored interface. */
+    uint32_t disc;                /* bfd.Discriminator. */
+    uint64_t last_flap_count;     /* Last check of bfd.flap_count value. */
+    atomic_int ref_cnt;           /* Reference counter. */
+
+    bool check_tnl_key;           /* Verify tunnel key of inbound packets? */
+    uint8_t eth_dst[ETH_ADDR_LEN];/* Ethernet destination address. */
+    bool eth_dst_set;             /* 'eth_dst' set through database. */
+    ovs_be32 ip_src;              /* IPv4 source address. */
+    ovs_be32 ip_dst;              /* IPv4 destination address. */
+    uint32_t udp_src;             /* UDP source port. */
+};
+
+
+static void
+bfd_ts_check_flap(struct bfd *bfd)  OVS_REQUIRES(mutex)
+{
+    struct bfd_ts *ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+    struct bfd_status s;
+
+    bfd_get_status(bfd, &s);
+
+    if (ts->last_flap_count != s.flap_count) {
+        seq_change(connectivity_seq_get());
+        ts->last_flap_count = s.flap_count;
+    }
+}
+
+static uint32_t
+bfd_ts_generate_discriminator(void) OVS_REQUIRES(mutex)
+{
+    uint32_t disc = 0;
+
+    /* RFC 5880 Section 6.8.1
+     * It SHOULD be set to a random (but still unique) value to improve
+     * security.  The value is otherwise outside the scope of this
+     * specification. */
+    while (!disc) {
+        struct bfd_ts *ts;
+
+        /* 'disc' is by definition random, so there's no reason to waste time
+         * hashing it. */
+        disc = random_uint32();
+        HMAP_FOR_EACH_IN_BUCKET (ts, node, disc, all_bfds) {
+            if (ts->disc == disc) {
+                disc = 0;
+                break;
+            }
+        }
+    }
+
+    return disc;
+}
+
+static bool
+bfd_ts_lookup_ip(const char *host_name, struct in_addr *addr)
+    OVS_REQUIRES(mutex)
+{
+    if (!inet_pton(AF_INET, host_name, addr)) {
+        VLOG_ERR("\"%s\" is not a valid IP address", host_name);
+
+        return false;
+    }
+
+    return true;
+}
+
+static struct bfd *
+bfd_ts_find_by_name(const char *name)
+    OVS_REQUIRES(mutex)
+{
+    struct bfd_ts *ts;
+
+    HMAP_FOR_EACH (ts, node, all_bfds) {
+        if (!strcmp(ts->name, name)) {
+            return &ts->bfd;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+bfd_ts_put_details(struct ds *ds, const struct bfd *bfd)
+    OVS_REQUIRES(mutex)
+{
+    struct bfd_status status;
+    bfd_get_status(bfd, &status);
+
+    ds_put_format(ds, "\tForwarding: %s\n",
+                  bfd_forwarding(bfd, time_msec()) ? "true" : "false");
+    ds_put_format(ds, "\tDetect Multiplier: %d\n", status.mult);
+    ds_put_format(ds, "\tConcatenated Path Down: %s\n",
+                  status.cpath_down ? "true" : "false");
+    ds_put_format(ds, "\tTX Interval: Approx %ums\n", status.tx_interval);
+    ds_put_format(ds, "\tRX Interval: Approx %ums\n", status.rx_interval);
+    ds_put_format(ds, "\tDetect Time: now %+lldms\n",
+                  time_msec() - status.detect_time);
+    ds_put_format(ds, "\tNext TX Time: now %+lldms\n",
+                  time_msec() - status.next_tx);
+    ds_put_format(ds, "\tLast TX Time: now %+lldms\n",
+                  time_msec() - status.last_tx);
+
+    ds_put_cstr(ds, "\n");
+
+    ds_put_format(ds, "\tLocal Flags: %s\n",
+                  bfd_flag_to_str(status.local_flags));
+    ds_put_format(ds, "\tLocal Session State: %s\n",
+                  bfd_state_to_str(status.local_state));
+    ds_put_format(ds, "\tLocal Diagnostic: %s\n",
+                  bfd_diag_to_str(status.local_diag));
+    ds_put_format(ds, "\tLocal Discriminator: 0x%"PRIx32"\n",
+                  status.local_disc);
+    ds_put_format(ds, "\tLocal Minimum TX Interval: %dms\n",
+                  status.local_min_tx);
+    ds_put_format(ds, "\tLocal Minimum RX Interval: %ums\n",
+                  status.local_min_rx);
+
+    ds_put_cstr(ds, "\n");
+
+    ds_put_format(ds, "\tRemote Flags: %s\n",
+                  bfd_flag_to_str(status.rmt_flags));
+    ds_put_format(ds, "\tRemote Session State: %s\n",
+                  bfd_state_to_str(status.rmt_state));
+    ds_put_format(ds, "\tRemote Diagnostic: %s\n",
+                  bfd_diag_to_str(status.rmt_diag));
+    ds_put_format(ds, "\tRemote Discriminator: 0x%"PRIx32"\n",
+                  status.rmt_disc);
+    ds_put_format(ds, "\tRemote Minimum TX Interval: %ums\n",
+                  status.rmt_min_tx);
+    ds_put_format(ds, "\tRemote Minimum RX Interval: %ums\n",
+                  status.rmt_min_rx);
+}
+
+static void
+bfd_ts_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
+                    void *aux OVS_UNUSED)
+    OVS_EXCLUDED(mutex)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    ovs_mutex_lock(&mutex);
+    if (argc > 1) {
+        struct bfd *bfd;
+
+        bfd = bfd_ts_find_by_name(argv[1]);
+        if (!bfd) {
+            unixctl_command_reply_error(conn, "no such bfd object");
+            goto out;
+        }
+        bfd_ts_put_details(&ds, bfd);
+    } else {
+        struct bfd_ts *ts;
+
+        HMAP_FOR_EACH (ts, node, all_bfds) {
+            ds_put_format(&ds, "---- %s ----\n", ts->name);
+            bfd_ts_put_details(&ds, &ts->bfd);
+        }
+    }
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+
+out:
+    ovs_mutex_unlock(&mutex);
+}
+
+static void
+bfd_ts_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc,
+                                       const char *argv[], void *aux OVS_UNUSED)
+    OVS_EXCLUDED(mutex)
+{
+    const char *forward_str = argv[argc - 1];
+    int forwarding_override;
+
+    ovs_mutex_lock(&mutex);
+    if (!strcasecmp("true", forward_str)) {
+        forwarding_override = 1;
+    } else if (!strcasecmp("false", forward_str)) {
+        forwarding_override = -1;
+    } else if (!strcasecmp("normal", forward_str)) {
+        forwarding_override = 0;
+    } else {
+        unixctl_command_reply_error(conn, "unknown fault string");
+        goto out;
+    }
+
+    if (argc > 2) {
+        struct bfd *bfd;
+
+        bfd = bfd_ts_find_by_name(argv[1]);
+        if (!bfd) {
+            unixctl_command_reply_error(conn, "no such BFD object");
+            goto out;
+        }
+        bfd->forwarding_override = forwarding_override;
+    } else {
+        struct bfd_ts *ts;
+
+        HMAP_FOR_EACH (ts, node, all_bfds) {
+            ts->bfd.forwarding_override = forwarding_override;
+        }
+    }
+
+    unixctl_command_reply(conn, "OK");
+
+out:
+    ovs_mutex_unlock(&mutex);
+}
+
+
+/* Initializes, destroys, or reconfigures the BFD session 'bfd' and the wrapper
+ * 'struct bfd_ts' (named 'name'), according to the database configuration
+ * contained in 'cfg'.  Takes ownership of 'bfd', which may be NULL.  Returns
+ * a BFD object which may be used as a handle for the session, or NULL if BFD
+ * is not enabled according to 'cfg'.  Also returns NULL if cfg is NULL. */
+struct bfd *
+bfd_ts_configure(struct bfd *bfd, const char *name, const struct smap *cfg)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static atomic_uint16_t udp_src = ATOMIC_VAR_INIT(0);
+    struct bfd_ts *ts;
+    struct bfd_setting setting;
+    const char *hwaddr, *ip_src, *ip_dst;
+    struct in_addr in_addr;
+    uint8_t ea[ETH_ADDR_LEN];
+
+    if (ovsthread_once_start(&once)) {
+        unixctl_command_register("bfd/show", "[interface]", 0, 1,
+                                 bfd_ts_unixctl_show, NULL);
+        unixctl_command_register("bfd/set-forwarding",
+                                 "[interface] normal|false|true", 1, 2,
+                                 bfd_ts_unixctl_set_forwarding_override, NULL);
+        ovsthread_once_done(&once);
+    }
+
+    if (!cfg || !smap_get_bool(cfg, "enable", false)) {
+        bfd_ts_unref(bfd);
+        return NULL;
+    }
+
+    ovs_mutex_lock(&mutex);
+    if (!bfd) {
+        ts = xzalloc(sizeof *ts);
+
+        ts->name = xstrdup(name);
+        ts->disc = bfd_ts_generate_discriminator();
+        atomic_init(&ts->ref_cnt, 1);
+
+        /* RFC 5881 section 4
+         * The source port MUST be in the range 49152 through 65535.  The same
+         * UDP source port number MUST be used for all BFD Control packets
+         * associated with a particular session.  The source port number SHOULD
+         * be unique among all BFD sessions on the system. */
+        atomic_add(&udp_src, 1, &ts->udp_src);
+        ts->udp_src = (ts->udp_src % 16384) + 49152;
+
+        hmap_insert(all_bfds, &ts->node, ts->disc);
+    } else {
+        ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+    }
+
+    /* Congfiures the 'struct bfd_ts'. */
+    ts->check_tnl_key = smap_get_bool(cfg, "check_tnl_key", false);
+
+    hwaddr = smap_get(cfg, "bfd_dst_mac");
+    if (hwaddr && eth_addr_from_string(hwaddr, ea) && !eth_addr_is_zero(ea)) {
+        memcpy(ts->eth_dst, ea, ETH_ADDR_LEN);
+        ts->eth_dst_set = true;
+    } else if (ts->eth_dst_set) {
+        memcpy(ts->eth_dst, eth_addr_bfd, ETH_ADDR_LEN);
+        ts->eth_dst_set = false;
+    }
+
+    ip_src = smap_get(cfg, "bfd_src_ip");
+    if (ip_src && bfd_ts_lookup_ip(ip_src, &in_addr)) {
+        memcpy(&ts->ip_src, &in_addr, sizeof in_addr);
+    } else {
+        ts->ip_src = htonl(0xA9FE0100); /* 169.254.1.0. */
+    }
+
+    ip_dst = smap_get(cfg, "bfd_dst_ip");
+    if (ip_dst && bfd_ts_lookup_ip(ip_dst, &in_addr)) {
+        memcpy(&ts->ip_dst, &in_addr, sizeof in_addr);
+    } else {
+        ts->ip_dst = htonl(0xA9FE0101); /* 169.254.1.1. */
+    }
+
+    /* Configures the 'struct bfd'. */
+    setting.disc = ts->disc;
+    setting.mult = 3;
+    setting.min_tx = smap_get_int(cfg, "min_tx", 100);
+    setting.min_rx = smap_get_int(cfg, "min_rx", 1000);
+    setting.cpath_down = smap_get_bool(cfg, "cpath_down", false);
+    setting.forward_if_rx_interval = smap_get_bool(cfg, "forwarding_if_rx",
+                                                   0) ? 3000 : 0;
+    setting.decay_min_rx = smap_get_int(cfg, "decay_min_rx", 0);
+
+    /* Since forwarding_override can only be set via 'ovs-appctl'
+     * command, use this hack to retain its configured value. */
+    setting.forwarding_override = ts->bfd.forwarding_override;
+    bfd_configure(&ts->bfd, &setting);
+
+    ovs_mutex_unlock(&mutex);
+
+    return &ts->bfd;
+}
+
+/* Called when accounting the number of packets received from the interface. */
+void
+bfd_ts_account_rx(struct bfd *bfd, uint32_t n_pkt)
+{
+    ovs_mutex_lock(&mutex);
+    bfd_account_rx(bfd, n_pkt);
+    ovs_mutex_unlock(&mutex);
+}
+
+/* References the 'bfd' in the corresponding 'strcut bfd_ts'. */
+struct bfd *
+bfd_ts_ref(const struct bfd *bfd_)
+{
+    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
+    struct bfd_ts *ts;
+
+    if (bfd) {
+        int orig;
+
+        ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+        atomic_add(&ts->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
+    }
+    return bfd;
+}
+
+/* Un-references the 'bfd' in the corresponding 'strcut bfd_ts'.  Frees
+ * the memory if the new 'ref_count' is 0. */
+void
+bfd_ts_unref(struct bfd *bfd)
+{
+    if (bfd) {
+        int orig;
+        struct bfd_ts *ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+
+        atomic_sub(&ts->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
+        if (orig == 1) {
+            ovs_mutex_lock(&mutex);
+            hmap_remove(all_bfds, &ts->node);
+            free(ts->name);
+            free(ts);
+            ovs_mutex_unlock(&mutex);
+        }
+    }
+}
+
+/* Returns a 'smap' of key value pairs representing the status of 'bfd'
+ * intended for the OVS database. */
+int
+bfd_ts_get_status(struct bfd *bfd, struct smap *smap)
+{
+    struct bfd_status status;
+
+    ovs_mutex_lock(&mutex);
+    if (!bfd) {
+        ovs_mutex_unlock(&mutex);
+        return ENOENT;
+    }
+
+    bfd_get_status(bfd, &status);
+
+    smap_add(smap, "forwarding", bfd_forwarding(bfd, time_msec())
+             ? "true" : "false");
+    smap_add(smap, "state", bfd_state_to_str(status.local_state));
+    smap_add(smap, "diagnostic", bfd_diag_to_str(status.local_diag));
+    smap_add_format(smap, "flap_count", "%"PRIu64, status.flap_count);
+
+    if (status.local_state != STATE_DOWN) {
+        smap_add(smap, "remote_state", bfd_state_to_str(status.rmt_state));
+        smap_add(smap, "remote_diagnostic", bfd_diag_to_str(status.rmt_diag));
+    }
+
+    ovs_mutex_unlock(&mutex);
+    return 0;
+}
+
+/* Queries the bfd.forwarding flag. */
+bool
+bfd_ts_forwarding(struct bfd *bfd)
+{
+    bool ret;
+
+    ovs_mutex_lock(&mutex);
+    ret = bfd_forwarding(bfd, time_msec());
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+/* Called to check the BFD sessions status, decay and forwarding_if_rx. */
+void
+bfd_ts_run(struct bfd *bfd)
+{
+    ovs_mutex_lock(&mutex);
+    bfd_run(bfd, time_msec());
+    bfd_ts_check_flap(bfd);
+    ovs_mutex_unlock(&mutex);
+}
+
+/* Registers the next wakeup time with the poll loop. */
+void
+bfd_ts_wait(const struct bfd *bfd)
+{
+    poll_timer_wait_until(bfd_ts_wake_time(bfd));
+}
+
+/* Returns the next wakeup time of the BFD session. */
+long long int
+bfd_ts_wake_time(const struct bfd *bfd)
+{
+    long long int ret;
+
+    ovs_mutex_lock(&mutex);
+    ret = bfd_wait(bfd);
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+/* Checks if the BFD control packet should be sent. */
+bool
+bfd_ts_should_send_packet(struct bfd *bfd)
+{
+    bool ret;
+
+    ovs_mutex_lock(&mutex);
+    ret = bfd_should_send_packet(bfd, time_msec());
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+/* Constructs the entire BFD control packet. */
+void
+bfd_ts_put_packet(struct bfd *bfd, struct ofpbuf *p,
+                  uint8_t eth_src[ETH_ADDR_LEN])
+{
+    struct udp_header *udp;
+    struct eth_header *eth;
+    struct ip_header *ip;
+    struct bfd_msg *msg;
+    struct bfd_ts *ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+    struct bfd_status s;
+
+    ovs_mutex_lock(&mutex);
+    bfd_get_status(bfd, &s);
+    if (s.next_tx) {
+        long long int delay = time_msec() - s.next_tx;
+        if (delay > s.tx_interval * 3 / 2) {
+            VLOG_INFO("%s: long delay of %lldms (expected %ums) sending BFD"
+                      " control message", ts->name, delay, s.tx_interval);
+        }
+    }
+
+    /* RFC 5880 Section 6.5
+     * A BFD Control packet MUST NOT have both the Poll (P) and Final (F) bits
+     * set. */
+    ovs_assert(!(s.local_flags & FLAG_POLL) || !(s.local_flags & FLAG_FINAL));
+
+    ofpbuf_reserve(p, 2); /* Properly align after the ethernet header. */
+    eth = ofpbuf_put_uninit(p, sizeof *eth);
+    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    memcpy(eth->eth_dst, ts->eth_dst, ETH_ADDR_LEN);
+    eth->eth_type = htons(ETH_TYPE_IP);
+
+    ip = ofpbuf_put_zeros(p, sizeof *ip);
+    ip->ip_ihl_ver = IP_IHL_VER(5, 4);
+    ip->ip_tot_len = htons(sizeof *ip + sizeof *udp + sizeof *msg);
+    ip->ip_ttl = MAXTTL;
+    ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
+    ip->ip_proto = IPPROTO_UDP;
+    put_16aligned_be32(&ip->ip_src, ts->ip_src);
+    put_16aligned_be32(&ip->ip_dst, ts->ip_dst);
+    ip->ip_csum = csum(ip, sizeof *ip);
+
+    udp = ofpbuf_put_zeros(p, sizeof *udp);
+    udp->udp_src = htons(ts->udp_src);
+    udp->udp_dst = htons(BFD_DEST_PORT);
+    udp->udp_len = htons(sizeof *udp + sizeof *msg);
+
+    bfd_put_packet(bfd, ofpbuf_put_uninit(p, sizeof *msg),
+                   BFD_PACKET_LEN, time_msec());
+    ovs_mutex_unlock(&mutex);
+}
+
+/* Checks if the received packet should be processed as BFD control
+ * packet. */
+bool
+bfd_ts_should_process_packet(struct bfd *bfd, const struct flow *flow,
+                             struct flow_wildcards *wc)
+{
+    struct bfd_ts *ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+    bool ret = false;
+
+    ovs_mutex_lock(&mutex);
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    if (ts->eth_dst_set && memcmp(ts->eth_dst, flow->dl_dst, ETH_ADDR_LEN)) {
+        goto out;
+    }
+
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+
+    if (ts->check_tnl_key) {
+        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+    }
+
+    ret = (!ts->check_tnl_key || flow->tunnel.tun_id == htonll(0))
+          && bfd_should_process_packet(flow->dl_type, flow->nw_proto,
+                                       flow->tp_dst);
+out:
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+
+/* Processes the BFD control packet.  Updates the corresponding BFD status. */
+int
+bfd_ts_process_packet(struct bfd *bfd, const struct flow *flow,
+                      const struct ofpbuf *p)
+{
+    struct bfd_ts *ts = CONTAINER_OF(bfd, struct bfd_ts, bfd);
+    void *msg;
+
+    ovs_mutex_lock(&mutex);
+    if (flow->nw_ttl != 255) {
+        /* XXX Should drop in the kernel to prevent DOS. */
+        goto out;
+    }
+
+    msg = ofpbuf_at(p, (uint8_t *)p->l7 - (uint8_t *)p->data, BFD_PACKET_LEN);
+    if (!msg) {
+        VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
+                     "%"PRIdPTR" bytes long, at least %d required).",
+                     ts->name, (uint8_t *) ofpbuf_tail(p) - (uint8_t *) p->l7,
+                     BFD_PACKET_LEN);
+        goto out;
+    }
+    bfd_process_packet(bfd, msg, BFD_PACKET_LEN, time_msec());
+
+    bfd_ts_check_flap(bfd);
+out:
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
diff --git a/lib/bfd_ts.h b/lib/bfd_ts.h
new file mode 100644
index 0000000..86f62e4
--- /dev/null
+++ b/lib/bfd_ts.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2014 Nicira, 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 BFD_TS_H
+#define BFD_TS_H 1
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "packets.h"
+
+/* A thread-safe wrapper for bfd.{c,h}. */
+struct bfd;
+struct flow;
+struct ofpbuf;
+struct smap;
+
+struct bfd * bfd_ts_configure(struct bfd *, const char *,
+                              const struct smap *);
+void bfd_ts_account_rx(struct bfd *, uint32_t n_pkt);
+struct bfd * bfd_ts_ref(const struct bfd *);
+void bfd_ts_unref(struct bfd *);
+int bfd_ts_get_status(struct bfd *, struct smap *);
+bool bfd_ts_forwarding(struct bfd *);
+void bfd_ts_run(struct bfd *);
+void bfd_ts_wait(const struct bfd *);
+long long int bfd_ts_wake_time(const struct bfd *);
+
+bool bfd_ts_should_send_packet(struct bfd *);
+void bfd_ts_put_packet(struct bfd *, struct ofpbuf *,
+                       uint8_t eth_src[ETH_ADDR_LEN]);
+bool bfd_ts_should_process_packet(struct bfd *, const struct flow *,
+                                  struct flow_wildcards *);
+int bfd_ts_process_packet(struct bfd *, const struct flow *,
+                          const struct ofpbuf *);
+
+#endif /* bfd_thread-safe.h */
diff --git a/ofproto/ofproto-dpif-monitor.c b/ofproto/ofproto-dpif-monitor.c
index b521735..aa20547 100644
--- a/ofproto/ofproto-dpif-monitor.c
+++ b/ofproto/ofproto-dpif-monitor.c
@@ -19,7 +19,7 @@
 
 #include <string.h>
 
-#include "bfd.h"
+#include "bfd_ts.h"
 #include "cfm.h"
 #include "hash.h"
 #include "heap.h"
@@ -140,8 +140,8 @@ mport_update(struct mport *mport, struct bfd *bfd, struct cfm *cfm,
         mport->cfm = cfm_ref(cfm);
     }
     if (mport->bfd != bfd) {
-        bfd_unref(mport->bfd);
-        mport->bfd = bfd_ref(bfd);
+        bfd_ts_unref(mport->bfd);
+        mport->bfd = bfd_ts_ref(bfd);
     }
     if (hw_addr && memcmp(mport->hw_addr, hw_addr, ETH_ADDR_LEN)) {
         memcpy(mport->hw_addr, hw_addr, ETH_ADDR_LEN);
@@ -198,9 +198,9 @@ monitor_run(void)
             cfm_compose_ccm(mport->cfm, &packet, mport->hw_addr);
             ofproto_dpif_send_packet(mport->ofport, &packet);
         }
-        if (mport->bfd && bfd_should_send_packet(mport->bfd)) {
+        if (mport->bfd && bfd_ts_should_send_packet(mport->bfd)) {
             ofpbuf_clear(&packet);
-            bfd_put_packet(mport->bfd, &packet, mport->hw_addr);
+            bfd_ts_put_packet(mport->bfd, &packet, mport->hw_addr);
             ofproto_dpif_send_packet(mport->ofport, &packet);
         }
         if (mport->cfm) {
@@ -208,11 +208,11 @@ monitor_run(void)
             cfm_wait(mport->cfm);
         }
         if (mport->bfd) {
-            bfd_run(mport->bfd);
-            bfd_wait(mport->bfd);
+            bfd_ts_run(mport->bfd);
+            bfd_ts_wait(mport->bfd);
         }
         /* Computes the next wakeup time for this mport. */
-        next_wake_time = MIN(bfd_wake_time(mport->bfd),
+        next_wake_time = MIN(bfd_ts_wake_time(mport->bfd),
                              cfm_wake_time(mport->cfm));
         heap_change(&monitor_heap, &mport->heap_node,
                     MSEC_TO_PRIO(next_wake_time));
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index eb4931e..edc4dd1 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -18,7 +18,7 @@
 
 #include <errno.h>
 
-#include "bfd.h"
+#include "bfd_ts.h"
 #include "bitmap.h"
 #include "bond.h"
 #include "bundle.h"
@@ -451,8 +451,8 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
     }
 
     if (xport->bfd != bfd) {
-        bfd_unref(xport->bfd);
-        xport->bfd = bfd_ref(bfd);
+        bfd_ts_unref(xport->bfd);
+        xport->bfd = bfd_ts_ref(bfd);
     }
 
     if (xport->peer) {
@@ -515,7 +515,7 @@ xlate_ofport_remove(struct ofport_dpif *ofport)
 
     netdev_close(xport->netdev);
     cfm_unref(xport->cfm);
-    bfd_unref(xport->bfd);
+    bfd_ts_unref(xport->bfd);
     free(xport);
 }
 
@@ -1641,11 +1641,13 @@ process_special(struct xlate_ctx *ctx, const struct flow *flow,
             cfm_process_heartbeat(xport->cfm, packet);
         }
         return SLOW_CFM;
-    } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
+    } else if (xport->bfd
+               && bfd_ts_should_process_packet(xport->bfd, flow, wc)) {
         if (packet) {
-            bfd_process_packet(xport->bfd, flow, packet);
+            bfd_ts_process_packet(xport->bfd, flow, packet);
+
             /* If POLL received, immediately sends FINAL back. */
-            if (bfd_should_send_packet(xport->bfd)) {
+            if (bfd_ts_should_send_packet(xport->bfd)) {
                 if (xport->peer) {
                     ofproto_dpif_monitor_port_send_soon(xport->ofport);
                 } else {
@@ -1741,7 +1743,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
             netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats);
             netdev_vport_inc_rx(peer->netdev, ctx->xin->resubmit_stats);
             if (peer->bfd) {
-                bfd_account_rx(peer->bfd, ctx->xin->resubmit_stats);
+                bfd_ts_account_rx(peer->bfd,
+                                  ctx->xin->resubmit_stats->n_packets);
             }
         }
 
@@ -3066,7 +3069,8 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     if (in_port && in_port->is_tunnel && ctx.xin->resubmit_stats) {
         netdev_vport_inc_rx(in_port->netdev, ctx.xin->resubmit_stats);
         if (in_port->bfd) {
-            bfd_account_rx(in_port->bfd, ctx.xin->resubmit_stats);
+            bfd_ts_account_rx(in_port->bfd,
+                              ctx.xin->resubmit_stats->n_packets);
         }
     }
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 8c43ee9..60d50df 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -21,7 +21,7 @@
 
 #include <errno.h>
 
-#include "bfd.h"
+#include "bfd_ts.h"
 #include "bond.h"
 #include "bundle.h"
 #include "byte-order.h"
@@ -77,6 +77,7 @@ enum { N_TABLES = 255 };
 enum { TBL_INTERNAL = N_TABLES - 1 };    /* Used for internal hidden rules. */
 BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
 
+struct bfd;
 struct flow_miss;
 
 struct rule_dpif {
@@ -1573,9 +1574,6 @@ port_modified(struct ofport *port_)
         cfm_set_netdev(port->cfm, port->up.netdev);
     }
 
-    if (port->bfd) {
-        bfd_set_netdev(port->bfd, port->up.netdev);
-    }
 
     ofproto_dpif_monitor_port_update(port, port->bfd, port->cfm,
                                      port->up.pp.hw_addr);
@@ -1722,8 +1720,8 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg)
     struct bfd *old;
 
     old = ofport->bfd;
-    ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev),
-                                cfg, ofport->up.netdev);
+    ofport->bfd = bfd_ts_configure(old, netdev_get_name(ofport->up.netdev),
+                                   cfg);
     if (ofport->bfd != old) {
         ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
@@ -1737,12 +1735,7 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap)
 {
     struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
 
-    if (ofport->bfd) {
-        bfd_get_status(ofport->bfd, smap);
-        return 0;
-    } else {
-        return ENOENT;
-    }
+    return bfd_ts_get_status(ofport->bfd, smap);
 }
 
 /* Spanning Tree. */
@@ -2648,7 +2641,7 @@ port_run(struct ofport_dpif *ofport)
     }
 
     if (ofport->bfd) {
-        bfd_enable = bfd_forwarding(ofport->bfd);
+        bfd_enable = bfd_ts_forwarding(ofport->bfd);
     }
 
     if (ofport->bfd || ofport->cfm) {
-- 
1.7.9.5




More information about the dev mailing list