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 }