xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rdsv3/bind.c (revision 9ff75ade7518b5023d90c864a4c081dba319589e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/types.h>
26 #include <sys/sysmacros.h>
27 #include <sys/random.h>
28 #include <sys/rds.h>
29 
30 #include <sys/ib/clients/rdsv3/rdsv3.h>
31 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
32 
33 /*
34  * XXX this probably still needs more work.. no INADDR_ANY, and rbtrees aren't
35  * particularly zippy.
36  *
37  * This is now called for every incoming frame so we arguably care much more
38  * about it than we used to.
39  */
40 kmutex_t	rdsv3_bind_lock;
41 avl_tree_t	rdsv3_bind_tree;
42 
43 static struct rdsv3_sock *
44 rdsv3_bind_tree_walk(uint32_be_t addr, uint16_be_t port,
45     struct rdsv3_sock *insert)
46 {
47 	struct rdsv3_sock *rs;
48 	avl_index_t	where;
49 	uint64_t	needle = ((uint64_t)addr << 32) | port;
50 
51 	rs = avl_find(&rdsv3_bind_tree, &needle, &where);
52 	if ((rs == NULL) && (insert != NULL)) {
53 		insert->rs_bound_addr = addr;
54 		insert->rs_bound_port = port;
55 		avl_insert(&rdsv3_bind_tree, insert, where);
56 	}
57 
58 	return (rs);
59 }
60 
61 /*
62  * Return the rdsv3_sock bound at the given local address.
63  *
64  * The rx path can race with rdsv3_release.  We notice if rdsv3_release() has
65  * marked this socket and don't return a rs ref to the rx path.
66  */
67 struct rdsv3_sock *
68 rdsv3_find_bound(uint32_be_t addr, uint16_be_t port)
69 {
70 	struct rdsv3_sock *rs;
71 
72 	RDSV3_DPRINTF4("rdsv3_find_bound", "Enter(port: %x)", port);
73 
74 	mutex_enter(&rdsv3_bind_lock);
75 	rs = rdsv3_bind_tree_walk(addr, port, NULL);
76 	if (rs && !rdsv3_sk_sock_flag(rdsv3_rs_to_sk(rs), SOCK_DEAD))
77 		rdsv3_sock_addref(rs);
78 	else
79 		rs = NULL;
80 	mutex_exit(&rdsv3_bind_lock);
81 
82 	RDSV3_DPRINTF5("rdsv3_find_bound", "returning rs %p for %u.%u.%u.%u:%x",
83 	    rs, NIPQUAD(addr), port);
84 
85 	return (rs);
86 }
87 
88 /* returns -ve errno or +ve port */
89 static int
90 rdsv3_add_bound(struct rdsv3_sock *rs, uint32_be_t addr, uint16_be_t *port)
91 {
92 	int ret = -EADDRINUSE;
93 	uint16_t rover, last;
94 
95 	RDSV3_DPRINTF4("rdsv3_add_bound", "Enter(port: %x)", *port);
96 
97 	if (*port != 0) {
98 		rover = ntohs(*port);
99 		last = rover;
100 	} else {
101 		(void) random_get_pseudo_bytes((uint8_t *)&rover,
102 		    sizeof (uint16_t));
103 		rover = MAX(rover, 2);
104 		last = rover - 1;
105 	}
106 
107 	mutex_enter(&rdsv3_bind_lock);
108 
109 	do {
110 		if (rover == 0)
111 			rover++;
112 		if (rdsv3_bind_tree_walk(addr, htons(rover), rs) == NULL) {
113 			*port = htons(rover);
114 			ret = 0;
115 			break;
116 		}
117 	} while (rover++ != last);
118 
119 	if (ret == 0)  {
120 		rs->rs_bound_addr = addr;
121 		rs->rs_bound_port = *port;
122 		rdsv3_sock_addref(rs);
123 
124 		RDSV3_DPRINTF5("rdsv3_add_bound",
125 		    "rs %p binding to %u.%u.%u.%u:%x",
126 		    rs, NIPQUAD(addr), *port);
127 	}
128 
129 	mutex_exit(&rdsv3_bind_lock);
130 
131 	RDSV3_DPRINTF4("rdsv3_add_bound", "Return(port: %x)", *port);
132 
133 	return (ret);
134 }
135 
136 void
137 rdsv3_remove_bound(struct rdsv3_sock *rs)
138 {
139 	RDSV3_DPRINTF4("rdsv3_remove_bound", "Enter(rs: %p)", rs);
140 
141 	mutex_enter(&rdsv3_bind_lock);
142 
143 	if (rs->rs_bound_addr) {
144 		RDSV3_DPRINTF5("rdsv3_remove_bound",
145 		    "rs %p unbinding from %u.%u.%u.%u:%x",
146 		    rs, NIPQUAD(rs->rs_bound_addr), rs->rs_bound_port);
147 		avl_remove(&rdsv3_bind_tree, rs);
148 		rdsv3_sock_put(rs);
149 		rs->rs_bound_addr = 0;
150 	}
151 
152 	mutex_exit(&rdsv3_bind_lock);
153 
154 	RDSV3_DPRINTF4("rdsv3_remove_bound", "Return(rs: %p)", rs);
155 }
156 
157 /* ARGSUSED */
158 int
159 rdsv3_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
160     socklen_t len, cred_t *cr)
161 {
162 	struct rsock	*sk = (struct rsock *)proto_handle;
163 	sin_t		*sin = (sin_t *)sa;
164 	struct rdsv3_sock	*rs = rdsv3_sk_to_rs(sk);
165 	int		ret;
166 
167 	if (len != sizeof (sin_t) || (sin == NULL) ||
168 	    !OK_32PTR((char *)sin)) {
169 		RDSV3_DPRINTF2("rdsv3_bind", "address to bind not specified");
170 		return (EINVAL);
171 	}
172 
173 	RDSV3_DPRINTF4("rdsv3_bind", "Enter(rs: %p, addr: 0x%x, port: %x)",
174 	    rs, ntohl(sin->sin_addr.s_addr), htons(sin->sin_port));
175 
176 	if (sin->sin_addr.s_addr == INADDR_ANY) {
177 		RDSV3_DPRINTF2("rdsv3_bind", "Invalid address");
178 		return (EINVAL);
179 	}
180 
181 	/* We don't allow multiple binds */
182 	if (rs->rs_bound_addr) {
183 		RDSV3_DPRINTF2("rdsv3_bind", "Multiple binds not allowed");
184 		return (EINVAL);
185 	}
186 
187 	ret = rdsv3_add_bound(rs, sin->sin_addr.s_addr, &sin->sin_port);
188 	if (ret) {
189 		return (ret);
190 	}
191 
192 	rs->rs_transport = rdsv3_trans_get_preferred(sin->sin_addr.s_addr);
193 	if (rs->rs_transport == NULL) {
194 		rdsv3_remove_bound(rs);
195 		if (rdsv3_printk_ratelimit()) {
196 			RDSV3_DPRINTF1("rdsv3_bind",
197 			    "RDS: rdsv3_bind() could not find a transport.\n");
198 		}
199 		return (EADDRNOTAVAIL);
200 	}
201 
202 	RDSV3_DPRINTF4("rdsv3_bind", "Return: Assigned port: %x to sock: %p",
203 	    sin->sin_port, rs);
204 
205 	return (0);
206 }
207