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, nla: attr, |
99 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]); |
123 | create.domain = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]); |
124 | create.direction = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]); |
125 | create.ifindex = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]); |
126 | |
127 | return br_cfm_mep_create(br, instance, create: &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, nla: attr, |
138 | policy: 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(nla: 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, nla: attr, |
162 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]); |
186 | nla_memcpy(dest: &config.unicast_mac.addr, |
187 | src: tb[IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC], |
188 | count: sizeof(config.unicast_mac.addr)); |
189 | config.mdlevel = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]); |
190 | config.mepid = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]); |
191 | |
192 | return br_cfm_mep_config_set(br, instance, config: &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, nla: attr, |
204 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]); |
228 | config.enable = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]); |
229 | config.exp_interval = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL]); |
230 | nla_memcpy(dest: &config.exp_maid.data, src: tb[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID], |
231 | count: sizeof(config.exp_maid.data)); |
232 | |
233 | return br_cfm_cc_config_set(br, instance, config: &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, nla: attr, |
244 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]); |
258 | peer_mep_id = nla_get_u32(nla: 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, nla: attr, |
271 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]); |
285 | peer_mep_id = nla_get_u32(nla: 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, nla: attr, |
298 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]); |
312 | rdi = nla_get_u32(nla: 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, nla: attr, |
326 | policy: 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(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE]); |
366 | nla_memcpy(dest: &tx_info.dmac.addr, |
367 | src: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC], |
368 | count: sizeof(tx_info.dmac.addr)); |
369 | tx_info.seq_no_update = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]); |
370 | tx_info.period = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]); |
371 | tx_info.if_tlv = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]); |
372 | tx_info.if_tlv_value = nla_get_u8(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]); |
373 | tx_info.port_tlv = nla_get_u32(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]); |
374 | tx_info.port_tlv_value = nla_get_u8(nla: tb[IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE]); |
375 | |
376 | return br_cfm_cc_ccm_tx(br, instance, tx_info: &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, nla: attr, |
392 | policy: 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, attr: 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, attr: 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, attr: 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, attr: 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, attr: 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, attr: 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, attr: 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, attr: 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, attrtype: IFLA_BRIDGE_CFM_MEP_CREATE_INFO); |
463 | if (!tb) |
464 | goto nla_info_failure; |
465 | |
466 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE, |
467 | value: mep->instance)) |
468 | goto nla_put_failure; |
469 | |
470 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN, |
471 | value: mep->create.domain)) |
472 | goto nla_put_failure; |
473 | |
474 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION, |
475 | value: mep->create.direction)) |
476 | goto nla_put_failure; |
477 | |
478 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX, |
479 | value: mep->create.ifindex)) |
480 | goto nla_put_failure; |
481 | |
482 | nla_nest_end(skb, start: tb); |
483 | |
484 | tb = nla_nest_start(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CONFIG_INFO); |
485 | |
486 | if (!tb) |
487 | goto nla_info_failure; |
488 | |
489 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE, |
490 | value: mep->instance)) |
491 | goto nla_put_failure; |
492 | |
493 | if (nla_put(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC, |
494 | attrlen: sizeof(mep->config.unicast_mac.addr), |
495 | data: mep->config.unicast_mac.addr)) |
496 | goto nla_put_failure; |
497 | |
498 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL, |
499 | value: mep->config.mdlevel)) |
500 | goto nla_put_failure; |
501 | |
502 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID, |
503 | value: mep->config.mepid)) |
504 | goto nla_put_failure; |
505 | |
506 | nla_nest_end(skb, start: tb); |
507 | |
508 | tb = nla_nest_start(skb, attrtype: IFLA_BRIDGE_CFM_CC_CONFIG_INFO); |
509 | |
510 | if (!tb) |
511 | goto nla_info_failure; |
512 | |
513 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE, |
514 | value: mep->instance)) |
515 | goto nla_put_failure; |
516 | |
517 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE, |
518 | value: mep->cc_config.enable)) |
519 | goto nla_put_failure; |
520 | |
521 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL, |
522 | value: mep->cc_config.exp_interval)) |
523 | goto nla_put_failure; |
524 | |
525 | if (nla_put(skb, attrtype: IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID, |
526 | attrlen: sizeof(mep->cc_config.exp_maid.data), |
527 | data: mep->cc_config.exp_maid.data)) |
528 | goto nla_put_failure; |
529 | |
530 | nla_nest_end(skb, start: tb); |
531 | |
532 | tb = nla_nest_start(skb, attrtype: IFLA_BRIDGE_CFM_CC_RDI_INFO); |
533 | |
534 | if (!tb) |
535 | goto nla_info_failure; |
536 | |
537 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_RDI_INSTANCE, |
538 | value: mep->instance)) |
539 | goto nla_put_failure; |
540 | |
541 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_RDI_RDI, |
542 | value: mep->rdi)) |
543 | goto nla_put_failure; |
544 | |
545 | nla_nest_end(skb, start: tb); |
546 | |
547 | tb = nla_nest_start(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_INFO); |
548 | |
549 | if (!tb) |
550 | goto nla_info_failure; |
551 | |
552 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE, |
553 | value: mep->instance)) |
554 | goto nla_put_failure; |
555 | |
556 | if (nla_put(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC, |
557 | attrlen: sizeof(mep->cc_ccm_tx_info.dmac), |
558 | data: mep->cc_ccm_tx_info.dmac.addr)) |
559 | goto nla_put_failure; |
560 | |
561 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE, |
562 | value: mep->cc_ccm_tx_info.seq_no_update)) |
563 | goto nla_put_failure; |
564 | |
565 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD, |
566 | value: mep->cc_ccm_tx_info.period)) |
567 | goto nla_put_failure; |
568 | |
569 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV, |
570 | value: mep->cc_ccm_tx_info.if_tlv)) |
571 | goto nla_put_failure; |
572 | |
573 | if (nla_put_u8(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE, |
574 | value: mep->cc_ccm_tx_info.if_tlv_value)) |
575 | goto nla_put_failure; |
576 | |
577 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV, |
578 | value: mep->cc_ccm_tx_info.port_tlv)) |
579 | goto nla_put_failure; |
580 | |
581 | if (nla_put_u8(skb, attrtype: IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE, |
582 | value: mep->cc_ccm_tx_info.port_tlv_value)) |
583 | goto nla_put_failure; |
584 | |
585 | nla_nest_end(skb, start: tb); |
586 | |
587 | hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) { |
588 | tb = nla_nest_start(skb, |
589 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO); |
590 | |
591 | if (!tb) |
592 | goto nla_info_failure; |
593 | |
594 | if (nla_put_u32(skb, |
595 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE, |
596 | value: mep->instance)) |
597 | goto nla_put_failure; |
598 | |
599 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_PEER_MEPID, |
600 | value: peer_mep->mepid)) |
601 | goto nla_put_failure; |
602 | |
603 | nla_nest_end(skb, start: tb); |
604 | } |
605 | } |
606 | |
607 | return 0; |
608 | |
609 | nla_put_failure: |
610 | nla_nest_cancel(skb, start: 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, attrtype: IFLA_BRIDGE_CFM_MEP_STATUS_INFO); |
626 | if (!tb) |
627 | goto nla_info_failure; |
628 | |
629 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE, |
630 | value: mep->instance)) |
631 | goto nla_put_failure; |
632 | |
633 | if (nla_put_u32(skb, |
634 | attrtype: IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN, |
635 | value: mep->status.opcode_unexp_seen)) |
636 | goto nla_put_failure; |
637 | |
638 | if (nla_put_u32(skb, |
639 | attrtype: IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN, |
640 | value: mep->status.version_unexp_seen)) |
641 | goto nla_put_failure; |
642 | |
643 | if (nla_put_u32(skb, |
644 | attrtype: IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN, |
645 | value: 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, start: tb); |
657 | |
658 | hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) { |
659 | tb = nla_nest_start(skb, |
660 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO); |
661 | if (!tb) |
662 | goto nla_info_failure; |
663 | |
664 | if (nla_put_u32(skb, |
665 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE, |
666 | value: mep->instance)) |
667 | goto nla_put_failure; |
668 | |
669 | if (nla_put_u32(skb, |
670 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID, |
671 | value: peer_mep->mepid)) |
672 | goto nla_put_failure; |
673 | |
674 | if (nla_put_u32(skb, |
675 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT, |
676 | value: peer_mep->cc_status.ccm_defect)) |
677 | goto nla_put_failure; |
678 | |
679 | if (nla_put_u32(skb, attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI, |
680 | value: peer_mep->cc_status.rdi)) |
681 | goto nla_put_failure; |
682 | |
683 | if (nla_put_u8(skb, |
684 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE, |
685 | value: peer_mep->cc_status.port_tlv_value)) |
686 | goto nla_put_failure; |
687 | |
688 | if (nla_put_u8(skb, |
689 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE, |
690 | value: peer_mep->cc_status.if_tlv_value)) |
691 | goto nla_put_failure; |
692 | |
693 | if (nla_put_u32(skb, |
694 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN, |
695 | value: peer_mep->cc_status.seen)) |
696 | goto nla_put_failure; |
697 | |
698 | if (nla_put_u32(skb, |
699 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN, |
700 | value: peer_mep->cc_status.tlv_seen)) |
701 | goto nla_put_failure; |
702 | |
703 | if (nla_put_u32(skb, |
704 | attrtype: IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN, |
705 | value: 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, start: tb); |
716 | } |
717 | } |
718 | |
719 | return 0; |
720 | |
721 | nla_put_failure: |
722 | nla_nest_cancel(skb, start: tb); |
723 | |
724 | nla_info_failure: |
725 | return -EMSGSIZE; |
726 | } |
727 | |