162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <net/genetlink.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <uapi/linux/mrp_bridge.h>
662306a36Sopenharmony_ci#include "br_private.h"
762306a36Sopenharmony_ci#include "br_private_mrp.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistatic const struct nla_policy br_mrp_policy[IFLA_BRIDGE_MRP_MAX + 1] = {
1062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_UNSPEC]	= { .type = NLA_REJECT },
1162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE]	= { .type = NLA_NESTED },
1262306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_STATE]	= { .type = NLA_NESTED },
1362306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_ROLE]	= { .type = NLA_NESTED },
1462306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_STATE]	= { .type = NLA_NESTED },
1562306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_ROLE]	= { .type = NLA_NESTED },
1662306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST]	= { .type = NLA_NESTED },
1762306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE]	= { .type = NLA_NESTED },
1862306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_STATE]	= { .type = NLA_NESTED },
1962306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST]	= { .type = NLA_NESTED },
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct nla_policy
2362306a36Sopenharmony_cibr_mrp_instance_policy[IFLA_BRIDGE_MRP_INSTANCE_MAX + 1] = {
2462306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE_UNSPEC]	= { .type = NLA_REJECT },
2562306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE_RING_ID]	= { .type = NLA_U32 },
2662306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX]	= { .type = NLA_U32 },
2762306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]	= { .type = NLA_U32 },
2862306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_INSTANCE_PRIO]		= { .type = NLA_U16 },
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int br_mrp_instance_parse(struct net_bridge *br, struct nlattr *attr,
3262306a36Sopenharmony_ci				 int cmd, struct netlink_ext_ack *extack)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_INSTANCE_MAX + 1];
3562306a36Sopenharmony_ci	struct br_mrp_instance inst;
3662306a36Sopenharmony_ci	int err;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_INSTANCE_MAX, attr,
3962306a36Sopenharmony_ci			       br_mrp_instance_policy, extack);
4062306a36Sopenharmony_ci	if (err)
4162306a36Sopenharmony_ci		return err;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_INSTANCE_RING_ID] ||
4462306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX] ||
4562306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]) {
4662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
4762306a36Sopenharmony_ci				   "Missing attribute: RING_ID or P_IFINDEX or S_IFINDEX");
4862306a36Sopenharmony_ci		return -EINVAL;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	memset(&inst, 0, sizeof(inst));
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	inst.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_RING_ID]);
5462306a36Sopenharmony_ci	inst.p_ifindex = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX]);
5562306a36Sopenharmony_ci	inst.s_ifindex = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]);
5662306a36Sopenharmony_ci	inst.prio = MRP_DEFAULT_PRIO;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_INSTANCE_PRIO])
5962306a36Sopenharmony_ci		inst.prio = nla_get_u16(tb[IFLA_BRIDGE_MRP_INSTANCE_PRIO]);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (cmd == RTM_SETLINK)
6262306a36Sopenharmony_ci		return br_mrp_add(br, &inst);
6362306a36Sopenharmony_ci	else
6462306a36Sopenharmony_ci		return br_mrp_del(br, &inst);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct nla_policy
7062306a36Sopenharmony_cibr_mrp_port_state_policy[IFLA_BRIDGE_MRP_PORT_STATE_MAX + 1] = {
7162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_STATE_UNSPEC]	= { .type = NLA_REJECT },
7262306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_STATE_STATE]	= { .type = NLA_U32 },
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int br_mrp_port_state_parse(struct net_bridge_port *p,
7662306a36Sopenharmony_ci				   struct nlattr *attr,
7762306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_PORT_STATE_MAX + 1];
8062306a36Sopenharmony_ci	enum br_mrp_port_state_type state;
8162306a36Sopenharmony_ci	int err;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_PORT_STATE_MAX, attr,
8462306a36Sopenharmony_ci			       br_mrp_port_state_policy, extack);
8562306a36Sopenharmony_ci	if (err)
8662306a36Sopenharmony_ci		return err;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_PORT_STATE_STATE]) {
8962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing attribute: STATE");
9062306a36Sopenharmony_ci		return -EINVAL;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	state = nla_get_u32(tb[IFLA_BRIDGE_MRP_PORT_STATE_STATE]);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return br_mrp_set_port_state(p, state);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct nla_policy
9962306a36Sopenharmony_cibr_mrp_port_role_policy[IFLA_BRIDGE_MRP_PORT_ROLE_MAX + 1] = {
10062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_ROLE_UNSPEC]	= { .type = NLA_REJECT },
10162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_PORT_ROLE_ROLE]	= { .type = NLA_U32 },
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int br_mrp_port_role_parse(struct net_bridge_port *p,
10562306a36Sopenharmony_ci				  struct nlattr *attr,
10662306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_PORT_ROLE_MAX + 1];
10962306a36Sopenharmony_ci	enum br_mrp_port_role_type role;
11062306a36Sopenharmony_ci	int err;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_PORT_ROLE_MAX, attr,
11362306a36Sopenharmony_ci			       br_mrp_port_role_policy, extack);
11462306a36Sopenharmony_ci	if (err)
11562306a36Sopenharmony_ci		return err;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_PORT_ROLE_ROLE]) {
11862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing attribute: ROLE");
11962306a36Sopenharmony_ci		return -EINVAL;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	role = nla_get_u32(tb[IFLA_BRIDGE_MRP_PORT_ROLE_ROLE]);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return br_mrp_set_port_role(p, role);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic const struct nla_policy
12862306a36Sopenharmony_cibr_mrp_ring_state_policy[IFLA_BRIDGE_MRP_RING_STATE_MAX + 1] = {
12962306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_STATE_UNSPEC]	= { .type = NLA_REJECT },
13062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_STATE_RING_ID]	= { .type = NLA_U32 },
13162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_STATE_STATE]	= { .type = NLA_U32 },
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int br_mrp_ring_state_parse(struct net_bridge *br, struct nlattr *attr,
13562306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_RING_STATE_MAX + 1];
13862306a36Sopenharmony_ci	struct br_mrp_ring_state state;
13962306a36Sopenharmony_ci	int err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_RING_STATE_MAX, attr,
14262306a36Sopenharmony_ci			       br_mrp_ring_state_policy, extack);
14362306a36Sopenharmony_ci	if (err)
14462306a36Sopenharmony_ci		return err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_RING_STATE_RING_ID] ||
14762306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_RING_STATE_STATE]) {
14862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
14962306a36Sopenharmony_ci				   "Missing attribute: RING_ID or STATE");
15062306a36Sopenharmony_ci		return -EINVAL;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	memset(&state, 0x0, sizeof(state));
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	state.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_RING_STATE_RING_ID]);
15662306a36Sopenharmony_ci	state.ring_state = nla_get_u32(tb[IFLA_BRIDGE_MRP_RING_STATE_STATE]);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return br_mrp_set_ring_state(br, &state);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic const struct nla_policy
16262306a36Sopenharmony_cibr_mrp_ring_role_policy[IFLA_BRIDGE_MRP_RING_ROLE_MAX + 1] = {
16362306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_ROLE_UNSPEC]	= { .type = NLA_REJECT },
16462306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_ROLE_RING_ID]	= { .type = NLA_U32 },
16562306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_RING_ROLE_ROLE]	= { .type = NLA_U32 },
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int br_mrp_ring_role_parse(struct net_bridge *br, struct nlattr *attr,
16962306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_RING_ROLE_MAX + 1];
17262306a36Sopenharmony_ci	struct br_mrp_ring_role role;
17362306a36Sopenharmony_ci	int err;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_RING_ROLE_MAX, attr,
17662306a36Sopenharmony_ci			       br_mrp_ring_role_policy, extack);
17762306a36Sopenharmony_ci	if (err)
17862306a36Sopenharmony_ci		return err;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_RING_ROLE_RING_ID] ||
18162306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_RING_ROLE_ROLE]) {
18262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
18362306a36Sopenharmony_ci				   "Missing attribute: RING_ID or ROLE");
18462306a36Sopenharmony_ci		return -EINVAL;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	memset(&role, 0x0, sizeof(role));
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	role.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_RING_ROLE_RING_ID]);
19062306a36Sopenharmony_ci	role.ring_role = nla_get_u32(tb[IFLA_BRIDGE_MRP_RING_ROLE_ROLE]);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return br_mrp_set_ring_role(br, &role);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic const struct nla_policy
19662306a36Sopenharmony_cibr_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
19762306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_UNSPEC]	= { .type = NLA_REJECT },
19862306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_RING_ID]	= { .type = NLA_U32 },
19962306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]	= { .type = NLA_U32 },
20062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]	= { .type = NLA_U32 },
20162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_PERIOD]	= { .type = NLA_U32 },
20262306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_TEST_MONITOR]	= { .type = NLA_U32 },
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int br_mrp_start_test_parse(struct net_bridge *br, struct nlattr *attr,
20662306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_START_TEST_MAX + 1];
20962306a36Sopenharmony_ci	struct br_mrp_start_test test;
21062306a36Sopenharmony_ci	int err;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_START_TEST_MAX, attr,
21362306a36Sopenharmony_ci			       br_mrp_start_test_policy, extack);
21462306a36Sopenharmony_ci	if (err)
21562306a36Sopenharmony_ci		return err;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_START_TEST_RING_ID] ||
21862306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_TEST_INTERVAL] ||
21962306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] ||
22062306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_TEST_PERIOD]) {
22162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
22262306a36Sopenharmony_ci				   "Missing attribute: RING_ID or INTERVAL or MAX_MISS or PERIOD");
22362306a36Sopenharmony_ci		return -EINVAL;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	memset(&test, 0x0, sizeof(test));
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	test.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_RING_ID]);
22962306a36Sopenharmony_ci	test.interval = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]);
23062306a36Sopenharmony_ci	test.max_miss = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]);
23162306a36Sopenharmony_ci	test.period = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_PERIOD]);
23262306a36Sopenharmony_ci	test.monitor = false;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR])
23562306a36Sopenharmony_ci		test.monitor =
23662306a36Sopenharmony_ci			nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR]);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return br_mrp_start_test(br, &test);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic const struct nla_policy
24262306a36Sopenharmony_cibr_mrp_in_state_policy[IFLA_BRIDGE_MRP_IN_STATE_MAX + 1] = {
24362306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_STATE_UNSPEC]	= { .type = NLA_REJECT },
24462306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_STATE_IN_ID]	= { .type = NLA_U32 },
24562306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_STATE_STATE]	= { .type = NLA_U32 },
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int br_mrp_in_state_parse(struct net_bridge *br, struct nlattr *attr,
24962306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_IN_STATE_MAX + 1];
25262306a36Sopenharmony_ci	struct br_mrp_in_state state;
25362306a36Sopenharmony_ci	int err;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_IN_STATE_MAX, attr,
25662306a36Sopenharmony_ci			       br_mrp_in_state_policy, extack);
25762306a36Sopenharmony_ci	if (err)
25862306a36Sopenharmony_ci		return err;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_IN_STATE_IN_ID] ||
26162306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_IN_STATE_STATE]) {
26262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
26362306a36Sopenharmony_ci				   "Missing attribute: IN_ID or STATE");
26462306a36Sopenharmony_ci		return -EINVAL;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	memset(&state, 0x0, sizeof(state));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	state.in_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_IN_STATE_IN_ID]);
27062306a36Sopenharmony_ci	state.in_state = nla_get_u32(tb[IFLA_BRIDGE_MRP_IN_STATE_STATE]);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return br_mrp_set_in_state(br, &state);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic const struct nla_policy
27662306a36Sopenharmony_cibr_mrp_in_role_policy[IFLA_BRIDGE_MRP_IN_ROLE_MAX + 1] = {
27762306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE_UNSPEC]	= { .type = NLA_REJECT },
27862306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE_RING_ID]	= { .type = NLA_U32 },
27962306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE_IN_ID]		= { .type = NLA_U16 },
28062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE_ROLE]		= { .type = NLA_U32 },
28162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX]	= { .type = NLA_U32 },
28262306a36Sopenharmony_ci};
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int br_mrp_in_role_parse(struct net_bridge *br, struct nlattr *attr,
28562306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_IN_ROLE_MAX + 1];
28862306a36Sopenharmony_ci	struct br_mrp_in_role role;
28962306a36Sopenharmony_ci	int err;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_IN_ROLE_MAX, attr,
29262306a36Sopenharmony_ci			       br_mrp_in_role_policy, extack);
29362306a36Sopenharmony_ci	if (err)
29462306a36Sopenharmony_ci		return err;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_IN_ROLE_RING_ID] ||
29762306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_IN_ROLE_IN_ID] ||
29862306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX] ||
29962306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_IN_ROLE_ROLE]) {
30062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
30162306a36Sopenharmony_ci				   "Missing attribute: RING_ID or ROLE or IN_ID or I_IFINDEX");
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	memset(&role, 0x0, sizeof(role));
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	role.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_IN_ROLE_RING_ID]);
30862306a36Sopenharmony_ci	role.in_id = nla_get_u16(tb[IFLA_BRIDGE_MRP_IN_ROLE_IN_ID]);
30962306a36Sopenharmony_ci	role.i_ifindex = nla_get_u32(tb[IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX]);
31062306a36Sopenharmony_ci	role.in_role = nla_get_u32(tb[IFLA_BRIDGE_MRP_IN_ROLE_ROLE]);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return br_mrp_set_in_role(br, &role);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic const struct nla_policy
31662306a36Sopenharmony_cibr_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = {
31762306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC]	= { .type = NLA_REJECT },
31862306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID]	= { .type = NLA_U32 },
31962306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL]	= { .type = NLA_U32 },
32062306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS]	= { .type = NLA_U32 },
32162306a36Sopenharmony_ci	[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]	= { .type = NLA_U32 },
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int br_mrp_start_in_test_parse(struct net_bridge *br,
32562306a36Sopenharmony_ci				      struct nlattr *attr,
32662306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1];
32962306a36Sopenharmony_ci	struct br_mrp_start_in_test test;
33062306a36Sopenharmony_ci	int err;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_START_IN_TEST_MAX, attr,
33362306a36Sopenharmony_ci			       br_mrp_start_in_test_policy, extack);
33462306a36Sopenharmony_ci	if (err)
33562306a36Sopenharmony_ci		return err;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!tb[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] ||
33862306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] ||
33962306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] ||
34062306a36Sopenharmony_ci	    !tb[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]) {
34162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
34262306a36Sopenharmony_ci				   "Missing attribute: RING_ID or INTERVAL or MAX_MISS or PERIOD");
34362306a36Sopenharmony_ci		return -EINVAL;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	memset(&test, 0x0, sizeof(test));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	test.in_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID]);
34962306a36Sopenharmony_ci	test.interval = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL]);
35062306a36Sopenharmony_ci	test.max_miss = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS]);
35162306a36Sopenharmony_ci	test.period = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return br_mrp_start_in_test(br, &test);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciint br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
35762306a36Sopenharmony_ci		 struct nlattr *attr, int cmd, struct netlink_ext_ack *extack)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_MRP_MAX + 1];
36062306a36Sopenharmony_ci	int err;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* When this function is called for a port then the br pointer is
36362306a36Sopenharmony_ci	 * invalid, therefor set the br to point correctly
36462306a36Sopenharmony_ci	 */
36562306a36Sopenharmony_ci	if (p)
36662306a36Sopenharmony_ci		br = p->br;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (br->stp_enabled != BR_NO_STP) {
36962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "MRP can't be enabled if STP is already enabled");
37062306a36Sopenharmony_ci		return -EINVAL;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_MAX, attr,
37462306a36Sopenharmony_ci			       br_mrp_policy, extack);
37562306a36Sopenharmony_ci	if (err)
37662306a36Sopenharmony_ci		return err;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_INSTANCE]) {
37962306a36Sopenharmony_ci		err = br_mrp_instance_parse(br, tb[IFLA_BRIDGE_MRP_INSTANCE],
38062306a36Sopenharmony_ci					    cmd, extack);
38162306a36Sopenharmony_ci		if (err)
38262306a36Sopenharmony_ci			return err;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_PORT_STATE]) {
38662306a36Sopenharmony_ci		err = br_mrp_port_state_parse(p, tb[IFLA_BRIDGE_MRP_PORT_STATE],
38762306a36Sopenharmony_ci					      extack);
38862306a36Sopenharmony_ci		if (err)
38962306a36Sopenharmony_ci			return err;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_PORT_ROLE]) {
39362306a36Sopenharmony_ci		err = br_mrp_port_role_parse(p, tb[IFLA_BRIDGE_MRP_PORT_ROLE],
39462306a36Sopenharmony_ci					     extack);
39562306a36Sopenharmony_ci		if (err)
39662306a36Sopenharmony_ci			return err;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_RING_STATE]) {
40062306a36Sopenharmony_ci		err = br_mrp_ring_state_parse(br,
40162306a36Sopenharmony_ci					      tb[IFLA_BRIDGE_MRP_RING_STATE],
40262306a36Sopenharmony_ci					      extack);
40362306a36Sopenharmony_ci		if (err)
40462306a36Sopenharmony_ci			return err;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_RING_ROLE]) {
40862306a36Sopenharmony_ci		err = br_mrp_ring_role_parse(br, tb[IFLA_BRIDGE_MRP_RING_ROLE],
40962306a36Sopenharmony_ci					     extack);
41062306a36Sopenharmony_ci		if (err)
41162306a36Sopenharmony_ci			return err;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_START_TEST]) {
41562306a36Sopenharmony_ci		err = br_mrp_start_test_parse(br,
41662306a36Sopenharmony_ci					      tb[IFLA_BRIDGE_MRP_START_TEST],
41762306a36Sopenharmony_ci					      extack);
41862306a36Sopenharmony_ci		if (err)
41962306a36Sopenharmony_ci			return err;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_IN_STATE]) {
42362306a36Sopenharmony_ci		err = br_mrp_in_state_parse(br, tb[IFLA_BRIDGE_MRP_IN_STATE],
42462306a36Sopenharmony_ci					    extack);
42562306a36Sopenharmony_ci		if (err)
42662306a36Sopenharmony_ci			return err;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_IN_ROLE]) {
43062306a36Sopenharmony_ci		err = br_mrp_in_role_parse(br, tb[IFLA_BRIDGE_MRP_IN_ROLE],
43162306a36Sopenharmony_ci					   extack);
43262306a36Sopenharmony_ci		if (err)
43362306a36Sopenharmony_ci			return err;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (tb[IFLA_BRIDGE_MRP_START_IN_TEST]) {
43762306a36Sopenharmony_ci		err = br_mrp_start_in_test_parse(br,
43862306a36Sopenharmony_ci						 tb[IFLA_BRIDGE_MRP_START_IN_TEST],
43962306a36Sopenharmony_ci						 extack);
44062306a36Sopenharmony_ci		if (err)
44162306a36Sopenharmony_ci			return err;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciint br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct nlattr *tb, *mrp_tb;
45062306a36Sopenharmony_ci	struct br_mrp *mrp;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	mrp_tb = nla_nest_start_noflag(skb, IFLA_BRIDGE_MRP);
45362306a36Sopenharmony_ci	if (!mrp_tb)
45462306a36Sopenharmony_ci		return -EMSGSIZE;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	hlist_for_each_entry_rcu(mrp, &br->mrp_list, list) {
45762306a36Sopenharmony_ci		struct net_bridge_port *p;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		tb = nla_nest_start_noflag(skb, IFLA_BRIDGE_MRP_INFO);
46062306a36Sopenharmony_ci		if (!tb)
46162306a36Sopenharmony_ci			goto nla_info_failure;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_RING_ID,
46462306a36Sopenharmony_ci				mrp->ring_id))
46562306a36Sopenharmony_ci			goto nla_put_failure;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		p = rcu_dereference(mrp->p_port);
46862306a36Sopenharmony_ci		if (p && nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_P_IFINDEX,
46962306a36Sopenharmony_ci				     p->dev->ifindex))
47062306a36Sopenharmony_ci			goto nla_put_failure;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		p = rcu_dereference(mrp->s_port);
47362306a36Sopenharmony_ci		if (p && nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_S_IFINDEX,
47462306a36Sopenharmony_ci				     p->dev->ifindex))
47562306a36Sopenharmony_ci			goto nla_put_failure;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		p = rcu_dereference(mrp->i_port);
47862306a36Sopenharmony_ci		if (p && nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_I_IFINDEX,
47962306a36Sopenharmony_ci				     p->dev->ifindex))
48062306a36Sopenharmony_ci			goto nla_put_failure;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		if (nla_put_u16(skb, IFLA_BRIDGE_MRP_INFO_PRIO,
48362306a36Sopenharmony_ci				mrp->prio))
48462306a36Sopenharmony_ci			goto nla_put_failure;
48562306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_RING_STATE,
48662306a36Sopenharmony_ci				mrp->ring_state))
48762306a36Sopenharmony_ci			goto nla_put_failure;
48862306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_RING_ROLE,
48962306a36Sopenharmony_ci				mrp->ring_role))
49062306a36Sopenharmony_ci			goto nla_put_failure;
49162306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL,
49262306a36Sopenharmony_ci				mrp->test_interval))
49362306a36Sopenharmony_ci			goto nla_put_failure;
49462306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS,
49562306a36Sopenharmony_ci				mrp->test_max_miss))
49662306a36Sopenharmony_ci			goto nla_put_failure;
49762306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_TEST_MONITOR,
49862306a36Sopenharmony_ci				mrp->test_monitor))
49962306a36Sopenharmony_ci			goto nla_put_failure;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_IN_STATE,
50262306a36Sopenharmony_ci				mrp->in_state))
50362306a36Sopenharmony_ci			goto nla_put_failure;
50462306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_IN_ROLE,
50562306a36Sopenharmony_ci				mrp->in_role))
50662306a36Sopenharmony_ci			goto nla_put_failure;
50762306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL,
50862306a36Sopenharmony_ci				mrp->in_test_interval))
50962306a36Sopenharmony_ci			goto nla_put_failure;
51062306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS,
51162306a36Sopenharmony_ci				mrp->in_test_max_miss))
51262306a36Sopenharmony_ci			goto nla_put_failure;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		nla_nest_end(skb, tb);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	nla_nest_end(skb, mrp_tb);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cinla_put_failure:
52162306a36Sopenharmony_ci	nla_nest_cancel(skb, tb);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cinla_info_failure:
52462306a36Sopenharmony_ci	nla_nest_cancel(skb, mrp_tb);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return -EMSGSIZE;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ciint br_mrp_ring_port_open(struct net_device *dev, u8 loc)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct net_bridge_port *p;
53262306a36Sopenharmony_ci	int err = 0;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	p = br_port_get_rcu(dev);
53562306a36Sopenharmony_ci	if (!p) {
53662306a36Sopenharmony_ci		err = -EINVAL;
53762306a36Sopenharmony_ci		goto out;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (loc)
54162306a36Sopenharmony_ci		p->flags |= BR_MRP_LOST_CONT;
54262306a36Sopenharmony_ci	else
54362306a36Sopenharmony_ci		p->flags &= ~BR_MRP_LOST_CONT;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	br_ifinfo_notify(RTM_NEWLINK, NULL, p);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciout:
54862306a36Sopenharmony_ci	return err;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ciint br_mrp_in_port_open(struct net_device *dev, u8 loc)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct net_bridge_port *p;
55462306a36Sopenharmony_ci	int err = 0;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	p = br_port_get_rcu(dev);
55762306a36Sopenharmony_ci	if (!p) {
55862306a36Sopenharmony_ci		err = -EINVAL;
55962306a36Sopenharmony_ci		goto out;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (loc)
56362306a36Sopenharmony_ci		p->flags |= BR_MRP_LOST_IN_CONT;
56462306a36Sopenharmony_ci	else
56562306a36Sopenharmony_ci		p->flags &= ~BR_MRP_LOST_IN_CONT;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	br_ifinfo_notify(RTM_NEWLINK, NULL, p);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ciout:
57062306a36Sopenharmony_ci	return err;
57162306a36Sopenharmony_ci}
572