bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
/* Copyright (c) 2013 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 <arpa/inet.h>
|
|
|
|
|
|
|
|
|
|
#include "csum.h"
|
|
|
|
|
#include "dpif.h"
|
|
|
|
|
#include "dynamic-string.h"
|
|
|
|
|
#include "flow.h"
|
|
|
|
|
#include "hash.h"
|
|
|
|
|
#include "hmap.h"
|
|
|
|
|
#include "list.h"
|
|
|
|
|
#include "netlink.h"
|
|
|
|
|
#include "odp-util.h"
|
|
|
|
|
#include "ofpbuf.h"
|
|
|
|
|
#include "openvswitch/types.h"
|
|
|
|
|
#include "packets.h"
|
|
|
|
|
#include "poll-loop.h"
|
|
|
|
|
#include "random.h"
|
|
|
|
|
#include "smap.h"
|
|
|
|
|
#include "timeval.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.
|
|
|
|
|
*
|
|
|
|
|
* - BFD show into ovs-bugtool.
|
|
|
|
|
*
|
|
|
|
|
* - Set TOS/PCP on inner BFD frame, and 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. */
|
|
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
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. */
|
2013-06-18 16:40:19 -07:00
|
|
|
|
|
|
|
|
|
int ref_cnt;
|
2013-06-29 10:09:02 +00:00
|
|
|
|
int forwarding_override; /* Manual override of 'forwarding' status. */
|
2013-07-16 09:58:42 +00:00
|
|
|
|
bool check_tnl_key; /* Verify tunnel key of inbound packets? */
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool bfd_in_poll(const struct bfd *);
|
|
|
|
|
static void bfd_poll(struct bfd *bfd);
|
|
|
|
|
static const char *bfd_diag_str(enum diag);
|
|
|
|
|
static const char *bfd_state_str(enum state);
|
|
|
|
|
static long long int bfd_min_tx(const struct bfd *);
|
|
|
|
|
static long long int bfd_tx_interval(const struct bfd *);
|
|
|
|
|
static long long int bfd_rx_interval(const struct bfd *);
|
|
|
|
|
static void bfd_set_next_tx(struct bfd *);
|
|
|
|
|
static void bfd_set_state(struct bfd *, enum state, enum diag);
|
|
|
|
|
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);
|
2013-06-29 10:09:02 +00:00
|
|
|
|
static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
|
|
|
|
|
int argc, const char *argv[],
|
|
|
|
|
void *aux OVS_UNUSED);
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
static void log_msg(enum vlog_level, const struct msg *, const char *message,
|
|
|
|
|
const struct bfd *);
|
|
|
|
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20);
|
|
|
|
|
static struct hmap all_bfds = HMAP_INITIALIZER(&all_bfds);
|
|
|
|
|
|
|
|
|
|
/* 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(const struct bfd *bfd)
|
|
|
|
|
{
|
2013-06-29 10:09:02 +00:00
|
|
|
|
if (bfd->forwarding_override != -1) {
|
|
|
|
|
return bfd->forwarding_override == 1;
|
|
|
|
|
}
|
|
|
|
|
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
return bfd->state == STATE_UP
|
|
|
|
|
&& bfd->rmt_diag != DIAG_PATH_DOWN
|
|
|
|
|
&& bfd->rmt_diag != DIAG_CPATH_DOWN
|
|
|
|
|
&& bfd->rmt_diag != DIAG_RCPATH_DOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
smap_add(smap, "forwarding", bfd_forwarding(bfd) ? "true" : "false");
|
|
|
|
|
smap_add(smap, "state", bfd_state_str(bfd->state));
|
|
|
|
|
smap_add(smap, "diagnostic", bfd_diag_str(bfd->diag));
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2013-05-28 12:35:06 -07:00
|
|
|
|
* handle for the session, or NULL if BFD is not enabled according to 'cfg'.
|
|
|
|
|
* Also returns NULL if cfg is NULL. */
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
struct bfd *
|
|
|
|
|
bfd_configure(struct bfd *bfd, const char *name,
|
|
|
|
|
const struct smap *cfg)
|
|
|
|
|
{
|
|
|
|
|
static uint16_t udp_src = 0;
|
|
|
|
|
static bool init = false;
|
|
|
|
|
|
|
|
|
|
long long int min_tx, min_rx;
|
|
|
|
|
bool cpath_down;
|
|
|
|
|
|
|
|
|
|
if (!init) {
|
|
|
|
|
unixctl_command_register("bfd/show", "[interface]", 0, 1,
|
|
|
|
|
bfd_unixctl_show, NULL);
|
2013-06-29 10:09:02 +00:00
|
|
|
|
unixctl_command_register("bfd/set-forwarding",
|
|
|
|
|
"[interface] normal|false|true", 1, 2,
|
|
|
|
|
bfd_unixctl_set_forwarding_override, NULL);
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
init = true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-28 12:35:06 -07:00
|
|
|
|
if (!cfg || !smap_get_bool(cfg, "enable", false)) {
|
2013-06-18 16:40:19 -07:00
|
|
|
|
bfd_unref(bfd);
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bfd) {
|
|
|
|
|
bfd = xzalloc(sizeof *bfd);
|
|
|
|
|
bfd->name = xstrdup(name);
|
2013-06-29 10:09:02 +00:00
|
|
|
|
bfd->forwarding_override = -1;
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
bfd->disc = generate_discriminator();
|
|
|
|
|
hmap_insert(&all_bfds, &bfd->node, bfd->disc);
|
|
|
|
|
|
|
|
|
|
bfd->diag = DIAG_NONE;
|
|
|
|
|
bfd->min_tx = 1000;
|
|
|
|
|
bfd->mult = 3;
|
2013-06-18 16:40:19 -07:00
|
|
|
|
bfd->ref_cnt = 1;
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
bfd->udp_src = (udp_src++ % 16384) + 49152;
|
|
|
|
|
|
|
|
|
|
bfd_set_state(bfd, STATE_DOWN, DIAG_NONE);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-16 09:58:42 +00:00
|
|
|
|
bfd->check_tnl_key = smap_get_bool(cfg, "check_tnl_key", false);
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
min_tx = smap_get_int(cfg, "min_tx", 100);
|
|
|
|
|
min_tx = MAX(min_tx, 100);
|
|
|
|
|
if (bfd->cfg_min_tx != min_tx) {
|
|
|
|
|
bfd->cfg_min_tx = min_tx;
|
|
|
|
|
if (bfd->state != STATE_UP
|
|
|
|
|
|| (!bfd_in_poll(bfd) && bfd->cfg_min_tx < bfd->min_tx)) {
|
|
|
|
|
bfd->min_tx = bfd->cfg_min_tx;
|
|
|
|
|
}
|
|
|
|
|
bfd_poll(bfd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
min_rx = smap_get_int(cfg, "min_rx", 1000);
|
|
|
|
|
min_rx = MAX(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;
|
|
|
|
|
}
|
|
|
|
|
bfd_poll(bfd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cpath_down = smap_get_bool(cfg, "cpath_down", false);
|
|
|
|
|
if (bfd->cpath_down != cpath_down) {
|
|
|
|
|
bfd->cpath_down = cpath_down;
|
|
|
|
|
if (bfd->diag == DIAG_NONE || bfd->diag == DIAG_CPATH_DOWN) {
|
|
|
|
|
bfd_set_state(bfd, bfd->state, DIAG_NONE);
|
|
|
|
|
}
|
|
|
|
|
bfd_poll(bfd);
|
|
|
|
|
}
|
|
|
|
|
return bfd;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 16:40:19 -07:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
void
|
|
|
|
|
bfd_wait(const struct bfd *bfd)
|
|
|
|
|
{
|
|
|
|
|
if (bfd->flags & FLAG_FINAL) {
|
|
|
|
|
poll_immediate_wake();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll_timer_wait_until(bfd->next_tx);
|
|
|
|
|
if (bfd->state > STATE_DOWN) {
|
|
|
|
|
poll_timer_wait_until(bfd->detect_time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bfd_run(struct bfd *bfd)
|
|
|
|
|
{
|
|
|
|
|
if (bfd->state > STATE_DOWN && time_msec() >= bfd->detect_time) {
|
|
|
|
|
bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bfd->min_tx != bfd->cfg_min_tx || bfd->min_rx != bfd->cfg_min_rx) {
|
|
|
|
|
bfd_poll(bfd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
bfd_should_send_packet(const struct bfd *bfd)
|
|
|
|
|
{
|
|
|
|
|
return bfd->flags & FLAG_FINAL || time_msec() >= bfd->next_tx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
|
|
|
|
|
uint8_t eth_src[ETH_ADDR_LEN])
|
|
|
|
|
{
|
|
|
|
|
long long int min_tx, min_rx;
|
|
|
|
|
struct udp_header *udp;
|
|
|
|
|
struct eth_header *eth;
|
|
|
|
|
struct ip_header *ip;
|
|
|
|
|
struct msg *msg;
|
|
|
|
|
|
|
|
|
|
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_WARN("%s: long delay of %lldms (expected %lldms) sending BFD"
|
|
|
|
|
" control message", bfd->name, delay, interval);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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_dst, eth_addr_broadcast, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(eth->eth_src, eth_src, 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 = 255;
|
|
|
|
|
ip->ip_proto = IPPROTO_UDP;
|
|
|
|
|
ip->ip_src = htonl(0xA9FE0100); /* 169.254.1.0 Link Local. */
|
|
|
|
|
ip->ip_dst = htonl(0xA9FE0101); /* 169.254.1.1 Link Local. */
|
|
|
|
|
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);
|
|
|
|
|
msg->vers_diag = (BFD_VERSION << 5) | bfd->diag;
|
|
|
|
|
msg->flags = (bfd->state & STATE_MASK) | bfd->flags;
|
|
|
|
|
|
|
|
|
|
msg->mult = bfd->mult;
|
|
|
|
|
msg->length = BFD_PACKET_LEN;
|
|
|
|
|
msg->my_disc = htonl(bfd->disc);
|
|
|
|
|
msg->your_disc = htonl(bfd->rmt_disc);
|
|
|
|
|
msg->min_rx_echo = htonl(0);
|
|
|
|
|
|
|
|
|
|
if (bfd_in_poll(bfd)) {
|
|
|
|
|
min_tx = bfd->poll_min_tx;
|
|
|
|
|
min_rx = bfd->poll_min_rx;
|
|
|
|
|
} else {
|
|
|
|
|
min_tx = bfd_min_tx(bfd);
|
|
|
|
|
min_rx = bfd->min_rx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg->min_tx = htonl(min_tx * 1000);
|
|
|
|
|
msg->min_rx = htonl(min_rx * 1000);
|
|
|
|
|
|
|
|
|
|
bfd->flags &= ~FLAG_FINAL;
|
|
|
|
|
|
|
|
|
|
log_msg(VLL_DBG, msg, "Sending BFD Message", bfd);
|
|
|
|
|
|
|
|
|
|
bfd->last_tx = time_msec();
|
|
|
|
|
bfd_set_next_tx(bfd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2013-07-16 09:58:42 +00:00
|
|
|
|
bfd_should_process_flow(const struct bfd *bfd, const struct flow *flow,
|
|
|
|
|
struct flow_wildcards *wc)
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
{
|
2013-06-17 18:07:33 -07:00
|
|
|
|
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
|
|
|
|
|
memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
|
2013-07-16 09:58:42 +00:00
|
|
|
|
if (bfd->check_tnl_key) {
|
|
|
|
|
memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
|
|
|
|
|
}
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
return (flow->dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& flow->nw_proto == IPPROTO_UDP
|
2013-07-16 09:58:42 +00:00
|
|
|
|
&& flow->tp_dst == htons(3784)
|
|
|
|
|
&& (!bfd->check_tnl_key || flow->tunnel.tun_id == htonl(0)));
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bfd_process_packet(struct bfd *bfd, const struct flow *flow,
|
|
|
|
|
const struct ofpbuf *p)
|
|
|
|
|
{
|
|
|
|
|
uint32_t rmt_min_rx, pkt_your_disc;
|
|
|
|
|
enum state rmt_state;
|
|
|
|
|
enum flags flags;
|
|
|
|
|
uint8_t version;
|
|
|
|
|
struct msg *msg;
|
|
|
|
|
|
|
|
|
|
/* This function is designed to follow section RFC 5880 6.8.6 closely. */
|
|
|
|
|
|
|
|
|
|
if (flow->nw_ttl != 255) {
|
|
|
|
|
/* XXX Should drop in the kernel to prevent DOS. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg = ofpbuf_at(p, (uint8_t *)p->l7 - (uint8_t *)p->data, BFD_PACKET_LEN);
|
|
|
|
|
if (!msg) {
|
|
|
|
|
VLOG_INFO_RL(&rl, "%s: Received unparseable BFD control message.",
|
|
|
|
|
bfd->name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* RFC 5880 Section 6.8.6
|
|
|
|
|
* If the Length field is greater than the payload of the encapsulating
|
|
|
|
|
* protocol, the packet MUST be discarded.
|
|
|
|
|
*
|
|
|
|
|
* Note that we make this check implicity. Above we use ofpbuf_at() to
|
|
|
|
|
* ensure that there are at least BFD_PACKET_LEN bytes in the payload of
|
|
|
|
|
* the encapsulating protocol. Below we require msg->length to be exactly
|
|
|
|
|
* BFD_PACKET_LEN bytes. */
|
|
|
|
|
|
|
|
|
|
flags = msg->flags & FLAGS_MASK;
|
|
|
|
|
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);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msg->length != BFD_PACKET_LEN) {
|
|
|
|
|
log_msg(VLL_WARN, msg, "Unexpected length", bfd);
|
|
|
|
|
if (msg->length < BFD_PACKET_LEN) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!msg->mult) {
|
|
|
|
|
log_msg(VLL_WARN, msg, "Zero multiplier", bfd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_MULTIPOINT) {
|
|
|
|
|
log_msg(VLL_WARN, msg, "Unsupported multipoint flag", bfd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!msg->my_disc) {
|
|
|
|
|
log_msg(VLL_WARN, msg, "NULL my_disc", bfd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
* 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);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (rmt_state > STATE_DOWN) {
|
|
|
|
|
log_msg(VLL_WARN, msg, "Null your_disc", bfd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bfd->rmt_disc = ntohl(msg->my_disc);
|
|
|
|
|
bfd->rmt_state = rmt_state;
|
|
|
|
|
bfd->rmt_flags = flags;
|
|
|
|
|
bfd->rmt_diag = msg->vers_diag & DIAG_MASK;
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_FINAL && bfd_in_poll(bfd)) {
|
|
|
|
|
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) {
|
|
|
|
|
/* RFC 5880 Section 6.5
|
|
|
|
|
* When the other system receives a Poll, it immediately transmits a
|
|
|
|
|
* BFD Control packet with the Final (F) bit set, independent of any
|
|
|
|
|
* periodic BFD Control packets it may be sending
|
|
|
|
|
* (see section 6.8.7). */
|
|
|
|
|
bfd->flags &= ~FLAG_POLL;
|
|
|
|
|
bfd->flags |= FLAG_FINAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1);
|
|
|
|
|
if (bfd->rmt_min_rx != rmt_min_rx) {
|
|
|
|
|
bfd->rmt_min_rx = rmt_min_rx;
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
if (bfd->state == STATE_ADMIN_DOWN) {
|
|
|
|
|
VLOG_DBG_RL(&rl, "Administratively down, dropping control message.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rmt_state == STATE_ADMIN_DOWN) {
|
|
|
|
|
if (bfd->state != STATE_DOWN) {
|
|
|
|
|
bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
switch (bfd->state) {
|
|
|
|
|
case STATE_DOWN:
|
|
|
|
|
if (rmt_state == STATE_DOWN) {
|
|
|
|
|
bfd_set_state(bfd, STATE_INIT, bfd->diag);
|
|
|
|
|
} else if (rmt_state == STATE_INIT) {
|
|
|
|
|
bfd_set_state(bfd, STATE_UP, bfd->diag);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STATE_INIT:
|
|
|
|
|
if (rmt_state > STATE_DOWN) {
|
|
|
|
|
bfd_set_state(bfd, STATE_UP, bfd->diag);
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STATE_ADMIN_DOWN:
|
|
|
|
|
default:
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helpers. */
|
|
|
|
|
static bool
|
|
|
|
|
bfd_in_poll(const struct bfd *bfd)
|
|
|
|
|
{
|
|
|
|
|
return (bfd->flags & FLAG_POLL) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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->cfg_min_rx;
|
|
|
|
|
bfd->flags |= FLAG_POLL;
|
|
|
|
|
bfd->next_tx = 0;
|
|
|
|
|
VLOG_INFO_RL(&rl, "%s: Initiating poll sequence", bfd->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static long long int
|
|
|
|
|
bfd_min_tx(const struct bfd *bfd)
|
|
|
|
|
{
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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 * 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";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_MULTIPOINT) {
|
|
|
|
|
ds_put_cstr(&ds, "multipoint ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_DEMAND) {
|
|
|
|
|
ds_put_cstr(&ds, "demand ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_AUTH) {
|
|
|
|
|
ds_put_cstr(&ds, "auth ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_CTL) {
|
|
|
|
|
ds_put_cstr(&ds, "ctl ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_FINAL) {
|
|
|
|
|
ds_put_cstr(&ds, "final ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FLAG_POLL) {
|
|
|
|
|
ds_put_cstr(&ds, "poll ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
case STATE_ADMIN_DOWN: return "admin_down";
|
|
|
|
|
case STATE_DOWN: return "down";
|
|
|
|
|
case STATE_INIT: return "init";
|
|
|
|
|
case STATE_UP: return "up";
|
|
|
|
|
default: return "invalid";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
bfd_diag_str(enum diag diag) {
|
|
|
|
|
switch (diag) {
|
|
|
|
|
case DIAG_NONE: return "No Diagnostic";
|
|
|
|
|
case DIAG_EXPIRED: return "Control Detection Time Expired";
|
|
|
|
|
case DIAG_ECHO_FAILED: return "Echo Function Failed";
|
|
|
|
|
case DIAG_RMT_DOWN: return "Neighbor Signaled Session Down";
|
|
|
|
|
case DIAG_FWD_RESET: return "Forwarding Plane Reset";
|
|
|
|
|
case DIAG_PATH_DOWN: return "Path Down";
|
|
|
|
|
case DIAG_CPATH_DOWN: return "Concatenated Path Down";
|
|
|
|
|
case DIAG_ADMIN_DOWN: return "Administratively Down";
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (diag == DIAG_NONE && 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2013-06-26 13:50:53 -07:00
|
|
|
|
/* 'disc' is by definition random, so there's no reason to waste time
|
bfd: Implement Bidirectional Forwarding Detection.
Traditionally, Open vSwitch has used a variant of 802.1ag "CFM" for
interface liveness detection. This has served us well until now,
but has several serious drawbacks which have steadily become more
inconvenient. First, the 802.1ag standard does not implement
several useful features forcing us to (optionally) break
compatibility. Second, 802.1.ag is not particularly popular
outside of carrier grade networking equipment. Third, 802.1ag is
simply quite awkward.
In an effort to solve the aforementioned problems, this patch
implements BFD which is ubiquitous, well designed, straight
forward, and implements required features in a standard way. The
initial cut of the protocol focuses on getting the basics of the
specification correct, leaving performance optimizations, and
advanced features as future work. The protocol should be
considered experimental pending future testing.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
2012-06-08 12:42:42 -07:00
|
|
|
|
* 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)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "\tForwarding: %s\n",
|
|
|
|
|
bfd_forwarding(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)
|
|
|
|
|
{
|
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
struct bfd *bfd;
|
|
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
|
bfd = bfd_find_by_name(argv[1]);
|
|
|
|
|
if (!bfd) {
|
|
|
|
|
unixctl_command_reply_error(conn, "no such bfd object");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
2013-06-29 10:09:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
}
|