xref: /openbmc/linux/net/hsr/hsr_netlink.c (revision 9504b3ee)
1f421436aSArvid Brodin /* Copyright 2011-2013 Autronica Fire and Security AS
2f421436aSArvid Brodin  *
3f421436aSArvid Brodin  * This program is free software; you can redistribute it and/or modify it
4f421436aSArvid Brodin  * under the terms of the GNU General Public License as published by the Free
5f421436aSArvid Brodin  * Software Foundation; either version 2 of the License, or (at your option)
6f421436aSArvid Brodin  * any later version.
7f421436aSArvid Brodin  *
8f421436aSArvid Brodin  * Author(s):
9f421436aSArvid Brodin  *	2011-2013 Arvid Brodin, arvid.brodin@xdin.com
10f421436aSArvid Brodin  *
11f421436aSArvid Brodin  * Routines for handling Netlink messages for HSR.
12f421436aSArvid Brodin  */
13f421436aSArvid Brodin 
14f421436aSArvid Brodin #include "hsr_netlink.h"
15f421436aSArvid Brodin #include <linux/kernel.h>
16f421436aSArvid Brodin #include <net/rtnetlink.h>
17f421436aSArvid Brodin #include <net/genetlink.h>
18f421436aSArvid Brodin #include "hsr_main.h"
19f421436aSArvid Brodin #include "hsr_device.h"
20f421436aSArvid Brodin #include "hsr_framereg.h"
21f421436aSArvid Brodin 
22f421436aSArvid Brodin static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
23f421436aSArvid Brodin 	[IFLA_HSR_SLAVE1]		= { .type = NLA_U32 },
24f421436aSArvid Brodin 	[IFLA_HSR_SLAVE2]		= { .type = NLA_U32 },
25f421436aSArvid Brodin 	[IFLA_HSR_MULTICAST_SPEC]	= { .type = NLA_U8 },
26f421436aSArvid Brodin };
27f421436aSArvid Brodin 
28f421436aSArvid Brodin 
29f421436aSArvid Brodin /* Here, it seems a netdevice has already been allocated for us, and the
30f421436aSArvid Brodin  * hsr_dev_setup routine has been executed. Nice!
31f421436aSArvid Brodin  */
32f421436aSArvid Brodin static int hsr_newlink(struct net *src_net, struct net_device *dev,
33f421436aSArvid Brodin 		       struct nlattr *tb[], struct nlattr *data[])
34f421436aSArvid Brodin {
35f421436aSArvid Brodin 	struct net_device *link[2];
36f421436aSArvid Brodin 	unsigned char multicast_spec;
37f421436aSArvid Brodin 
38f421436aSArvid Brodin 	if (!data[IFLA_HSR_SLAVE1]) {
39f421436aSArvid Brodin 		netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n");
40f421436aSArvid Brodin 		return -EINVAL;
41f421436aSArvid Brodin 	}
42f421436aSArvid Brodin 	link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1]));
43f421436aSArvid Brodin 	if (!data[IFLA_HSR_SLAVE2]) {
44f421436aSArvid Brodin 		netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n");
45f421436aSArvid Brodin 		return -EINVAL;
46f421436aSArvid Brodin 	}
47f421436aSArvid Brodin 	link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2]));
48f421436aSArvid Brodin 
49f421436aSArvid Brodin 	if (!link[0] || !link[1])
50f421436aSArvid Brodin 		return -ENODEV;
51f421436aSArvid Brodin 	if (link[0] == link[1])
52f421436aSArvid Brodin 		return -EINVAL;
53f421436aSArvid Brodin 
54f421436aSArvid Brodin 	if (!data[IFLA_HSR_MULTICAST_SPEC])
55f421436aSArvid Brodin 		multicast_spec = 0;
56f421436aSArvid Brodin 	else
57f421436aSArvid Brodin 		multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
58f421436aSArvid Brodin 
59f421436aSArvid Brodin 	return hsr_dev_finalize(dev, link, multicast_spec);
60f421436aSArvid Brodin }
61f421436aSArvid Brodin 
62f421436aSArvid Brodin static struct rtnl_link_ops hsr_link_ops __read_mostly = {
63f421436aSArvid Brodin 	.kind		= "hsr",
64f421436aSArvid Brodin 	.maxtype	= IFLA_HSR_MAX,
65f421436aSArvid Brodin 	.policy		= hsr_policy,
66f421436aSArvid Brodin 	.priv_size	= sizeof(struct hsr_priv),
67f421436aSArvid Brodin 	.setup		= hsr_dev_setup,
68f421436aSArvid Brodin 	.newlink	= hsr_newlink,
69f421436aSArvid Brodin };
70f421436aSArvid Brodin 
71f421436aSArvid Brodin 
72f421436aSArvid Brodin 
73f421436aSArvid Brodin /* attribute policy */
74f421436aSArvid Brodin /* NLA_BINARY missing in libnl; use NLA_UNSPEC in userspace instead. */
75f421436aSArvid Brodin static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
76f421436aSArvid Brodin 	[HSR_A_NODE_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
77f421436aSArvid Brodin 	[HSR_A_NODE_ADDR_B] = { .type = NLA_BINARY, .len = ETH_ALEN },
78f421436aSArvid Brodin 	[HSR_A_IFINDEX] = { .type = NLA_U32 },
79f421436aSArvid Brodin 	[HSR_A_IF1_AGE] = { .type = NLA_U32 },
80f421436aSArvid Brodin 	[HSR_A_IF2_AGE] = { .type = NLA_U32 },
81f421436aSArvid Brodin 	[HSR_A_IF1_SEQ] = { .type = NLA_U16 },
82f421436aSArvid Brodin 	[HSR_A_IF2_SEQ] = { .type = NLA_U16 },
83f421436aSArvid Brodin };
84f421436aSArvid Brodin 
85f421436aSArvid Brodin static struct genl_family hsr_genl_family = {
86f421436aSArvid Brodin 	.id = GENL_ID_GENERATE,
87f421436aSArvid Brodin 	.hdrsize = 0,
88f421436aSArvid Brodin 	.name = "HSR",
89f421436aSArvid Brodin 	.version = 1,
90f421436aSArvid Brodin 	.maxattr = HSR_A_MAX,
91f421436aSArvid Brodin };
92f421436aSArvid Brodin 
93f421436aSArvid Brodin static struct genl_multicast_group hsr_network_genl_mcgrp = {
94f421436aSArvid Brodin 	.name = "hsr-network",
95f421436aSArvid Brodin };
96f421436aSArvid Brodin 
97f421436aSArvid Brodin 
98f421436aSArvid Brodin 
99f421436aSArvid Brodin /* This is called if for some node with MAC address addr, we only get frames
100f421436aSArvid Brodin  * over one of the slave interfaces. This would indicate an open network ring
101f421436aSArvid Brodin  * (i.e. a link has failed somewhere).
102f421436aSArvid Brodin  */
103f421436aSArvid Brodin void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
104f421436aSArvid Brodin 		      enum hsr_dev_idx dev_idx)
105f421436aSArvid Brodin {
106f421436aSArvid Brodin 	struct sk_buff *skb;
107f421436aSArvid Brodin 	void *msg_head;
108f421436aSArvid Brodin 	int res;
109f421436aSArvid Brodin 	int ifindex;
110f421436aSArvid Brodin 
111f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
112f421436aSArvid Brodin 	if (!skb)
113f421436aSArvid Brodin 		goto fail;
114f421436aSArvid Brodin 
115f421436aSArvid Brodin 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_RING_ERROR);
116f421436aSArvid Brodin 	if (!msg_head)
117f421436aSArvid Brodin 		goto nla_put_failure;
118f421436aSArvid Brodin 
119f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
120f421436aSArvid Brodin 	if (res < 0)
121f421436aSArvid Brodin 		goto nla_put_failure;
122f421436aSArvid Brodin 
123f421436aSArvid Brodin 	if (hsr_priv->slave[dev_idx])
124f421436aSArvid Brodin 		ifindex = hsr_priv->slave[dev_idx]->ifindex;
125f421436aSArvid Brodin 	else
126f421436aSArvid Brodin 		ifindex = -1;
127f421436aSArvid Brodin 	res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex);
128f421436aSArvid Brodin 	if (res < 0)
129f421436aSArvid Brodin 		goto nla_put_failure;
130f421436aSArvid Brodin 
131f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
132f421436aSArvid Brodin 	genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
133f421436aSArvid Brodin 
134f421436aSArvid Brodin 	return;
135f421436aSArvid Brodin 
136f421436aSArvid Brodin nla_put_failure:
137f421436aSArvid Brodin 	kfree_skb(skb);
138f421436aSArvid Brodin 
139f421436aSArvid Brodin fail:
140f421436aSArvid Brodin 	netdev_warn(hsr_priv->dev, "Could not send HSR ring error message\n");
141f421436aSArvid Brodin }
142f421436aSArvid Brodin 
143f421436aSArvid Brodin /* This is called when we haven't heard from the node with MAC address addr for
144f421436aSArvid Brodin  * some time (just before the node is removed from the node table/list).
145f421436aSArvid Brodin  */
146f421436aSArvid Brodin void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN])
147f421436aSArvid Brodin {
148f421436aSArvid Brodin 	struct sk_buff *skb;
149f421436aSArvid Brodin 	void *msg_head;
150f421436aSArvid Brodin 	int res;
151f421436aSArvid Brodin 
152f421436aSArvid Brodin 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
153f421436aSArvid Brodin 	if (!skb)
154f421436aSArvid Brodin 		goto fail;
155f421436aSArvid Brodin 
156f421436aSArvid Brodin 	msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN);
157f421436aSArvid Brodin 	if (!msg_head)
158f421436aSArvid Brodin 		goto nla_put_failure;
159f421436aSArvid Brodin 
160f421436aSArvid Brodin 
161f421436aSArvid Brodin 	res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
162f421436aSArvid Brodin 	if (res < 0)
163f421436aSArvid Brodin 		goto nla_put_failure;
164f421436aSArvid Brodin 
165f421436aSArvid Brodin 	genlmsg_end(skb, msg_head);
166f421436aSArvid Brodin 	genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
167f421436aSArvid Brodin 
168f421436aSArvid Brodin 	return;
169f421436aSArvid Brodin 
170f421436aSArvid Brodin nla_put_failure:
171f421436aSArvid Brodin 	kfree_skb(skb);
172f421436aSArvid Brodin 
173f421436aSArvid Brodin fail:
174f421436aSArvid Brodin 	netdev_warn(hsr_priv->dev, "Could not send HSR node down\n");
175f421436aSArvid Brodin }
176f421436aSArvid Brodin 
177f421436aSArvid Brodin 
178f421436aSArvid Brodin /* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table
179f421436aSArvid Brodin  * about the status of a specific node in the network, defined by its MAC
180f421436aSArvid Brodin  * address.
181f421436aSArvid Brodin  *
182f421436aSArvid Brodin  * Input: hsr ifindex, node mac address
183f421436aSArvid Brodin  * Output: hsr ifindex, node mac address (copied from request),
184f421436aSArvid Brodin  *	   age of latest frame from node over slave 1, slave 2 [ms]
185f421436aSArvid Brodin  */
186f421436aSArvid Brodin static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
187f421436aSArvid Brodin {
188f421436aSArvid Brodin 	/* For receiving */
189f421436aSArvid Brodin 	struct nlattr *na;
190f421436aSArvid Brodin 	struct net_device *hsr_dev;
191f421436aSArvid Brodin 
192f421436aSArvid Brodin 	/* For sending */
193f421436aSArvid Brodin 	struct sk_buff *skb_out;
194f421436aSArvid Brodin 	void *msg_head;
195f421436aSArvid Brodin 	struct hsr_priv *hsr_priv;
196f421436aSArvid Brodin 	unsigned char hsr_node_addr_b[ETH_ALEN];
197f421436aSArvid Brodin 	int hsr_node_if1_age;
198f421436aSArvid Brodin 	u16 hsr_node_if1_seq;
199f421436aSArvid Brodin 	int hsr_node_if2_age;
200f421436aSArvid Brodin 	u16 hsr_node_if2_seq;
201f421436aSArvid Brodin 	int addr_b_ifindex;
202f421436aSArvid Brodin 	int res;
203f421436aSArvid Brodin 
204f421436aSArvid Brodin 	if (!info)
205f421436aSArvid Brodin 		goto invalid;
206f421436aSArvid Brodin 
207f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
208f421436aSArvid Brodin 	if (!na)
209f421436aSArvid Brodin 		goto invalid;
210f421436aSArvid Brodin 	na = info->attrs[HSR_A_NODE_ADDR];
211f421436aSArvid Brodin 	if (!na)
212f421436aSArvid Brodin 		goto invalid;
213f421436aSArvid Brodin 
214f421436aSArvid Brodin 	hsr_dev = __dev_get_by_index(genl_info_net(info),
215f421436aSArvid Brodin 					nla_get_u32(info->attrs[HSR_A_IFINDEX]));
216f421436aSArvid Brodin 	if (!hsr_dev)
217f421436aSArvid Brodin 		goto invalid;
218f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
219f421436aSArvid Brodin 		goto invalid;
220f421436aSArvid Brodin 
221f421436aSArvid Brodin 
222f421436aSArvid Brodin 	/* Send reply */
223f421436aSArvid Brodin 
224f421436aSArvid Brodin 	skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
225f421436aSArvid Brodin 	if (!skb_out) {
226f421436aSArvid Brodin 		res = -ENOMEM;
227f421436aSArvid Brodin 		goto fail;
228f421436aSArvid Brodin 	}
229f421436aSArvid Brodin 
230f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
231f421436aSArvid Brodin 				info->snd_seq, &hsr_genl_family, 0,
232f421436aSArvid Brodin 				HSR_C_SET_NODE_STATUS);
233f421436aSArvid Brodin 	if (!msg_head) {
234f421436aSArvid Brodin 		res = -ENOMEM;
235f421436aSArvid Brodin 		goto nla_put_failure;
236f421436aSArvid Brodin 	}
237f421436aSArvid Brodin 
238f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
239f421436aSArvid Brodin 	if (res < 0)
240f421436aSArvid Brodin 		goto nla_put_failure;
241f421436aSArvid Brodin 
242f421436aSArvid Brodin 	hsr_priv = netdev_priv(hsr_dev);
243f421436aSArvid Brodin 	res = hsr_get_node_data(hsr_priv,
244f421436aSArvid Brodin 			(unsigned char *) nla_data(info->attrs[HSR_A_NODE_ADDR]),
245f421436aSArvid Brodin 			hsr_node_addr_b,
246f421436aSArvid Brodin 			&addr_b_ifindex,
247f421436aSArvid Brodin 			&hsr_node_if1_age,
248f421436aSArvid Brodin 			&hsr_node_if1_seq,
249f421436aSArvid Brodin 			&hsr_node_if2_age,
250f421436aSArvid Brodin 			&hsr_node_if2_seq);
251f421436aSArvid Brodin 	if (res < 0)
252f421436aSArvid Brodin 		goto fail;
253f421436aSArvid Brodin 
254f421436aSArvid Brodin 	res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
255f421436aSArvid Brodin 					nla_data(info->attrs[HSR_A_NODE_ADDR]));
256f421436aSArvid Brodin 	if (res < 0)
257f421436aSArvid Brodin 		goto nla_put_failure;
258f421436aSArvid Brodin 
259f421436aSArvid Brodin 	if (addr_b_ifindex > -1) {
260f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN,
261f421436aSArvid Brodin 								hsr_node_addr_b);
262f421436aSArvid Brodin 		if (res < 0)
263f421436aSArvid Brodin 			goto nla_put_failure;
264f421436aSArvid Brodin 
265f421436aSArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, addr_b_ifindex);
266f421436aSArvid Brodin 		if (res < 0)
267f421436aSArvid Brodin 			goto nla_put_failure;
268f421436aSArvid Brodin 	}
269f421436aSArvid Brodin 
270f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age);
271f421436aSArvid Brodin 	if (res < 0)
272f421436aSArvid Brodin 		goto nla_put_failure;
273f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
274f421436aSArvid Brodin 	if (res < 0)
275f421436aSArvid Brodin 		goto nla_put_failure;
276f421436aSArvid Brodin 	if (hsr_priv->slave[0])
277f421436aSArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
278f421436aSArvid Brodin 						hsr_priv->slave[0]->ifindex);
279f421436aSArvid Brodin 	if (res < 0)
280f421436aSArvid Brodin 		goto nla_put_failure;
281f421436aSArvid Brodin 
282f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age);
283f421436aSArvid Brodin 	if (res < 0)
284f421436aSArvid Brodin 		goto nla_put_failure;
285f421436aSArvid Brodin 	res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
286f421436aSArvid Brodin 	if (res < 0)
287f421436aSArvid Brodin 		goto nla_put_failure;
288f421436aSArvid Brodin 	if (hsr_priv->slave[1])
289f421436aSArvid Brodin 		res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
290f421436aSArvid Brodin 						hsr_priv->slave[1]->ifindex);
291f421436aSArvid Brodin 
292f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
293f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
294f421436aSArvid Brodin 
295f421436aSArvid Brodin 	return 0;
296f421436aSArvid Brodin 
297f421436aSArvid Brodin invalid:
298f421436aSArvid Brodin 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
299f421436aSArvid Brodin 	return 0;
300f421436aSArvid Brodin 
301f421436aSArvid Brodin nla_put_failure:
302f421436aSArvid Brodin 	kfree_skb(skb_out);
303f421436aSArvid Brodin 	/* Fall through */
304f421436aSArvid Brodin 
305f421436aSArvid Brodin fail:
306f421436aSArvid Brodin 	return res;
307f421436aSArvid Brodin }
308f421436aSArvid Brodin 
309f421436aSArvid Brodin /* Get a list of MacAddressA of all nodes known to this node (other than self).
310f421436aSArvid Brodin  */
311f421436aSArvid Brodin static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
312f421436aSArvid Brodin {
313f421436aSArvid Brodin 	/* For receiving */
314f421436aSArvid Brodin 	struct nlattr *na;
315f421436aSArvid Brodin 	struct net_device *hsr_dev;
316f421436aSArvid Brodin 
317f421436aSArvid Brodin 	/* For sending */
318f421436aSArvid Brodin 	struct sk_buff *skb_out;
319f421436aSArvid Brodin 	void *msg_head;
320f421436aSArvid Brodin 	struct hsr_priv *hsr_priv;
321f421436aSArvid Brodin 	void *pos;
322f421436aSArvid Brodin 	unsigned char addr[ETH_ALEN];
323f421436aSArvid Brodin 	int res;
324f421436aSArvid Brodin 
325f421436aSArvid Brodin 	if (!info)
326f421436aSArvid Brodin 		goto invalid;
327f421436aSArvid Brodin 
328f421436aSArvid Brodin 	na = info->attrs[HSR_A_IFINDEX];
329f421436aSArvid Brodin 	if (!na)
330f421436aSArvid Brodin 		goto invalid;
331f421436aSArvid Brodin 
332f421436aSArvid Brodin 	hsr_dev = __dev_get_by_index(genl_info_net(info),
333f421436aSArvid Brodin 				     nla_get_u32(info->attrs[HSR_A_IFINDEX]));
334f421436aSArvid Brodin 	if (!hsr_dev)
335f421436aSArvid Brodin 		goto invalid;
336f421436aSArvid Brodin 	if (!is_hsr_master(hsr_dev))
337f421436aSArvid Brodin 		goto invalid;
338f421436aSArvid Brodin 
339f421436aSArvid Brodin 
340f421436aSArvid Brodin 	/* Send reply */
341f421436aSArvid Brodin 
342f421436aSArvid Brodin 	skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
343f421436aSArvid Brodin 	if (!skb_out) {
344f421436aSArvid Brodin 		res = -ENOMEM;
345f421436aSArvid Brodin 		goto fail;
346f421436aSArvid Brodin 	}
347f421436aSArvid Brodin 
348f421436aSArvid Brodin 	msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
349f421436aSArvid Brodin 				info->snd_seq, &hsr_genl_family, 0,
350f421436aSArvid Brodin 				HSR_C_SET_NODE_LIST);
351f421436aSArvid Brodin 	if (!msg_head) {
352f421436aSArvid Brodin 		res = -ENOMEM;
353f421436aSArvid Brodin 		goto nla_put_failure;
354f421436aSArvid Brodin 	}
355f421436aSArvid Brodin 
356f421436aSArvid Brodin 	res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
357f421436aSArvid Brodin 	if (res < 0)
358f421436aSArvid Brodin 		goto nla_put_failure;
359f421436aSArvid Brodin 
360f421436aSArvid Brodin 	hsr_priv = netdev_priv(hsr_dev);
361f421436aSArvid Brodin 
362f421436aSArvid Brodin 	rcu_read_lock();
363f421436aSArvid Brodin 	pos = hsr_get_next_node(hsr_priv, NULL, addr);
364f421436aSArvid Brodin 	while (pos) {
365f421436aSArvid Brodin 		res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
366f421436aSArvid Brodin 		if (res < 0) {
367f421436aSArvid Brodin 			rcu_read_unlock();
368f421436aSArvid Brodin 			goto nla_put_failure;
369f421436aSArvid Brodin 		}
370f421436aSArvid Brodin 		pos = hsr_get_next_node(hsr_priv, pos, addr);
371f421436aSArvid Brodin 	}
372f421436aSArvid Brodin 	rcu_read_unlock();
373f421436aSArvid Brodin 
374f421436aSArvid Brodin 	genlmsg_end(skb_out, msg_head);
375f421436aSArvid Brodin 	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
376f421436aSArvid Brodin 
377f421436aSArvid Brodin 	return 0;
378f421436aSArvid Brodin 
379f421436aSArvid Brodin invalid:
380f421436aSArvid Brodin 	netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
381f421436aSArvid Brodin 	return 0;
382f421436aSArvid Brodin 
383f421436aSArvid Brodin nla_put_failure:
384f421436aSArvid Brodin 	kfree_skb(skb_out);
385f421436aSArvid Brodin 	/* Fall through */
386f421436aSArvid Brodin 
387f421436aSArvid Brodin fail:
388f421436aSArvid Brodin 	return res;
389f421436aSArvid Brodin }
390f421436aSArvid Brodin 
391f421436aSArvid Brodin 
3929504b3eeSJohannes Berg static struct genl_ops hsr_ops[] = {
3939504b3eeSJohannes Berg 	{
3949504b3eeSJohannes Berg 		.cmd = HSR_C_GET_NODE_STATUS,
3959504b3eeSJohannes Berg 		.flags = 0,
3969504b3eeSJohannes Berg 		.policy = hsr_genl_policy,
3979504b3eeSJohannes Berg 		.doit = hsr_get_node_status,
3989504b3eeSJohannes Berg 		.dumpit = NULL,
3999504b3eeSJohannes Berg 	},
4009504b3eeSJohannes Berg 	{
401f421436aSArvid Brodin 		.cmd = HSR_C_GET_NODE_LIST,
402f421436aSArvid Brodin 		.flags = 0,
403f421436aSArvid Brodin 		.policy = hsr_genl_policy,
404f421436aSArvid Brodin 		.doit = hsr_get_node_list,
405f421436aSArvid Brodin 		.dumpit = NULL,
4069504b3eeSJohannes Berg 	},
407f421436aSArvid Brodin };
408f421436aSArvid Brodin 
409f421436aSArvid Brodin int __init hsr_netlink_init(void)
410f421436aSArvid Brodin {
411f421436aSArvid Brodin 	int rc;
412f421436aSArvid Brodin 
413f421436aSArvid Brodin 	rc = rtnl_link_register(&hsr_link_ops);
414f421436aSArvid Brodin 	if (rc)
415f421436aSArvid Brodin 		goto fail_rtnl_link_register;
416f421436aSArvid Brodin 
4179504b3eeSJohannes Berg 	rc = genl_register_family_with_ops(&hsr_genl_family, hsr_ops,
4189504b3eeSJohannes Berg 					   ARRAY_SIZE(hsr_ops));
419f421436aSArvid Brodin 	if (rc)
420f421436aSArvid Brodin 		goto fail_genl_register_family;
421f421436aSArvid Brodin 
422f421436aSArvid Brodin 	rc = genl_register_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
423f421436aSArvid Brodin 	if (rc)
424f421436aSArvid Brodin 		goto fail_genl_register_mc_group;
425f421436aSArvid Brodin 
426f421436aSArvid Brodin 	return 0;
427f421436aSArvid Brodin 
428f421436aSArvid Brodin fail_genl_register_mc_group:
429f421436aSArvid Brodin 	genl_unregister_family(&hsr_genl_family);
430f421436aSArvid Brodin fail_genl_register_family:
431f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
432f421436aSArvid Brodin fail_rtnl_link_register:
433f421436aSArvid Brodin 
434f421436aSArvid Brodin 	return rc;
435f421436aSArvid Brodin }
436f421436aSArvid Brodin 
437f421436aSArvid Brodin void __exit hsr_netlink_exit(void)
438f421436aSArvid Brodin {
439f421436aSArvid Brodin 	genl_unregister_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
440f421436aSArvid Brodin 	genl_unregister_family(&hsr_genl_family);
441f421436aSArvid Brodin 
442f421436aSArvid Brodin 	rtnl_link_unregister(&hsr_link_ops);
443f421436aSArvid Brodin }
444f421436aSArvid Brodin 
445f421436aSArvid Brodin MODULE_ALIAS_RTNL_LINK("hsr");
446