2010-11-15 16:20:01 -08:00
|
|
|
/*
|
2015-02-20 14:17:10 -05:00
|
|
|
* Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
|
2010-11-15 16:20:01 -08:00
|
|
|
*
|
|
|
|
* 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 "cfm.h"
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2011-09-01 13:28:25 -07:00
|
|
|
#include "byte-order.h"
|
2013-12-13 03:33:47 +00:00
|
|
|
#include "connectivity.h"
|
2015-02-22 03:21:09 -08:00
|
|
|
#include "dp-packet.h"
|
2016-03-03 10:20:46 -08:00
|
|
|
#include "openvswitch/dynamic-string.h"
|
2010-11-15 16:20:01 -08:00
|
|
|
#include "flow.h"
|
|
|
|
#include "hash.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
#include "openvswitch/hmap.h"
|
2013-05-15 14:31:06 -07:00
|
|
|
#include "netdev.h"
|
2015-03-29 15:49:29 -07:00
|
|
|
#include "ovs-atomic.h"
|
2010-11-15 16:20:01 -08:00
|
|
|
#include "packets.h"
|
2017-11-03 13:53:53 +08:00
|
|
|
#include "openvswitch/poll-loop.h"
|
2012-03-09 18:16:20 -08:00
|
|
|
#include "random.h"
|
2013-12-13 03:33:47 +00:00
|
|
|
#include "seq.h"
|
2011-03-31 13:54:44 -07:00
|
|
|
#include "timer.h"
|
2010-11-15 16:20:01 -08:00
|
|
|
#include "timeval.h"
|
2011-05-13 16:53:24 -07:00
|
|
|
#include "unixctl.h"
|
2014-12-15 14:10:38 +01:00
|
|
|
#include "openvswitch/vlog.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
#include "util.h"
|
2010-11-15 16:20:01 -08:00
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(cfm);
|
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
#define CFM_MAX_RMPS 256
|
|
|
|
|
2011-05-13 18:11:43 -07:00
|
|
|
/* Ethernet destination address of CCM packets. */
|
2017-11-28 15:32:24 -08:00
|
|
|
static const struct eth_addr eth_addr_ccm = ETH_ADDR_C(01,80,c2,00,00,30);
|
|
|
|
static const struct eth_addr eth_addr_ccm_x = ETH_ADDR_C(01,23,20,00,00,30);
|
2011-05-13 18:11:43 -07:00
|
|
|
|
|
|
|
#define ETH_TYPE_CFM 0x8902
|
|
|
|
|
|
|
|
/* A 'ccm' represents a Continuity Check Message from the 802.1ag
|
|
|
|
* specification. Continuity Check Messages are broadcast periodically so that
|
2011-10-07 15:43:43 -07:00
|
|
|
* hosts can determine whom they have connectivity to.
|
|
|
|
*
|
|
|
|
* The minimum length of a CCM as specified by IEEE 802.1ag is 75 bytes.
|
|
|
|
* Previous versions of Open vSwitch generated 74-byte CCM messages, so we
|
|
|
|
* accept such messages too. */
|
|
|
|
#define CCM_LEN 75
|
|
|
|
#define CCM_ACCEPT_LEN 74
|
2011-05-13 18:11:43 -07:00
|
|
|
#define CCM_MAID_LEN 48
|
|
|
|
#define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */
|
2011-05-17 16:07:58 -07:00
|
|
|
#define CCM_RDI_MASK 0x80
|
2012-04-05 14:30:23 -07:00
|
|
|
#define CFM_HEALTH_INTERVAL 6
|
2013-07-17 12:08:25 -07:00
|
|
|
|
|
|
|
OVS_PACKED(
|
2011-05-13 18:11:43 -07:00
|
|
|
struct ccm {
|
2012-06-20 16:04:27 -07:00
|
|
|
uint8_t mdlevel_version; /* MD Level and Version */
|
|
|
|
uint8_t opcode;
|
|
|
|
uint8_t flags;
|
|
|
|
uint8_t tlv_offset;
|
2011-05-13 18:11:43 -07:00
|
|
|
ovs_be32 seq;
|
|
|
|
ovs_be16 mpid;
|
2012-06-20 16:04:27 -07:00
|
|
|
uint8_t maid[CCM_MAID_LEN];
|
2011-08-30 17:37:15 -07:00
|
|
|
|
|
|
|
/* Defined by ITU-T Y.1731 should be zero */
|
|
|
|
ovs_be16 interval_ms_x; /* Transmission interval in ms. */
|
2011-09-01 13:28:25 -07:00
|
|
|
ovs_be64 mpid64; /* MPID in extended mode. */
|
2011-10-06 22:43:05 -07:00
|
|
|
uint8_t opdown; /* Operationally down. */
|
2012-06-20 16:04:27 -07:00
|
|
|
uint8_t zero[5];
|
2011-10-07 15:43:43 -07:00
|
|
|
|
|
|
|
/* TLV space. */
|
|
|
|
uint8_t end_tlv;
|
2013-07-17 12:08:25 -07:00
|
|
|
});
|
2011-05-13 18:11:43 -07:00
|
|
|
BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm));
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
struct cfm {
|
2013-05-15 14:31:06 -07:00
|
|
|
const char *name; /* Name of this CFM object. */
|
2011-05-23 16:05:41 -07:00
|
|
|
struct hmap_node hmap_node; /* Node in all_cfms list. */
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2013-05-15 14:31:06 -07:00
|
|
|
struct netdev *netdev;
|
|
|
|
uint64_t rx_packets; /* Packets received by 'netdev'. */
|
|
|
|
|
2011-09-01 13:28:25 -07:00
|
|
|
uint64_t mpid;
|
2013-05-15 14:31:06 -07:00
|
|
|
bool demand; /* Demand mode. */
|
2013-06-04 17:35:36 -05:00
|
|
|
bool booted; /* A full fault interval has occurred. */
|
2012-04-04 17:36:00 -07:00
|
|
|
enum cfm_fault_reason fault; /* Connectivity fault status. */
|
2013-06-04 17:35:36 -05:00
|
|
|
enum cfm_fault_reason recv_fault; /* Bit mask of faults occurring on
|
2012-04-04 17:36:00 -07:00
|
|
|
receive. */
|
2011-10-06 22:43:05 -07:00
|
|
|
bool opup; /* Operational State. */
|
|
|
|
bool remote_opup; /* Remote Operational State. */
|
2011-05-13 15:37:23 -07:00
|
|
|
|
2012-01-26 18:58:51 -08:00
|
|
|
int fault_override; /* Manual override of 'fault' status.
|
|
|
|
Ignored if negative. */
|
|
|
|
|
2011-05-13 16:53:24 -07:00
|
|
|
uint32_t seq; /* The sequence number of our last CCM. */
|
2010-11-15 16:20:01 -08:00
|
|
|
uint8_t ccm_interval; /* The CCM transmission interval. */
|
|
|
|
int ccm_interval_ms; /* 'ccm_interval' in milliseconds. */
|
2012-03-09 18:16:20 -08:00
|
|
|
uint16_t ccm_vlan; /* Vlan tag of CCM PDUs. CFM_RANDOM_VLAN if
|
|
|
|
random. */
|
2012-02-02 15:48:13 -08:00
|
|
|
uint8_t ccm_pcp; /* Priority of CCM PDUs. */
|
2011-05-12 16:08:52 -07:00
|
|
|
uint8_t maid[CCM_MAID_LEN]; /* The MAID of this CFM. */
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-03-31 13:54:44 -07:00
|
|
|
struct timer tx_timer; /* Send CCM when expired. */
|
|
|
|
struct timer fault_timer; /* Check for faults when expired. */
|
2011-05-12 15:28:43 -07:00
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
struct hmap remote_mps; /* Remote MPs. */
|
2011-08-26 14:54:34 -07:00
|
|
|
|
|
|
|
/* Result of cfm_get_remote_mpids(). Updated only during fault check to
|
|
|
|
* avoid flapping. */
|
|
|
|
uint64_t *rmps_array; /* Cache of remote_mps. */
|
|
|
|
size_t rmps_array_len; /* Number of rmps in 'rmps_array'. */
|
2012-04-05 14:30:23 -07:00
|
|
|
|
|
|
|
int health; /* Percentage of the number of CCM frames
|
|
|
|
received. */
|
|
|
|
int health_interval; /* Number of fault_intervals since health was
|
|
|
|
recomputed. */
|
2012-06-19 13:24:43 -07:00
|
|
|
long long int last_tx; /* Last CCM transmission time. */
|
2013-06-18 16:31:43 -07:00
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
/* These bools are atomic to allow readers to check their values
|
|
|
|
* without taking 'mutex'. Such readers do not assume the values they
|
|
|
|
* read are synchronized with any other members. */
|
2013-07-23 13:09:38 -07:00
|
|
|
atomic_bool check_tnl_key; /* Verify the tunnel key of inbound packets? */
|
|
|
|
atomic_bool extended; /* Extended mode. */
|
2013-12-27 19:39:24 -08:00
|
|
|
struct ovs_refcount ref_cnt;
|
2013-10-22 05:16:23 +00:00
|
|
|
|
|
|
|
uint64_t flap_count; /* Count the flaps since boot. */
|
2014-04-03 10:20:44 -07:00
|
|
|
|
|
|
|
/* True when the variables returned by cfm_get_*() are changed
|
|
|
|
* since last check. */
|
|
|
|
bool status_changed;
|
2014-04-09 10:58:54 -07:00
|
|
|
|
|
|
|
/* When 'cfm->demand' is set, at least one ccm is required to be received
|
|
|
|
* every 100 * cfm_interval. If ccm is not received within this interval,
|
|
|
|
* even if data packets are received, the cfm fault will be set. */
|
|
|
|
struct timer demand_rx_ccm_t;
|
2011-05-12 15:28:43 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Remote MPs represent foreign network entities that are configured to have
|
|
|
|
* the same MAID as this CFM instance. */
|
|
|
|
struct remote_mp {
|
2011-09-01 13:28:25 -07:00
|
|
|
uint64_t mpid; /* The Maintenance Point ID of this 'remote_mp'. */
|
2011-05-12 15:28:43 -07:00
|
|
|
struct hmap_node node; /* Node in 'remote_mps' map. */
|
|
|
|
|
|
|
|
bool recv; /* CCM was received since last fault check. */
|
2011-10-06 22:43:05 -07:00
|
|
|
bool opup; /* Operational State. */
|
2012-01-16 16:42:08 -08:00
|
|
|
uint32_t seq; /* Most recently received sequence number. */
|
2012-04-05 14:30:23 -07:00
|
|
|
uint8_t num_health_ccm; /* Number of received ccm frames every
|
|
|
|
CFM_HEALTH_INTERVAL * 'fault_interval'. */
|
2012-07-18 20:39:54 -07:00
|
|
|
long long int last_rx; /* Last CCM reception time. */
|
2012-04-05 14:30:23 -07:00
|
|
|
|
2010-11-15 16:20:01 -08:00
|
|
|
};
|
|
|
|
|
2012-01-16 16:42:08 -08:00
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 30);
|
2013-07-23 13:09:38 -07:00
|
|
|
|
|
|
|
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
|
|
|
|
static struct hmap all_cfms__ = HMAP_INITIALIZER(&all_cfms__);
|
|
|
|
static struct hmap *const all_cfms OVS_GUARDED_BY(mutex) = &all_cfms__;
|
2011-05-13 16:53:24 -07:00
|
|
|
|
2011-12-02 15:29:19 -08:00
|
|
|
static unixctl_cb_func cfm_unixctl_show;
|
2012-01-26 18:58:51 -08:00
|
|
|
static unixctl_cb_func cfm_unixctl_set_fault;
|
2011-05-11 18:13:35 -07:00
|
|
|
|
2013-05-15 14:31:06 -07:00
|
|
|
static uint64_t
|
2013-08-08 15:14:19 -07:00
|
|
|
cfm_rx_packets(const struct cfm *cfm) OVS_REQUIRES(mutex)
|
2013-05-15 14:31:06 -07:00
|
|
|
{
|
|
|
|
struct netdev_stats stats;
|
|
|
|
|
|
|
|
if (!netdev_get_stats(cfm->netdev, &stats)) {
|
|
|
|
return stats.rx_packets;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-28 14:55:11 -07:00
|
|
|
static struct eth_addr
|
2013-08-20 10:46:15 -07:00
|
|
|
cfm_ccm_addr(struct cfm *cfm)
|
2011-08-30 17:37:06 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
bool extended;
|
2014-08-29 10:34:52 -07:00
|
|
|
|
|
|
|
atomic_read_relaxed(&cfm->extended, &extended);
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
return extended ? eth_addr_ccm_x : eth_addr_ccm;
|
2011-08-30 17:37:06 -07:00
|
|
|
}
|
|
|
|
|
2012-02-07 14:35:09 -08:00
|
|
|
/* Returns the string representation of the given cfm_fault_reason 'reason'. */
|
|
|
|
const char *
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_fault_reason_to_str(int reason)
|
|
|
|
{
|
2012-02-07 14:35:09 -08:00
|
|
|
switch (reason) {
|
|
|
|
#define CFM_FAULT_REASON(NAME, STR) case CFM_FAULT_##NAME: return #STR;
|
|
|
|
CFM_FAULT_REASONS
|
|
|
|
#undef CFM_FAULT_REASON
|
|
|
|
default: return "<unknown>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-07-18 20:39:54 -07:00
|
|
|
ds_put_cfm_fault(struct ds *ds, int fault)
|
2012-02-07 14:35:09 -08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CFM_FAULT_N_REASONS; i++) {
|
|
|
|
int reason = 1 << i;
|
|
|
|
|
2012-07-18 20:39:54 -07:00
|
|
|
if (fault & reason) {
|
|
|
|
ds_put_format(ds, "%s ", cfm_fault_reason_to_str(reason));
|
2012-02-07 14:35:09 -08:00
|
|
|
}
|
|
|
|
}
|
2012-07-18 20:39:54 -07:00
|
|
|
|
|
|
|
ds_chomp(ds, ' ');
|
2012-02-07 14:35:09 -08:00
|
|
|
}
|
|
|
|
|
2011-05-12 16:08:52 -07:00
|
|
|
static void
|
2013-08-08 15:14:19 -07:00
|
|
|
cfm_generate_maid(struct cfm *cfm) OVS_REQUIRES(mutex)
|
2011-05-12 16:08:52 -07:00
|
|
|
{
|
2011-05-25 18:23:31 -07:00
|
|
|
const char *ovs_md_name = "ovs";
|
|
|
|
const char *ovs_ma_name = "ovs";
|
2011-05-12 16:08:52 -07:00
|
|
|
uint8_t *ma_p;
|
|
|
|
size_t md_len, ma_len;
|
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
memset(cfm->maid, 0, CCM_MAID_LEN);
|
2011-05-12 16:08:52 -07:00
|
|
|
|
|
|
|
md_len = strlen(ovs_md_name);
|
|
|
|
ma_len = strlen(ovs_ma_name);
|
|
|
|
|
2012-11-06 13:14:55 -08:00
|
|
|
ovs_assert(md_len && ma_len && md_len + ma_len + 4 <= CCM_MAID_LEN);
|
2011-05-12 16:08:52 -07:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
cfm->maid[0] = 4; /* MD name string format. */
|
|
|
|
cfm->maid[1] = md_len; /* MD name size. */
|
|
|
|
memcpy(&cfm->maid[2], ovs_md_name, md_len); /* MD name. */
|
2011-05-12 16:08:52 -07:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
ma_p = cfm->maid + 2 + md_len;
|
2011-05-12 16:08:52 -07:00
|
|
|
ma_p[0] = 2; /* MA name string format. */
|
|
|
|
ma_p[1] = ma_len; /* MA name size. */
|
|
|
|
memcpy(&ma_p[2], ovs_ma_name, ma_len); /* MA name. */
|
|
|
|
}
|
|
|
|
|
2010-11-15 16:20:01 -08:00
|
|
|
static int
|
|
|
|
ccm_interval_to_ms(uint8_t interval)
|
|
|
|
{
|
|
|
|
switch (interval) {
|
2013-12-17 10:32:12 -08:00
|
|
|
case 0: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
|
2010-11-15 16:20:01 -08:00
|
|
|
case 1: return 3; /* Not recommended due to timer resolution. */
|
|
|
|
case 2: return 10; /* Not recommended due to timer resolution. */
|
|
|
|
case 3: return 100;
|
|
|
|
case 4: return 1000;
|
|
|
|
case 5: return 10000;
|
|
|
|
case 6: return 60000;
|
|
|
|
case 7: return 600000;
|
2013-12-17 10:32:12 -08:00
|
|
|
default: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2013-12-17 10:32:12 -08:00
|
|
|
OVS_NOT_REACHED();
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2011-04-01 13:10:49 -07:00
|
|
|
static long long int
|
2013-08-08 15:14:19 -07:00
|
|
|
cfm_fault_interval(struct cfm *cfm) OVS_REQUIRES(mutex)
|
2011-04-01 13:10:49 -07:00
|
|
|
{
|
|
|
|
/* According to the 802.1ag specification we should assume every other MP
|
|
|
|
* with the same MAID has the same transmission interval that we have. If
|
|
|
|
* an MP has a different interval, cfm_process_heartbeat will register it
|
|
|
|
* as a fault (likely due to a configuration error). Thus we can check all
|
|
|
|
* MPs at once making this quite a bit simpler.
|
|
|
|
*
|
2013-09-20 06:13:33 +00:00
|
|
|
* When cfm is not in demand mode, we check when (ccm_interval_ms * 3.5) ms
|
|
|
|
* have passed. When cfm is in demand mode, we check when
|
|
|
|
* (MAX(ccm_interval_ms, 500) * 3.5) ms have passed. This ensures that
|
|
|
|
* ovs-vswitchd has enough time to pull statistics from the datapath. */
|
|
|
|
|
|
|
|
return (MAX(cfm->ccm_interval_ms, cfm->demand ? 500 : cfm->ccm_interval_ms)
|
|
|
|
* 7) / 2;
|
2011-04-01 13:10:49 -07:00
|
|
|
}
|
|
|
|
|
2010-11-15 16:20:01 -08:00
|
|
|
static uint8_t
|
|
|
|
ms_to_ccm_interval(int interval_ms)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
for (i = 7; i > 0; i--) {
|
|
|
|
if (ccm_interval_to_ms(i) <= interval_ms) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
2011-09-01 13:28:25 -07:00
|
|
|
hash_mpid(uint64_t mpid)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2014-03-27 19:38:04 -07:00
|
|
|
return hash_uint64(mpid);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2011-09-01 13:28:25 -07:00
|
|
|
cfm_is_valid_mpid(bool extended, uint64_t mpid)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2011-09-01 13:28:25 -07:00
|
|
|
/* 802.1ag specification requires MPIDs to be within the range [1, 8191].
|
|
|
|
* In extended mode we relax this requirement. */
|
|
|
|
return mpid >= 1 && (extended || mpid <= 8191);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct remote_mp *
|
2013-08-08 15:14:19 -07:00
|
|
|
lookup_remote_mp(const struct cfm *cfm, uint64_t mpid) OVS_REQUIRES(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
|
|
|
struct remote_mp *rmp;
|
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
HMAP_FOR_EACH_IN_BUCKET (rmp, node, hash_mpid(mpid), &cfm->remote_mps) {
|
2010-11-15 16:20:01 -08:00
|
|
|
if (rmp->mpid == mpid) {
|
|
|
|
return rmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-05-13 16:53:24 -07:00
|
|
|
void
|
|
|
|
cfm_init(void)
|
|
|
|
{
|
2011-12-02 15:29:19 -08:00
|
|
|
unixctl_command_register("cfm/show", "[interface]", 0, 1, cfm_unixctl_show,
|
2011-09-26 15:40:53 -07:00
|
|
|
NULL);
|
2012-01-26 18:58:51 -08:00
|
|
|
unixctl_command_register("cfm/set-fault", "[interface] normal|false|true",
|
|
|
|
1, 2, cfm_unixctl_set_fault, NULL);
|
2011-05-13 16:53:24 -07:00
|
|
|
}
|
|
|
|
|
2014-05-06 23:21:36 -07:00
|
|
|
/* Records the status change and changes the global connectivity seq. */
|
|
|
|
static void
|
|
|
|
cfm_status_changed(struct cfm *cfm) OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
seq_change(connectivity_seq_get());
|
|
|
|
cfm->status_changed = true;
|
|
|
|
}
|
|
|
|
|
2011-05-23 16:05:41 -07:00
|
|
|
/* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by
|
|
|
|
* cfm_configure() before use. */
|
2010-11-15 16:20:01 -08:00
|
|
|
struct cfm *
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
|
|
|
struct cfm *cfm;
|
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
cfm = xzalloc(sizeof *cfm);
|
2013-05-15 14:31:06 -07:00
|
|
|
cfm->netdev = netdev_ref(netdev);
|
|
|
|
cfm->name = netdev_get_name(cfm->netdev);
|
2011-05-13 15:37:23 -07:00
|
|
|
hmap_init(&cfm->remote_mps);
|
2011-10-06 22:43:05 -07:00
|
|
|
cfm->remote_opup = true;
|
2012-01-26 18:58:51 -08:00
|
|
|
cfm->fault_override = -1;
|
2012-04-05 14:30:23 -07:00
|
|
|
cfm->health = -1;
|
2012-06-19 13:24:43 -07:00
|
|
|
cfm->last_tx = 0;
|
2013-10-22 05:16:23 +00:00
|
|
|
cfm->flap_count = 0;
|
2013-07-23 13:09:38 -07:00
|
|
|
atomic_init(&cfm->extended, false);
|
|
|
|
atomic_init(&cfm->check_tnl_key, false);
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_init(&cfm->ref_cnt);
|
2013-07-23 13:09:38 -07:00
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
2014-05-06 23:21:36 -07:00
|
|
|
cfm_status_changed(cfm);
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_generate_maid(cfm);
|
|
|
|
hmap_insert(all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0));
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2014-04-03 10:20:44 -07:00
|
|
|
|
2010-11-15 16:20:01 -08:00
|
|
|
return cfm;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2016-04-06 18:53:59 -07:00
|
|
|
struct remote_mp *rmp;
|
2010-11-15 16:20:01 -08:00
|
|
|
|
|
|
|
if (!cfm) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-07 13:18:46 -07:00
|
|
|
if (ovs_refcount_unref_relaxed(&cfm->ref_cnt) != 1) {
|
2013-06-18 16:31:43 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2014-05-06 23:21:36 -07:00
|
|
|
cfm_status_changed(cfm);
|
2013-07-23 13:09:38 -07:00
|
|
|
hmap_remove(all_cfms, &cfm->hmap_node);
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
|
2016-04-06 18:53:59 -07:00
|
|
|
HMAP_FOR_EACH_POP (rmp, node, &cfm->remote_mps) {
|
2010-11-15 16:20:01 -08:00
|
|
|
free(rmp);
|
|
|
|
}
|
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
hmap_destroy(&cfm->remote_mps);
|
2013-05-15 14:31:06 -07:00
|
|
|
netdev_close(cfm->netdev);
|
2011-08-26 14:54:34 -07:00
|
|
|
free(cfm->rmps_array);
|
2014-01-08 10:42:12 -08:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
free(cfm);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2013-06-18 16:31:43 -07:00
|
|
|
struct cfm *
|
|
|
|
cfm_ref(const struct cfm *cfm_)
|
|
|
|
{
|
|
|
|
struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
|
|
|
|
if (cfm) {
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_ref(&cfm->ref_cnt);
|
2013-06-18 16:31:43 -07:00
|
|
|
}
|
|
|
|
return cfm;
|
|
|
|
}
|
|
|
|
|
2011-03-22 16:57:43 -07:00
|
|
|
/* Should be run periodically to update fault statistics messages. */
|
|
|
|
void
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-05-13 15:37:23 -07:00
|
|
|
if (timer_expired(&cfm->fault_timer)) {
|
|
|
|
long long int interval = cfm_fault_interval(cfm);
|
2022-03-23 12:56:17 +01:00
|
|
|
struct remote_mp *rmp;
|
2014-03-19 16:19:28 -07:00
|
|
|
enum cfm_fault_reason old_cfm_fault = cfm->fault;
|
2014-03-14 18:30:39 -07:00
|
|
|
uint64_t old_flap_count = cfm->flap_count;
|
|
|
|
int old_health = cfm->health;
|
|
|
|
size_t old_rmps_array_len = cfm->rmps_array_len;
|
|
|
|
bool old_rmps_deleted = false;
|
2013-12-13 03:33:47 +00:00
|
|
|
bool old_rmp_opup = cfm->remote_opup;
|
2013-05-15 14:31:06 -07:00
|
|
|
bool demand_override;
|
2013-08-03 07:12:36 +00:00
|
|
|
bool rmp_set_opup = false;
|
|
|
|
bool rmp_set_opdown = false;
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2012-02-07 14:35:09 -08:00
|
|
|
cfm->fault = cfm->recv_fault;
|
|
|
|
cfm->recv_fault = 0;
|
2011-08-29 18:56:28 -07:00
|
|
|
|
2011-08-26 14:54:34 -07:00
|
|
|
cfm->rmps_array_len = 0;
|
|
|
|
free(cfm->rmps_array);
|
|
|
|
cfm->rmps_array = xmalloc(hmap_count(&cfm->remote_mps) *
|
|
|
|
sizeof *cfm->rmps_array);
|
|
|
|
|
2012-04-05 14:30:23 -07:00
|
|
|
if (cfm->health_interval == CFM_HEALTH_INTERVAL) {
|
|
|
|
/* Calculate the cfm health of the interface. If the number of
|
|
|
|
* remote_mpids of a cfm interface is > 1, the cfm health is
|
|
|
|
* undefined. If the number of remote_mpids is 1, the cfm health is
|
|
|
|
* the percentage of the ccm frames received in the
|
|
|
|
* (CFM_HEALTH_INTERVAL * 3.5)ms, else it is 0. */
|
|
|
|
if (hmap_count(&cfm->remote_mps) > 1) {
|
|
|
|
cfm->health = -1;
|
|
|
|
} else if (hmap_is_empty(&cfm->remote_mps)) {
|
|
|
|
cfm->health = 0;
|
|
|
|
} else {
|
|
|
|
int exp_ccm_recvd;
|
|
|
|
|
|
|
|
rmp = CONTAINER_OF(hmap_first(&cfm->remote_mps),
|
|
|
|
struct remote_mp, node);
|
|
|
|
exp_ccm_recvd = (CFM_HEALTH_INTERVAL * 7) / 2;
|
|
|
|
/* Calculate the percentage of healthy ccm frames received.
|
|
|
|
* Since the 'fault_interval' is (3.5 * cfm_interval), and
|
|
|
|
* 1 CCM packet must be received every cfm_interval,
|
|
|
|
* the 'remote_mpid' health reports the percentage of
|
|
|
|
* healthy CCM frames received every
|
|
|
|
* 'CFM_HEALTH_INTERVAL'th 'fault_interval'. */
|
|
|
|
cfm->health = (rmp->num_health_ccm * 100) / exp_ccm_recvd;
|
|
|
|
cfm->health = MIN(cfm->health, 100);
|
|
|
|
rmp->num_health_ccm = 0;
|
2012-11-06 13:14:55 -08:00
|
|
|
ovs_assert(cfm->health >= 0 && cfm->health <= 100);
|
2012-04-05 14:30:23 -07:00
|
|
|
}
|
|
|
|
cfm->health_interval = 0;
|
|
|
|
}
|
|
|
|
cfm->health_interval++;
|
|
|
|
|
2013-05-15 14:31:06 -07:00
|
|
|
demand_override = false;
|
|
|
|
if (cfm->demand) {
|
|
|
|
uint64_t rx_packets = cfm_rx_packets(cfm);
|
|
|
|
demand_override = hmap_count(&cfm->remote_mps) == 1
|
2014-04-09 10:58:54 -07:00
|
|
|
&& rx_packets > cfm->rx_packets
|
|
|
|
&& !timer_expired(&cfm->demand_rx_ccm_t);
|
2013-05-15 14:31:06 -07:00
|
|
|
cfm->rx_packets = rx_packets;
|
|
|
|
}
|
2011-03-31 13:54:44 -07:00
|
|
|
|
2022-03-23 12:56:17 +01:00
|
|
|
HMAP_FOR_EACH_SAFE (rmp, node, &cfm->remote_mps) {
|
2011-08-19 13:58:56 -07:00
|
|
|
if (!rmp->recv) {
|
2012-07-18 20:39:54 -07:00
|
|
|
VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last"
|
|
|
|
" %lldms", cfm->name, rmp->mpid,
|
|
|
|
time_msec() - rmp->last_rx);
|
2013-05-15 14:31:06 -07:00
|
|
|
if (!demand_override) {
|
2014-03-14 18:30:39 -07:00
|
|
|
old_rmps_deleted = true;
|
2013-05-15 14:31:06 -07:00
|
|
|
hmap_remove(&cfm->remote_mps, &rmp->node);
|
|
|
|
free(rmp);
|
|
|
|
}
|
2011-08-19 13:58:56 -07:00
|
|
|
} else {
|
|
|
|
rmp->recv = false;
|
|
|
|
|
2013-08-03 07:12:36 +00:00
|
|
|
if (rmp->opup) {
|
|
|
|
rmp_set_opup = true;
|
|
|
|
} else {
|
|
|
|
rmp_set_opdown = true;
|
2011-10-06 22:43:05 -07:00
|
|
|
}
|
|
|
|
|
2011-08-26 14:54:34 -07:00
|
|
|
cfm->rmps_array[cfm->rmps_array_len++] = rmp->mpid;
|
2011-03-31 13:54:44 -07:00
|
|
|
}
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2013-08-03 07:12:36 +00:00
|
|
|
if (rmp_set_opdown) {
|
|
|
|
cfm->remote_opup = false;
|
|
|
|
}
|
|
|
|
else if (rmp_set_opup) {
|
|
|
|
cfm->remote_opup = true;
|
|
|
|
}
|
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
if (hmap_is_empty(&cfm->remote_mps)) {
|
2012-02-07 14:35:09 -08:00
|
|
|
cfm->fault |= CFM_FAULT_RECV;
|
2011-05-11 18:13:35 -07:00
|
|
|
}
|
|
|
|
|
2013-11-06 10:36:17 -08:00
|
|
|
if (old_cfm_fault != cfm->fault) {
|
|
|
|
if (!VLOG_DROP_INFO(&rl)) {
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
|
|
|
ds_put_cstr(&ds, "from [");
|
|
|
|
ds_put_cfm_fault(&ds, old_cfm_fault);
|
|
|
|
ds_put_cstr(&ds, "] to [");
|
|
|
|
ds_put_cfm_fault(&ds, cfm->fault);
|
|
|
|
ds_put_char(&ds, ']');
|
|
|
|
VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
|
|
|
|
ds_destroy(&ds);
|
|
|
|
}
|
2013-10-22 05:16:23 +00:00
|
|
|
|
|
|
|
/* If there is a flap, increments the counter. */
|
2014-03-19 16:19:28 -07:00
|
|
|
if (old_cfm_fault == 0 || cfm->fault == 0) {
|
2013-10-22 05:16:23 +00:00
|
|
|
cfm->flap_count++;
|
|
|
|
}
|
2014-03-14 18:30:39 -07:00
|
|
|
}
|
2013-12-13 03:33:47 +00:00
|
|
|
|
2014-03-14 18:30:39 -07:00
|
|
|
/* These variables represent the cfm session status, it is desirable
|
|
|
|
* to update them to database immediately after change. */
|
|
|
|
if (old_health != cfm->health
|
|
|
|
|| old_rmp_opup != cfm->remote_opup
|
|
|
|
|| (old_rmps_array_len != cfm->rmps_array_len || old_rmps_deleted)
|
|
|
|
|| old_cfm_fault != cfm->fault
|
|
|
|
|| old_flap_count != cfm->flap_count) {
|
2014-04-03 10:20:44 -07:00
|
|
|
cfm_status_changed(cfm);
|
2012-01-16 16:42:08 -08:00
|
|
|
}
|
|
|
|
|
2012-07-20 13:07:49 -07:00
|
|
|
cfm->booted = true;
|
2011-05-13 15:37:23 -07:00
|
|
|
timer_set_duration(&cfm->fault_timer, interval);
|
2012-06-19 13:03:16 -07:00
|
|
|
VLOG_DBG("%s: new fault interval", cfm->name);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2011-03-22 16:57:43 -07:00
|
|
|
}
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-03-22 16:57:43 -07:00
|
|
|
/* Should be run periodically to check if the CFM module has a CCM message it
|
|
|
|
* wishes to send. */
|
|
|
|
bool
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_should_send_ccm(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2011-03-22 16:57:43 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
bool ret;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
ret = timer_expired(&cfm->tx_timer);
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return ret;
|
2011-03-22 16:57:43 -07:00
|
|
|
}
|
|
|
|
|
2011-05-13 18:11:43 -07:00
|
|
|
/* Composes a CCM message into 'packet'. Messages generated with this function
|
2011-03-22 16:57:43 -07:00
|
|
|
* should be sent whenever cfm_should_send_ccm() indicates. */
|
|
|
|
void
|
2015-02-22 03:21:09 -08:00
|
|
|
cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet,
|
2015-08-28 14:55:11 -07:00
|
|
|
const struct eth_addr eth_src) OVS_EXCLUDED(mutex)
|
2011-03-22 16:57:43 -07:00
|
|
|
{
|
2012-03-09 18:16:20 -08:00
|
|
|
uint16_t ccm_vlan;
|
2011-05-13 18:11:43 -07:00
|
|
|
struct ccm *ccm;
|
2013-07-23 13:09:38 -07:00
|
|
|
bool extended;
|
2011-05-13 18:11:43 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-05-13 15:37:23 -07:00
|
|
|
timer_set_duration(&cfm->tx_timer, cfm->ccm_interval_ms);
|
2011-11-03 13:03:16 -07:00
|
|
|
eth_compose(packet, cfm_ccm_addr(cfm), eth_src, ETH_TYPE_CFM, sizeof *ccm);
|
|
|
|
|
2012-03-09 18:16:20 -08:00
|
|
|
ccm_vlan = (cfm->ccm_vlan != CFM_RANDOM_VLAN
|
|
|
|
? cfm->ccm_vlan
|
|
|
|
: random_uint16());
|
|
|
|
ccm_vlan = ccm_vlan & VLAN_VID_MASK;
|
|
|
|
|
|
|
|
if (ccm_vlan || cfm->ccm_pcp) {
|
|
|
|
uint16_t tci = ccm_vlan | (cfm->ccm_pcp << VLAN_PCP_SHIFT);
|
2014-01-15 17:17:01 +09:00
|
|
|
eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(tci));
|
2011-11-03 13:03:16 -07:00
|
|
|
}
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
atomic_read_relaxed(&cfm->extended, &extended);
|
|
|
|
|
2015-02-22 03:21:09 -08:00
|
|
|
ccm = dp_packet_l3(packet);
|
2011-03-22 16:57:43 -07:00
|
|
|
ccm->mdlevel_version = 0;
|
|
|
|
ccm->opcode = CCM_OPCODE;
|
|
|
|
ccm->tlv_offset = 70;
|
2011-05-13 15:37:23 -07:00
|
|
|
ccm->seq = htonl(++cfm->seq);
|
|
|
|
ccm->flags = cfm->ccm_interval;
|
|
|
|
memcpy(ccm->maid, cfm->maid, sizeof ccm->maid);
|
2011-08-26 17:12:21 -07:00
|
|
|
memset(ccm->zero, 0, sizeof ccm->zero);
|
2011-10-07 15:43:43 -07:00
|
|
|
ccm->end_tlv = 0;
|
2011-05-17 16:07:58 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
if (extended) {
|
2011-09-01 13:28:25 -07:00
|
|
|
ccm->mpid = htons(hash_mpid(cfm->mpid));
|
|
|
|
ccm->mpid64 = htonll(cfm->mpid);
|
2011-10-06 22:43:05 -07:00
|
|
|
ccm->opdown = !cfm->opup;
|
2011-09-01 13:28:25 -07:00
|
|
|
} else {
|
|
|
|
ccm->mpid = htons(cfm->mpid);
|
|
|
|
ccm->mpid64 = htonll(0);
|
2011-10-06 22:43:05 -07:00
|
|
|
ccm->opdown = 0;
|
2011-09-01 13:28:25 -07:00
|
|
|
}
|
|
|
|
|
2011-08-30 17:37:15 -07:00
|
|
|
if (cfm->ccm_interval == 0) {
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_assert(extended);
|
2011-08-30 17:37:15 -07:00
|
|
|
ccm->interval_ms_x = htons(cfm->ccm_interval_ms);
|
2012-07-10 14:51:29 -07:00
|
|
|
} else {
|
|
|
|
ccm->interval_ms_x = htons(0);
|
2011-08-30 17:37:15 -07:00
|
|
|
}
|
|
|
|
|
2012-07-20 13:07:49 -07:00
|
|
|
if (cfm->booted && hmap_is_empty(&cfm->remote_mps)) {
|
2011-05-17 16:07:58 -07:00
|
|
|
ccm->flags |= CCM_RDI_MASK;
|
|
|
|
}
|
2012-06-19 13:24:43 -07:00
|
|
|
|
|
|
|
if (cfm->last_tx) {
|
|
|
|
long long int delay = time_msec() - cfm->last_tx;
|
|
|
|
if (delay > (cfm->ccm_interval_ms * 3 / 2)) {
|
2014-07-25 09:19:17 -07:00
|
|
|
VLOG_INFO("%s: long delay of %lldms (expected %dms) sending CCM"
|
2012-06-19 13:24:43 -07:00
|
|
|
" seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms,
|
|
|
|
cfm->seq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cfm->last_tx = time_msec();
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2015-02-20 14:17:10 -05:00
|
|
|
long long int
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_wait(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2015-02-20 14:17:10 -05:00
|
|
|
long long int wake_time = cfm_wake_time(cfm);
|
|
|
|
poll_timer_wait_until(wake_time);
|
|
|
|
return wake_time;
|
2013-10-16 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns the next cfm wakeup time. */
|
|
|
|
long long int
|
|
|
|
cfm_wake_time(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
long long int retval;
|
|
|
|
|
|
|
|
if (!cfm) {
|
|
|
|
return LLONG_MAX;
|
|
|
|
}
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2013-10-16 03:32:33 +00:00
|
|
|
retval = MIN(cfm->tx_timer.t, cfm->fault_timer.t);
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2013-10-16 03:32:33 +00:00
|
|
|
return retval;
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2013-10-16 03:32:33 +00:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
/* Configures 'cfm' with settings from 's'. */
|
2010-11-15 16:20:01 -08:00
|
|
|
bool
|
2011-05-13 15:37:23 -07:00
|
|
|
cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
|
2013-07-23 13:09:38 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2011-04-01 13:22:44 -07:00
|
|
|
uint8_t interval;
|
2011-08-30 17:37:15 -07:00
|
|
|
int interval_ms;
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-09-01 13:28:25 -07:00
|
|
|
if (!cfm_is_valid_mpid(s->extended, s->mpid) || s->interval <= 0) {
|
2010-11-15 16:20:01 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-05-13 15:37:23 -07:00
|
|
|
cfm->mpid = s->mpid;
|
2011-10-06 22:43:05 -07:00
|
|
|
cfm->opup = s->opup;
|
2011-05-13 15:37:23 -07:00
|
|
|
interval = ms_to_ccm_interval(s->interval);
|
2011-08-30 17:37:15 -07:00
|
|
|
interval_ms = ccm_interval_to_ms(interval);
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
atomic_store_relaxed(&cfm->check_tnl_key, s->check_tnl_key);
|
|
|
|
atomic_store_relaxed(&cfm->extended, s->extended);
|
2013-07-23 13:09:38 -07:00
|
|
|
|
2012-03-09 18:16:20 -08:00
|
|
|
cfm->ccm_vlan = s->ccm_vlan;
|
2012-02-02 15:48:13 -08:00
|
|
|
cfm->ccm_pcp = s->ccm_pcp & (VLAN_PCP_MASK >> VLAN_PCP_SHIFT);
|
2013-07-23 13:09:38 -07:00
|
|
|
if (s->extended && interval_ms != s->interval) {
|
2011-08-30 17:37:15 -07:00
|
|
|
interval = 0;
|
|
|
|
interval_ms = MIN(s->interval, UINT16_MAX);
|
|
|
|
}
|
2011-04-01 13:22:44 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
if (s->extended && s->demand) {
|
2013-05-15 14:31:06 -07:00
|
|
|
if (!cfm->demand) {
|
|
|
|
cfm->demand = true;
|
|
|
|
cfm->rx_packets = cfm_rx_packets(cfm);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cfm->demand = false;
|
|
|
|
}
|
|
|
|
|
2011-08-30 17:37:15 -07:00
|
|
|
if (interval != cfm->ccm_interval || interval_ms != cfm->ccm_interval_ms) {
|
2011-05-13 15:37:23 -07:00
|
|
|
cfm->ccm_interval = interval;
|
2011-08-30 17:37:15 -07:00
|
|
|
cfm->ccm_interval_ms = interval_ms;
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
timer_set_expired(&cfm->tx_timer);
|
|
|
|
timer_set_duration(&cfm->fault_timer, cfm_fault_interval(cfm));
|
|
|
|
}
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2011-05-13 15:37:23 -07:00
|
|
|
return true;
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2013-06-03 13:49:13 -07:00
|
|
|
/* Must be called when the netdev owned by 'cfm' should change. */
|
|
|
|
void
|
|
|
|
cfm_set_netdev(struct cfm *cfm, const struct netdev *netdev)
|
2013-07-23 13:09:38 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2013-06-03 13:49:13 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2013-06-03 13:49:13 -07:00
|
|
|
if (cfm->netdev != netdev) {
|
|
|
|
netdev_close(cfm->netdev);
|
|
|
|
cfm->netdev = netdev_ref(netdev);
|
|
|
|
}
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2013-06-03 13:49:13 -07:00
|
|
|
}
|
|
|
|
|
2013-06-17 18:07:33 -07:00
|
|
|
/* Returns true if 'cfm' should process packets from 'flow'. Sets
|
|
|
|
* fields in 'wc' that were used to make the determination. */
|
2010-11-15 16:20:01 -08:00
|
|
|
bool
|
2013-08-20 10:46:15 -07:00
|
|
|
cfm_should_process_flow(const struct cfm *cfm_, const struct flow *flow,
|
2013-06-17 18:07:33 -07:00
|
|
|
struct flow_wildcards *wc)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
2013-08-20 10:46:15 -07:00
|
|
|
struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
|
2013-07-23 13:09:38 -07:00
|
|
|
bool check_tnl_key;
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
/* Most packets are not CFM. */
|
|
|
|
if (OVS_LIKELY(flow->dl_type != htons(ETH_TYPE_CFM))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-17 18:07:33 -07:00
|
|
|
memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
|
2014-08-29 10:34:52 -07:00
|
|
|
if (OVS_UNLIKELY(!eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm)))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_read_relaxed(&cfm->check_tnl_key, &check_tnl_key);
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
if (check_tnl_key) {
|
2013-06-17 18:07:33 -07:00
|
|
|
memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
|
2014-08-29 10:34:52 -07:00
|
|
|
return flow->tunnel.tun_id == htonll(0);
|
2013-06-17 18:07:33 -07:00
|
|
|
}
|
2014-08-29 10:34:52 -07:00
|
|
|
return true;
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Updates internal statistics relevant to packet 'p'. Should be called on
|
|
|
|
* every packet whose flow returned true when passed to
|
|
|
|
* cfm_should_process_flow. */
|
|
|
|
void
|
2015-02-22 03:21:09 -08:00
|
|
|
cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p)
|
2013-07-23 13:09:38 -07:00
|
|
|
OVS_EXCLUDED(mutex)
|
2010-11-15 16:20:01 -08:00
|
|
|
{
|
|
|
|
struct ccm *ccm;
|
2011-03-28 13:10:12 -07:00
|
|
|
struct eth_header *eth;
|
2014-08-29 10:34:52 -07:00
|
|
|
bool extended;
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
atomic_read_relaxed(&cfm->extended, &extended);
|
|
|
|
|
2017-04-25 16:29:59 +00:00
|
|
|
eth = dp_packet_eth(p);
|
2015-02-22 03:21:09 -08:00
|
|
|
ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
|
lib/ofpbuf: Compact
This patch shrinks the struct ofpbuf from 104 to 48 bytes on 64-bit
systems, or from 52 to 36 bytes on 32-bit systems (counting in the
'l7' removal from an earlier patch). This may help contribute to
cache efficiency, and will speed up initializing, copying and
manipulating ofpbufs. This is potentially important for the DPDK
datapath, but the rest of the code base may also see a little benefit.
Changes are:
- Remove 'l7' pointer (previous patch).
- Use offsets instead of layer pointers for l2_5, l3, and l4 using
'l2' as basis. Usually 'data' is the same as 'l2', but this is not
always the case (e.g., when parsing or constructing a packet), so it
can not be easily used as the offset basis. Also, packet parsing is
faster if we do not need to maintain the offsets each time we pull
data from the ofpbuf.
- Use uint32_t for 'allocated' and 'size', as 2^32 is enough even for
largest possible messages/packets.
- Use packed enum for 'source'.
- Rearrange to avoid unnecessary padding.
- Remove 'private_p', which was used only in two cases, both of which
had the invariant ('l2' == 'data'), so we can temporarily use 'l2'
as a private pointer.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2014-03-24 09:17:01 -07:00
|
|
|
CCM_ACCEPT_LEN);
|
2010-11-15 16:20:01 -08:00
|
|
|
|
|
|
|
if (!ccm) {
|
2011-05-23 16:10:25 -07:00
|
|
|
VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.",
|
|
|
|
cfm->name);
|
2013-07-23 13:09:38 -07:00
|
|
|
goto out;
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ccm->opcode != CCM_OPCODE) {
|
2011-05-23 16:10:25 -07:00
|
|
|
VLOG_INFO_RL(&rl, "%s: Received an unsupported 802.1ag message. "
|
|
|
|
"(opcode %u)", cfm->name, ccm->opcode);
|
2013-07-23 13:09:38 -07:00
|
|
|
goto out;
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
|
|
|
|
2011-05-11 17:55:41 -07:00
|
|
|
/* According to the 802.1ag specification, reception of a CCM with an
|
2011-05-11 17:50:16 -07:00
|
|
|
* incorrect ccm_interval, unexpected MAID, or unexpected MPID should
|
|
|
|
* trigger a fault. We ignore this requirement for several reasons.
|
2011-05-11 17:55:41 -07:00
|
|
|
*
|
|
|
|
* Faults can cause a controller or Open vSwitch to make potentially
|
|
|
|
* expensive changes to the network topology. It seems prudent to trigger
|
2020-06-17 14:16:08 -07:00
|
|
|
* them judiciously, especially when CFM is used to check status of bond
|
|
|
|
* members. Furthermore, faults can be maliciously triggered by crafting
|
2012-04-04 17:36:00 -07:00
|
|
|
* unexpected CCMs. */
|
2011-05-13 15:37:23 -07:00
|
|
|
if (memcmp(ccm->maid, cfm->maid, sizeof ccm->maid)) {
|
2012-02-07 14:35:09 -08:00
|
|
|
cfm->recv_fault |= CFM_FAULT_MAID;
|
2011-05-23 16:10:25 -07:00
|
|
|
VLOG_WARN_RL(&rl, "%s: Received unexpected remote MAID from MAC "
|
|
|
|
ETH_ADDR_FMT, cfm->name, ETH_ADDR_ARGS(eth->eth_src));
|
2011-03-24 19:04:21 -07:00
|
|
|
} else {
|
2011-08-30 17:37:15 -07:00
|
|
|
uint8_t ccm_interval = ccm->flags & 0x7;
|
|
|
|
bool ccm_rdi = ccm->flags & CCM_RDI_MASK;
|
|
|
|
uint16_t ccm_interval_ms_x = ntohs(ccm->interval_ms_x);
|
|
|
|
|
|
|
|
struct remote_mp *rmp;
|
2011-09-01 13:28:25 -07:00
|
|
|
uint64_t ccm_mpid;
|
2012-01-16 16:42:08 -08:00
|
|
|
uint32_t ccm_seq;
|
2011-10-06 22:43:05 -07:00
|
|
|
bool ccm_opdown;
|
2012-04-04 17:36:00 -07:00
|
|
|
enum cfm_fault_reason cfm_fault = 0;
|
2011-09-01 13:28:25 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
if (extended) {
|
2011-10-06 22:43:05 -07:00
|
|
|
ccm_mpid = ntohll(ccm->mpid64);
|
|
|
|
ccm_opdown = ccm->opdown;
|
|
|
|
} else {
|
|
|
|
ccm_mpid = ntohs(ccm->mpid);
|
|
|
|
ccm_opdown = false;
|
|
|
|
}
|
2012-01-16 16:42:08 -08:00
|
|
|
ccm_seq = ntohl(ccm->seq);
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
if (ccm_interval != cfm->ccm_interval) {
|
2012-04-04 17:36:00 -07:00
|
|
|
VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected interval"
|
2011-09-01 13:28:25 -07:00
|
|
|
" (%"PRIu8") from RMP %"PRIu64, cfm->name,
|
2011-08-19 13:58:56 -07:00
|
|
|
ccm_interval, ccm_mpid);
|
|
|
|
}
|
2010-11-15 16:20:01 -08:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
if (extended && ccm_interval == 0
|
2011-08-30 17:37:15 -07:00
|
|
|
&& ccm_interval_ms_x != cfm->ccm_interval_ms) {
|
2012-04-04 17:36:00 -07:00
|
|
|
VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected extended"
|
2011-09-01 13:28:25 -07:00
|
|
|
" interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name,
|
2011-08-30 17:37:15 -07:00
|
|
|
ccm_interval_ms_x, ccm_mpid);
|
|
|
|
}
|
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
rmp = lookup_remote_mp(cfm, ccm_mpid);
|
2011-10-06 22:43:05 -07:00
|
|
|
if (!rmp) {
|
|
|
|
if (hmap_count(&cfm->remote_mps) < CFM_MAX_RMPS) {
|
2012-01-16 16:42:08 -08:00
|
|
|
rmp = xzalloc(sizeof *rmp);
|
2011-10-06 22:43:05 -07:00
|
|
|
hmap_insert(&cfm->remote_mps, &rmp->node, hash_mpid(ccm_mpid));
|
|
|
|
} else {
|
2012-04-04 17:36:00 -07:00
|
|
|
cfm_fault |= CFM_FAULT_OVERFLOW;
|
2011-10-06 22:43:05 -07:00
|
|
|
VLOG_WARN_RL(&rl,
|
|
|
|
"%s: dropped CCM with MPID %"PRIu64" from MAC "
|
|
|
|
ETH_ADDR_FMT, cfm->name, ccm_mpid,
|
|
|
|
ETH_ADDR_ARGS(eth->eth_src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-04 17:36:00 -07:00
|
|
|
if (ccm_rdi) {
|
|
|
|
cfm_fault |= CFM_FAULT_RDI;
|
|
|
|
VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
|
2012-05-13 16:31:15 -07:00
|
|
|
ccm_mpid);
|
2012-04-04 17:36:00 -07:00
|
|
|
}
|
|
|
|
|
2012-01-16 16:42:08 -08:00
|
|
|
VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")"
|
|
|
|
" (interval %"PRIu8") (RDI %s)", cfm->name, ccm_seq,
|
|
|
|
ccm_mpid, ccm_interval, ccm_rdi ? "true" : "false");
|
|
|
|
|
2011-03-28 13:10:12 -07:00
|
|
|
if (rmp) {
|
2012-04-04 17:36:00 -07:00
|
|
|
if (rmp->mpid == cfm->mpid) {
|
|
|
|
cfm_fault |= CFM_FAULT_LOOPBACK;
|
|
|
|
VLOG_WARN_RL(&rl,"%s: received CCM with local MPID"
|
|
|
|
" %"PRIu64, cfm->name, rmp->mpid);
|
|
|
|
}
|
|
|
|
|
2012-01-16 16:42:08 -08:00
|
|
|
if (rmp->seq && ccm_seq != (rmp->seq + 1)) {
|
|
|
|
VLOG_WARN_RL(&rl, "%s: (mpid %"PRIu64") detected sequence"
|
|
|
|
" numbers which indicate possible connectivity"
|
|
|
|
" problems (previous %"PRIu32") (current %"PRIu32
|
|
|
|
")", cfm->name, ccm_mpid, rmp->seq, ccm_seq);
|
|
|
|
}
|
|
|
|
|
2011-08-19 13:58:56 -07:00
|
|
|
rmp->mpid = ccm_mpid;
|
2012-04-04 17:36:00 -07:00
|
|
|
if (!cfm_fault) {
|
2012-04-05 14:30:23 -07:00
|
|
|
rmp->num_health_ccm++;
|
2014-04-09 10:58:54 -07:00
|
|
|
if (cfm->demand) {
|
|
|
|
timer_set_duration(&cfm->demand_rx_ccm_t,
|
|
|
|
100 * cfm->ccm_interval_ms);
|
|
|
|
}
|
2012-04-05 14:30:23 -07:00
|
|
|
}
|
2012-04-04 17:36:00 -07:00
|
|
|
rmp->recv = true;
|
|
|
|
cfm->recv_fault |= cfm_fault;
|
2012-01-16 16:42:08 -08:00
|
|
|
rmp->seq = ccm_seq;
|
2011-10-06 22:43:05 -07:00
|
|
|
rmp->opup = !ccm_opdown;
|
2012-07-18 20:39:54 -07:00
|
|
|
rmp->last_rx = time_msec();
|
2011-03-24 19:04:21 -07:00
|
|
|
}
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
2013-07-23 13:09:38 -07:00
|
|
|
|
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2010-11-15 16:20:01 -08:00
|
|
|
}
|
2011-03-25 15:30:33 -07:00
|
|
|
|
2014-04-03 10:20:44 -07:00
|
|
|
/* Returns and resets the 'cfm->status_changed'. */
|
|
|
|
bool
|
|
|
|
cfm_check_status_change(struct cfm *cfm) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
ret = cfm->status_changed;
|
|
|
|
cfm->status_changed = false;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
static int
|
2013-08-08 15:14:19 -07:00
|
|
|
cfm_get_fault__(const struct cfm *cfm) OVS_REQUIRES(mutex)
|
2011-05-13 15:37:23 -07:00
|
|
|
{
|
2012-01-26 18:58:51 -08:00
|
|
|
if (cfm->fault_override >= 0) {
|
2012-02-07 14:35:09 -08:00
|
|
|
return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0;
|
2012-01-26 18:58:51 -08:00
|
|
|
}
|
2011-05-13 15:37:23 -07:00
|
|
|
return cfm->fault;
|
|
|
|
}
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
/* Gets the fault status of 'cfm'. Returns a bit mask of 'cfm_fault_reason's
|
|
|
|
* indicating the cause of the connectivity fault, or zero if there is no
|
|
|
|
* fault. */
|
|
|
|
int
|
|
|
|
cfm_get_fault(const struct cfm *cfm) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
int fault;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
fault = cfm_get_fault__(cfm);
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return fault;
|
|
|
|
}
|
|
|
|
|
2013-10-22 05:16:23 +00:00
|
|
|
/* Gets the number of cfm fault flapping since start. */
|
|
|
|
uint64_t
|
|
|
|
cfm_get_flap_count(const struct cfm *cfm) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
uint64_t flap_count;
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
flap_count = cfm->flap_count;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return flap_count;
|
|
|
|
}
|
|
|
|
|
2012-04-05 14:30:23 -07:00
|
|
|
/* Gets the health of 'cfm'. Returns an integer between 0 and 100 indicating
|
|
|
|
* the health of the link as a percentage of ccm frames received in
|
|
|
|
* CFM_HEALTH_INTERVAL * 'fault_interval' if there is only 1 remote_mpid,
|
|
|
|
* returns 0 if there are no remote_mpids, and returns -1 if there are more
|
|
|
|
* than 1 remote_mpids. */
|
|
|
|
int
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_get_health(const struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2012-04-05 14:30:23 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
int health;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
health = cfm->health;
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
return health;
|
2012-04-05 14:30:23 -07:00
|
|
|
}
|
|
|
|
|
2014-06-09 18:35:35 -07:00
|
|
|
static int
|
|
|
|
cfm_get_opup__(const struct cfm *cfm_) OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
|
|
|
|
bool extended;
|
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
atomic_read_relaxed(&cfm->extended, &extended);
|
2014-06-09 18:35:35 -07:00
|
|
|
|
|
|
|
return extended ? cfm->remote_opup : -1;
|
|
|
|
}
|
|
|
|
|
2011-10-06 22:43:05 -07:00
|
|
|
/* Gets the operational state of 'cfm'. 'cfm' is considered operationally down
|
|
|
|
* if it has received a CCM with the operationally down bit set from any of its
|
2012-08-10 16:36:18 -07:00
|
|
|
* remote maintenance points. Returns 1 if 'cfm' is operationally up, 0 if
|
|
|
|
* 'cfm' is operationally down, or -1 if 'cfm' has no operational state
|
|
|
|
* (because it isn't in extended mode). */
|
|
|
|
int
|
2014-06-09 18:35:35 -07:00
|
|
|
cfm_get_opup(const struct cfm *cfm) OVS_EXCLUDED(mutex)
|
2011-10-06 22:43:05 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
int opup;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&mutex);
|
2014-06-09 18:35:35 -07:00
|
|
|
opup = cfm_get_opup__(cfm);
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
return opup;
|
2011-10-06 22:43:05 -07:00
|
|
|
}
|
|
|
|
|
2014-06-09 18:35:35 -07:00
|
|
|
static void
|
|
|
|
cfm_get_remote_mpids__(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps)
|
|
|
|
OVS_REQUIRES(mutex)
|
|
|
|
{
|
|
|
|
*rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len * sizeof **rmps);
|
|
|
|
*n_rmps = cfm->rmps_array_len;
|
|
|
|
}
|
|
|
|
|
2011-08-26 14:54:34 -07:00
|
|
|
/* Populates 'rmps' with an array of remote maintenance points reachable by
|
|
|
|
* 'cfm'. The number of remote maintenance points is written to 'n_rmps'.
|
|
|
|
* 'cfm' retains ownership of the array written to 'rmps' */
|
|
|
|
void
|
2013-07-23 13:09:38 -07:00
|
|
|
cfm_get_remote_mpids(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps)
|
|
|
|
OVS_EXCLUDED(mutex)
|
2011-08-26 14:54:34 -07:00
|
|
|
{
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2014-06-09 18:35:35 -07:00
|
|
|
cfm_get_remote_mpids__(cfm, rmps, n_rmps);
|
|
|
|
ovs_mutex_unlock(&mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extracts the status of 'cfm' and fills in the 's'. */
|
|
|
|
void
|
|
|
|
cfm_get_status(const struct cfm *cfm, struct cfm_status *s) OVS_EXCLUDED(mutex)
|
|
|
|
{
|
|
|
|
ovs_mutex_lock(&mutex);
|
|
|
|
s->faults = cfm_get_fault__(cfm);
|
|
|
|
s->remote_opstate = cfm_get_opup__(cfm);
|
|
|
|
s->flap_count = cfm->flap_count;
|
|
|
|
s->health = cfm->health;
|
|
|
|
cfm_get_remote_mpids__(cfm, &s->rmps, &s->n_rmps);
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_unlock(&mutex);
|
2011-08-26 14:54:34 -07:00
|
|
|
}
|
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
static struct cfm *
|
2013-08-08 15:14:20 -07:00
|
|
|
cfm_find(const char *name) OVS_REQUIRES(mutex)
|
2011-03-25 15:30:33 -07:00
|
|
|
{
|
2011-05-13 15:37:23 -07:00
|
|
|
struct cfm *cfm;
|
2011-05-13 16:53:24 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
HMAP_FOR_EACH_WITH_HASH (cfm, hmap_node, hash_string(name, 0), all_cfms) {
|
2011-05-23 16:05:41 -07:00
|
|
|
if (!strcmp(cfm->name, name)) {
|
2011-05-13 15:37:23 -07:00
|
|
|
return cfm;
|
2011-05-13 16:53:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-08-20 10:46:15 -07:00
|
|
|
cfm_print_details(struct ds *ds, struct cfm *cfm) OVS_REQUIRES(mutex)
|
2011-05-13 16:53:24 -07:00
|
|
|
{
|
2011-03-25 15:30:33 -07:00
|
|
|
struct remote_mp *rmp;
|
2013-07-23 13:09:38 -07:00
|
|
|
bool extended;
|
2012-04-20 14:52:16 -07:00
|
|
|
int fault;
|
2011-03-25 15:30:33 -07:00
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
atomic_read_relaxed(&cfm->extended, &extended);
|
2013-07-23 13:09:38 -07:00
|
|
|
|
2011-09-26 15:40:53 -07:00
|
|
|
ds_put_format(ds, "---- %s ----\n", cfm->name);
|
2012-02-07 14:35:09 -08:00
|
|
|
ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid,
|
2013-07-23 13:09:38 -07:00
|
|
|
extended ? " extended" : "",
|
2012-02-07 14:35:09 -08:00
|
|
|
cfm->fault_override >= 0 ? " fault_override" : "");
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
fault = cfm_get_fault__(cfm);
|
2012-04-20 14:52:16 -07:00
|
|
|
if (fault) {
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_cstr(ds, " fault: ");
|
2012-07-18 20:39:54 -07:00
|
|
|
ds_put_cfm_fault(ds, fault);
|
2012-02-07 14:35:09 -08:00
|
|
|
ds_put_cstr(ds, "\n");
|
|
|
|
}
|
2011-05-13 16:53:24 -07:00
|
|
|
|
2012-04-05 14:30:23 -07:00
|
|
|
if (cfm->health == -1) {
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " average health: undefined\n");
|
2012-04-05 14:30:23 -07:00
|
|
|
} else {
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " average health: %d\n", cfm->health);
|
2012-04-05 14:30:23 -07:00
|
|
|
}
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " opstate: %s\n", cfm->opup ? "up" : "down");
|
|
|
|
ds_put_format(ds, " remote_opstate: %s\n",
|
2011-10-06 22:43:05 -07:00
|
|
|
cfm->remote_opup ? "up" : "down");
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " interval: %dms\n", cfm->ccm_interval_ms);
|
|
|
|
ds_put_format(ds, " next CCM tx: %lldms\n",
|
2011-05-13 15:37:23 -07:00
|
|
|
timer_msecs_until_expired(&cfm->tx_timer));
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " next fault check: %lldms\n",
|
2011-05-13 15:37:23 -07:00
|
|
|
timer_msecs_until_expired(&cfm->fault_timer));
|
2011-03-25 15:30:33 -07:00
|
|
|
|
2011-05-13 15:37:23 -07:00
|
|
|
HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
|
2012-04-04 17:36:00 -07:00
|
|
|
ds_put_format(ds, "Remote MPID %"PRIu64"\n", rmp->mpid);
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " recv since check: %s\n",
|
2011-05-11 18:13:35 -07:00
|
|
|
rmp->recv ? "true" : "false");
|
2018-05-25 16:50:54 -07:00
|
|
|
ds_put_format(ds, " opstate: %s\n", rmp->opup? "up" : "down");
|
2011-03-25 15:30:33 -07:00
|
|
|
}
|
2011-09-26 15:40:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-12-02 15:29:19 -08:00
|
|
|
cfm_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
|
2013-07-23 13:09:38 -07:00
|
|
|
void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
|
2011-09-26 15:40:53 -07:00
|
|
|
{
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
2013-08-20 10:46:15 -07:00
|
|
|
struct cfm *cfm;
|
2011-09-26 15:40:53 -07:00
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2011-12-02 15:29:19 -08:00
|
|
|
if (argc > 1) {
|
|
|
|
cfm = cfm_find(argv[1]);
|
2011-09-26 15:40:53 -07:00
|
|
|
if (!cfm) {
|
2012-02-14 20:53:59 -08:00
|
|
|
unixctl_command_reply_error(conn, "no such CFM object");
|
2013-07-23 13:09:38 -07:00
|
|
|
goto out;
|
2011-09-26 15:40:53 -07:00
|
|
|
}
|
|
|
|
cfm_print_details(&ds, cfm);
|
|
|
|
} else {
|
2013-07-23 13:09:38 -07:00
|
|
|
HMAP_FOR_EACH (cfm, hmap_node, all_cfms) {
|
2011-09-26 15:40:53 -07:00
|
|
|
cfm_print_details(&ds, cfm);
|
|
|
|
}
|
|
|
|
}
|
2011-05-13 16:53:24 -07:00
|
|
|
|
2012-02-14 20:53:59 -08:00
|
|
|
unixctl_command_reply(conn, ds_cstr(&ds));
|
2011-05-13 16:53:24 -07:00
|
|
|
ds_destroy(&ds);
|
2013-07-23 13:09:38 -07:00
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2011-03-25 15:30:33 -07:00
|
|
|
}
|
2012-01-26 18:58:51 -08:00
|
|
|
|
|
|
|
static void
|
|
|
|
cfm_unixctl_set_fault(struct unixctl_conn *conn, int argc, const char *argv[],
|
2013-07-23 13:09:38 -07:00
|
|
|
void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
|
2012-01-26 18:58:51 -08:00
|
|
|
{
|
|
|
|
const char *fault_str = argv[argc - 1];
|
|
|
|
int fault_override;
|
|
|
|
struct cfm *cfm;
|
|
|
|
|
2013-07-23 13:09:38 -07:00
|
|
|
ovs_mutex_lock(&mutex);
|
2012-01-26 18:58:51 -08:00
|
|
|
if (!strcasecmp("true", fault_str)) {
|
|
|
|
fault_override = 1;
|
|
|
|
} else if (!strcasecmp("false", fault_str)) {
|
|
|
|
fault_override = 0;
|
|
|
|
} else if (!strcasecmp("normal", fault_str)) {
|
|
|
|
fault_override = -1;
|
|
|
|
} else {
|
2012-02-14 20:53:59 -08:00
|
|
|
unixctl_command_reply_error(conn, "unknown fault string");
|
2013-07-23 13:09:38 -07:00
|
|
|
goto out;
|
2012-01-26 18:58:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 2) {
|
|
|
|
cfm = cfm_find(argv[1]);
|
|
|
|
if (!cfm) {
|
2012-02-14 20:53:59 -08:00
|
|
|
unixctl_command_reply_error(conn, "no such CFM object");
|
2013-07-23 13:09:38 -07:00
|
|
|
goto out;
|
2012-01-26 18:58:51 -08:00
|
|
|
}
|
|
|
|
cfm->fault_override = fault_override;
|
2014-04-03 10:20:44 -07:00
|
|
|
cfm_status_changed(cfm);
|
2012-01-26 18:58:51 -08:00
|
|
|
} else {
|
2013-07-23 13:09:38 -07:00
|
|
|
HMAP_FOR_EACH (cfm, hmap_node, all_cfms) {
|
2012-01-26 18:58:51 -08:00
|
|
|
cfm->fault_override = fault_override;
|
2014-04-03 10:20:44 -07:00
|
|
|
cfm_status_changed(cfm);
|
2012-01-26 18:58:51 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-14 20:53:59 -08:00
|
|
|
unixctl_command_reply(conn, "OK");
|
2013-07-23 13:09:38 -07:00
|
|
|
|
|
|
|
out:
|
|
|
|
ovs_mutex_unlock(&mutex);
|
2012-01-26 18:58:51 -08:00
|
|
|
}
|