linux/net/bridge/br_cfm.c

868 lines
20 KiB
C
Raw Normal View History

bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/cfm_bridge.h>
#include <uapi/linux/cfm_bridge.h>
#include "br_private_cfm.h"
static struct br_cfm_mep *br_mep_find(struct net_bridge *br, u32 instance)
{
struct br_cfm_mep *mep;
hlist_for_each_entry(mep, &br->mep_list, head)
if (mep->instance == instance)
return mep;
return NULL;
}
static struct br_cfm_mep *br_mep_find_ifindex(struct net_bridge *br,
u32 ifindex)
{
struct br_cfm_mep *mep;
hlist_for_each_entry_rcu(mep, &br->mep_list, head,
lockdep_rtnl_is_held())
if (mep->create.ifindex == ifindex)
return mep;
return NULL;
}
static struct br_cfm_peer_mep *br_peer_mep_find(struct br_cfm_mep *mep,
u32 mepid)
{
struct br_cfm_peer_mep *peer_mep;
hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head,
lockdep_rtnl_is_held())
if (peer_mep->mepid == mepid)
return peer_mep;
return NULL;
}
static struct net_bridge_port *br_mep_get_port(struct net_bridge *br,
u32 ifindex)
{
struct net_bridge_port *port;
list_for_each_entry(port, &br->port_list, list)
if (port->dev->ifindex == ifindex)
return port;
return NULL;
}
/* Calculate the CCM interval in us. */
static u32 interval_to_us(enum br_cfm_ccm_interval interval)
{
switch (interval) {
case BR_CFM_CCM_INTERVAL_NONE:
return 0;
case BR_CFM_CCM_INTERVAL_3_3_MS:
return 3300;
case BR_CFM_CCM_INTERVAL_10_MS:
return 10 * 1000;
case BR_CFM_CCM_INTERVAL_100_MS:
return 100 * 1000;
case BR_CFM_CCM_INTERVAL_1_SEC:
return 1000 * 1000;
case BR_CFM_CCM_INTERVAL_10_SEC:
return 10 * 1000 * 1000;
case BR_CFM_CCM_INTERVAL_1_MIN:
return 60 * 1000 * 1000;
case BR_CFM_CCM_INTERVAL_10_MIN:
return 10 * 60 * 1000 * 1000;
}
return 0;
}
/* Convert the interface interval to CCM PDU value. */
static u32 interval_to_pdu(enum br_cfm_ccm_interval interval)
{
switch (interval) {
case BR_CFM_CCM_INTERVAL_NONE:
return 0;
case BR_CFM_CCM_INTERVAL_3_3_MS:
return 1;
case BR_CFM_CCM_INTERVAL_10_MS:
return 2;
case BR_CFM_CCM_INTERVAL_100_MS:
return 3;
case BR_CFM_CCM_INTERVAL_1_SEC:
return 4;
case BR_CFM_CCM_INTERVAL_10_SEC:
return 5;
case BR_CFM_CCM_INTERVAL_1_MIN:
return 6;
case BR_CFM_CCM_INTERVAL_10_MIN:
return 7;
}
return 0;
}
/* Convert the CCM PDU value to interval on interface. */
static u32 pdu_to_interval(u32 value)
{
switch (value) {
case 0:
return BR_CFM_CCM_INTERVAL_NONE;
case 1:
return BR_CFM_CCM_INTERVAL_3_3_MS;
case 2:
return BR_CFM_CCM_INTERVAL_10_MS;
case 3:
return BR_CFM_CCM_INTERVAL_100_MS;
case 4:
return BR_CFM_CCM_INTERVAL_1_SEC;
case 5:
return BR_CFM_CCM_INTERVAL_10_SEC;
case 6:
return BR_CFM_CCM_INTERVAL_1_MIN;
case 7:
return BR_CFM_CCM_INTERVAL_10_MIN;
}
return BR_CFM_CCM_INTERVAL_NONE;
}
static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep)
{
u32 interval_us;
interval_us = interval_to_us(peer_mep->mep->cc_config.exp_interval);
/* Function ccm_rx_dwork must be called with 1/4
* of the configured CC 'expected_interval'
* in order to detect CCM defect after 3.25 interval.
*/
queue_delayed_work(system_wq, &peer_mep->ccm_rx_dwork,
usecs_to_jiffies(interval_us / 4));
}
bridge: cfm: Netlink Notifications. This is the implementation of Netlink notifications out of CFM. Notifications are initiated whenever a state change happens in CFM. IFLA_BRIDGE_CFM: Points to the CFM information. IFLA_BRIDGE_CFM_MEP_STATUS_INFO: This indicate that the MEP instance status are following. IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO: This indicate that the peer MEP status are following. CFM nested attribute has the following attributes in next level. IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected Opcode. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected version. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN: The MEP instance received CCM PDU with MD level lower than configured level. This frame is discarded. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID: The added Peer MEP ID of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT: The CCM defect status. The type is NLA_U32 (bool). True means no CCM frame is received for 3.25 intervals. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL. IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI: The last received CCM PDU RDI. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE: The last received CCM PDU Port Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE: The last received CCM PDU Interface Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN: A CCM frame has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN: A CCM frame with TLV has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN: A CCM frame with unexpected sequence number has been received from Peer MEP. The type is NLA_U32 (bool). When a sequence number is not one higher than previously received then it is unexpected. This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:51 +08:00
static void br_cfm_notify(int event, const struct net_bridge_port *port)
{
u32 filter = RTEXT_FILTER_CFM_STATUS;
return br_info_notify(event, port->br, NULL, filter);
}
static void cc_peer_enable(struct br_cfm_peer_mep *peer_mep)
{
memset(&peer_mep->cc_status, 0, sizeof(peer_mep->cc_status));
peer_mep->ccm_rx_count_miss = 0;
ccm_rx_timer_start(peer_mep);
}
static void cc_peer_disable(struct br_cfm_peer_mep *peer_mep)
{
cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
}
static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
const struct br_cfm_cc_ccm_tx_info *const tx_info)
{
struct br_cfm_common_hdr *common_hdr;
struct net_bridge_port *b_port;
struct br_cfm_maid *maid;
u8 *itu_reserved, *e_tlv;
struct ethhdr *eth_hdr;
struct sk_buff *skb;
__be32 *status_tlv;
__be32 *snumber;
__be16 *mepid;
skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH);
if (!skb)
return NULL;
rcu_read_lock();
b_port = rcu_dereference(mep->b_port);
if (!b_port) {
kfree_skb(skb);
rcu_read_unlock();
return NULL;
}
skb->dev = b_port->dev;
rcu_read_unlock();
/* The device cannot be deleted until the work_queue functions has
* completed. This function is called from ccm_tx_work_expired()
* that is a work_queue functions.
*/
skb->protocol = htons(ETH_P_CFM);
skb->priority = CFM_FRAME_PRIO;
/* Ethernet header */
eth_hdr = skb_put(skb, sizeof(*eth_hdr));
ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr);
ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr);
eth_hdr->h_proto = htons(ETH_P_CFM);
/* Common CFM Header */
common_hdr = skb_put(skb, sizeof(*common_hdr));
common_hdr->mdlevel_version = mep->config.mdlevel << 5;
common_hdr->opcode = BR_CFM_OPCODE_CCM;
common_hdr->flags = (mep->rdi << 7) |
interval_to_pdu(mep->cc_config.exp_interval);
common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET;
/* Sequence number */
snumber = skb_put(skb, sizeof(*snumber));
if (tx_info->seq_no_update) {
*snumber = cpu_to_be32(mep->ccm_tx_snumber);
mep->ccm_tx_snumber += 1;
} else {
*snumber = 0;
}
mepid = skb_put(skb, sizeof(*mepid));
*mepid = cpu_to_be16((u16)mep->config.mepid);
maid = skb_put(skb, sizeof(*maid));
memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data));
/* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */
itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE);
memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE);
/* Generel CFM TLV format:
* TLV type: one byte
* TLV value length: two bytes
* TLV value: 'TLV value length' bytes
*/
/* Port status TLV. The value length is 1. Total of 4 bytes. */
if (tx_info->port_tlv) {
status_tlv = skb_put(skb, sizeof(*status_tlv));
*status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) |
(1 << 8) | /* Value length */
(tx_info->port_tlv_value & 0xFF));
}
/* Interface status TLV. The value length is 1. Total of 4 bytes. */
if (tx_info->if_tlv) {
status_tlv = skb_put(skb, sizeof(*status_tlv));
*status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) |
(1 << 8) | /* Value length */
(tx_info->if_tlv_value & 0xFF));
}
/* End TLV */
e_tlv = skb_put(skb, sizeof(*e_tlv));
*e_tlv = CFM_ENDE_TLV_TYPE;
return skb;
}
static void ccm_frame_tx(struct sk_buff *skb)
{
skb_reset_network_header(skb);
dev_queue_xmit(skb);
}
/* This function is called with the configured CC 'expected_interval'
* in order to drive CCM transmission when enabled.
*/
static void ccm_tx_work_expired(struct work_struct *work)
{
struct delayed_work *del_work;
struct br_cfm_mep *mep;
struct sk_buff *skb;
u32 interval_us;
del_work = to_delayed_work(work);
mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork);
if (time_before_eq(mep->ccm_tx_end, jiffies)) {
/* Transmission period has ended */
mep->cc_ccm_tx_info.period = 0;
return;
}
skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
if (skb)
ccm_frame_tx(skb);
interval_us = interval_to_us(mep->cc_config.exp_interval);
queue_delayed_work(system_wq, &mep->ccm_tx_dwork,
usecs_to_jiffies(interval_us));
}
/* This function is called with 1/4 of the configured CC 'expected_interval'
* in order to detect CCM defect after 3.25 interval.
*/
static void ccm_rx_work_expired(struct work_struct *work)
{
struct br_cfm_peer_mep *peer_mep;
bridge: cfm: Netlink Notifications. This is the implementation of Netlink notifications out of CFM. Notifications are initiated whenever a state change happens in CFM. IFLA_BRIDGE_CFM: Points to the CFM information. IFLA_BRIDGE_CFM_MEP_STATUS_INFO: This indicate that the MEP instance status are following. IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO: This indicate that the peer MEP status are following. CFM nested attribute has the following attributes in next level. IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected Opcode. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected version. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN: The MEP instance received CCM PDU with MD level lower than configured level. This frame is discarded. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID: The added Peer MEP ID of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT: The CCM defect status. The type is NLA_U32 (bool). True means no CCM frame is received for 3.25 intervals. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL. IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI: The last received CCM PDU RDI. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE: The last received CCM PDU Port Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE: The last received CCM PDU Interface Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN: A CCM frame has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN: A CCM frame with TLV has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN: A CCM frame with unexpected sequence number has been received from Peer MEP. The type is NLA_U32 (bool). When a sequence number is not one higher than previously received then it is unexpected. This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:51 +08:00
struct net_bridge_port *b_port;
struct delayed_work *del_work;
del_work = to_delayed_work(work);
peer_mep = container_of(del_work, struct br_cfm_peer_mep, ccm_rx_dwork);
/* After 13 counts (4 * 3,25) then 3.25 intervals are expired */
if (peer_mep->ccm_rx_count_miss < 13) {
/* 3.25 intervals are NOT expired without CCM reception */
peer_mep->ccm_rx_count_miss++;
/* Start timer again */
ccm_rx_timer_start(peer_mep);
} else {
/* 3.25 intervals are expired without CCM reception.
* CCM defect detected
*/
peer_mep->cc_status.ccm_defect = true;
bridge: cfm: Netlink Notifications. This is the implementation of Netlink notifications out of CFM. Notifications are initiated whenever a state change happens in CFM. IFLA_BRIDGE_CFM: Points to the CFM information. IFLA_BRIDGE_CFM_MEP_STATUS_INFO: This indicate that the MEP instance status are following. IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO: This indicate that the peer MEP status are following. CFM nested attribute has the following attributes in next level. IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected Opcode. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected version. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN: The MEP instance received CCM PDU with MD level lower than configured level. This frame is discarded. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID: The added Peer MEP ID of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT: The CCM defect status. The type is NLA_U32 (bool). True means no CCM frame is received for 3.25 intervals. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL. IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI: The last received CCM PDU RDI. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE: The last received CCM PDU Port Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE: The last received CCM PDU Interface Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN: A CCM frame has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN: A CCM frame with TLV has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN: A CCM frame with unexpected sequence number has been received from Peer MEP. The type is NLA_U32 (bool). When a sequence number is not one higher than previously received then it is unexpected. This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:51 +08:00
/* Change in CCM defect status - notify */
rcu_read_lock();
b_port = rcu_dereference(peer_mep->mep->b_port);
if (b_port)
br_cfm_notify(RTM_NEWLINK, b_port);
rcu_read_unlock();
}
}
static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index,
struct br_cfm_peer_mep *peer_mep)
{
__be32 *s_tlv;
__be32 _s_tlv;
u32 h_s_tlv;
u8 *e_tlv;
u8 _e_tlv;
e_tlv = skb_header_pointer(skb, index, sizeof(_e_tlv), &_e_tlv);
if (!e_tlv)
return 0;
/* TLV is present - get the status TLV */
s_tlv = skb_header_pointer(skb,
index,
sizeof(_s_tlv), &_s_tlv);
if (!s_tlv)
return 0;
h_s_tlv = ntohl(*s_tlv);
if ((h_s_tlv >> 24) == CFM_IF_STATUS_TLV_TYPE) {
/* Interface status TLV */
peer_mep->cc_status.tlv_seen = true;
peer_mep->cc_status.if_tlv_value = (h_s_tlv & 0xFF);
}
if ((h_s_tlv >> 24) == CFM_PORT_STATUS_TLV_TYPE) {
/* Port status TLV */
peer_mep->cc_status.tlv_seen = true;
peer_mep->cc_status.port_tlv_value = (h_s_tlv & 0xFF);
}
/* The Sender ID TLV is not handled */
/* The Organization-Specific TLV is not handled */
/* Return the length of this tlv.
* This is the length of the value field plus 3 bytes for size of type
* field and length field
*/
return ((h_s_tlv >> 8) & 0xFFFF) + 3;
}
/* note: already called with rcu_read_lock */
static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
{
u32 mdlevel, interval, size, index, max;
const struct br_cfm_common_hdr *hdr;
struct br_cfm_peer_mep *peer_mep;
const struct br_cfm_maid *maid;
struct br_cfm_common_hdr _hdr;
struct br_cfm_maid _maid;
struct br_cfm_mep *mep;
struct net_bridge *br;
__be32 *snumber;
__be32 _snumber;
__be16 *mepid;
__be16 _mepid;
if (port->state == BR_STATE_DISABLED)
return 0;
hdr = skb_header_pointer(skb, 0, sizeof(_hdr), &_hdr);
if (!hdr)
return 1;
br = port->br;
mep = br_mep_find_ifindex(br, port->dev->ifindex);
if (unlikely(!mep))
/* No MEP on this port - must be forwarded */
return 0;
mdlevel = hdr->mdlevel_version >> 5;
if (mdlevel > mep->config.mdlevel)
/* The level is above this MEP level - must be forwarded */
return 0;
if ((hdr->mdlevel_version & 0x1F) != 0) {
/* Invalid version */
mep->status.version_unexp_seen = true;
return 1;
}
if (mdlevel < mep->config.mdlevel) {
/* The level is below this MEP level */
mep->status.rx_level_low_seen = true;
return 1;
}
if (hdr->opcode == BR_CFM_OPCODE_CCM) {
/* CCM PDU received. */
/* MA ID is after common header + sequence number + MEP ID */
maid = skb_header_pointer(skb,
CFM_CCM_PDU_MAID_OFFSET,
sizeof(_maid), &_maid);
if (!maid)
return 1;
if (memcmp(maid->data, mep->cc_config.exp_maid.data,
sizeof(maid->data)))
/* MA ID not as expected */
return 1;
/* MEP ID is after common header + sequence number */
mepid = skb_header_pointer(skb,
CFM_CCM_PDU_MEPID_OFFSET,
sizeof(_mepid), &_mepid);
if (!mepid)
return 1;
peer_mep = br_peer_mep_find(mep, (u32)ntohs(*mepid));
if (!peer_mep)
return 1;
/* Interval is in common header flags */
interval = hdr->flags & 0x07;
if (mep->cc_config.exp_interval != pdu_to_interval(interval))
/* Interval not as expected */
return 1;
/* A valid CCM frame is received */
if (peer_mep->cc_status.ccm_defect) {
peer_mep->cc_status.ccm_defect = false;
bridge: cfm: Netlink Notifications. This is the implementation of Netlink notifications out of CFM. Notifications are initiated whenever a state change happens in CFM. IFLA_BRIDGE_CFM: Points to the CFM information. IFLA_BRIDGE_CFM_MEP_STATUS_INFO: This indicate that the MEP instance status are following. IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO: This indicate that the peer MEP status are following. CFM nested attribute has the following attributes in next level. IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected Opcode. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected version. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN: The MEP instance received CCM PDU with MD level lower than configured level. This frame is discarded. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID: The added Peer MEP ID of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT: The CCM defect status. The type is NLA_U32 (bool). True means no CCM frame is received for 3.25 intervals. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL. IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI: The last received CCM PDU RDI. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE: The last received CCM PDU Port Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE: The last received CCM PDU Interface Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN: A CCM frame has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN: A CCM frame with TLV has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN: A CCM frame with unexpected sequence number has been received from Peer MEP. The type is NLA_U32 (bool). When a sequence number is not one higher than previously received then it is unexpected. This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:51 +08:00
/* Change in CCM defect status - notify */
br_cfm_notify(RTM_NEWLINK, port);
/* Start CCM RX timer */
ccm_rx_timer_start(peer_mep);
}
peer_mep->cc_status.seen = true;
peer_mep->ccm_rx_count_miss = 0;
/* RDI is in common header flags */
peer_mep->cc_status.rdi = (hdr->flags & 0x80) ? true : false;
/* Sequence number is after common header */
snumber = skb_header_pointer(skb,
CFM_CCM_PDU_SEQNR_OFFSET,
sizeof(_snumber), &_snumber);
if (!snumber)
return 1;
if (ntohl(*snumber) != (mep->ccm_rx_snumber + 1))
/* Unexpected sequence number */
peer_mep->cc_status.seq_unexp_seen = true;
mep->ccm_rx_snumber = ntohl(*snumber);
/* TLV end is after common header + sequence number + MEP ID +
* MA ID + ITU reserved
*/
index = CFM_CCM_PDU_TLV_OFFSET;
max = 0;
do { /* Handle all TLVs */
size = ccm_tlv_extract(skb, index, peer_mep);
index += size;
max += 1;
} while (size != 0 && max < 4); /* Max four TLVs possible */
return 1;
}
mep->status.opcode_unexp_seen = true;
return 1;
}
static struct br_frame_type cfm_frame_type __read_mostly = {
.type = cpu_to_be16(ETH_P_CFM),
.frame_handler = br_cfm_frame_rx,
};
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
int br_cfm_mep_create(struct net_bridge *br,
const u32 instance,
struct br_cfm_mep_create *const create,
struct netlink_ext_ack *extack)
{
struct net_bridge_port *p;
struct br_cfm_mep *mep;
ASSERT_RTNL();
if (create->domain == BR_CFM_VLAN) {
NL_SET_ERR_MSG_MOD(extack,
"VLAN domain not supported");
return -EINVAL;
}
if (create->domain != BR_CFM_PORT) {
NL_SET_ERR_MSG_MOD(extack,
"Invalid domain value");
return -EINVAL;
}
if (create->direction == BR_CFM_MEP_DIRECTION_UP) {
NL_SET_ERR_MSG_MOD(extack,
"Up-MEP not supported");
return -EINVAL;
}
if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) {
NL_SET_ERR_MSG_MOD(extack,
"Invalid direction value");
return -EINVAL;
}
p = br_mep_get_port(br, create->ifindex);
if (!p) {
NL_SET_ERR_MSG_MOD(extack,
"Port is not related to bridge");
return -EINVAL;
}
mep = br_mep_find(br, instance);
if (mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance already exists");
return -EEXIST;
}
/* In PORT domain only one instance can be created per port */
if (create->domain == BR_CFM_PORT) {
mep = br_mep_find_ifindex(br, create->ifindex);
if (mep) {
NL_SET_ERR_MSG_MOD(extack,
"Only one Port MEP on a port allowed");
return -EINVAL;
}
}
mep = kzalloc(sizeof(*mep), GFP_KERNEL);
if (!mep)
return -ENOMEM;
mep->create = *create;
mep->instance = instance;
rcu_assign_pointer(mep->b_port, p);
INIT_HLIST_HEAD(&mep->peer_mep_list);
INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
if (hlist_empty(&br->mep_list))
br_add_frame(br, &cfm_frame_type);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
hlist_add_tail_rcu(&mep->head, &br->mep_list);
return 0;
}
static void mep_delete_implementation(struct net_bridge *br,
struct br_cfm_mep *mep)
{
struct br_cfm_peer_mep *peer_mep;
struct hlist_node *n_store;
ASSERT_RTNL();
/* Empty and free peer MEP list */
hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);
}
cancel_delayed_work_sync(&mep->ccm_tx_dwork);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
RCU_INIT_POINTER(mep->b_port, NULL);
hlist_del_rcu(&mep->head);
kfree_rcu(mep, rcu);
if (hlist_empty(&br->mep_list))
br_del_frame(br, &cfm_frame_type);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
}
int br_cfm_mep_delete(struct net_bridge *br,
const u32 instance,
struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
mep_delete_implementation(br, mep);
return 0;
}
int br_cfm_mep_config_set(struct net_bridge *br,
const u32 instance,
const struct br_cfm_mep_config *const config,
struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
mep->config = *config;
return 0;
}
int br_cfm_cc_config_set(struct net_bridge *br,
const u32 instance,
const struct br_cfm_cc_config *const config,
struct netlink_ext_ack *extack)
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
/* Check for no change in configuration */
if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
return 0;
if (config->enable && !mep->cc_config.enable)
/* CC is enabled */
hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
cc_peer_enable(peer_mep);
if (!config->enable && mep->cc_config.enable)
/* CC is disabled */
hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
cc_peer_disable(peer_mep);
mep->cc_config = *config;
mep->ccm_rx_snumber = 0;
mep->ccm_tx_snumber = 1;
return 0;
}
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
u32 mepid,
struct netlink_ext_ack *extack)
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
peer_mep = br_peer_mep_find(mep, mepid);
if (peer_mep) {
NL_SET_ERR_MSG_MOD(extack,
"Peer MEP-ID already exists");
return -EEXIST;
}
peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
if (!peer_mep)
return -ENOMEM;
peer_mep->mepid = mepid;
peer_mep->mep = mep;
INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
if (mep->cc_config.enable)
cc_peer_enable(peer_mep);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
return 0;
}
int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
u32 mepid,
struct netlink_ext_ack *extack)
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
peer_mep = br_peer_mep_find(mep, mepid);
if (!peer_mep) {
NL_SET_ERR_MSG_MOD(extack,
"Peer MEP-ID does not exists");
return -ENOENT;
}
cc_peer_disable(peer_mep);
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);
return 0;
}
int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
const bool rdi, struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
mep->rdi = rdi;
return 0;
}
int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
const struct br_cfm_cc_ccm_tx_info *const tx_info,
struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
ASSERT_RTNL();
mep = br_mep_find(br, instance);
if (!mep) {
NL_SET_ERR_MSG_MOD(extack,
"MEP instance does not exists");
return -ENOENT;
}
if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
/* No change in tx_info. */
if (mep->cc_ccm_tx_info.period == 0)
/* Transmission is not enabled - just return */
return 0;
/* Transmission is ongoing, the end time is recalculated */
mep->ccm_tx_end = jiffies +
usecs_to_jiffies(tx_info->period * 1000000);
return 0;
}
if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0)
/* Some change in info and transmission is not ongoing */
goto save;
if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
/* Some change in info and transmission is ongoing
* The end time is recalculated
*/
mep->ccm_tx_end = jiffies +
usecs_to_jiffies(tx_info->period * 1000000);
goto save;
}
if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
cancel_delayed_work_sync(&mep->ccm_tx_dwork);
goto save;
}
/* Start delayed work to transmit CCM frames. It is done with zero delay
* to send first frame immediately
*/
mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
save:
mep->cc_ccm_tx_info = *tx_info;
return 0;
}
bridge: cfm: Netlink Notifications. This is the implementation of Netlink notifications out of CFM. Notifications are initiated whenever a state change happens in CFM. IFLA_BRIDGE_CFM: Points to the CFM information. IFLA_BRIDGE_CFM_MEP_STATUS_INFO: This indicate that the MEP instance status are following. IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO: This indicate that the peer MEP status are following. CFM nested attribute has the following attributes in next level. IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected Opcode. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN: The MEP instance received CFM PDU with unexpected version. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN: The MEP instance received CCM PDU with MD level lower than configured level. This frame is discarded. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE: The MEP instance number of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID: The added Peer MEP ID of the delivered status. The type is NLA_U32. IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT: The CCM defect status. The type is NLA_U32 (bool). True means no CCM frame is received for 3.25 intervals. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL. IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI: The last received CCM PDU RDI. The type is NLA_U32 (bool). IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE: The last received CCM PDU Port Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE: The last received CCM PDU Interface Status TLV value field. The type is NLA_U8. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN: A CCM frame has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN: A CCM frame with TLV has been received from Peer MEP. The type is NLA_U32 (bool). This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN: A CCM frame with unexpected sequence number has been received from Peer MEP. The type is NLA_U32 (bool). When a sequence number is not one higher than previously received then it is unexpected. This is cleared after GETLINK IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:51 +08:00
int br_cfm_mep_count(struct net_bridge *br, u32 *count)
{
struct br_cfm_mep *mep;
*count = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(mep, &br->mep_list, head)
*count += 1;
rcu_read_unlock();
return 0;
}
int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count)
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
*count = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(mep, &br->mep_list, head)
hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head)
*count += 1;
rcu_read_unlock();
return 0;
}
bridge: cfm: Netlink SET configuration Interface. This is the implementation of CFM netlink configuration set information interface. Add new nested netlink attributes. These attributes are used by the user space to create/delete/configure CFM instances. SETLINK: IFLA_BRIDGE_CFM: Indicate that the following attributes are CFM. IFLA_BRIDGE_CFM_MEP_CREATE: This indicate that a MEP instance must be created. IFLA_BRIDGE_CFM_MEP_DELETE: This indicate that a MEP instance must be deleted. IFLA_BRIDGE_CFM_MEP_CONFIG: This indicate that a MEP instance must be configured. IFLA_BRIDGE_CFM_CC_CONFIG: This indicate that a MEP instance Continuity Check (CC) functionality must be configured. IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD: This indicate that a CC Peer MEP must be added. IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE: This indicate that a CC Peer MEP must be removed. IFLA_BRIDGE_CFM_CC_CCM_TX: This indicate that the CC transmitted CCM PDU must be configured. IFLA_BRIDGE_CFM_CC_RDI: This indicate that the CC transmitted CCM PDU RDI must be configured. CFM nested attribute has the following attributes in next level. SETLINK RTEXT_FILTER_CFM_CONFIG: IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE: The created MEP instance number. The type is u32. IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN: The created MEP domain. The type is u32 (br_cfm_domain). It must be BR_CFM_PORT. This means that CFM frames are transmitted and received directly on the port - untagged. Not in a VLAN. IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION: The created MEP direction. The type is u32 (br_cfm_mep_direction). It must be BR_CFM_MEP_DIRECTION_DOWN. This means that CFM frames are transmitted and received on the port. Not in the bridge. IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX: The created MEP residence port ifindex. The type is u32 (ifindex). IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE: The deleted MEP instance number. The type is u32. IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE: The configured MEP instance number. The type is u32. IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC: The configured MEP unicast MAC address. The type is 6*u8 (array). This is used as SMAC in all transmitted CFM frames. IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL: The configured MEP unicast MD level. The type is u32. It must be in the range 1-7. No CFM frames are passing through this MEP on lower levels. IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID: The configured MEP ID. The type is u32. It must be in the range 0-0x1FFF. This MEP ID is inserted in any transmitted CCM frame. IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE: The configured MEP instance number. The type is u32. IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE: The Continuity Check (CC) functionality is enabled or disabled. The type is u32 (bool). IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL: The CC expected receive interval of CCM frames. The type is u32 (br_cfm_ccm_interval). This is also the transmission interval of CCM frames when enabled. IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID: The CC expected receive MAID in CCM frames. The type is CFM_MAID_LENGTH*u8. This is MAID is also inserted in transmitted CCM frames. IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE: The configured MEP instance number. The type is u32. IFLA_BRIDGE_CFM_CC_PEER_MEPID: The CC Peer MEP ID added. The type is u32. When a Peer MEP ID is added and CC is enabled it is expected to receive CCM frames from that Peer MEP. IFLA_BRIDGE_CFM_CC_RDI_INSTANCE: The configured MEP instance number. The type is u32. IFLA_BRIDGE_CFM_CC_RDI_RDI: The RDI that is inserted in transmitted CCM PDU. The type is u32 (bool). IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE: The configured MEP instance number. The type is u32. IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC: The transmitted CCM frame destination MAC address. The type is 6*u8 (array). This is used as DMAC in all transmitted CFM frames. IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE: The transmitted CCM frame update (increment) of sequence number is enabled or disabled. The type is u32 (bool). IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD: The period of time where CCM frame are transmitted. The type is u32. The time is given in seconds. SETLINK IFLA_BRIDGE_CFM_CC_CCM_TX must be done before timeout to keep transmission alive. When period is zero any ongoing CCM frame transmission will be stopped. IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV: The transmitted CCM frame update with Interface Status TLV is enabled or disabled. The type is u32 (bool). IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE: The transmitted Interface Status TLV value field. The type is u8. IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV: The transmitted CCM frame update with Port Status TLV is enabled or disabled. The type is u32 (bool). IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE: The transmitted Port Status TLV value field. The type is u8. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:48 +08:00
bool br_cfm_created(struct net_bridge *br)
{
return !hlist_empty(&br->mep_list);
}
bridge: cfm: Kernel space implementation of CFM. MEP create/delete. This is the first commit of the implementation of the CFM protocol according to 802.1Q section 12.14. It contains MEP instance create, delete and configuration. Connectivity Fault Management (CFM) comprises capabilities for detecting, verifying, and isolating connectivity failures in Virtual Bridged Networks. These capabilities can be used in networks operated by multiple independent organizations, each with restricted management access to each others equipment. CFM functions are partitioned as follows: - Path discovery - Fault detection - Fault verification and isolation - Fault notification - Fault recovery Interface consists of these functions: br_cfm_mep_create() br_cfm_mep_delete() br_cfm_mep_config_set() br_cfm_cc_config_set() br_cfm_cc_peer_mep_add() br_cfm_cc_peer_mep_remove() A MEP instance is created by br_cfm_mep_create() -It is the Maintenance association End Point described in 802.1Q section 19.2. -It is created on a specific level (1-7) and is assuring that no CFM frames are passing through this MEP on lower levels. -It initiates and validates CFM frames on its level. -It can only exist on a port that is related to a bridge. -Attributes given cannot be changed until the instance is deleted. A MEP instance can be deleted by br_cfm_mep_delete(). A created MEP instance has attributes that can be configured by br_cfm_mep_config_set(). A MEP Continuity Check feature can be configured by br_cfm_cc_config_set() The Continuity Check Receiver state machine can be enabled and disabled. According to 802.1Q section 19.2.8 A MEP can have Peer MEPs added and removed by br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove() The Continuity Check feature can maintain connectivity status on each added Peer MEP. Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com> Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-27 18:02:45 +08:00
/* Deletes the CFM instances on a specific bridge port
*/
void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
{
struct hlist_node *n_store;
struct br_cfm_mep *mep;
ASSERT_RTNL();
hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head)
if (mep->create.ifindex == port->dev->ifindex)
mep_delete_implementation(br, mep);
}