10e7623bdSMurali Karicheri // SPDX-License-Identifier: GPL-2.0 270ebe4a4SArvid Brodin /* Copyright 2011-2014 Autronica Fire and Security AS 3f421436aSArvid Brodin * 4f421436aSArvid Brodin * Author(s): 570ebe4a4SArvid Brodin * 2011-2014 Arvid Brodin, arvid.brodin@alten.se 6f421436aSArvid Brodin * 78f4c0e01SMurali Karicheri * Routines for handling Netlink messages for HSR and PRP. 8f421436aSArvid Brodin */ 9f421436aSArvid Brodin 10f421436aSArvid Brodin #include "hsr_netlink.h" 11f421436aSArvid Brodin #include <linux/kernel.h> 12f421436aSArvid Brodin #include <net/rtnetlink.h> 13f421436aSArvid Brodin #include <net/genetlink.h> 14f421436aSArvid Brodin #include "hsr_main.h" 15f421436aSArvid Brodin #include "hsr_device.h" 16f421436aSArvid Brodin #include "hsr_framereg.h" 17f421436aSArvid Brodin 18f421436aSArvid Brodin static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = { 19f421436aSArvid Brodin [IFLA_HSR_SLAVE1] = { .type = NLA_U32 }, 20f421436aSArvid Brodin [IFLA_HSR_SLAVE2] = { .type = NLA_U32 }, 21f421436aSArvid Brodin [IFLA_HSR_MULTICAST_SPEC] = { .type = NLA_U8 }, 22ee1c2797SPeter Heise [IFLA_HSR_VERSION] = { .type = NLA_U8 }, 23f9375729SPeter Heise [IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN }, 2498bf8362SArvid Brodin [IFLA_HSR_SEQ_NR] = { .type = NLA_U16 }, 258f4c0e01SMurali Karicheri [IFLA_HSR_PROTOCOL] = { .type = NLA_U8 }, 26f421436aSArvid Brodin }; 27f421436aSArvid Brodin 28f421436aSArvid Brodin /* Here, it seems a netdevice has already been allocated for us, and the 29f421436aSArvid Brodin * hsr_dev_setup routine has been executed. Nice! 30f421436aSArvid Brodin */ 31f421436aSArvid Brodin static int hsr_newlink(struct net *src_net, struct net_device *dev, 327a3f4a18SMatthias Schiffer struct nlattr *tb[], struct nlattr *data[], 337a3f4a18SMatthias Schiffer struct netlink_ext_ack *extack) 34f421436aSArvid Brodin { 358f4c0e01SMurali Karicheri enum hsr_version proto_version; 368f4c0e01SMurali Karicheri unsigned char multicast_spec; 378f4c0e01SMurali Karicheri u8 proto = HSR_PROTOCOL_HSR; 38f421436aSArvid Brodin struct net_device *link[2]; 39f421436aSArvid Brodin 40a718dcc5SArvid Brodin if (!data) { 4113eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "No slave devices specified"); 42a718dcc5SArvid Brodin return -EINVAL; 43a718dcc5SArvid Brodin } 44f421436aSArvid Brodin if (!data[IFLA_HSR_SLAVE1]) { 4513eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Slave1 device not specified"); 46f421436aSArvid Brodin return -EINVAL; 47f421436aSArvid Brodin } 48d595b85aSMurali Karicheri link[0] = __dev_get_by_index(src_net, 49d595b85aSMurali Karicheri nla_get_u32(data[IFLA_HSR_SLAVE1])); 5013eeb5feSTaehee Yoo if (!link[0]) { 5113eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Slave1 does not exist"); 5213eeb5feSTaehee Yoo return -EINVAL; 5313eeb5feSTaehee Yoo } 54f421436aSArvid Brodin if (!data[IFLA_HSR_SLAVE2]) { 5513eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Slave2 device not specified"); 56f421436aSArvid Brodin return -EINVAL; 57f421436aSArvid Brodin } 58d595b85aSMurali Karicheri link[1] = __dev_get_by_index(src_net, 59d595b85aSMurali Karicheri nla_get_u32(data[IFLA_HSR_SLAVE2])); 6013eeb5feSTaehee Yoo if (!link[1]) { 6113eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Slave2 does not exist"); 62f421436aSArvid Brodin return -EINVAL; 6313eeb5feSTaehee Yoo } 6413eeb5feSTaehee Yoo 6513eeb5feSTaehee Yoo if (link[0] == link[1]) { 6613eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same"); 6713eeb5feSTaehee Yoo return -EINVAL; 6813eeb5feSTaehee Yoo } 69f421436aSArvid Brodin 70f421436aSArvid Brodin if (!data[IFLA_HSR_MULTICAST_SPEC]) 71f421436aSArvid Brodin multicast_spec = 0; 72f421436aSArvid Brodin else 73f421436aSArvid Brodin multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]); 74f421436aSArvid Brodin 758f4c0e01SMurali Karicheri if (data[IFLA_HSR_PROTOCOL]) 768f4c0e01SMurali Karicheri proto = nla_get_u8(data[IFLA_HSR_PROTOCOL]); 778f4c0e01SMurali Karicheri 788f4c0e01SMurali Karicheri if (proto >= HSR_PROTOCOL_MAX) { 79b87f9fe1SYe Bin NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol"); 808f4c0e01SMurali Karicheri return -EINVAL; 818f4c0e01SMurali Karicheri } 828f4c0e01SMurali Karicheri 834faab8c4STaehee Yoo if (!data[IFLA_HSR_VERSION]) { 848f4c0e01SMurali Karicheri proto_version = HSR_V0; 854faab8c4STaehee Yoo } else { 868f4c0e01SMurali Karicheri if (proto == HSR_PROTOCOL_PRP) { 87b87f9fe1SYe Bin NL_SET_ERR_MSG_MOD(extack, "PRP version unsupported"); 888f4c0e01SMurali Karicheri return -EINVAL; 898f4c0e01SMurali Karicheri } 908f4c0e01SMurali Karicheri 918f4c0e01SMurali Karicheri proto_version = nla_get_u8(data[IFLA_HSR_VERSION]); 928f4c0e01SMurali Karicheri if (proto_version > HSR_V1) { 934faab8c4STaehee Yoo NL_SET_ERR_MSG_MOD(extack, 94b87f9fe1SYe Bin "Only HSR version 0/1 supported"); 954faab8c4STaehee Yoo return -EINVAL; 964faab8c4STaehee Yoo } 974faab8c4STaehee Yoo } 98ee1c2797SPeter Heise 998f4c0e01SMurali Karicheri if (proto == HSR_PROTOCOL_PRP) 1008f4c0e01SMurali Karicheri proto_version = PRP_V1; 1018f4c0e01SMurali Karicheri 1028f4c0e01SMurali Karicheri return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack); 103f421436aSArvid Brodin } 104f421436aSArvid Brodin 105de0083c7STaehee Yoo static void hsr_dellink(struct net_device *dev, struct list_head *head) 106de0083c7STaehee Yoo { 107de0083c7STaehee Yoo struct hsr_priv *hsr = netdev_priv(dev); 108*4acc45dbSJuhee Kang int i; 109de0083c7STaehee Yoo 110de0083c7STaehee Yoo del_timer_sync(&hsr->prune_timer); 111de0083c7STaehee Yoo del_timer_sync(&hsr->announce_timer); 112de0083c7STaehee Yoo 113de0083c7STaehee Yoo hsr_debugfs_term(hsr); 114de0083c7STaehee Yoo hsr_del_ports(hsr); 115de0083c7STaehee Yoo 116de0083c7STaehee Yoo hsr_del_self_node(hsr); 117*4acc45dbSJuhee Kang for (i = 0; i < hsr->hash_buckets; i++) 118*4acc45dbSJuhee Kang hsr_del_nodes(&hsr->node_db[i]); 119de0083c7STaehee Yoo 120de0083c7STaehee Yoo unregister_netdevice_queue(dev, head); 121de0083c7STaehee Yoo } 122de0083c7STaehee Yoo 12398bf8362SArvid Brodin static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) 12498bf8362SArvid Brodin { 12581390d0cSTaehee Yoo struct hsr_priv *hsr = netdev_priv(dev); 1268f4c0e01SMurali Karicheri u8 proto = HSR_PROTOCOL_HSR; 127c5a75911SArvid Brodin struct hsr_port *port; 12898bf8362SArvid Brodin 129c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 13081390d0cSTaehee Yoo if (port) { 13181390d0cSTaehee Yoo if (nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex)) 13298bf8362SArvid Brodin goto nla_put_failure; 13381390d0cSTaehee Yoo } 13498bf8362SArvid Brodin 135c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 13681390d0cSTaehee Yoo if (port) { 13781390d0cSTaehee Yoo if (nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex)) 13898bf8362SArvid Brodin goto nla_put_failure; 13981390d0cSTaehee Yoo } 14098bf8362SArvid Brodin 14198bf8362SArvid Brodin if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, 14270ebe4a4SArvid Brodin hsr->sup_multicast_addr) || 14370ebe4a4SArvid Brodin nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr)) 14498bf8362SArvid Brodin goto nla_put_failure; 1458f4c0e01SMurali Karicheri if (hsr->prot_version == PRP_V1) 1468f4c0e01SMurali Karicheri proto = HSR_PROTOCOL_PRP; 1478f4c0e01SMurali Karicheri if (nla_put_u8(skb, IFLA_HSR_PROTOCOL, proto)) 1488f4c0e01SMurali Karicheri goto nla_put_failure; 14998bf8362SArvid Brodin 15098bf8362SArvid Brodin return 0; 15198bf8362SArvid Brodin 15298bf8362SArvid Brodin nla_put_failure: 15398bf8362SArvid Brodin return -EMSGSIZE; 15498bf8362SArvid Brodin } 15598bf8362SArvid Brodin 156f421436aSArvid Brodin static struct rtnl_link_ops hsr_link_ops __read_mostly = { 157f421436aSArvid Brodin .kind = "hsr", 158f421436aSArvid Brodin .maxtype = IFLA_HSR_MAX, 159f421436aSArvid Brodin .policy = hsr_policy, 160f421436aSArvid Brodin .priv_size = sizeof(struct hsr_priv), 161f421436aSArvid Brodin .setup = hsr_dev_setup, 162f421436aSArvid Brodin .newlink = hsr_newlink, 163de0083c7STaehee Yoo .dellink = hsr_dellink, 16498bf8362SArvid Brodin .fill_info = hsr_fill_info, 165f421436aSArvid Brodin }; 166f421436aSArvid Brodin 167f421436aSArvid Brodin /* attribute policy */ 168f421436aSArvid Brodin static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { 169f9375729SPeter Heise [HSR_A_NODE_ADDR] = { .len = ETH_ALEN }, 170f9375729SPeter Heise [HSR_A_NODE_ADDR_B] = { .len = ETH_ALEN }, 171f421436aSArvid Brodin [HSR_A_IFINDEX] = { .type = NLA_U32 }, 172f421436aSArvid Brodin [HSR_A_IF1_AGE] = { .type = NLA_U32 }, 173f421436aSArvid Brodin [HSR_A_IF2_AGE] = { .type = NLA_U32 }, 174f421436aSArvid Brodin [HSR_A_IF1_SEQ] = { .type = NLA_U16 }, 175f421436aSArvid Brodin [HSR_A_IF2_SEQ] = { .type = NLA_U16 }, 176f421436aSArvid Brodin }; 177f421436aSArvid Brodin 178489111e5SJohannes Berg static struct genl_family hsr_genl_family; 179f421436aSArvid Brodin 1802a94fe48SJohannes Berg static const struct genl_multicast_group hsr_mcgrps[] = { 1812a94fe48SJohannes Berg { .name = "hsr-network", }, 182f421436aSArvid Brodin }; 183f421436aSArvid Brodin 184f421436aSArvid Brodin /* This is called if for some node with MAC address addr, we only get frames 185f421436aSArvid Brodin * over one of the slave interfaces. This would indicate an open network ring 186f421436aSArvid Brodin * (i.e. a link has failed somewhere). 187f421436aSArvid Brodin */ 18870ebe4a4SArvid Brodin void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], 189c5a75911SArvid Brodin struct hsr_port *port) 190f421436aSArvid Brodin { 191f421436aSArvid Brodin struct sk_buff *skb; 192f421436aSArvid Brodin void *msg_head; 193c5a75911SArvid Brodin struct hsr_port *master; 194f421436aSArvid Brodin int res; 195f421436aSArvid Brodin 196f421436aSArvid Brodin skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 197f421436aSArvid Brodin if (!skb) 198f421436aSArvid Brodin goto fail; 199f421436aSArvid Brodin 200d595b85aSMurali Karicheri msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, 201d595b85aSMurali Karicheri HSR_C_RING_ERROR); 202f421436aSArvid Brodin if (!msg_head) 203f421436aSArvid Brodin goto nla_put_failure; 204f421436aSArvid Brodin 205f421436aSArvid Brodin res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 206f421436aSArvid Brodin if (res < 0) 207f421436aSArvid Brodin goto nla_put_failure; 208f421436aSArvid Brodin 209c5a75911SArvid Brodin res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex); 210f421436aSArvid Brodin if (res < 0) 211f421436aSArvid Brodin goto nla_put_failure; 212f421436aSArvid Brodin 213f421436aSArvid Brodin genlmsg_end(skb, msg_head); 2142a94fe48SJohannes Berg genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 215f421436aSArvid Brodin 216f421436aSArvid Brodin return; 217f421436aSArvid Brodin 218f421436aSArvid Brodin nla_put_failure: 219f421436aSArvid Brodin kfree_skb(skb); 220f421436aSArvid Brodin 221f421436aSArvid Brodin fail: 222c5a75911SArvid Brodin rcu_read_lock(); 223c5a75911SArvid Brodin master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 224c5a75911SArvid Brodin netdev_warn(master->dev, "Could not send HSR ring error message\n"); 225c5a75911SArvid Brodin rcu_read_unlock(); 226f421436aSArvid Brodin } 227f421436aSArvid Brodin 228f421436aSArvid Brodin /* This is called when we haven't heard from the node with MAC address addr for 229f421436aSArvid Brodin * some time (just before the node is removed from the node table/list). 230f421436aSArvid Brodin */ 23170ebe4a4SArvid Brodin void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]) 232f421436aSArvid Brodin { 233f421436aSArvid Brodin struct sk_buff *skb; 234f421436aSArvid Brodin void *msg_head; 235c5a75911SArvid Brodin struct hsr_port *master; 236f421436aSArvid Brodin int res; 237f421436aSArvid Brodin 238f421436aSArvid Brodin skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 239f421436aSArvid Brodin if (!skb) 240f421436aSArvid Brodin goto fail; 241f421436aSArvid Brodin 242f421436aSArvid Brodin msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN); 243f421436aSArvid Brodin if (!msg_head) 244f421436aSArvid Brodin goto nla_put_failure; 245f421436aSArvid Brodin 246f421436aSArvid Brodin res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); 247f421436aSArvid Brodin if (res < 0) 248f421436aSArvid Brodin goto nla_put_failure; 249f421436aSArvid Brodin 250f421436aSArvid Brodin genlmsg_end(skb, msg_head); 2512a94fe48SJohannes Berg genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); 252f421436aSArvid Brodin 253f421436aSArvid Brodin return; 254f421436aSArvid Brodin 255f421436aSArvid Brodin nla_put_failure: 256f421436aSArvid Brodin kfree_skb(skb); 257f421436aSArvid Brodin 258f421436aSArvid Brodin fail: 259c5a75911SArvid Brodin rcu_read_lock(); 260c5a75911SArvid Brodin master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 261c5a75911SArvid Brodin netdev_warn(master->dev, "Could not send HSR node down\n"); 262c5a75911SArvid Brodin rcu_read_unlock(); 263f421436aSArvid Brodin } 264f421436aSArvid Brodin 265f421436aSArvid Brodin /* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table 266f421436aSArvid Brodin * about the status of a specific node in the network, defined by its MAC 267f421436aSArvid Brodin * address. 268f421436aSArvid Brodin * 269f421436aSArvid Brodin * Input: hsr ifindex, node mac address 270f421436aSArvid Brodin * Output: hsr ifindex, node mac address (copied from request), 271f421436aSArvid Brodin * age of latest frame from node over slave 1, slave 2 [ms] 272f421436aSArvid Brodin */ 273f421436aSArvid Brodin static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) 274f421436aSArvid Brodin { 275f421436aSArvid Brodin /* For receiving */ 276f421436aSArvid Brodin struct nlattr *na; 277c5a75911SArvid Brodin struct net_device *hsr_dev; 278f421436aSArvid Brodin 279f421436aSArvid Brodin /* For sending */ 280f421436aSArvid Brodin struct sk_buff *skb_out; 281f421436aSArvid Brodin void *msg_head; 28270ebe4a4SArvid Brodin struct hsr_priv *hsr; 283c5a75911SArvid Brodin struct hsr_port *port; 284f421436aSArvid Brodin unsigned char hsr_node_addr_b[ETH_ALEN]; 285f421436aSArvid Brodin int hsr_node_if1_age; 286f421436aSArvid Brodin u16 hsr_node_if1_seq; 287f421436aSArvid Brodin int hsr_node_if2_age; 288f421436aSArvid Brodin u16 hsr_node_if2_seq; 289f421436aSArvid Brodin int addr_b_ifindex; 290f421436aSArvid Brodin int res; 291f421436aSArvid Brodin 292f421436aSArvid Brodin if (!info) 293f421436aSArvid Brodin goto invalid; 294f421436aSArvid Brodin 295f421436aSArvid Brodin na = info->attrs[HSR_A_IFINDEX]; 296f421436aSArvid Brodin if (!na) 297f421436aSArvid Brodin goto invalid; 298f421436aSArvid Brodin na = info->attrs[HSR_A_NODE_ADDR]; 299f421436aSArvid Brodin if (!na) 300f421436aSArvid Brodin goto invalid; 301f421436aSArvid Brodin 302173756b8STaehee Yoo rcu_read_lock(); 303173756b8STaehee Yoo hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 304f421436aSArvid Brodin nla_get_u32(info->attrs[HSR_A_IFINDEX])); 305f421436aSArvid Brodin if (!hsr_dev) 306173756b8STaehee Yoo goto rcu_unlock; 307f421436aSArvid Brodin if (!is_hsr_master(hsr_dev)) 308173756b8STaehee Yoo goto rcu_unlock; 309f421436aSArvid Brodin 310f421436aSArvid Brodin /* Send reply */ 311173756b8STaehee Yoo skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 312f421436aSArvid Brodin if (!skb_out) { 313f421436aSArvid Brodin res = -ENOMEM; 314f421436aSArvid Brodin goto fail; 315f421436aSArvid Brodin } 316f421436aSArvid Brodin 317f421436aSArvid Brodin msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 318f421436aSArvid Brodin info->snd_seq, &hsr_genl_family, 0, 319f421436aSArvid Brodin HSR_C_SET_NODE_STATUS); 320f421436aSArvid Brodin if (!msg_head) { 321f421436aSArvid Brodin res = -ENOMEM; 322f421436aSArvid Brodin goto nla_put_failure; 323f421436aSArvid Brodin } 324f421436aSArvid Brodin 325f421436aSArvid Brodin res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 326f421436aSArvid Brodin if (res < 0) 327f421436aSArvid Brodin goto nla_put_failure; 328f421436aSArvid Brodin 32970ebe4a4SArvid Brodin hsr = netdev_priv(hsr_dev); 33070ebe4a4SArvid Brodin res = hsr_get_node_data(hsr, 331d595b85aSMurali Karicheri (unsigned char *) 332d595b85aSMurali Karicheri nla_data(info->attrs[HSR_A_NODE_ADDR]), 333f421436aSArvid Brodin hsr_node_addr_b, 334f421436aSArvid Brodin &addr_b_ifindex, 335f421436aSArvid Brodin &hsr_node_if1_age, 336f421436aSArvid Brodin &hsr_node_if1_seq, 337f421436aSArvid Brodin &hsr_node_if2_age, 338f421436aSArvid Brodin &hsr_node_if2_seq); 339f421436aSArvid Brodin if (res < 0) 34084a035f6SGeyslan G. Bem goto nla_put_failure; 341f421436aSArvid Brodin 342f421436aSArvid Brodin res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, 343f421436aSArvid Brodin nla_data(info->attrs[HSR_A_NODE_ADDR])); 344f421436aSArvid Brodin if (res < 0) 345f421436aSArvid Brodin goto nla_put_failure; 346f421436aSArvid Brodin 347f421436aSArvid Brodin if (addr_b_ifindex > -1) { 348f421436aSArvid Brodin res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN, 349f421436aSArvid Brodin hsr_node_addr_b); 350f421436aSArvid Brodin if (res < 0) 351f421436aSArvid Brodin goto nla_put_failure; 352f421436aSArvid Brodin 353d595b85aSMurali Karicheri res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, 354d595b85aSMurali Karicheri addr_b_ifindex); 355f421436aSArvid Brodin if (res < 0) 356f421436aSArvid Brodin goto nla_put_failure; 357f421436aSArvid Brodin } 358f421436aSArvid Brodin 359f421436aSArvid Brodin res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age); 360f421436aSArvid Brodin if (res < 0) 361f421436aSArvid Brodin goto nla_put_failure; 362f421436aSArvid Brodin res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); 363f421436aSArvid Brodin if (res < 0) 364f421436aSArvid Brodin goto nla_put_failure; 365c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); 366c5a75911SArvid Brodin if (port) 367c5a75911SArvid Brodin res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, 368c5a75911SArvid Brodin port->dev->ifindex); 369f421436aSArvid Brodin if (res < 0) 370f421436aSArvid Brodin goto nla_put_failure; 371f421436aSArvid Brodin 372f421436aSArvid Brodin res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age); 373f421436aSArvid Brodin if (res < 0) 374f421436aSArvid Brodin goto nla_put_failure; 375f421436aSArvid Brodin res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); 376f421436aSArvid Brodin if (res < 0) 377f421436aSArvid Brodin goto nla_put_failure; 378c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 379c5a75911SArvid Brodin if (port) 380c5a75911SArvid Brodin res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, 381c5a75911SArvid Brodin port->dev->ifindex); 38251f3c605SArvid Brodin if (res < 0) 38351f3c605SArvid Brodin goto nla_put_failure; 384f421436aSArvid Brodin 385173756b8STaehee Yoo rcu_read_unlock(); 386173756b8STaehee Yoo 387f421436aSArvid Brodin genlmsg_end(skb_out, msg_head); 388f421436aSArvid Brodin genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 389f421436aSArvid Brodin 390f421436aSArvid Brodin return 0; 391f421436aSArvid Brodin 392173756b8STaehee Yoo rcu_unlock: 393173756b8STaehee Yoo rcu_read_unlock(); 394f421436aSArvid Brodin invalid: 3952d4bc933SJohannes Berg netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 396f421436aSArvid Brodin return 0; 397f421436aSArvid Brodin 398f421436aSArvid Brodin nla_put_failure: 399f421436aSArvid Brodin kfree_skb(skb_out); 400f421436aSArvid Brodin /* Fall through */ 401f421436aSArvid Brodin 402f421436aSArvid Brodin fail: 403173756b8STaehee Yoo rcu_read_unlock(); 404f421436aSArvid Brodin return res; 405f421436aSArvid Brodin } 406f421436aSArvid Brodin 407f266a683SArvid Brodin /* Get a list of MacAddressA of all nodes known to this node (including self). 408f421436aSArvid Brodin */ 409f421436aSArvid Brodin static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) 410f421436aSArvid Brodin { 411f421436aSArvid Brodin unsigned char addr[ETH_ALEN]; 412ca19c70fSTaehee Yoo struct net_device *hsr_dev; 413ca19c70fSTaehee Yoo struct sk_buff *skb_out; 414ca19c70fSTaehee Yoo struct hsr_priv *hsr; 415ca19c70fSTaehee Yoo bool restart = false; 416ca19c70fSTaehee Yoo struct nlattr *na; 417ca19c70fSTaehee Yoo void *pos = NULL; 418ca19c70fSTaehee Yoo void *msg_head; 419f421436aSArvid Brodin int res; 420f421436aSArvid Brodin 421f421436aSArvid Brodin if (!info) 422f421436aSArvid Brodin goto invalid; 423f421436aSArvid Brodin 424f421436aSArvid Brodin na = info->attrs[HSR_A_IFINDEX]; 425f421436aSArvid Brodin if (!na) 426f421436aSArvid Brodin goto invalid; 427f421436aSArvid Brodin 428173756b8STaehee Yoo rcu_read_lock(); 429173756b8STaehee Yoo hsr_dev = dev_get_by_index_rcu(genl_info_net(info), 430f421436aSArvid Brodin nla_get_u32(info->attrs[HSR_A_IFINDEX])); 431f421436aSArvid Brodin if (!hsr_dev) 432173756b8STaehee Yoo goto rcu_unlock; 433f421436aSArvid Brodin if (!is_hsr_master(hsr_dev)) 434173756b8STaehee Yoo goto rcu_unlock; 435f421436aSArvid Brodin 436ca19c70fSTaehee Yoo restart: 437f421436aSArvid Brodin /* Send reply */ 438ca19c70fSTaehee Yoo skb_out = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); 439f421436aSArvid Brodin if (!skb_out) { 440f421436aSArvid Brodin res = -ENOMEM; 441f421436aSArvid Brodin goto fail; 442f421436aSArvid Brodin } 443f421436aSArvid Brodin 444f421436aSArvid Brodin msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, 445f421436aSArvid Brodin info->snd_seq, &hsr_genl_family, 0, 446f421436aSArvid Brodin HSR_C_SET_NODE_LIST); 447f421436aSArvid Brodin if (!msg_head) { 448f421436aSArvid Brodin res = -ENOMEM; 449f421436aSArvid Brodin goto nla_put_failure; 450f421436aSArvid Brodin } 451f421436aSArvid Brodin 452ca19c70fSTaehee Yoo if (!restart) { 453f421436aSArvid Brodin res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); 454f421436aSArvid Brodin if (res < 0) 455f421436aSArvid Brodin goto nla_put_failure; 456ca19c70fSTaehee Yoo } 457f421436aSArvid Brodin 45870ebe4a4SArvid Brodin hsr = netdev_priv(hsr_dev); 459f421436aSArvid Brodin 460ca19c70fSTaehee Yoo if (!pos) 46170ebe4a4SArvid Brodin pos = hsr_get_next_node(hsr, NULL, addr); 462f421436aSArvid Brodin while (pos) { 463f421436aSArvid Brodin res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr); 464f421436aSArvid Brodin if (res < 0) { 465ca19c70fSTaehee Yoo if (res == -EMSGSIZE) { 466ca19c70fSTaehee Yoo genlmsg_end(skb_out, msg_head); 467ca19c70fSTaehee Yoo genlmsg_unicast(genl_info_net(info), skb_out, 468ca19c70fSTaehee Yoo info->snd_portid); 469ca19c70fSTaehee Yoo restart = true; 470ca19c70fSTaehee Yoo goto restart; 471ca19c70fSTaehee Yoo } 472f421436aSArvid Brodin goto nla_put_failure; 473f421436aSArvid Brodin } 47470ebe4a4SArvid Brodin pos = hsr_get_next_node(hsr, pos, addr); 475f421436aSArvid Brodin } 476f421436aSArvid Brodin rcu_read_unlock(); 477f421436aSArvid Brodin 478f421436aSArvid Brodin genlmsg_end(skb_out, msg_head); 479f421436aSArvid Brodin genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); 480f421436aSArvid Brodin 481f421436aSArvid Brodin return 0; 482f421436aSArvid Brodin 483173756b8STaehee Yoo rcu_unlock: 484173756b8STaehee Yoo rcu_read_unlock(); 485f421436aSArvid Brodin invalid: 4862d4bc933SJohannes Berg netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); 487f421436aSArvid Brodin return 0; 488f421436aSArvid Brodin 489f421436aSArvid Brodin nla_put_failure: 490ca19c70fSTaehee Yoo nlmsg_free(skb_out); 491f421436aSArvid Brodin /* Fall through */ 492f421436aSArvid Brodin 493f421436aSArvid Brodin fail: 494173756b8STaehee Yoo rcu_read_unlock(); 495f421436aSArvid Brodin return res; 496f421436aSArvid Brodin } 497f421436aSArvid Brodin 49866a9b928SJakub Kicinski static const struct genl_small_ops hsr_ops[] = { 4999504b3eeSJohannes Berg { 5009504b3eeSJohannes Berg .cmd = HSR_C_GET_NODE_STATUS, 501ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 5029504b3eeSJohannes Berg .flags = 0, 5039504b3eeSJohannes Berg .doit = hsr_get_node_status, 5049504b3eeSJohannes Berg .dumpit = NULL, 5059504b3eeSJohannes Berg }, 5069504b3eeSJohannes Berg { 507f421436aSArvid Brodin .cmd = HSR_C_GET_NODE_LIST, 508ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 509f421436aSArvid Brodin .flags = 0, 510f421436aSArvid Brodin .doit = hsr_get_node_list, 511f421436aSArvid Brodin .dumpit = NULL, 5129504b3eeSJohannes Berg }, 513f421436aSArvid Brodin }; 514f421436aSArvid Brodin 51556989f6dSJohannes Berg static struct genl_family hsr_genl_family __ro_after_init = { 516489111e5SJohannes Berg .hdrsize = 0, 517489111e5SJohannes Berg .name = "HSR", 518489111e5SJohannes Berg .version = 1, 519489111e5SJohannes Berg .maxattr = HSR_A_MAX, 5203b0f31f2SJohannes Berg .policy = hsr_genl_policy, 52109e91dbeSTaehee Yoo .netnsok = true, 522489111e5SJohannes Berg .module = THIS_MODULE, 52366a9b928SJakub Kicinski .small_ops = hsr_ops, 52466a9b928SJakub Kicinski .n_small_ops = ARRAY_SIZE(hsr_ops), 525489111e5SJohannes Berg .mcgrps = hsr_mcgrps, 526489111e5SJohannes Berg .n_mcgrps = ARRAY_SIZE(hsr_mcgrps), 527489111e5SJohannes Berg }; 528489111e5SJohannes Berg 529f421436aSArvid Brodin int __init hsr_netlink_init(void) 530f421436aSArvid Brodin { 531f421436aSArvid Brodin int rc; 532f421436aSArvid Brodin 533f421436aSArvid Brodin rc = rtnl_link_register(&hsr_link_ops); 534f421436aSArvid Brodin if (rc) 535f421436aSArvid Brodin goto fail_rtnl_link_register; 536f421436aSArvid Brodin 537489111e5SJohannes Berg rc = genl_register_family(&hsr_genl_family); 538f421436aSArvid Brodin if (rc) 539f421436aSArvid Brodin goto fail_genl_register_family; 540f421436aSArvid Brodin 541c6c4ccd7STaehee Yoo hsr_debugfs_create_root(); 542f421436aSArvid Brodin return 0; 543f421436aSArvid Brodin 544f421436aSArvid Brodin fail_genl_register_family: 545f421436aSArvid Brodin rtnl_link_unregister(&hsr_link_ops); 546f421436aSArvid Brodin fail_rtnl_link_register: 547f421436aSArvid Brodin 548f421436aSArvid Brodin return rc; 549f421436aSArvid Brodin } 550f421436aSArvid Brodin 551f421436aSArvid Brodin void __exit hsr_netlink_exit(void) 552f421436aSArvid Brodin { 553f421436aSArvid Brodin genl_unregister_family(&hsr_genl_family); 554f421436aSArvid Brodin rtnl_link_unregister(&hsr_link_ops); 555f421436aSArvid Brodin } 556f421436aSArvid Brodin 557f421436aSArvid Brodin MODULE_ALIAS_RTNL_LINK("hsr"); 558