1b2cbae2cSRoland Dreier /*
2b2cbae2cSRoland Dreier  * Copyright (c) 2010 Voltaire Inc.  All rights reserved.
3b2cbae2cSRoland Dreier  *
4b2cbae2cSRoland Dreier  * This software is available to you under a choice of one of two
5b2cbae2cSRoland Dreier  * licenses.  You may choose to be licensed under the terms of the GNU
6b2cbae2cSRoland Dreier  * General Public License (GPL) Version 2, available from the file
7b2cbae2cSRoland Dreier  * COPYING in the main directory of this source tree, or the
8b2cbae2cSRoland Dreier  * OpenIB.org BSD license below:
9b2cbae2cSRoland Dreier  *
10b2cbae2cSRoland Dreier  *     Redistribution and use in source and binary forms, with or
11b2cbae2cSRoland Dreier  *     without modification, are permitted provided that the following
12b2cbae2cSRoland Dreier  *     conditions are met:
13b2cbae2cSRoland Dreier  *
14b2cbae2cSRoland Dreier  *      - Redistributions of source code must retain the above
15b2cbae2cSRoland Dreier  *        copyright notice, this list of conditions and the following
16b2cbae2cSRoland Dreier  *        disclaimer.
17b2cbae2cSRoland Dreier  *
18b2cbae2cSRoland Dreier  *      - Redistributions in binary form must reproduce the above
19b2cbae2cSRoland Dreier  *        copyright notice, this list of conditions and the following
20b2cbae2cSRoland Dreier  *        disclaimer in the documentation and/or other materials
21b2cbae2cSRoland Dreier  *        provided with the distribution.
22b2cbae2cSRoland Dreier  *
23b2cbae2cSRoland Dreier  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24b2cbae2cSRoland Dreier  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25b2cbae2cSRoland Dreier  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26b2cbae2cSRoland Dreier  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27b2cbae2cSRoland Dreier  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28b2cbae2cSRoland Dreier  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29b2cbae2cSRoland Dreier  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30b2cbae2cSRoland Dreier  * SOFTWARE.
31b2cbae2cSRoland Dreier  */
32b2cbae2cSRoland Dreier 
33b2cbae2cSRoland Dreier #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
34b2cbae2cSRoland Dreier 
35b108d976SPaul Gortmaker #include <linux/export.h>
36b2cbae2cSRoland Dreier #include <net/netlink.h>
37b2cbae2cSRoland Dreier #include <net/net_namespace.h>
38b2cbae2cSRoland Dreier #include <net/sock.h>
39b2cbae2cSRoland Dreier #include <rdma/rdma_netlink.h>
40233c1955SLeon Romanovsky #include "core_priv.h"
41b2cbae2cSRoland Dreier 
42c9901724SLeon Romanovsky #include "core_priv.h"
43b2cbae2cSRoland Dreier 
44c9901724SLeon Romanovsky static DEFINE_MUTEX(rdma_nl_mutex);
45b2cbae2cSRoland Dreier static struct sock *nls;
46c9901724SLeon Romanovsky static struct {
47c9901724SLeon Romanovsky 	const struct ibnl_client_cbs   *cb_table;
48c9901724SLeon Romanovsky } rdma_nl_types[RDMA_NL_NUM_CLIENTS];
49b2cbae2cSRoland Dreier 
50bc10ed7dSKaike Wan int ibnl_chk_listeners(unsigned int group)
51bc10ed7dSKaike Wan {
52bc10ed7dSKaike Wan 	if (netlink_has_listeners(nls, group) == 0)
53bc10ed7dSKaike Wan 		return -1;
54bc10ed7dSKaike Wan 	return 0;
55bc10ed7dSKaike Wan }
56bc10ed7dSKaike Wan 
57c9901724SLeon Romanovsky static bool is_nl_msg_valid(unsigned int type, unsigned int op)
58c9901724SLeon Romanovsky {
59c9901724SLeon Romanovsky 	static const unsigned int max_num_ops[RDMA_NL_NUM_CLIENTS - 1] = {
60c9901724SLeon Romanovsky 				  RDMA_NL_RDMA_CM_NUM_OPS,
61c9901724SLeon Romanovsky 				  RDMA_NL_IWPM_NUM_OPS,
62c9901724SLeon Romanovsky 				  0,
63c9901724SLeon Romanovsky 				  RDMA_NL_LS_NUM_OPS,
64c9901724SLeon Romanovsky 				  0 };
65c9901724SLeon Romanovsky 
66c9901724SLeon Romanovsky 	/*
67c9901724SLeon Romanovsky 	 * This BUILD_BUG_ON is intended to catch addition of new
68c9901724SLeon Romanovsky 	 * RDMA netlink protocol without updating the array above.
69c9901724SLeon Romanovsky 	 */
70c9901724SLeon Romanovsky 	BUILD_BUG_ON(RDMA_NL_NUM_CLIENTS != 6);
71c9901724SLeon Romanovsky 
72c9901724SLeon Romanovsky 	if (type > RDMA_NL_NUM_CLIENTS - 1)
73c9901724SLeon Romanovsky 		return false;
74c9901724SLeon Romanovsky 
75c9901724SLeon Romanovsky 	return (op < max_num_ops[type - 1]) ? true : false;
76c9901724SLeon Romanovsky }
77c9901724SLeon Romanovsky 
78c9901724SLeon Romanovsky static bool is_nl_valid(unsigned int type, unsigned int op)
79c9901724SLeon Romanovsky {
80c9901724SLeon Romanovsky 	if (!is_nl_msg_valid(type, op) ||
81c9901724SLeon Romanovsky 	    !rdma_nl_types[type].cb_table ||
82c9901724SLeon Romanovsky 	    !rdma_nl_types[type].cb_table[op].dump)
83c9901724SLeon Romanovsky 		return false;
84c9901724SLeon Romanovsky 	return true;
85c9901724SLeon Romanovsky }
86c9901724SLeon Romanovsky 
87c9901724SLeon Romanovsky void rdma_nl_register(unsigned int index,
88b2cbae2cSRoland Dreier 		      const struct ibnl_client_cbs cb_table[])
89b2cbae2cSRoland Dreier {
90c9901724SLeon Romanovsky 	mutex_lock(&rdma_nl_mutex);
91c9901724SLeon Romanovsky 	if (!is_nl_msg_valid(index, 0)) {
92c9901724SLeon Romanovsky 		/*
93c9901724SLeon Romanovsky 		 * All clients are not interesting in success/failure of
94c9901724SLeon Romanovsky 		 * this call. They want to see the print to error log and
95c9901724SLeon Romanovsky 		 * continue their initialization. Print warning for them,
96c9901724SLeon Romanovsky 		 * because it is programmer's error to be here.
97c9901724SLeon Romanovsky 		 */
98c9901724SLeon Romanovsky 		mutex_unlock(&rdma_nl_mutex);
99c9901724SLeon Romanovsky 		WARN(true,
100c9901724SLeon Romanovsky 		     "The not-valid %u index was supplied to RDMA netlink\n",
101c9901724SLeon Romanovsky 		     index);
102c9901724SLeon Romanovsky 		return;
103b2cbae2cSRoland Dreier 	}
104b2cbae2cSRoland Dreier 
105c9901724SLeon Romanovsky 	if (rdma_nl_types[index].cb_table) {
106c9901724SLeon Romanovsky 		mutex_unlock(&rdma_nl_mutex);
107c9901724SLeon Romanovsky 		WARN(true,
108c9901724SLeon Romanovsky 		     "The %u index is already registered in RDMA netlink\n",
109c9901724SLeon Romanovsky 		     index);
110c9901724SLeon Romanovsky 		return;
111b2cbae2cSRoland Dreier 	}
112b2cbae2cSRoland Dreier 
113c9901724SLeon Romanovsky 	rdma_nl_types[index].cb_table = cb_table;
114c9901724SLeon Romanovsky 	mutex_unlock(&rdma_nl_mutex);
115c9901724SLeon Romanovsky }
116c9901724SLeon Romanovsky EXPORT_SYMBOL(rdma_nl_register);
117c9901724SLeon Romanovsky 
118c9901724SLeon Romanovsky void rdma_nl_unregister(unsigned int index)
119b2cbae2cSRoland Dreier {
120c9901724SLeon Romanovsky 	mutex_lock(&rdma_nl_mutex);
121c9901724SLeon Romanovsky 	rdma_nl_types[index].cb_table = NULL;
122c9901724SLeon Romanovsky 	mutex_unlock(&rdma_nl_mutex);
123b2cbae2cSRoland Dreier }
124c9901724SLeon Romanovsky EXPORT_SYMBOL(rdma_nl_unregister);
125b2cbae2cSRoland Dreier 
126b2cbae2cSRoland Dreier void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
12730dc5e63STatyana Nikolova 		   int len, int client, int op, int flags)
128b2cbae2cSRoland Dreier {
1291a1c116fSLeon Romanovsky 	*nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), len, flags);
130e0527334SDavid S. Miller 	if (!*nlh)
131b2cbae2cSRoland Dreier 		return NULL;
1321a1c116fSLeon Romanovsky 	return nlmsg_data(*nlh);
133b2cbae2cSRoland Dreier }
134b2cbae2cSRoland Dreier EXPORT_SYMBOL(ibnl_put_msg);
135b2cbae2cSRoland Dreier 
136b2cbae2cSRoland Dreier int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
137b2cbae2cSRoland Dreier 		  int len, void *data, int type)
138b2cbae2cSRoland Dreier {
1391a1c116fSLeon Romanovsky 	if (nla_put(skb, type, len, data)) {
1401a1c116fSLeon Romanovsky 		nlmsg_cancel(skb, nlh);
141b2cbae2cSRoland Dreier 		return -EMSGSIZE;
142b2cbae2cSRoland Dreier 	}
1431a1c116fSLeon Romanovsky 	return 0;
1441a1c116fSLeon Romanovsky }
145b2cbae2cSRoland Dreier EXPORT_SYMBOL(ibnl_put_attr);
146b2cbae2cSRoland Dreier 
1473c3e75d5SLeon Romanovsky static int rdma_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
1482d4bc933SJohannes Berg 			   struct netlink_ext_ack *extack)
149b2cbae2cSRoland Dreier {
150b2cbae2cSRoland Dreier 	int type = nlh->nlmsg_type;
151c9901724SLeon Romanovsky 	unsigned int index = RDMA_NL_GET_CLIENT(type);
1521ae5ccc7SMark Bloch 	unsigned int op = RDMA_NL_GET_OP(type);
153c9901724SLeon Romanovsky 	struct netlink_callback cb = {};
154c9901724SLeon Romanovsky 	struct netlink_dump_control c = {};
155b2cbae2cSRoland Dreier 
156c9901724SLeon Romanovsky 	if (!is_nl_valid(index, op))
157b2cbae2cSRoland Dreier 		return -EINVAL;
15880d326faSPablo Neira Ayuso 
159e3a2b93dSLeon Romanovsky 	if ((rdma_nl_types[index].cb_table[op].flags & RDMA_NL_ADMIN_PERM) &&
160e3a2b93dSLeon Romanovsky 	    !netlink_capable(skb, CAP_NET_ADMIN))
161e3a2b93dSLeon Romanovsky 		return -EPERM;
162e3a2b93dSLeon Romanovsky 
163bc10ed7dSKaike Wan 	/*
164bc10ed7dSKaike Wan 	 * For response or local service set_timeout request,
165bc10ed7dSKaike Wan 	 * there is no need to use netlink_dump_start.
166bc10ed7dSKaike Wan 	 */
167bc10ed7dSKaike Wan 	if (!(nlh->nlmsg_flags & NLM_F_REQUEST) ||
168c9901724SLeon Romanovsky 	    (index == RDMA_NL_LS && op == RDMA_NL_LS_OP_SET_TIMEOUT)) {
169c9901724SLeon Romanovsky 		cb.skb = skb;
170c9901724SLeon Romanovsky 		cb.nlh = nlh;
171c9901724SLeon Romanovsky 		cb.dump = rdma_nl_types[index].cb_table[op].dump;
172bc10ed7dSKaike Wan 		return cb.dump(skb, &cb);
173bc10ed7dSKaike Wan 	}
174bc10ed7dSKaike Wan 
175c9901724SLeon Romanovsky 	c.dump = rdma_nl_types[index].cb_table[op].dump;
17680d326faSPablo Neira Ayuso 	return netlink_dump_start(nls, skb, nlh, &c);
17780d326faSPablo Neira Ayuso }
178b2cbae2cSRoland Dreier 
1793c3e75d5SLeon Romanovsky /*
1803c3e75d5SLeon Romanovsky  * This function is similar to netlink_rcv_skb with one exception:
1813c3e75d5SLeon Romanovsky  * It calls to the callback for the netlink messages without NLM_F_REQUEST
1823c3e75d5SLeon Romanovsky  * flag. These messages are intended for RDMA_NL_LS consumer, so it is allowed
1833c3e75d5SLeon Romanovsky  * for that consumer only.
1843c3e75d5SLeon Romanovsky  */
1853c3e75d5SLeon Romanovsky static int rdma_nl_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
1863c3e75d5SLeon Romanovsky 						   struct nlmsghdr *,
1873c3e75d5SLeon Romanovsky 						   struct netlink_ext_ack *))
188bc10ed7dSKaike Wan {
1893c3e75d5SLeon Romanovsky 	struct netlink_ext_ack extack = {};
190bc10ed7dSKaike Wan 	struct nlmsghdr *nlh;
1913c3e75d5SLeon Romanovsky 	int err;
1923c3e75d5SLeon Romanovsky 
1933c3e75d5SLeon Romanovsky 	while (skb->len >= nlmsg_total_size(0)) {
194bc10ed7dSKaike Wan 		int msglen;
195bc10ed7dSKaike Wan 
196bc10ed7dSKaike Wan 		nlh = nlmsg_hdr(skb);
1973c3e75d5SLeon Romanovsky 		err = 0;
198bc10ed7dSKaike Wan 
199bc10ed7dSKaike Wan 		if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
2003c3e75d5SLeon Romanovsky 			return 0;
201bc10ed7dSKaike Wan 
2023c3e75d5SLeon Romanovsky 		/*
2033c3e75d5SLeon Romanovsky 		 * Generally speaking, the only requests are handled
2043c3e75d5SLeon Romanovsky 		 * by the kernel, but RDMA_NL_LS is different, because it
2053c3e75d5SLeon Romanovsky 		 * runs backward netlink scheme. Kernel initiates messages
2063c3e75d5SLeon Romanovsky 		 * and waits for reply with data to keep pathrecord cache
2073c3e75d5SLeon Romanovsky 		 * in sync.
2083c3e75d5SLeon Romanovsky 		 */
2093c3e75d5SLeon Romanovsky 		if (!(nlh->nlmsg_flags & NLM_F_REQUEST) &&
2103c3e75d5SLeon Romanovsky 		    (RDMA_NL_GET_CLIENT(nlh->nlmsg_type) != RDMA_NL_LS))
2113c3e75d5SLeon Romanovsky 			goto ack;
212bc10ed7dSKaike Wan 
2133c3e75d5SLeon Romanovsky 		/* Skip control messages */
2143c3e75d5SLeon Romanovsky 		if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
2153c3e75d5SLeon Romanovsky 			goto ack;
216bc10ed7dSKaike Wan 
2173c3e75d5SLeon Romanovsky 		err = cb(skb, nlh, &extack);
2183c3e75d5SLeon Romanovsky 		if (err == -EINTR)
2193c3e75d5SLeon Romanovsky 			goto skip;
2203c3e75d5SLeon Romanovsky 
2213c3e75d5SLeon Romanovsky ack:
2223c3e75d5SLeon Romanovsky 		if (nlh->nlmsg_flags & NLM_F_ACK || err)
2233c3e75d5SLeon Romanovsky 			netlink_ack(skb, nlh, err, &extack);
2243c3e75d5SLeon Romanovsky 
2253c3e75d5SLeon Romanovsky skip:
226bc10ed7dSKaike Wan 		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
227bc10ed7dSKaike Wan 		if (msglen > skb->len)
228bc10ed7dSKaike Wan 			msglen = skb->len;
229bc10ed7dSKaike Wan 		skb_pull(skb, msglen);
230bc10ed7dSKaike Wan 	}
2313c3e75d5SLeon Romanovsky 
2323c3e75d5SLeon Romanovsky 	return 0;
233bc10ed7dSKaike Wan }
234bc10ed7dSKaike Wan 
2353c3e75d5SLeon Romanovsky static void rdma_nl_rcv(struct sk_buff *skb)
236b2cbae2cSRoland Dreier {
237c9901724SLeon Romanovsky 	mutex_lock(&rdma_nl_mutex);
2383c3e75d5SLeon Romanovsky 	rdma_nl_rcv_skb(skb, &rdma_nl_rcv_msg);
239c9901724SLeon Romanovsky 	mutex_unlock(&rdma_nl_mutex);
240b2cbae2cSRoland Dreier }
241b2cbae2cSRoland Dreier 
242f00e6463SLeon Romanovsky int rdma_nl_unicast(struct sk_buff *skb, u32 pid)
24330dc5e63STatyana Nikolova {
244cea05eadSMustafa Ismail 	int err;
245cea05eadSMustafa Ismail 
2469047811bSIsmail, Mustafa 	err = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
247cea05eadSMustafa Ismail 	return (err < 0) ? err : 0;
24830dc5e63STatyana Nikolova }
249f00e6463SLeon Romanovsky EXPORT_SYMBOL(rdma_nl_unicast);
25030dc5e63STatyana Nikolova 
251f00e6463SLeon Romanovsky int rdma_nl_unicast_wait(struct sk_buff *skb, __u32 pid)
2529047811bSIsmail, Mustafa {
2539047811bSIsmail, Mustafa 	int err;
2549047811bSIsmail, Mustafa 
2559047811bSIsmail, Mustafa 	err = netlink_unicast(nls, skb, pid, 0);
2569047811bSIsmail, Mustafa 	return (err < 0) ? err : 0;
2579047811bSIsmail, Mustafa }
258f00e6463SLeon Romanovsky EXPORT_SYMBOL(rdma_nl_unicast_wait);
2599047811bSIsmail, Mustafa 
2604d7f693aSLeon Romanovsky int rdma_nl_multicast(struct sk_buff *skb, unsigned int group, gfp_t flags)
26130dc5e63STatyana Nikolova {
26230dc5e63STatyana Nikolova 	return nlmsg_multicast(nls, skb, 0, group, flags);
26330dc5e63STatyana Nikolova }
2644d7f693aSLeon Romanovsky EXPORT_SYMBOL(rdma_nl_multicast);
26530dc5e63STatyana Nikolova 
266c9901724SLeon Romanovsky int __init rdma_nl_init(void)
267b2cbae2cSRoland Dreier {
268a31f2d17SPablo Neira Ayuso 	struct netlink_kernel_cfg cfg = {
2693c3e75d5SLeon Romanovsky 		.input	= rdma_nl_rcv,
270a31f2d17SPablo Neira Ayuso 	};
271a31f2d17SPablo Neira Ayuso 
2729f00d977SPablo Neira Ayuso 	nls = netlink_kernel_create(&init_net, NETLINK_RDMA, &cfg);
273c9901724SLeon Romanovsky 	if (!nls)
274b2cbae2cSRoland Dreier 		return -ENOMEM;
275b2cbae2cSRoland Dreier 
276cea05eadSMustafa Ismail 	nls->sk_sndtimeo = 10 * HZ;
277b2cbae2cSRoland Dreier 	return 0;
278b2cbae2cSRoland Dreier }
279b2cbae2cSRoland Dreier 
280c9901724SLeon Romanovsky void rdma_nl_exit(void)
281b2cbae2cSRoland Dreier {
282c9901724SLeon Romanovsky 	int idx;
283b2cbae2cSRoland Dreier 
284c9901724SLeon Romanovsky 	for (idx = 0; idx < RDMA_NL_NUM_CLIENTS; idx++)
285c9901724SLeon Romanovsky 		rdma_nl_unregister(idx);
286b2cbae2cSRoland Dreier 
287b2cbae2cSRoland Dreier 	netlink_kernel_release(nls);
288b2cbae2cSRoland Dreier }
289