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 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2012 David Hoeppner. All rights reserved.
29 */
30
31 /*
32 * This file contains function related to binding.
33 */
34
35 #include <sys/types.h>
36 #include <sys/stream.h>
37 #include <sys/strsun.h>
38 #include <sys/strsubr.h>
39 #include <sys/stropts.h>
40 #include <sys/strlog.h>
41 #define _SUN_TPI_VERSION 2
42 #include <sys/tihdr.h>
43 #include <sys/suntpi.h>
44 #include <sys/xti_inet.h>
45 #include <sys/squeue_impl.h>
46 #include <sys/squeue.h>
47 #include <sys/tsol/tnet.h>
48
49 #include <inet/common.h>
50 #include <inet/ip.h>
51 #include <inet/proto_set.h>
52
53 #include <sys/cmn_err.h>
54
55 #include "dccp_impl.h"
56
57 /* Setable in /etc/system */
58 static uint32_t dccp_random_anon_port = 1;
59
60 static int dccp_bind_select_lport(dccp_t *, in_port_t *, boolean_t,
61 cred_t *);
62
63 void
64 dccp_bind_hash_insert(dccp_df_t *tbf, dccp_t *dccp, int caller_holds_lock)
65 {
66 conn_t *connp = dccp->dccp_connp;
67 conn_t *connext;
68 dccp_t **dccpp;
69 dccp_t *dccpnext;
70 dccp_t *dccphash;
71
72 cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_hash_insert");
73
74 /* XXX:DCCP */
75
76 dccpp = &tbf->df_dccp;
77 if (!caller_holds_lock) {
78 mutex_enter(&tbf->df_lock);
79 } else {
80 ASSERT(MUTEX_HELD(&tbf->df_lock));
81 }
82 dccphash = dccpp[0];
83 dccpnext = NULL;
84
85 if (dccphash != NULL) {
86 /* XXX:DCCP */
87 }
88
89 insert:
90 dccp->dccp_bind_hash_port = dccpnext;
91 dccp->dccp_bind_hash = dccphash;
92 dccp->dccp_ptpbhn = dccpp;
93 dccpp[0] = dccp;
94
95 if (!caller_holds_lock) {
96 mutex_exit(&tbf->df_lock);
97 }
98 }
99
100 void
101 dccp_bind_hash_remove(dccp_t *dccp)
102 {
103 }
104
105 /*
106 * Check for a valid address and get a local port.
107 */
108 int
109 dccp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
110 boolean_t bind_to_req_port_only)
111 {
112 dccp_t *dccp = connp->conn_dccp;
113 ip_stack_t *ips = connp->conn_netstack->netstack_ip;
114 ip_xmit_attr_t *ixa = connp->conn_ixa;
115 sin_t *sin;
116 sin6_t *sin6;
117 ipaddr_t v4addr;
118 in6_addr_t v6addr;
119 ip_laddr_t laddr_type = IPVL_UNICAST_UP;
120 zoneid_t zoneid = IPCL_ZONEID(connp);
121 in_port_t requested_port;
122 uint_t scopeid = 0;
123 int error;
124
125 cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_check");
126
127 ASSERT((uintptr_t)len <= (uintptr_t)INT_MAX);
128
129 /*
130 * We should be in a pre-listen state.
131 */
132 if (dccp->dccp_state == DCCPS_LISTEN) {
133 return (0);
134 } else if (dccp->dccp_state > DCCPS_LISTEN) {
135 if (connp->conn_debug) {
136 (void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
137 "dccp_bind: bad state, %d", dccp->dccp_state);
138 }
139 return (-TOUTSTATE);
140 }
141
142 /*
143 * Check for a valid address parameter. Then validate the
144 * addresses and copy them and the required port in.
145 */
146 ASSERT(sa != NULL && len != 0);
147 if (!OK_32PTR((char *)sa)) {
148 if (connp->conn_debug) {
149 (void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
150 "dccp_bind: bad address parameter, "
151 "address %p, len %d", (void *)sa, len);
152 }
153 return (-TPROTO);
154 }
155
156 error = proto_verify_ip_addr(connp->conn_family, sa, len);
157 if (error != 0) {
158 return (error);
159 }
160
161 switch (len) {
162 case sizeof (sin_t):
163 sin = (sin_t *)sa;
164 v4addr = sin->sin_addr.s_addr;
165 requested_port = ntohs(sin->sin_port);
166 IN6_IPADDR_TO_V4MAPPED(v4addr, &v6addr);
167 if (v4addr != INADDR_ANY) {
168 laddr_type = ip_laddr_verify_v4(v4addr, zoneid, ips,
169 B_FALSE);
170 }
171 break;
172
173 case sizeof (sin6_t):
174 sin6 = (sin6_t *)sa;
175 v6addr = sin6->sin6_addr;
176 requested_port = ntohs(sin6->sin6_port);
177 if (IN6_IS_ADDR_V4MAPPED(&v6addr)) {
178 if (connp->conn_ipv6_v6only) {
179 return (EADDRNOTAVAIL);
180 }
181
182 IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr);
183 if (v4addr != INADDR_ANY) {
184 laddr_type = ip_laddr_verify_v4(v4addr, zoneid,
185 ips, B_FALSE);
186 }
187 } else {
188 if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr)) {
189 if (IN6_IS_ADDR_LINKSCOPE(&v6addr)) {
190 scopeid = sin6->sin6_scope_id;
191 laddr_type = ip_laddr_verify_v6(&v6addr,
192 zoneid, ips, B_FALSE, scopeid);
193 }
194 }
195 }
196 break;
197
198 default:
199 if (connp->conn_debug) {
200 (void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
201 "dccp_bind: bad address length, %d", len);
202 }
203 return (EAFNOSUPPORT);
204 }
205
206 if (laddr_type == IPVL_BAD) {
207 return (EADDRNOTAVAIL);
208 }
209
210 connp->conn_bound_addr_v6 = v6addr;
211 if (scopeid != 0) {
212 ixa->ixa_flags |= IXAF_SCOPEID_SET;
213 ixa->ixa_scopeid = scopeid;
214 connp->conn_incoming_ifindex = scopeid;
215 } else {
216 ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
217 connp->conn_incoming_ifindex = connp->conn_bound_if;
218 }
219
220 connp->conn_laddr_v6 = v6addr;
221 connp->conn_saddr_v6 = v6addr;
222
223 bind_to_req_port_only = requested_port != 0 && bind_to_req_port_only;
224
225 error = dccp_bind_select_lport(dccp, &requested_port,
226 bind_to_req_port_only, cr);
227 if (error != 0) {
228 connp->conn_laddr_v6 = ipv6_all_zeros;
229 connp->conn_saddr_v6 = ipv6_all_zeros;
230 connp->conn_bound_addr_v6 = ipv6_all_zeros;
231 }
232
233 return (error);
234 }
235
236 /*
237 * Bind to a local port.
238 */
239 static int
240 dccp_bind_select_lport(dccp_t *dccp, in_port_t *requested_port_ptr,
241 boolean_t bind_to_req_port_only, cred_t *cr)
242 {
243 dccp_stack_t *dccps = dccp->dccp_dccps;
244 conn_t *connp = dccp->dccp_connp;
245 zone_t *zone;
246 in_port_t allocated_port;
247 in_port_t requested_port = *requested_port_ptr;
248 in6_addr_t v6addr = connp->conn_laddr_v6;
249 boolean_t user_specified;
250
251 cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_select_lport");
252
253 ASSERT(cr != NULL);
254
255 if (requested_port == 0) {
256 requested_port =
257 dccp_update_next_port(dccps->dccps_next_port_to_try,
258 dccp, B_TRUE);
259 if (requested_port == 0) {
260 return (-TNOADDR);
261 }
262 user_specified = B_FALSE;
263
264 } else {
265 int i;
266 boolean_t priv = B_FALSE;
267
268 if (requested_port < dccps->dccps_smallest_nonpriv_port) {
269 priv = B_TRUE;
270 } else {
271 for (i = 0; i < dccps->dccps_num_epriv_ports; i++) {
272 if (requested_port ==
273 dccps->dccps_epriv_ports[i]) {
274 priv = B_TRUE;
275 break;
276 }
277 }
278 }
279
280 if (priv) {
281 if (secpolicy_net_privaddr(cr, requested_port,
282 IPPROTO_DCCP) != 0) {
283 if (connp->conn_debug) {
284 (void) strlog(DCCP_MOD_ID, 0, 1,
285 SL_ERROR|SL_TRACE,
286 "tcp_bind: no priv for port %d",
287 requested_port);
288 }
289 return (-TACCES);
290 }
291 }
292
293 user_specified = B_TRUE;
294 //connp = dccp->dccp_connp;
295
296 /* XXX */
297 }
298
299 allocated_port = dccp_bindi(dccp, requested_port, &v6addr,
300 connp->conn_reuseaddr, B_FALSE, bind_to_req_port_only,
301 user_specified);
302
303 if (allocated_port == 0) {
304 /* XXX */
305 if (bind_to_req_port_only) {
306 if (connp->conn_debug) {
307 (void) strlog(DCCP_MOD_ID, 0, 1,
308 SL_ERROR|SL_TRACE,
309 "dccp_bind: requested addr busy");
310 }
311 return (-TADDRBUSY);
312 } else {
313 if (connp->conn_debug) {
314 (void) strlog(DCCP_MOD_ID, 0, 1,
315 SL_ERROR|SL_TRACE,
316 "dccp_bind: out of ports?");
317 }
318 return (-TNOADDR);
319 }
320 }
321
322 *requested_port_ptr = allocated_port;
323 return (0);
324 }
325
326 in_port_t
327 dccp_bindi(dccp_t *dccp, in_port_t port, const in6_addr_t *laddr,
328 int reuseaddr, boolean_t quick_connect, boolean_t bind_to_req_port_only,
329 boolean_t user_specified)
330 {
331 dccp_stack_t *dccps = dccp->dccp_dccps;
332 conn_t *connp = dccp->dccp_connp;
333 int count = 0;
334 int loopmax;
335
336 cmn_err(CE_NOTE, "dccp_bind.c: dccp_bindi");
337
338 if (bind_to_req_port_only) {
339 loopmax = 1;
340 } else {
341 if (connp->conn_anon_priv_bind) {
342 loopmax = IPPORT_RESERVED -
343 dccps->dccps_min_anonpriv_port;
344 } else {
345 loopmax = (dccps->dccps_largest_anon_port -
346 dccps->dccps_smallest_anon_port + 1);
347 }
348 }
349
350 do {
351 conn_t *lconnp;
352 dccp_t *ldccp;
353 dccp_df_t *ldf;
354 uint16_t lport;
355
356 lport = htons(port);
357
358 dccp_bind_hash_remove(dccp);
359 ldf = &dccps->dccps_bind_fanout[DCCP_BIND_HASH(lport,
360 dccps->dccps_bind_fanout_size)];
361 mutex_enter(&ldf->df_lock);
362 for (ldccp = ldf->df_dccp; ldccp != NULL;
363 ldccp = ldccp->dccp_bind_hash) {
364 if (lport == ldccp->dccp_connp->conn_lport) {
365 break;
366 }
367 }
368
369 if (ldccp != NULL) {
370 mutex_exit(&ldf->df_lock);
371 } else {
372 dccp->dccp_state = DCCPS_BOUND;
373
374 connp->conn_lport = htons(port);
375
376 ASSERT(&dccps->dccps_bind_fanout[DCCP_BIND_HASH(
377 connp->conn_lport,
378 dccps->dccps_bind_fanout_size)] == ldf);
379 dccp_bind_hash_insert(ldf, dccp, 1);
380
381 mutex_exit(&ldf->df_lock);
382
383 if (user_specified) {
384 return (port);
385 }
386
387 if (!connp->conn_anon_priv_bind) {
388 dccps->dccps_next_port_to_try = port + 1;
389 }
390
391 return (port);
392 }
393
394 if (port == 0) {
395 break;
396 }
397
398 } while (++count < loopmax);
399
400 cmn_err(CE_NOTE, "dccp_bind.c: dccp_bindi exit");
401
402 return (0);
403 }
404
405 in_port_t
406 dccp_update_next_port(in_port_t port, const dccp_t *dccp, boolean_t random)
407 {
408 dccp_stack_t *dccps = dccp->dccp_dccps;
409 boolean_t restart = B_FALSE;
410 int i;
411
412 cmn_err(CE_NOTE, "dccp_bind.c: dccp_update_next_port");
413
414 if (random && dccp_random_anon_port != 0) {
415 (void) random_get_pseudo_bytes((uint8_t *)&port,
416 sizeof (in_port_t));
417
418 if (port < dccps->dccps_smallest_anon_port) {
419 port = dccps->dccps_smallest_anon_port +
420 port % (dccps->dccps_largest_anon_port -
421 dccps->dccps_smallest_anon_port);
422 }
423 }
424
425 retry:
426 if (port < dccps->dccps_smallest_anon_port) {
427 port = (in_port_t)dccps->dccps_smallest_anon_port;
428 }
429
430 if (port > dccps->dccps_largest_anon_port) {
431 if (restart) {
432 return (0);
433 }
434 restart = B_TRUE;
435 port = (in_port_t)dccps->dccps_smallest_anon_port;
436 }
437
438 if (port < dccps->dccps_smallest_nonpriv_port) {
439 port = (in_port_t)dccps->dccps_smallest_nonpriv_port;
440 }
441
442 for (i = 0; i < dccps->dccps_num_epriv_ports; i++) {
443 if (port == dccps->dccps_epriv_ports[i]) {
444 port++;
445 goto retry;
446 }
447 }
448
449 return (port);
450 }