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

Merge branch 'net-bridge-cfm-add-support-for-connectivity-fault-management-cfm'

Henrik Bjoernlund says:

====================
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 user interacts with CFM using the 'cfm' user space client
program, the client talks with the kernel using netlink.

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.

v2 -> v3
-The switchdev definition and utilization has been removed as
there was no switchdev implementation.
-Some compiling issues are fixed as Reported-by:
kernel test robot <lkp@intel.com>.

v3 -> v4
-Fixed potential crash during hlist walk where elements are
removed.
-Giving all commits unique titles.
-NETLINK implementation split into three commits.
-Commit "bridge: cfm: Bridge port remove" is merged with
commit "bridge: cfm: Kernel space implementation of CFM. MEP
create/delete."

v4 -> v5
-Reordered members in struct net_bridge to bring member
frame_type_list to the first cache line.
-Helper functions nla_get_mac() and nla_get_maid() are removed.
-The NLA_POLICY_NESTED() macro is used to initialize the
br_cfm_policy array.
-Fixed reverse xmas tree.

v5 -> v6
-Fixed that the SKB buffer was not freed during error handling return.
-Removed unused struct definition.
-Changed bool to u8 bitfields for space save.
-Utilizing the NETLINK policy validation feature.

v6 -> v7
-Removed check of parameters in br_cfm_mep_config_set() and
br_cfm_cc_peer_mep_add() in first commit of MEP implementation
(Patch 4 out of 10)

Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>
====================

Link: https://lore.kernel.org/r/20201027100251.3241719-1-henrik.bjoernlund@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+2171 -23
+64
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 ETHER_HEADER_LENGTH (6+6+4+2) 10 + #define CFM_MAID_LENGTH 48 11 + #define CFM_CCM_PDU_LENGTH 75 12 + #define CFM_PORT_STATUS_TLV_LENGTH 4 13 + #define CFM_IF_STATUS_TLV_LENGTH 4 14 + #define CFM_IF_STATUS_TLV_TYPE 4 15 + #define CFM_PORT_STATUS_TLV_TYPE 2 16 + #define CFM_ENDE_TLV_TYPE 0 17 + #define CFM_CCM_MAX_FRAME_LENGTH (ETHER_HEADER_LENGTH+\ 18 + CFM_CCM_PDU_LENGTH+\ 19 + CFM_PORT_STATUS_TLV_LENGTH+\ 20 + CFM_IF_STATUS_TLV_LENGTH) 21 + #define CFM_FRAME_PRIO 7 22 + #define CFM_CCM_TLV_OFFSET 70 23 + #define CFM_CCM_PDU_MAID_OFFSET 10 24 + #define CFM_CCM_PDU_MEPID_OFFSET 8 25 + #define CFM_CCM_PDU_SEQNR_OFFSET 4 26 + #define CFM_CCM_PDU_TLV_OFFSET 74 27 + #define CFM_CCM_ITU_RESERVED_SIZE 16 28 + 29 + struct br_cfm_common_hdr { 30 + __u8 mdlevel_version; 31 + __u8 opcode; 32 + __u8 flags; 33 + __u8 tlv_offset; 34 + }; 35 + 36 + enum br_cfm_opcodes { 37 + BR_CFM_OPCODE_CCM = 0x1, 38 + }; 39 + 40 + /* MEP domain */ 41 + enum br_cfm_domain { 42 + BR_CFM_PORT, 43 + BR_CFM_VLAN, 44 + }; 45 + 46 + /* MEP direction */ 47 + enum br_cfm_mep_direction { 48 + BR_CFM_MEP_DIRECTION_DOWN, 49 + BR_CFM_MEP_DIRECTION_UP, 50 + }; 51 + 52 + /* CCM interval supported. */ 53 + enum br_cfm_ccm_interval { 54 + BR_CFM_CCM_INTERVAL_NONE, 55 + BR_CFM_CCM_INTERVAL_3_3_MS, 56 + BR_CFM_CCM_INTERVAL_10_MS, 57 + BR_CFM_CCM_INTERVAL_100_MS, 58 + BR_CFM_CCM_INTERVAL_1_SEC, 59 + BR_CFM_CCM_INTERVAL_10_SEC, 60 + BR_CFM_CCM_INTERVAL_1_MIN, 61 + BR_CFM_CCM_INTERVAL_10_MIN, 62 + }; 63 + 64 + #endif
+125
include/uapi/linux/if_bridge.h
··· 121 121 IFLA_BRIDGE_VLAN_INFO, 122 122 IFLA_BRIDGE_VLAN_TUNNEL_INFO, 123 123 IFLA_BRIDGE_MRP, 124 + IFLA_BRIDGE_CFM, 124 125 __IFLA_BRIDGE_MAX, 125 126 }; 126 127 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) ··· 328 327 __u32 period; 329 328 __u16 in_id; 330 329 }; 330 + 331 + enum { 332 + IFLA_BRIDGE_CFM_UNSPEC, 333 + IFLA_BRIDGE_CFM_MEP_CREATE, 334 + IFLA_BRIDGE_CFM_MEP_DELETE, 335 + IFLA_BRIDGE_CFM_MEP_CONFIG, 336 + IFLA_BRIDGE_CFM_CC_CONFIG, 337 + IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD, 338 + IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE, 339 + IFLA_BRIDGE_CFM_CC_RDI, 340 + IFLA_BRIDGE_CFM_CC_CCM_TX, 341 + IFLA_BRIDGE_CFM_MEP_CREATE_INFO, 342 + IFLA_BRIDGE_CFM_MEP_CONFIG_INFO, 343 + IFLA_BRIDGE_CFM_CC_CONFIG_INFO, 344 + IFLA_BRIDGE_CFM_CC_RDI_INFO, 345 + IFLA_BRIDGE_CFM_CC_CCM_TX_INFO, 346 + IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO, 347 + IFLA_BRIDGE_CFM_MEP_STATUS_INFO, 348 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO, 349 + __IFLA_BRIDGE_CFM_MAX, 350 + }; 351 + 352 + #define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1) 353 + 354 + enum { 355 + IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC, 356 + IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE, 357 + IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN, 358 + IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION, 359 + IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX, 360 + __IFLA_BRIDGE_CFM_MEP_CREATE_MAX, 361 + }; 362 + 363 + #define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1) 364 + 365 + enum { 366 + IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC, 367 + IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE, 368 + __IFLA_BRIDGE_CFM_MEP_DELETE_MAX, 369 + }; 370 + 371 + #define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1) 372 + 373 + enum { 374 + IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC, 375 + IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE, 376 + IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC, 377 + IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL, 378 + IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID, 379 + __IFLA_BRIDGE_CFM_MEP_CONFIG_MAX, 380 + }; 381 + 382 + #define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1) 383 + 384 + enum { 385 + IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC, 386 + IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE, 387 + IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE, 388 + IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL, 389 + IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID, 390 + __IFLA_BRIDGE_CFM_CC_CONFIG_MAX, 391 + }; 392 + 393 + #define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1) 394 + 395 + enum { 396 + IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC, 397 + IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE, 398 + IFLA_BRIDGE_CFM_CC_PEER_MEPID, 399 + __IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, 400 + }; 401 + 402 + #define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1) 403 + 404 + enum { 405 + IFLA_BRIDGE_CFM_CC_RDI_UNSPEC, 406 + IFLA_BRIDGE_CFM_CC_RDI_INSTANCE, 407 + IFLA_BRIDGE_CFM_CC_RDI_RDI, 408 + __IFLA_BRIDGE_CFM_CC_RDI_MAX, 409 + }; 410 + 411 + #define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1) 412 + 413 + enum { 414 + IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC, 415 + IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE, 416 + IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC, 417 + IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE, 418 + IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD, 419 + IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV, 420 + IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE, 421 + IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV, 422 + IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE, 423 + __IFLA_BRIDGE_CFM_CC_CCM_TX_MAX, 424 + }; 425 + 426 + #define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1) 427 + 428 + enum { 429 + IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC, 430 + IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE, 431 + IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN, 432 + IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN, 433 + IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN, 434 + __IFLA_BRIDGE_CFM_MEP_STATUS_MAX, 435 + }; 436 + 437 + #define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1) 438 + 439 + enum { 440 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC, 441 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE, 442 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID, 443 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT, 444 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI, 445 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE, 446 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE, 447 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN, 448 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN, 449 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN, 450 + __IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX, 451 + }; 452 + 453 + #define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1) 331 454 332 455 struct bridge_stp_xstats { 333 456 __u64 transition_blk;
+1
include/uapi/linux/if_ether.h
··· 99 99 #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ 100 100 #define ETH_P_NCSI 0x88F8 /* NCSI protocol */ 101 101 #define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ 102 + #define ETH_P_CFM 0x8902 /* Connectivity Fault Management */ 102 103 #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ 103 104 #define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */ 104 105 #define ETH_P_TDLS 0x890D /* TDLS */
+2
include/uapi/linux/rtnetlink.h
··· 779 779 #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) 780 780 #define RTEXT_FILTER_SKIP_STATS (1 << 3) 781 781 #define RTEXT_FILTER_MRP (1 << 4) 782 + #define RTEXT_FILTER_CFM_CONFIG (1 << 5) 783 + #define RTEXT_FILTER_CFM_STATUS (1 << 6) 782 784 783 785 /* End of information exported to user level */ 784 786
+11
net/bridge/Kconfig
··· 73 73 Say N to exclude this support and reduce the binary size. 74 74 75 75 If unsure, say N. 76 + 77 + config BRIDGE_CFM 78 + bool "CFM protocol" 79 + depends on BRIDGE 80 + help 81 + If you say Y here, then the Ethernet bridge will be able to run CFM 82 + protocol according to 802.1Q section 12.14 83 + 84 + Say N to exclude this support and reduce the binary size. 85 + 86 + If unsure, say N.
+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 br_cfm_netlink.o
+867
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 + /* Calculate the CCM interval in us. */ 57 + static u32 interval_to_us(enum br_cfm_ccm_interval interval) 58 + { 59 + switch (interval) { 60 + case BR_CFM_CCM_INTERVAL_NONE: 61 + return 0; 62 + case BR_CFM_CCM_INTERVAL_3_3_MS: 63 + return 3300; 64 + case BR_CFM_CCM_INTERVAL_10_MS: 65 + return 10 * 1000; 66 + case BR_CFM_CCM_INTERVAL_100_MS: 67 + return 100 * 1000; 68 + case BR_CFM_CCM_INTERVAL_1_SEC: 69 + return 1000 * 1000; 70 + case BR_CFM_CCM_INTERVAL_10_SEC: 71 + return 10 * 1000 * 1000; 72 + case BR_CFM_CCM_INTERVAL_1_MIN: 73 + return 60 * 1000 * 1000; 74 + case BR_CFM_CCM_INTERVAL_10_MIN: 75 + return 10 * 60 * 1000 * 1000; 76 + } 77 + return 0; 78 + } 79 + 80 + /* Convert the interface interval to CCM PDU value. */ 81 + static u32 interval_to_pdu(enum br_cfm_ccm_interval interval) 82 + { 83 + switch (interval) { 84 + case BR_CFM_CCM_INTERVAL_NONE: 85 + return 0; 86 + case BR_CFM_CCM_INTERVAL_3_3_MS: 87 + return 1; 88 + case BR_CFM_CCM_INTERVAL_10_MS: 89 + return 2; 90 + case BR_CFM_CCM_INTERVAL_100_MS: 91 + return 3; 92 + case BR_CFM_CCM_INTERVAL_1_SEC: 93 + return 4; 94 + case BR_CFM_CCM_INTERVAL_10_SEC: 95 + return 5; 96 + case BR_CFM_CCM_INTERVAL_1_MIN: 97 + return 6; 98 + case BR_CFM_CCM_INTERVAL_10_MIN: 99 + return 7; 100 + } 101 + return 0; 102 + } 103 + 104 + /* Convert the CCM PDU value to interval on interface. */ 105 + static u32 pdu_to_interval(u32 value) 106 + { 107 + switch (value) { 108 + case 0: 109 + return BR_CFM_CCM_INTERVAL_NONE; 110 + case 1: 111 + return BR_CFM_CCM_INTERVAL_3_3_MS; 112 + case 2: 113 + return BR_CFM_CCM_INTERVAL_10_MS; 114 + case 3: 115 + return BR_CFM_CCM_INTERVAL_100_MS; 116 + case 4: 117 + return BR_CFM_CCM_INTERVAL_1_SEC; 118 + case 5: 119 + return BR_CFM_CCM_INTERVAL_10_SEC; 120 + case 6: 121 + return BR_CFM_CCM_INTERVAL_1_MIN; 122 + case 7: 123 + return BR_CFM_CCM_INTERVAL_10_MIN; 124 + } 125 + return BR_CFM_CCM_INTERVAL_NONE; 126 + } 127 + 128 + static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep) 129 + { 130 + u32 interval_us; 131 + 132 + interval_us = interval_to_us(peer_mep->mep->cc_config.exp_interval); 133 + /* Function ccm_rx_dwork must be called with 1/4 134 + * of the configured CC 'expected_interval' 135 + * in order to detect CCM defect after 3.25 interval. 136 + */ 137 + queue_delayed_work(system_wq, &peer_mep->ccm_rx_dwork, 138 + usecs_to_jiffies(interval_us / 4)); 139 + } 140 + 141 + static void br_cfm_notify(int event, const struct net_bridge_port *port) 142 + { 143 + u32 filter = RTEXT_FILTER_CFM_STATUS; 144 + 145 + return br_info_notify(event, port->br, NULL, filter); 146 + } 147 + 148 + static void cc_peer_enable(struct br_cfm_peer_mep *peer_mep) 149 + { 150 + memset(&peer_mep->cc_status, 0, sizeof(peer_mep->cc_status)); 151 + peer_mep->ccm_rx_count_miss = 0; 152 + 153 + ccm_rx_timer_start(peer_mep); 154 + } 155 + 156 + static void cc_peer_disable(struct br_cfm_peer_mep *peer_mep) 157 + { 158 + cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork); 159 + } 160 + 161 + static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep, 162 + const struct br_cfm_cc_ccm_tx_info *const tx_info) 163 + 164 + { 165 + struct br_cfm_common_hdr *common_hdr; 166 + struct net_bridge_port *b_port; 167 + struct br_cfm_maid *maid; 168 + u8 *itu_reserved, *e_tlv; 169 + struct ethhdr *eth_hdr; 170 + struct sk_buff *skb; 171 + __be32 *status_tlv; 172 + __be32 *snumber; 173 + __be16 *mepid; 174 + 175 + skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH); 176 + if (!skb) 177 + return NULL; 178 + 179 + rcu_read_lock(); 180 + b_port = rcu_dereference(mep->b_port); 181 + if (!b_port) { 182 + kfree_skb(skb); 183 + rcu_read_unlock(); 184 + return NULL; 185 + } 186 + skb->dev = b_port->dev; 187 + rcu_read_unlock(); 188 + /* The device cannot be deleted until the work_queue functions has 189 + * completed. This function is called from ccm_tx_work_expired() 190 + * that is a work_queue functions. 191 + */ 192 + 193 + skb->protocol = htons(ETH_P_CFM); 194 + skb->priority = CFM_FRAME_PRIO; 195 + 196 + /* Ethernet header */ 197 + eth_hdr = skb_put(skb, sizeof(*eth_hdr)); 198 + ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr); 199 + ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr); 200 + eth_hdr->h_proto = htons(ETH_P_CFM); 201 + 202 + /* Common CFM Header */ 203 + common_hdr = skb_put(skb, sizeof(*common_hdr)); 204 + common_hdr->mdlevel_version = mep->config.mdlevel << 5; 205 + common_hdr->opcode = BR_CFM_OPCODE_CCM; 206 + common_hdr->flags = (mep->rdi << 7) | 207 + interval_to_pdu(mep->cc_config.exp_interval); 208 + common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET; 209 + 210 + /* Sequence number */ 211 + snumber = skb_put(skb, sizeof(*snumber)); 212 + if (tx_info->seq_no_update) { 213 + *snumber = cpu_to_be32(mep->ccm_tx_snumber); 214 + mep->ccm_tx_snumber += 1; 215 + } else { 216 + *snumber = 0; 217 + } 218 + 219 + mepid = skb_put(skb, sizeof(*mepid)); 220 + *mepid = cpu_to_be16((u16)mep->config.mepid); 221 + 222 + maid = skb_put(skb, sizeof(*maid)); 223 + memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data)); 224 + 225 + /* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */ 226 + itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE); 227 + memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE); 228 + 229 + /* Generel CFM TLV format: 230 + * TLV type: one byte 231 + * TLV value length: two bytes 232 + * TLV value: 'TLV value length' bytes 233 + */ 234 + 235 + /* Port status TLV. The value length is 1. Total of 4 bytes. */ 236 + if (tx_info->port_tlv) { 237 + status_tlv = skb_put(skb, sizeof(*status_tlv)); 238 + *status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) | 239 + (1 << 8) | /* Value length */ 240 + (tx_info->port_tlv_value & 0xFF)); 241 + } 242 + 243 + /* Interface status TLV. The value length is 1. Total of 4 bytes. */ 244 + if (tx_info->if_tlv) { 245 + status_tlv = skb_put(skb, sizeof(*status_tlv)); 246 + *status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) | 247 + (1 << 8) | /* Value length */ 248 + (tx_info->if_tlv_value & 0xFF)); 249 + } 250 + 251 + /* End TLV */ 252 + e_tlv = skb_put(skb, sizeof(*e_tlv)); 253 + *e_tlv = CFM_ENDE_TLV_TYPE; 254 + 255 + return skb; 256 + } 257 + 258 + static void ccm_frame_tx(struct sk_buff *skb) 259 + { 260 + skb_reset_network_header(skb); 261 + dev_queue_xmit(skb); 262 + } 263 + 264 + /* This function is called with the configured CC 'expected_interval' 265 + * in order to drive CCM transmission when enabled. 266 + */ 267 + static void ccm_tx_work_expired(struct work_struct *work) 268 + { 269 + struct delayed_work *del_work; 270 + struct br_cfm_mep *mep; 271 + struct sk_buff *skb; 272 + u32 interval_us; 273 + 274 + del_work = to_delayed_work(work); 275 + mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork); 276 + 277 + if (time_before_eq(mep->ccm_tx_end, jiffies)) { 278 + /* Transmission period has ended */ 279 + mep->cc_ccm_tx_info.period = 0; 280 + return; 281 + } 282 + 283 + skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info); 284 + if (skb) 285 + ccm_frame_tx(skb); 286 + 287 + interval_us = interval_to_us(mep->cc_config.exp_interval); 288 + queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 289 + usecs_to_jiffies(interval_us)); 290 + } 291 + 292 + /* This function is called with 1/4 of the configured CC 'expected_interval' 293 + * in order to detect CCM defect after 3.25 interval. 294 + */ 295 + static void ccm_rx_work_expired(struct work_struct *work) 296 + { 297 + struct br_cfm_peer_mep *peer_mep; 298 + struct net_bridge_port *b_port; 299 + struct delayed_work *del_work; 300 + 301 + del_work = to_delayed_work(work); 302 + peer_mep = container_of(del_work, struct br_cfm_peer_mep, ccm_rx_dwork); 303 + 304 + /* After 13 counts (4 * 3,25) then 3.25 intervals are expired */ 305 + if (peer_mep->ccm_rx_count_miss < 13) { 306 + /* 3.25 intervals are NOT expired without CCM reception */ 307 + peer_mep->ccm_rx_count_miss++; 308 + 309 + /* Start timer again */ 310 + ccm_rx_timer_start(peer_mep); 311 + } else { 312 + /* 3.25 intervals are expired without CCM reception. 313 + * CCM defect detected 314 + */ 315 + peer_mep->cc_status.ccm_defect = true; 316 + 317 + /* Change in CCM defect status - notify */ 318 + rcu_read_lock(); 319 + b_port = rcu_dereference(peer_mep->mep->b_port); 320 + if (b_port) 321 + br_cfm_notify(RTM_NEWLINK, b_port); 322 + rcu_read_unlock(); 323 + } 324 + } 325 + 326 + static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index, 327 + struct br_cfm_peer_mep *peer_mep) 328 + { 329 + __be32 *s_tlv; 330 + __be32 _s_tlv; 331 + u32 h_s_tlv; 332 + u8 *e_tlv; 333 + u8 _e_tlv; 334 + 335 + e_tlv = skb_header_pointer(skb, index, sizeof(_e_tlv), &_e_tlv); 336 + if (!e_tlv) 337 + return 0; 338 + 339 + /* TLV is present - get the status TLV */ 340 + s_tlv = skb_header_pointer(skb, 341 + index, 342 + sizeof(_s_tlv), &_s_tlv); 343 + if (!s_tlv) 344 + return 0; 345 + 346 + h_s_tlv = ntohl(*s_tlv); 347 + if ((h_s_tlv >> 24) == CFM_IF_STATUS_TLV_TYPE) { 348 + /* Interface status TLV */ 349 + peer_mep->cc_status.tlv_seen = true; 350 + peer_mep->cc_status.if_tlv_value = (h_s_tlv & 0xFF); 351 + } 352 + 353 + if ((h_s_tlv >> 24) == CFM_PORT_STATUS_TLV_TYPE) { 354 + /* Port status TLV */ 355 + peer_mep->cc_status.tlv_seen = true; 356 + peer_mep->cc_status.port_tlv_value = (h_s_tlv & 0xFF); 357 + } 358 + 359 + /* The Sender ID TLV is not handled */ 360 + /* The Organization-Specific TLV is not handled */ 361 + 362 + /* Return the length of this tlv. 363 + * This is the length of the value field plus 3 bytes for size of type 364 + * field and length field 365 + */ 366 + return ((h_s_tlv >> 8) & 0xFFFF) + 3; 367 + } 368 + 369 + /* note: already called with rcu_read_lock */ 370 + static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) 371 + { 372 + u32 mdlevel, interval, size, index, max; 373 + const struct br_cfm_common_hdr *hdr; 374 + struct br_cfm_peer_mep *peer_mep; 375 + const struct br_cfm_maid *maid; 376 + struct br_cfm_common_hdr _hdr; 377 + struct br_cfm_maid _maid; 378 + struct br_cfm_mep *mep; 379 + struct net_bridge *br; 380 + __be32 *snumber; 381 + __be32 _snumber; 382 + __be16 *mepid; 383 + __be16 _mepid; 384 + 385 + if (port->state == BR_STATE_DISABLED) 386 + return 0; 387 + 388 + hdr = skb_header_pointer(skb, 0, sizeof(_hdr), &_hdr); 389 + if (!hdr) 390 + return 1; 391 + 392 + br = port->br; 393 + mep = br_mep_find_ifindex(br, port->dev->ifindex); 394 + if (unlikely(!mep)) 395 + /* No MEP on this port - must be forwarded */ 396 + return 0; 397 + 398 + mdlevel = hdr->mdlevel_version >> 5; 399 + if (mdlevel > mep->config.mdlevel) 400 + /* The level is above this MEP level - must be forwarded */ 401 + return 0; 402 + 403 + if ((hdr->mdlevel_version & 0x1F) != 0) { 404 + /* Invalid version */ 405 + mep->status.version_unexp_seen = true; 406 + return 1; 407 + } 408 + 409 + if (mdlevel < mep->config.mdlevel) { 410 + /* The level is below this MEP level */ 411 + mep->status.rx_level_low_seen = true; 412 + return 1; 413 + } 414 + 415 + if (hdr->opcode == BR_CFM_OPCODE_CCM) { 416 + /* CCM PDU received. */ 417 + /* MA ID is after common header + sequence number + MEP ID */ 418 + maid = skb_header_pointer(skb, 419 + CFM_CCM_PDU_MAID_OFFSET, 420 + sizeof(_maid), &_maid); 421 + if (!maid) 422 + return 1; 423 + if (memcmp(maid->data, mep->cc_config.exp_maid.data, 424 + sizeof(maid->data))) 425 + /* MA ID not as expected */ 426 + return 1; 427 + 428 + /* MEP ID is after common header + sequence number */ 429 + mepid = skb_header_pointer(skb, 430 + CFM_CCM_PDU_MEPID_OFFSET, 431 + sizeof(_mepid), &_mepid); 432 + if (!mepid) 433 + return 1; 434 + peer_mep = br_peer_mep_find(mep, (u32)ntohs(*mepid)); 435 + if (!peer_mep) 436 + return 1; 437 + 438 + /* Interval is in common header flags */ 439 + interval = hdr->flags & 0x07; 440 + if (mep->cc_config.exp_interval != pdu_to_interval(interval)) 441 + /* Interval not as expected */ 442 + return 1; 443 + 444 + /* A valid CCM frame is received */ 445 + if (peer_mep->cc_status.ccm_defect) { 446 + peer_mep->cc_status.ccm_defect = false; 447 + 448 + /* Change in CCM defect status - notify */ 449 + br_cfm_notify(RTM_NEWLINK, port); 450 + 451 + /* Start CCM RX timer */ 452 + ccm_rx_timer_start(peer_mep); 453 + } 454 + 455 + peer_mep->cc_status.seen = true; 456 + peer_mep->ccm_rx_count_miss = 0; 457 + 458 + /* RDI is in common header flags */ 459 + peer_mep->cc_status.rdi = (hdr->flags & 0x80) ? true : false; 460 + 461 + /* Sequence number is after common header */ 462 + snumber = skb_header_pointer(skb, 463 + CFM_CCM_PDU_SEQNR_OFFSET, 464 + sizeof(_snumber), &_snumber); 465 + if (!snumber) 466 + return 1; 467 + if (ntohl(*snumber) != (mep->ccm_rx_snumber + 1)) 468 + /* Unexpected sequence number */ 469 + peer_mep->cc_status.seq_unexp_seen = true; 470 + 471 + mep->ccm_rx_snumber = ntohl(*snumber); 472 + 473 + /* TLV end is after common header + sequence number + MEP ID + 474 + * MA ID + ITU reserved 475 + */ 476 + index = CFM_CCM_PDU_TLV_OFFSET; 477 + max = 0; 478 + do { /* Handle all TLVs */ 479 + size = ccm_tlv_extract(skb, index, peer_mep); 480 + index += size; 481 + max += 1; 482 + } while (size != 0 && max < 4); /* Max four TLVs possible */ 483 + 484 + return 1; 485 + } 486 + 487 + mep->status.opcode_unexp_seen = true; 488 + 489 + return 1; 490 + } 491 + 492 + static struct br_frame_type cfm_frame_type __read_mostly = { 493 + .type = cpu_to_be16(ETH_P_CFM), 494 + .frame_handler = br_cfm_frame_rx, 495 + }; 496 + 497 + int br_cfm_mep_create(struct net_bridge *br, 498 + const u32 instance, 499 + struct br_cfm_mep_create *const create, 500 + struct netlink_ext_ack *extack) 501 + { 502 + struct net_bridge_port *p; 503 + struct br_cfm_mep *mep; 504 + 505 + ASSERT_RTNL(); 506 + 507 + if (create->domain == BR_CFM_VLAN) { 508 + NL_SET_ERR_MSG_MOD(extack, 509 + "VLAN domain not supported"); 510 + return -EINVAL; 511 + } 512 + if (create->domain != BR_CFM_PORT) { 513 + NL_SET_ERR_MSG_MOD(extack, 514 + "Invalid domain value"); 515 + return -EINVAL; 516 + } 517 + if (create->direction == BR_CFM_MEP_DIRECTION_UP) { 518 + NL_SET_ERR_MSG_MOD(extack, 519 + "Up-MEP not supported"); 520 + return -EINVAL; 521 + } 522 + if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) { 523 + NL_SET_ERR_MSG_MOD(extack, 524 + "Invalid direction value"); 525 + return -EINVAL; 526 + } 527 + p = br_mep_get_port(br, create->ifindex); 528 + if (!p) { 529 + NL_SET_ERR_MSG_MOD(extack, 530 + "Port is not related to bridge"); 531 + return -EINVAL; 532 + } 533 + mep = br_mep_find(br, instance); 534 + if (mep) { 535 + NL_SET_ERR_MSG_MOD(extack, 536 + "MEP instance already exists"); 537 + return -EEXIST; 538 + } 539 + 540 + /* In PORT domain only one instance can be created per port */ 541 + if (create->domain == BR_CFM_PORT) { 542 + mep = br_mep_find_ifindex(br, create->ifindex); 543 + if (mep) { 544 + NL_SET_ERR_MSG_MOD(extack, 545 + "Only one Port MEP on a port allowed"); 546 + return -EINVAL; 547 + } 548 + } 549 + 550 + mep = kzalloc(sizeof(*mep), GFP_KERNEL); 551 + if (!mep) 552 + return -ENOMEM; 553 + 554 + mep->create = *create; 555 + mep->instance = instance; 556 + rcu_assign_pointer(mep->b_port, p); 557 + 558 + INIT_HLIST_HEAD(&mep->peer_mep_list); 559 + INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired); 560 + 561 + if (hlist_empty(&br->mep_list)) 562 + br_add_frame(br, &cfm_frame_type); 563 + 564 + hlist_add_tail_rcu(&mep->head, &br->mep_list); 565 + 566 + return 0; 567 + } 568 + 569 + static void mep_delete_implementation(struct net_bridge *br, 570 + struct br_cfm_mep *mep) 571 + { 572 + struct br_cfm_peer_mep *peer_mep; 573 + struct hlist_node *n_store; 574 + 575 + ASSERT_RTNL(); 576 + 577 + /* Empty and free peer MEP list */ 578 + hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) { 579 + cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork); 580 + hlist_del_rcu(&peer_mep->head); 581 + kfree_rcu(peer_mep, rcu); 582 + } 583 + 584 + cancel_delayed_work_sync(&mep->ccm_tx_dwork); 585 + 586 + RCU_INIT_POINTER(mep->b_port, NULL); 587 + hlist_del_rcu(&mep->head); 588 + kfree_rcu(mep, rcu); 589 + 590 + if (hlist_empty(&br->mep_list)) 591 + br_del_frame(br, &cfm_frame_type); 592 + } 593 + 594 + int br_cfm_mep_delete(struct net_bridge *br, 595 + const u32 instance, 596 + struct netlink_ext_ack *extack) 597 + { 598 + struct br_cfm_mep *mep; 599 + 600 + ASSERT_RTNL(); 601 + 602 + mep = br_mep_find(br, instance); 603 + if (!mep) { 604 + NL_SET_ERR_MSG_MOD(extack, 605 + "MEP instance does not exists"); 606 + return -ENOENT; 607 + } 608 + 609 + mep_delete_implementation(br, mep); 610 + 611 + return 0; 612 + } 613 + 614 + int br_cfm_mep_config_set(struct net_bridge *br, 615 + const u32 instance, 616 + const struct br_cfm_mep_config *const config, 617 + struct netlink_ext_ack *extack) 618 + { 619 + struct br_cfm_mep *mep; 620 + 621 + ASSERT_RTNL(); 622 + 623 + mep = br_mep_find(br, instance); 624 + if (!mep) { 625 + NL_SET_ERR_MSG_MOD(extack, 626 + "MEP instance does not exists"); 627 + return -ENOENT; 628 + } 629 + 630 + mep->config = *config; 631 + 632 + return 0; 633 + } 634 + 635 + int br_cfm_cc_config_set(struct net_bridge *br, 636 + const u32 instance, 637 + const struct br_cfm_cc_config *const config, 638 + struct netlink_ext_ack *extack) 639 + { 640 + struct br_cfm_peer_mep *peer_mep; 641 + struct br_cfm_mep *mep; 642 + 643 + ASSERT_RTNL(); 644 + 645 + mep = br_mep_find(br, instance); 646 + if (!mep) { 647 + NL_SET_ERR_MSG_MOD(extack, 648 + "MEP instance does not exists"); 649 + return -ENOENT; 650 + } 651 + 652 + /* Check for no change in configuration */ 653 + if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0) 654 + return 0; 655 + 656 + if (config->enable && !mep->cc_config.enable) 657 + /* CC is enabled */ 658 + hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head) 659 + cc_peer_enable(peer_mep); 660 + 661 + if (!config->enable && mep->cc_config.enable) 662 + /* CC is disabled */ 663 + hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head) 664 + cc_peer_disable(peer_mep); 665 + 666 + mep->cc_config = *config; 667 + mep->ccm_rx_snumber = 0; 668 + mep->ccm_tx_snumber = 1; 669 + 670 + return 0; 671 + } 672 + 673 + int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance, 674 + u32 mepid, 675 + struct netlink_ext_ack *extack) 676 + { 677 + struct br_cfm_peer_mep *peer_mep; 678 + struct br_cfm_mep *mep; 679 + 680 + ASSERT_RTNL(); 681 + 682 + mep = br_mep_find(br, instance); 683 + if (!mep) { 684 + NL_SET_ERR_MSG_MOD(extack, 685 + "MEP instance does not exists"); 686 + return -ENOENT; 687 + } 688 + 689 + peer_mep = br_peer_mep_find(mep, mepid); 690 + if (peer_mep) { 691 + NL_SET_ERR_MSG_MOD(extack, 692 + "Peer MEP-ID already exists"); 693 + return -EEXIST; 694 + } 695 + 696 + peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL); 697 + if (!peer_mep) 698 + return -ENOMEM; 699 + 700 + peer_mep->mepid = mepid; 701 + peer_mep->mep = mep; 702 + INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired); 703 + 704 + if (mep->cc_config.enable) 705 + cc_peer_enable(peer_mep); 706 + 707 + hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list); 708 + 709 + return 0; 710 + } 711 + 712 + int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance, 713 + u32 mepid, 714 + struct netlink_ext_ack *extack) 715 + { 716 + struct br_cfm_peer_mep *peer_mep; 717 + struct br_cfm_mep *mep; 718 + 719 + ASSERT_RTNL(); 720 + 721 + mep = br_mep_find(br, instance); 722 + if (!mep) { 723 + NL_SET_ERR_MSG_MOD(extack, 724 + "MEP instance does not exists"); 725 + return -ENOENT; 726 + } 727 + 728 + peer_mep = br_peer_mep_find(mep, mepid); 729 + if (!peer_mep) { 730 + NL_SET_ERR_MSG_MOD(extack, 731 + "Peer MEP-ID does not exists"); 732 + return -ENOENT; 733 + } 734 + 735 + cc_peer_disable(peer_mep); 736 + 737 + hlist_del_rcu(&peer_mep->head); 738 + kfree_rcu(peer_mep, rcu); 739 + 740 + return 0; 741 + } 742 + 743 + int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance, 744 + const bool rdi, struct netlink_ext_ack *extack) 745 + { 746 + struct br_cfm_mep *mep; 747 + 748 + ASSERT_RTNL(); 749 + 750 + mep = br_mep_find(br, instance); 751 + if (!mep) { 752 + NL_SET_ERR_MSG_MOD(extack, 753 + "MEP instance does not exists"); 754 + return -ENOENT; 755 + } 756 + 757 + mep->rdi = rdi; 758 + 759 + return 0; 760 + } 761 + 762 + int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance, 763 + const struct br_cfm_cc_ccm_tx_info *const tx_info, 764 + struct netlink_ext_ack *extack) 765 + { 766 + struct br_cfm_mep *mep; 767 + 768 + ASSERT_RTNL(); 769 + 770 + mep = br_mep_find(br, instance); 771 + if (!mep) { 772 + NL_SET_ERR_MSG_MOD(extack, 773 + "MEP instance does not exists"); 774 + return -ENOENT; 775 + } 776 + 777 + if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) { 778 + /* No change in tx_info. */ 779 + if (mep->cc_ccm_tx_info.period == 0) 780 + /* Transmission is not enabled - just return */ 781 + return 0; 782 + 783 + /* Transmission is ongoing, the end time is recalculated */ 784 + mep->ccm_tx_end = jiffies + 785 + usecs_to_jiffies(tx_info->period * 1000000); 786 + return 0; 787 + } 788 + 789 + if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0) 790 + /* Some change in info and transmission is not ongoing */ 791 + goto save; 792 + 793 + if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) { 794 + /* Some change in info and transmission is ongoing 795 + * The end time is recalculated 796 + */ 797 + mep->ccm_tx_end = jiffies + 798 + usecs_to_jiffies(tx_info->period * 1000000); 799 + 800 + goto save; 801 + } 802 + 803 + if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) { 804 + cancel_delayed_work_sync(&mep->ccm_tx_dwork); 805 + goto save; 806 + } 807 + 808 + /* Start delayed work to transmit CCM frames. It is done with zero delay 809 + * to send first frame immediately 810 + */ 811 + mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000); 812 + queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0); 813 + 814 + save: 815 + mep->cc_ccm_tx_info = *tx_info; 816 + 817 + return 0; 818 + } 819 + 820 + int br_cfm_mep_count(struct net_bridge *br, u32 *count) 821 + { 822 + struct br_cfm_mep *mep; 823 + 824 + *count = 0; 825 + 826 + rcu_read_lock(); 827 + hlist_for_each_entry_rcu(mep, &br->mep_list, head) 828 + *count += 1; 829 + rcu_read_unlock(); 830 + 831 + return 0; 832 + } 833 + 834 + int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count) 835 + { 836 + struct br_cfm_peer_mep *peer_mep; 837 + struct br_cfm_mep *mep; 838 + 839 + *count = 0; 840 + 841 + rcu_read_lock(); 842 + hlist_for_each_entry_rcu(mep, &br->mep_list, head) 843 + hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) 844 + *count += 1; 845 + rcu_read_unlock(); 846 + 847 + return 0; 848 + } 849 + 850 + bool br_cfm_created(struct net_bridge *br) 851 + { 852 + return !hlist_empty(&br->mep_list); 853 + } 854 + 855 + /* Deletes the CFM instances on a specific bridge port 856 + */ 857 + void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port) 858 + { 859 + struct hlist_node *n_store; 860 + struct br_cfm_mep *mep; 861 + 862 + ASSERT_RTNL(); 863 + 864 + hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head) 865 + if (mep->create.ifindex == port->dev->ifindex) 866 + mep_delete_implementation(br, mep); 867 + }
+726
net/bridge/br_cfm_netlink.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + #include <net/genetlink.h> 4 + 5 + #include "br_private.h" 6 + #include "br_private_cfm.h" 7 + 8 + static const struct nla_policy 9 + br_cfm_mep_create_policy[IFLA_BRIDGE_CFM_MEP_CREATE_MAX + 1] = { 10 + [IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC] = { .type = NLA_REJECT }, 11 + [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE] = { .type = NLA_U32 }, 12 + [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN] = { .type = NLA_U32 }, 13 + [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION] = { .type = NLA_U32 }, 14 + [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX] = { .type = NLA_U32 }, 15 + }; 16 + 17 + static const struct nla_policy 18 + br_cfm_mep_delete_policy[IFLA_BRIDGE_CFM_MEP_DELETE_MAX + 1] = { 19 + [IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC] = { .type = NLA_REJECT }, 20 + [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE] = { .type = NLA_U32 }, 21 + }; 22 + 23 + static const struct nla_policy 24 + br_cfm_mep_config_policy[IFLA_BRIDGE_CFM_MEP_CONFIG_MAX + 1] = { 25 + [IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC] = { .type = NLA_REJECT }, 26 + [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE] = { .type = NLA_U32 }, 27 + [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = NLA_POLICY_ETH_ADDR, 28 + [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL] = NLA_POLICY_MAX(NLA_U32, 7), 29 + [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID] = NLA_POLICY_MAX(NLA_U32, 0x1FFF), 30 + }; 31 + 32 + static const struct nla_policy 33 + br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = { 34 + [IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT }, 35 + [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 }, 36 + [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 }, 37 + [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 }, 38 + [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = { 39 + .type = NLA_BINARY, .len = CFM_MAID_LENGTH }, 40 + }; 41 + 42 + static const struct nla_policy 43 + br_cfm_cc_peer_mep_policy[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1] = { 44 + [IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC] = { .type = NLA_REJECT }, 45 + [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE] = { .type = NLA_U32 }, 46 + [IFLA_BRIDGE_CFM_CC_PEER_MEPID] = NLA_POLICY_MAX(NLA_U32, 0x1FFF), 47 + }; 48 + 49 + static const struct nla_policy 50 + br_cfm_cc_rdi_policy[IFLA_BRIDGE_CFM_CC_RDI_MAX + 1] = { 51 + [IFLA_BRIDGE_CFM_CC_RDI_UNSPEC] = { .type = NLA_REJECT }, 52 + [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE] = { .type = NLA_U32 }, 53 + [IFLA_BRIDGE_CFM_CC_RDI_RDI] = { .type = NLA_U32 }, 54 + }; 55 + 56 + static const struct nla_policy 57 + br_cfm_cc_ccm_tx_policy[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1] = { 58 + [IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC] = { .type = NLA_REJECT }, 59 + [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE] = { .type = NLA_U32 }, 60 + [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC] = NLA_POLICY_ETH_ADDR, 61 + [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE] = { .type = NLA_U32 }, 62 + [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD] = { .type = NLA_U32 }, 63 + [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV] = { .type = NLA_U32 }, 64 + [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE] = { .type = NLA_U8 }, 65 + [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV] = { .type = NLA_U32 }, 66 + [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = { .type = NLA_U8 }, 67 + }; 68 + 69 + static const struct nla_policy 70 + br_cfm_policy[IFLA_BRIDGE_CFM_MAX + 1] = { 71 + [IFLA_BRIDGE_CFM_UNSPEC] = { .type = NLA_REJECT }, 72 + [IFLA_BRIDGE_CFM_MEP_CREATE] = 73 + NLA_POLICY_NESTED(br_cfm_mep_create_policy), 74 + [IFLA_BRIDGE_CFM_MEP_DELETE] = 75 + NLA_POLICY_NESTED(br_cfm_mep_delete_policy), 76 + [IFLA_BRIDGE_CFM_MEP_CONFIG] = 77 + NLA_POLICY_NESTED(br_cfm_mep_config_policy), 78 + [IFLA_BRIDGE_CFM_CC_CONFIG] = 79 + NLA_POLICY_NESTED(br_cfm_cc_config_policy), 80 + [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD] = 81 + NLA_POLICY_NESTED(br_cfm_cc_peer_mep_policy), 82 + [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE] = 83 + NLA_POLICY_NESTED(br_cfm_cc_peer_mep_policy), 84 + [IFLA_BRIDGE_CFM_CC_RDI] = 85 + NLA_POLICY_NESTED(br_cfm_cc_rdi_policy), 86 + [IFLA_BRIDGE_CFM_CC_CCM_TX] = 87 + NLA_POLICY_NESTED(br_cfm_cc_ccm_tx_policy), 88 + }; 89 + 90 + static int br_mep_create_parse(struct net_bridge *br, struct nlattr *attr, 91 + struct netlink_ext_ack *extack) 92 + { 93 + struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_CREATE_MAX + 1]; 94 + struct br_cfm_mep_create create; 95 + u32 instance; 96 + int err; 97 + 98 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_CREATE_MAX, attr, 99 + br_cfm_mep_create_policy, extack); 100 + if (err) 101 + return err; 102 + 103 + if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]) { 104 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 105 + return -EINVAL; 106 + } 107 + if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]) { 108 + NL_SET_ERR_MSG_MOD(extack, "Missing DOMAIN attribute"); 109 + return -EINVAL; 110 + } 111 + if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]) { 112 + NL_SET_ERR_MSG_MOD(extack, "Missing DIRECTION attribute"); 113 + return -EINVAL; 114 + } 115 + if (!tb[IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]) { 116 + NL_SET_ERR_MSG_MOD(extack, "Missing IFINDEX attribute"); 117 + return -EINVAL; 118 + } 119 + 120 + memset(&create, 0, sizeof(create)); 121 + 122 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]); 123 + create.domain = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]); 124 + create.direction = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]); 125 + create.ifindex = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]); 126 + 127 + return br_cfm_mep_create(br, instance, &create, extack); 128 + } 129 + 130 + static int br_mep_delete_parse(struct net_bridge *br, struct nlattr *attr, 131 + struct netlink_ext_ack *extack) 132 + { 133 + struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_DELETE_MAX + 1]; 134 + u32 instance; 135 + int err; 136 + 137 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_DELETE_MAX, attr, 138 + br_cfm_mep_delete_policy, extack); 139 + if (err) 140 + return err; 141 + 142 + if (!tb[IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]) { 143 + NL_SET_ERR_MSG_MOD(extack, 144 + "Missing INSTANCE attribute"); 145 + return -EINVAL; 146 + } 147 + 148 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]); 149 + 150 + return br_cfm_mep_delete(br, instance, extack); 151 + } 152 + 153 + static int br_mep_config_parse(struct net_bridge *br, struct nlattr *attr, 154 + struct netlink_ext_ack *extack) 155 + { 156 + struct nlattr *tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MAX + 1]; 157 + struct br_cfm_mep_config config; 158 + u32 instance; 159 + int err; 160 + 161 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MEP_CONFIG_MAX, attr, 162 + br_cfm_mep_config_policy, extack); 163 + if (err) 164 + return err; 165 + 166 + if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]) { 167 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 168 + return -EINVAL; 169 + } 170 + if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC]) { 171 + NL_SET_ERR_MSG_MOD(extack, "Missing UNICAST_MAC attribute"); 172 + return -EINVAL; 173 + } 174 + if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]) { 175 + NL_SET_ERR_MSG_MOD(extack, "Missing MDLEVEL attribute"); 176 + return -EINVAL; 177 + } 178 + if (!tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]) { 179 + NL_SET_ERR_MSG_MOD(extack, "Missing MEPID attribute"); 180 + return -EINVAL; 181 + } 182 + 183 + memset(&config, 0, sizeof(config)); 184 + 185 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]); 186 + nla_memcpy(&config.unicast_mac.addr, 187 + tb[IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC], 188 + sizeof(config.unicast_mac.addr)); 189 + config.mdlevel = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]); 190 + config.mepid = nla_get_u32(tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]); 191 + 192 + return br_cfm_mep_config_set(br, instance, &config, extack); 193 + } 194 + 195 + static int br_cc_config_parse(struct net_bridge *br, struct nlattr *attr, 196 + struct netlink_ext_ack *extack) 197 + { 198 + struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1]; 199 + struct br_cfm_cc_config config; 200 + u32 instance; 201 + int err; 202 + 203 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_CONFIG_MAX, attr, 204 + br_cfm_cc_config_policy, extack); 205 + if (err) 206 + return err; 207 + 208 + if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]) { 209 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 210 + return -EINVAL; 211 + } 212 + if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]) { 213 + NL_SET_ERR_MSG_MOD(extack, "Missing ENABLE attribute"); 214 + return -EINVAL; 215 + } 216 + if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL]) { 217 + NL_SET_ERR_MSG_MOD(extack, "Missing INTERVAL attribute"); 218 + return -EINVAL; 219 + } 220 + if (!tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID]) { 221 + NL_SET_ERR_MSG_MOD(extack, "Missing MAID attribute"); 222 + return -EINVAL; 223 + } 224 + 225 + memset(&config, 0, sizeof(config)); 226 + 227 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]); 228 + config.enable = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]); 229 + config.exp_interval = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL]); 230 + nla_memcpy(&config.exp_maid.data, tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID], 231 + sizeof(config.exp_maid.data)); 232 + 233 + return br_cfm_cc_config_set(br, instance, &config, extack); 234 + } 235 + 236 + static int br_cc_peer_mep_add_parse(struct net_bridge *br, struct nlattr *attr, 237 + struct netlink_ext_ack *extack) 238 + { 239 + struct nlattr *tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1]; 240 + u32 instance, peer_mep_id; 241 + int err; 242 + 243 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, attr, 244 + br_cfm_cc_peer_mep_policy, extack); 245 + if (err) 246 + return err; 247 + 248 + if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]) { 249 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 250 + return -EINVAL; 251 + } 252 + if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]) { 253 + NL_SET_ERR_MSG_MOD(extack, "Missing PEER_MEP_ID attribute"); 254 + return -EINVAL; 255 + } 256 + 257 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]); 258 + peer_mep_id = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]); 259 + 260 + return br_cfm_cc_peer_mep_add(br, instance, peer_mep_id, extack); 261 + } 262 + 263 + static int br_cc_peer_mep_remove_parse(struct net_bridge *br, struct nlattr *attr, 264 + struct netlink_ext_ack *extack) 265 + { 266 + struct nlattr *tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX + 1]; 267 + u32 instance, peer_mep_id; 268 + int err; 269 + 270 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, attr, 271 + br_cfm_cc_peer_mep_policy, extack); 272 + if (err) 273 + return err; 274 + 275 + if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]) { 276 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 277 + return -EINVAL; 278 + } 279 + if (!tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]) { 280 + NL_SET_ERR_MSG_MOD(extack, "Missing PEER_MEP_ID attribute"); 281 + return -EINVAL; 282 + } 283 + 284 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]); 285 + peer_mep_id = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_PEER_MEPID]); 286 + 287 + return br_cfm_cc_peer_mep_remove(br, instance, peer_mep_id, extack); 288 + } 289 + 290 + static int br_cc_rdi_parse(struct net_bridge *br, struct nlattr *attr, 291 + struct netlink_ext_ack *extack) 292 + { 293 + struct nlattr *tb[IFLA_BRIDGE_CFM_CC_RDI_MAX + 1]; 294 + u32 instance, rdi; 295 + int err; 296 + 297 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_RDI_MAX, attr, 298 + br_cfm_cc_rdi_policy, extack); 299 + if (err) 300 + return err; 301 + 302 + if (!tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]) { 303 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 304 + return -EINVAL; 305 + } 306 + if (!tb[IFLA_BRIDGE_CFM_CC_RDI_RDI]) { 307 + NL_SET_ERR_MSG_MOD(extack, "Missing RDI attribute"); 308 + return -EINVAL; 309 + } 310 + 311 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]); 312 + rdi = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_RDI]); 313 + 314 + return br_cfm_cc_rdi_set(br, instance, rdi, extack); 315 + } 316 + 317 + static int br_cc_ccm_tx_parse(struct net_bridge *br, struct nlattr *attr, 318 + struct netlink_ext_ack *extack) 319 + { 320 + struct nlattr *tb[IFLA_BRIDGE_CFM_CC_CCM_TX_MAX + 1]; 321 + struct br_cfm_cc_ccm_tx_info tx_info; 322 + u32 instance; 323 + int err; 324 + 325 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_CC_CCM_TX_MAX, attr, 326 + br_cfm_cc_ccm_tx_policy, extack); 327 + if (err) 328 + return err; 329 + 330 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE]) { 331 + NL_SET_ERR_MSG_MOD(extack, "Missing INSTANCE attribute"); 332 + return -EINVAL; 333 + } 334 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC]) { 335 + NL_SET_ERR_MSG_MOD(extack, "Missing DMAC attribute"); 336 + return -EINVAL; 337 + } 338 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]) { 339 + NL_SET_ERR_MSG_MOD(extack, "Missing SEQ_NO_UPDATE attribute"); 340 + return -EINVAL; 341 + } 342 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]) { 343 + NL_SET_ERR_MSG_MOD(extack, "Missing PERIOD attribute"); 344 + return -EINVAL; 345 + } 346 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]) { 347 + NL_SET_ERR_MSG_MOD(extack, "Missing IF_TLV attribute"); 348 + return -EINVAL; 349 + } 350 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]) { 351 + NL_SET_ERR_MSG_MOD(extack, "Missing IF_TLV_VALUE attribute"); 352 + return -EINVAL; 353 + } 354 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]) { 355 + NL_SET_ERR_MSG_MOD(extack, "Missing PORT_TLV attribute"); 356 + return -EINVAL; 357 + } 358 + if (!tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE]) { 359 + NL_SET_ERR_MSG_MOD(extack, "Missing PORT_TLV_VALUE attribute"); 360 + return -EINVAL; 361 + } 362 + 363 + memset(&tx_info, 0, sizeof(tx_info)); 364 + 365 + instance = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]); 366 + nla_memcpy(&tx_info.dmac.addr, 367 + tb[IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC], 368 + sizeof(tx_info.dmac.addr)); 369 + tx_info.seq_no_update = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]); 370 + tx_info.period = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]); 371 + tx_info.if_tlv = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]); 372 + tx_info.if_tlv_value = nla_get_u8(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]); 373 + tx_info.port_tlv = nla_get_u32(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]); 374 + tx_info.port_tlv_value = nla_get_u8(tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE]); 375 + 376 + return br_cfm_cc_ccm_tx(br, instance, &tx_info, extack); 377 + } 378 + 379 + int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p, 380 + struct nlattr *attr, int cmd, struct netlink_ext_ack *extack) 381 + { 382 + struct nlattr *tb[IFLA_BRIDGE_CFM_MAX + 1]; 383 + int err; 384 + 385 + /* When this function is called for a port then the br pointer is 386 + * invalid, therefor set the br to point correctly 387 + */ 388 + if (p) 389 + br = p->br; 390 + 391 + err = nla_parse_nested(tb, IFLA_BRIDGE_CFM_MAX, attr, 392 + br_cfm_policy, extack); 393 + if (err) 394 + return err; 395 + 396 + if (tb[IFLA_BRIDGE_CFM_MEP_CREATE]) { 397 + err = br_mep_create_parse(br, tb[IFLA_BRIDGE_CFM_MEP_CREATE], 398 + extack); 399 + if (err) 400 + return err; 401 + } 402 + 403 + if (tb[IFLA_BRIDGE_CFM_MEP_DELETE]) { 404 + err = br_mep_delete_parse(br, tb[IFLA_BRIDGE_CFM_MEP_DELETE], 405 + extack); 406 + if (err) 407 + return err; 408 + } 409 + 410 + if (tb[IFLA_BRIDGE_CFM_MEP_CONFIG]) { 411 + err = br_mep_config_parse(br, tb[IFLA_BRIDGE_CFM_MEP_CONFIG], 412 + extack); 413 + if (err) 414 + return err; 415 + } 416 + 417 + if (tb[IFLA_BRIDGE_CFM_CC_CONFIG]) { 418 + err = br_cc_config_parse(br, tb[IFLA_BRIDGE_CFM_CC_CONFIG], 419 + extack); 420 + if (err) 421 + return err; 422 + } 423 + 424 + if (tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD]) { 425 + err = br_cc_peer_mep_add_parse(br, tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD], 426 + extack); 427 + if (err) 428 + return err; 429 + } 430 + 431 + if (tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE]) { 432 + err = br_cc_peer_mep_remove_parse(br, tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE], 433 + extack); 434 + if (err) 435 + return err; 436 + } 437 + 438 + if (tb[IFLA_BRIDGE_CFM_CC_RDI]) { 439 + err = br_cc_rdi_parse(br, tb[IFLA_BRIDGE_CFM_CC_RDI], 440 + extack); 441 + if (err) 442 + return err; 443 + } 444 + 445 + if (tb[IFLA_BRIDGE_CFM_CC_CCM_TX]) { 446 + err = br_cc_ccm_tx_parse(br, tb[IFLA_BRIDGE_CFM_CC_CCM_TX], 447 + extack); 448 + if (err) 449 + return err; 450 + } 451 + 452 + return 0; 453 + } 454 + 455 + int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br) 456 + { 457 + struct br_cfm_peer_mep *peer_mep; 458 + struct br_cfm_mep *mep; 459 + struct nlattr *tb; 460 + 461 + hlist_for_each_entry_rcu(mep, &br->mep_list, head) { 462 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_CREATE_INFO); 463 + if (!tb) 464 + goto nla_info_failure; 465 + 466 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE, 467 + mep->instance)) 468 + goto nla_put_failure; 469 + 470 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN, 471 + mep->create.domain)) 472 + goto nla_put_failure; 473 + 474 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION, 475 + mep->create.direction)) 476 + goto nla_put_failure; 477 + 478 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX, 479 + mep->create.ifindex)) 480 + goto nla_put_failure; 481 + 482 + nla_nest_end(skb, tb); 483 + 484 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_INFO); 485 + 486 + if (!tb) 487 + goto nla_info_failure; 488 + 489 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE, 490 + mep->instance)) 491 + goto nla_put_failure; 492 + 493 + if (nla_put(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC, 494 + sizeof(mep->config.unicast_mac.addr), 495 + mep->config.unicast_mac.addr)) 496 + goto nla_put_failure; 497 + 498 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL, 499 + mep->config.mdlevel)) 500 + goto nla_put_failure; 501 + 502 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID, 503 + mep->config.mepid)) 504 + goto nla_put_failure; 505 + 506 + nla_nest_end(skb, tb); 507 + 508 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_CONFIG_INFO); 509 + 510 + if (!tb) 511 + goto nla_info_failure; 512 + 513 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE, 514 + mep->instance)) 515 + goto nla_put_failure; 516 + 517 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE, 518 + mep->cc_config.enable)) 519 + goto nla_put_failure; 520 + 521 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL, 522 + mep->cc_config.exp_interval)) 523 + goto nla_put_failure; 524 + 525 + if (nla_put(skb, IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID, 526 + sizeof(mep->cc_config.exp_maid.data), 527 + mep->cc_config.exp_maid.data)) 528 + goto nla_put_failure; 529 + 530 + nla_nest_end(skb, tb); 531 + 532 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_RDI_INFO); 533 + 534 + if (!tb) 535 + goto nla_info_failure; 536 + 537 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_RDI_INSTANCE, 538 + mep->instance)) 539 + goto nla_put_failure; 540 + 541 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_RDI_RDI, 542 + mep->rdi)) 543 + goto nla_put_failure; 544 + 545 + nla_nest_end(skb, tb); 546 + 547 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_INFO); 548 + 549 + if (!tb) 550 + goto nla_info_failure; 551 + 552 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE, 553 + mep->instance)) 554 + goto nla_put_failure; 555 + 556 + if (nla_put(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC, 557 + sizeof(mep->cc_ccm_tx_info.dmac), 558 + mep->cc_ccm_tx_info.dmac.addr)) 559 + goto nla_put_failure; 560 + 561 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE, 562 + mep->cc_ccm_tx_info.seq_no_update)) 563 + goto nla_put_failure; 564 + 565 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD, 566 + mep->cc_ccm_tx_info.period)) 567 + goto nla_put_failure; 568 + 569 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV, 570 + mep->cc_ccm_tx_info.if_tlv)) 571 + goto nla_put_failure; 572 + 573 + if (nla_put_u8(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE, 574 + mep->cc_ccm_tx_info.if_tlv_value)) 575 + goto nla_put_failure; 576 + 577 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV, 578 + mep->cc_ccm_tx_info.port_tlv)) 579 + goto nla_put_failure; 580 + 581 + if (nla_put_u8(skb, IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE, 582 + mep->cc_ccm_tx_info.port_tlv_value)) 583 + goto nla_put_failure; 584 + 585 + nla_nest_end(skb, tb); 586 + 587 + hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) { 588 + tb = nla_nest_start(skb, 589 + IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO); 590 + 591 + if (!tb) 592 + goto nla_info_failure; 593 + 594 + if (nla_put_u32(skb, 595 + IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE, 596 + mep->instance)) 597 + goto nla_put_failure; 598 + 599 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_MEPID, 600 + peer_mep->mepid)) 601 + goto nla_put_failure; 602 + 603 + nla_nest_end(skb, tb); 604 + } 605 + } 606 + 607 + return 0; 608 + 609 + nla_put_failure: 610 + nla_nest_cancel(skb, tb); 611 + 612 + nla_info_failure: 613 + return -EMSGSIZE; 614 + } 615 + 616 + int br_cfm_status_fill_info(struct sk_buff *skb, 617 + struct net_bridge *br, 618 + bool getlink) 619 + { 620 + struct br_cfm_peer_mep *peer_mep; 621 + struct br_cfm_mep *mep; 622 + struct nlattr *tb; 623 + 624 + hlist_for_each_entry_rcu(mep, &br->mep_list, head) { 625 + tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO); 626 + if (!tb) 627 + goto nla_info_failure; 628 + 629 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE, 630 + mep->instance)) 631 + goto nla_put_failure; 632 + 633 + if (nla_put_u32(skb, 634 + IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN, 635 + mep->status.opcode_unexp_seen)) 636 + goto nla_put_failure; 637 + 638 + if (nla_put_u32(skb, 639 + IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN, 640 + mep->status.version_unexp_seen)) 641 + goto nla_put_failure; 642 + 643 + if (nla_put_u32(skb, 644 + IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN, 645 + mep->status.rx_level_low_seen)) 646 + goto nla_put_failure; 647 + 648 + /* Only clear if this is a GETLINK */ 649 + if (getlink) { 650 + /* Clear all 'seen' indications */ 651 + mep->status.opcode_unexp_seen = false; 652 + mep->status.version_unexp_seen = false; 653 + mep->status.rx_level_low_seen = false; 654 + } 655 + 656 + nla_nest_end(skb, tb); 657 + 658 + hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) { 659 + tb = nla_nest_start(skb, 660 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO); 661 + if (!tb) 662 + goto nla_info_failure; 663 + 664 + if (nla_put_u32(skb, 665 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE, 666 + mep->instance)) 667 + goto nla_put_failure; 668 + 669 + if (nla_put_u32(skb, 670 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID, 671 + peer_mep->mepid)) 672 + goto nla_put_failure; 673 + 674 + if (nla_put_u32(skb, 675 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT, 676 + peer_mep->cc_status.ccm_defect)) 677 + goto nla_put_failure; 678 + 679 + if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI, 680 + peer_mep->cc_status.rdi)) 681 + goto nla_put_failure; 682 + 683 + if (nla_put_u8(skb, 684 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE, 685 + peer_mep->cc_status.port_tlv_value)) 686 + goto nla_put_failure; 687 + 688 + if (nla_put_u8(skb, 689 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE, 690 + peer_mep->cc_status.if_tlv_value)) 691 + goto nla_put_failure; 692 + 693 + if (nla_put_u32(skb, 694 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN, 695 + peer_mep->cc_status.seen)) 696 + goto nla_put_failure; 697 + 698 + if (nla_put_u32(skb, 699 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN, 700 + peer_mep->cc_status.tlv_seen)) 701 + goto nla_put_failure; 702 + 703 + if (nla_put_u32(skb, 704 + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN, 705 + peer_mep->cc_status.seq_unexp_seen)) 706 + goto nla_put_failure; 707 + 708 + if (getlink) { /* Only clear if this is a GETLINK */ 709 + /* Clear all 'seen' indications */ 710 + peer_mep->cc_status.seen = false; 711 + peer_mep->cc_status.tlv_seen = false; 712 + peer_mep->cc_status.seq_unexp_seen = false; 713 + } 714 + 715 + nla_nest_end(skb, tb); 716 + } 717 + } 718 + 719 + return 0; 720 + 721 + nla_put_failure: 722 + nla_nest_cancel(skb, tb); 723 + 724 + nla_info_failure: 725 + return -EMSGSIZE; 726 + }
+4
net/bridge/br_device.c
··· 454 454 spin_lock_init(&br->lock); 455 455 INIT_LIST_HEAD(&br->port_list); 456 456 INIT_HLIST_HEAD(&br->fdb_list); 457 + INIT_HLIST_HEAD(&br->frame_type_list); 457 458 #if IS_ENABLED(CONFIG_BRIDGE_MRP) 458 459 INIT_LIST_HEAD(&br->mrp_list); 460 + #endif 461 + #if IS_ENABLED(CONFIG_BRIDGE_CFM) 462 + INIT_HLIST_HEAD(&br->mep_list); 459 463 #endif 460 464 spin_lock_init(&br->hash_lock); 461 465
+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
+32 -1
net/bridge/br_input.c
··· 254 254 return RX_HANDLER_CONSUMED; 255 255 } 256 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 + 257 272 /* 258 273 * Return NULL if skb is handled 259 274 * note: already called with rcu_read_lock ··· 358 343 } 359 344 } 360 345 361 - if (unlikely(br_mrp_process(p, skb))) 346 + if (unlikely(br_process_frame_type(p, skb))) 362 347 return RX_HANDLER_PASS; 363 348 364 349 forward: ··· 394 379 return br_handle_frame_dummy; 395 380 396 381 return br_handle_frame; 382 + } 383 + 384 + void br_add_frame(struct net_bridge *br, struct br_frame_type *ft) 385 + { 386 + hlist_add_head_rcu(&ft->list, &br->frame_type_list); 387 + } 388 + 389 + void br_del_frame(struct net_bridge *br, struct br_frame_type *ft) 390 + { 391 + struct br_frame_type *tmp; 392 + 393 + hlist_for_each_entry(tmp, &br->frame_type_list, list) 394 + if (ft == tmp) { 395 + hlist_del_rcu(&ft->list); 396 + return; 397 + } 397 398 }
+15 -4
net/bridge/br_mrp.c
··· 6 6 static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 }; 7 7 static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 }; 8 8 9 + static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); 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 + 9 16 static bool br_mrp_is_ring_port(struct net_bridge_port *p_port, 10 17 struct net_bridge_port *s_port, 11 18 struct net_bridge_port *port) ··· 452 445 453 446 list_del_rcu(&mrp->list); 454 447 kfree_rcu(mrp, rcu); 448 + 449 + if (list_empty(&br->mrp_list)) 450 + br_del_frame(br, &mrp_frame_type); 455 451 } 456 452 457 453 /* Adds a new MRP instance. ··· 502 492 p->flags |= BR_MRP_AWARE; 503 493 spin_unlock_bh(&br->lock); 504 494 rcu_assign_pointer(mrp->s_port, p); 495 + 496 + if (list_empty(&br->mrp_list)) 497 + br_add_frame(br, &mrp_frame_type); 505 498 506 499 INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired); 507 500 INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired); ··· 1185 1172 * normal forwarding. 1186 1173 * note: already called with rcu_read_lock 1187 1174 */ 1188 - int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) 1175 + static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) 1189 1176 { 1190 1177 /* If there is no MRP instance do normal forwarding */ 1191 1178 if (likely(!(p->flags & BR_MRP_AWARE))) 1192 1179 goto out; 1193 1180 1194 - if (unlikely(skb->protocol == htons(ETH_P_MRP))) 1195 - return br_mrp_rcv(p, skb, p->dev); 1196 - 1181 + return br_mrp_rcv(p, skb, p->dev); 1197 1182 out: 1198 1183 return 0; 1199 1184 }
+104 -11
net/bridge/br_netlink.c
··· 16 16 17 17 #include "br_private.h" 18 18 #include "br_private_stp.h" 19 + #include "br_private_cfm.h" 19 20 #include "br_private_tunnel.h" 20 21 21 22 static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg, ··· 94 93 { 95 94 struct net_bridge_vlan_group *vg = NULL; 96 95 struct net_bridge_port *p = NULL; 97 - struct net_bridge *br; 98 - int num_vlan_infos; 96 + struct net_bridge *br = NULL; 97 + u32 num_cfm_peer_mep_infos; 98 + u32 num_cfm_mep_infos; 99 99 size_t vinfo_sz = 0; 100 + int num_vlan_infos; 100 101 101 102 rcu_read_lock(); 102 103 if (netif_is_bridge_port(dev)) { ··· 116 113 117 114 /* Each VLAN is returned in bridge_vlan_info along with flags */ 118 115 vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)); 116 + 117 + if (!(filter_mask & RTEXT_FILTER_CFM_STATUS)) 118 + return vinfo_sz; 119 + 120 + if (!br) 121 + return vinfo_sz; 122 + 123 + /* CFM status info must be added */ 124 + br_cfm_mep_count(br, &num_cfm_mep_infos); 125 + br_cfm_peer_mep_count(br, &num_cfm_peer_mep_infos); 126 + 127 + vinfo_sz += nla_total_size(0); /* IFLA_BRIDGE_CFM */ 128 + /* For each status struct the MEP instance (u32) is added */ 129 + /* MEP instance (u32) + br_cfm_mep_status */ 130 + vinfo_sz += num_cfm_mep_infos * 131 + /*IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE */ 132 + (nla_total_size(sizeof(u32)) 133 + /* IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN */ 134 + + nla_total_size(sizeof(u32)) 135 + /* IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN */ 136 + + nla_total_size(sizeof(u32)) 137 + /* IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN */ 138 + + nla_total_size(sizeof(u32))); 139 + /* MEP instance (u32) + br_cfm_cc_peer_status */ 140 + vinfo_sz += num_cfm_peer_mep_infos * 141 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE */ 142 + (nla_total_size(sizeof(u32)) 143 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID */ 144 + + nla_total_size(sizeof(u32)) 145 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT */ 146 + + nla_total_size(sizeof(u32)) 147 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI */ 148 + + nla_total_size(sizeof(u32)) 149 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE */ 150 + + nla_total_size(sizeof(u8)) 151 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE */ 152 + + nla_total_size(sizeof(u8)) 153 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN */ 154 + + nla_total_size(sizeof(u32)) 155 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN */ 156 + + nla_total_size(sizeof(u32)) 157 + /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN */ 158 + + nla_total_size(sizeof(u32))); 119 159 120 160 return vinfo_sz; 121 161 } ··· 423 377 static int br_fill_ifinfo(struct sk_buff *skb, 424 378 const struct net_bridge_port *port, 425 379 u32 pid, u32 seq, int event, unsigned int flags, 426 - u32 filter_mask, const struct net_device *dev) 380 + u32 filter_mask, const struct net_device *dev, 381 + bool getlink) 427 382 { 428 383 u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; 429 384 struct nlattr *af = NULL; ··· 473 426 474 427 if (filter_mask & (RTEXT_FILTER_BRVLAN | 475 428 RTEXT_FILTER_BRVLAN_COMPRESSED | 476 - RTEXT_FILTER_MRP)) { 429 + RTEXT_FILTER_MRP | 430 + RTEXT_FILTER_CFM_CONFIG | 431 + RTEXT_FILTER_CFM_STATUS)) { 477 432 af = nla_nest_start_noflag(skb, IFLA_AF_SPEC); 478 433 if (!af) 479 434 goto nla_put_failure; ··· 524 475 goto nla_put_failure; 525 476 } 526 477 478 + if (filter_mask & (RTEXT_FILTER_CFM_CONFIG | RTEXT_FILTER_CFM_STATUS)) { 479 + struct nlattr *cfm_nest = NULL; 480 + int err; 481 + 482 + if (!br_cfm_created(br) || port) 483 + goto done; 484 + 485 + cfm_nest = nla_nest_start(skb, IFLA_BRIDGE_CFM); 486 + if (!cfm_nest) 487 + goto nla_put_failure; 488 + 489 + if (filter_mask & RTEXT_FILTER_CFM_CONFIG) { 490 + rcu_read_lock(); 491 + err = br_cfm_config_fill_info(skb, br); 492 + rcu_read_unlock(); 493 + if (err) 494 + goto nla_put_failure; 495 + } 496 + 497 + if (filter_mask & RTEXT_FILTER_CFM_STATUS) { 498 + rcu_read_lock(); 499 + err = br_cfm_status_fill_info(skb, br, getlink); 500 + rcu_read_unlock(); 501 + if (err) 502 + goto nla_put_failure; 503 + } 504 + 505 + nla_nest_end(skb, cfm_nest); 506 + } 507 + 527 508 done: 528 509 if (af) 529 510 nla_nest_end(skb, af); ··· 565 486 return -EMSGSIZE; 566 487 } 567 488 568 - /* Notify listeners of a change in bridge or port information */ 569 - void br_ifinfo_notify(int event, const struct net_bridge *br, 570 - const struct net_bridge_port *port) 489 + void br_info_notify(int event, const struct net_bridge *br, 490 + const struct net_bridge_port *port, u32 filter) 571 491 { 572 - u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED; 573 492 struct net_device *dev; 574 493 struct sk_buff *skb; 575 494 int err = -ENOBUFS; ··· 592 515 if (skb == NULL) 593 516 goto errout; 594 517 595 - err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev); 518 + err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false); 596 519 if (err < 0) { 597 520 /* -EMSGSIZE implies BUG in br_nlmsg_size() */ 598 521 WARN_ON(err == -EMSGSIZE); ··· 605 528 rtnl_set_sk_err(net, RTNLGRP_LINK, err); 606 529 } 607 530 531 + /* Notify listeners of a change in bridge or port information */ 532 + void br_ifinfo_notify(int event, const struct net_bridge *br, 533 + const struct net_bridge_port *port) 534 + { 535 + u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED; 536 + 537 + return br_info_notify(event, br, port, filter); 538 + } 539 + 608 540 /* 609 541 * Dump information about all ports, in response to GETLINK 610 542 */ ··· 624 538 625 539 if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) && 626 540 !(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) && 627 - !(filter_mask & RTEXT_FILTER_MRP)) 541 + !(filter_mask & RTEXT_FILTER_MRP) && 542 + !(filter_mask & RTEXT_FILTER_CFM_CONFIG) && 543 + !(filter_mask & RTEXT_FILTER_CFM_STATUS)) 628 544 return 0; 629 545 630 546 return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags, 631 - filter_mask, dev); 547 + filter_mask, dev, true); 632 548 } 633 549 634 550 static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, ··· 785 697 break; 786 698 case IFLA_BRIDGE_MRP: 787 699 err = br_mrp_parse(br, p, attr, cmd, extack); 700 + if (err) 701 + return err; 702 + break; 703 + case IFLA_BRIDGE_CFM: 704 + err = br_cfm_parse(br, p, attr, cmd, extack); 788 705 if (err) 789 706 return err; 790 707 break;
+70 -7
net/bridge/br_private.h
··· 383 383 struct net_bridge { 384 384 spinlock_t lock; 385 385 spinlock_t hash_lock; 386 - struct list_head port_list; 386 + struct hlist_head frame_type_list; 387 387 struct net_device *dev; 388 388 struct pcpu_sw_netstats __percpu *stats; 389 389 unsigned long options; ··· 395 395 #endif 396 396 397 397 struct rhashtable fdb_hash_tbl; 398 + struct list_head port_list; 398 399 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 399 400 union { 400 401 struct rtable fake_rtable; ··· 483 482 484 483 #if IS_ENABLED(CONFIG_BRIDGE_MRP) 485 484 struct list_head mrp_list; 485 + #endif 486 + #if IS_ENABLED(CONFIG_BRIDGE_CFM) 487 + struct hlist_head mep_list; 486 488 #endif 487 489 }; 488 490 ··· 758 754 /* br_input.c */ 759 755 int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); 760 756 rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); 757 + 758 + struct br_frame_type { 759 + __be16 type; 760 + int (*frame_handler)(struct net_bridge_port *port, 761 + struct sk_buff *skb); 762 + struct hlist_node list; 763 + }; 764 + 765 + void br_add_frame(struct net_bridge *br, struct br_frame_type *ft); 766 + void br_del_frame(struct net_bridge *br, struct br_frame_type *ft); 761 767 762 768 static inline bool br_rx_handler_check_rcu(const struct net_device *dev) 763 769 { ··· 1431 1417 #if IS_ENABLED(CONFIG_BRIDGE_MRP) 1432 1418 int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p, 1433 1419 struct nlattr *attr, int cmd, struct netlink_ext_ack *extack); 1434 - int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); 1435 1420 bool br_mrp_enabled(struct net_bridge *br); 1436 1421 void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p); 1437 1422 int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br); ··· 1440 1427 struct netlink_ext_ack *extack) 1441 1428 { 1442 1429 return -EOPNOTSUPP; 1443 - } 1444 - 1445 - static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) 1446 - { 1447 - return 0; 1448 1430 } 1449 1431 1450 1432 static inline bool br_mrp_enabled(struct net_bridge *br) ··· 1459 1451 1460 1452 #endif 1461 1453 1454 + /* br_cfm.c */ 1455 + #if IS_ENABLED(CONFIG_BRIDGE_CFM) 1456 + int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p, 1457 + struct nlattr *attr, int cmd, struct netlink_ext_ack *extack); 1458 + bool br_cfm_created(struct net_bridge *br); 1459 + void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p); 1460 + int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br); 1461 + int br_cfm_status_fill_info(struct sk_buff *skb, 1462 + struct net_bridge *br, 1463 + bool getlink); 1464 + int br_cfm_mep_count(struct net_bridge *br, u32 *count); 1465 + int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count); 1466 + #else 1467 + static inline int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p, 1468 + struct nlattr *attr, int cmd, 1469 + struct netlink_ext_ack *extack) 1470 + { 1471 + return -EOPNOTSUPP; 1472 + } 1473 + 1474 + static inline bool br_cfm_created(struct net_bridge *br) 1475 + { 1476 + return false; 1477 + } 1478 + 1479 + static inline void br_cfm_port_del(struct net_bridge *br, 1480 + struct net_bridge_port *p) 1481 + { 1482 + } 1483 + 1484 + static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br) 1485 + { 1486 + return -EOPNOTSUPP; 1487 + } 1488 + 1489 + static inline int br_cfm_status_fill_info(struct sk_buff *skb, 1490 + struct net_bridge *br, 1491 + bool getlink) 1492 + { 1493 + return -EOPNOTSUPP; 1494 + } 1495 + 1496 + static inline int br_cfm_mep_count(struct net_bridge *br, u32 *count) 1497 + { 1498 + return -EOPNOTSUPP; 1499 + } 1500 + 1501 + static inline int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count) 1502 + { 1503 + return -EOPNOTSUPP; 1504 + } 1505 + #endif 1506 + 1462 1507 /* br_netlink.c */ 1463 1508 extern struct rtnl_link_ops br_link_ops; 1464 1509 int br_netlink_init(void); 1465 1510 void br_netlink_fini(void); 1466 1511 void br_ifinfo_notify(int event, const struct net_bridge *br, 1467 1512 const struct net_bridge_port *port); 1513 + void br_info_notify(int event, const struct net_bridge *br, 1514 + const struct net_bridge_port *port, u32 filter); 1468 1515 int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags, 1469 1516 struct netlink_ext_ack *extack); 1470 1517 int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
+147
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 + struct br_cfm_maid { 36 + u8 data[CFM_MAID_LENGTH]; 37 + }; 38 + 39 + struct br_cfm_cc_config { 40 + /* Expected received CCM PDU MAID. */ 41 + struct br_cfm_maid exp_maid; 42 + 43 + /* Expected received CCM PDU interval. */ 44 + /* Transmitting CCM PDU interval when CCM tx is enabled. */ 45 + enum br_cfm_ccm_interval exp_interval; 46 + 47 + bool enable; /* Enable/disable CCM PDU handling */ 48 + }; 49 + 50 + int br_cfm_cc_config_set(struct net_bridge *br, 51 + const u32 instance, 52 + const struct br_cfm_cc_config *const config, 53 + struct netlink_ext_ack *extack); 54 + 55 + int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance, 56 + u32 peer_mep_id, 57 + struct netlink_ext_ack *extack); 58 + int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance, 59 + u32 peer_mep_id, 60 + struct netlink_ext_ack *extack); 61 + 62 + /* Transmitted CCM Remote Defect Indication status set. 63 + * This RDI is inserted in transmitted CCM PDUs if CCM transmission is enabled. 64 + * See br_cfm_cc_ccm_tx() with interval != BR_CFM_CCM_INTERVAL_NONE 65 + */ 66 + int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance, 67 + const bool rdi, struct netlink_ext_ack *extack); 68 + 69 + /* OAM PDU Tx information */ 70 + struct br_cfm_cc_ccm_tx_info { 71 + struct mac_addr dmac; 72 + /* The CCM will be transmitted for this period in seconds. 73 + * Call br_cfm_cc_ccm_tx before timeout to keep transmission alive. 74 + * When period is zero any ongoing transmission will be stopped. 75 + */ 76 + u32 period; 77 + 78 + bool seq_no_update; /* Update Tx CCM sequence number */ 79 + bool if_tlv; /* Insert Interface Status TLV */ 80 + u8 if_tlv_value; /* Interface Status TLV value */ 81 + bool port_tlv; /* Insert Port Status TLV */ 82 + u8 port_tlv_value; /* Port Status TLV value */ 83 + /* Sender ID TLV ?? 84 + * Organization-Specific TLV ?? 85 + */ 86 + }; 87 + 88 + int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance, 89 + const struct br_cfm_cc_ccm_tx_info *const tx_info, 90 + struct netlink_ext_ack *extack); 91 + 92 + struct br_cfm_mep_status { 93 + /* Indications that an OAM PDU has been seen. */ 94 + bool opcode_unexp_seen; /* RX of OAM PDU with unexpected opcode */ 95 + bool version_unexp_seen; /* RX of OAM PDU with unexpected version */ 96 + bool rx_level_low_seen; /* Rx of OAM PDU with level low */ 97 + }; 98 + 99 + struct br_cfm_cc_peer_status { 100 + /* This CCM related status is based on the latest received CCM PDU. */ 101 + u8 port_tlv_value; /* Port Status TLV value */ 102 + u8 if_tlv_value; /* Interface Status TLV value */ 103 + 104 + /* CCM has not been received for 3.25 intervals */ 105 + u8 ccm_defect:1; 106 + 107 + /* (RDI == 1) for last received CCM PDU */ 108 + u8 rdi:1; 109 + 110 + /* Indications that a CCM PDU has been seen. */ 111 + u8 seen:1; /* CCM PDU received */ 112 + u8 tlv_seen:1; /* CCM PDU with TLV received */ 113 + /* CCM PDU with unexpected sequence number received */ 114 + u8 seq_unexp_seen:1; 115 + }; 116 + 117 + struct br_cfm_mep { 118 + /* list header of MEP instances */ 119 + struct hlist_node head; 120 + u32 instance; 121 + struct br_cfm_mep_create create; 122 + struct br_cfm_mep_config config; 123 + struct br_cfm_cc_config cc_config; 124 + struct br_cfm_cc_ccm_tx_info cc_ccm_tx_info; 125 + /* List of multiple peer MEPs */ 126 + struct hlist_head peer_mep_list; 127 + struct net_bridge_port __rcu *b_port; 128 + unsigned long ccm_tx_end; 129 + struct delayed_work ccm_tx_dwork; 130 + u32 ccm_tx_snumber; 131 + u32 ccm_rx_snumber; 132 + struct br_cfm_mep_status status; 133 + bool rdi; 134 + struct rcu_head rcu; 135 + }; 136 + 137 + struct br_cfm_peer_mep { 138 + struct hlist_node head; 139 + struct br_cfm_mep *mep; 140 + struct delayed_work ccm_rx_dwork; 141 + u32 mepid; 142 + struct br_cfm_cc_peer_status cc_status; 143 + u32 ccm_rx_count_miss; 144 + struct rcu_head rcu; 145 + }; 146 + 147 + #endif /* _BR_PRIVATE_CFM_H_ */