[net-next RFC PATCH 09/13] net: hsr: move re-usable code for PRP to hsr_prp_netlink.c
From: Murali Karicheri
Date: Wed May 06 2020 - 12:30:54 EST
Before introducing PRP netlink code, move the re-usable code to a
common hsr_prp_netlink.c.
Signed-off-by: Murali Karicheri <m-karicheri2@xxxxxx>
---
net/hsr-prp/Makefile | 2 +-
net/hsr-prp/hsr_netlink.c | 337 +------------------------------
net/hsr-prp/hsr_prp_netlink.c | 367 ++++++++++++++++++++++++++++++++++
net/hsr-prp/hsr_prp_netlink.h | 27 +++
4 files changed, 402 insertions(+), 331 deletions(-)
create mode 100644 net/hsr-prp/hsr_prp_netlink.c
create mode 100644 net/hsr-prp/hsr_prp_netlink.h
diff --git a/net/hsr-prp/Makefile b/net/hsr-prp/Makefile
index 608045f088a4..76f266cd1976 100644
--- a/net/hsr-prp/Makefile
+++ b/net/hsr-prp/Makefile
@@ -7,5 +7,5 @@ obj-$(CONFIG_HSR_PRP) += hsr-prp.o
hsr-prp-y := hsr_prp_main.o hsr_prp_framereg.o \
hsr_prp_device.o hsr_netlink.o hsr_prp_slave.o \
- hsr_prp_forward.o
+ hsr_prp_forward.o hsr_prp_netlink.o
hsr-prp-$(CONFIG_DEBUG_FS) += hsr_prp_debugfs.o
diff --git a/net/hsr-prp/hsr_netlink.c b/net/hsr-prp/hsr_netlink.c
index 1f7c3be8d96e..6bdb369ced36 100644
--- a/net/hsr-prp/hsr_netlink.c
+++ b/net/hsr-prp/hsr_netlink.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <net/rtnetlink.h>
#include <net/genetlink.h>
+#include "hsr_prp_netlink.h"
#include "hsr_prp_main.h"
#include "hsr_prp_device.h"
#include "hsr_prp_framereg.h"
@@ -31,85 +32,7 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- struct net_device *link[2];
- unsigned char multicast_spec, hsr_version;
-
- if (!data) {
- NL_SET_ERR_MSG_MOD(extack, "No slave devices specified");
- return -EINVAL;
- }
- if (!data[IFLA_HSR_PRP_SLAVE1]) {
- NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified");
- return -EINVAL;
- }
- link[0] = __dev_get_by_index(src_net,
- nla_get_u32(data[IFLA_HSR_PRP_SLAVE1]));
- if (!link[0]) {
- NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist");
- return -EINVAL;
- }
- if (!data[IFLA_HSR_PRP_SLAVE2]) {
- NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified");
- return -EINVAL;
- }
- link[1] = __dev_get_by_index(src_net,
- nla_get_u32(data[IFLA_HSR_PRP_SLAVE2]));
- if (!link[1]) {
- NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist");
- return -EINVAL;
- }
-
- if (link[0] == link[1]) {
- NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same");
- return -EINVAL;
- }
-
- if (!data[IFLA_HSR_PRP_SF_MC_ADDR_LSB])
- multicast_spec = 0;
- else
- multicast_spec = nla_get_u8(data[IFLA_HSR_PRP_SF_MC_ADDR_LSB]);
-
- if (!data[IFLA_HSR_PRP_VERSION]) {
- hsr_version = 0;
- } else {
- hsr_version = nla_get_u8(data[IFLA_HSR_PRP_VERSION]);
- if (hsr_version > 1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Only versions 0..1 are supported");
- return -EINVAL;
- }
- }
-
- return hsr_prp_dev_finalize(dev, link, multicast_spec, hsr_version,
- extack);
-}
-
-static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
-{
- struct hsr_prp_priv *priv = netdev_priv(dev);
- struct hsr_prp_port *port;
-
- port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_A);
- if (port) {
- if (nla_put_u32(skb, IFLA_HSR_PRP_SLAVE1, port->dev->ifindex))
- goto nla_put_failure;
- }
-
- port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_B);
- if (port) {
- if (nla_put_u32(skb, IFLA_HSR_PRP_SLAVE2, port->dev->ifindex))
- goto nla_put_failure;
- }
-
- if (nla_put(skb, IFLA_HSR_PRP_SF_MC_ADDR, ETH_ALEN,
- priv->sup_multicast_addr) ||
- nla_put_u16(skb, IFLA_HSR_PRP_SEQ_NR, priv->sequence_nr))
- goto nla_put_failure;
-
- return 0;
-
-nla_put_failure:
- return -EMSGSIZE;
+ return hsr_prp_newlink(src_net, dev, tb, data, extack);
}
static struct rtnl_link_ops hsr_link_ops __read_mostly = {
@@ -119,7 +42,7 @@ static struct rtnl_link_ops hsr_link_ops __read_mostly = {
.priv_size = sizeof(struct hsr_prp_priv),
.setup = hsr_prp_dev_setup,
.newlink = hsr_newlink,
- .fill_info = hsr_fill_info,
+ .fill_info = hsr_prp_fill_info,
};
/* attribute policy */
@@ -187,40 +110,9 @@ void hsr_nl_ringerror(struct hsr_prp_priv *priv,
/* This is called when we haven't heard from the node with MAC address addr for
* some time (just before the node is removed from the node table/list).
*/
-void hsr_nl_nodedown(struct hsr_prp_priv *priv,
- unsigned char addr[ETH_ALEN])
+void hsr_nl_nodedown(struct hsr_prp_priv *priv, unsigned char addr[ETH_ALEN])
{
- struct sk_buff *skb;
- void *msg_head;
- struct hsr_prp_port *master;
- int res;
-
- skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
- if (!skb)
- goto fail;
-
- msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0,
- HSR_PRP_C_NODE_DOWN);
- if (!msg_head)
- goto nla_put_failure;
-
- res = nla_put(skb, HSR_PRP_A_NODE_ADDR, ETH_ALEN, addr);
- if (res < 0)
- goto nla_put_failure;
-
- genlmsg_end(skb, msg_head);
- genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC);
-
- return;
-
-nla_put_failure:
- kfree_skb(skb);
-
-fail:
- rcu_read_lock();
- master = hsr_prp_get_port(priv, HSR_PRP_PT_MASTER);
- netdev_warn(master->dev, "Could not send HSR node down\n");
- rcu_read_unlock();
+ hsr_prp_nl_nodedown(priv, &hsr_genl_family, addr);
}
/* HSR_PRP_C_GET_NODE_STATUS lets userspace query the internal HSR node table
@@ -233,229 +125,14 @@ void hsr_nl_nodedown(struct hsr_prp_priv *priv,
*/
static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
{
- /* For receiving */
- struct nlattr *na;
- struct net_device *hsr_dev;
-
- /* For sending */
- struct sk_buff *skb_out;
- void *msg_head;
- struct hsr_prp_priv *priv;
- struct hsr_prp_port *port;
- unsigned char node_addr_b[ETH_ALEN];
- int hsr_node_if1_age;
- u16 hsr_node_if1_seq;
- int hsr_node_if2_age;
- u16 hsr_node_if2_seq;
- int addr_b_ifindex;
- int res;
-
- if (!info)
- goto invalid;
-
- na = info->attrs[HSR_PRP_A_IFINDEX];
- if (!na)
- goto invalid;
- na = info->attrs[HSR_PRP_A_NODE_ADDR];
- if (!na)
- goto invalid;
-
- rcu_read_lock();
- hsr_dev =
- dev_get_by_index_rcu(genl_info_net(info),
- nla_get_u32(info->attrs[HSR_PRP_A_IFINDEX]));
- if (!hsr_dev)
- goto rcu_unlock;
- if (!is_hsr_prp_master(hsr_dev))
- goto rcu_unlock;
-
- /* Send reply */
- skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
- if (!skb_out) {
- res = -ENOMEM;
- goto fail;
- }
-
- msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
- info->snd_seq, &hsr_genl_family, 0,
- HSR_PRP_C_SET_NODE_STATUS);
- if (!msg_head) {
- res = -ENOMEM;
- goto nla_put_failure;
- }
-
- res = nla_put_u32(skb_out, HSR_PRP_A_IFINDEX, hsr_dev->ifindex);
- if (res < 0)
- goto nla_put_failure;
-
- priv = netdev_priv(hsr_dev);
- res = hsr_prp_get_node_data(priv,
- (unsigned char *)
- nla_data(info->attrs[HSR_PRP_A_NODE_ADDR]),
- node_addr_b,
- &addr_b_ifindex,
- &hsr_node_if1_age,
- &hsr_node_if1_seq,
- &hsr_node_if2_age,
- &hsr_node_if2_seq);
- if (res < 0)
- goto nla_put_failure;
-
- res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR, ETH_ALEN,
- nla_data(info->attrs[HSR_PRP_A_NODE_ADDR]));
- if (res < 0)
- goto nla_put_failure;
-
- if (addr_b_ifindex > -1) {
- res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR_B, ETH_ALEN,
- node_addr_b);
- if (res < 0)
- goto nla_put_failure;
-
- res = nla_put_u32(skb_out, HSR_PRP_A_ADDR_B_IFINDEX,
- addr_b_ifindex);
- if (res < 0)
- goto nla_put_failure;
- }
-
- res = nla_put_u32(skb_out, HSR_PRP_A_IF1_AGE, hsr_node_if1_age);
- if (res < 0)
- goto nla_put_failure;
- res = nla_put_u16(skb_out, HSR_PRP_A_IF1_SEQ, hsr_node_if1_seq);
- if (res < 0)
- goto nla_put_failure;
- port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_A);
- if (port)
- res = nla_put_u32(skb_out, HSR_PRP_A_IF1_IFINDEX,
- port->dev->ifindex);
- if (res < 0)
- goto nla_put_failure;
-
- res = nla_put_u32(skb_out, HSR_PRP_A_IF2_AGE, hsr_node_if2_age);
- if (res < 0)
- goto nla_put_failure;
- res = nla_put_u16(skb_out, HSR_PRP_A_IF2_SEQ, hsr_node_if2_seq);
- if (res < 0)
- goto nla_put_failure;
- port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_B);
- if (port)
- res = nla_put_u32(skb_out, HSR_PRP_A_IF2_IFINDEX,
- port->dev->ifindex);
- if (res < 0)
- goto nla_put_failure;
-
- rcu_read_unlock();
-
- genlmsg_end(skb_out, msg_head);
- genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
-
- return 0;
-
-rcu_unlock:
- rcu_read_unlock();
-invalid:
- netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
- return 0;
-
-nla_put_failure:
- kfree_skb(skb_out);
- /* Fall through */
-
-fail:
- rcu_read_unlock();
- return res;
+ return hsr_prp_get_node_status(&hsr_genl_family, skb_in, info);
}
/* Get a list of MacAddressA of all nodes known to this node (including self).
*/
static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
{
- unsigned char addr[ETH_ALEN];
- struct net_device *hsr_dev;
- struct hsr_prp_priv *priv;
- struct sk_buff *skb_out;
- bool restart = false;
- struct nlattr *na;
- void *pos = NULL;
- void *msg_head;
- int res;
-
- if (!info)
- goto invalid;
-
- na = info->attrs[HSR_PRP_A_IFINDEX];
- if (!na)
- goto invalid;
-
- rcu_read_lock();
- hsr_dev =
- dev_get_by_index_rcu(genl_info_net(info),
- nla_get_u32(info->attrs[HSR_PRP_A_IFINDEX]));
- if (!hsr_dev)
- goto rcu_unlock;
- if (!is_hsr_prp_master(hsr_dev))
- goto rcu_unlock;
-
-restart:
- /* Send reply */
- skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
- if (!skb_out) {
- res = -ENOMEM;
- goto fail;
- }
-
- msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
- info->snd_seq, &hsr_genl_family, 0,
- HSR_PRP_C_SET_NODE_LIST);
- if (!msg_head) {
- res = -ENOMEM;
- goto nla_put_failure;
- }
-
- if (!restart) {
- res = nla_put_u32(skb_out, HSR_PRP_A_IFINDEX, hsr_dev->ifindex);
- if (res < 0)
- goto nla_put_failure;
- }
-
- priv = netdev_priv(hsr_dev);
-
- if (!pos)
- pos = hsr_prp_get_next_node(priv, NULL, addr);
- while (pos) {
- res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR, ETH_ALEN, addr);
- if (res < 0) {
- if (res == -EMSGSIZE) {
- genlmsg_end(skb_out, msg_head);
- genlmsg_unicast(genl_info_net(info), skb_out,
- info->snd_portid);
- restart = true;
- goto restart;
- }
- goto nla_put_failure;
- }
- pos = hsr_prp_get_next_node(priv, pos, addr);
- }
- rcu_read_unlock();
-
- genlmsg_end(skb_out, msg_head);
- genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
-
- return 0;
-
-rcu_unlock:
- rcu_read_unlock();
-invalid:
- netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
- return 0;
-
-nla_put_failure:
- nlmsg_free(skb_out);
- /* Fall through */
-
-fail:
- rcu_read_unlock();
- return res;
+ return hsr_prp_get_node_list(&hsr_genl_family, skb_in, info);
}
static const struct genl_ops hsr_ops[] = {
diff --git a/net/hsr-prp/hsr_prp_netlink.c b/net/hsr-prp/hsr_prp_netlink.c
new file mode 100644
index 000000000000..04d51cd97496
--- /dev/null
+++ b/net/hsr-prp/hsr_prp_netlink.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2011-2014 Autronica Fire and Security AS
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Based on Code by Arvid Brodin, arvid.brodin@xxxxxxxx
+ *
+ * Common routines for handling Netlink messages for HSR and PRP
+ */
+
+#include "hsr_prp_netlink.h"
+#include "hsr_prp_main.h"
+#include "hsr_prp_device.h"
+#include "hsr_prp_framereg.h"
+
+int hsr_prp_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *link[2];
+ unsigned char multicast_spec, hsr_version;
+
+ if (!data) {
+ NL_SET_ERR_MSG_MOD(extack, "No slave devices specified");
+ return -EINVAL;
+ }
+ if (!data[IFLA_HSR_PRP_SLAVE1]) {
+ NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified");
+ return -EINVAL;
+ }
+ link[0] = __dev_get_by_index(src_net,
+ nla_get_u32(data[IFLA_HSR_PRP_SLAVE1]));
+ if (!link[0]) {
+ NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist");
+ return -EINVAL;
+ }
+ if (!data[IFLA_HSR_PRP_SLAVE2]) {
+ NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified");
+ return -EINVAL;
+ }
+ link[1] = __dev_get_by_index(src_net,
+ nla_get_u32(data[IFLA_HSR_PRP_SLAVE2]));
+ if (!link[1]) {
+ NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist");
+ return -EINVAL;
+ }
+
+ if (link[0] == link[1]) {
+ NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same");
+ return -EINVAL;
+ }
+
+ if (!data[IFLA_HSR_PRP_SF_MC_ADDR_LSB])
+ multicast_spec = 0;
+ else
+ multicast_spec = nla_get_u8(data[IFLA_HSR_PRP_SF_MC_ADDR_LSB]);
+
+ if (!data[IFLA_HSR_PRP_VERSION]) {
+ hsr_version = 0;
+ } else {
+ hsr_version = nla_get_u8(data[IFLA_HSR_PRP_VERSION]);
+ if (hsr_version > 1) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only versions 0..1 are supported");
+ return -EINVAL;
+ }
+ }
+
+ return hsr_prp_dev_finalize(dev, link, multicast_spec, hsr_version,
+ extack);
+}
+
+int hsr_prp_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct hsr_prp_priv *priv = netdev_priv(dev);
+ struct hsr_prp_port *port;
+
+ port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_A);
+ if (port) {
+ if (nla_put_u32(skb, IFLA_HSR_PRP_SLAVE1, port->dev->ifindex))
+ goto nla_put_failure;
+ }
+
+ port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_B);
+ if (port) {
+ if (nla_put_u32(skb, IFLA_HSR_PRP_SLAVE2, port->dev->ifindex))
+ goto nla_put_failure;
+ }
+
+ if (nla_put(skb, IFLA_HSR_PRP_SF_MC_ADDR, ETH_ALEN,
+ priv->sup_multicast_addr) ||
+ nla_put_u16(skb, IFLA_HSR_PRP_SEQ_NR, priv->sequence_nr))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+/* This is called when we haven't heard from the node with MAC address addr for
+ * some time (just before the node is removed from the node table/list).
+ */
+void hsr_prp_nl_nodedown(struct hsr_prp_priv *priv,
+ struct genl_family *genl_family,
+ unsigned char addr[ETH_ALEN])
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ struct hsr_prp_port *master;
+ int res;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!skb)
+ goto fail;
+
+ msg_head = genlmsg_put(skb, 0, 0, genl_family, 0,
+ HSR_PRP_C_NODE_DOWN);
+ if (!msg_head)
+ goto nla_put_failure;
+
+ res = nla_put(skb, HSR_PRP_A_NODE_ADDR, ETH_ALEN, addr);
+ if (res < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_multicast(genl_family, skb, 0, 0, GFP_ATOMIC);
+
+ return;
+
+nla_put_failure:
+ kfree_skb(skb);
+
+fail:
+ rcu_read_lock();
+ master = hsr_prp_get_port(priv, HSR_PRP_PT_MASTER);
+ netdev_warn(master->dev, "Could not send HSR node down\n");
+ rcu_read_unlock();
+}
+
+int hsr_prp_get_node_status(struct genl_family *genl_family,
+ struct sk_buff *skb_in, struct genl_info *info)
+{
+ /* For receiving */
+ struct nlattr *na;
+ struct net_device *hsr_dev;
+
+ /* For sending */
+ struct sk_buff *skb_out;
+ void *msg_head;
+ struct hsr_prp_priv *priv;
+ struct hsr_prp_port *port;
+ unsigned char node_addr_b[ETH_ALEN];
+ int hsr_node_if1_age;
+ u16 hsr_node_if1_seq;
+ int hsr_node_if2_age;
+ u16 hsr_node_if2_seq;
+ int addr_b_ifindex;
+ int res;
+
+ if (!info)
+ goto invalid;
+
+ na = info->attrs[HSR_PRP_A_IFINDEX];
+ if (!na)
+ goto invalid;
+ na = info->attrs[HSR_PRP_A_NODE_ADDR];
+ if (!na)
+ goto invalid;
+
+ rcu_read_lock();
+ hsr_dev =
+ dev_get_by_index_rcu(genl_info_net(info),
+ nla_get_u32(info->attrs[HSR_PRP_A_IFINDEX]));
+ if (!hsr_dev)
+ goto rcu_unlock;
+ if (!is_hsr_prp_master(hsr_dev))
+ goto rcu_unlock;
+
+ /* Send reply */
+ skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!skb_out) {
+ res = -ENOMEM;
+ goto fail;
+ }
+
+ msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+ info->snd_seq, genl_family, 0,
+ HSR_PRP_C_SET_NODE_STATUS);
+ if (!msg_head) {
+ res = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ res = nla_put_u32(skb_out, HSR_PRP_A_IFINDEX, hsr_dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ priv = netdev_priv(hsr_dev);
+ res = hsr_prp_get_node_data(priv,
+ (unsigned char *)
+ nla_data(info->attrs[HSR_PRP_A_NODE_ADDR]),
+ node_addr_b,
+ &addr_b_ifindex,
+ &hsr_node_if1_age,
+ &hsr_node_if1_seq,
+ &hsr_node_if2_age,
+ &hsr_node_if2_seq);
+ if (res < 0)
+ goto nla_put_failure;
+
+ res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR, ETH_ALEN,
+ nla_data(info->attrs[HSR_PRP_A_NODE_ADDR]));
+ if (res < 0)
+ goto nla_put_failure;
+
+ if (addr_b_ifindex > -1) {
+ res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR_B, ETH_ALEN,
+ node_addr_b);
+ if (res < 0)
+ goto nla_put_failure;
+
+ res = nla_put_u32(skb_out, HSR_PRP_A_ADDR_B_IFINDEX,
+ addr_b_ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+ }
+
+ res = nla_put_u32(skb_out, HSR_PRP_A_IF1_AGE, hsr_node_if1_age);
+ if (res < 0)
+ goto nla_put_failure;
+ res = nla_put_u16(skb_out, HSR_PRP_A_IF1_SEQ, hsr_node_if1_seq);
+ if (res < 0)
+ goto nla_put_failure;
+ port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_A);
+ if (port)
+ res = nla_put_u32(skb_out, HSR_PRP_A_IF1_IFINDEX,
+ port->dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ res = nla_put_u32(skb_out, HSR_PRP_A_IF2_AGE, hsr_node_if2_age);
+ if (res < 0)
+ goto nla_put_failure;
+ res = nla_put_u16(skb_out, HSR_PRP_A_IF2_SEQ, hsr_node_if2_seq);
+ if (res < 0)
+ goto nla_put_failure;
+ port = hsr_prp_get_port(priv, HSR_PRP_PT_SLAVE_B);
+ if (port)
+ res = nla_put_u32(skb_out, HSR_PRP_A_IF2_IFINDEX,
+ port->dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ rcu_read_unlock();
+
+ genlmsg_end(skb_out, msg_head);
+ genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+ return 0;
+
+rcu_unlock:
+ rcu_read_unlock();
+invalid:
+ netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb_out);
+ /* Fall through */
+
+fail:
+ rcu_read_unlock();
+ return res;
+}
+
+/* Get a list of MacAddressA of all nodes known to this node (including self).
+ */
+int hsr_prp_get_node_list(struct genl_family *genl_family,
+ struct sk_buff *skb_in, struct genl_info *info)
+{
+ unsigned char addr[ETH_ALEN];
+ struct net_device *hsr_dev;
+ struct hsr_prp_priv *priv;
+ struct sk_buff *skb_out;
+ bool restart = false;
+ struct nlattr *na;
+ void *pos = NULL;
+ void *msg_head;
+ int res;
+
+ if (!info)
+ goto invalid;
+
+ na = info->attrs[HSR_PRP_A_IFINDEX];
+ if (!na)
+ goto invalid;
+
+ rcu_read_lock();
+ hsr_dev =
+ dev_get_by_index_rcu(genl_info_net(info),
+ nla_get_u32(info->attrs[HSR_PRP_A_IFINDEX]));
+ if (!hsr_dev)
+ goto rcu_unlock;
+ if (!is_hsr_prp_master(hsr_dev))
+ goto rcu_unlock;
+
+restart:
+ /* Send reply */
+ skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!skb_out) {
+ res = -ENOMEM;
+ goto fail;
+ }
+
+ msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+ info->snd_seq, genl_family, 0,
+ HSR_PRP_C_SET_NODE_LIST);
+ if (!msg_head) {
+ res = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ if (!restart) {
+ res = nla_put_u32(skb_out, HSR_PRP_A_IFINDEX, hsr_dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+ }
+
+ priv = netdev_priv(hsr_dev);
+
+ if (!pos)
+ pos = hsr_prp_get_next_node(priv, NULL, addr);
+ while (pos) {
+ res = nla_put(skb_out, HSR_PRP_A_NODE_ADDR, ETH_ALEN, addr);
+ if (res < 0) {
+ if (res == -EMSGSIZE) {
+ genlmsg_end(skb_out, msg_head);
+ genlmsg_unicast(genl_info_net(info), skb_out,
+ info->snd_portid);
+ restart = true;
+ goto restart;
+ }
+ goto nla_put_failure;
+ }
+ pos = hsr_prp_get_next_node(priv, pos, addr);
+ }
+ rcu_read_unlock();
+
+ genlmsg_end(skb_out, msg_head);
+ genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+ return 0;
+
+rcu_unlock:
+ rcu_read_unlock();
+invalid:
+ netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(skb_out);
+ /* Fall through */
+
+fail:
+ rcu_read_unlock();
+ return res;
+}
diff --git a/net/hsr-prp/hsr_prp_netlink.h b/net/hsr-prp/hsr_prp_netlink.h
new file mode 100644
index 000000000000..a3a4e6252e45
--- /dev/null
+++ b/net/hsr-prp/hsr_prp_netlink.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2011-2014 Autronica Fire and Security AS
+ *
+ * Author(s):
+ * 2011-2014 Arvid Brodin, arvid.brodin@xxxxxxxx
+ */
+
+#ifndef __HSR_PRP_NETLINK_H
+#define __HSR_PRP_NETLINK_H
+
+#include <uapi/linux/hsr_prp_netlink.h>
+#include <net/genetlink.h>
+
+#include "hsr_prp_main.h"
+
+int hsr_prp_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack);
+int hsr_prp_fill_info(struct sk_buff *skb, const struct net_device *dev);
+void hsr_prp_nl_nodedown(struct hsr_prp_priv *priv,
+ struct genl_family *genl_family,
+ unsigned char addr[ETH_ALEN]);
+int hsr_prp_get_node_status(struct genl_family *genl_family,
+ struct sk_buff *skb_in, struct genl_info *info);
+int hsr_prp_get_node_list(struct genl_family *genl_family,
+ struct sk_buff *skb_in, struct genl_info *info);
+#endif /* __HSR_PRP_NETLINK_H */
--
2.17.1