18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Author(s): 58c2ecf20Sopenharmony_ci * 2011-2014 Arvid Brodin, arvid.brodin@alten.se 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Routines for handling Netlink messages for HSR and PRP. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "hsr_netlink.h" 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 138c2ecf20Sopenharmony_ci#include <net/genetlink.h> 148c2ecf20Sopenharmony_ci#include "hsr_main.h" 158c2ecf20Sopenharmony_ci#include "hsr_device.h" 168c2ecf20Sopenharmony_ci#include "hsr_framereg.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = { 198c2ecf20Sopenharmony_ci [IFLA_HSR_SLAVE1] = { .type = NLA_U32 }, 208c2ecf20Sopenharmony_ci [IFLA_HSR_SLAVE2] = { .type = NLA_U32 }, 218c2ecf20Sopenharmony_ci [IFLA_HSR_MULTICAST_SPEC] = { .type = NLA_U8 }, 228c2ecf20Sopenharmony_ci [IFLA_HSR_VERSION] = { .type = NLA_U8 }, 238c2ecf20Sopenharmony_ci [IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN }, 248c2ecf20Sopenharmony_ci [IFLA_HSR_SEQ_NR] = { .type = NLA_U16 }, 258c2ecf20Sopenharmony_ci [IFLA_HSR_PROTOCOL] = { .type = NLA_U8 }, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Here, it seems a netdevice has already been allocated for us, and the 298c2ecf20Sopenharmony_ci * hsr_dev_setup routine has been executed. Nice! 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic int hsr_newlink(struct net *src_net, struct net_device *dev, 328c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci enum hsr_version proto_version; 368c2ecf20Sopenharmony_ci unsigned char multicast_spec; 378c2ecf20Sopenharmony_ci u8 proto = HSR_PROTOCOL_HSR; 388c2ecf20Sopenharmony_ci struct net_device *link[2]; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!data) { 418c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No slave devices specified"); 428c2ecf20Sopenharmony_ci return -EINVAL; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci if (!data[IFLA_HSR_SLAVE1]) { 458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified"); 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci link[0] = __dev_get_by_index(src_net, 498c2ecf20Sopenharmony_ci nla_get_u32(data[IFLA_HSR_SLAVE1])); 508c2ecf20Sopenharmony_ci if (!link[0]) { 518c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist"); 528c2ecf20Sopenharmony_ci return -EINVAL; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci if (!data[IFLA_HSR_SLAVE2]) { 558c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified"); 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci link[1] = __dev_get_by_index(src_net, 598c2ecf20Sopenharmony_ci nla_get_u32(data[IFLA_HSR_SLAVE2])); 608c2ecf20Sopenharmony_ci if (!link[1]) { 618c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist"); 628c2ecf20Sopenharmony_ci return -EINVAL; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (link[0] == link[1]) { 668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same"); 678c2ecf20Sopenharmony_ci return -EINVAL; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (!data[IFLA_HSR_MULTICAST_SPEC]) 718c2ecf20Sopenharmony_ci multicast_spec = 0; 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (data[IFLA_HSR_PROTOCOL]) 768c2ecf20Sopenharmony_ci proto = nla_get_u8(data[IFLA_HSR_PROTOCOL]); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (proto >= HSR_PROTOCOL_MAX) { 798c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol"); 808c2ecf20Sopenharmony_ci return -EINVAL; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!data[IFLA_HSR_VERSION]) { 848c2ecf20Sopenharmony_ci proto_version = HSR_V0; 858c2ecf20Sopenharmony_ci } else { 868c2ecf20Sopenharmony_ci if (proto == HSR_PROTOCOL_PRP) { 878c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "PRP version unsupported"); 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci proto_version = nla_get_u8(data[IFLA_HSR_VERSION]); 928c2ecf20Sopenharmony_ci if (proto_version > HSR_V1) { 938c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 948c2ecf20Sopenharmony_ci "Only HSR version 0/1 supported"); 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (proto == HSR_PROTOCOL_PRP) 1008c2ecf20Sopenharmony_ci proto_version = PRP_V1; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void hsr_dellink(struct net_device *dev, struct list_head *head) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct hsr_priv *hsr = netdev_priv(dev); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci del_timer_sync(&hsr->prune_timer); 1108c2ecf20Sopenharmony_ci del_timer_sync(&hsr->announce_timer); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci hsr_debugfs_term(hsr); 1138c2ecf20Sopenharmony_ci hsr_del_ports(hsr); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci hsr_del_self_node(hsr); 1168c2ecf20Sopenharmony_ci hsr_del_nodes(&hsr->node_db); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci unregister_netdevice_queue(dev, head); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct hsr_priv *hsr = netdev_priv(dev); 1248c2ecf20Sopenharmony_ci u8 proto = HSR_PROTOCOL_HSR; 1258c2ecf20Sopenharmony_ci struct hsr_port *port; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 1288c2ecf20Sopenharmony_ci if (port) { 1298c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex)) 1308c2ecf20Sopenharmony_ci goto nla_put_failure; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 1348c2ecf20Sopenharmony_ci if (port) { 1358c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex)) 1368c2ecf20Sopenharmony_ci goto nla_put_failure; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, 1408c2ecf20Sopenharmony_ci hsr->sup_multicast_addr) || 1418c2ecf20Sopenharmony_ci nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr)) 1428c2ecf20Sopenharmony_ci goto nla_put_failure; 1438c2ecf20Sopenharmony_ci if (hsr->prot_version == PRP_V1) 1448c2ecf20Sopenharmony_ci proto = HSR_PROTOCOL_PRP; 1458c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IFLA_HSR_PROTOCOL, proto)) 1468c2ecf20Sopenharmony_ci goto nla_put_failure; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cinla_put_failure: 1518c2ecf20Sopenharmony_ci return -EMSGSIZE; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic struct rtnl_link_ops hsr_link_ops __read_mostly = { 1558c2ecf20Sopenharmony_ci .kind = "hsr", 1568c2ecf20Sopenharmony_ci .maxtype = IFLA_HSR_MAX, 1578c2ecf20Sopenharmony_ci .policy = hsr_policy, 1588c2ecf20Sopenharmony_ci .priv_size = sizeof(struct hsr_priv), 1598c2ecf20Sopenharmony_ci .setup = hsr_dev_setup, 1608c2ecf20Sopenharmony_ci .newlink = hsr_newlink, 1618c2ecf20Sopenharmony_ci .dellink = hsr_dellink, 1628c2ecf20Sopenharmony_ci .fill_info = hsr_fill_info, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* attribute policy */ 1668c2ecf20Sopenharmony_cistatic const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { 1678c2ecf20Sopenharmony_ci [HSR_A_NODE_ADDR] = { .len = ETH_ALEN }, 1688c2ecf20Sopenharmony_ci [HSR_A_NODE_ADDR_B] = { .len = ETH_ALEN }, 1698c2ecf20Sopenharmony_ci [HSR_A_IFINDEX] = { .type = NLA_U32 }, 1708c2ecf20Sopenharmony_ci [HSR_A_IF1_AGE] = { .type = NLA_U32 }, 1718c2ecf20Sopenharmony_ci [HSR_A_IF2_AGE] = { .type = NLA_U32 }, 1728c2ecf20Sopenharmony_ci [HSR_A_IF1_SEQ] = { .type = NLA_U16 }, 1738c2ecf20Sopenharmony_ci [HSR_A_IF2_SEQ] = { .type = NLA_U16 }, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic struct genl_family hsr_genl_family; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct genl_multicast_group hsr_mcgrps[] = { 1798c2ecf20Sopenharmony_ci { .name = "hsr-network", }, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* This is called if for some node with MAC address addr, we only get frames 1838c2ecf20Sopenharmony_ci * over one of the slave interfaces. This would indicate an open network ring 1848c2ecf20Sopenharmony_ci * (i.e. a link has failed somewhere). 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_civoid hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], 1878c2ecf20Sopenharmony_ci struct hsr_port *port) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct sk_buff *skb; 1908c2ecf20Sopenharmony_ci void *msg_head; 1918c2ecf20Sopenharmony_ci struct hsr_port *master; 1928c2ecf20Sopenharmony_ci int res; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 1958c2ecf20Sopenharmony_ci if (!skb) 1968c2ecf20Sopenharmony_ci goto fail; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, 1998c2ecf20Sopenharmony_ci HSR_C_RING_ERROR); 2008c2ecf20Sopenharmony_ci if (!msg_head) 2018c2ecf20Sopenharmony_ci goto nla_put_failure; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 2048c2ecf20Sopenharmony_ci if (res < 0) 2058c2ecf20Sopenharmony_ci goto nla_put_failure; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex); 2088c2ecf20Sopenharmony_ci if (res < 0) 2098c2ecf20Sopenharmony_ci goto nla_put_failure; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci genlmsg_end(skb, msg_head); 2128c2ecf20Sopenharmony_ci genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cinla_put_failure: 2178c2ecf20Sopenharmony_ci kfree_skb(skb); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cifail: 2208c2ecf20Sopenharmony_ci rcu_read_lock(); 2218c2ecf20Sopenharmony_ci master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 2228c2ecf20Sopenharmony_ci netdev_warn(master->dev, "Could not send HSR ring error message\n"); 2238c2ecf20Sopenharmony_ci rcu_read_unlock(); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* This is called when we haven't heard from the node with MAC address addr for 2278c2ecf20Sopenharmony_ci * some time (just before the node is removed from the node table/list). 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_civoid hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct sk_buff *skb; 2328c2ecf20Sopenharmony_ci void *msg_head; 2338c2ecf20Sopenharmony_ci struct hsr_port *master; 2348c2ecf20Sopenharmony_ci int res; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 2378c2ecf20Sopenharmony_ci if (!skb) 2388c2ecf20Sopenharmony_ci goto fail; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN); 2418c2ecf20Sopenharmony_ci if (!msg_head) 2428c2ecf20Sopenharmony_ci goto nla_put_failure; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 2458c2ecf20Sopenharmony_ci if (res < 0) 2468c2ecf20Sopenharmony_ci goto nla_put_failure; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci genlmsg_end(skb, msg_head); 2498c2ecf20Sopenharmony_ci genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cinla_put_failure: 2548c2ecf20Sopenharmony_ci kfree_skb(skb); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cifail: 2578c2ecf20Sopenharmony_ci rcu_read_lock(); 2588c2ecf20Sopenharmony_ci master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 2598c2ecf20Sopenharmony_ci netdev_warn(master->dev, "Could not send HSR node down\n"); 2608c2ecf20Sopenharmony_ci rcu_read_unlock(); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table 2648c2ecf20Sopenharmony_ci * about the status of a specific node in the network, defined by its MAC 2658c2ecf20Sopenharmony_ci * address. 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * Input: hsr ifindex, node mac address 2688c2ecf20Sopenharmony_ci * Output: hsr ifindex, node mac address (copied from request), 2698c2ecf20Sopenharmony_ci * age of latest frame from node over slave 1, slave 2 [ms] 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci /* For receiving */ 2748c2ecf20Sopenharmony_ci struct nlattr *na; 2758c2ecf20Sopenharmony_ci struct net_device *hsr_dev; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* For sending */ 2788c2ecf20Sopenharmony_ci struct sk_buff *skb_out; 2798c2ecf20Sopenharmony_ci void *msg_head; 2808c2ecf20Sopenharmony_ci struct hsr_priv *hsr; 2818c2ecf20Sopenharmony_ci struct hsr_port *port; 2828c2ecf20Sopenharmony_ci unsigned char hsr_node_addr_b[ETH_ALEN]; 2838c2ecf20Sopenharmony_ci int hsr_node_if1_age; 2848c2ecf20Sopenharmony_ci u16 hsr_node_if1_seq; 2858c2ecf20Sopenharmony_ci int hsr_node_if2_age; 2868c2ecf20Sopenharmony_ci u16 hsr_node_if2_seq; 2878c2ecf20Sopenharmony_ci int addr_b_ifindex; 2888c2ecf20Sopenharmony_ci int res; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!info) 2918c2ecf20Sopenharmony_ci goto invalid; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci na = info->attrs[HSR_A_IFINDEX]; 2948c2ecf20Sopenharmony_ci if (!na) 2958c2ecf20Sopenharmony_ci goto invalid; 2968c2ecf20Sopenharmony_ci na = info->attrs[HSR_A_NODE_ADDR]; 2978c2ecf20Sopenharmony_ci if (!na) 2988c2ecf20Sopenharmony_ci goto invalid; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci rcu_read_lock(); 3018c2ecf20Sopenharmony_ci hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 3028c2ecf20Sopenharmony_ci nla_get_u32(info->attrs[HSR_A_IFINDEX])); 3038c2ecf20Sopenharmony_ci if (!hsr_dev) 3048c2ecf20Sopenharmony_ci goto rcu_unlock; 3058c2ecf20Sopenharmony_ci if (!is_hsr_master(hsr_dev)) 3068c2ecf20Sopenharmony_ci goto rcu_unlock; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Send reply */ 3098c2ecf20Sopenharmony_ci skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 3108c2ecf20Sopenharmony_ci if (!skb_out) { 3118c2ecf20Sopenharmony_ci res = -ENOMEM; 3128c2ecf20Sopenharmony_ci goto fail; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 3168c2ecf20Sopenharmony_ci info->snd_seq, &hsr_genl_family, 0, 3178c2ecf20Sopenharmony_ci HSR_C_SET_NODE_STATUS); 3188c2ecf20Sopenharmony_ci if (!msg_head) { 3198c2ecf20Sopenharmony_ci res = -ENOMEM; 3208c2ecf20Sopenharmony_ci goto nla_put_failure; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 3248c2ecf20Sopenharmony_ci if (res < 0) 3258c2ecf20Sopenharmony_ci goto nla_put_failure; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci hsr = netdev_priv(hsr_dev); 3288c2ecf20Sopenharmony_ci res = hsr_get_node_data(hsr, 3298c2ecf20Sopenharmony_ci (unsigned char *) 3308c2ecf20Sopenharmony_ci nla_data(info->attrs[HSR_A_NODE_ADDR]), 3318c2ecf20Sopenharmony_ci hsr_node_addr_b, 3328c2ecf20Sopenharmony_ci &addr_b_ifindex, 3338c2ecf20Sopenharmony_ci &hsr_node_if1_age, 3348c2ecf20Sopenharmony_ci &hsr_node_if1_seq, 3358c2ecf20Sopenharmony_ci &hsr_node_if2_age, 3368c2ecf20Sopenharmony_ci &hsr_node_if2_seq); 3378c2ecf20Sopenharmony_ci if (res < 0) 3388c2ecf20Sopenharmony_ci goto nla_put_failure; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, 3418c2ecf20Sopenharmony_ci nla_data(info->attrs[HSR_A_NODE_ADDR])); 3428c2ecf20Sopenharmony_ci if (res < 0) 3438c2ecf20Sopenharmony_ci goto nla_put_failure; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (addr_b_ifindex > -1) { 3468c2ecf20Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN, 3478c2ecf20Sopenharmony_ci hsr_node_addr_b); 3488c2ecf20Sopenharmony_ci if (res < 0) 3498c2ecf20Sopenharmony_ci goto nla_put_failure; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, 3528c2ecf20Sopenharmony_ci addr_b_ifindex); 3538c2ecf20Sopenharmony_ci if (res < 0) 3548c2ecf20Sopenharmony_ci goto nla_put_failure; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age); 3588c2ecf20Sopenharmony_ci if (res < 0) 3598c2ecf20Sopenharmony_ci goto nla_put_failure; 3608c2ecf20Sopenharmony_ci res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); 3618c2ecf20Sopenharmony_ci if (res < 0) 3628c2ecf20Sopenharmony_ci goto nla_put_failure; 3638c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 3648c2ecf20Sopenharmony_ci if (port) 3658c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, 3668c2ecf20Sopenharmony_ci port->dev->ifindex); 3678c2ecf20Sopenharmony_ci if (res < 0) 3688c2ecf20Sopenharmony_ci goto nla_put_failure; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age); 3718c2ecf20Sopenharmony_ci if (res < 0) 3728c2ecf20Sopenharmony_ci goto nla_put_failure; 3738c2ecf20Sopenharmony_ci res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); 3748c2ecf20Sopenharmony_ci if (res < 0) 3758c2ecf20Sopenharmony_ci goto nla_put_failure; 3768c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 3778c2ecf20Sopenharmony_ci if (port) 3788c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, 3798c2ecf20Sopenharmony_ci port->dev->ifindex); 3808c2ecf20Sopenharmony_ci if (res < 0) 3818c2ecf20Sopenharmony_ci goto nla_put_failure; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci rcu_read_unlock(); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci genlmsg_end(skb_out, msg_head); 3868c2ecf20Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_circu_unlock: 3918c2ecf20Sopenharmony_ci rcu_read_unlock(); 3928c2ecf20Sopenharmony_ciinvalid: 3938c2ecf20Sopenharmony_ci netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cinla_put_failure: 3978c2ecf20Sopenharmony_ci kfree_skb(skb_out); 3988c2ecf20Sopenharmony_ci /* Fall through */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cifail: 4018c2ecf20Sopenharmony_ci rcu_read_unlock(); 4028c2ecf20Sopenharmony_ci return res; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* Get a list of MacAddressA of all nodes known to this node (including self). 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned char addr[ETH_ALEN]; 4108c2ecf20Sopenharmony_ci struct net_device *hsr_dev; 4118c2ecf20Sopenharmony_ci struct sk_buff *skb_out; 4128c2ecf20Sopenharmony_ci struct hsr_priv *hsr; 4138c2ecf20Sopenharmony_ci bool restart = false; 4148c2ecf20Sopenharmony_ci struct nlattr *na; 4158c2ecf20Sopenharmony_ci void *pos = NULL; 4168c2ecf20Sopenharmony_ci void *msg_head; 4178c2ecf20Sopenharmony_ci int res; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (!info) 4208c2ecf20Sopenharmony_ci goto invalid; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci na = info->attrs[HSR_A_IFINDEX]; 4238c2ecf20Sopenharmony_ci if (!na) 4248c2ecf20Sopenharmony_ci goto invalid; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci rcu_read_lock(); 4278c2ecf20Sopenharmony_ci hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 4288c2ecf20Sopenharmony_ci nla_get_u32(info->attrs[HSR_A_IFINDEX])); 4298c2ecf20Sopenharmony_ci if (!hsr_dev) 4308c2ecf20Sopenharmony_ci goto rcu_unlock; 4318c2ecf20Sopenharmony_ci if (!is_hsr_master(hsr_dev)) 4328c2ecf20Sopenharmony_ci goto rcu_unlock; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cirestart: 4358c2ecf20Sopenharmony_ci /* Send reply */ 4368c2ecf20Sopenharmony_ci skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); 4378c2ecf20Sopenharmony_ci if (!skb_out) { 4388c2ecf20Sopenharmony_ci res = -ENOMEM; 4398c2ecf20Sopenharmony_ci goto fail; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 4438c2ecf20Sopenharmony_ci info->snd_seq, &hsr_genl_family, 0, 4448c2ecf20Sopenharmony_ci HSR_C_SET_NODE_LIST); 4458c2ecf20Sopenharmony_ci if (!msg_head) { 4468c2ecf20Sopenharmony_ci res = -ENOMEM; 4478c2ecf20Sopenharmony_ci goto nla_put_failure; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (!restart) { 4518c2ecf20Sopenharmony_ci res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 4528c2ecf20Sopenharmony_ci if (res < 0) 4538c2ecf20Sopenharmony_ci goto nla_put_failure; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci hsr = netdev_priv(hsr_dev); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!pos) 4598c2ecf20Sopenharmony_ci pos = hsr_get_next_node(hsr, NULL, addr); 4608c2ecf20Sopenharmony_ci while (pos) { 4618c2ecf20Sopenharmony_ci res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr); 4628c2ecf20Sopenharmony_ci if (res < 0) { 4638c2ecf20Sopenharmony_ci if (res == -EMSGSIZE) { 4648c2ecf20Sopenharmony_ci genlmsg_end(skb_out, msg_head); 4658c2ecf20Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, 4668c2ecf20Sopenharmony_ci info->snd_portid); 4678c2ecf20Sopenharmony_ci restart = true; 4688c2ecf20Sopenharmony_ci goto restart; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci goto nla_put_failure; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci pos = hsr_get_next_node(hsr, pos, addr); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci rcu_read_unlock(); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci genlmsg_end(skb_out, msg_head); 4778c2ecf20Sopenharmony_ci genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_circu_unlock: 4828c2ecf20Sopenharmony_ci rcu_read_unlock(); 4838c2ecf20Sopenharmony_ciinvalid: 4848c2ecf20Sopenharmony_ci netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cinla_put_failure: 4888c2ecf20Sopenharmony_ci nlmsg_free(skb_out); 4898c2ecf20Sopenharmony_ci /* Fall through */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cifail: 4928c2ecf20Sopenharmony_ci rcu_read_unlock(); 4938c2ecf20Sopenharmony_ci return res; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic const struct genl_small_ops hsr_ops[] = { 4978c2ecf20Sopenharmony_ci { 4988c2ecf20Sopenharmony_ci .cmd = HSR_C_GET_NODE_STATUS, 4998c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 5008c2ecf20Sopenharmony_ci .flags = 0, 5018c2ecf20Sopenharmony_ci .doit = hsr_get_node_status, 5028c2ecf20Sopenharmony_ci .dumpit = NULL, 5038c2ecf20Sopenharmony_ci }, 5048c2ecf20Sopenharmony_ci { 5058c2ecf20Sopenharmony_ci .cmd = HSR_C_GET_NODE_LIST, 5068c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 5078c2ecf20Sopenharmony_ci .flags = 0, 5088c2ecf20Sopenharmony_ci .doit = hsr_get_node_list, 5098c2ecf20Sopenharmony_ci .dumpit = NULL, 5108c2ecf20Sopenharmony_ci }, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic struct genl_family hsr_genl_family __ro_after_init = { 5148c2ecf20Sopenharmony_ci .hdrsize = 0, 5158c2ecf20Sopenharmony_ci .name = "HSR", 5168c2ecf20Sopenharmony_ci .version = 1, 5178c2ecf20Sopenharmony_ci .maxattr = HSR_A_MAX, 5188c2ecf20Sopenharmony_ci .policy = hsr_genl_policy, 5198c2ecf20Sopenharmony_ci .netnsok = true, 5208c2ecf20Sopenharmony_ci .module = THIS_MODULE, 5218c2ecf20Sopenharmony_ci .small_ops = hsr_ops, 5228c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(hsr_ops), 5238c2ecf20Sopenharmony_ci .mcgrps = hsr_mcgrps, 5248c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(hsr_mcgrps), 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciint __init hsr_netlink_init(void) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int rc; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci rc = rtnl_link_register(&hsr_link_ops); 5328c2ecf20Sopenharmony_ci if (rc) 5338c2ecf20Sopenharmony_ci goto fail_rtnl_link_register; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci rc = genl_register_family(&hsr_genl_family); 5368c2ecf20Sopenharmony_ci if (rc) 5378c2ecf20Sopenharmony_ci goto fail_genl_register_family; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci hsr_debugfs_create_root(); 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cifail_genl_register_family: 5438c2ecf20Sopenharmony_ci rtnl_link_unregister(&hsr_link_ops); 5448c2ecf20Sopenharmony_cifail_rtnl_link_register: 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return rc; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_civoid __exit hsr_netlink_exit(void) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci genl_unregister_family(&hsr_genl_family); 5528c2ecf20Sopenharmony_ci rtnl_link_unregister(&hsr_link_ops); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("hsr"); 556