xref: /openbmc/linux/net/hsr/hsr_netlink.c (revision e012764c)
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  */
hsr_newlink(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)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 
hsr_dellink(struct net_device * dev,struct list_head * head)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);
108de0083c7STaehee Yoo 
109de0083c7STaehee Yoo 	del_timer_sync(&hsr->prune_timer);
110de0083c7STaehee Yoo 	del_timer_sync(&hsr->announce_timer);
111de0083c7STaehee Yoo 
112de0083c7STaehee Yoo 	hsr_debugfs_term(hsr);
113de0083c7STaehee Yoo 	hsr_del_ports(hsr);
114de0083c7STaehee Yoo 
115de0083c7STaehee Yoo 	hsr_del_self_node(hsr);
116*e012764cSSebastian Andrzej Siewior 	hsr_del_nodes(&hsr->node_db);
117de0083c7STaehee Yoo 
118de0083c7STaehee Yoo 	unregister_netdevice_queue(dev, head);
119de0083c7STaehee Yoo }
120de0083c7STaehee Yoo 
hsr_fill_info(struct sk_buff * skb,const struct net_device * dev)12198bf8362SArvid Brodin static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
12298bf8362SArvid Brodin {
12381390d0cSTaehee Yoo 	struct hsr_priv *hsr = netdev_priv(dev);
1248f4c0e01SMurali Karicheri 	u8 proto = HSR_PROTOCOL_HSR;
125c5a75911SArvid Brodin 	struct hsr_port *port;
12698bf8362SArvid Brodin 
127c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
12881390d0cSTaehee Yoo 	if (port) {
12981390d0cSTaehee Yoo 		if (nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex))
13098bf8362SArvid Brodin 			goto nla_put_failure;
13181390d0cSTaehee Yoo 	}
13298bf8362SArvid Brodin 
133c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
13481390d0cSTaehee Yoo 	if (port) {
13581390d0cSTaehee Yoo 		if (nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex))
13698bf8362SArvid Brodin 			goto nla_put_failure;
13781390d0cSTaehee Yoo 	}
13898bf8362SArvid Brodin 
13998bf8362SArvid Brodin 	if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN,
14070ebe4a4SArvid Brodin 		    hsr->sup_multicast_addr) ||
14170ebe4a4SArvid Brodin 	    nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr))
14298bf8362SArvid Brodin 		goto nla_put_failure;
1438f4c0e01SMurali Karicheri 	if (hsr->prot_version == PRP_V1)
1448f4c0e01SMurali Karicheri 		proto = HSR_PROTOCOL_PRP;
1458f4c0e01SMurali Karicheri 	if (nla_put_u8(skb, IFLA_HSR_PROTOCOL, proto))
1468f4c0e01SMurali Karicheri 		goto nla_put_failure;
14798bf8362SArvid Brodin 
14898bf8362SArvid Brodin 	return 0;
14998bf8362SArvid Brodin 
15098bf8362SArvid Brodin nla_put_failure:
15198bf8362SArvid Brodin 	return -EMSGSIZE;
15298bf8362SArvid Brodin }
15398bf8362SArvid Brodin 
154f421436aSArvid Brodin static struct rtnl_link_ops hsr_link_ops __read_mostly = {
155f421436aSArvid Brodin 	.kind		= "hsr",
156f421436aSArvid Brodin 	.maxtype	= IFLA_HSR_MAX,
157f421436aSArvid Brodin 	.policy		= hsr_policy,
158f421436aSArvid Brodin 	.priv_size	= sizeof(struct hsr_priv),
159f421436aSArvid Brodin 	.setup		= hsr_dev_setup,
160f421436aSArvid Brodin 	.newlink	= hsr_newlink,
161de0083c7STaehee Yoo 	.dellink	= hsr_dellink,
16298bf8362SArvid Brodin 	.fill_info	= hsr_fill_info,
163f421436aSArvid Brodin };
164f421436aSArvid Brodin 
165f421436aSArvid Brodin /* attribute policy */
166f421436aSArvid Brodin static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
167f9375729SPeter Heise 	[HSR_A_NODE_ADDR] = { .len = ETH_ALEN },
168f9375729SPeter Heise 	[HSR_A_NODE_ADDR_B] = { .len = ETH_ALEN },
169f421436aSArvid Brodin 	[HSR_A_IFINDEX] = { .type = NLA_U32 },
170f421436aSArvid Brodin 	[HSR_A_IF1_AGE] = { .type = NLA_U32 },
171f421436aSArvid Brodin 	[HSR_A_IF2_AGE] = { .type = NLA_U32 },
172f421436aSArvid Brodin 	[HSR_A_IF1_SEQ] = { .type = NLA_U16 },
173f421436aSArvid Brodin 	[HSR_A_IF2_SEQ] = { .type = NLA_U16 },
174f421436aSArvid Brodin };
175f421436aSArvid Brodin 
176489111e5SJohannes Berg static struct genl_family hsr_genl_family;
177f421436aSArvid Brodin 
1782a94fe48SJohannes Berg static const struct genl_multicast_group hsr_mcgrps[] = {
1792a94fe48SJohannes Berg 	{ .name = "hsr-network", },
180f421436aSArvid Brodin };
181f421436aSArvid Brodin 
182f421436aSArvid Brodin /* This is called if for some node with MAC address addr, we only get frames
183f421436aSArvid Brodin  * over one of the slave interfaces. This would indicate an open network ring
184f421436aSArvid Brodin  * (i.e. a link has failed somewhere).
185f421436aSArvid Brodin  */
hsr_nl_ringerror(struct hsr_priv * hsr,unsigned char addr[ETH_ALEN],struct hsr_port * port)18670ebe4a4SArvid Brodin void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN],
187c5a75911SArvid Brodin 		      struct hsr_port *port)
188f421436aSArvid Brodin {
189f421436aSArvid Brodin 	struct sk_buff *skb;
190f421436aSArvid Brodin 	void *msg_head;
191c5a75911SArvid Brodin 	struct hsr_port *master;
192f421436aSArvid Brodin 	int res;
193f421436aSArvid Brodin 
194f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
195f421436aSArvid Brodin 	if (!skb)
196f421436aSArvid Brodin 		goto fail;
197f421436aSArvid Brodin 
198d595b85aSMurali Karicheri 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0,
199d595b85aSMurali Karicheri 			       HSR_C_RING_ERROR);
200f421436aSArvid Brodin 	if (!msg_head)
201f421436aSArvid Brodin 		goto nla_put_failure;
202f421436aSArvid Brodin 
203f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
204f421436aSArvid Brodin 	if (res < 0)
205f421436aSArvid Brodin 		goto nla_put_failure;
206f421436aSArvid Brodin 
207c5a75911SArvid Brodin 	res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex);
208f421436aSArvid Brodin 	if (res < 0)
209f421436aSArvid Brodin 		goto nla_put_failure;
210f421436aSArvid Brodin 
211f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
2122a94fe48SJohannes Berg 	genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
213f421436aSArvid Brodin 
214f421436aSArvid Brodin 	return;
215f421436aSArvid Brodin 
216f421436aSArvid Brodin nla_put_failure:
217f421436aSArvid Brodin 	kfree_skb(skb);
218f421436aSArvid Brodin 
219f421436aSArvid Brodin fail:
220c5a75911SArvid Brodin 	rcu_read_lock();
221c5a75911SArvid Brodin 	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
222c5a75911SArvid Brodin 	netdev_warn(master->dev, "Could not send HSR ring error message\n");
223c5a75911SArvid Brodin 	rcu_read_unlock();
224f421436aSArvid Brodin }
225f421436aSArvid Brodin 
226f421436aSArvid Brodin /* This is called when we haven't heard from the node with MAC address addr for
227f421436aSArvid Brodin  * some time (just before the node is removed from the node table/list).
228f421436aSArvid Brodin  */
hsr_nl_nodedown(struct hsr_priv * hsr,unsigned char addr[ETH_ALEN])22970ebe4a4SArvid Brodin void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN])
230f421436aSArvid Brodin {
231f421436aSArvid Brodin 	struct sk_buff *skb;
232f421436aSArvid Brodin 	void *msg_head;
233c5a75911SArvid Brodin 	struct hsr_port *master;
234f421436aSArvid Brodin 	int res;
235f421436aSArvid Brodin 
236f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
237f421436aSArvid Brodin 	if (!skb)
238f421436aSArvid Brodin 		goto fail;
239f421436aSArvid Brodin 
240f421436aSArvid Brodin 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN);
241f421436aSArvid Brodin 	if (!msg_head)
242f421436aSArvid Brodin 		goto nla_put_failure;
243f421436aSArvid Brodin 
244f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
245f421436aSArvid Brodin 	if (res < 0)
246f421436aSArvid Brodin 		goto nla_put_failure;
247f421436aSArvid Brodin 
248f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
2492a94fe48SJohannes Berg 	genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
250f421436aSArvid Brodin 
251f421436aSArvid Brodin 	return;
252f421436aSArvid Brodin 
253f421436aSArvid Brodin nla_put_failure:
254f421436aSArvid Brodin 	kfree_skb(skb);
255f421436aSArvid Brodin 
256f421436aSArvid Brodin fail:
257c5a75911SArvid Brodin 	rcu_read_lock();
258c5a75911SArvid Brodin 	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
259c5a75911SArvid Brodin 	netdev_warn(master->dev, "Could not send HSR node down\n");
260c5a75911SArvid Brodin 	rcu_read_unlock();
261f421436aSArvid Brodin }
262f421436aSArvid Brodin 
263f421436aSArvid Brodin /* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table
264f421436aSArvid Brodin  * about the status of a specific node in the network, defined by its MAC
265f421436aSArvid Brodin  * address.
266f421436aSArvid Brodin  *
267f421436aSArvid Brodin  * Input: hsr ifindex, node mac address
268f421436aSArvid Brodin  * Output: hsr ifindex, node mac address (copied from request),
269f421436aSArvid Brodin  *	   age of latest frame from node over slave 1, slave 2 [ms]
270f421436aSArvid Brodin  */
hsr_get_node_status(struct sk_buff * skb_in,struct genl_info * info)271f421436aSArvid Brodin static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
272f421436aSArvid Brodin {
273f421436aSArvid Brodin 	/* For receiving */
274f421436aSArvid Brodin 	struct nlattr *na;
275c5a75911SArvid Brodin 	struct net_device *hsr_dev;
276f421436aSArvid Brodin 
277f421436aSArvid Brodin 	/* For sending */
278f421436aSArvid Brodin 	struct sk_buff *skb_out;
279f421436aSArvid Brodin 	void *msg_head;
28070ebe4a4SArvid Brodin 	struct hsr_priv *hsr;
281c5a75911SArvid Brodin 	struct hsr_port *port;
282f421436aSArvid Brodin 	unsigned char hsr_node_addr_b[ETH_ALEN];
283f421436aSArvid Brodin 	int hsr_node_if1_age;
284f421436aSArvid Brodin 	u16 hsr_node_if1_seq;
285f421436aSArvid Brodin 	int hsr_node_if2_age;
286f421436aSArvid Brodin 	u16 hsr_node_if2_seq;
287f421436aSArvid Brodin 	int addr_b_ifindex;
288f421436aSArvid Brodin 	int res;
289f421436aSArvid Brodin 
290f421436aSArvid Brodin 	if (!info)
291f421436aSArvid Brodin 		goto invalid;
292f421436aSArvid Brodin 
293f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
294f421436aSArvid Brodin 	if (!na)
295f421436aSArvid Brodin 		goto invalid;
296f421436aSArvid Brodin 	na = info->attrs[HSR_A_NODE_ADDR];
297f421436aSArvid Brodin 	if (!na)
298f421436aSArvid Brodin 		goto invalid;
299f421436aSArvid Brodin 
300173756b8STaehee Yoo 	rcu_read_lock();
301173756b8STaehee Yoo 	hsr_dev = dev_get_by_index_rcu(genl_info_net(info),
302f421436aSArvid Brodin 				       nla_get_u32(info->attrs[HSR_A_IFINDEX]));
303f421436aSArvid Brodin 	if (!hsr_dev)
304173756b8STaehee Yoo 		goto rcu_unlock;
305f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
306173756b8STaehee Yoo 		goto rcu_unlock;
307f421436aSArvid Brodin 
308f421436aSArvid Brodin 	/* Send reply */
309173756b8STaehee Yoo 	skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
310f421436aSArvid Brodin 	if (!skb_out) {
311f421436aSArvid Brodin 		res = -ENOMEM;
312f421436aSArvid Brodin 		goto fail;
313f421436aSArvid Brodin 	}
314f421436aSArvid Brodin 
315f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
316f421436aSArvid Brodin 			       info->snd_seq, &hsr_genl_family, 0,
317f421436aSArvid Brodin 			       HSR_C_SET_NODE_STATUS);
318f421436aSArvid Brodin 	if (!msg_head) {
319f421436aSArvid Brodin 		res = -ENOMEM;
320f421436aSArvid Brodin 		goto nla_put_failure;
321f421436aSArvid Brodin 	}
322f421436aSArvid Brodin 
323f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
324f421436aSArvid Brodin 	if (res < 0)
325f421436aSArvid Brodin 		goto nla_put_failure;
326f421436aSArvid Brodin 
32770ebe4a4SArvid Brodin 	hsr = netdev_priv(hsr_dev);
32870ebe4a4SArvid Brodin 	res = hsr_get_node_data(hsr,
329d595b85aSMurali Karicheri 				(unsigned char *)
330d595b85aSMurali Karicheri 				nla_data(info->attrs[HSR_A_NODE_ADDR]),
331f421436aSArvid Brodin 					 hsr_node_addr_b,
332f421436aSArvid Brodin 					 &addr_b_ifindex,
333f421436aSArvid Brodin 					 &hsr_node_if1_age,
334f421436aSArvid Brodin 					 &hsr_node_if1_seq,
335f421436aSArvid Brodin 					 &hsr_node_if2_age,
336f421436aSArvid Brodin 					 &hsr_node_if2_seq);
337f421436aSArvid Brodin 	if (res < 0)
33884a035f6SGeyslan G. Bem 		goto nla_put_failure;
339f421436aSArvid Brodin 
340f421436aSArvid Brodin 	res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
341f421436aSArvid Brodin 		      nla_data(info->attrs[HSR_A_NODE_ADDR]));
342f421436aSArvid Brodin 	if (res < 0)
343f421436aSArvid Brodin 		goto nla_put_failure;
344f421436aSArvid Brodin 
345f421436aSArvid Brodin 	if (addr_b_ifindex > -1) {
346f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN,
347f421436aSArvid Brodin 			      hsr_node_addr_b);
348f421436aSArvid Brodin 		if (res < 0)
349f421436aSArvid Brodin 			goto nla_put_failure;
350f421436aSArvid Brodin 
351d595b85aSMurali Karicheri 		res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX,
352d595b85aSMurali Karicheri 				  addr_b_ifindex);
353f421436aSArvid Brodin 		if (res < 0)
354f421436aSArvid Brodin 			goto nla_put_failure;
355f421436aSArvid Brodin 	}
356f421436aSArvid Brodin 
357f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age);
358f421436aSArvid Brodin 	if (res < 0)
359f421436aSArvid Brodin 		goto nla_put_failure;
360f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
361f421436aSArvid Brodin 	if (res < 0)
362f421436aSArvid Brodin 		goto nla_put_failure;
363c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
364c5a75911SArvid Brodin 	if (port)
365c5a75911SArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
366c5a75911SArvid Brodin 				  port->dev->ifindex);
367f421436aSArvid Brodin 	if (res < 0)
368f421436aSArvid Brodin 		goto nla_put_failure;
369f421436aSArvid Brodin 
370f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age);
371f421436aSArvid Brodin 	if (res < 0)
372f421436aSArvid Brodin 		goto nla_put_failure;
373f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
374f421436aSArvid Brodin 	if (res < 0)
375f421436aSArvid Brodin 		goto nla_put_failure;
376c5a75911SArvid Brodin 	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
377c5a75911SArvid Brodin 	if (port)
378c5a75911SArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
379c5a75911SArvid Brodin 				  port->dev->ifindex);
38051f3c605SArvid Brodin 	if (res < 0)
38151f3c605SArvid Brodin 		goto nla_put_failure;
382f421436aSArvid Brodin 
383173756b8STaehee Yoo 	rcu_read_unlock();
384173756b8STaehee Yoo 
385f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
386f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
387f421436aSArvid Brodin 
388f421436aSArvid Brodin 	return 0;
389f421436aSArvid Brodin 
390173756b8STaehee Yoo rcu_unlock:
391173756b8STaehee Yoo 	rcu_read_unlock();
392f421436aSArvid Brodin invalid:
3932d4bc933SJohannes Berg 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
394f421436aSArvid Brodin 	return 0;
395f421436aSArvid Brodin 
396f421436aSArvid Brodin nla_put_failure:
397f421436aSArvid Brodin 	kfree_skb(skb_out);
398f421436aSArvid Brodin 	/* Fall through */
399f421436aSArvid Brodin 
400f421436aSArvid Brodin fail:
401173756b8STaehee Yoo 	rcu_read_unlock();
402f421436aSArvid Brodin 	return res;
403f421436aSArvid Brodin }
404f421436aSArvid Brodin 
405f266a683SArvid Brodin /* Get a list of MacAddressA of all nodes known to this node (including self).
406f421436aSArvid Brodin  */
hsr_get_node_list(struct sk_buff * skb_in,struct genl_info * info)407f421436aSArvid Brodin static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
408f421436aSArvid Brodin {
409f421436aSArvid Brodin 	unsigned char addr[ETH_ALEN];
410ca19c70fSTaehee Yoo 	struct net_device *hsr_dev;
411ca19c70fSTaehee Yoo 	struct sk_buff *skb_out;
412ca19c70fSTaehee Yoo 	struct hsr_priv *hsr;
413ca19c70fSTaehee Yoo 	bool restart = false;
414ca19c70fSTaehee Yoo 	struct nlattr *na;
415ca19c70fSTaehee Yoo 	void *pos = NULL;
416ca19c70fSTaehee Yoo 	void *msg_head;
417f421436aSArvid Brodin 	int res;
418f421436aSArvid Brodin 
419f421436aSArvid Brodin 	if (!info)
420f421436aSArvid Brodin 		goto invalid;
421f421436aSArvid Brodin 
422f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
423f421436aSArvid Brodin 	if (!na)
424f421436aSArvid Brodin 		goto invalid;
425f421436aSArvid Brodin 
426173756b8STaehee Yoo 	rcu_read_lock();
427173756b8STaehee Yoo 	hsr_dev = dev_get_by_index_rcu(genl_info_net(info),
428f421436aSArvid Brodin 				       nla_get_u32(info->attrs[HSR_A_IFINDEX]));
429f421436aSArvid Brodin 	if (!hsr_dev)
430173756b8STaehee Yoo 		goto rcu_unlock;
431f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
432173756b8STaehee Yoo 		goto rcu_unlock;
433f421436aSArvid Brodin 
434ca19c70fSTaehee Yoo restart:
435f421436aSArvid Brodin 	/* Send reply */
436ca19c70fSTaehee Yoo 	skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
437f421436aSArvid Brodin 	if (!skb_out) {
438f421436aSArvid Brodin 		res = -ENOMEM;
439f421436aSArvid Brodin 		goto fail;
440f421436aSArvid Brodin 	}
441f421436aSArvid Brodin 
442f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
443f421436aSArvid Brodin 			       info->snd_seq, &hsr_genl_family, 0,
444f421436aSArvid Brodin 			       HSR_C_SET_NODE_LIST);
445f421436aSArvid Brodin 	if (!msg_head) {
446f421436aSArvid Brodin 		res = -ENOMEM;
447f421436aSArvid Brodin 		goto nla_put_failure;
448f421436aSArvid Brodin 	}
449f421436aSArvid Brodin 
450ca19c70fSTaehee Yoo 	if (!restart) {
451f421436aSArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
452f421436aSArvid Brodin 		if (res < 0)
453f421436aSArvid Brodin 			goto nla_put_failure;
454ca19c70fSTaehee Yoo 	}
455f421436aSArvid Brodin 
45670ebe4a4SArvid Brodin 	hsr = netdev_priv(hsr_dev);
457f421436aSArvid Brodin 
458ca19c70fSTaehee Yoo 	if (!pos)
45970ebe4a4SArvid Brodin 		pos = hsr_get_next_node(hsr, NULL, addr);
460f421436aSArvid Brodin 	while (pos) {
461f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
462f421436aSArvid Brodin 		if (res < 0) {
463ca19c70fSTaehee Yoo 			if (res == -EMSGSIZE) {
464ca19c70fSTaehee Yoo 				genlmsg_end(skb_out, msg_head);
465ca19c70fSTaehee Yoo 				genlmsg_unicast(genl_info_net(info), skb_out,
466ca19c70fSTaehee Yoo 						info->snd_portid);
467ca19c70fSTaehee Yoo 				restart = true;
468ca19c70fSTaehee Yoo 				goto restart;
469ca19c70fSTaehee Yoo 			}
470f421436aSArvid Brodin 			goto nla_put_failure;
471f421436aSArvid Brodin 		}
47270ebe4a4SArvid Brodin 		pos = hsr_get_next_node(hsr, pos, addr);
473f421436aSArvid Brodin 	}
474f421436aSArvid Brodin 	rcu_read_unlock();
475f421436aSArvid Brodin 
476f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
477f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
478f421436aSArvid Brodin 
479f421436aSArvid Brodin 	return 0;
480f421436aSArvid Brodin 
481173756b8STaehee Yoo rcu_unlock:
482173756b8STaehee Yoo 	rcu_read_unlock();
483f421436aSArvid Brodin invalid:
4842d4bc933SJohannes Berg 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
485f421436aSArvid Brodin 	return 0;
486f421436aSArvid Brodin 
487f421436aSArvid Brodin nla_put_failure:
488ca19c70fSTaehee Yoo 	nlmsg_free(skb_out);
489f421436aSArvid Brodin 	/* Fall through */
490f421436aSArvid Brodin 
491f421436aSArvid Brodin fail:
492173756b8STaehee Yoo 	rcu_read_unlock();
493f421436aSArvid Brodin 	return res;
494f421436aSArvid Brodin }
495f421436aSArvid Brodin 
49666a9b928SJakub Kicinski static const struct genl_small_ops hsr_ops[] = {
4979504b3eeSJohannes Berg 	{
4989504b3eeSJohannes Berg 		.cmd = HSR_C_GET_NODE_STATUS,
499ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5009504b3eeSJohannes Berg 		.flags = 0,
5019504b3eeSJohannes Berg 		.doit = hsr_get_node_status,
5029504b3eeSJohannes Berg 		.dumpit = NULL,
5039504b3eeSJohannes Berg 	},
5049504b3eeSJohannes Berg 	{
505f421436aSArvid Brodin 		.cmd = HSR_C_GET_NODE_LIST,
506ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
507f421436aSArvid Brodin 		.flags = 0,
508f421436aSArvid Brodin 		.doit = hsr_get_node_list,
509f421436aSArvid Brodin 		.dumpit = NULL,
5109504b3eeSJohannes Berg 	},
511f421436aSArvid Brodin };
512f421436aSArvid Brodin 
51356989f6dSJohannes Berg static struct genl_family hsr_genl_family __ro_after_init = {
514489111e5SJohannes Berg 	.hdrsize = 0,
515489111e5SJohannes Berg 	.name = "HSR",
516489111e5SJohannes Berg 	.version = 1,
517489111e5SJohannes Berg 	.maxattr = HSR_A_MAX,
5183b0f31f2SJohannes Berg 	.policy = hsr_genl_policy,
51909e91dbeSTaehee Yoo 	.netnsok = true,
520489111e5SJohannes Berg 	.module = THIS_MODULE,
52166a9b928SJakub Kicinski 	.small_ops = hsr_ops,
52266a9b928SJakub Kicinski 	.n_small_ops = ARRAY_SIZE(hsr_ops),
5239c5d03d3SJakub Kicinski 	.resv_start_op = HSR_C_SET_NODE_LIST + 1,
524489111e5SJohannes Berg 	.mcgrps = hsr_mcgrps,
525489111e5SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(hsr_mcgrps),
526489111e5SJohannes Berg };
527489111e5SJohannes Berg 
hsr_netlink_init(void)528f421436aSArvid Brodin int __init hsr_netlink_init(void)
529f421436aSArvid Brodin {
530f421436aSArvid Brodin 	int rc;
531f421436aSArvid Brodin 
532f421436aSArvid Brodin 	rc = rtnl_link_register(&hsr_link_ops);
533f421436aSArvid Brodin 	if (rc)
534f421436aSArvid Brodin 		goto fail_rtnl_link_register;
535f421436aSArvid Brodin 
536489111e5SJohannes Berg 	rc = genl_register_family(&hsr_genl_family);
537f421436aSArvid Brodin 	if (rc)
538f421436aSArvid Brodin 		goto fail_genl_register_family;
539f421436aSArvid Brodin 
540c6c4ccd7STaehee Yoo 	hsr_debugfs_create_root();
541f421436aSArvid Brodin 	return 0;
542f421436aSArvid Brodin 
543f421436aSArvid Brodin fail_genl_register_family:
544f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
545f421436aSArvid Brodin fail_rtnl_link_register:
546f421436aSArvid Brodin 
547f421436aSArvid Brodin 	return rc;
548f421436aSArvid Brodin }
549f421436aSArvid Brodin 
hsr_netlink_exit(void)550f421436aSArvid Brodin void __exit hsr_netlink_exit(void)
551f421436aSArvid Brodin {
552f421436aSArvid Brodin 	genl_unregister_family(&hsr_genl_family);
553f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
554f421436aSArvid Brodin }
555f421436aSArvid Brodin 
556f421436aSArvid Brodin MODULE_ALIAS_RTNL_LINK("hsr");
557