162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2017 Mellanox Technologies Inc. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2010 Voltaire Inc. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/export.h> 3762306a36Sopenharmony_ci#include <net/netlink.h> 3862306a36Sopenharmony_ci#include <net/net_namespace.h> 3962306a36Sopenharmony_ci#include <net/netns/generic.h> 4062306a36Sopenharmony_ci#include <net/sock.h> 4162306a36Sopenharmony_ci#include <rdma/rdma_netlink.h> 4262306a36Sopenharmony_ci#include <linux/module.h> 4362306a36Sopenharmony_ci#include "core_priv.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct { 4662306a36Sopenharmony_ci const struct rdma_nl_cbs *cb_table; 4762306a36Sopenharmony_ci /* Synchronizes between ongoing netlink commands and netlink client 4862306a36Sopenharmony_ci * unregistration. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci struct rw_semaphore sem; 5162306a36Sopenharmony_ci} rdma_nl_types[RDMA_NL_NUM_CLIENTS]; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cibool rdma_nl_chk_listeners(unsigned int group) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct rdma_dev_net *rnet = rdma_net_to_dev_net(&init_net); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return netlink_has_listeners(rnet->nl_sock, group); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_chk_listeners); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic bool is_nl_msg_valid(unsigned int type, unsigned int op) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci static const unsigned int max_num_ops[RDMA_NL_NUM_CLIENTS] = { 6462306a36Sopenharmony_ci [RDMA_NL_IWCM] = RDMA_NL_IWPM_NUM_OPS, 6562306a36Sopenharmony_ci [RDMA_NL_LS] = RDMA_NL_LS_NUM_OPS, 6662306a36Sopenharmony_ci [RDMA_NL_NLDEV] = RDMA_NLDEV_NUM_OPS, 6762306a36Sopenharmony_ci }; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * This BUILD_BUG_ON is intended to catch addition of new 7162306a36Sopenharmony_ci * RDMA netlink protocol without updating the array above. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci BUILD_BUG_ON(RDMA_NL_NUM_CLIENTS != 6); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (type >= RDMA_NL_NUM_CLIENTS) 7662306a36Sopenharmony_ci return false; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return op < max_num_ops[type]; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic const struct rdma_nl_cbs * 8262306a36Sopenharmony_ciget_cb_table(const struct sk_buff *skb, unsigned int type, unsigned int op) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci const struct rdma_nl_cbs *cb_table; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Currently only NLDEV client is supporting netlink commands in 8862306a36Sopenharmony_ci * non init_net net namespace. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (sock_net(skb->sk) != &init_net && type != RDMA_NL_NLDEV) 9162306a36Sopenharmony_ci return NULL; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci cb_table = READ_ONCE(rdma_nl_types[type].cb_table); 9462306a36Sopenharmony_ci if (!cb_table) { 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Didn't get valid reference of the table, attempt module 9762306a36Sopenharmony_ci * load once. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci up_read(&rdma_nl_types[type].sem); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci request_module("rdma-netlink-subsys-%u", type); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci down_read(&rdma_nl_types[type].sem); 10462306a36Sopenharmony_ci cb_table = READ_ONCE(rdma_nl_types[type].cb_table); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci if (!cb_table || (!cb_table[op].dump && !cb_table[op].doit)) 10762306a36Sopenharmony_ci return NULL; 10862306a36Sopenharmony_ci return cb_table; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid rdma_nl_register(unsigned int index, 11262306a36Sopenharmony_ci const struct rdma_nl_cbs cb_table[]) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci if (WARN_ON(!is_nl_msg_valid(index, 0)) || 11562306a36Sopenharmony_ci WARN_ON(READ_ONCE(rdma_nl_types[index].cb_table))) 11662306a36Sopenharmony_ci return; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Pairs with the READ_ONCE in is_nl_valid() */ 11962306a36Sopenharmony_ci smp_store_release(&rdma_nl_types[index].cb_table, cb_table); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_register); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_civoid rdma_nl_unregister(unsigned int index) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci down_write(&rdma_nl_types[index].sem); 12662306a36Sopenharmony_ci rdma_nl_types[index].cb_table = NULL; 12762306a36Sopenharmony_ci up_write(&rdma_nl_types[index].sem); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_unregister); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq, 13262306a36Sopenharmony_ci int len, int client, int op, int flags) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci *nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), len, flags); 13562306a36Sopenharmony_ci if (!*nlh) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci return nlmsg_data(*nlh); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ciEXPORT_SYMBOL(ibnl_put_msg); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciint ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh, 14262306a36Sopenharmony_ci int len, void *data, int type) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (nla_put(skb, type, len, data)) { 14562306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 14662306a36Sopenharmony_ci return -EMSGSIZE; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciEXPORT_SYMBOL(ibnl_put_attr); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int rdma_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, 15362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int type = nlh->nlmsg_type; 15662306a36Sopenharmony_ci unsigned int index = RDMA_NL_GET_CLIENT(type); 15762306a36Sopenharmony_ci unsigned int op = RDMA_NL_GET_OP(type); 15862306a36Sopenharmony_ci const struct rdma_nl_cbs *cb_table; 15962306a36Sopenharmony_ci int err = -EINVAL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!is_nl_msg_valid(index, op)) 16262306a36Sopenharmony_ci return -EINVAL; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci down_read(&rdma_nl_types[index].sem); 16562306a36Sopenharmony_ci cb_table = get_cb_table(skb, index, op); 16662306a36Sopenharmony_ci if (!cb_table) 16762306a36Sopenharmony_ci goto done; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if ((cb_table[op].flags & RDMA_NL_ADMIN_PERM) && 17062306a36Sopenharmony_ci !netlink_capable(skb, CAP_NET_ADMIN)) { 17162306a36Sopenharmony_ci err = -EPERM; 17262306a36Sopenharmony_ci goto done; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* 17662306a36Sopenharmony_ci * LS responses overload the 0x100 (NLM_F_ROOT) flag. Don't 17762306a36Sopenharmony_ci * mistakenly call the .dump() function. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci if (index == RDMA_NL_LS) { 18062306a36Sopenharmony_ci if (cb_table[op].doit) 18162306a36Sopenharmony_ci err = cb_table[op].doit(skb, nlh, extack); 18262306a36Sopenharmony_ci goto done; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci /* FIXME: Convert IWCM to properly handle doit callbacks */ 18562306a36Sopenharmony_ci if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_IWCM) { 18662306a36Sopenharmony_ci struct netlink_dump_control c = { 18762306a36Sopenharmony_ci .dump = cb_table[op].dump, 18862306a36Sopenharmony_ci }; 18962306a36Sopenharmony_ci if (c.dump) 19062306a36Sopenharmony_ci err = netlink_dump_start(skb->sk, skb, nlh, &c); 19162306a36Sopenharmony_ci goto done; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (cb_table[op].doit) 19562306a36Sopenharmony_ci err = cb_table[op].doit(skb, nlh, extack); 19662306a36Sopenharmony_cidone: 19762306a36Sopenharmony_ci up_read(&rdma_nl_types[index].sem); 19862306a36Sopenharmony_ci return err; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * This function is similar to netlink_rcv_skb with one exception: 20362306a36Sopenharmony_ci * It calls to the callback for the netlink messages without NLM_F_REQUEST 20462306a36Sopenharmony_ci * flag. These messages are intended for RDMA_NL_LS consumer, so it is allowed 20562306a36Sopenharmony_ci * for that consumer only. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_cistatic int rdma_nl_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, 20862306a36Sopenharmony_ci struct nlmsghdr *, 20962306a36Sopenharmony_ci struct netlink_ext_ack *)) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct netlink_ext_ack extack = {}; 21262306a36Sopenharmony_ci struct nlmsghdr *nlh; 21362306a36Sopenharmony_ci int err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci while (skb->len >= nlmsg_total_size(0)) { 21662306a36Sopenharmony_ci int msglen; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci nlh = nlmsg_hdr(skb); 21962306a36Sopenharmony_ci err = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Generally speaking, the only requests are handled 22662306a36Sopenharmony_ci * by the kernel, but RDMA_NL_LS is different, because it 22762306a36Sopenharmony_ci * runs backward netlink scheme. Kernel initiates messages 22862306a36Sopenharmony_ci * and waits for reply with data to keep pathrecord cache 22962306a36Sopenharmony_ci * in sync. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci if (!(nlh->nlmsg_flags & NLM_F_REQUEST) && 23262306a36Sopenharmony_ci (RDMA_NL_GET_CLIENT(nlh->nlmsg_type) != RDMA_NL_LS)) 23362306a36Sopenharmony_ci goto ack; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Skip control messages */ 23662306a36Sopenharmony_ci if (nlh->nlmsg_type < NLMSG_MIN_TYPE) 23762306a36Sopenharmony_ci goto ack; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci err = cb(skb, nlh, &extack); 24062306a36Sopenharmony_ci if (err == -EINTR) 24162306a36Sopenharmony_ci goto skip; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciack: 24462306a36Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_ACK || err) 24562306a36Sopenharmony_ci netlink_ack(skb, nlh, err, &extack); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciskip: 24862306a36Sopenharmony_ci msglen = NLMSG_ALIGN(nlh->nlmsg_len); 24962306a36Sopenharmony_ci if (msglen > skb->len) 25062306a36Sopenharmony_ci msglen = skb->len; 25162306a36Sopenharmony_ci skb_pull(skb, msglen); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void rdma_nl_rcv(struct sk_buff *skb) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci rdma_nl_rcv_skb(skb, &rdma_nl_rcv_msg); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ciint rdma_nl_unicast(struct net *net, struct sk_buff *skb, u32 pid) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct rdma_dev_net *rnet = rdma_net_to_dev_net(net); 26562306a36Sopenharmony_ci int err; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci err = netlink_unicast(rnet->nl_sock, skb, pid, MSG_DONTWAIT); 26862306a36Sopenharmony_ci return (err < 0) ? err : 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_unicast); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciint rdma_nl_unicast_wait(struct net *net, struct sk_buff *skb, __u32 pid) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct rdma_dev_net *rnet = rdma_net_to_dev_net(net); 27562306a36Sopenharmony_ci int err; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci err = netlink_unicast(rnet->nl_sock, skb, pid, 0); 27862306a36Sopenharmony_ci return (err < 0) ? err : 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_unicast_wait); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciint rdma_nl_multicast(struct net *net, struct sk_buff *skb, 28362306a36Sopenharmony_ci unsigned int group, gfp_t flags) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct rdma_dev_net *rnet = rdma_net_to_dev_net(net); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return nlmsg_multicast(rnet->nl_sock, skb, 0, group, flags); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_multicast); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_civoid rdma_nl_init(void) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci int idx; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci for (idx = 0; idx < RDMA_NL_NUM_CLIENTS; idx++) 29662306a36Sopenharmony_ci init_rwsem(&rdma_nl_types[idx].sem); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_civoid rdma_nl_exit(void) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int idx; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci for (idx = 0; idx < RDMA_NL_NUM_CLIENTS; idx++) 30462306a36Sopenharmony_ci WARN(rdma_nl_types[idx].cb_table, 30562306a36Sopenharmony_ci "Netlink client %d wasn't released prior to unloading %s\n", 30662306a36Sopenharmony_ci idx, KBUILD_MODNAME); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciint rdma_nl_net_init(struct rdma_dev_net *rnet) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct net *net = read_pnet(&rnet->net); 31262306a36Sopenharmony_ci struct netlink_kernel_cfg cfg = { 31362306a36Sopenharmony_ci .input = rdma_nl_rcv, 31462306a36Sopenharmony_ci }; 31562306a36Sopenharmony_ci struct sock *nls; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci nls = netlink_kernel_create(net, NETLINK_RDMA, &cfg); 31862306a36Sopenharmony_ci if (!nls) 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci nls->sk_sndtimeo = 10 * HZ; 32262306a36Sopenharmony_ci rnet->nl_sock = nls; 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid rdma_nl_net_exit(struct rdma_dev_net *rnet) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci netlink_kernel_release(rnet->nl_sock); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_RDMA); 332