162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author(s): 562306a36Sopenharmony_ci * 2011-2014 Arvid Brodin, arvid.brodin@alten.se 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Routines for handling Netlink messages for HSR and PRP. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "hsr_netlink.h" 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <net/rtnetlink.h> 1362306a36Sopenharmony_ci#include <net/genetlink.h> 1462306a36Sopenharmony_ci#include "hsr_main.h" 1562306a36Sopenharmony_ci#include "hsr_device.h" 1662306a36Sopenharmony_ci#include "hsr_framereg.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = { 1962306a36Sopenharmony_ci [IFLA_HSR_SLAVE1] = { .type = NLA_U32 }, 2062306a36Sopenharmony_ci [IFLA_HSR_SLAVE2] = { .type = NLA_U32 }, 2162306a36Sopenharmony_ci [IFLA_HSR_MULTICAST_SPEC] = { .type = NLA_U8 }, 2262306a36Sopenharmony_ci [IFLA_HSR_VERSION] = { .type = NLA_U8 }, 2362306a36Sopenharmony_ci [IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN }, 2462306a36Sopenharmony_ci [IFLA_HSR_SEQ_NR] = { .type = NLA_U16 }, 2562306a36Sopenharmony_ci [IFLA_HSR_PROTOCOL] = { .type = NLA_U8 }, 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Here, it seems a netdevice has already been allocated for us, and the 2962306a36Sopenharmony_ci * hsr_dev_setup routine has been executed. Nice! 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic int hsr_newlink(struct net *src_net, struct net_device *dev, 3262306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 3362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci enum hsr_version proto_version; 3662306a36Sopenharmony_ci unsigned char multicast_spec; 3762306a36Sopenharmony_ci u8 proto = HSR_PROTOCOL_HSR; 3862306a36Sopenharmony_ci struct net_device *link[2]; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (!data) { 4162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No slave devices specified"); 4262306a36Sopenharmony_ci return -EINVAL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci if (!data[IFLA_HSR_SLAVE1]) { 4562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified"); 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci link[0] = __dev_get_by_index(src_net, 4962306a36Sopenharmony_ci nla_get_u32(data[IFLA_HSR_SLAVE1])); 5062306a36Sopenharmony_ci if (!link[0]) { 5162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist"); 5262306a36Sopenharmony_ci return -EINVAL; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (!data[IFLA_HSR_SLAVE2]) { 5562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified"); 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci link[1] = __dev_get_by_index(src_net, 5962306a36Sopenharmony_ci nla_get_u32(data[IFLA_HSR_SLAVE2])); 6062306a36Sopenharmony_ci if (!link[1]) { 6162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist"); 6262306a36Sopenharmony_ci return -EINVAL; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (link[0] == link[1]) { 6662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same"); 6762306a36Sopenharmony_ci return -EINVAL; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!data[IFLA_HSR_MULTICAST_SPEC]) 7162306a36Sopenharmony_ci multicast_spec = 0; 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (data[IFLA_HSR_PROTOCOL]) 7662306a36Sopenharmony_ci proto = nla_get_u8(data[IFLA_HSR_PROTOCOL]); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (proto >= HSR_PROTOCOL_MAX) { 7962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol"); 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!data[IFLA_HSR_VERSION]) { 8462306a36Sopenharmony_ci proto_version = HSR_V0; 8562306a36Sopenharmony_ci } else { 8662306a36Sopenharmony_ci if (proto == HSR_PROTOCOL_PRP) { 8762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "PRP version unsupported"); 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci proto_version = nla_get_u8(data[IFLA_HSR_VERSION]); 9262306a36Sopenharmony_ci if (proto_version > HSR_V1) { 9362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 9462306a36Sopenharmony_ci "Only HSR version 0/1 supported"); 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (proto == HSR_PROTOCOL_PRP) 10062306a36Sopenharmony_ci proto_version = PRP_V1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void hsr_dellink(struct net_device *dev, struct list_head *head) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct hsr_priv *hsr = netdev_priv(dev); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci del_timer_sync(&hsr->prune_timer); 11062306a36Sopenharmony_ci del_timer_sync(&hsr->announce_timer); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci hsr_debugfs_term(hsr); 11362306a36Sopenharmony_ci hsr_del_ports(hsr); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci hsr_del_self_node(hsr); 11662306a36Sopenharmony_ci hsr_del_nodes(&hsr->node_db); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct hsr_priv *hsr = netdev_priv(dev); 12462306a36Sopenharmony_ci u8 proto = HSR_PROTOCOL_HSR; 12562306a36Sopenharmony_ci struct hsr_port *port; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 12862306a36Sopenharmony_ci if (port) { 12962306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex)) 13062306a36Sopenharmony_ci goto nla_put_failure; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 13462306a36Sopenharmony_ci if (port) { 13562306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex)) 13662306a36Sopenharmony_ci goto nla_put_failure; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, 14062306a36Sopenharmony_ci hsr->sup_multicast_addr) || 14162306a36Sopenharmony_ci nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr)) 14262306a36Sopenharmony_ci goto nla_put_failure; 14362306a36Sopenharmony_ci if (hsr->prot_version == PRP_V1) 14462306a36Sopenharmony_ci proto = HSR_PROTOCOL_PRP; 14562306a36Sopenharmony_ci if (nla_put_u8(skb, IFLA_HSR_PROTOCOL, proto)) 14662306a36Sopenharmony_ci goto nla_put_failure; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cinla_put_failure: 15162306a36Sopenharmony_ci return -EMSGSIZE; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic struct rtnl_link_ops hsr_link_ops __read_mostly = { 15562306a36Sopenharmony_ci .kind = "hsr", 15662306a36Sopenharmony_ci .maxtype = IFLA_HSR_MAX, 15762306a36Sopenharmony_ci .policy = hsr_policy, 15862306a36Sopenharmony_ci .priv_size = sizeof(struct hsr_priv), 15962306a36Sopenharmony_ci .setup = hsr_dev_setup, 16062306a36Sopenharmony_ci .newlink = hsr_newlink, 16162306a36Sopenharmony_ci .dellink = hsr_dellink, 16262306a36Sopenharmony_ci .fill_info = hsr_fill_info, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* attribute policy */ 16662306a36Sopenharmony_cistatic const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { 16762306a36Sopenharmony_ci [HSR_A_NODE_ADDR] = { .len = ETH_ALEN }, 16862306a36Sopenharmony_ci [HSR_A_NODE_ADDR_B] = { .len = ETH_ALEN }, 16962306a36Sopenharmony_ci [HSR_A_IFINDEX] = { .type = NLA_U32 }, 17062306a36Sopenharmony_ci [HSR_A_IF1_AGE] = { .type = NLA_U32 }, 17162306a36Sopenharmony_ci [HSR_A_IF2_AGE] = { .type = NLA_U32 }, 17262306a36Sopenharmony_ci [HSR_A_IF1_SEQ] = { .type = NLA_U16 }, 17362306a36Sopenharmony_ci [HSR_A_IF2_SEQ] = { .type = NLA_U16 }, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct genl_family hsr_genl_family; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const struct genl_multicast_group hsr_mcgrps[] = { 17962306a36Sopenharmony_ci { .name = "hsr-network", }, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* This is called if for some node with MAC address addr, we only get frames 18362306a36Sopenharmony_ci * over one of the slave interfaces. This would indicate an open network ring 18462306a36Sopenharmony_ci * (i.e. a link has failed somewhere). 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_civoid hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], 18762306a36Sopenharmony_ci struct hsr_port *port) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct sk_buff *skb; 19062306a36Sopenharmony_ci void *msg_head; 19162306a36Sopenharmony_ci struct hsr_port *master; 19262306a36Sopenharmony_ci int res; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 19562306a36Sopenharmony_ci if (!skb) 19662306a36Sopenharmony_ci goto fail; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, 19962306a36Sopenharmony_ci HSR_C_RING_ERROR); 20062306a36Sopenharmony_ci if (!msg_head) 20162306a36Sopenharmony_ci goto nla_put_failure; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 20462306a36Sopenharmony_ci if (res < 0) 20562306a36Sopenharmony_ci goto nla_put_failure; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex); 20862306a36Sopenharmony_ci if (res < 0) 20962306a36Sopenharmony_ci goto nla_put_failure; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci genlmsg_end(skb, msg_head); 21262306a36Sopenharmony_ci genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cinla_put_failure: 21762306a36Sopenharmony_ci kfree_skb(skb); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cifail: 22062306a36Sopenharmony_ci rcu_read_lock(); 22162306a36Sopenharmony_ci master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 22262306a36Sopenharmony_ci netdev_warn(master->dev, "Could not send HSR ring error message\n"); 22362306a36Sopenharmony_ci rcu_read_unlock(); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* This is called when we haven't heard from the node with MAC address addr for 22762306a36Sopenharmony_ci * some time (just before the node is removed from the node table/list). 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_civoid hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct sk_buff *skb; 23262306a36Sopenharmony_ci void *msg_head; 23362306a36Sopenharmony_ci struct hsr_port *master; 23462306a36Sopenharmony_ci int res; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 23762306a36Sopenharmony_ci if (!skb) 23862306a36Sopenharmony_ci goto fail; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN); 24162306a36Sopenharmony_ci if (!msg_head) 24262306a36Sopenharmony_ci goto nla_put_failure; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 24562306a36Sopenharmony_ci if (res < 0) 24662306a36Sopenharmony_ci goto nla_put_failure; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci genlmsg_end(skb, msg_head); 24962306a36Sopenharmony_ci genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cinla_put_failure: 25462306a36Sopenharmony_ci kfree_skb(skb); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cifail: 25762306a36Sopenharmony_ci rcu_read_lock(); 25862306a36Sopenharmony_ci master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 25962306a36Sopenharmony_ci netdev_warn(master->dev, "Could not send HSR node down\n"); 26062306a36Sopenharmony_ci rcu_read_unlock(); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table 26462306a36Sopenharmony_ci * about the status of a specific node in the network, defined by its MAC 26562306a36Sopenharmony_ci * address. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * Input: hsr ifindex, node mac address 26862306a36Sopenharmony_ci * Output: hsr ifindex, node mac address (copied from request), 26962306a36Sopenharmony_ci * age of latest frame from node over slave 1, slave 2 [ms] 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci /* For receiving */ 27462306a36Sopenharmony_ci struct nlattr *na; 27562306a36Sopenharmony_ci struct net_device *hsr_dev; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* For sending */ 27862306a36Sopenharmony_ci struct sk_buff *skb_out; 27962306a36Sopenharmony_ci void *msg_head; 28062306a36Sopenharmony_ci struct hsr_priv *hsr; 28162306a36Sopenharmony_ci struct hsr_port *port; 28262306a36Sopenharmony_ci unsigned char hsr_node_addr_b[ETH_ALEN]; 28362306a36Sopenharmony_ci int hsr_node_if1_age; 28462306a36Sopenharmony_ci u16 hsr_node_if1_seq; 28562306a36Sopenharmony_ci int hsr_node_if2_age; 28662306a36Sopenharmony_ci u16 hsr_node_if2_seq; 28762306a36Sopenharmony_ci int addr_b_ifindex; 28862306a36Sopenharmony_ci int res; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (!info) 29162306a36Sopenharmony_ci goto invalid; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci na = info->attrs[HSR_A_IFINDEX]; 29462306a36Sopenharmony_ci if (!na) 29562306a36Sopenharmony_ci goto invalid; 29662306a36Sopenharmony_ci na = info->attrs[HSR_A_NODE_ADDR]; 29762306a36Sopenharmony_ci if (!na) 29862306a36Sopenharmony_ci goto invalid; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci rcu_read_lock(); 30162306a36Sopenharmony_ci hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 30262306a36Sopenharmony_ci nla_get_u32(info->attrs[HSR_A_IFINDEX])); 30362306a36Sopenharmony_ci if (!hsr_dev) 30462306a36Sopenharmony_ci goto rcu_unlock; 30562306a36Sopenharmony_ci if (!is_hsr_master(hsr_dev)) 30662306a36Sopenharmony_ci goto rcu_unlock; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Send reply */ 30962306a36Sopenharmony_ci skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 31062306a36Sopenharmony_ci if (!skb_out) { 31162306a36Sopenharmony_ci res = -ENOMEM; 31262306a36Sopenharmony_ci goto fail; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 31662306a36Sopenharmony_ci info->snd_seq, &hsr_genl_family, 0, 31762306a36Sopenharmony_ci HSR_C_SET_NODE_STATUS); 31862306a36Sopenharmony_ci if (!msg_head) { 31962306a36Sopenharmony_ci res = -ENOMEM; 32062306a36Sopenharmony_ci goto nla_put_failure; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 32462306a36Sopenharmony_ci if (res < 0) 32562306a36Sopenharmony_ci goto nla_put_failure; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci hsr = netdev_priv(hsr_dev); 32862306a36Sopenharmony_ci res = hsr_get_node_data(hsr, 32962306a36Sopenharmony_ci (unsigned char *) 33062306a36Sopenharmony_ci nla_data(info->attrs[HSR_A_NODE_ADDR]), 33162306a36Sopenharmony_ci hsr_node_addr_b, 33262306a36Sopenharmony_ci &addr_b_ifindex, 33362306a36Sopenharmony_ci &hsr_node_if1_age, 33462306a36Sopenharmony_ci &hsr_node_if1_seq, 33562306a36Sopenharmony_ci &hsr_node_if2_age, 33662306a36Sopenharmony_ci &hsr_node_if2_seq); 33762306a36Sopenharmony_ci if (res < 0) 33862306a36Sopenharmony_ci goto nla_put_failure; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, 34162306a36Sopenharmony_ci nla_data(info->attrs[HSR_A_NODE_ADDR])); 34262306a36Sopenharmony_ci if (res < 0) 34362306a36Sopenharmony_ci goto nla_put_failure; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (addr_b_ifindex > -1) { 34662306a36Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN, 34762306a36Sopenharmony_ci hsr_node_addr_b); 34862306a36Sopenharmony_ci if (res < 0) 34962306a36Sopenharmony_ci goto nla_put_failure; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, 35262306a36Sopenharmony_ci addr_b_ifindex); 35362306a36Sopenharmony_ci if (res < 0) 35462306a36Sopenharmony_ci goto nla_put_failure; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age); 35862306a36Sopenharmony_ci if (res < 0) 35962306a36Sopenharmony_ci goto nla_put_failure; 36062306a36Sopenharmony_ci res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); 36162306a36Sopenharmony_ci if (res < 0) 36262306a36Sopenharmony_ci goto nla_put_failure; 36362306a36Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 36462306a36Sopenharmony_ci if (port) 36562306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, 36662306a36Sopenharmony_ci port->dev->ifindex); 36762306a36Sopenharmony_ci if (res < 0) 36862306a36Sopenharmony_ci goto nla_put_failure; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age); 37162306a36Sopenharmony_ci if (res < 0) 37262306a36Sopenharmony_ci goto nla_put_failure; 37362306a36Sopenharmony_ci res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); 37462306a36Sopenharmony_ci if (res < 0) 37562306a36Sopenharmony_ci goto nla_put_failure; 37662306a36Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 37762306a36Sopenharmony_ci if (port) 37862306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, 37962306a36Sopenharmony_ci port->dev->ifindex); 38062306a36Sopenharmony_ci if (res < 0) 38162306a36Sopenharmony_ci goto nla_put_failure; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci rcu_read_unlock(); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci genlmsg_end(skb_out, msg_head); 38662306a36Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_circu_unlock: 39162306a36Sopenharmony_ci rcu_read_unlock(); 39262306a36Sopenharmony_ciinvalid: 39362306a36Sopenharmony_ci netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cinla_put_failure: 39762306a36Sopenharmony_ci kfree_skb(skb_out); 39862306a36Sopenharmony_ci /* Fall through */ 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cifail: 40162306a36Sopenharmony_ci rcu_read_unlock(); 40262306a36Sopenharmony_ci return res; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* Get a list of MacAddressA of all nodes known to this node (including self). 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_cistatic int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci unsigned char addr[ETH_ALEN]; 41062306a36Sopenharmony_ci struct net_device *hsr_dev; 41162306a36Sopenharmony_ci struct sk_buff *skb_out; 41262306a36Sopenharmony_ci struct hsr_priv *hsr; 41362306a36Sopenharmony_ci bool restart = false; 41462306a36Sopenharmony_ci struct nlattr *na; 41562306a36Sopenharmony_ci void *pos = NULL; 41662306a36Sopenharmony_ci void *msg_head; 41762306a36Sopenharmony_ci int res; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!info) 42062306a36Sopenharmony_ci goto invalid; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci na = info->attrs[HSR_A_IFINDEX]; 42362306a36Sopenharmony_ci if (!na) 42462306a36Sopenharmony_ci goto invalid; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci rcu_read_lock(); 42762306a36Sopenharmony_ci hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 42862306a36Sopenharmony_ci nla_get_u32(info->attrs[HSR_A_IFINDEX])); 42962306a36Sopenharmony_ci if (!hsr_dev) 43062306a36Sopenharmony_ci goto rcu_unlock; 43162306a36Sopenharmony_ci if (!is_hsr_master(hsr_dev)) 43262306a36Sopenharmony_ci goto rcu_unlock; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cirestart: 43562306a36Sopenharmony_ci /* Send reply */ 43662306a36Sopenharmony_ci skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); 43762306a36Sopenharmony_ci if (!skb_out) { 43862306a36Sopenharmony_ci res = -ENOMEM; 43962306a36Sopenharmony_ci goto fail; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 44362306a36Sopenharmony_ci info->snd_seq, &hsr_genl_family, 0, 44462306a36Sopenharmony_ci HSR_C_SET_NODE_LIST); 44562306a36Sopenharmony_ci if (!msg_head) { 44662306a36Sopenharmony_ci res = -ENOMEM; 44762306a36Sopenharmony_ci goto nla_put_failure; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!restart) { 45162306a36Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 45262306a36Sopenharmony_ci if (res < 0) 45362306a36Sopenharmony_ci goto nla_put_failure; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci hsr = netdev_priv(hsr_dev); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!pos) 45962306a36Sopenharmony_ci pos = hsr_get_next_node(hsr, NULL, addr); 46062306a36Sopenharmony_ci while (pos) { 46162306a36Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr); 46262306a36Sopenharmony_ci if (res < 0) { 46362306a36Sopenharmony_ci if (res == -EMSGSIZE) { 46462306a36Sopenharmony_ci genlmsg_end(skb_out, msg_head); 46562306a36Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, 46662306a36Sopenharmony_ci info->snd_portid); 46762306a36Sopenharmony_ci restart = true; 46862306a36Sopenharmony_ci goto restart; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci goto nla_put_failure; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci pos = hsr_get_next_node(hsr, pos, addr); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci rcu_read_unlock(); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci genlmsg_end(skb_out, msg_head); 47762306a36Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_circu_unlock: 48262306a36Sopenharmony_ci rcu_read_unlock(); 48362306a36Sopenharmony_ciinvalid: 48462306a36Sopenharmony_ci netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cinla_put_failure: 48862306a36Sopenharmony_ci nlmsg_free(skb_out); 48962306a36Sopenharmony_ci /* Fall through */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cifail: 49262306a36Sopenharmony_ci rcu_read_unlock(); 49362306a36Sopenharmony_ci return res; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic const struct genl_small_ops hsr_ops[] = { 49762306a36Sopenharmony_ci { 49862306a36Sopenharmony_ci .cmd = HSR_C_GET_NODE_STATUS, 49962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 50062306a36Sopenharmony_ci .flags = 0, 50162306a36Sopenharmony_ci .doit = hsr_get_node_status, 50262306a36Sopenharmony_ci .dumpit = NULL, 50362306a36Sopenharmony_ci }, 50462306a36Sopenharmony_ci { 50562306a36Sopenharmony_ci .cmd = HSR_C_GET_NODE_LIST, 50662306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 50762306a36Sopenharmony_ci .flags = 0, 50862306a36Sopenharmony_ci .doit = hsr_get_node_list, 50962306a36Sopenharmony_ci .dumpit = NULL, 51062306a36Sopenharmony_ci }, 51162306a36Sopenharmony_ci}; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic struct genl_family hsr_genl_family __ro_after_init = { 51462306a36Sopenharmony_ci .hdrsize = 0, 51562306a36Sopenharmony_ci .name = "HSR", 51662306a36Sopenharmony_ci .version = 1, 51762306a36Sopenharmony_ci .maxattr = HSR_A_MAX, 51862306a36Sopenharmony_ci .policy = hsr_genl_policy, 51962306a36Sopenharmony_ci .netnsok = true, 52062306a36Sopenharmony_ci .module = THIS_MODULE, 52162306a36Sopenharmony_ci .small_ops = hsr_ops, 52262306a36Sopenharmony_ci .n_small_ops = ARRAY_SIZE(hsr_ops), 52362306a36Sopenharmony_ci .resv_start_op = HSR_C_SET_NODE_LIST + 1, 52462306a36Sopenharmony_ci .mcgrps = hsr_mcgrps, 52562306a36Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(hsr_mcgrps), 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciint __init hsr_netlink_init(void) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci int rc; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci rc = rtnl_link_register(&hsr_link_ops); 53362306a36Sopenharmony_ci if (rc) 53462306a36Sopenharmony_ci goto fail_rtnl_link_register; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci rc = genl_register_family(&hsr_genl_family); 53762306a36Sopenharmony_ci if (rc) 53862306a36Sopenharmony_ci goto fail_genl_register_family; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci hsr_debugfs_create_root(); 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cifail_genl_register_family: 54462306a36Sopenharmony_ci rtnl_link_unregister(&hsr_link_ops); 54562306a36Sopenharmony_cifail_rtnl_link_register: 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return rc; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_civoid __exit hsr_netlink_exit(void) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci genl_unregister_family(&hsr_genl_family); 55362306a36Sopenharmony_ci rtnl_link_unregister(&hsr_link_ops); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("hsr"); 557