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 }