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