Merge remote-tracking branch 'ovs-dev/master'
[sliver-openvswitch.git] / lib / bfd.c
index ddf8c4c..3ac257a 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -17,6 +17,7 @@
 
 #include <arpa/inet.h>
 
+#include "byte-order.h"
 #include "csum.h"
 #include "dpif.h"
 #include "dynamic-string.h"
@@ -58,10 +59,6 @@ VLOG_DEFINE_THIS_MODULE(bfd);
  *
  * - Set TOS/PCP on inner BFD frame, and outer tunnel header when encapped.
  *
- * - CFM "check_tnl_key" option equivalent.
- *
- * - CFM "fault override" equivalent.
- *
  * - Sending BFD messages should be in its own thread/process.
  *
  * - Scale testing.  How does it operate when there are large number of bfd
@@ -179,6 +176,10 @@ struct bfd {
     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. */
+
+    int ref_cnt;
+    int forwarding_override;      /* Manual override of 'forwarding' status. */
+    bool check_tnl_key;           /* Verify tunnel key of inbound packets? */
 };
 
 static bool bfd_in_poll(const struct bfd *);
@@ -194,6 +195,9 @@ static uint32_t generate_discriminator(void);
 static void bfd_put_details(struct ds *, const struct bfd *);
 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 *);
 
@@ -205,6 +209,10 @@ static struct hmap all_bfds = HMAP_INITIALIZER(&all_bfds);
 bool
 bfd_forwarding(const struct bfd *bfd)
 {
+    if (bfd->forwarding_override != -1) {
+        return bfd->forwarding_override == 1;
+    }
+
     return bfd->state == STATE_UP
         && bfd->rmt_diag != DIAG_PATH_DOWN
         && bfd->rmt_diag != DIAG_CPATH_DOWN
@@ -244,27 +252,28 @@ bfd_configure(struct bfd *bfd, const char *name,
     if (!init) {
         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);
         init = true;
     }
 
     if (!cfg || !smap_get_bool(cfg, "enable", false)) {
-        if (bfd) {
-            hmap_remove(&all_bfds, &bfd->node);
-            free(bfd->name);
-            free(bfd);
-        }
+        bfd_unref(bfd);
         return NULL;
     }
 
     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;
+        bfd->ref_cnt = 1;
 
         /* RFC 5881 section 4
          * The source port MUST be in the range 49152 through 65535.  The same
@@ -276,6 +285,7 @@ bfd_configure(struct bfd *bfd, const char *name,
         bfd_set_state(bfd, STATE_DOWN, DIAG_NONE);
     }
 
+    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->cfg_min_tx != min_tx) {
@@ -309,6 +319,30 @@ bfd_configure(struct bfd *bfd, const char *name,
     return bfd;
 }
 
+struct bfd *
+bfd_ref(const struct bfd *bfd_)
+{
+    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
+    if (bfd) {
+        ovs_assert(bfd->ref_cnt > 0);
+        bfd->ref_cnt++;
+    }
+    return bfd;
+}
+
+void
+bfd_unref(struct bfd *bfd)
+{
+    if (bfd) {
+        ovs_assert(bfd->ref_cnt > 0);
+        if (!--bfd->ref_cnt) {
+            hmap_remove(&all_bfds, &bfd->node);
+            free(bfd->name);
+            free(bfd);
+        }
+    }
+}
+
 void
 bfd_wait(const struct bfd *bfd)
 {
@@ -414,14 +448,18 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
 }
 
 bool
-bfd_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
+bfd_should_process_flow(const struct bfd *bfd, const struct flow *flow,
+                        struct flow_wildcards *wc)
 {
-    memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type);
     memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
     memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+    if (bfd->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(3784));
+            && flow->tp_dst == htons(3784)
+            && (!bfd->check_tnl_key || flow->tunnel.tun_id == htonll(0)));
 }
 
 void
@@ -783,7 +821,7 @@ generate_discriminator(void)
     while (!disc) {
         struct bfd *bfd;
 
-        /* 'disc' is by defnition random, so there's no reason to waste time
+        /* '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) {
@@ -875,3 +913,39 @@ bfd_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
 }
+
+
+static void
+bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc,
+                                    const char *argv[], void *aux OVS_UNUSED)
+{
+    const char *forward_str = argv[argc - 1];
+    int forwarding_override;
+    struct bfd *bfd;
+
+    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");
+        return;
+    }
+
+    if (argc > 2) {
+        bfd = bfd_find_by_name(argv[1]);
+        if (!bfd) {
+            unixctl_command_reply_error(conn, "no such BFD object");
+            return;
+        }
+        bfd->forwarding_override = forwarding_override;
+    } else {
+        HMAP_FOR_EACH (bfd, node, &all_bfds) {
+            bfd->forwarding_override = forwarding_override;
+        }
+    }
+
+    unixctl_command_reply(conn, "OK");
+}