2020-10-01 10:34:03

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 00/11] net: bridge: cfm: Add support for Connectivity Fault Management(CFM)

Connectivity Fault Management (CFM) is defined in 802.1Q section 12.14.

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 other’s equipment.

CFM functions are partitioned as follows:
— Path discovery
— Fault detection
— Fault verification and isolation
— Fault notification
— Fault recovery

The primary CFM protocol shims are called Maintenance Points (MPs).
A MP can be either a MEP or a MHF.
The MEP:
-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 terminates/validates CFM frames on its level.
-It can only exist on a port that is related to a bridge.
The MHF:
-It is the Maintenance Domain Intermediate Point
(MIP) Half Function (MHF) described in 802.1Q section 19.3.
-It is created on a specific level (1-7).
-It is extracting/injecting certain CFM frame on this level.
-It can only exist on a port that is related to a bridge.
-Currently not supported.

There are defined the following CFM protocol functions:
-Continuity Check
-Loopback. Currently not supported.
-Linktrace. Currently not supported.

This CFM component supports create/delete of MEP instances and configuration of
the different CFM protocols. Also status information can be fetched and delivered
through notification due to defect status change.

The CFM component is trying to offload CFM functionality to HW by calling the
switchdev interface.

The user interacts with CFM using the 'cfm' user space client program,
the client talks with the kernel using netlink. The kernel will try to
offload the requests to the HW via switchdev API (not implemented yet).

Any notification emitted by CFM from the kernel can be monitored in user space
by starting 'cfm_server' program.

Currently this 'cfm' and 'cfm_server' programs are standalone placed in a cfm
repository https://github.com/microchip-ung/cfm but it is considered to integrate
this into 'iproute2'.

v1 -> v2
Added the CFM switchdev interface and also added utilization by calling the
interface from the kernel CFM implementation trying to offload CFM functionality
to HW. This offload (CFM driver) is currently not implemented.

Corrections based on RCF comments:
-The single CFM kernel implementation Patch is broken up into three patches.
-Changed the list of MEP instances from list_head to hlist_head.
-Removed unnecessary RCU list traversing.
-Solved RCU unlocking problem.
-Removed unnecessary comments.
-Added ASSERT_RTNL() where required.
-Shaping up on error messages.
-Correction NETLINK br_fill_ifinfo() to be able to handle 'filter_mask'
with multiple flags asserted.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>

Henrik Bjoernlund (11):
net: bridge: extend the process of special frames
bridge: cfm: Add BRIDGE_CFM to Kconfig.
bridge: uapi: cfm: Added EtherType used by the CFM protocol.
bridge: cfm: Kernel space implementation of CFM.
bridge: cfm: Kernel space implementation of CFM.
bridge: cfm: Kernel space implementation of CFM.
bridge: cfm: Netlink Interface.
bridge: cfm: Netlink Notifications.
bridge: cfm: Bridge port remove.
bridge: switchdev: cfm: switchdev interface implementation
bridge: cfm: Added CFM switchdev utilization.

include/linux/if_bridge.h | 13 +
include/net/switchdev.h | 115 ++++
include/uapi/linux/cfm_bridge.h | 70 ++
include/uapi/linux/if_bridge.h | 125 ++++
include/uapi/linux/if_ether.h | 1 +
include/uapi/linux/rtnetlink.h | 2 +
net/bridge/Kconfig | 11 +
net/bridge/Makefile | 2 +
net/bridge/br_cfm.c | 1092 +++++++++++++++++++++++++++++++
net/bridge/br_cfm_netlink.c | 728 +++++++++++++++++++++
net/bridge/br_cfm_switchdev.c | 203 ++++++
net/bridge/br_device.c | 4 +
net/bridge/br_if.c | 1 +
net/bridge/br_input.c | 31 +-
net/bridge/br_mrp.c | 19 +-
net/bridge/br_netlink.c | 138 +++-
net/bridge/br_private.h | 76 ++-
net/bridge/br_private_cfm.h | 208 ++++++
net/switchdev/switchdev.c | 54 ++
19 files changed, 2858 insertions(+), 35 deletions(-)
create mode 100644 include/uapi/linux/cfm_bridge.h
create mode 100644 net/bridge/br_cfm.c
create mode 100644 net/bridge/br_cfm_netlink.c
create mode 100644 net/bridge/br_cfm_switchdev.c
create mode 100644 net/bridge/br_private_cfm.h

--
2.28.0


2020-10-01 10:34:14

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 01/11] net: bridge: extend the process of special frames

This patch extends the processing of frames in the bridge. Currently MRP
frames needs special processing and the current implementation doesn't
allow a nice way to process different frame types. Therefore try to
improve this by adding a list that contains frame types that need
special processing. This list is iterated for each input frame and if
there is a match based on frame type then these functions will be called
and decide what to do with the frame. It can process the frame then the
bridge doesn't need to do anything or don't process so then the bridge
will do normal forwarding.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
net/bridge/br_device.c | 1 +
net/bridge/br_input.c | 31 ++++++++++++++++++++++++++++++-
net/bridge/br_mrp.c | 19 +++++++++++++++----
net/bridge/br_private.h | 18 ++++++++++++------
4 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 9a2fb4aa1a10..206c4ba51cd2 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -473,6 +473,7 @@ void br_dev_setup(struct net_device *dev)
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
INIT_HLIST_HEAD(&br->fdb_list);
+ INIT_HLIST_HEAD(&br->frame_type_list);
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
INIT_LIST_HEAD(&br->mrp_list);
#endif
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 59a318b9f646..8b2638cb550d 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -254,6 +254,21 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
return RX_HANDLER_CONSUMED;
}

+/* Return 0 if the frame was not processed otherwise 1
+ * note: already called with rcu_read_lock
+ */
+static int br_process_frame_type(struct net_bridge_port *p,
+ struct sk_buff *skb)
+{
+ struct br_frame_type *tmp;
+
+ hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list)
+ if (unlikely(tmp->type == skb->protocol))
+ return tmp->frame_handler(p, skb);
+
+ return 0;
+}
+
/*
* Return NULL if skb is handled
* note: already called with rcu_read_lock
@@ -343,7 +358,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
}
}

- if (unlikely(br_mrp_process(p, skb)))
+ if (unlikely(br_process_frame_type(p, skb)))
return RX_HANDLER_PASS;

forward:
@@ -380,3 +395,17 @@ rx_handler_func_t *br_get_rx_handler(const struct net_device *dev)

return br_handle_frame;
}
+
+void br_add_frame(struct net_bridge *br, struct br_frame_type *ft)
+{
+ hlist_add_head_rcu(&ft->list, &br->frame_type_list);
+}
+
+void br_del_frame(struct net_bridge *br, struct br_frame_type *ft)
+{
+ struct br_frame_type *tmp;
+
+ hlist_for_each_entry(tmp, &br->frame_type_list, list)
+ if (ft == tmp)
+ hlist_del_rcu(&ft->list);
+}
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index b36689e6e7cb..f94d72bb7c32 100644
--- a/net/bridge/br_mrp.c
+++ b/net/bridge/br_mrp.c
@@ -6,6 +6,13 @@
static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 };

+static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
+
+static struct br_frame_type mrp_frame_type __read_mostly = {
+ .type = cpu_to_be16(ETH_P_MRP),
+ .frame_handler = br_mrp_process,
+};
+
static bool br_mrp_is_ring_port(struct net_bridge_port *p_port,
struct net_bridge_port *s_port,
struct net_bridge_port *port)
@@ -445,6 +452,9 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)

list_del_rcu(&mrp->list);
kfree_rcu(mrp, rcu);
+
+ if (list_empty(&br->mrp_list))
+ br_del_frame(br, &mrp_frame_type);
}

/* Adds a new MRP instance.
@@ -493,6 +503,9 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
spin_unlock_bh(&br->lock);
rcu_assign_pointer(mrp->s_port, p);

+ if (list_empty(&br->mrp_list))
+ br_add_frame(br, &mrp_frame_type);
+
INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired);
list_add_tail_rcu(&mrp->list, &br->mrp_list);
@@ -1172,15 +1185,13 @@ static int br_mrp_rcv(struct net_bridge_port *p,
* normal forwarding.
* note: already called with rcu_read_lock
*/
-int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
{
/* If there is no MRP instance do normal forwarding */
if (likely(!(p->flags & BR_MRP_AWARE)))
goto out;

- if (unlikely(skb->protocol == htons(ETH_P_MRP)))
- return br_mrp_rcv(p, skb, p->dev);
-
+ return br_mrp_rcv(p, skb, p->dev);
out:
return 0;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 345118e35c42..747f6f08f439 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -89,6 +89,13 @@ struct bridge_mcast_stats {
};
#endif

+struct br_frame_type {
+ __be16 type;
+ int (*frame_handler)(struct net_bridge_port *port,
+ struct sk_buff *skb);
+ struct hlist_node list;
+};
+
struct br_vlan_stats {
u64 rx_bytes;
u64 rx_packets;
@@ -480,6 +487,8 @@ struct net_bridge {
#endif
struct hlist_head fdb_list;

+ struct hlist_head frame_type_list;
+
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
struct list_head mrp_list;
#endif
@@ -755,6 +764,9 @@ int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev);
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
rx_handler_func_t *br_get_rx_handler(const struct net_device *dev);

+void br_add_frame(struct net_bridge *br, struct br_frame_type *ft);
+void br_del_frame(struct net_bridge *br, struct br_frame_type *ft);
+
static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
{
return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev);
@@ -1417,7 +1429,6 @@ extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr)
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
-int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
bool br_mrp_enabled(struct net_bridge *br);
void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p);
int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br);
@@ -1429,11 +1440,6 @@ static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
return -EOPNOTSUPP;
}

-static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
-{
- return 0;
-}
-
static inline bool br_mrp_enabled(struct net_bridge *br)
{
return false;
--
2.28.0

2020-10-01 10:34:37

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 04/11] bridge: cfm: Kernel space implementation of CFM.

This is the first commit of the implementation of the CFM protocol
according to 802.1Q section 12.14.

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 other<E2><80><99>s 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.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/uapi/linux/cfm_bridge.h | 23 +++
net/bridge/Makefile | 2 +
net/bridge/br_cfm.c | 263 ++++++++++++++++++++++++++++++++
net/bridge/br_private_cfm.h | 61 ++++++++
4 files changed, 349 insertions(+)
create mode 100644 include/uapi/linux/cfm_bridge.h
create mode 100644 net/bridge/br_cfm.c
create mode 100644 net/bridge/br_private_cfm.h

diff --git a/include/uapi/linux/cfm_bridge.h b/include/uapi/linux/cfm_bridge.h
new file mode 100644
index 000000000000..a262a8c0e085
--- /dev/null
+++ b/include/uapi/linux/cfm_bridge.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+#ifndef _UAPI_LINUX_CFM_BRIDGE_H_
+#define _UAPI_LINUX_CFM_BRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define CFM_MAID_LENGTH 48
+
+/* MEP domain */
+enum br_cfm_domain {
+ BR_CFM_PORT,
+ BR_CFM_VLAN,
+};
+
+/* MEP direction */
+enum br_cfm_mep_direction {
+ BR_CFM_MEP_DIRECTION_DOWN,
+ BR_CFM_MEP_DIRECTION_UP,
+};
+
+#endif
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index ccb394236fbd..ddc0a9192348 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -27,3 +27,5 @@ bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
obj-$(CONFIG_NETFILTER) += netfilter/

bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
+
+bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
new file mode 100644
index 000000000000..86d6e7b73375
--- /dev/null
+++ b/net/bridge/br_cfm.c
@@ -0,0 +1,263 @@
+// 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;
+}
+
+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);
+
+ 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;
+
+ ASSERT_RTNL();
+
+ /* Empty and free peer MEP list */
+ hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head) {
+ hlist_del_rcu(&peer_mep->head);
+ kfree_rcu(peer_mep, rcu);
+ }
+
+ RCU_INIT_POINTER(mep->b_port, NULL);
+ hlist_del_rcu(&mep->head);
+ kfree_rcu(mep, rcu);
+}
+
+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();
+
+ if (config->mdlevel > 7) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "MD level is invalid");
+ return -EINVAL;
+ }
+ /* The MEP-ID is a 13 bit field in the CCM PDU identifying the MEP */
+ if (config->mepid > 0x1FFF) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "MEP-ID is invalid");
+ return -EINVAL;
+ }
+
+ 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_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;
+ }
+ /* The MEP-ID is a 13 bit field in the CCM PDU identifying the MEP */
+ if (mepid > 0x1FFF) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "MEP-ID is invalid");
+ return -EINVAL;
+ }
+
+ 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;
+
+ 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;
+ }
+
+ hlist_del_rcu(&peer_mep->head);
+ kfree_rcu(peer_mep, rcu);
+
+ return 0;
+}
diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
new file mode 100644
index 000000000000..40fe982added
--- /dev/null
+++ b/net/bridge/br_private_cfm.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _BR_PRIVATE_CFM_H_
+#define _BR_PRIVATE_CFM_H_
+
+#include "br_private.h"
+#include <uapi/linux/cfm_bridge.h>
+
+struct br_cfm_mep_create {
+ enum br_cfm_domain domain; /* Domain for this MEP */
+ enum br_cfm_mep_direction direction; /* Up or Down MEP direction */
+ u32 ifindex; /* Residence port */
+};
+
+int br_cfm_mep_create(struct net_bridge *br,
+ const u32 instance,
+ struct br_cfm_mep_create *const create,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_mep_delete(struct net_bridge *br,
+ const u32 instance,
+ struct netlink_ext_ack *extack);
+
+struct br_cfm_mep_config {
+ u32 mdlevel;
+ u32 mepid; /* MEPID for this MEP */
+ struct mac_addr unicast_mac; /* The MEP unicast MAC */
+};
+
+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);
+
+int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack);
+int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack);
+
+struct br_cfm_mep {
+ /* list header of MEP instances */
+ struct hlist_node head;
+ u32 instance;
+ struct br_cfm_mep_create create;
+ struct br_cfm_mep_config config;
+ /* List of multiple peer MEPs */
+ struct hlist_head peer_mep_list;
+ struct net_bridge_port __rcu *b_port;
+ struct rcu_head rcu;
+};
+
+struct br_cfm_peer_mep {
+ struct hlist_node head;
+ struct br_cfm_mep *mep;
+ u32 mepid;
+ struct rcu_head rcu;
+};
+
+#endif /* _BR_PRIVATE_CFM_H_ */
--
2.28.0

2020-10-01 10:34:51

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 09/11] bridge: cfm: Bridge port remove.

This is addition of CFM functionality to delete MEP instances
on a port that is removed from the bridge.
A MEP can only exist on a port that is related to a bridge.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>m
---
net/bridge/br_cfm.c | 13 +++++++++++++
net/bridge/br_if.c | 1 +
net/bridge/br_private.h | 6 ++++++
3 files changed, 20 insertions(+)

diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 6fbfef44c235..fc8268cb76c1 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -867,3 +867,16 @@ bool br_cfm_created(struct net_bridge *br)
{
return !hlist_empty(&br->mep_list);
}
+
+/* Deletes the CFM instances on a specific bridge port
+ */
+void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
+{
+ struct br_cfm_mep *mep;
+
+ ASSERT_RTNL();
+
+ hlist_for_each_entry(mep, &br->mep_list, head)
+ if (mep->create.ifindex == port->dev->ifindex)
+ mep_delete_implementation(br, mep);
+}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index a0e9a7937412..f7d2f472ae24 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -334,6 +334,7 @@ static void del_nbp(struct net_bridge_port *p)
spin_unlock_bh(&br->lock);

br_mrp_port_del(br, p);
+ br_cfm_port_del(br, p);

br_ifinfo_notify(RTM_DELLINK, NULL, p);

diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 5954ee45af80..735dd0028b40 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1465,6 +1465,7 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br)
int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
bool br_cfm_created(struct net_bridge *br);
+void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p);
int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
int br_cfm_status_fill_info(struct sk_buff *skb,
struct net_bridge *br,
@@ -1484,6 +1485,11 @@ static inline bool br_cfm_created(struct net_bridge *br)
return false;
}

+static inline void br_cfm_port_del(struct net_bridge *br,
+ struct net_bridge_port *p)
+{
+}
+
static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
{
return -EOPNOTSUPP;
--
2.28.0

2020-10-01 10:35:03

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 06/11] bridge: cfm: Kernel space implementation of CFM.

This is the third commit of the implementation of the CFM protocol
according to 802.1Q section 12.14.

Functionality is extended with CCM frame reception.
The MEP instance now contains CCM based status information.
Most important is the CCM defect status indicating if correct
CCM frames are received with the expected interval.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/uapi/linux/cfm_bridge.h | 10 ++
net/bridge/br_cfm.c | 269 ++++++++++++++++++++++++++++++++
net/bridge/br_private_cfm.h | 32 ++++
3 files changed, 311 insertions(+)

diff --git a/include/uapi/linux/cfm_bridge.h b/include/uapi/linux/cfm_bridge.h
index 84a3817da90b..4be195cc6b70 100644
--- a/include/uapi/linux/cfm_bridge.h
+++ b/include/uapi/linux/cfm_bridge.h
@@ -20,6 +20,10 @@
CFM_IF_STATUS_TLV_LENGTH)
#define CFM_FRAME_PRIO 7
#define CFM_CCM_TLV_OFFSET 70
+#define CFM_CCM_PDU_MAID_OFFSET 10
+#define CFM_CCM_PDU_MEPID_OFFSET 8
+#define CFM_CCM_PDU_SEQNR_OFFSET 4
+#define CFM_CCM_PDU_TLV_OFFSET 74
#define CFM_CCM_ITU_RESERVED_SIZE 16

struct br_cfm_common_hdr {
@@ -29,6 +33,12 @@ struct br_cfm_common_hdr {
__u8 tlv_offset;
};

+struct br_cfm_status_tlv {
+ __u8 type;
+ __be16 length;
+ __u8 value;
+};
+
enum br_cfm_opcodes {
BR_CFM_OPCODE_CCM = 0x1,
};
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 19ee95cddc46..6373c80f204a 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -101,6 +101,56 @@ static u32 interval_to_pdu(enum br_cfm_ccm_interval interval)
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));
+}
+
+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)

@@ -231,6 +281,200 @@ static void ccm_tx_work_expired(struct work_struct *work)
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;
+ 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;
+ }
+}
+
+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;
+
+ /* 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,
+};
+
int br_cfm_mep_create(struct net_bridge *br,
const u32 instance,
struct br_cfm_mep_create *const create,
@@ -295,6 +539,9 @@ int br_cfm_mep_create(struct net_bridge *br,
INIT_HLIST_HEAD(&mep->peer_mep_list);
INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);

+ if (hlist_empty(&br->mep_list))
+ br_add_frame(br, &cfm_frame_type);
+
hlist_add_tail_rcu(&mep->head, &br->mep_list);

return 0;
@@ -309,6 +556,7 @@ static void mep_delete_implementation(struct net_bridge *br,

/* Empty and free peer MEP list */
hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head) {
+ cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);
}
@@ -318,6 +566,9 @@ static void mep_delete_implementation(struct net_bridge *br,
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);
}

int br_cfm_mep_delete(struct net_bridge *br,
@@ -378,6 +629,7 @@ int br_cfm_cc_config_set(struct net_bridge *br,
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();
@@ -393,7 +645,18 @@ int br_cfm_cc_config_set(struct net_bridge *br,
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;
@@ -434,6 +697,10 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,

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);

hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);

@@ -463,6 +730,8 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
return -ENOENT;
}

+ cc_peer_disable(peer_mep);
+
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);

diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
index 8d1b449acfbf..6a2294c0ea79 100644
--- a/net/bridge/br_private_cfm.h
+++ b/net/bridge/br_private_cfm.h
@@ -43,6 +43,8 @@ struct br_cfm_cc_config {
/* Expected received CCM PDU interval. */
/* Transmitting CCM PDU interval when CCM tx is enabled. */
enum br_cfm_ccm_interval exp_interval;
+
+ bool enable; /* Enable/disable CCM PDU handling */
};

int br_cfm_cc_config_set(struct net_bridge *br,
@@ -87,6 +89,31 @@ 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_status {
+ /* Indications that an OAM PDU has been seen. */
+ bool opcode_unexp_seen; /* RX of OAM PDU with unexpected opcode */
+ bool version_unexp_seen; /* RX of OAM PDU with unexpected version */
+ bool rx_level_low_seen; /* Rx of OAM PDU with level low */
+};
+
+struct br_cfm_cc_peer_status {
+ /* This CCM related status is based on the latest received CCM PDU. */
+ u8 port_tlv_value; /* Port Status TLV value */
+ u8 if_tlv_value; /* Interface Status TLV value */
+
+ /* CCM has not been received for 3.25 intervals */
+ bool ccm_defect;
+
+ /* (RDI == 1) for last received CCM PDU */
+ bool rdi;
+
+ /* Indications that a CCM PDU has been seen. */
+ bool seen; /* CCM PDU received */
+ bool tlv_seen; /* CCM PDU with TLV received */
+ /* CCM PDU with unexpected sequence number received */
+ bool seq_unexp_seen;
+};
+
struct br_cfm_mep {
/* list header of MEP instances */
struct hlist_node head;
@@ -101,6 +128,8 @@ struct br_cfm_mep {
unsigned long ccm_tx_end;
struct delayed_work ccm_tx_dwork;
u32 ccm_tx_snumber;
+ u32 ccm_rx_snumber;
+ struct br_cfm_mep_status status;
bool rdi;
struct rcu_head rcu;
};
@@ -108,7 +137,10 @@ struct br_cfm_mep {
struct br_cfm_peer_mep {
struct hlist_node head;
struct br_cfm_mep *mep;
+ struct delayed_work ccm_rx_dwork;
u32 mepid;
+ struct br_cfm_cc_peer_status cc_status;
+ u32 ccm_rx_count_miss;
struct rcu_head rcu;
};

--
2.28.0

2020-10-01 10:35:42

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 07/11] bridge: cfm: Netlink Interface.

This is the implementation of CFM netlink configuration
and status information interface.

Add new nested netlink attributes. These attributes are used by the
user space to create/delete/configure CFM instances and get status.
Also they are used by the kernel to notify the user space when changes
in any status happens.

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.

GETLINK:
Request filter RTEXT_FILTER_CFM_CONFIG:
Indicating that CFM configuration information must be delivered.

IFLA_BRIDGE_CFM:
Points to the CFM information.

IFLA_BRIDGE_CFM_MEP_CREATE_INFO:
This indicate that MEP instance create parameters are following.
IFLA_BRIDGE_CFM_MEP_CONFIG_INFO:
This indicate that MEP instance config parameters are following.
IFLA_BRIDGE_CFM_CC_CONFIG_INFO:
This indicate that MEP instance CC functionality
parameters are following.
IFLA_BRIDGE_CFM_CC_RDI_INFO:
This indicate that CC transmitted CCM PDU RDI
parameters are following.
IFLA_BRIDGE_CFM_CC_CCM_TX_INFO:
This indicate that CC transmitted CCM PDU parameters are
following.
IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO:
This indicate that the added peer MEP IDs are following.

Request filter RTEXT_FILTER_CFM_STATUS:
Indicating that CFM status information must be delivered.

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.

SETLINK and GETLINK 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.

GETLINK RTEXT_FILTER_CFM_STATUS:
IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE:
The MEP instance number of the delivered status.
The type is u32.
IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN:
The MEP instance received CFM PDU with unexpected Opcode.
The type is u32 (bool).
IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN:
The MEP instance received CFM PDU with unexpected version.
The type is 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 u32 (bool).

IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE:
The MEP instance number of the delivered status.
The type is u32.
IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID:
The added Peer MEP ID of the delivered status.
The type is u32.
IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT:
The CCM defect status.
The type is 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 u32 (bool).
IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE:
The last received CCM PDU Port Status TLV value field.
The type is u8.
IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE:
The last received CCM PDU Interface Status TLV value field.
The type is u8.
IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN:
A CCM frame has been received from Peer MEP.
The type is 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 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 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.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/uapi/linux/if_bridge.h | 125 ++++++
include/uapi/linux/rtnetlink.h | 2 +
net/bridge/Makefile | 2 +-
net/bridge/br_cfm.c | 5 +
net/bridge/br_cfm_netlink.c | 724 +++++++++++++++++++++++++++++++++
net/bridge/br_netlink.c | 67 ++-
net/bridge/br_private.h | 31 ++
7 files changed, 940 insertions(+), 16 deletions(-)
create mode 100644 net/bridge/br_cfm_netlink.c

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 4c687686aa8f..8cfecd441e48 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -121,6 +121,7 @@ enum {
IFLA_BRIDGE_VLAN_INFO,
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
IFLA_BRIDGE_MRP,
+ IFLA_BRIDGE_CFM,
__IFLA_BRIDGE_MAX,
};
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -328,6 +329,130 @@ struct br_mrp_start_in_test {
__u16 in_id;
};

+enum {
+ IFLA_BRIDGE_CFM_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CREATE,
+ IFLA_BRIDGE_CFM_MEP_DELETE,
+ IFLA_BRIDGE_CFM_MEP_CONFIG,
+ IFLA_BRIDGE_CFM_CC_CONFIG,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE,
+ IFLA_BRIDGE_CFM_CC_RDI,
+ IFLA_BRIDGE_CFM_CC_CCM_TX,
+ IFLA_BRIDGE_CFM_MEP_CREATE_INFO,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_INFO,
+ IFLA_BRIDGE_CFM_CC_CONFIG_INFO,
+ IFLA_BRIDGE_CFM_CC_RDI_INFO,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_INFO,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO,
+ IFLA_BRIDGE_CFM_MEP_STATUS_INFO,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO,
+ __IFLA_BRIDGE_CFM_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN,
+ IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION,
+ IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX,
+ __IFLA_BRIDGE_CFM_MEP_CREATE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE,
+ __IFLA_BRIDGE_CFM_MEP_DELETE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID,
+ __IFLA_BRIDGE_CFM_MEP_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
+ IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
+ IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
+ __IFLA_BRIDGE_CFM_MEP_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE,
+ IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL,
+ IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID,
+ __IFLA_BRIDGE_CFM_CC_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_PEER_MEPID,
+ __IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_RDI_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_RDI_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_RDI_RDI,
+ __IFLA_BRIDGE_CFM_CC_RDI_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE,
+ __IFLA_BRIDGE_CFM_CC_CCM_TX_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
+ __IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1)
+
struct bridge_stp_xstats {
__u64 transition_blk;
__u64 transition_fwd;
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 9b814c92de12..fdd408f6a5d2 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -779,6 +779,8 @@ enum {
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
#define RTEXT_FILTER_MRP (1 << 4)
+#define RTEXT_FILTER_CFM_CONFIG (1 << 5)
+#define RTEXT_FILTER_CFM_STATUS (1 << 6)

/* End of information exported to user level */

diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index ddc0a9192348..4702702a74d3 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_NETFILTER) += netfilter/

bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o

-bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o
+bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o br_cfm_netlink.o
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 6373c80f204a..22685fc74c7b 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -814,3 +814,8 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,

return 0;
}
+
+bool br_cfm_created(struct net_bridge *br)
+{
+ return !hlist_empty(&br->mep_list);
+}
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
new file mode 100644
index 000000000000..7bdf890b8ccc
--- /dev/null
+++ b/net/bridge/br_cfm_netlink.c
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/genetlink.h>
+
+#include "br_private.h"
+#include "br_private_cfm.h"
+
+static inline struct mac_addr nla_get_mac(const struct nlattr *nla)
+{
+ struct mac_addr mac;
+
+ nla_memcpy(&mac.addr, nla, sizeof(mac.addr));
+
+ return mac;
+}
+
+static inline struct br_cfm_maid nla_get_maid(const struct nlattr *nla)
+{
+ struct br_cfm_maid maid;
+
+ nla_memcpy(&maid.data, nla, sizeof(maid.data));
+
+ return maid;
+}
+
+static const struct nla_policy
+br_cfm_policy[IFLA_BRIDGE_CFM_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_MEP_CREATE] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_MEP_DELETE] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_MEP_CONFIG] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_CC_CONFIG] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_CC_RDI] = { .type = NLA_NESTED },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+br_cfm_mep_create_policy[IFLA_BRIDGE_CFM_MEP_CREATE_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+br_cfm_mep_delete_policy[IFLA_BRIDGE_CFM_MEP_DELETE_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+br_cfm_mep_config_policy[IFLA_BRIDGE_CFM_MEP_CONFIG_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = NLA_POLICY_ETH_ADDR,
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = {
+ .type = NLA_BINARY, .len = CFM_MAID_LENGTH },
+};
+
+static const struct nla_policy
+br_cfm_cc_peer_mep_policy[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_PEER_MEPID] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+br_cfm_cc_rdi_policy[IFLA_BRIDGE_CFM_CC_RDI_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_CC_RDI_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_RDI_RDI] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+br_cfm_cc_ccm_tx_policy[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1] = {
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC] = NLA_POLICY_ETH_ADDR,
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE] = { .type = NLA_U8 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = { .type = NLA_U8 },
+};
+
+static int br_mep_create_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_CREATE_MAX + 1];
+ struct br_cfm_mep_create create;
+ u32 instance;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_CREATE_MAX, attr,
+ br_cfm_mep_create_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing DOMAIN attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing DIRECTION attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing IFINDEX attribute");
+ return -EINVAL;
+ }
+
+ memset(&create, 0, sizeof(create));
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]);
+ create.domain = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]);
+ create.direction = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]);
+ create.ifindex = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]);
+
+ return br_cfm_mep_create(br, instance, &create, extack);
+}
+
+static int br_mep_delete_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_DELETE_MAX + 1];
+ u32 instance;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_DELETE_MAX, attr,
+ br_cfm_mep_delete_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]);
+
+ return br_cfm_mep_delete(br, instance, extack);
+}
+
+static int br_mep_config_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MAX + 1];
+ struct br_cfm_mep_config config;
+ u32 instance;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_CONFIG_MAX, attr,
+ br_cfm_mep_config_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing UNICAST_MAC attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing MDLEVEL attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing MEPID attribute");
+ return -EINVAL;
+ }
+
+ memset(&config, 0, sizeof(config));
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]);
+ config.unicast_mac = nla_get_mac(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC]);
+ config.mdlevel = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]);
+ config.mepid = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]);
+
+ return br_cfm_mep_config_set(br, instance, &config, extack);
+}
+
+static int br_cc_config_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1];
+ struct br_cfm_cc_config config;
+ u32 instance;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_CONFIG_MAX, attr,
+ br_cfm_cc_config_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing ENABLE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INTERVAL attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing MAID attribute");
+ return -EINVAL;
+ }
+
+ memset(&config, 0, sizeof(config));
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]);
+ config.enable = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]);
+ config.exp_interval = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL]);
+ config.exp_maid = nla_get_maid(tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID]);
+
+ return br_cfm_cc_config_set(br, instance, &config, extack);
+}
+
+static int br_cc_peer_mep_add_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1];
+ u32 instance, peer_mep_id;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, attr,
+ br_cfm_cc_peer_mep_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing PEER_MEP_ID attribute");
+ return -EINVAL;
+ }
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]);
+ peer_mep_id = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]);
+
+ return br_cfm_cc_peer_mep_add(br, instance, peer_mep_id, extack);
+}
+
+static int br_cc_peer_mep_remove_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1];
+ u32 instance, peer_mep_id;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, attr,
+ br_cfm_cc_peer_mep_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing PEER_MEP_ID attribute");
+ return -EINVAL;
+ }
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]);
+ peer_mep_id = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]);
+
+ return br_cfm_cc_peer_mep_remove(br, instance, peer_mep_id, extack);
+}
+
+static int br_cc_rdi_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_CC_RDI_MAX + 1];
+ u32 instance, rdi;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_RDI_MAX, attr,
+ br_cfm_cc_rdi_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_RDI_RDI]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing RDI attribute");
+ return -EINVAL;
+ }
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]);
+ rdi = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_RDI]);
+
+ return br_cfm_cc_rdi_set(br, instance, rdi, extack);
+}
+
+static int br_cc_ccm_tx_parse(struct net_bridge *br, struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1];
+ u32 instance;
+ struct br_cfm_cc_ccm_tx_info tx_info;
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_CCM_TX_MAX, attr,
+ br_cfm_cc_ccm_tx_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing DMAC attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing SEQ_NO_UPDATE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing PERIOD attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing IF_TLV attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing IF_TLV_VALUE attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing PORT_TLV attribute");
+ return -EINVAL;
+ }
+ if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE]) {
+ NL_SET_ERR_MSG_MOD(extack, "Missing PORT_TLV_VALUE attribute");
+ return -EINVAL;
+ }
+
+ memset(&tx_info, 0, sizeof(tx_info));
+
+ instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]);
+ tx_info.dmac = nla_get_mac(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC]);
+ tx_info.seq_no_update = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]);
+ tx_info.period = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]);
+ tx_info.if_tlv = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]);
+ tx_info.if_tlv_value = nla_get_u8(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]);
+ tx_info.port_tlv = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]);
+ tx_info.port_tlv_value = nla_get_u8(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE]);
+
+ return br_cfm_cc_ccm_tx(br, instance, &tx_info, extack);
+}
+
+int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
+ struct nlattr *attr, int cmd, struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_CFM_MAX + 1];
+ int err;
+
+ /* When this function is called for a port then the br pointer is
+ * invalid, therefor set the br to point correctly
+ */
+ if (p)
+ br = p->br;
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MAX, attr,
+ br_cfm_policy, extack);
+ if (err)
+ return err;
+
+ if (tb[IFLA_BRIDGE_CFM_MEP_CREATE]) {
+ err = br_mep_create_parse(br, tb[IFLA_BRIDGE_CFM_MEP_CREATE],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_MEP_DELETE]) {
+ err = br_mep_delete_parse(br, tb[IFLA_BRIDGE_CFM_MEP_DELETE],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_MEP_CONFIG]) {
+ err = br_mep_config_parse(br, tb[IFLA_BRIDGE_CFM_MEP_CONFIG],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_CC_CONFIG]) {
+ err = br_cc_config_parse(br, tb[IFLA_BRIDGE_CFM_CC_CONFIG],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD]) {
+ err = br_cc_peer_mep_add_parse(br, tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE]) {
+ err = br_cc_peer_mep_remove_parse(br, tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_CC_RDI]) {
+ err = br_cc_rdi_parse(br, tb[IFLA_BRIDGE_CFM_CC_RDI],
+ extack);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_CFM_CC_CCM_TX]) {
+ err = br_cc_ccm_tx_parse(br, tb[IFLA_BRIDGE_CFM_CC_CCM_TX],
+ extack);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
+{
+ struct nlattr *tb;
+ struct br_cfm_mep *mep;
+ struct br_cfm_peer_mep *peer_mep;
+
+ hlist_for_each_entry_rcu(mep, &br->mep_list, head) {
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_CREATE_INFO);
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN,
+ mep->create.domain))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION,
+ mep->create.direction))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX,
+ mep->create.ifindex))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_INFO);
+
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC,
+ sizeof(mep->config.unicast_mac.addr),
+ mep->config.unicast_mac.addr))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL,
+ mep->config.mdlevel))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID,
+ mep->config.mepid))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_CONFIG_INFO);
+
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE,
+ mep->cc_config.enable))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL,
+ mep->cc_config.exp_interval))
+ goto nla_put_failure;
+
+ if (nla_put(skb, IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID,
+ sizeof(mep->cc_config.exp_maid.data),
+ mep->cc_config.exp_maid.data))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_RDI_INFO);
+
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_RDI_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_RDI_RDI,
+ mep->rdi))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_INFO);
+
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC,
+ sizeof(mep->cc_ccm_tx_info.dmac),
+ mep->cc_ccm_tx_info.dmac.addr))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE,
+ mep->cc_ccm_tx_info.seq_no_update))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD,
+ mep->cc_ccm_tx_info.period))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV,
+ mep->cc_ccm_tx_info.if_tlv))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE,
+ mep->cc_ccm_tx_info.if_tlv_value))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV,
+ mep->cc_ccm_tx_info.port_tlv))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE,
+ mep->cc_ccm_tx_info.port_tlv_value))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+
+ hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) {
+ tb = nla_nest_start(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO);
+
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_MEPID,
+ peer_mep->mepid))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, tb);
+ }
+ }
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, tb);
+
+nla_info_failure:
+ return -EMSGSIZE;
+}
+
+int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
+{
+ struct nlattr *tb;
+ struct br_cfm_mep *mep;
+ struct br_cfm_peer_mep *peer_mep;
+
+ hlist_for_each_entry_rcu(mep, &br->mep_list, head) {
+ tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO);
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
+ mep->status.opcode_unexp_seen))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
+ mep->status.version_unexp_seen))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
+ mep->status.rx_level_low_seen))
+ goto nla_put_failure;
+
+ /* Clear all 'seen' indications */
+ mep->status.opcode_unexp_seen = false;
+ mep->status.version_unexp_seen = false;
+ mep->status.rx_level_low_seen = false;
+
+ nla_nest_end(skb, tb);
+
+ hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) {
+ tb = nla_nest_start(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO);
+ if (!tb)
+ goto nla_info_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
+ mep->instance))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID,
+ peer_mep->mepid))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
+ peer_mep->cc_status.ccm_defect))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
+ peer_mep->cc_status.rdi))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
+ peer_mep->cc_status.port_tlv_value))
+ goto nla_put_failure;
+
+ if (nla_put_u8(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
+ peer_mep->cc_status.if_tlv_value))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
+ peer_mep->cc_status.seen))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
+ peer_mep->cc_status.tlv_seen))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
+ peer_mep->cc_status.seq_unexp_seen))
+ goto nla_put_failure;
+
+ /* Clear all 'seen' indications */
+ peer_mep->cc_status.seen = false;
+ peer_mep->cc_status.tlv_seen = false;
+ peer_mep->cc_status.seq_unexp_seen = false;
+
+ nla_nest_end(skb, tb);
+ }
+ }
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, tb);
+
+nla_info_failure:
+ return -EMSGSIZE;
+}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 8a71c60fa357..46c9d5c91ebe 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -16,6 +16,7 @@

#include "br_private.h"
#include "br_private_stp.h"
+#include "br_private_cfm.h"
#include "br_private_tunnel.h"

static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
@@ -380,6 +381,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
u32 filter_mask, const struct net_device *dev)
{
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
+
+ struct nlattr *af = NULL;
struct net_bridge *br;
struct ifinfomsg *hdr;
struct nlmsghdr *nlh;
@@ -423,11 +426,20 @@ static int br_fill_ifinfo(struct sk_buff *skb,
nla_nest_end(skb, nest);
}

+ if (filter_mask & (RTEXT_FILTER_BRVLAN |
+ RTEXT_FILTER_BRVLAN_COMPRESSED |
+ RTEXT_FILTER_MRP |
+ RTEXT_FILTER_CFM_CONFIG |
+ RTEXT_FILTER_CFM_STATUS)) {
+ af = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
+ if (!af)
+ goto nla_put_failure;
+ }
+
/* Check if the VID information is requested */
if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
struct net_bridge_vlan_group *vg;
- struct nlattr *af;
int err;

/* RCU needed because of the VLAN locking rules (rcu || rtnl) */
@@ -441,11 +453,6 @@ static int br_fill_ifinfo(struct sk_buff *skb,
rcu_read_unlock();
goto done;
}
- af = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
- if (!af) {
- rcu_read_unlock();
- goto nla_put_failure;
- }
if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
err = br_fill_ifvlaninfo_compressed(skb, vg);
else
@@ -456,32 +463,55 @@ static int br_fill_ifinfo(struct sk_buff *skb,
rcu_read_unlock();
if (err)
goto nla_put_failure;
-
- nla_nest_end(skb, af);
}

if (filter_mask & RTEXT_FILTER_MRP) {
- struct nlattr *af;
int err;

if (!br_mrp_enabled(br) || port)
goto done;

- af = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
- if (!af)
- goto nla_put_failure;
-
rcu_read_lock();
err = br_mrp_fill_info(skb, br);
rcu_read_unlock();

if (err)
goto nla_put_failure;
+ }

- nla_nest_end(skb, af);
+ if (filter_mask & (RTEXT_FILTER_CFM_CONFIG | RTEXT_FILTER_CFM_STATUS)) {
+ struct nlattr *cfm_nest = NULL;
+ int err;
+
+ if (!br_cfm_created(br) || port)
+ goto done;
+
+ cfm_nest = nla_nest_start(skb, IFLA_BRIDGE_CFM);
+ if (!cfm_nest)
+ goto nla_put_failure;
+
+ if (filter_mask & RTEXT_FILTER_CFM_CONFIG) {
+ rcu_read_lock();
+ err = br_cfm_config_fill_info(skb, br);
+ rcu_read_unlock();
+ if (err)
+ goto nla_put_failure;
+ }
+
+ if (filter_mask & RTEXT_FILTER_CFM_STATUS) {
+ rcu_read_lock();
+ err = br_cfm_status_fill_info(skb, br);
+ rcu_read_unlock();
+ if (err)
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(skb, cfm_nest);
}

done:
+ if (af)
+ nla_nest_end(skb, af);
nlmsg_end(skb, nlh);
return 0;

@@ -542,7 +572,9 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,

if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) &&
!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) &&
- !(filter_mask & RTEXT_FILTER_MRP))
+ !(filter_mask & RTEXT_FILTER_MRP) &&
+ !(filter_mask & RTEXT_FILTER_CFM_CONFIG) &&
+ !(filter_mask & RTEXT_FILTER_CFM_STATUS))
return 0;

return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
@@ -704,6 +736,11 @@ static int br_afspec(struct net_bridge *br,
if (err)
return err;
break;
+ case IFLA_BRIDGE_CFM:
+ err = br_cfm_parse(br, p, attr, cmd, extack);
+ if (err)
+ return err;
+ break;
}
}

diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index c7b0e91547f6..86cffaf511a3 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1460,6 +1460,37 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br)

#endif

+/* br_cfm.c */
+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
+ struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
+bool br_cfm_created(struct net_bridge *br);
+int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
+int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br);
+#else
+static inline int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
+ struct nlattr *attr, int cmd,
+ struct netlink_ext_ack *extack)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline bool br_cfm_created(struct net_bridge *br)
+{
+ return false;
+}
+
+static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/* br_netlink.c */
extern struct rtnl_link_ops br_link_ops;
int br_netlink_init(void);
--
2.28.0

2020-10-01 10:36:02

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 08/11] 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.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
net/bridge/br_cfm.c | 48 ++++++++++++++++++++++++
net/bridge/br_cfm_netlink.c | 27 +++++++++-----
net/bridge/br_netlink.c | 73 ++++++++++++++++++++++++++++++++-----
net/bridge/br_private.h | 22 ++++++++++-
4 files changed, 148 insertions(+), 22 deletions(-)

diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 22685fc74c7b..6fbfef44c235 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -138,6 +138,13 @@ static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep)
usecs_to_jiffies(interval_us / 4));
}

+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));
@@ -287,6 +294,7 @@ static void ccm_tx_work_expired(struct work_struct *work)
static void ccm_rx_work_expired(struct work_struct *work)
{
struct br_cfm_peer_mep *peer_mep;
+ struct net_bridge_port *b_port;
struct delayed_work *del_work;

del_work = to_delayed_work(work);
@@ -304,6 +312,13 @@ static void ccm_rx_work_expired(struct work_struct *work)
* CCM defect detected
*/
peer_mep->cc_status.ccm_defect = true;
+
+ /* 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();
}
}

@@ -429,6 +444,9 @@ static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
if (peer_mep->cc_status.ccm_defect) {
peer_mep->cc_status.ccm_defect = false;

+ /* Change in CCM defect status - notify */
+ br_cfm_notify(RTM_NEWLINK, port);
+
/* Start CCM RX timer */
ccm_rx_timer_start(peer_mep);
}
@@ -815,6 +833,36 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
return 0;
}

+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;
+}
+
bool br_cfm_created(struct net_bridge *br)
{
return !hlist_empty(&br->mep_list);
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 7bdf890b8ccc..5f81262c9caa 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -325,8 +325,8 @@ static int br_cc_ccm_tx_parse(struct net_bridge *br, struct nlattr *attr,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1];
- u32 instance;
struct br_cfm_cc_ccm_tx_info tx_info;
+ u32 instance;
int err;

err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_CCM_TX_MAX, attr,
@@ -618,7 +618,9 @@ int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
return -EMSGSIZE;
}

-int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
+int br_cfm_status_fill_info(struct sk_buff *skb,
+ struct net_bridge *br,
+ bool getlink)
{
struct nlattr *tb;
struct br_cfm_mep *mep;
@@ -648,10 +650,13 @@ int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
mep->status.rx_level_low_seen))
goto nla_put_failure;

- /* Clear all 'seen' indications */
- mep->status.opcode_unexp_seen = false;
- mep->status.version_unexp_seen = false;
- mep->status.rx_level_low_seen = false;
+ /* Only clear if this is a GETLINK */
+ if (getlink) {
+ /* Clear all 'seen' indications */
+ mep->status.opcode_unexp_seen = false;
+ mep->status.version_unexp_seen = false;
+ mep->status.rx_level_low_seen = false;
+ }

nla_nest_end(skb, tb);

@@ -705,10 +710,12 @@ int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
peer_mep->cc_status.seq_unexp_seen))
goto nla_put_failure;

- /* Clear all 'seen' indications */
- peer_mep->cc_status.seen = false;
- peer_mep->cc_status.tlv_seen = false;
- peer_mep->cc_status.seq_unexp_seen = false;
+ if (getlink) { /* Only clear if this is a GETLINK */
+ /* Clear all 'seen' indications */
+ peer_mep->cc_status.seen = false;
+ peer_mep->cc_status.tlv_seen = false;
+ peer_mep->cc_status.seq_unexp_seen = false;
+ }

nla_nest_end(skb, tb);
}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 46c9d5c91ebe..8a7986713372 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -94,9 +94,11 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
{
struct net_bridge_vlan_group *vg = NULL;
struct net_bridge_port *p = NULL;
- struct net_bridge *br;
- int num_vlan_infos;
+ struct net_bridge *br = NULL;
+ u32 num_cfm_peer_mep_infos;
+ u32 num_cfm_mep_infos;
size_t vinfo_sz = 0;
+ int num_vlan_infos;

rcu_read_lock();
if (netif_is_bridge_port(dev)) {
@@ -115,6 +117,49 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
/* Each VLAN is returned in bridge_vlan_info along with flags */
vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));

+ if (!(filter_mask & RTEXT_FILTER_CFM_STATUS))
+ return vinfo_sz;
+
+ if (!br)
+ return vinfo_sz;
+
+ /* CFM status info must be added */
+ br_cfm_mep_count(br, &num_cfm_mep_infos);
+ br_cfm_peer_mep_count(br, &num_cfm_peer_mep_infos);
+
+ vinfo_sz += nla_total_size(0); /* IFLA_BRIDGE_CFM */
+ /* For each status struct the MEP instance (u32) is added */
+ /* MEP instance (u32) + br_cfm_mep_status */
+ vinfo_sz += num_cfm_mep_infos *
+ /*IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE */
+ (nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN */
+ + nla_total_size(sizeof(u32)));
+ /* MEP instance (u32) + br_cfm_cc_peer_status */
+ vinfo_sz += num_cfm_peer_mep_infos *
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE */
+ (nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE */
+ + nla_total_size(sizeof(u8))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE */
+ + nla_total_size(sizeof(u8))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN */
+ + nla_total_size(sizeof(u32))
+ /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN */
+ + nla_total_size(sizeof(u32)));
+
return vinfo_sz;
}

@@ -378,7 +423,8 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
static int br_fill_ifinfo(struct sk_buff *skb,
const struct net_bridge_port *port,
u32 pid, u32 seq, int event, unsigned int flags,
- u32 filter_mask, const struct net_device *dev)
+ u32 filter_mask, const struct net_device *dev,
+ bool getlink)
{
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;

@@ -500,7 +546,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,

if (filter_mask & RTEXT_FILTER_CFM_STATUS) {
rcu_read_lock();
- err = br_cfm_status_fill_info(skb, br);
+ err = br_cfm_status_fill_info(skb, br, getlink);
rcu_read_unlock();
if (err)
goto nla_put_failure;
@@ -520,11 +566,9 @@ static int br_fill_ifinfo(struct sk_buff *skb,
return -EMSGSIZE;
}

-/* Notify listeners of a change in bridge or port information */
-void br_ifinfo_notify(int event, const struct net_bridge *br,
- const struct net_bridge_port *port)
+void br_info_notify(int event, const struct net_bridge *br,
+ const struct net_bridge_port *port, u32 filter)
{
- u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED;
struct net_device *dev;
struct sk_buff *skb;
int err = -ENOBUFS;
@@ -549,7 +593,7 @@ void br_ifinfo_notify(int event, const struct net_bridge *br,
if (skb == NULL)
goto errout;

- err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev);
+ err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false);
if (err < 0) {
/* -EMSGSIZE implies BUG in br_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -562,6 +606,15 @@ void br_ifinfo_notify(int event, const struct net_bridge *br,
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
}

+/* Notify listeners of a change in bridge or port information */
+void br_ifinfo_notify(int event, const struct net_bridge *br,
+ const struct net_bridge_port *port)
+{
+ u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED;
+
+ return br_info_notify(event, br, port, filter);
+}
+
/*
* Dump information about all ports, in response to GETLINK
*/
@@ -578,7 +631,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;

return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
- filter_mask, dev);
+ filter_mask, dev, true);
}

static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 86cffaf511a3..5954ee45af80 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1466,7 +1466,11 @@ int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
bool br_cfm_created(struct net_bridge *br);
int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
-int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br);
+int br_cfm_status_fill_info(struct sk_buff *skb,
+ struct net_bridge *br,
+ bool getlink);
+int br_cfm_mep_count(struct net_bridge *br, u32 *count);
+int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count);
#else
static inline int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
struct nlattr *attr, int cmd,
@@ -1485,7 +1489,19 @@ static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge
return -EOPNOTSUPP;
}

-static inline int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br)
+static inline int br_cfm_status_fill_info(struct sk_buff *skb,
+ struct net_bridge *br,
+ bool getlink)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int br_cfm_mep_count(struct net_bridge *br, u32 *count)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count)
{
return -EOPNOTSUPP;
}
@@ -1497,6 +1513,8 @@ int br_netlink_init(void);
void br_netlink_fini(void);
void br_ifinfo_notify(int event, const struct net_bridge *br,
const struct net_bridge_port *port);
+void br_info_notify(int event, const struct net_bridge *br,
+ const struct net_bridge_port *port, u32 filter);
int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags,
struct netlink_ext_ack *extack);
int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
--
2.28.0

2020-10-01 10:36:30

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 05/11] bridge: cfm: Kernel space implementation of CFM.

This is the second commit of the implementation of the CFM protocol
according to 802.1Q section 12.14.

Functionality is extended with CCM frame transmission.

Interface is extended with these functions:
br_cfm_cc_rdi_set()
br_cfm_cc_ccm_tx()
br_cfm_cc_config_set()

A MEP Continuity Check feature can be configured by
br_cfm_cc_config_set()
The Continuity Check parameters can be configured to be used when
transmitting CCM.

A MEP can be configured to start or stop transmission of CCM frames by
br_cfm_cc_ccm_tx()
The CCM will be transmitted for a selected period in seconds.
Must call this function before timeout to keep transmission alive.

A MEP transmitting CCM can be configured with inserted RDI in PDU by
br_cfm_cc_rdi_set()

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/uapi/linux/cfm_bridge.h | 39 ++++-
net/bridge/br_cfm.c | 284 ++++++++++++++++++++++++++++++++
net/bridge/br_private_cfm.h | 54 ++++++
3 files changed, 376 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/cfm_bridge.h b/include/uapi/linux/cfm_bridge.h
index a262a8c0e085..84a3817da90b 100644
--- a/include/uapi/linux/cfm_bridge.h
+++ b/include/uapi/linux/cfm_bridge.h
@@ -6,7 +6,32 @@
#include <linux/types.h>
#include <linux/if_ether.h>

-#define CFM_MAID_LENGTH 48
+#define ETHER_HEADER_LENGTH (6+6+4+2)
+#define CFM_MAID_LENGTH 48
+#define CFM_CCM_PDU_LENGTH 75
+#define CFM_PORT_STATUS_TLV_LENGTH 4
+#define CFM_IF_STATUS_TLV_LENGTH 4
+#define CFM_IF_STATUS_TLV_TYPE 4
+#define CFM_PORT_STATUS_TLV_TYPE 2
+#define CFM_ENDE_TLV_TYPE 0
+#define CFM_CCM_MAX_FRAME_LENGTH (ETHER_HEADER_LENGTH+\
+ CFM_CCM_PDU_LENGTH+\
+ CFM_PORT_STATUS_TLV_LENGTH+\
+ CFM_IF_STATUS_TLV_LENGTH)
+#define CFM_FRAME_PRIO 7
+#define CFM_CCM_TLV_OFFSET 70
+#define CFM_CCM_ITU_RESERVED_SIZE 16
+
+struct br_cfm_common_hdr {
+ __u8 mdlevel_version;
+ __u8 opcode;
+ __u8 flags;
+ __u8 tlv_offset;
+};
+
+enum br_cfm_opcodes {
+ BR_CFM_OPCODE_CCM = 0x1,
+};

/* MEP domain */
enum br_cfm_domain {
@@ -20,4 +45,16 @@ enum br_cfm_mep_direction {
BR_CFM_MEP_DIRECTION_UP,
};

+/* CCM interval supported. */
+enum br_cfm_ccm_interval {
+ BR_CFM_CCM_INTERVAL_NONE,
+ BR_CFM_CCM_INTERVAL_3_3_MS,
+ BR_CFM_CCM_INTERVAL_10_MS,
+ BR_CFM_CCM_INTERVAL_100_MS,
+ BR_CFM_CCM_INTERVAL_1_SEC,
+ BR_CFM_CCM_INTERVAL_10_SEC,
+ BR_CFM_CCM_INTERVAL_1_MIN,
+ BR_CFM_CCM_INTERVAL_10_MIN,
+};
+
#endif
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 86d6e7b73375..19ee95cddc46 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -53,6 +53,184 @@ static struct net_bridge_port *br_mep_get_port(struct net_bridge *br,
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;
+}
+
+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) {
+ 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));
+}
+
int br_cfm_mep_create(struct net_bridge *br,
const u32 instance,
struct br_cfm_mep_create *const create,
@@ -115,6 +293,7 @@ int br_cfm_mep_create(struct net_bridge *br,
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);

hlist_add_tail_rcu(&mep->head, &br->mep_list);

@@ -134,6 +313,8 @@ static void mep_delete_implementation(struct net_bridge *br,
kfree_rcu(peer_mep, rcu);
}

+ cancel_delayed_work_sync(&mep->ccm_tx_dwork);
+
RCU_INIT_POINTER(mep->b_port, NULL);
hlist_del_rcu(&mep->head);
kfree_rcu(mep, rcu);
@@ -192,6 +373,32 @@ int br_cfm_mep_config_set(struct net_bridge *br,
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_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;
+
+ mep->cc_config = *config;
+ mep->ccm_tx_snumber = 1;
+
+ return 0;
+}
+
int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
u32 mepid,
struct netlink_ext_ack *extack)
@@ -261,3 +468,80 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,

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;
+}
diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
index 40fe982added..8d1b449acfbf 100644
--- a/net/bridge/br_private_cfm.h
+++ b/net/bridge/br_private_cfm.h
@@ -32,6 +32,24 @@ int br_cfm_mep_config_set(struct net_bridge *br,
const struct br_cfm_mep_config *const config,
struct netlink_ext_ack *extack);

+struct br_cfm_maid {
+ u8 data[CFM_MAID_LENGTH];
+};
+
+struct br_cfm_cc_config {
+ /* Expected received CCM PDU MAID. */
+ struct br_cfm_maid exp_maid;
+
+ /* Expected received CCM PDU interval. */
+ /* Transmitting CCM PDU interval when CCM tx is enabled. */
+ enum br_cfm_ccm_interval exp_interval;
+};
+
+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);
+
int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
u32 peer_mep_id,
struct netlink_ext_ack *extack);
@@ -39,15 +57,51 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
u32 peer_mep_id,
struct netlink_ext_ack *extack);

+/* Transmitted CCM Remote Defect Indication status set.
+ * This RDI is inserted in transmitted CCM PDUs if CCM transmission is enabled.
+ * See br_cfm_cc_ccm_tx() with interval != BR_CFM_CCM_INTERVAL_NONE
+ */
+int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
+ const bool rdi, struct netlink_ext_ack *extack);
+
+/* OAM PDU Tx information */
+struct br_cfm_cc_ccm_tx_info {
+ struct mac_addr dmac;
+ /* The CCM will be transmitted for this period in seconds.
+ * Call br_cfm_cc_ccm_tx before timeout to keep transmission alive.
+ * When period is zero any ongoing transmission will be stopped.
+ */
+ u32 period;
+
+ bool seq_no_update; /* Update Tx CCM sequence number */
+ bool if_tlv; /* Insert Interface Status TLV */
+ u8 if_tlv_value; /* Interface Status TLV value */
+ bool port_tlv; /* Insert Port Status TLV */
+ u8 port_tlv_value; /* Port Status TLV value */
+ /* Sender ID TLV ??
+ * Organization-Specific TLV ??
+ */
+};
+
+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 {
/* list header of MEP instances */
struct hlist_node head;
u32 instance;
struct br_cfm_mep_create create;
struct br_cfm_mep_config config;
+ struct br_cfm_cc_config cc_config;
+ struct br_cfm_cc_ccm_tx_info cc_ccm_tx_info;
/* List of multiple peer MEPs */
struct hlist_head peer_mep_list;
struct net_bridge_port __rcu *b_port;
+ unsigned long ccm_tx_end;
+ struct delayed_work ccm_tx_dwork;
+ u32 ccm_tx_snumber;
+ bool rdi;
struct rcu_head rcu;
};

--
2.28.0

2020-10-01 10:37:27

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 11/11] bridge: cfm: Added CFM switchdev utilization.

The CFM kernel implementation is now trying to offload functionallity
in HW by utilizing the switchdev interface.

MEP instances are created/deleted and CCM frames are transmitted in HW.
Also handling of received CCM frames and the defect calculation is dome
in HW.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
net/bridge/Makefile | 2 +-
net/bridge/br_cfm.c | 270 ++++++++++++++++++++++++++++++----
net/bridge/br_cfm_netlink.c | 51 +++----
net/bridge/br_cfm_switchdev.c | 203 +++++++++++++++++++++++++
net/bridge/br_private_cfm.h | 63 +++++++-
5 files changed, 530 insertions(+), 59 deletions(-)
create mode 100644 net/bridge/br_cfm_switchdev.c

diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 4702702a74d3..5d0a399825ef 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_NETFILTER) += netfilter/

bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o

-bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o br_cfm_netlink.o
+bridge-$(CONFIG_BRIDGE_CFM) += br_cfm_switchdev.o br_cfm.o br_cfm_netlink.o
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index fc8268cb76c1..bfaee33acffb 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -184,9 +184,11 @@ static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
}
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.
+ /* This function is called from ccm_tx_work_expired that
+ * is a work_queue function.
+ * It is also called from br_cfm_cc_rdi_set and br_cfm_cc_ccm_tx
+ * that has the RTNL.
+ * Due to this the device cannot be deleted.
*/

skb->protocol = htons(ETH_P_CFM);
@@ -500,6 +502,7 @@ int br_cfm_mep_create(struct net_bridge *br,
{
struct net_bridge_port *p;
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -546,6 +549,11 @@ int br_cfm_mep_create(struct net_bridge *br,
}
}

+ /* Try create MEP in Switchdev */
+ swd_ret = br_cfm_switchdev_mep_create(br, instance, create, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
mep = kzalloc(sizeof(*mep), GFP_KERNEL);
if (!mep)
return -ENOMEM;
@@ -555,20 +563,21 @@ int br_cfm_mep_create(struct net_bridge *br,
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);
-
- if (hlist_empty(&br->mep_list))
+ if ((swd_ret == -EOPNOTSUPP) && hlist_empty(&br->mep_list))
br_add_frame(br, &cfm_frame_type);
-
hlist_add_tail_rcu(&mep->head, &br->mep_list);

+ INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
+
return 0;
}

-static void mep_delete_implementation(struct net_bridge *br,
- struct br_cfm_mep *mep)
+static int mep_delete_implementation(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ struct netlink_ext_ack *extack)
{
struct br_cfm_peer_mep *peer_mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -579,14 +588,23 @@ static void mep_delete_implementation(struct net_bridge *br,
kfree_rcu(peer_mep, rcu);
}

- cancel_delayed_work_sync(&mep->ccm_tx_dwork);
-
RCU_INIT_POINTER(mep->b_port, NULL);
hlist_del_rcu(&mep->head);
- kfree_rcu(mep, rcu);

+ /* Try delete MEP in Switchdev */
+ swd_ret = br_cfm_switchdev_mep_delete(br, mep, extack);
+ if (swd_ret != -EOPNOTSUPP)
+ goto free;
+
+ swd_ret = 0;
if (hlist_empty(&br->mep_list))
br_del_frame(br, &cfm_frame_type);
+ cancel_delayed_work_sync(&mep->ccm_tx_dwork);
+
+free:
+ kfree_rcu(mep, rcu);
+
+ return swd_ret;
}

int br_cfm_mep_delete(struct net_bridge *br,
@@ -604,9 +622,7 @@ int br_cfm_mep_delete(struct net_bridge *br,
return -ENOENT;
}

- mep_delete_implementation(br, mep);
-
- return 0;
+ return mep_delete_implementation(br, mep, extack);
}

int br_cfm_mep_config_set(struct net_bridge *br,
@@ -615,6 +631,7 @@ int br_cfm_mep_config_set(struct net_bridge *br,
struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -637,6 +654,11 @@ int br_cfm_mep_config_set(struct net_bridge *br,
return -ENOENT;
}

+ /* Try configure MEP in Switchdev */
+ swd_ret = br_cfm_switchdev_mep_config_set(br, mep, config, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
mep->config = *config;

return 0;
@@ -649,6 +671,7 @@ int br_cfm_cc_config_set(struct net_bridge *br,
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -663,6 +686,19 @@ int br_cfm_cc_config_set(struct net_bridge *br,
if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
return 0;

+ /* Try configure CC in Switchdev */
+ swd_ret = br_cfm_switchdev_cc_config_set(br, mep, config, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
+ mep->cc_config = *config;
+ mep->ccm_rx_snumber = 0;
+ mep->ccm_tx_snumber = 1;
+
+ /* Return if switchdev. CCM is not transmitted or received here */
+ if (!swd_ret)
+ return 0;
+
if (config->enable && !mep->cc_config.enable)
/* CC is enabled */
hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
@@ -673,10 +709,6 @@ int br_cfm_cc_config_set(struct net_bridge *br,
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;
}

@@ -686,6 +718,7 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -709,19 +742,29 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
return -EEXIST;
}

+ /* Try add peer MEP in Switchdev */
+ swd_ret = br_cfm_switchdev_cc_peer_mep_add(br, mep, mepid, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
if (!peer_mep)
return -ENOMEM;

peer_mep->mepid = mepid;
peer_mep->mep = mep;
+
+ hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
+
+ /* Return if switchdev. CCM is not transmitted or received here */
+ if (!swd_ret)
+ return 0;
+
INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);

if (mep->cc_config.enable)
cc_peer_enable(peer_mep);

- hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
-
return 0;
}

@@ -731,6 +774,7 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
{
struct br_cfm_peer_mep *peer_mep;
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -748,18 +792,42 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
return -ENOENT;
}

+ hlist_del_rcu(&peer_mep->head);
+
+ /* Try remove peer MEP in Switchdev */
+ swd_ret = br_cfm_switchdev_cc_peer_mep_remove(br, mep, mepid, extack);
+ if (swd_ret != -EOPNOTSUPP)
+ goto free;
+
+ swd_ret = 0;
cc_peer_disable(peer_mep);

- hlist_del_rcu(&peer_mep->head);
+free:
kfree_rcu(peer_mep, rcu);

- return 0;
+ return swd_ret;
+}
+
+static int swd_ccm_tx(struct net_bridge *br, struct br_cfm_mep *mep,
+ struct netlink_ext_ack *extack)
+{
+ struct sk_buff *skb;
+
+ skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
+ if (!skb)
+ return -ENOMEM;
+ return br_cfm_switchdev_cc_ccm_tx(br, mep, skb,
+ mep->cc_ccm_tx_info.period,
+ mep->cc_config.exp_interval,
+ extack);
}

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;
+ bool rdi_changed;
+ int swd_ret;

ASSERT_RTNL();

@@ -770,8 +838,18 @@ int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
return -ENOENT;
}

+ rdi_changed = (mep->rdi != rdi) ? true : false;
mep->rdi = rdi;

+ if (mep->ccm_tx_swd && rdi_changed) {
+ /* Transmitting using Switchdev is ongoing.
+ * Restart with new RDI status
+ */
+ swd_ret = swd_ccm_tx(br, mep, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+ }
+
return 0;
}

@@ -780,6 +858,7 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
struct netlink_ext_ack *extack)
{
struct br_cfm_mep *mep;
+ int swd_ret;

ASSERT_RTNL();

@@ -792,11 +871,21 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,

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 */
+ /* Transmission is ongoing */
+
+ if (mep->ccm_tx_swd) {
+ /* Switchdev transmission started. Re-start transmission */
+ swd_ret = swd_ccm_tx(br, mep, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+ }
+
+ /* The period end time is recalculated */
mep->ccm_tx_end = jiffies +
usecs_to_jiffies(tx_info->period * 1000000);
return 0;
@@ -807,9 +896,16 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
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
- */
+ /* Some change in info and transmission is ongoing */
+
+ if (mep->ccm_tx_swd) {
+ /* Switchdev transmission started. Re-start transmission */
+ swd_ret = swd_ccm_tx(br, mep, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+ }
+
+ /* The period end time is recalculated */
mep->ccm_tx_end = jiffies +
usecs_to_jiffies(tx_info->period * 1000000);

@@ -817,12 +913,31 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
}

if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
+ /* Stop transmission */
+
+ /* Try stop transmission in Switchdev */
+ (void)br_cfm_switchdev_cc_ccm_tx(br, mep, NULL, 0, 0, extack);
+
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
+ /* Try start transmitting using Switchdev */
+ swd_ret = swd_ccm_tx(br, mep, extack);
+ if (swd_ret && swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+ if (!swd_ret) {
+ /* Switchdev transmission started */
+ mep->ccm_tx_swd = true;
+ goto save;
+ }
+
+ /* Switchdev CCM tx is not supported */
+ swd_ret = 0;
+ mep->ccm_tx_swd = false;
+
+ /* 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);
@@ -830,6 +945,78 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
save:
mep->cc_ccm_tx_info = *tx_info;

+ return swd_ret;
+}
+
+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+ bool clear, struct br_cfm_mep_status *const status,
+ struct netlink_ext_ack *extack)
+{
+ struct br_cfm_mep *mep;
+ int swd_ret;
+
+ memset(status, 0, sizeof(*status));
+
+ mep = br_mep_find(br, instance);
+ if (!mep) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "MEP instance does not exists");
+ return -ENOENT;
+ }
+
+ /* Try get MEP status in Switchdev */
+ swd_ret = br_cfm_switchdev_mep_status_get(br, mep, clear, status, extack);
+ if (swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
+ *status = mep->status;
+ if (clear) {
+ mep->status.opcode_unexp_seen = false;
+ mep->status.version_unexp_seen = false;
+ mep->status.rx_level_low_seen = false;
+ }
+
+ return 0;
+}
+
+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+ u32 mepid, bool clear,
+ struct br_cfm_cc_peer_status *const status,
+ struct netlink_ext_ack *extack)
+{
+ struct br_cfm_peer_mep *peer_mep;
+ struct br_cfm_mep *mep;
+ int swd_ret;
+
+ memset(status, 0, sizeof(*status));
+
+ 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;
+ }
+
+ /* Try get Peer MEP status in Switchdev */
+ swd_ret = br_cfm_switchdev_cc_peer_status_get(br, mep, mepid, clear,
+ status, extack);
+ if (swd_ret != -EOPNOTSUPP)
+ return swd_ret;
+
+ *status = peer_mep->cc_status;
+ if (clear) {
+ peer_mep->cc_status.seen = false;
+ peer_mep->cc_status.tlv_seen = false;
+ peer_mep->cc_status.seq_unexp_seen = false;
+ }
+
return 0;
}

@@ -878,5 +1065,28 @@ void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)

hlist_for_each_entry(mep, &br->mep_list, head)
if (mep->create.ifindex == port->dev->ifindex)
- mep_delete_implementation(br, mep);
+ (void)mep_delete_implementation(br, mep, NULL);
+}
+
+/* Notification function called from CFM driver */
+void br_cfm_notification(struct net_device *dev, const struct br_cfm_notif_info *const notif_info)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct br_cfm_peer_mep *peer_mep;
+ struct net_bridge_port *b_port;
+ struct br_cfm_mep *mep;
+
+ mep = br_mep_find(br, notif_info->instance);
+ if (!mep)
+ return;
+
+ peer_mep = br_peer_mep_find(mep, notif_info->peer_mep);
+ if (!peer_mep)
+ return;
+
+ rcu_read_lock();
+ b_port = rcu_dereference(mep->b_port);
+ if (b_port)
+ br_cfm_notify(RTM_NEWLINK, b_port);
+ rcu_read_unlock();
}
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 5f81262c9caa..3d8561e59ace 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -622,42 +622,40 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
struct net_bridge *br,
bool getlink)
{
- struct nlattr *tb;
- struct br_cfm_mep *mep;
+ struct br_cfm_cc_peer_status cc_peer_status;
+ struct br_cfm_mep_status mep_status;
struct br_cfm_peer_mep *peer_mep;
+ struct br_cfm_mep *mep;
+ struct nlattr *tb;

hlist_for_each_entry_rcu(mep, &br->mep_list, head) {
tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO);
if (!tb)
goto nla_info_failure;

+ if (br_cfm_mep_status_get(br, mep->instance, getlink,
+ &mep_status, NULL))
+ goto nla_info_failure;
+
if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
mep->instance))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
- mep->status.opcode_unexp_seen))
+ mep_status.opcode_unexp_seen))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
- mep->status.version_unexp_seen))
+ mep_status.version_unexp_seen))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
- mep->status.rx_level_low_seen))
+ mep_status.rx_level_low_seen))
goto nla_put_failure;

- /* Only clear if this is a GETLINK */
- if (getlink) {
- /* Clear all 'seen' indications */
- mep->status.opcode_unexp_seen = false;
- mep->status.version_unexp_seen = false;
- mep->status.rx_level_low_seen = false;
- }
-
nla_nest_end(skb, tb);

hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) {
@@ -666,6 +664,12 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
if (!tb)
goto nla_info_failure;

+ if (br_cfm_cc_peer_status_get(br, mep->instance,
+ peer_mep->mepid,
+ getlink,
+ &cc_peer_status, NULL))
+ goto nla_info_failure;
+
if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
mep->instance))
@@ -678,45 +682,38 @@ int br_cfm_status_fill_info(struct sk_buff *skb,

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
- peer_mep->cc_status.ccm_defect))
+ cc_peer_status.ccm_defect))
goto nla_put_failure;

if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
- peer_mep->cc_status.rdi))
+ cc_peer_status.rdi))
goto nla_put_failure;

if (nla_put_u8(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
- peer_mep->cc_status.port_tlv_value))
+ cc_peer_status.port_tlv_value))
goto nla_put_failure;

if (nla_put_u8(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
- peer_mep->cc_status.if_tlv_value))
+ cc_peer_status.if_tlv_value))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
- peer_mep->cc_status.seen))
+ cc_peer_status.seen))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
- peer_mep->cc_status.tlv_seen))
+ cc_peer_status.tlv_seen))
goto nla_put_failure;

if (nla_put_u32(skb,
IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
- peer_mep->cc_status.seq_unexp_seen))
+ cc_peer_status.seq_unexp_seen))
goto nla_put_failure;

- if (getlink) { /* Only clear if this is a GETLINK */
- /* Clear all 'seen' indications */
- peer_mep->cc_status.seen = false;
- peer_mep->cc_status.tlv_seen = false;
- peer_mep->cc_status.seq_unexp_seen = false;
- }
-
nla_nest_end(skb, tb);
}
}
diff --git a/net/bridge/br_cfm_switchdev.c b/net/bridge/br_cfm_switchdev.c
new file mode 100644
index 000000000000..d7441d57d113
--- /dev/null
+++ b/net/bridge/br_cfm_switchdev.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/switchdev.h>
+
+#include "br_private_cfm.h"
+
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+ const u32 instance,
+ struct br_cfm_mep_create *const create,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_mep cfm_obj;
+ struct net_bridge_port *port;
+ bool found = false;
+
+ list_for_each_entry(port, &br->port_list, list)
+ if (port->dev->ifindex == create->ifindex) {
+ found = true;
+ break;
+ }
+ if (!found)
+ return -EINVAL;
+
+ cfm_obj.obj.orig_dev = br->dev;
+ cfm_obj.obj.id = SWITCHDEV_OBJ_ID_MEP_CFM;
+ cfm_obj.obj.flags = 0;
+ cfm_obj.domain = create->domain;
+ cfm_obj.direction = create->direction;
+ cfm_obj.port = port->dev;
+
+ return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_mep cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_MEP_CFM,
+ .obj.flags = 0,
+ .domain = 0,
+ .direction = 0,
+ .port = NULL,
+ };
+
+ return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ const struct br_cfm_mep_config *const config,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_mep_config_set cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .unicast_mac = config->unicast_mac,
+ .mdlevel = config->mdlevel,
+ .mepid = config->mepid,
+ };
+
+ return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ const struct br_cfm_cc_config *const config,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_cc_config_set cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .interval = config->exp_interval,
+ .maid = config->exp_maid,
+ .enable = config->enable,
+ };
+
+ return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .peer_mep_id = peer_mep_id,
+ };
+
+ return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .peer_mep_id = peer_mep_id,
+ };
+
+ return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ struct sk_buff *skb,
+ u32 period,
+ enum br_cfm_ccm_interval interval,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_cc_ccm_tx cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .skb = skb,
+ .interval = interval,
+ .period = period,
+ };
+
+ return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ bool clear,
+ struct br_cfm_mep_status *const status,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_mep_status_get cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .clear = clear,
+ .opcode_unexp_seen = false,
+ .version_unexp_seen = false,
+ .rx_level_low_seen = false,
+ };
+ int err;
+
+ err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+ if (err)
+ return err;
+
+ status->opcode_unexp_seen = cfm_obj.opcode_unexp_seen;
+ status->version_unexp_seen = cfm_obj.version_unexp_seen;
+ status->rx_level_low_seen = cfm_obj.rx_level_low_seen;
+
+ return 0;
+}
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ bool clear,
+ struct br_cfm_cc_peer_status *const status,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_cfm_cc_peer_status_get cfm_obj = {
+ .obj.orig_dev = br->dev,
+ .obj.id = SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM,
+ .obj.flags = 0,
+ .instance = mep->instance,
+ .clear = clear,
+ .port_tlv_value = 0,
+ .if_tlv_value = 0,
+ .ccm_defect = false,
+ .rdi = false,
+ .seen = false,
+ .tlv_seen = false,
+ .seq_unexp_seen = false,
+ };
+ int err;
+
+ err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+ if (err)
+ return err;
+
+ status->port_tlv_value = cfm_obj.port_tlv_value;
+ status->if_tlv_value = cfm_obj.if_tlv_value;
+ status->ccm_defect = cfm_obj.ccm_defect;
+ status->rdi = cfm_obj.rdi;
+ status->seen = cfm_obj.seen;
+ status->tlv_seen = cfm_obj.tlv_seen;
+ status->seq_unexp_seen = cfm_obj.seq_unexp_seen;
+
+ return 0;
+}
diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
index 6a2294c0ea79..a91d0b59c27f 100644
--- a/net/bridge/br_private_cfm.h
+++ b/net/bridge/br_private_cfm.h
@@ -6,6 +6,7 @@
#include "br_private.h"
#include <uapi/linux/cfm_bridge.h>

+/* br_cfm.c */
struct br_cfm_mep_create {
enum br_cfm_domain domain; /* Domain for this MEP */
enum br_cfm_mep_direction direction; /* Up or Down MEP direction */
@@ -55,13 +56,13 @@ int br_cfm_cc_config_set(struct net_bridge *br,
int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
u32 peer_mep_id,
struct netlink_ext_ack *extack);
+
int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
u32 peer_mep_id,
struct netlink_ext_ack *extack);

/* Transmitted CCM Remote Defect Indication status set.
* This RDI is inserted in transmitted CCM PDUs if CCM transmission is enabled.
- * See br_cfm_cc_ccm_tx() with interval != BR_CFM_CCM_INTERVAL_NONE
*/
int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
const bool rdi, struct netlink_ext_ack *extack);
@@ -96,6 +97,10 @@ struct br_cfm_mep_status {
bool rx_level_low_seen; /* Rx of OAM PDU with level low */
};

+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+ bool clear, struct br_cfm_mep_status *const status,
+ struct netlink_ext_ack *extack);
+
struct br_cfm_cc_peer_status {
/* This CCM related status is based on the latest received CCM PDU. */
u8 port_tlv_value; /* Port Status TLV value */
@@ -114,6 +119,11 @@ struct br_cfm_cc_peer_status {
bool seq_unexp_seen;
};

+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+ u32 mepid, bool clear,
+ struct br_cfm_cc_peer_status *const status,
+ struct netlink_ext_ack *extack);
+
struct br_cfm_mep {
/* list header of MEP instances */
struct hlist_node head;
@@ -131,6 +141,7 @@ struct br_cfm_mep {
u32 ccm_rx_snumber;
struct br_cfm_mep_status status;
bool rdi;
+ bool ccm_tx_swd;
struct rcu_head rcu;
};

@@ -144,4 +155,54 @@ struct br_cfm_peer_mep {
struct rcu_head rcu;
};

+/* br_cfm_switchdev.c */
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+ const u32 instance,
+ struct br_cfm_mep_create *const create,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ const struct br_cfm_mep_config *const config,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ const struct br_cfm_cc_config *const config,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ struct sk_buff *skb,
+ u32 period,
+ enum br_cfm_ccm_interval interval,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ bool clear,
+ struct br_cfm_mep_status *const status,
+ struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+ struct br_cfm_mep *mep,
+ u32 peer_mep_id,
+ bool clear,
+ struct br_cfm_cc_peer_status *const status,
+ struct netlink_ext_ack *extack);
+
#endif /* _BR_PRIVATE_CFM_H_ */
--
2.28.0

2020-10-01 10:37:57

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

This is the definition of the CFM switchdev interface.

The interface consist of these objects:
SWITCHDEV_OBJ_ID_MEP_CFM,
SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM

MEP instance add/del
switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)

MEP cofigure
switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)

MEP CC cofigure
switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)

Peer MEP add/del
switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)

Start/stop CCM transmission
switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)

Get MEP status
switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)

Get Peer MEP status
switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/linux/if_bridge.h | 13 +++++
include/net/switchdev.h | 115 ++++++++++++++++++++++++++++++++++++++
net/switchdev/switchdev.c | 54 ++++++++++++++++++
3 files changed, 182 insertions(+)

diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 556caed00258..5476880319ae 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -155,4 +155,17 @@ br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
}
#endif

+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+#define BR_CFM_EVENT_CCM_DEFECT (1<<0)
+
+struct br_cfm_notif_info {
+ u32 instance;
+ u32 peer_mep;
+ u32 events;
+};
+
+/* Function to be called from CFM switchdev driver to notify change in status */
+void br_cfm_notification(struct net_device *dev, const struct br_cfm_notif_info *const notif_info);
+#endif
+
#endif
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 53e8b4994296..cd5194cd54d0 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -11,6 +11,9 @@
#include <linux/notifier.h>
#include <linux/list.h>
#include <net/ip_fib.h>
+#include <uapi/linux/cfm_bridge.h>
+#include "../net/bridge/br_private.h"
+#include "../net/bridge/br_private_cfm.h"

#define SWITCHDEV_F_NO_RECURSE BIT(0)
#define SWITCHDEV_F_SKIP_EOPNOTSUPP BIT(1)
@@ -81,6 +84,15 @@ enum switchdev_obj_id {
SWITCHDEV_OBJ_ID_IN_STATE_MRP,

#endif
+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+ SWITCHDEV_OBJ_ID_MEP_CFM,
+ SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
+ SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
+ SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+ SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
+ SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
+ SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
+#endif
};

struct switchdev_obj {
@@ -112,6 +124,97 @@ struct switchdev_obj_port_mdb {
#define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
container_of((OBJ), struct switchdev_obj_port_mdb, obj)

+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+/* SWITCHDEV_OBJ_ID_MEP_CFM */
+struct switchdev_obj_cfm_mep {
+ struct switchdev_obj obj;
+ u32 instance;
+ enum br_cfm_domain domain; /* Domain for this MEP */
+ enum br_cfm_mep_direction direction; /* Up or Down MEP direction */
+ struct net_device *port; /* Residence port */
+};
+
+#define SWITCHDEV_OBJ_CFM_MEP(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_mep, obj)
+
+/* SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM */
+struct switchdev_obj_cfm_mep_config_set {
+ struct switchdev_obj obj;
+ u32 instance;
+ u32 mdlevel;
+ u32 mepid;
+ struct mac_addr unicast_mac;
+};
+
+#define SWITCHDEV_OBJ_CFM_MEP_CONFIG_SET(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_mep_config_set, obj)
+
+#define SWITCHDEV_CFM_MAID_LENGTH 48
+
+/* SWITCHDEV_OBJ_ID_CC_CONFIG_CFM */
+struct switchdev_obj_cfm_cc_config_set {
+ struct switchdev_obj obj;
+ u32 instance;
+ struct br_cfm_maid maid;
+ enum br_cfm_ccm_interval interval;
+ bool enable;
+};
+
+#define SWITCHDEV_OBJ_CFM_CC_CONFIG_SET(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_cc_config_set, obj)
+
+/* SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM */
+struct switchdev_obj_cfm_cc_peer_mep {
+ struct switchdev_obj obj;
+ u32 instance;
+ u32 peer_mep_id;
+};
+
+#define SWITCHDEV_OBJ_CFM_CC_PEER_MEP(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_cc_peer_mep, obj)
+
+/* SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM */
+struct switchdev_obj_cfm_cc_ccm_tx {
+ struct switchdev_obj obj;
+ u32 instance;
+ u32 period;
+ struct sk_buff *skb;
+ enum br_cfm_ccm_interval interval;
+};
+
+#define SWITCHDEV_OBJ_CFM_CC_CCM_TX(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_cc_ccm_tx, obj)
+
+/* SWITCHDEV_OBJ_ID_MEP_STATUS_CFM */
+struct switchdev_obj_cfm_mep_status_get {
+ struct switchdev_obj obj;
+ u32 instance;
+ bool clear;
+ bool opcode_unexp_seen;
+ bool version_unexp_seen;
+ bool rx_level_low_seen;
+};
+
+#define SWITCHDEV_OBJ_CFM_MEP_STATUS_get(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_mep_status_get, obj)
+
+/* SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM */
+struct switchdev_obj_cfm_cc_peer_status_get {
+ struct switchdev_obj obj;
+ u32 instance;
+ bool clear;
+ u8 port_tlv_value;
+ u8 if_tlv_value;
+ bool ccm_defect;
+ bool rdi;
+ bool seen;
+ bool tlv_seen;
+ bool seq_unexp_seen;
+};
+#define SWITCHDEV_OBJ_CFM_CC_PEER_STATUS_get(OBJ) \
+ container_of((OBJ), struct switchdev_obj_cfm_cc_peer_status_get, obj)
+
+#endif

#if IS_ENABLED(CONFIG_BRIDGE_MRP)
/* SWITCHDEV_OBJ_ID_MRP */
@@ -208,6 +311,7 @@ enum switchdev_notifier_type {
SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
SWITCHDEV_PORT_ATTR_SET, /* May be blocking . */
+ SWITCHDEV_PORT_OBJ_GET, /* Blocking */

SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
@@ -265,6 +369,9 @@ int switchdev_port_obj_add(struct net_device *dev,
struct netlink_ext_ack *extack);
int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj);
+int switchdev_port_obj_get(struct net_device *dev,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack);

int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb);
@@ -326,6 +433,14 @@ static inline int switchdev_port_obj_del(struct net_device *dev,
return -EOPNOTSUPP;
}

+int switchdev_port_obj_get(struct net_device *dev,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
+{
+ return -EOPNOTSUPP;
+}
+
+
static inline int register_switchdev_notifier(struct notifier_block *nb)
{
return 0;
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 865f3e037425..fefb05b885e0 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -372,6 +372,60 @@ int switchdev_port_obj_del(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_del);

+static int switchdev_port_obj_get_now(struct net_device *dev,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_trans trans;
+ int err;
+
+ /* Phase I: prepare for obj add. Driver/device should fail
+ * here if there are going to be issues in the commit phase,
+ * such as lack of resources or support. The driver/device
+ * should reserve resources needed for the commit phase here,
+ * but should not commit the obj.
+ */
+
+ trans.ph_prepare = true;
+ err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_GET,
+ dev, obj, &trans, extack);
+ if (err)
+ return err;
+
+ /* Phase II: commit obj add. This cannot fail as a fault
+ * of driver/device. If it does, it's a bug in the driver/device
+ * because the driver said everythings was OK in phase I.
+ */
+
+ trans.ph_prepare = false;
+ err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_GET,
+ dev, obj, &trans, extack);
+ WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
+
+ return err;
+}
+
+/**
+ * switchdev_port_obj_get - Get information from a port object
+ * It is expected that the driver fill in information in the
+ * obj structure.
+ *
+ * @dev: port device
+ * @obj: object to get information from
+ * @extack: netlink extended ack
+ *
+ * Use a 2-phase prepare-commit transaction model to ensure
+ * system is not left in a partially updated state due to
+ * failure from driver/device.
+ */
+int switchdev_port_obj_get(struct net_device *dev,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
+{
+ return switchdev_port_obj_get_now(dev, obj, extack);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_obj_get);
+
static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);

--
2.28.0

2020-10-01 10:41:13

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 02/11] bridge: cfm: Add BRIDGE_CFM to Kconfig.

This makes it possible to include or exclude the CFM
protocol according to 802.1Q section 12.14.

Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
net/bridge/Kconfig | 11 +++++++++++
net/bridge/br_device.c | 3 +++
net/bridge/br_private.h | 3 +++
3 files changed, 17 insertions(+)

diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index 80879196560c..3c8ded7d3e84 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -73,3 +73,14 @@ config BRIDGE_MRP
Say N to exclude this support and reduce the binary size.

If unsure, say N.
+
+config BRIDGE_CFM
+ bool "CFM protocol"
+ depends on BRIDGE
+ help
+ If you say Y here, then the Ethernet bridge will be able to run CFM
+ protocol according to 802.1Q section 12.14
+
+ Say N to exclude this support and reduce the binary size.
+
+ If unsure, say N.
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 206c4ba51cd2..2a3b316f32eb 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -476,6 +476,9 @@ void br_dev_setup(struct net_device *dev)
INIT_HLIST_HEAD(&br->frame_type_list);
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
INIT_LIST_HEAD(&br->mrp_list);
+#endif
+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+ INIT_HLIST_HEAD(&br->mep_list);
#endif
spin_lock_init(&br->hash_lock);

diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 747f6f08f439..c7b0e91547f6 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -492,6 +492,9 @@ struct net_bridge {
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
struct list_head mrp_list;
#endif
+#if IS_ENABLED(CONFIG_BRIDGE_CFM)
+ struct hlist_head mep_list;
+#endif
};

struct br_input_skb_cb {
--
2.28.0

2020-10-01 10:42:15

by Henrik Bjoernlund

[permalink] [raw]
Subject: [net-next v2 03/11] bridge: uapi: cfm: Added EtherType used by the CFM protocol.

This EtherType is used by all CFM protocal frames transmitted
according to 802.1Q section 12.14.

Acked-by: Nikolay Aleksandrov <[email protected]>
Reviewed-by: Horatiu Vultur <[email protected]>
Signed-off-by: Henrik Bjoernlund <[email protected]>
---
include/uapi/linux/if_ether.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index d6de2b167448..a0b637911d3c 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -99,6 +99,7 @@
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
+#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */
#define ETH_P_TDLS 0x890D /* TDLS */
--
2.28.0

2020-10-01 12:51:17

by Jiri Pirko

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

Thu, Oct 01, 2020 at 12:30:18PM CEST, [email protected] wrote:
>This is the definition of the CFM switchdev interface.
>
>The interface consist of these objects:
> SWITCHDEV_OBJ_ID_MEP_CFM,
> SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
> SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
> SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
> SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
> SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
> SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
>
>MEP instance add/del
> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
> switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)
>
>MEP cofigure
> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)
>
>MEP CC cofigure
> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)
>
>Peer MEP add/del
> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
> switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>
>Start/stop CCM transmission
> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)
>
>Get MEP status
> switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)
>
>Get Peer MEP status
> switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)
>
>Reviewed-by: Horatiu Vultur <[email protected]>
>Signed-off-by: Henrik Bjoernlund <[email protected]>

You have to submit the driver parts as a part of this patchset.
Otherwise it is no good.

Thanks!

2020-10-01 15:24:19

by kernel test robot

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

Hi Henrik,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url: https://github.com/0day-ci/linux/commits/Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git f2e834694b0d92187d889172da842e27829df371
config: arm-bcm2835_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/11484cf00dba95e52678e269d11425358dcde310
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
git checkout 11484cf00dba95e52678e269d11425358dcde310
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

arm-linux-gnueabi-ld: net/core/flow_dissector.o: in function `switchdev_port_obj_get':
>> flow_dissector.c:(.text+0x838): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/core/dev.o: in function `switchdev_port_obj_get':
dev.c:(.text+0x7760): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/core/dev_ioctl.o: in function `switchdev_port_obj_get':
dev_ioctl.c:(.text+0x3f0): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/ethernet/eth.o: in function `switchdev_port_obj_get':
eth.c:(.text+0x838): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/ethtool/tunnels.o: in function `switchdev_port_obj_get':
tunnels.c:(.text+0x400): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/ipv4/ip_tunnel_core.o: in function `switchdev_port_obj_get':
ip_tunnel_core.c:(.text+0xd6c): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here
arm-linux-gnueabi-ld: net/ipv4/ipconfig.o: in function `switchdev_port_obj_get':
ipconfig.c:(.text+0x1d8): multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:lan78xx.c:(.text+0x68b4): first defined here

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (3.02 kB)
.config.gz (27.06 kB)
Download all attachments

2020-10-01 15:40:15

by kernel test robot

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

Hi Henrik,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url: https://github.com/0day-ci/linux/commits/Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git f2e834694b0d92187d889172da842e27829df371
config: i386-randconfig-a004-20200930 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/11484cf00dba95e52678e269d11425358dcde310
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
git checkout 11484cf00dba95e52678e269d11425358dcde310
# save the attached .config to linux build tree
make W=1 ARCH=i386

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

ld: net/core/flow_dissector.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/core/dev.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/core/dev_ioctl.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/ethernet/eth.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/ethtool/tunnels.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/ipv4/ip_tunnel_core.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here
ld: net/ipv4/ipconfig.o: in function `switchdev_port_obj_get':
>> include/net/switchdev.h:441: multiple definition of `switchdev_port_obj_get'; drivers/net/usb/lan78xx.o:include/net/switchdev.h:441: first defined here

vim +441 include/net/switchdev.h

435
436 int switchdev_port_obj_get(struct net_device *dev,
437 const struct switchdev_obj *obj,
438 struct netlink_ext_ack *extack)
439 {
440 return -EOPNOTSUPP;
> 441 }
442

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (3.00 kB)
.config.gz (30.69 kB)
Download all attachments

2020-10-01 16:35:44

by kernel test robot

[permalink] [raw]
Subject: Re: [net-next v2 01/11] net: bridge: extend the process of special frames

Hi Henrik,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url: https://github.com/0day-ci/linux/commits/Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git f2e834694b0d92187d889172da842e27829df371
config: c6x-randconfig-r032-20200930 (attached as .config)
compiler: c6x-elf-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/1c0b81655468c16fd143f325138a856ca7727071
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
git checkout 1c0b81655468c16fd143f325138a856ca7727071
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=c6x

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All error/warnings (new ones prefixed by >>):

In file included from net/bridge/br_private_mrp.h:6,
from net/bridge/br_mrp.c:4:
>> net/bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~
>> net/bridge/br_mrp.c:13:19: error: initialization of 'int (*)(struct net_bridge_port *, struct sk_buff *)' from incompatible pointer type 'int (*)(struct net_bridge_port *, struct sk_buff *)' [-Werror=incompatible-pointer-types]
13 | .frame_handler = br_mrp_process,
| ^~~~~~~~~~~~~~
net/bridge/br_mrp.c:13:19: note: (near initialization for 'mrp_frame_type.frame_handler')
cc1: some warnings being treated as errors
--
In file included from net/bridge/br.c:20:
>> net/bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~
--
In file included from include/trace/events/bridge.h:10,
from net/bridge/br_fdb.c:24:
>> include/trace/events/../../../net/bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~
--
In file included from net/bridge/br_input.c:23:
>> net/bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~
net/bridge/br_input.c: In function 'br_process_frame_type':
>> net/bridge/br_input.c:267:30: error: passing argument 1 of 'tmp->frame_handler' from incompatible pointer type [-Werror=incompatible-pointer-types]
267 | return tmp->frame_handler(p, skb);
| ^
| |
| struct net_bridge_port *
net/bridge/br_input.c:267:30: note: expected 'struct net_bridge_port *' but argument is of type 'struct net_bridge_port *'
cc1: some warnings being treated as errors
--
In file included from net/bridge/br_netlink_tunnel.c:18:
>> net/bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~
net/bridge/br_netlink_tunnel.c:29:6: warning: no previous prototype for 'vlan_tunid_inrange' [-Wmissing-prototypes]
29 | bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
| ^~~~~~~~~~~~~~~~~~
net/bridge/br_netlink_tunnel.c:196:5: warning: no previous prototype for 'br_vlan_tunnel_info' [-Wmissing-prototypes]
196 | int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
| ^~~~~~~~~~~~~~~~~~~
--
In file included from net/atm/lec.c:36:
>> net/atm/../bridge/br_private.h:94:32: warning: 'struct net_bridge_port' declared inside parameter list will not be visible outside of this definition or declaration
94 | int (*frame_handler)(struct net_bridge_port *port,
| ^~~~~~~~~~~~~~~

vim +13 net/bridge/br_mrp.c

10
11 static struct br_frame_type mrp_frame_type __read_mostly = {
12 .type = cpu_to_be16(ETH_P_MRP),
> 13 .frame_handler = br_mrp_process,
14 };
15

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (5.25 kB)
.config.gz (26.12 kB)
Download all attachments

2020-10-01 18:05:06

by kernel test robot

[permalink] [raw]
Subject: Re: [net-next v2 01/11] net: bridge: extend the process of special frames

Hi Henrik,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url: https://github.com/0day-ci/linux/commits/Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git f2e834694b0d92187d889172da842e27829df371
config: x86_64-randconfig-a005-20200930 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project bcd05599d0e53977a963799d6ee4f6e0bc21331b)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# https://github.com/0day-ci/linux/commit/1c0b81655468c16fd143f325138a856ca7727071
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
git checkout 1c0b81655468c16fd143f325138a856ca7727071
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

In file included from net/ipv6/netfilter.c:21:
>> net/ipv6/../bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
1 warning generated.

vim +94 net/ipv6/../bridge/br_private.h

91
92 struct br_frame_type {
93 __be16 type;
> 94 int (*frame_handler)(struct net_bridge_port *port,
95 struct sk_buff *skb);
96 struct hlist_node list;
97 };
98

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.21 kB)
.config.gz (32.24 kB)
Download all attachments

2020-10-01 18:38:07

by kernel test robot

[permalink] [raw]
Subject: Re: [net-next v2 01/11] net: bridge: extend the process of special frames

Hi Henrik,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url: https://github.com/0day-ci/linux/commits/Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git f2e834694b0d92187d889172da842e27829df371
config: x86_64-randconfig-a003-20200930 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project bcd05599d0e53977a963799d6ee4f6e0bc21331b)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# https://github.com/0day-ci/linux/commit/1c0b81655468c16fd143f325138a856ca7727071
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Henrik-Bjoernlund/net-bridge-cfm-Add-support-for-Connectivity-Fault-Management-CFM/20201001-184031
git checkout 1c0b81655468c16fd143f325138a856ca7727071
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All error/warnings (new ones prefixed by >>):

In file included from net/atm/lec.c:36:
>> net/atm/../bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
1 warning generated.
--
In file included from net/bridge/br_sysfs_if.c:18:
>> net/bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
1 warning generated.
--
In file included from net/bridge/br_input.c:23:
>> net/bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
>> net/bridge/br_input.c:267:30: error: incompatible pointer types passing 'struct net_bridge_port *' to parameter of type 'struct net_bridge_port *' [-Werror,-Wincompatible-pointer-types]
return tmp->frame_handler(p, skb);
^
1 warning and 1 error generated.
--
In file included from net/bridge/br_mrp.c:4:
In file included from net/bridge/br_private_mrp.h:6:
>> net/bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
>> net/bridge/br_mrp.c:13:19: error: incompatible function pointer types initializing 'int (*)(struct net_bridge_port *, struct sk_buff *)' with an expression of type 'int (struct net_bridge_port *, struct sk_buff *)' [-Werror,-Wincompatible-function-pointer-types]
.frame_handler = br_mrp_process,
^~~~~~~~~~~~~~
1 warning and 1 error generated.
--
In file included from net/bridge/br_fdb.c:24:
In file included from include/trace/events/bridge.h:10:
>> include/trace/events/../../../net/bridge/br_private.h:94:32: warning: declaration of 'struct net_bridge_port' will not be visible outside of this function [-Wvisibility]
int (*frame_handler)(struct net_bridge_port *port,
^
1 warning generated.

vim +267 net/bridge/br_input.c

256
257 /* Return 0 if the frame was not processed otherwise 1
258 * note: already called with rcu_read_lock
259 */
260 static int br_process_frame_type(struct net_bridge_port *p,
261 struct sk_buff *skb)
262 {
263 struct br_frame_type *tmp;
264
265 hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list)
266 if (unlikely(tmp->type == skb->protocol))
> 267 return tmp->frame_handler(p, skb);
268
269 return 0;
270 }
271

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (4.74 kB)
.config.gz (33.87 kB)
Download all attachments

2020-10-05 13:10:41

by [email protected]

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

Hi Jiri

On 01.10.2020 14:49, Jiri Pirko wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>Thu, Oct 01, 2020 at 12:30:18PM CEST, [email protected] wrote:
>>This is the definition of the CFM switchdev interface.
>>
>>The interface consist of these objects:
>> SWITCHDEV_OBJ_ID_MEP_CFM,
>> SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
>> SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
>> SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
>> SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
>> SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
>> SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
>>
>>MEP instance add/del
>> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
>> switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)
>>
>>MEP cofigure
>> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)
>>
>>MEP CC cofigure
>> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)
>>
>>Peer MEP add/del
>> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>> switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>>
>>Start/stop CCM transmission
>> switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)
>>
>>Get MEP status
>> switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)
>>
>>Get Peer MEP status
>> switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)
>>
>>Reviewed-by: Horatiu Vultur <[email protected]>
>>Signed-off-by: Henrik Bjoernlund <[email protected]>
>
>You have to submit the driver parts as a part of this patchset.
>Otherwise it is no good.
Fair enough.

With MRP we did it like this, and after Nik asked for details on what is
being offload, we thought that adding this would help.

The reason why we did not include the implementation of this interface
is that it is for a new SoC which is still not fully available which is
why we have not done the basic SwitchDev driver for it yet. But the
basic functionality clearly needs to come first.

Our preference is to continue fixing the comments we got on the pure SW
implementation and then get back to the SwitchDev offloading.

This will mean dropping the last 2 patches in the serie.

Does that work for you Jiri, and Nik?

/Allan

2020-10-06 09:03:29

by Jiri Pirko

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

Mon, Oct 05, 2020 at 03:07:12PM CEST, [email protected] wrote:
>Hi Jiri
>
>On 01.10.2020 14:49, Jiri Pirko wrote:
>> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>>
>> Thu, Oct 01, 2020 at 12:30:18PM CEST, [email protected] wrote:
>> > This is the definition of the CFM switchdev interface.
>> >
>> > The interface consist of these objects:
>> > SWITCHDEV_OBJ_ID_MEP_CFM,
>> > SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
>> > SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
>> > SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
>> > SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
>> > SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
>> > SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
>> >
>> > MEP instance add/del
>> > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
>> > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)
>> >
>> > MEP cofigure
>> > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)
>> >
>> > MEP CC cofigure
>> > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)
>> >
>> > Peer MEP add/del
>> > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>> > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>> >
>> > Start/stop CCM transmission
>> > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)
>> >
>> > Get MEP status
>> > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)
>> >
>> > Get Peer MEP status
>> > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)
>> >
>> > Reviewed-by: Horatiu Vultur <[email protected]>
>> > Signed-off-by: Henrik Bjoernlund <[email protected]>
>>
>> You have to submit the driver parts as a part of this patchset.
>> Otherwise it is no good.
>Fair enough.
>
>With MRP we did it like this, and after Nik asked for details on what is
>being offload, we thought that adding this would help.
>
>The reason why we did not include the implementation of this interface
>is that it is for a new SoC which is still not fully available which is
>why we have not done the basic SwitchDev driver for it yet. But the
>basic functionality clearly needs to come first.
>
>Our preference is to continue fixing the comments we got on the pure SW
>implementation and then get back to the SwitchDev offloading.
>
>This will mean dropping the last 2 patches in the serie.
>
>Does that work for you Jiri, and Nik?

Sure.

>
>/Allan
>

2020-10-06 10:52:09

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

On Mon, 2020-10-05 at 15:07 +0200, Allan W. Nielsen wrote:
> Hi Jiri
>
> On 01.10.2020 14:49, Jiri Pirko wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> >
> > Thu, Oct 01, 2020 at 12:30:18PM CEST, [email protected] wrote:
> > > This is the definition of the CFM switchdev interface.
> > >
> > > The interface consist of these objects:
> > > SWITCHDEV_OBJ_ID_MEP_CFM,
> > > SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
> > > SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
> > > SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
> > > SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
> > > SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
> > > SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
> > >
> > > MEP instance add/del
> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
> > > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)
> > >
> > > MEP cofigure
> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)
> > >
> > > MEP CC cofigure
> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)
> > >
> > > Peer MEP add/del
> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
> > > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
> > >
> > > Start/stop CCM transmission
> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)
> > >
> > > Get MEP status
> > > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)
> > >
> > > Get Peer MEP status
> > > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)
> > >
> > > Reviewed-by: Horatiu Vultur <[email protected]>
> > > Signed-off-by: Henrik Bjoernlund <[email protected]>
> >
> > You have to submit the driver parts as a part of this patchset.
> > Otherwise it is no good.
> Fair enough.
>
> With MRP we did it like this, and after Nik asked for details on what is
> being offload, we thought that adding this would help.
>
> The reason why we did not include the implementation of this interface
> is that it is for a new SoC which is still not fully available which is
> why we have not done the basic SwitchDev driver for it yet. But the
> basic functionality clearly needs to come first.
>
> Our preference is to continue fixing the comments we got on the pure SW
> implementation and then get back to the SwitchDev offloading.
>
> This will mean dropping the last 2 patches in the serie.
>
> Does that work for you Jiri, and Nik?
>
> /Allan
>

Sounds good to me. Sorry I was unresponsive last week, but I was sick and
couldn't get to netdev@. I'll review the set today.

Cheers,
Nik

2020-10-06 10:54:32

by [email protected]

[permalink] [raw]
Subject: Re: [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation

On 06.10.2020 10:50, Nikolay Aleksandrov wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>On Mon, 2020-10-05 at 15:07 +0200, Allan W. Nielsen wrote:
>> Hi Jiri
>>
>> On 01.10.2020 14:49, Jiri Pirko wrote:
>> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>> >
>> > Thu, Oct 01, 2020 at 12:30:18PM CEST, [email protected] wrote:
>> > > This is the definition of the CFM switchdev interface.
>> > >
>> > > The interface consist of these objects:
>> > > SWITCHDEV_OBJ_ID_MEP_CFM,
>> > > SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
>> > > SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
>> > > SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
>> > > SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
>> > > SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
>> > > SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM
>> > >
>> > > MEP instance add/del
>> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CFM)
>> > > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_MEP_CFM)
>> > >
>> > > MEP cofigure
>> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM)
>> > >
>> > > MEP CC cofigure
>> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CONFIG_CFM)
>> > >
>> > > Peer MEP add/del
>> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>> > > switchdev_port_obj_del(SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM)
>> > >
>> > > Start/stop CCM transmission
>> > > switchdev_port_obj_add(SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM)
>> > >
>> > > Get MEP status
>> > > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_MEP_STATUS_CFM)
>> > >
>> > > Get Peer MEP status
>> > > switchdev_port_obj_get(SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM)
>> > >
>> > > Reviewed-by: Horatiu Vultur <[email protected]>
>> > > Signed-off-by: Henrik Bjoernlund <[email protected]>
>> >
>> > You have to submit the driver parts as a part of this patchset.
>> > Otherwise it is no good.
>> Fair enough.
>>
>> With MRP we did it like this, and after Nik asked for details on what is
>> being offload, we thought that adding this would help.
>>
>> The reason why we did not include the implementation of this interface
>> is that it is for a new SoC which is still not fully available which is
>> why we have not done the basic SwitchDev driver for it yet. But the
>> basic functionality clearly needs to come first.
>>
>> Our preference is to continue fixing the comments we got on the pure SW
>> implementation and then get back to the SwitchDev offloading.
>>
>> This will mean dropping the last 2 patches in the serie.
>>
>> Does that work for you Jiri, and Nik?
>>
>> /Allan
>>
>
>Sounds good to me. Sorry I was unresponsive last week, but I was sick and
>couldn't get to netdev@. I'll review the set today.

Perfect. Thanks for the support.

/Allan


2020-10-06 14:14:54

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 01/11] net: bridge: extend the process of special frames

On Thu, 2020-10-01 at 10:30 +0000, Henrik Bjoernlund wrote:
> This patch extends the processing of frames in the bridge. Currently MRP
> frames needs special processing and the current implementation doesn't
> allow a nice way to process different frame types. Therefore try to
> improve this by adding a list that contains frame types that need
> special processing. This list is iterated for each input frame and if
> there is a match based on frame type then these functions will be called
> and decide what to do with the frame. It can process the frame then the
> bridge doesn't need to do anything or don't process so then the bridge
> will do normal forwarding.
>
> Reviewed-by: Horatiu Vultur <[email protected]>
> Signed-off-by: Henrik Bjoernlund <[email protected]>
> ---
> net/bridge/br_device.c | 1 +
> net/bridge/br_input.c | 31 ++++++++++++++++++++++++++++++-
> net/bridge/br_mrp.c | 19 +++++++++++++++----
> net/bridge/br_private.h | 18 ++++++++++++------
> 4 files changed, 58 insertions(+), 11 deletions(-)
>

Hi,
Mostly looks good, one comment below.

> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 9a2fb4aa1a10..206c4ba51cd2 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> [snip]
> @@ -380,3 +395,17 @@ rx_handler_func_t *br_get_rx_handler(const struct net_device *dev)
>
> return br_handle_frame;
> }
> +
> +void br_add_frame(struct net_bridge *br, struct br_frame_type *ft)
> +{
> + hlist_add_head_rcu(&ft->list, &br->frame_type_list);
> +}
> +
> +void br_del_frame(struct net_bridge *br, struct br_frame_type *ft)
> +{
> + struct br_frame_type *tmp;
> +
> + hlist_for_each_entry(tmp, &br->frame_type_list, list)
> + if (ft == tmp)
> + hlist_del_rcu(&ft->list);

This hasn't crashed only because you're using hlist_del_rcu(), otherwise it's
wrong. You should use hlist_for_each_entry_safe() when deleting from the list
while walking it or you should end the walk after the delete since there can't
be two elements with the same address anyway.

Thanks,
Nik



2020-10-06 14:31:20

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 04/11] bridge: cfm: Kernel space implementation of CFM.

On Thu, 2020-10-01 at 10:30 +0000, Henrik Bjoernlund wrote:
> This is the first commit of the implementation of the CFM protocol
> according to 802.1Q section 12.14.
>
> 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 other<E2><80><99>s 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.
>
> Reviewed-by: Horatiu Vultur <[email protected]>
> Signed-off-by: Henrik Bjoernlund <[email protected]>
> ---

Thank you for breaking the big patch into 3 smaller pieces, but could you please
name them appropriately? I'm sure they add different things, so just give them
something more descriptive. Having the same subject for 3 patches looks odd.

> include/uapi/linux/cfm_bridge.h | 23 +++
> net/bridge/Makefile | 2 +
> net/bridge/br_cfm.c | 263 ++++++++++++++++++++++++++++++++
> net/bridge/br_private_cfm.h | 61 ++++++++
> 4 files changed, 349 insertions(+)
> create mode 100644 include/uapi/linux/cfm_bridge.h
> create mode 100644 net/bridge/br_cfm.c
> create mode 100644 net/bridge/br_private_cfm.h
>
[snip]
> +
> + 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);
> +
> + 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;
> +
> + ASSERT_RTNL();
> +
> + /* Empty and free peer MEP list */
> + hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head) {

hlist_for_each_entry_safe()

> + hlist_del_rcu(&peer_mep->head);
> + kfree_rcu(peer_mep, rcu);
> + }
> +
> + RCU_INIT_POINTER(mep->b_port, NULL);
> + hlist_del_rcu(&mep->head);
> + kfree_rcu(mep, rcu);
> +}

2020-10-06 14:48:12

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 07/11] bridge: cfm: Netlink Interface.

On Thu, 2020-10-01 at 10:30 +0000, Henrik Bjoernlund wrote:
> This is the implementation of CFM netlink configuration
> and status information interface.
>
> Add new nested netlink attributes. These attributes are used by the
> user space to create/delete/configure CFM instances and get status.
> Also they are used by the kernel to notify the user space when changes
> in any status happens.
>
> 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.
>
> GETLINK:
> Request filter RTEXT_FILTER_CFM_CONFIG:
> Indicating that CFM configuration information must be delivered.
>
> IFLA_BRIDGE_CFM:
> Points to the CFM information.
>
> IFLA_BRIDGE_CFM_MEP_CREATE_INFO:
> This indicate that MEP instance create parameters are following.
> IFLA_BRIDGE_CFM_MEP_CONFIG_INFO:
> This indicate that MEP instance config parameters are following.
> IFLA_BRIDGE_CFM_CC_CONFIG_INFO:
> This indicate that MEP instance CC functionality
> parameters are following.
> IFLA_BRIDGE_CFM_CC_RDI_INFO:
> This indicate that CC transmitted CCM PDU RDI
> parameters are following.
> IFLA_BRIDGE_CFM_CC_CCM_TX_INFO:
> This indicate that CC transmitted CCM PDU parameters are
> following.
> IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO:
> This indicate that the added peer MEP IDs are following.
>
> Request filter RTEXT_FILTER_CFM_STATUS:
> Indicating that CFM status information must be delivered.
>
> 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.
>
> SETLINK and GETLINK 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.
>
> GETLINK RTEXT_FILTER_CFM_STATUS:
> IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE:
> The MEP instance number of the delivered status.
> The type is u32.
> IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN:
> The MEP instance received CFM PDU with unexpected Opcode.
> The type is u32 (bool).
> IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN:
> The MEP instance received CFM PDU with unexpected version.
> The type is 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 u32 (bool).
>
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE:
> The MEP instance number of the delivered status.
> The type is u32.
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID:
> The added Peer MEP ID of the delivered status.
> The type is u32.
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT:
> The CCM defect status.
> The type is 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 u32 (bool).
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE:
> The last received CCM PDU Port Status TLV value field.
> The type is u8.
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE:
> The last received CCM PDU Interface Status TLV value field.
> The type is u8.
> IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN:
> A CCM frame has been received from Peer MEP.
> The type is 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 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 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.
>
> Reviewed-by: Horatiu Vultur <[email protected]>
> Signed-off-by: Henrik Bjoernlund <[email protected]>
> ---
> include/uapi/linux/if_bridge.h | 125 ++++++
> include/uapi/linux/rtnetlink.h | 2 +
> net/bridge/Makefile | 2 +-
> net/bridge/br_cfm.c | 5 +
> net/bridge/br_cfm_netlink.c | 724 +++++++++++++++++++++++++++++++++
> net/bridge/br_netlink.c | 67 ++-
> net/bridge/br_private.h | 31 ++
> 7 files changed, 940 insertions(+), 16 deletions(-)
> create mode 100644 net/bridge/br_cfm_netlink.c
>

Hi Henrik,
This patch definitely can be broken into a few smaller ones that add support for
configuring/dumping different cfm parts.

Also, and more importantly, the IFLA_AF_SPEC fix must go to -net as a separate
patch since the problem is there even now. After it gets accepted and -net is
merged into net-next you can rebase on top of it.

Thanks,
Nik


2020-10-06 14:53:26

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 08/11] bridge: cfm: Netlink Notifications.

On Thu, 2020-10-01 at 10:30 +0000, Henrik Bjoernlund wrote:
> 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.
>
> Reviewed-by: Horatiu Vultur <[email protected]>
> Signed-off-by: Henrik Bjoernlund <[email protected]>
> ---
> net/bridge/br_cfm.c | 48 ++++++++++++++++++++++++
> net/bridge/br_cfm_netlink.c | 27 +++++++++-----
> net/bridge/br_netlink.c | 73 ++++++++++++++++++++++++++++++++-----
> net/bridge/br_private.h | 22 ++++++++++-
> 4 files changed, 148 insertions(+), 22 deletions(-)
>
[snip]
> return !hlist_empty(&br->mep_list);
> diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
> index 7bdf890b8ccc..5f81262c9caa 100644
> --- a/net/bridge/br_cfm_netlink.c
> +++ b/net/bridge/br_cfm_netlink.c
> @@ -325,8 +325,8 @@ static int br_cc_ccm_tx_parse(struct net_bridge *br, struct nlattr *attr,
> struct netlink_ext_ack *extack)
> {
> struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1];
> - u32 instance;
> struct br_cfm_cc_ccm_tx_info tx_info;
> + u32 instance;
> int err;

This hunk is unnecessary as it's new code added by this set, just add it
correctly in the first place and drop this change.

Thanks,
Nik

2020-10-06 14:55:10

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [net-next v2 09/11] bridge: cfm: Bridge port remove.

On Thu, 2020-10-01 at 10:30 +0000, Henrik Bjoernlund wrote:
> This is addition of CFM functionality to delete MEP instances
> on a port that is removed from the bridge.
> A MEP can only exist on a port that is related to a bridge.
>
> Reviewed-by: Horatiu Vultur <[email protected]>
> Signed-off-by: Henrik Bjoernlund <[email protected]>m
> ---
> net/bridge/br_cfm.c | 13 +++++++++++++
> net/bridge/br_if.c | 1 +
> net/bridge/br_private.h | 6 ++++++
> 3 files changed, 20 insertions(+)
>

This patch should be a part of the one which adds the ability to attach MEPs to
ports so they will get cleaned up properly on port del in the same patch.
Is there a reason for it to be a separate patch?

One more comment below.

Thanks,
Nik

> diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
> index 6fbfef44c235..fc8268cb76c1 100644
> --- a/net/bridge/br_cfm.c
> +++ b/net/bridge/br_cfm.c
> @@ -867,3 +867,16 @@ bool br_cfm_created(struct net_bridge *br)
> {
> return !hlist_empty(&br->mep_list);
> }
> +
> +/* Deletes the CFM instances on a specific bridge port
> + */
> +void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
> +{
> + struct br_cfm_mep *mep;
> +
> + ASSERT_RTNL();
> +
> + hlist_for_each_entry(mep, &br->mep_list, head)

hlist_for_each_entry_safe()

> + if (mep->create.ifindex == port->dev->ifindex)
> + mep_delete_implementation(br, mep);
> +}
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index a0e9a7937412..f7d2f472ae24 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -334,6 +334,7 @@ static void del_nbp(struct net_bridge_port *p)
> spin_unlock_bh(&br->lock);
>
> br_mrp_port_del(br, p);
> + br_cfm_port_del(br, p);
>
> br_ifinfo_notify(RTM_DELLINK, NULL, p);
>
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 5954ee45af80..735dd0028b40 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -1465,6 +1465,7 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br)
> int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p,
> struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
> bool br_cfm_created(struct net_bridge *br);
> +void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p);
> int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
> int br_cfm_status_fill_info(struct sk_buff *skb,
> struct net_bridge *br,
> @@ -1484,6 +1485,11 @@ static inline bool br_cfm_created(struct net_bridge *br)
> return false;
> }
>
> +static inline void br_cfm_port_del(struct net_bridge *br,
> + struct net_bridge_port *p)
> +{
> +}
> +
> static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
> {
> return -EOPNOTSUPP;