bridge: cfm: Kernel space implementation of CFM. MEP create/delete.
[linux-block.git] / net / bridge / br_cfm.c
CommitLineData
86a14b79
HB
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
7static 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
18static 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
31static 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
44static 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
56int 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
124static 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
143int 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
163int 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
184int 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
219int 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 */
250void 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}