xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rdsv3/bind.c (revision 16e76cdd6e3cfaac7d91c3b0644ee1bc6cf52347)
1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * This file contains code imported from the OFED rds source file bind.c
7  * Oracle elects to have and use the contents of bind.c under and governed
8  * by the OpenIB.org BSD license (see below for full license text). However,
9  * the following notice accompanied the original version of this file:
10  */
11 
12 /*
13  * Copyright (c) 2006 Oracle.  All rights reserved.
14  *
15  * This software is available to you under a choice of one of two
16  * licenses.  You may choose to be licensed under the terms of the GNU
17  * General Public License (GPL) Version 2, available from the file
18  * COPYING in the main directory of this source tree, or the
19  * OpenIB.org BSD license below:
20  *
21  *     Redistribution and use in source and binary forms, with or
22  *     without modification, are permitted provided that the following
23  *     conditions are met:
24  *
25  *      - Redistributions of source code must retain the above
26  *        copyright notice, this list of conditions and the following
27  *        disclaimer.
28  *
29  *      - Redistributions in binary form must reproduce the above
30  *        copyright notice, this list of conditions and the following
31  *        disclaimer in the documentation and/or other materials
32  *        provided with the distribution.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
38  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
39  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
40  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41  * SOFTWARE.
42  *
43  */
44 #include <sys/types.h>
45 #include <sys/sysmacros.h>
46 #include <sys/random.h>
47 #include <sys/rds.h>
48 
49 #include <sys/ib/clients/rdsv3/rdsv3.h>
50 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
51 
52 /*
53  * XXX this probably still needs more work.. no INADDR_ANY, and rbtrees aren't
54  * particularly zippy.
55  *
56  * This is now called for every incoming frame so we arguably care much more
57  * about it than we used to.
58  */
59 kmutex_t	rdsv3_bind_lock;
60 avl_tree_t	rdsv3_bind_tree;
61 
62 static struct rdsv3_sock *
63 rdsv3_bind_tree_walk(uint32_be_t addr, uint16_be_t port,
64     struct rdsv3_sock *insert)
65 {
66 	struct rdsv3_sock *rs;
67 	avl_index_t	where;
68 	uint64_t	needle = ((uint64_t)addr << 32) | port;
69 
70 	rs = avl_find(&rdsv3_bind_tree, &needle, &where);
71 	if ((rs == NULL) && (insert != NULL)) {
72 		insert->rs_bound_addr = addr;
73 		insert->rs_bound_port = port;
74 		avl_insert(&rdsv3_bind_tree, insert, where);
75 	}
76 
77 	return (rs);
78 }
79 
80 /*
81  * Return the rdsv3_sock bound at the given local address.
82  *
83  * The rx path can race with rdsv3_release.  We notice if rdsv3_release() has
84  * marked this socket and don't return a rs ref to the rx path.
85  */
86 struct rdsv3_sock *
87 rdsv3_find_bound(uint32_be_t addr, uint16_be_t port)
88 {
89 	struct rdsv3_sock *rs;
90 
91 	RDSV3_DPRINTF4("rdsv3_find_bound", "Enter(port: %x)", port);
92 
93 	mutex_enter(&rdsv3_bind_lock);
94 	rs = rdsv3_bind_tree_walk(addr, port, NULL);
95 	if (rs && !rdsv3_sk_sock_flag(rdsv3_rs_to_sk(rs), SOCK_DEAD))
96 		rdsv3_sock_addref(rs);
97 	else
98 		rs = NULL;
99 	mutex_exit(&rdsv3_bind_lock);
100 
101 	RDSV3_DPRINTF5("rdsv3_find_bound", "returning rs %p for %u.%u.%u.%u:%x",
102 	    rs, NIPQUAD(addr), port);
103 
104 	return (rs);
105 }
106 
107 /* returns -ve errno or +ve port */
108 static int
109 rdsv3_add_bound(struct rdsv3_sock *rs, uint32_be_t addr, uint16_be_t *port)
110 {
111 	int ret = -EADDRINUSE;
112 	uint16_t rover, last;
113 
114 	RDSV3_DPRINTF4("rdsv3_add_bound", "Enter(port: %x)", *port);
115 
116 	if (*port != 0) {
117 		rover = ntohs(*port);
118 		last = rover;
119 	} else {
120 		(void) random_get_pseudo_bytes((uint8_t *)&rover,
121 		    sizeof (uint16_t));
122 		rover = MAX(rover, 2);
123 		last = rover - 1;
124 	}
125 
126 	mutex_enter(&rdsv3_bind_lock);
127 
128 	do {
129 		if (rover == 0)
130 			rover++;
131 
132 		if (rdsv3_bind_tree_walk(addr, htons(rover), rs) == NULL) {
133 			*port = htons(rover);
134 			ret = 0;
135 			break;
136 		}
137 	} while (rover++ != last);
138 
139 	if (ret == 0)  {
140 		rs->rs_bound_addr = addr;
141 		rs->rs_bound_port = *port;
142 		rdsv3_sock_addref(rs);
143 
144 		RDSV3_DPRINTF5("rdsv3_add_bound",
145 		    "rs %p binding to %u.%u.%u.%u:%x",
146 		    rs, NIPQUAD(addr), *port);
147 	}
148 
149 	mutex_exit(&rdsv3_bind_lock);
150 
151 	RDSV3_DPRINTF4("rdsv3_add_bound", "Return(port: %x)", *port);
152 
153 	return (ret);
154 }
155 
156 void
157 rdsv3_remove_bound(struct rdsv3_sock *rs)
158 {
159 	RDSV3_DPRINTF4("rdsv3_remove_bound", "Enter(rs: %p)", rs);
160 
161 	mutex_enter(&rdsv3_bind_lock);
162 
163 	if (rs->rs_bound_addr) {
164 		RDSV3_DPRINTF5("rdsv3_remove_bound",
165 		    "rs %p unbinding from %u.%u.%u.%u:%x",
166 		    rs, NIPQUAD(rs->rs_bound_addr), rs->rs_bound_port);
167 		avl_remove(&rdsv3_bind_tree, rs);
168 		rdsv3_sock_put(rs);
169 		rs->rs_bound_addr = 0;
170 	}
171 
172 	mutex_exit(&rdsv3_bind_lock);
173 
174 	RDSV3_DPRINTF4("rdsv3_remove_bound", "Return(rs: %p)", rs);
175 }
176 
177 /* ARGSUSED */
178 int
179 rdsv3_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
180     socklen_t len, cred_t *cr)
181 {
182 	struct rsock	*sk = (struct rsock *)proto_handle;
183 	sin_t		*sin = (sin_t *)sa;
184 	struct rdsv3_sock	*rs = rdsv3_sk_to_rs(sk);
185 	int		ret;
186 
187 	if (len != sizeof (sin_t) || (sin == NULL) ||
188 	    !OK_32PTR((char *)sin)) {
189 		RDSV3_DPRINTF2("rdsv3_bind", "address to bind not specified");
190 		return (EINVAL);
191 	}
192 
193 	RDSV3_DPRINTF4("rdsv3_bind", "Enter(rs: %p, addr: 0x%x, port: %x)",
194 	    rs, ntohl(sin->sin_addr.s_addr), htons(sin->sin_port));
195 
196 	if (sin->sin_addr.s_addr == INADDR_ANY) {
197 		RDSV3_DPRINTF2("rdsv3_bind", "Invalid address");
198 		return (EINVAL);
199 	}
200 
201 	/* We don't allow multiple binds */
202 	if (rs->rs_bound_addr) {
203 		RDSV3_DPRINTF2("rdsv3_bind", "Multiple binds not allowed");
204 		return (EINVAL);
205 	}
206 
207 	ret = rdsv3_add_bound(rs, sin->sin_addr.s_addr, &sin->sin_port);
208 	if (ret) {
209 		return (ret);
210 	}
211 
212 	rs->rs_transport = rdsv3_trans_get_preferred(sin->sin_addr.s_addr);
213 	if (!rs->rs_transport) {
214 		rdsv3_remove_bound(rs);
215 		if (rdsv3_printk_ratelimit()) {
216 			RDSV3_DPRINTF1("rdsv3_bind",
217 			    "RDS: rdsv3_bind() could not find a transport.\n");
218 		}
219 		return (EADDRNOTAVAIL);
220 	}
221 
222 	RDSV3_DPRINTF4("rdsv3_bind", "Return: Assigned port: %x to sock: %p",
223 	    sin->sin_port, rs);
224 
225 	return (0);
226 }
227