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 <et...@nicira.com> --- lib/automake.mk | 2 + lib/bfd.c | 849 ++++++++++++++++++++++++++++++++++++++++++++ lib/bfd.h | 45 +++ lib/odp-util.c | 2 + lib/odp-util.h | 5 +- lib/util.h | 1 + ofproto/ofproto-dpif.c | 65 +++- ofproto/ofproto-provider.h | 16 + ofproto/ofproto.c | 39 ++ ofproto/ofproto.h | 6 + vswitchd/bridge.c | 12 + vswitchd/vswitch.ovsschema | 10 +- vswitchd/vswitch.xml | 78 ++++ 13 files changed, 1124 insertions(+), 6 deletions(-) create mode 100644 lib/bfd.c create mode 100644 lib/bfd.h diff --git a/lib/automake.mk b/lib/automake.mk index ce3edc3..859bb1c 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -12,6 +12,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/aes128.h \ lib/backtrace.c \ lib/backtrace.h \ + lib/bfd.c \ + lib/bfd.h \ lib/bitmap.c \ lib/bitmap.h \ lib/bond.c \ diff --git a/lib/bfd.c b/lib/bfd.c new file mode 100644 index 0000000..cbaefc2 --- /dev/null +++ b/lib/bfd.c @@ -0,0 +1,849 @@ +/* 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 Experimnetal BFD support. + * + * 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. + * + * - Unit tests. + * + * - BFD show into ovs-bugtool. + * + * - 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 + * sessions? Do we ever have random flaps? What's the CPU utilization? + * + * - CFM random VLAN option equivalent. Instead of vlan, randomly modulate the + * UDP source port to get the required entropy. + * + * - 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. */ + +#define BFD_VERSION 1 +#define OVS_BFD_MIN_RX 200000 + +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, + STATE_DOWN = 1, + STATE_INIT = 2, + STATE_UP = 3 +}; + +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 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_SHIFT 6 +#define FLAGS_MASK 0x3f + +struct bfd { + struct list node; /* In 'all_bfds'. */ + 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 disc; /* bfd.LocalDiscr. */ + uint32_t rmt_disc; /* bfd.RemoteDiscr. */ + + 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. */ + + uint16_t udp_src; /* UDP source port. */ +}; + +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); +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 list all_bfds = LIST_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) +{ + return bfd->state == STATE_UP && bfd->rmt_diag == DIAG_NONE; +} + +/* 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 + * handle for the session, or NULL if BFD is not enabled according to 'cfg'. */ +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); + init = true; + } + + if (!smap_get_bool(cfg, "enable", false)) { + if (bfd) { + list_remove(&bfd->node); + free(bfd->name); + free(bfd); + } + return NULL; + } + + if (!bfd) { + bfd = xzalloc(sizeof *bfd); + bfd->name = xstrdup(name); + list_push_back(&all_bfds, &bfd->node); + + bfd->disc = generate_discriminator(); + bfd->diag = DIAG_NONE; + bfd->min_tx = 1000; + bfd->mult = 3; + + /* 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. */ + udp_src++; + udp_src = udp_src < 49152 ? 49152 : udp_src; + bfd->udp_src = udp_src; + + bfd_set_state(bfd, STATE_DOWN, DIAG_NONE); + } + + min_tx = MAX(smap_get_int(cfg, "min_tx", 100), 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 = MAX(smap_get_int(cfg, "min_rx", 1000), 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; +} + +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(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)); + + eth = ofpbuf_put_uninit(p, sizeof *eth); + memcpy(eth->eth_dst, eth_addr_broadcast, sizeof eth->eth_dst); + 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(struct msg)); + ip->ip_ttl = 1; + 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(struct msg)); + + msg = ofpbuf_put_uninit(p, sizeof(struct msg)); + msg->vers_diag = (BFD_VERSION << 5) | bfd->diag; + msg->flags = (bfd->state << 6) | 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 +bfd_should_process_flow(const struct flow *flow) +{ + return (flow->dl_type == htons(ETH_TYPE_IP) + && flow->nw_proto == IPPROTO_UDP + && flow->tp_dst == htons(3784)); +} + +void +bfd_process_packet(struct bfd *bfd, 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. */ + + 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; + } + + flags = msg->flags & FLAGS_MASK; + rmt_state = msg->flags >> STATE_SHIFT; + 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, DIAG_NONE); + } else if (rmt_state == STATE_INIT) { + bfd_set_state(bfd, STATE_UP, DIAG_NONE); + } + break; + case STATE_INIT: + if (rmt_state > STATE_DOWN) { + bfd_set_state(bfd, STATE_UP, DIAG_NONE); + } + 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, "Initiating poll sequence"); + } +} + +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) +{ + return MAX(bfd_min_tx(bfd), 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 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 "); + } + + strncpy(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 State"; + } +} + +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 (level != VLL_DBG && 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 + " min_tx:%"PRIu32"ms min_rx:%"PRIu32"ms min_rx_echo:%" + PRIu32"ms\n\n", + bfd->name, message, p->vers_diag >> VERS_SHIFT, + bfd_diag_str(p->vers_diag & DIAG_MASK), + bfd_state_str(p->flags >> STATE_SHIFT), + p->mult, p->length, bfd_flag_str(p->flags & FLAGS_MASK), + ntohl(p->my_disc), ntohl(p->your_disc), + ntohl(p->min_tx) / 1000, ntohl(p->min_rx) / 1000, + 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; + + disc = random_uint32(); + LIST_FOR_EACH (bfd, node, &all_bfds) { + if (bfd->disc == disc) { + disc = 0; + break; + } + } + } + + return disc; +} + +static struct bfd * +bfd_find(const char *name) +{ + struct bfd *bfd; + + LIST_FOR_EACH (bfd, node, &all_bfds) { + if (!strcmp(bfd->name, name)) { + return bfd; + } + } + return NULL; +} + +static long long int +msec_diff(long long int earlier, long long int later) { + later -= earlier; + return later < 0 ? 0 : later; +} + +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: %lldms from now\n", + msec_diff(time_msec(), bfd->detect_time)); + ds_put_format(ds, "\tNext TX Time: %lldms from now\n", + msec_diff(time_msec(), bfd->next_tx)); + ds_put_format(ds, "\tLast TX Time: %lldms ago\n", + msec_diff(bfd->last_tx, time_msec())); + + 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(argv[1]); + if (!bfd) { + unixctl_command_reply_error(conn, "no such bfd object"); + return; + } + bfd_put_details(&ds, bfd); + } else { + LIST_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); +} diff --git a/lib/bfd.h b/lib/bfd.h new file mode 100644 index 0000000..19ce435 --- /dev/null +++ b/lib/bfd.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2012 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_H +#define BFD_H 1 + +#define BFD_PACKET_LEN 24 +#define BFD_DEST_PORT 3784 + +#include <stdbool.h> +#include <inttypes.h> + +struct bfd; +struct flow; +struct ofpbuf; +struct smap; + +void bfd_wait(const struct bfd *); +void bfd_run(struct bfd *); + +bool bfd_should_send_packet(struct bfd *); +void bfd_put_packet(struct bfd *bfd, struct ofpbuf *packet, + uint8_t eth_src[6]); + +bool bfd_should_process_flow(const struct flow *); +void bfd_process_packet(struct bfd *, const struct ofpbuf *); + +struct bfd *bfd_configure(struct bfd *, const char *name, + const struct smap *smap); + +bool bfd_forwarding(const struct bfd *); +void bfd_get_status(const struct bfd *, struct smap *); + +#endif /* bfd.h */ diff --git a/lib/odp-util.c b/lib/odp-util.c index 80acad3..604701d 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -183,6 +183,8 @@ slow_path_reason_to_string(uint32_t data) return "stp"; case SLOW_IN_BAND: return "in_band"; + case SLOW_BFD: + return "bfd"; case SLOW_CONTROLLER: return "controller"; case SLOW_MATCH: diff --git a/lib/odp-util.h b/lib/odp-util.h index ad0fb30..f195e58 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -165,14 +165,15 @@ enum slow_path_reason { SLOW_LACP = 1 << 1, /* LACP packets need per-packet processing. */ SLOW_STP = 1 << 2, /* STP packets need per-packet processing. */ SLOW_IN_BAND = 1 << 3, /* In-band control needs every packet. */ + SLOW_BFD = 1 << 4, /* BFD packets need per-packet processing. */ /* Mutually exclusive with SLOW_CFM, SLOW_LACP, SLOW_STP. * Could possibly appear with SLOW_IN_BAND. */ - SLOW_CONTROLLER = 1 << 4, /* Packets must go to OpenFlow controller. */ + SLOW_CONTROLLER = 1 << 5, /* Packets must go to OpenFlow controller. */ /* This can appear on its own, or, theoretically at least, along with any * other combination of reasons. */ - SLOW_MATCH = 1 << 5, /* Datapath can't match specifically enough. */ + SLOW_MATCH = 1 << 6, /* Datapath can't match specifically enough. */ }; #endif /* odp-util.h */ diff --git a/lib/util.h b/lib/util.h index b944ec7..6fa42b8 100644 --- a/lib/util.h +++ b/lib/util.h @@ -23,6 +23,7 @@ #include <stddef.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "compiler.h" diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 7400002..18ebbae 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -20,6 +20,7 @@ #include <errno.h> +#include "bfd.h" #include "bond.h" #include "bundle.h" #include "byte-order.h" @@ -525,6 +526,7 @@ struct ofport_dpif { struct ofbundle *bundle; /* Bundle that contains this port, if any. */ struct list bundle_node; /* In struct ofbundle's "ports" list. */ struct cfm *cfm; /* Connectivity Fault Management, if any. */ + struct bfd *bfd; /* BFD, if any. */ tag_type tag; /* Tag associated with this port. */ bool may_enable; /* May be enabled in bonds. */ long long int carrier_seq; /* Carrier status changes. */ @@ -1420,6 +1422,7 @@ run_fast(struct ofproto *ofproto_) struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofport_dpif *ofport; + time_refresh(); HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { port_run_fast(ofport); } @@ -1634,6 +1637,7 @@ port_construct(struct ofport *port_) ofproto->backer->need_revalidate = REV_RECONFIGURE; port->bundle = NULL; port->cfm = NULL; + port->bfd = NULL; port->tag = tag_create_random(); port->may_enable = true; port->stp_port = NULL; @@ -1820,6 +1824,35 @@ get_cfm_status(const struct ofport *ofport_, return false; } } + +static int +set_bfd(struct ofport *ofport_, const struct smap *cfg) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto); + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct bfd *old; + + old = ofport->bfd; + ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), cfg); + if (ofport->bfd != old) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + + return 0; +} + +static int +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; + } +} /* Spanning Tree. */ @@ -2957,6 +2990,15 @@ port_run_fast(struct ofport_dpif *ofport) send_packet(ofport, &packet); ofpbuf_uninit(&packet); } + + if (ofport->bfd && bfd_should_send_packet(ofport->bfd)) { + struct ofpbuf packet; + + ofpbuf_init(&packet, 0); + bfd_put_packet(ofport->bfd, &packet, ofport->up.pp.hw_addr); + send_packet(ofport, &packet); + ofpbuf_uninit(&packet); + } } static void @@ -2987,6 +3029,11 @@ port_run(struct ofport_dpif *ofport) } } + if (ofport->bfd) { + bfd_run(ofport->bfd); + enable = enable && bfd_forwarding(ofport->bfd); + } + if (ofport->bundle) { enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport); if (carrier_changed) { @@ -3011,6 +3058,10 @@ port_wait(struct ofport_dpif *ofport) if (ofport->cfm) { cfm_wait(ofport->cfm); } + + if (ofport->bfd) { + bfd_wait(ofport->bfd); + } } static int @@ -3332,6 +3383,11 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, cfm_process_heartbeat(ofport->cfm, packet); } return SLOW_CFM; + } else if (ofport->bfd && bfd_should_process_flow(flow)) { + if (packet) { + bfd_process_packet(ofport->bfd, packet); + } + return SLOW_BFD; } else if (ofport->bundle && ofport->bundle->lacp && flow->dl_type == htons(ETH_TYPE_LACP)) { if (packet) { @@ -4261,7 +4317,7 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle) &ofproto->subfacets) { long long int cutoff; - cutoff = (subfacet->slow & (SLOW_CFM | SLOW_LACP | SLOW_STP) + cutoff = (subfacet->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP) ? special_cutoff : normal_cutoff); if (subfacet->used < cutoff) { @@ -5578,7 +5634,7 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow, cookie.slow_path.reason = slow; ofpbuf_use_stack(&buf, stub, stub_size); - if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) { + if (slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)) { uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX); odp_put_userspace_action(pid, &cookie, sizeof cookie, &buf); } else { @@ -7900,6 +7956,9 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, case SLOW_STP: ds_put_cstr(ds, "\n\t- Consists of STP packets."); break; + case SLOW_BFD: + ds_put_cstr(ds, "\n\t- Consists of BFD packets."); + break; case SLOW_IN_BAND: ds_put_cstr(ds, "\n\t- Needs in-band special case " "processing."); @@ -8523,6 +8582,8 @@ const struct ofproto_class ofproto_dpif_class = { set_sflow, set_cfm, get_cfm_status, + set_bfd, + get_bfd_status, set_stp, get_stp_status, set_stp_port, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index d8db3ae..be009d9 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -33,6 +33,7 @@ struct match; struct ofpact; struct ofputil_flow_mod; +struct bfd_cfg; /* An OpenFlow switch. * @@ -1130,6 +1131,21 @@ struct ofproto_class { bool (*get_cfm_status)(const struct ofport *ofport, struct ofproto_cfm_status *status); + /* Configures BFD on 'ofport'. + * + * If 'cfg' is NULL, or 'cfg' does not contain the key value pair + * "enable=true", removes BFD from 'ofport'. Otherwise, configures BFD + * according to 'cfg'. + * + * EOPNOTSUPP as a return value indicates that this ofproto_class does not + * support BFD, as does a null pointer. */ + int (*set_bfd)(struct ofport *ofport, const struct smap *cfg); + + /* Populates 'smap' with the status of BFD on 'ofport'. Returns 0 on + * success, or a positive errno. EOPNOTSUPP as a return value indicates + * that this ofproto_class does not support BFD, as does a null pointer. */ + int (*get_bfd_status)(struct ofport *ofport, struct smap *smap); + /* Configures spanning tree protocol (STP) on 'ofproto' using the * settings defined in 's'. * diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 03ca59b..4770f50 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -786,6 +786,45 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port, } } +/* Configures BFD on 'ofp_port' in 'ofproto'. This function has no effect if + * 'ofproto' does not have a port 'ofp_port'. */ +void +ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port, + const struct smap *cfg) +{ + struct ofport *ofport; + int error; + + ofport = ofproto_get_port(ofproto, ofp_port); + if (!ofport) { + VLOG_WARN("%s: cannot configure bfd on nonexistent port %"PRIu16, + ofproto->name, ofp_port); + } + + error = (ofproto->ofproto_class->set_bfd + ? ofproto->ofproto_class->set_bfd(ofport, cfg) + : EOPNOTSUPP); + if (error) { + VLOG_WARN("%s: bfd configuration on port %"PRIu16" (%s) failed (%s)", + ofproto->name, ofp_port, netdev_get_name(ofport->netdev), + strerror(error)); + } +} + +/* Populates 'status' with key value pairs indicating the status of the BFD + * session on 'ofp_port'. This information is intended to be populated in the + * OVS database. Has no effect if 'ofp_port' is not na OpenFlow port in + * 'ofproto'. */ +int +ofproto_port_get_bfd_status(struct ofproto *ofproto, uint16_t ofp_port, + struct smap *status) +{ + struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); + return (ofport && ofproto->ofproto_class->get_bfd_status + ? ofproto->ofproto_class->get_bfd_status(ofport, status) + : EOPNOTSUPP); +} + /* Checks the status of LACP negotiation for 'ofp_port' within ofproto. * Returns 1 if LACP partner information for 'ofp_port' is up-to-date, * 0 if LACP partner information is not current (generally indicating a diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 5960d7b..a2d5cdb 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -33,6 +33,7 @@ extern "C" { #endif +struct bfd_cfg; struct cfm_settings; struct cls_rule; struct netdev; @@ -40,6 +41,7 @@ struct ofproto; struct ofport; struct shash; struct simap; +struct smap; struct netdev_stats; struct ofproto_controller_info { @@ -239,6 +241,10 @@ void ofproto_port_unregister(struct ofproto *, uint16_t ofp_port); void ofproto_port_clear_cfm(struct ofproto *, uint16_t ofp_port); void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port, const struct cfm_settings *); +void ofproto_port_set_bfd(struct ofproto *, uint16_t ofp_port, + const struct smap *cfg); +int ofproto_port_get_bfd_status(struct ofproto *, uint16_t ofp_port, + struct smap *); int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port); int ofproto_port_set_stp(struct ofproto *, uint16_t ofp_port, const struct ofproto_port_stp_settings *); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index f7932c7..a08fd87 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> #include <stdlib.h> +#include "bfd.h" #include "bitmap.h" #include "bond.h" #include "cfm.h" @@ -354,6 +355,7 @@ bridge_init(const char *remote) ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_health); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_opstate); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_bfd_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); @@ -585,6 +587,8 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg) iface_configure_cfm(iface); iface_configure_qos(iface, port->cfg->qos); iface_set_mac(iface); + ofproto_port_set_bfd(br->ofproto, iface->ofp_port, + &iface->cfg->bfd); } } bridge_configure_mirrors(br); @@ -2032,6 +2036,7 @@ refresh_instant_stats(void) HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) { enum netdev_flags flags; + struct smap smap; const char *link_state; int64_t link_resets; int current, error; @@ -2064,6 +2069,13 @@ refresh_instant_stats(void) ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1); iface_refresh_cfm_stats(iface); + + smap_init(&smap); + if (!ofproto_port_get_bfd_status(br->ofproto, iface->ofp_port, + &smap)) { + ovsrec_interface_set_bfd_status(iface->cfg, &smap); + smap_destroy(&smap); + } } } } diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index a7901a8..8214bd1 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "7.0.0", - "cksum": "3537583872 17299", + "version": "7.1.0", + "cksum": "2838277223 17544", "tables": { "Open_vSwitch": { "columns": { @@ -188,6 +188,12 @@ "maxInteger": 65279}, "min": 0, "max": 1}}, + "bfd": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "bfd_status": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, "cfm_mpid": { "type": { "key": {"type": "integer"}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index e1d9372..eef6dac 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1698,6 +1698,84 @@ </column> </group> + <group title="Bidirectional Forwarding Detection (BFD)"> + <p> + BFD, defined in RFC 5880, allows point to point detection of + connectivity failures by occasional transmission of BFD control + messages. It is implemented in Open vSwitch to serve as a more + popular and standards compliant alternative to CFM. + </p> + + <p> + BFD operates by regularly transmitting BFD control messages at a + rate negotiated independently in each direction. Each endpoint + specifies the rate at which it expects to receive control messages, + and the rate at which it's willing to transmit them. An endpoint + which fails to receive BFD control messages for a period of three + times the expected reception rate, will signal a connectivity + fault. In the case of a unidirectional connectivity issue, the + system not receiving BFD control messages will signal the problem + to its peer in the messages is transmists. + </p> + + <p> + The Open vSwitch implementation of BFD aims comply faithfully with + the requirements put forth in RFC 5880. Currently, the only known + omission is "Demand Mode", which we hope to include in future. + Open vSwitch does not implement the optional Authentication or + "Echo Mode" features. + </p> + + <column name="bfd" key="min_rx"> + The minimum rate, in milliseconds, at which this BFD session is + willing to receive BFD control messages. The actual rate may slower + if the remote endpoint isn't willing to transmit as quickly as + specified. Defaults to <code>1000</code>. + </column> + + <column name="bfd" key="min_tx"> + The minimum rate, in milliseconds, at which this BFD session is + willing to transmit BFD control messages. The actual rate may be + slower if the remote endpoint isn't willing to receive as quickly as + specified. Defaults to <code>100</code>. + </column> + + <column name="bfd" key="cpath_down"> + Concatenated path down may be used when the local system should not + have traffic forwarded to it for some reason other than a connectivty + failure on the interface being monitored. The local BFD session will + notify the remote session of the connectivity problem, at which time + the remote session may choose not to forward traffic. Defaults to + <code>false</code>. + </column> + + <column name="bfd_status" key="state"> + State of the BFD session. One of <code>ADMIN_DOWN</code>, + <code>DOWN</code>, <code>INIT</code>, or <code>UP</code>. + </column> + + <column name="bfd_status" key="forwarding"> + True if the BFD session believes this <ref table="Interface"/> may be + used to forward traffic. Typically this means the local session is + up, and the remote system isn't signalling a problem such as + concatenated path down. + </column> + + <column name="bfd_status" key="diagnostic"> + A short message indicating what the BFD session thinks is wrong in + case of a problem. + </column> + + <column name="bfd_status" key="remote state"> + State of the remote endpoint's BFD session. + </column> + + <column name="bfd_status" key="remote diagnostic"> + A short message indicating what the remote endpoint's BFD session + thinks is wrong in case of a problem. + </column> + </group> + <group title="Connectivity Fault Management"> <p> 802.1ag Connectivity Fault Management (CFM) allows a group of -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev