Commit | Line | Data |
---|---|---|
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 | ||
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 | } |