xref: /openbmc/linux/net/hsr/hsr_netlink.c (revision 9c5d03d3)
10e7623bdSMurali Karicheri // SPDX-License-Identifier: GPL-2.0
270ebe4a4SArvid Brodin /* Copyright 2011-2014 Autronica Fire and Security AS
3f421436aSArvid Brodin  *
4f421436aSArvid Brodin  * Author(s):
570ebe4a4SArvid Brodin  *	2011-2014 Arvid Brodin, arvid.brodin@alten.se
6f421436aSArvid Brodin  *
78f4c0e01SMurali Karicheri  * Routines for handling Netlink messages for HSR and PRP.
8f421436aSArvid Brodin  */
9f421436aSArvid Brodin 
10f421436aSArvid Brodin #include "hsr_netlink.h"
11f421436aSArvid Brodin #include <linux/kernel.h>
12f421436aSArvid Brodin #include <net/rtnetlink.h>
13f421436aSArvid Brodin #include <net/genetlink.h>
14f421436aSArvid Brodin #include "hsr_main.h"
15f421436aSArvid Brodin #include "hsr_device.h"
16f421436aSArvid Brodin #include "hsr_framereg.h"
17f421436aSArvid Brodin 
18f421436aSArvid Brodin static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
19f421436aSArvid Brodin 	[IFLA_HSR_SLAVE1]		= { .type = NLA_U32 },
20f421436aSArvid Brodin 	[IFLA_HSR_SLAVE2]		= { .type = NLA_U32 },
21f421436aSArvid Brodin 	[IFLA_HSR_MULTICAST_SPEC]	= { .type = NLA_U8 },
22ee1c2797SPeter Heise 	[IFLA_HSR_VERSION]	= { .type = NLA_U8 },
23f9375729SPeter Heise 	[IFLA_HSR_SUPERVISION_ADDR]	= { .len = ETH_ALEN },
2498bf8362SArvid Brodin 	[IFLA_HSR_SEQ_NR]		= { .type = NLA_U16 },
258f4c0e01SMurali Karicheri 	[IFLA_HSR_PROTOCOL]		= { .type = NLA_U8 },
26f421436aSArvid Brodin };
27f421436aSArvid Brodin 
28f421436aSArvid Brodin /* Here, it seems a netdevice has already been allocated for us, and the
29f421436aSArvid Brodin  * hsr_dev_setup routine has been executed. Nice!
30f421436aSArvid Brodin  */
31f421436aSArvid Brodin static int hsr_newlink(struct net *src_net, struct net_device *dev,
327a3f4a18SMatthias Schiffer 		       struct nlattr *tb[], struct nlattr *data[],
337a3f4a18SMatthias Schiffer 		       struct netlink_ext_ack *extack)
34f421436aSArvid Brodin {
358f4c0e01SMurali Karicheri 	enum hsr_version proto_version;
368f4c0e01SMurali Karicheri 	unsigned char multicast_spec;
378f4c0e01SMurali Karicheri 	u8 proto = HSR_PROTOCOL_HSR;
38f421436aSArvid Brodin 	struct net_device *link[2];
39f421436aSArvid Brodin 
40a718dcc5SArvid Brodin 	if (!data) {
4113eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "No slave devices specified");
42a718dcc5SArvid Brodin 		return -EINVAL;
43a718dcc5SArvid Brodin 	}
44f421436aSArvid Brodin 	if (!data[IFLA_HSR_SLAVE1]) {
4513eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified");
46f421436aSArvid Brodin 		return -EINVAL;
47f421436aSArvid Brodin 	}
48d595b85aSMurali Karicheri 	link[0] = __dev_get_by_index(src_net,
49d595b85aSMurali Karicheri 				     nla_get_u32(data[IFLA_HSR_SLAVE1]));
5013eeb5feSTaehee Yoo 	if (!link[0]) {
5113eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist");
5213eeb5feSTaehee Yoo 		return -EINVAL;
5313eeb5feSTaehee Yoo 	}
54f421436aSArvid Brodin 	if (!data[IFLA_HSR_SLAVE2]) {
5513eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified");
56f421436aSArvid Brodin 		return -EINVAL;
57f421436aSArvid Brodin 	}
58d595b85aSMurali Karicheri 	link[1] = __dev_get_by_index(src_net,
59d595b85aSMurali Karicheri 				     nla_get_u32(data[IFLA_HSR_SLAVE2]));
6013eeb5feSTaehee Yoo 	if (!link[1]) {
6113eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist");
62f421436aSArvid Brodin 		return -EINVAL;
6313eeb5feSTaehee Yoo 	}
6413eeb5feSTaehee Yoo 
6513eeb5feSTaehee Yoo 	if (link[0] == link[1]) {
6613eeb5feSTaehee Yoo 		NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same");
6713eeb5feSTaehee Yoo 		return -EINVAL;
6813eeb5feSTaehee Yoo 	}
69f421436aSArvid Brodin 
70f421436aSArvid Brodin 	if (!data[IFLA_HSR_MULTICAST_SPEC])
71f421436aSArvid Brodin 		multicast_spec = 0;
72f421436aSArvid Brodin 	else
73f421436aSArvid Brodin 		multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
74f421436aSArvid Brodin 
758f4c0e01SMurali Karicheri 	if (data[IFLA_HSR_PROTOCOL])
768f4c0e01SMurali Karicheri 		proto = nla_get_u8(data[IFLA_HSR_PROTOCOL]);
778f4c0e01SMurali Karicheri 
788f4c0e01SMurali Karicheri 	if (proto >= HSR_PROTOCOL_MAX) {
79b87f9fe1SYe Bin 		NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol");
808f4c0e01SMurali Karicheri 		return -EINVAL;
818f4c0e01SMurali Karicheri 	}
828f4c0e01SMurali Karicheri 
834faab8c4STaehee Yoo 	if (!data[IFLA_HSR_VERSION]) {
848f4c0e01SMurali Karicheri 		proto_version = HSR_V0;
854faab8c4STaehee Yoo 	} else {
868f4c0e01SMurali Karicheri 		if (proto == HSR_PROTOCOL_PRP) {
87b87f9fe1SYe Bin 			NL_SET_ERR_MSG_MOD(extack, "PRP version unsupported");
888f4c0e01SMurali Karicheri 			return -EINVAL;
898f4c0e01SMurali Karicheri 		}
908f4c0e01SMurali Karicheri 
918f4c0e01SMurali Karicheri 		proto_version = nla_get_u8(data[IFLA_HSR_VERSION]);
928f4c0e01SMurali Karicheri 		if (proto_version > HSR_V1) {
934faab8c4STaehee Yoo 			NL_SET_ERR_MSG_MOD(extack,
94b87f9fe1SYe Bin 					   "Only HSR version 0/1 supported");
954faab8c4STaehee Yoo 			return -EINVAL;
964faab8c4STaehee Yoo 		}
974faab8c4STaehee Yoo 	}
98ee1c2797SPeter Heise 
998f4c0e01SMurali Karicheri 	if (proto == HSR_PROTOCOL_PRP)
1008f4c0e01SMurali Karicheri 		proto_version = PRP_V1;
1018f4c0e01SMurali Karicheri 
1028f4c0e01SMurali Karicheri 	return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack);
103f421436aSArvid Brodin }
104f421436aSArvid Brodin 
105de0083c7STaehee Yoo static void hsr_dellink(struct net_device *dev, struct list_head *head)
106de0083c7STaehee Yoo {
107de0083c7STaehee Yoo 	struct hsr_priv *hsr = netdev_priv(dev);
1084acc45dbSJuhee Kang 	int i;
109de0083c7STaehee Yoo 
110de0083c7STaehee Yoo 	del_timer_sync(&hsr->prune_timer);
111de0083c7STaehee Yoo 	del_timer_sync(&hsr->announce_timer);
112de0083c7STaehee Yoo 
113de0083c7STaehee Yoo 	hsr_debugfs_term(hsr);
114de0083c7STaehee Yoo 	hsr_del_ports(hsr);
115de0083c7STaehee Yoo 
116de0083c7STaehee Yoo 	hsr_del_self_node(hsr);
1174acc45dbSJuhee Kang 	for (i = 0; i < hsr->hash_buckets; i++)
1184acc45dbSJuhee Kang 		hsr_del_nodes(&hsr->node_db[i]);
119de0083c7STaehee Yoo 
120de0083c7STaehee Yoo 	unregister_netdevice_queue(dev, head);
121de0083c7STaehee Yoo }
122de0083c7STaehee Yoo 
12398bf8362SArvid Brodin static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
12498bf8362SArvid Brodin {
12581390d0cSTaehee Yoo 	struct hsr_priv *hsr = netdev_priv(dev);
1268f4c0e01SMurali Karicheri 	u8 proto = HSR_PROTOCOL_HSR;
127c5a75911SArvid Brodin 	struct hsr_port *port;
12898bf8362SArvid Brodin 
129c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
13081390d0cSTaehee Yoo 	if (port) {
13181390d0cSTaehee Yoo 		if (nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex))
13298bf8362SArvid Brodin 			goto nla_put_failure;
13381390d0cSTaehee Yoo 	}
13498bf8362SArvid Brodin 
135c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
13681390d0cSTaehee Yoo 	if (port) {
13781390d0cSTaehee Yoo 		if (nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex))
13898bf8362SArvid Brodin 			goto nla_put_failure;
13981390d0cSTaehee Yoo 	}
14098bf8362SArvid Brodin 
14198bf8362SArvid Brodin 	if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN,
14270ebe4a4SArvid Brodin 		    hsr->sup_multicast_addr) ||
14370ebe4a4SArvid Brodin 	    nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr))
14498bf8362SArvid Brodin 		goto nla_put_failure;
1458f4c0e01SMurali Karicheri 	if (hsr->prot_version == PRP_V1)
1468f4c0e01SMurali Karicheri 		proto = HSR_PROTOCOL_PRP;
1478f4c0e01SMurali Karicheri 	if (nla_put_u8(skb, IFLA_HSR_PROTOCOL, proto))
1488f4c0e01SMurali Karicheri 		goto nla_put_failure;
14998bf8362SArvid Brodin 
15098bf8362SArvid Brodin 	return 0;
15198bf8362SArvid Brodin 
15298bf8362SArvid Brodin nla_put_failure:
15398bf8362SArvid Brodin 	return -EMSGSIZE;
15498bf8362SArvid Brodin }
15598bf8362SArvid Brodin 
156f421436aSArvid Brodin static struct rtnl_link_ops hsr_link_ops __read_mostly = {
157f421436aSArvid Brodin 	.kind		= "hsr",
158f421436aSArvid Brodin 	.maxtype	= IFLA_HSR_MAX,
159f421436aSArvid Brodin 	.policy		= hsr_policy,
160f421436aSArvid Brodin 	.priv_size	= sizeof(struct hsr_priv),
161f421436aSArvid Brodin 	.setup		= hsr_dev_setup,
162f421436aSArvid Brodin 	.newlink	= hsr_newlink,
163de0083c7STaehee Yoo 	.dellink	= hsr_dellink,
16498bf8362SArvid Brodin 	.fill_info	= hsr_fill_info,
165f421436aSArvid Brodin };
166f421436aSArvid Brodin 
167f421436aSArvid Brodin /* attribute policy */
168f421436aSArvid Brodin static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
169f9375729SPeter Heise 	[HSR_A_NODE_ADDR] = { .len = ETH_ALEN },
170f9375729SPeter Heise 	[HSR_A_NODE_ADDR_B] = { .len = ETH_ALEN },
171f421436aSArvid Brodin 	[HSR_A_IFINDEX] = { .type = NLA_U32 },
172f421436aSArvid Brodin 	[HSR_A_IF1_AGE] = { .type = NLA_U32 },
173f421436aSArvid Brodin 	[HSR_A_IF2_AGE] = { .type = NLA_U32 },
174f421436aSArvid Brodin 	[HSR_A_IF1_SEQ] = { .type = NLA_U16 },
175f421436aSArvid Brodin 	[HSR_A_IF2_SEQ] = { .type = NLA_U16 },
176f421436aSArvid Brodin };
177f421436aSArvid Brodin 
178489111e5SJohannes Berg static struct genl_family hsr_genl_family;
179f421436aSArvid Brodin 
1802a94fe48SJohannes Berg static const struct genl_multicast_group hsr_mcgrps[] = {
1812a94fe48SJohannes Berg 	{ .name = "hsr-network", },
182f421436aSArvid Brodin };
183f421436aSArvid Brodin 
184f421436aSArvid Brodin /* This is called if for some node with MAC address addr, we only get frames
185f421436aSArvid Brodin  * over one of the slave interfaces. This would indicate an open network ring
186f421436aSArvid Brodin  * (i.e. a link has failed somewhere).
187f421436aSArvid Brodin  */
18870ebe4a4SArvid Brodin void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN],
189c5a75911SArvid Brodin 		      struct hsr_port *port)
190f421436aSArvid Brodin {
191f421436aSArvid Brodin 	struct sk_buff *skb;
192f421436aSArvid Brodin 	void *msg_head;
193c5a75911SArvid Brodin 	struct hsr_port *master;
194f421436aSArvid Brodin 	int res;
195f421436aSArvid Brodin 
196f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
197f421436aSArvid Brodin 	if (!skb)
198f421436aSArvid Brodin 		goto fail;
199f421436aSArvid Brodin 
200d595b85aSMurali Karicheri 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0,
201d595b85aSMurali Karicheri 			       HSR_C_RING_ERROR);
202f421436aSArvid Brodin 	if (!msg_head)
203f421436aSArvid Brodin 		goto nla_put_failure;
204f421436aSArvid Brodin 
205f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
206f421436aSArvid Brodin 	if (res < 0)
207f421436aSArvid Brodin 		goto nla_put_failure;
208f421436aSArvid Brodin 
209c5a75911SArvid Brodin 	res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex);
210f421436aSArvid Brodin 	if (res < 0)
211f421436aSArvid Brodin 		goto nla_put_failure;
212f421436aSArvid Brodin 
213f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
2142a94fe48SJohannes Berg 	genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
215f421436aSArvid Brodin 
216f421436aSArvid Brodin 	return;
217f421436aSArvid Brodin 
218f421436aSArvid Brodin nla_put_failure:
219f421436aSArvid Brodin 	kfree_skb(skb);
220f421436aSArvid Brodin 
221f421436aSArvid Brodin fail:
222c5a75911SArvid Brodin 	rcu_read_lock();
223c5a75911SArvid Brodin 	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
224c5a75911SArvid Brodin 	netdev_warn(master->dev, "Could not send HSR ring error message\n");
225c5a75911SArvid Brodin 	rcu_read_unlock();
226f421436aSArvid Brodin }
227f421436aSArvid Brodin 
228f421436aSArvid Brodin /* This is called when we haven't heard from the node with MAC address addr for
229f421436aSArvid Brodin  * some time (just before the node is removed from the node table/list).
230f421436aSArvid Brodin  */
23170ebe4a4SArvid Brodin void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN])
232f421436aSArvid Brodin {
233f421436aSArvid Brodin 	struct sk_buff *skb;
234f421436aSArvid Brodin 	void *msg_head;
235c5a75911SArvid Brodin 	struct hsr_port *master;
236f421436aSArvid Brodin 	int res;
237f421436aSArvid Brodin 
238f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
239f421436aSArvid Brodin 	if (!skb)
240f421436aSArvid Brodin 		goto fail;
241f421436aSArvid Brodin 
242f421436aSArvid Brodin 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN);
243f421436aSArvid Brodin 	if (!msg_head)
244f421436aSArvid Brodin 		goto nla_put_failure;
245f421436aSArvid Brodin 
246f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
247f421436aSArvid Brodin 	if (res < 0)
248f421436aSArvid Brodin 		goto nla_put_failure;
249f421436aSArvid Brodin 
250f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
2512a94fe48SJohannes Berg 	genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
252f421436aSArvid Brodin 
253f421436aSArvid Brodin 	return;
254f421436aSArvid Brodin 
255f421436aSArvid Brodin nla_put_failure:
256f421436aSArvid Brodin 	kfree_skb(skb);
257f421436aSArvid Brodin 
258f421436aSArvid Brodin fail:
259c5a75911SArvid Brodin 	rcu_read_lock();
260c5a75911SArvid Brodin 	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
261c5a75911SArvid Brodin 	netdev_warn(master->dev, "Could not send HSR node down\n");
262c5a75911SArvid Brodin 	rcu_read_unlock();
263f421436aSArvid Brodin }
264f421436aSArvid Brodin 
265f421436aSArvid Brodin /* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table
266f421436aSArvid Brodin  * about the status of a specific node in the network, defined by its MAC
267f421436aSArvid Brodin  * address.
268f421436aSArvid Brodin  *
269f421436aSArvid Brodin  * Input: hsr ifindex, node mac address
270f421436aSArvid Brodin  * Output: hsr ifindex, node mac address (copied from request),
271f421436aSArvid Brodin  *	   age of latest frame from node over slave 1, slave 2 [ms]
272f421436aSArvid Brodin  */
273f421436aSArvid Brodin static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
274f421436aSArvid Brodin {
275f421436aSArvid Brodin 	/* For receiving */
276f421436aSArvid Brodin 	struct nlattr *na;
277c5a75911SArvid Brodin 	struct net_device *hsr_dev;
278f421436aSArvid Brodin 
279f421436aSArvid Brodin 	/* For sending */
280f421436aSArvid Brodin 	struct sk_buff *skb_out;
281f421436aSArvid Brodin 	void *msg_head;
28270ebe4a4SArvid Brodin 	struct hsr_priv *hsr;
283c5a75911SArvid Brodin 	struct hsr_port *port;
284f421436aSArvid Brodin 	unsigned char hsr_node_addr_b[ETH_ALEN];
285f421436aSArvid Brodin 	int hsr_node_if1_age;
286f421436aSArvid Brodin 	u16 hsr_node_if1_seq;
287f421436aSArvid Brodin 	int hsr_node_if2_age;
288f421436aSArvid Brodin 	u16 hsr_node_if2_seq;
289f421436aSArvid Brodin 	int addr_b_ifindex;
290f421436aSArvid Brodin 	int res;
291f421436aSArvid Brodin 
292f421436aSArvid Brodin 	if (!info)
293f421436aSArvid Brodin 		goto invalid;
294f421436aSArvid Brodin 
295f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
296f421436aSArvid Brodin 	if (!na)
297f421436aSArvid Brodin 		goto invalid;
298f421436aSArvid Brodin 	na = info->attrs[HSR_A_NODE_ADDR];
299f421436aSArvid Brodin 	if (!na)
300f421436aSArvid Brodin 		goto invalid;
301f421436aSArvid Brodin 
302173756b8STaehee Yoo 	rcu_read_lock();
303173756b8STaehee Yoo 	hsr_dev = dev_get_by_index_rcu(genl_info_net(info),
304f421436aSArvid Brodin 				       nla_get_u32(info->attrs[HSR_A_IFINDEX]));
305f421436aSArvid Brodin 	if (!hsr_dev)
306173756b8STaehee Yoo 		goto rcu_unlock;
307f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
308173756b8STaehee Yoo 		goto rcu_unlock;
309f421436aSArvid Brodin 
310f421436aSArvid Brodin 	/* Send reply */
311173756b8STaehee Yoo 	skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
312f421436aSArvid Brodin 	if (!skb_out) {
313f421436aSArvid Brodin 		res = -ENOMEM;
314f421436aSArvid Brodin 		goto fail;
315f421436aSArvid Brodin 	}
316f421436aSArvid Brodin 
317f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
318f421436aSArvid Brodin 			       info->snd_seq, &hsr_genl_family, 0,
319f421436aSArvid Brodin 			       HSR_C_SET_NODE_STATUS);
320f421436aSArvid Brodin 	if (!msg_head) {
321f421436aSArvid Brodin 		res = -ENOMEM;
322f421436aSArvid Brodin 		goto nla_put_failure;
323f421436aSArvid Brodin 	}
324f421436aSArvid Brodin 
325f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
326f421436aSArvid Brodin 	if (res < 0)
327f421436aSArvid Brodin 		goto nla_put_failure;
328f421436aSArvid Brodin 
32970ebe4a4SArvid Brodin 	hsr = netdev_priv(hsr_dev);
33070ebe4a4SArvid Brodin 	res = hsr_get_node_data(hsr,
331d595b85aSMurali Karicheri 				(unsigned char *)
332d595b85aSMurali Karicheri 				nla_data(info->attrs[HSR_A_NODE_ADDR]),
333f421436aSArvid Brodin 					 hsr_node_addr_b,
334f421436aSArvid Brodin 					 &addr_b_ifindex,
335f421436aSArvid Brodin 					 &hsr_node_if1_age,
336f421436aSArvid Brodin 					 &hsr_node_if1_seq,
337f421436aSArvid Brodin 					 &hsr_node_if2_age,
338f421436aSArvid Brodin 					 &hsr_node_if2_seq);
339f421436aSArvid Brodin 	if (res < 0)
34084a035f6SGeyslan G. Bem 		goto nla_put_failure;
341f421436aSArvid Brodin 
342f421436aSArvid Brodin 	res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
343f421436aSArvid Brodin 		      nla_data(info->attrs[HSR_A_NODE_ADDR]));
344f421436aSArvid Brodin 	if (res < 0)
345f421436aSArvid Brodin 		goto nla_put_failure;
346f421436aSArvid Brodin 
347f421436aSArvid Brodin 	if (addr_b_ifindex > -1) {
348f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN,
349f421436aSArvid Brodin 			      hsr_node_addr_b);
350f421436aSArvid Brodin 		if (res < 0)
351f421436aSArvid Brodin 			goto nla_put_failure;
352f421436aSArvid Brodin 
353d595b85aSMurali Karicheri 		res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX,
354d595b85aSMurali Karicheri 				  addr_b_ifindex);
355f421436aSArvid Brodin 		if (res < 0)
356f421436aSArvid Brodin 			goto nla_put_failure;
357f421436aSArvid Brodin 	}
358f421436aSArvid Brodin 
359f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age);
360f421436aSArvid Brodin 	if (res < 0)
361f421436aSArvid Brodin 		goto nla_put_failure;
362f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
363f421436aSArvid Brodin 	if (res < 0)
364f421436aSArvid Brodin 		goto nla_put_failure;
365c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
366c5a75911SArvid Brodin 	if (port)
367c5a75911SArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
368c5a75911SArvid Brodin 				  port->dev->ifindex);
369f421436aSArvid Brodin 	if (res < 0)
370f421436aSArvid Brodin 		goto nla_put_failure;
371f421436aSArvid Brodin 
372f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age);
373f421436aSArvid Brodin 	if (res < 0)
374f421436aSArvid Brodin 		goto nla_put_failure;
375f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
376f421436aSArvid Brodin 	if (res < 0)
377f421436aSArvid Brodin 		goto nla_put_failure;
378c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
379c5a75911SArvid Brodin 	if (port)
380c5a75911SArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
381c5a75911SArvid Brodin 				  port->dev->ifindex);
38251f3c605SArvid Brodin 	if (res < 0)
38351f3c605SArvid Brodin 		goto nla_put_failure;
384f421436aSArvid Brodin 
385173756b8STaehee Yoo 	rcu_read_unlock();
386173756b8STaehee Yoo 
387f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
388f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
389f421436aSArvid Brodin 
390f421436aSArvid Brodin 	return 0;
391f421436aSArvid Brodin 
392173756b8STaehee Yoo rcu_unlock:
393173756b8STaehee Yoo 	rcu_read_unlock();
394f421436aSArvid Brodin invalid:
3952d4bc933SJohannes Berg 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
396f421436aSArvid Brodin 	return 0;
397f421436aSArvid Brodin 
398f421436aSArvid Brodin nla_put_failure:
399f421436aSArvid Brodin 	kfree_skb(skb_out);
400f421436aSArvid Brodin 	/* Fall through */
401f421436aSArvid Brodin 
402f421436aSArvid Brodin fail:
403173756b8STaehee Yoo 	rcu_read_unlock();
404f421436aSArvid Brodin 	return res;
405f421436aSArvid Brodin }
406f421436aSArvid Brodin 
407f266a683SArvid Brodin /* Get a list of MacAddressA of all nodes known to this node (including self).
408f421436aSArvid Brodin  */
409f421436aSArvid Brodin static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
410f421436aSArvid Brodin {
411f421436aSArvid Brodin 	unsigned char addr[ETH_ALEN];
412ca19c70fSTaehee Yoo 	struct net_device *hsr_dev;
413ca19c70fSTaehee Yoo 	struct sk_buff *skb_out;
414ca19c70fSTaehee Yoo 	struct hsr_priv *hsr;
415ca19c70fSTaehee Yoo 	bool restart = false;
416ca19c70fSTaehee Yoo 	struct nlattr *na;
417ca19c70fSTaehee Yoo 	void *pos = NULL;
418ca19c70fSTaehee Yoo 	void *msg_head;
419f421436aSArvid Brodin 	int res;
420f421436aSArvid Brodin 
421f421436aSArvid Brodin 	if (!info)
422f421436aSArvid Brodin 		goto invalid;
423f421436aSArvid Brodin 
424f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
425f421436aSArvid Brodin 	if (!na)
426f421436aSArvid Brodin 		goto invalid;
427f421436aSArvid Brodin 
428173756b8STaehee Yoo 	rcu_read_lock();
429173756b8STaehee Yoo 	hsr_dev = dev_get_by_index_rcu(genl_info_net(info),
430f421436aSArvid Brodin 				       nla_get_u32(info->attrs[HSR_A_IFINDEX]));
431f421436aSArvid Brodin 	if (!hsr_dev)
432173756b8STaehee Yoo 		goto rcu_unlock;
433f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
434173756b8STaehee Yoo 		goto rcu_unlock;
435f421436aSArvid Brodin 
436ca19c70fSTaehee Yoo restart:
437f421436aSArvid Brodin 	/* Send reply */
438ca19c70fSTaehee Yoo 	skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
439f421436aSArvid Brodin 	if (!skb_out) {
440f421436aSArvid Brodin 		res = -ENOMEM;
441f421436aSArvid Brodin 		goto fail;
442f421436aSArvid Brodin 	}
443f421436aSArvid Brodin 
444f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
445f421436aSArvid Brodin 			       info->snd_seq, &hsr_genl_family, 0,
446f421436aSArvid Brodin 			       HSR_C_SET_NODE_LIST);
447f421436aSArvid Brodin 	if (!msg_head) {
448f421436aSArvid Brodin 		res = -ENOMEM;
449f421436aSArvid Brodin 		goto nla_put_failure;
450f421436aSArvid Brodin 	}
451f421436aSArvid Brodin 
452ca19c70fSTaehee Yoo 	if (!restart) {
453f421436aSArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
454f421436aSArvid Brodin 		if (res < 0)
455f421436aSArvid Brodin 			goto nla_put_failure;
456ca19c70fSTaehee Yoo 	}
457f421436aSArvid Brodin 
45870ebe4a4SArvid Brodin 	hsr = netdev_priv(hsr_dev);
459f421436aSArvid Brodin 
460ca19c70fSTaehee Yoo 	if (!pos)
46170ebe4a4SArvid Brodin 		pos = hsr_get_next_node(hsr, NULL, addr);
462f421436aSArvid Brodin 	while (pos) {
463f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
464f421436aSArvid Brodin 		if (res < 0) {
465ca19c70fSTaehee Yoo 			if (res == -EMSGSIZE) {
466ca19c70fSTaehee Yoo 				genlmsg_end(skb_out, msg_head);
467ca19c70fSTaehee Yoo 				genlmsg_unicast(genl_info_net(info), skb_out,
468ca19c70fSTaehee Yoo 						info->snd_portid);
469ca19c70fSTaehee Yoo 				restart = true;
470ca19c70fSTaehee Yoo 				goto restart;
471ca19c70fSTaehee Yoo 			}
472f421436aSArvid Brodin 			goto nla_put_failure;
473f421436aSArvid Brodin 		}
47470ebe4a4SArvid Brodin 		pos = hsr_get_next_node(hsr, pos, addr);
475f421436aSArvid Brodin 	}
476f421436aSArvid Brodin 	rcu_read_unlock();
477f421436aSArvid Brodin 
478f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
479f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
480f421436aSArvid Brodin 
481f421436aSArvid Brodin 	return 0;
482f421436aSArvid Brodin 
483173756b8STaehee Yoo rcu_unlock:
484173756b8STaehee Yoo 	rcu_read_unlock();
485f421436aSArvid Brodin invalid:
4862d4bc933SJohannes Berg 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
487f421436aSArvid Brodin 	return 0;
488f421436aSArvid Brodin 
489f421436aSArvid Brodin nla_put_failure:
490ca19c70fSTaehee Yoo 	nlmsg_free(skb_out);
491f421436aSArvid Brodin 	/* Fall through */
492f421436aSArvid Brodin 
493f421436aSArvid Brodin fail:
494173756b8STaehee Yoo 	rcu_read_unlock();
495f421436aSArvid Brodin 	return res;
496f421436aSArvid Brodin }
497f421436aSArvid Brodin 
49866a9b928SJakub Kicinski static const struct genl_small_ops hsr_ops[] = {
4999504b3eeSJohannes Berg 	{
5009504b3eeSJohannes Berg 		.cmd = HSR_C_GET_NODE_STATUS,
501ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5029504b3eeSJohannes Berg 		.flags = 0,
5039504b3eeSJohannes Berg 		.doit = hsr_get_node_status,
5049504b3eeSJohannes Berg 		.dumpit = NULL,
5059504b3eeSJohannes Berg 	},
5069504b3eeSJohannes Berg 	{
507f421436aSArvid Brodin 		.cmd = HSR_C_GET_NODE_LIST,
508ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
509f421436aSArvid Brodin 		.flags = 0,
510f421436aSArvid Brodin 		.doit = hsr_get_node_list,
511f421436aSArvid Brodin 		.dumpit = NULL,
5129504b3eeSJohannes Berg 	},
513f421436aSArvid Brodin };
514f421436aSArvid Brodin 
51556989f6dSJohannes Berg static struct genl_family hsr_genl_family __ro_after_init = {
516489111e5SJohannes Berg 	.hdrsize = 0,
517489111e5SJohannes Berg 	.name = "HSR",
518489111e5SJohannes Berg 	.version = 1,
519489111e5SJohannes Berg 	.maxattr = HSR_A_MAX,
5203b0f31f2SJohannes Berg 	.policy = hsr_genl_policy,
52109e91dbeSTaehee Yoo 	.netnsok = true,
522489111e5SJohannes Berg 	.module = THIS_MODULE,
52366a9b928SJakub Kicinski 	.small_ops = hsr_ops,
52466a9b928SJakub Kicinski 	.n_small_ops = ARRAY_SIZE(hsr_ops),
525*9c5d03d3SJakub Kicinski 	.resv_start_op = HSR_C_SET_NODE_LIST + 1,
526489111e5SJohannes Berg 	.mcgrps = hsr_mcgrps,
527489111e5SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(hsr_mcgrps),
528489111e5SJohannes Berg };
529489111e5SJohannes Berg 
530f421436aSArvid Brodin int __init hsr_netlink_init(void)
531f421436aSArvid Brodin {
532f421436aSArvid Brodin 	int rc;
533f421436aSArvid Brodin 
534f421436aSArvid Brodin 	rc = rtnl_link_register(&hsr_link_ops);
535f421436aSArvid Brodin 	if (rc)
536f421436aSArvid Brodin 		goto fail_rtnl_link_register;
537f421436aSArvid Brodin 
538489111e5SJohannes Berg 	rc = genl_register_family(&hsr_genl_family);
539f421436aSArvid Brodin 	if (rc)
540f421436aSArvid Brodin 		goto fail_genl_register_family;
541f421436aSArvid Brodin 
542c6c4ccd7STaehee Yoo 	hsr_debugfs_create_root();
543f421436aSArvid Brodin 	return 0;
544f421436aSArvid Brodin 
545f421436aSArvid Brodin fail_genl_register_family:
546f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
547f421436aSArvid Brodin fail_rtnl_link_register:
548f421436aSArvid Brodin 
549f421436aSArvid Brodin 	return rc;
550f421436aSArvid Brodin }
551f421436aSArvid Brodin 
552f421436aSArvid Brodin void __exit hsr_netlink_exit(void)
553f421436aSArvid Brodin {
554f421436aSArvid Brodin 	genl_unregister_family(&hsr_genl_family);
555f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
556f421436aSArvid Brodin }
557f421436aSArvid Brodin 
558f421436aSArvid Brodin MODULE_ALIAS_RTNL_LINK("hsr");
559