Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bridge: cfm: Kernel space implementation of CFM. MEP create/delete.

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

It contains MEP instance create, delete and configuration.

Connectivity Fault Management (CFM) comprises capabilities for
detecting, verifying, and isolating connectivity failures in
Virtual Bridged Networks. These capabilities can be used in
networks operated by multiple independent organizations, each
with restricted management access to each others equipment.

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

Interface consists of these functions:
br_cfm_mep_create()
br_cfm_mep_delete()
br_cfm_mep_config_set()
br_cfm_cc_config_set()
br_cfm_cc_peer_mep_add()
br_cfm_cc_peer_mep_remove()

A MEP instance is created by br_cfm_mep_create()
-It is the Maintenance association End Point
described in 802.1Q section 19.2.
-It is created on a specific level (1-7) and is assuring
that no CFM frames are passing through this MEP on lower levels.
-It initiates and validates CFM frames on its level.
-It can only exist on a port that is related to a bridge.
-Attributes given cannot be changed until the instance is
deleted.

A MEP instance can be deleted by br_cfm_mep_delete().

A created MEP instance has attributes that can be
configured by br_cfm_mep_config_set().

A MEP Continuity Check feature can be configured by
br_cfm_cc_config_set()
The Continuity Check Receiver state machine can be
enabled and disabled.
According to 802.1Q section 19.2.8

A MEP can have Peer MEPs added and removed by
br_cfm_cc_peer_mep_add() and br_cfm_cc_peer_mep_remove()
The Continuity Check feature can maintain connectivity
status on each added Peer MEP.

Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Henrik Bjoernlund and committed by
Jakub Kicinski
86a14b79 fbaedb41

+357
+23
include/uapi/linux/cfm_bridge.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 + 3 + #ifndef _UAPI_LINUX_CFM_BRIDGE_H_ 4 + #define _UAPI_LINUX_CFM_BRIDGE_H_ 5 + 6 + #include <linux/types.h> 7 + #include <linux/if_ether.h> 8 + 9 + #define CFM_MAID_LENGTH 48 10 + 11 + /* MEP domain */ 12 + enum br_cfm_domain { 13 + BR_CFM_PORT, 14 + BR_CFM_VLAN, 15 + }; 16 + 17 + /* MEP direction */ 18 + enum br_cfm_mep_direction { 19 + BR_CFM_MEP_DIRECTION_DOWN, 20 + BR_CFM_MEP_DIRECTION_UP, 21 + }; 22 + 23 + #endif
+2
net/bridge/Makefile
··· 27 27 obj-$(CONFIG_NETFILTER) += netfilter/ 28 28 29 29 bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o 30 + 31 + bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o
+260
net/bridge/br_cfm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + #include <linux/cfm_bridge.h> 4 + #include <uapi/linux/cfm_bridge.h> 5 + #include "br_private_cfm.h" 6 + 7 + static struct br_cfm_mep *br_mep_find(struct net_bridge *br, u32 instance) 8 + { 9 + struct br_cfm_mep *mep; 10 + 11 + hlist_for_each_entry(mep, &br->mep_list, head) 12 + if (mep->instance == instance) 13 + return mep; 14 + 15 + return NULL; 16 + } 17 + 18 + static struct br_cfm_mep *br_mep_find_ifindex(struct net_bridge *br, 19 + u32 ifindex) 20 + { 21 + struct br_cfm_mep *mep; 22 + 23 + hlist_for_each_entry_rcu(mep, &br->mep_list, head, 24 + lockdep_rtnl_is_held()) 25 + if (mep->create.ifindex == ifindex) 26 + return mep; 27 + 28 + return NULL; 29 + } 30 + 31 + static struct br_cfm_peer_mep *br_peer_mep_find(struct br_cfm_mep *mep, 32 + u32 mepid) 33 + { 34 + struct br_cfm_peer_mep *peer_mep; 35 + 36 + hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head, 37 + lockdep_rtnl_is_held()) 38 + if (peer_mep->mepid == mepid) 39 + return peer_mep; 40 + 41 + return NULL; 42 + } 43 + 44 + static struct net_bridge_port *br_mep_get_port(struct net_bridge *br, 45 + u32 ifindex) 46 + { 47 + struct net_bridge_port *port; 48 + 49 + list_for_each_entry(port, &br->port_list, list) 50 + if (port->dev->ifindex == ifindex) 51 + return port; 52 + 53 + return NULL; 54 + } 55 + 56 + int br_cfm_mep_create(struct net_bridge *br, 57 + const u32 instance, 58 + struct br_cfm_mep_create *const create, 59 + struct netlink_ext_ack *extack) 60 + { 61 + struct net_bridge_port *p; 62 + struct br_cfm_mep *mep; 63 + 64 + ASSERT_RTNL(); 65 + 66 + if (create->domain == BR_CFM_VLAN) { 67 + NL_SET_ERR_MSG_MOD(extack, 68 + "VLAN domain not supported"); 69 + return -EINVAL; 70 + } 71 + if (create->domain != BR_CFM_PORT) { 72 + NL_SET_ERR_MSG_MOD(extack, 73 + "Invalid domain value"); 74 + return -EINVAL; 75 + } 76 + if (create->direction == BR_CFM_MEP_DIRECTION_UP) { 77 + NL_SET_ERR_MSG_MOD(extack, 78 + "Up-MEP not supported"); 79 + return -EINVAL; 80 + } 81 + if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) { 82 + NL_SET_ERR_MSG_MOD(extack, 83 + "Invalid direction value"); 84 + return -EINVAL; 85 + } 86 + p = br_mep_get_port(br, create->ifindex); 87 + if (!p) { 88 + NL_SET_ERR_MSG_MOD(extack, 89 + "Port is not related to bridge"); 90 + return -EINVAL; 91 + } 92 + mep = br_mep_find(br, instance); 93 + if (mep) { 94 + NL_SET_ERR_MSG_MOD(extack, 95 + "MEP instance already exists"); 96 + return -EEXIST; 97 + } 98 + 99 + /* In PORT domain only one instance can be created per port */ 100 + if (create->domain == BR_CFM_PORT) { 101 + mep = br_mep_find_ifindex(br, create->ifindex); 102 + if (mep) { 103 + NL_SET_ERR_MSG_MOD(extack, 104 + "Only one Port MEP on a port allowed"); 105 + return -EINVAL; 106 + } 107 + } 108 + 109 + mep = kzalloc(sizeof(*mep), GFP_KERNEL); 110 + if (!mep) 111 + return -ENOMEM; 112 + 113 + mep->create = *create; 114 + mep->instance = instance; 115 + rcu_assign_pointer(mep->b_port, p); 116 + 117 + INIT_HLIST_HEAD(&mep->peer_mep_list); 118 + 119 + hlist_add_tail_rcu(&mep->head, &br->mep_list); 120 + 121 + return 0; 122 + } 123 + 124 + static void mep_delete_implementation(struct net_bridge *br, 125 + struct br_cfm_mep *mep) 126 + { 127 + struct br_cfm_peer_mep *peer_mep; 128 + struct hlist_node *n_store; 129 + 130 + ASSERT_RTNL(); 131 + 132 + /* Empty and free peer MEP list */ 133 + hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) { 134 + hlist_del_rcu(&peer_mep->head); 135 + kfree_rcu(peer_mep, rcu); 136 + } 137 + 138 + RCU_INIT_POINTER(mep->b_port, NULL); 139 + hlist_del_rcu(&mep->head); 140 + kfree_rcu(mep, rcu); 141 + } 142 + 143 + int br_cfm_mep_delete(struct net_bridge *br, 144 + const u32 instance, 145 + struct netlink_ext_ack *extack) 146 + { 147 + struct br_cfm_mep *mep; 148 + 149 + ASSERT_RTNL(); 150 + 151 + mep = br_mep_find(br, instance); 152 + if (!mep) { 153 + NL_SET_ERR_MSG_MOD(extack, 154 + "MEP instance does not exists"); 155 + return -ENOENT; 156 + } 157 + 158 + mep_delete_implementation(br, mep); 159 + 160 + return 0; 161 + } 162 + 163 + int br_cfm_mep_config_set(struct net_bridge *br, 164 + const u32 instance, 165 + const struct br_cfm_mep_config *const config, 166 + struct netlink_ext_ack *extack) 167 + { 168 + struct br_cfm_mep *mep; 169 + 170 + ASSERT_RTNL(); 171 + 172 + mep = br_mep_find(br, instance); 173 + if (!mep) { 174 + NL_SET_ERR_MSG_MOD(extack, 175 + "MEP instance does not exists"); 176 + return -ENOENT; 177 + } 178 + 179 + mep->config = *config; 180 + 181 + return 0; 182 + } 183 + 184 + int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance, 185 + u32 mepid, 186 + struct netlink_ext_ack *extack) 187 + { 188 + struct br_cfm_peer_mep *peer_mep; 189 + struct br_cfm_mep *mep; 190 + 191 + ASSERT_RTNL(); 192 + 193 + mep = br_mep_find(br, instance); 194 + if (!mep) { 195 + NL_SET_ERR_MSG_MOD(extack, 196 + "MEP instance does not exists"); 197 + return -ENOENT; 198 + } 199 + 200 + peer_mep = br_peer_mep_find(mep, mepid); 201 + if (peer_mep) { 202 + NL_SET_ERR_MSG_MOD(extack, 203 + "Peer MEP-ID already exists"); 204 + return -EEXIST; 205 + } 206 + 207 + peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL); 208 + if (!peer_mep) 209 + return -ENOMEM; 210 + 211 + peer_mep->mepid = mepid; 212 + peer_mep->mep = mep; 213 + 214 + hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list); 215 + 216 + return 0; 217 + } 218 + 219 + int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance, 220 + u32 mepid, 221 + struct netlink_ext_ack *extack) 222 + { 223 + struct br_cfm_peer_mep *peer_mep; 224 + struct br_cfm_mep *mep; 225 + 226 + ASSERT_RTNL(); 227 + 228 + mep = br_mep_find(br, instance); 229 + if (!mep) { 230 + NL_SET_ERR_MSG_MOD(extack, 231 + "MEP instance does not exists"); 232 + return -ENOENT; 233 + } 234 + 235 + peer_mep = br_peer_mep_find(mep, mepid); 236 + if (!peer_mep) { 237 + NL_SET_ERR_MSG_MOD(extack, 238 + "Peer MEP-ID does not exists"); 239 + return -ENOENT; 240 + } 241 + 242 + hlist_del_rcu(&peer_mep->head); 243 + kfree_rcu(peer_mep, rcu); 244 + 245 + return 0; 246 + } 247 + 248 + /* Deletes the CFM instances on a specific bridge port 249 + */ 250 + void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port) 251 + { 252 + struct hlist_node *n_store; 253 + struct br_cfm_mep *mep; 254 + 255 + ASSERT_RTNL(); 256 + 257 + hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head) 258 + if (mep->create.ifindex == port->dev->ifindex) 259 + mep_delete_implementation(br, mep); 260 + }
+1
net/bridge/br_if.c
··· 334 334 spin_unlock_bh(&br->lock); 335 335 336 336 br_mrp_port_del(br, p); 337 + br_cfm_port_del(br, p); 337 338 338 339 br_ifinfo_notify(RTM_DELLINK, NULL, p); 339 340
+10
net/bridge/br_private.h
··· 1459 1459 1460 1460 #endif 1461 1461 1462 + /* br_mrp.c */ 1463 + #if IS_ENABLED(CONFIG_BRIDGE_CFM) 1464 + void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p); 1465 + #else 1466 + static inline void br_cfm_port_del(struct net_bridge *br, 1467 + struct net_bridge_port *p) 1468 + { 1469 + } 1470 + #endif 1471 + 1462 1472 /* br_netlink.c */ 1463 1473 extern struct rtnl_link_ops br_link_ops; 1464 1474 int br_netlink_init(void);
+61
net/bridge/br_private_cfm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef _BR_PRIVATE_CFM_H_ 4 + #define _BR_PRIVATE_CFM_H_ 5 + 6 + #include "br_private.h" 7 + #include <uapi/linux/cfm_bridge.h> 8 + 9 + struct br_cfm_mep_create { 10 + enum br_cfm_domain domain; /* Domain for this MEP */ 11 + enum br_cfm_mep_direction direction; /* Up or Down MEP direction */ 12 + u32 ifindex; /* Residence port */ 13 + }; 14 + 15 + int br_cfm_mep_create(struct net_bridge *br, 16 + const u32 instance, 17 + struct br_cfm_mep_create *const create, 18 + struct netlink_ext_ack *extack); 19 + 20 + int br_cfm_mep_delete(struct net_bridge *br, 21 + const u32 instance, 22 + struct netlink_ext_ack *extack); 23 + 24 + struct br_cfm_mep_config { 25 + u32 mdlevel; 26 + u32 mepid; /* MEPID for this MEP */ 27 + struct mac_addr unicast_mac; /* The MEP unicast MAC */ 28 + }; 29 + 30 + int br_cfm_mep_config_set(struct net_bridge *br, 31 + const u32 instance, 32 + const struct br_cfm_mep_config *const config, 33 + struct netlink_ext_ack *extack); 34 + 35 + int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance, 36 + u32 peer_mep_id, 37 + struct netlink_ext_ack *extack); 38 + int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance, 39 + u32 peer_mep_id, 40 + struct netlink_ext_ack *extack); 41 + 42 + struct br_cfm_mep { 43 + /* list header of MEP instances */ 44 + struct hlist_node head; 45 + u32 instance; 46 + struct br_cfm_mep_create create; 47 + struct br_cfm_mep_config config; 48 + /* List of multiple peer MEPs */ 49 + struct hlist_head peer_mep_list; 50 + struct net_bridge_port __rcu *b_port; 51 + struct rcu_head rcu; 52 + }; 53 + 54 + struct br_cfm_peer_mep { 55 + struct hlist_node head; 56 + struct br_cfm_mep *mep; 57 + u32 mepid; 58 + struct rcu_head rcu; 59 + }; 60 + 61 + #endif /* _BR_PRIVATE_CFM_H_ */