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 the socket interface.
  33  */
  34 
  35 #include <sys/types.h>
  36 #include <sys/strlog.h>
  37 #include <sys/policy.h>
  38 #include <sys/sockio.h>
  39 #include <sys/strsubr.h>
  40 #include <sys/strsun.h>
  41 #define _SUN_TPI_VERSION 2
  42 #include <sys/tihdr.h>
  43 #include <sys/squeue_impl.h>
  44 #include <sys/squeue.h>
  45 #include <sys/socketvar.h>
  46 
  47 #include <inet/common.h>
  48 #include <inet/proto_set.h>
  49 #include <inet/ip.h>
  50 
  51 #include <sys/cmn_err.h>
  52 
  53 #include "dccp_impl.h"
  54 #include "dccp_stack.h"
  55 
  56 static void     dccp_activate(sock_lower_handle_t, sock_upper_handle_t,
  57                     sock_upcalls_t *, int, cred_t *);
  58 static int      dccp_accept(sock_lower_handle_t, sock_lower_handle_t,
  59                     sock_upper_handle_t, cred_t *);
  60 static int      dccp_bind(sock_lower_handle_t, struct sockaddr *,
  61                     socklen_t, cred_t *);
  62 static int      dccp_listen(sock_lower_handle_t, int, cred_t *);
  63 static int      dccp_connect(sock_lower_handle_t, const struct sockaddr *,
  64                     socklen_t, sock_connid_t *, cred_t *);
  65 static int      dccp_getpeername(sock_lower_handle_t, struct sockaddr *,
  66                     socklen_t *, cred_t *);
  67 static int      dccp_getsockname(sock_lower_handle_t, struct sockaddr *,
  68                     socklen_t *, cred_t *);
  69 static int      dccp_getsockopt(sock_lower_handle_t, int, int, void *,
  70                     socklen_t *, cred_t *);
  71 static int      dccp_setsockopt(sock_lower_handle_t, int, int, const void *,
  72                     socklen_t, cred_t *);
  73 static int      dccp_send(sock_lower_handle_t, mblk_t *, struct nmsghdr *,
  74                     cred_t *);
  75 static int      dccp_shutdown(sock_lower_handle_t, int, cred_t *);
  76 static void     dccp_clr_flowctrl(sock_lower_handle_t);
  77 static int      dccp_ioctl(sock_lower_handle_t, int, intptr_t, int, int32_t *,
  78                     cred_t *);
  79 static int      dccp_close(sock_lower_handle_t, int, cred_t *);
  80 
  81 sock_downcalls_t sock_dccp_downcalls = {
  82         dccp_activate,          /* sd_activate */
  83         dccp_accept,            /* sd_accept */
  84         dccp_bind,              /* sd_bind */
  85         dccp_listen,            /* sd_listen */
  86         dccp_connect,           /* sd_connect */
  87         dccp_getpeername,       /* sd_getpeername */
  88         dccp_getsockname,       /* sd_getsockname */
  89         dccp_getsockopt,        /* sd_getsockopt */
  90         dccp_setsockopt,        /* sd_setsockopt */
  91         dccp_send,              /* sd_send */
  92         NULL,                   /* sd_send_uio */
  93         NULL,                   /* sd_recv_uio */
  94         NULL,                   /* sd_poll */
  95         dccp_shutdown,          /* sd_shutdown */
  96         dccp_clr_flowctrl,      /* sd_setflowctrl */
  97         dccp_ioctl,             /* sd_ioctl */
  98         dccp_close,             /* sd_close */
  99 };
 100 
 101 /* ARGSUSED */
 102 static void
 103 dccp_activate(sock_lower_handle_t proto_handle, sock_upper_handle_t sock_handle,
 104     sock_upcalls_t *sock_upcalls, int flags, cred_t *cr)
 105 {
 106         conn_t  *connp = (conn_t *)proto_handle;
 107         struct sock_proto_props         sopp;
 108         //extern struct module_info     dccp_rinfo;
 109 
 110         cmn_err(CE_NOTE, "dccp_socket.c: dccp_activate");
 111 
 112         ASSERT(connp->conn_upper_handle == NULL);
 113 
 114         /* All Solaris components should pass a cred for this operation */
 115         ASSERT(cr != NULL);
 116 
 117         sopp.sopp_flags = SOCKOPT_RCVHIWAT | SOCKOPT_RCVLOWAT |
 118             SOCKOPT_MAXPSZ | SOCKOPT_MAXBLK | SOCKOPT_RCVTIMER |
 119             SOCKOPT_RCVTHRESH | SOCKOPT_MAXADDRLEN | SOCKOPT_MINPSZ;
 120 
 121         sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
 122         sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
 123         sopp.sopp_maxpsz = INFPSZ;
 124         sopp.sopp_maxblk = INFPSZ;
 125         sopp.sopp_rcvtimer = SOCKET_TIMER_INTERVAL;
 126         sopp.sopp_rcvthresh = SOCKET_RECVHIWATER >> 3;
 127         sopp.sopp_maxaddrlen = sizeof (sin6_t);
 128 /*
 129         sopp.sopp_minpsz = (dccp_rinfo.mi_minpsz == 1) ? 0 :
 130             dccp_rinfo.mi_minpsz;
 131 */
 132         connp->conn_upcalls = sock_upcalls;
 133         connp->conn_upper_handle = sock_handle;
 134 
 135         /* XXX */
 136         (*connp->conn_upcalls->su_set_proto_props)(connp->conn_upper_handle,
 137             &sopp);
 138 }
 139 
 140 /*ARGSUSED*/
 141 static int
 142 dccp_accept(sock_lower_handle_t lproto_handle,
 143     sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle,
 144     cred_t *cr)
 145 {
 146         conn_t  *lconnp, *econnp;
 147         dccp_t  *listener, *eager;
 148 
 149         cmn_err(CE_NOTE, "dccp_socket.c: dccp_accept");
 150 
 151         econnp = (conn_t *)eproto_handle;
 152         eager = econnp->conn_dccp;
 153 
 154         ASSERT(IPCL_IS_NONSTR(econnp));
 155 
 156         ASSERT(econnp->conn_upper_handle == NULL ||
 157             econnp->conn_upper_handle == sock_handle);
 158 
 159         return (ENOTSUP);
 160 }
 161 
 162 static int
 163 dccp_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
 164     socklen_t len, cred_t *cr)
 165 {
 166         conn_t  *connp = (conn_t *)proto_handle;
 167         int     error;
 168 
 169         cmn_err(CE_NOTE, "dccp_socket.c: dccp_bind");
 170 
 171         ASSERT(connp->conn_upper_handle != NULL);
 172 
 173         /* All Solaris components should pass a cred for this operation */
 174         ASSERT(cr != NULL);
 175 
 176         error = squeue_synch_enter(connp, NULL);
 177         if (error != 0) {
 178                 /* Failed to enter */
 179                 return (ENOSR);
 180         }
 181 
 182         /* Binding to NULL address means unbind */
 183         if (sa == NULL) {
 184                 if (connp->conn_dccp->dccp_state < DCCPS_LISTEN) {
 185                         error = dccp_do_unbind(connp);
 186                 } else {
 187                         error = EINVAL;
 188                 }
 189         } else {
 190                 error = dccp_do_bind(connp, sa, len, cr, B_TRUE);
 191         }
 192 
 193         squeue_synch_exit(connp);
 194 
 195         if (error < 0) {
 196                 if (error == -TOUTSTATE) {
 197                         error = EINVAL;
 198                 } else {
 199                         error = proto_tlitosyserr(-error);
 200                 }
 201         }
 202 
 203         return (error);
 204 }
 205 
 206 /* ARGSUSED */
 207 static int
 208 dccp_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr)
 209 {
 210         conn_t  *connp = (conn_t *)proto_handle;
 211         dccp_t  *dccp = connp->conn_dccp;
 212         int     error;
 213 
 214         cmn_err(CE_NOTE, "dccp_socket.c: dccp_listen");
 215 
 216         ASSERT(connp->conn_upper_handle != NULL);
 217 
 218         /* All Solaris components should pass a cred for this operation */
 219         ASSERT(cr != NULL);
 220 
 221         error = squeue_synch_enter(connp, NULL);
 222         if (error != 0) {
 223                 /* Failed to enter */
 224                 return (ENOBUFS);
 225         }
 226 
 227         error = dccp_do_listen(connp, NULL, 0, backlog, cr, B_FALSE);
 228         if (error == 0) {
 229                 /* XXX:DCCP */
 230                 (*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
 231                     SOCK_OPCTL_ENAB_ACCEPT,
 232                     (uintptr_t)(10));
 233         } else if (error < 0) {
 234                 if (error == -TOUTSTATE) {
 235                         error = EINVAL;
 236                 } else {
 237                         error = proto_tlitosyserr(-error);
 238                 }
 239         }
 240 
 241         squeue_synch_exit(connp);
 242 
 243         return (error);
 244 }
 245 
 246 static int
 247 dccp_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa,
 248     socklen_t len, sock_connid_t *id, cred_t *cr)
 249 {
 250         conn_t  *connp = (conn_t *)proto_handle;
 251         int     error;
 252 
 253         cmn_err(CE_NOTE, "dccp_socket.c: dccp_connect");
 254 
 255         ASSERT(connp->conn_upper_handle != NULL);
 256 
 257         /* All Solaris components should pass a cred for this operation */
 258         ASSERT(cr != NULL);
 259 
 260         error = proto_verify_ip_addr(connp->conn_family, sa, len);
 261         if (error != 0) {
 262                 return (error);
 263         }
 264 
 265         error = squeue_synch_enter(connp, NULL);
 266         if (error != 0) {
 267                 /* Failed to enter */
 268                 return (ENOSR);
 269         }
 270 
 271         error = dccp_do_connect(connp, sa, len, cr, curproc->p_pid);
 272         if (error == 0) {
 273                 *id = connp->conn_dccp->dccp_connid;
 274         } else if (error < 0) {
 275                 if (error == -TOUTSTATE) {
 276                         switch (connp->conn_dccp->dccp_state) {
 277                         case DCCPS_ACK_SENT:
 278                                 error = EALREADY;
 279                                 break;
 280                         case DCCPS_ESTABLISHED:
 281                                 error = EISCONN;
 282                                 break;
 283                         case DCCPS_LISTEN:
 284                                 error = EOPNOTSUPP;
 285                                 break;
 286                         default:
 287                                 error = EINVAL;
 288                                 break;
 289                         }
 290                 } else {
 291                         error = proto_tlitosyserr(-error);
 292                 }
 293         }
 294 
 295         squeue_synch_exit(connp);
 296 
 297         cmn_err(CE_NOTE, "dccp_connect.c: exit %d", error);
 298         return ((error == 0) ? EINPROGRESS : error);
 299 }
 300 
 301 /* ARGSUSED3 */
 302 static int
 303 dccp_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *addr,
 304     socklen_t *addrlenp, cred_t *cr)
 305 {
 306         conn_t  *connp = (conn_t *)proto_handle;
 307         dccp_t  *dccp = connp->conn_dccp;
 308 
 309         cmn_err(CE_NOTE, "dccp_socket.c: dccp_getpeername");
 310 
 311         /* All Solaris components should pass a cred for this operation */
 312         ASSERT(cr != NULL);
 313 
 314         ASSERT(dccp != NULL);
 315         if (dccp->dccp_state < DCCPS_ACK_RCVD) {
 316                 return (ENOTCONN);
 317         }
 318 
 319         return (conn_getpeername(connp, addr, addrlenp));
 320 }
 321 
 322 /* ARGSUSED3 */
 323 static int
 324 dccp_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *addr,
 325     socklen_t *addrlenp, cred_t *cr)
 326 {
 327         conn_t  *connp = (conn_t *)proto_handle;
 328         int     error;
 329 
 330         cmn_err(CE_NOTE, "dccp_socket.c: dccp_getsockname");
 331 
 332         /* All Solaris components should pass a cred for this operation */
 333         ASSERT(cr != NULL);
 334 
 335         /* XXX UDP has locks here, TCP not */
 336         mutex_enter(&connp->conn_lock);
 337         error = conn_getsockname(connp, addr, addrlenp);
 338         mutex_exit(&connp->conn_lock);
 339 
 340         return (error);
 341 }
 342 
 343 static int
 344 dccp_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
 345     void *optvalp, socklen_t *optlen, cred_t *cr)
 346 {
 347         conn_t          *connp = (conn_t *)proto_handle;
 348         void            *optvalp_buf;
 349         t_uscalar_t     max_optbuf_len;
 350         int             len;
 351         int             error;
 352 
 353         cmn_err(CE_NOTE, "dccp_socket.c: dccp_getsockopt");
 354 
 355         ASSERT(connp->conn_upper_handle != NULL);
 356 
 357         /* All Solaris components should pass a cred for this operation */
 358         ASSERT(cr != NULL);
 359 
 360         error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len,
 361             dccp_opt_obj.odb_opt_des_arr,
 362             dccp_opt_obj.odb_opt_arr_cnt,
 363             B_FALSE, B_TRUE, cr);
 364         if (error != 0) {
 365                 if (error < 0) {
 366                         error = proto_tlitosyserr(-error);
 367                 }
 368                 return (error);
 369         }
 370 
 371         optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP);
 372         if (optvalp_buf == NULL) {
 373                 return (ENOMEM);
 374         }
 375 
 376         error = squeue_synch_enter(connp, NULL);
 377         if (error == ENOMEM) {
 378                 kmem_free(optvalp_buf, max_optbuf_len);
 379                 return (ENOMEM);
 380         }
 381 
 382         len = dccp_opt_get(connp, level, option_name, optvalp_buf);
 383         squeue_synch_exit(connp);
 384 
 385         if (len == -1) {
 386                 kmem_free(optvalp_buf, max_optbuf_len);
 387                 return (EINVAL);
 388         }
 389 
 390         t_uscalar_t size = MIN(len, *optlen);
 391 
 392         bcopy(optvalp_buf, optvalp, size);
 393         bcopy(&size, optlen, sizeof (size));
 394 
 395         kmem_free(optvalp_buf, max_optbuf_len);
 396 
 397         return (0);
 398 }
 399 
 400 static int
 401 dccp_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
 402     const void *optvalp, socklen_t optlen, cred_t *cr)
 403 {
 404         conn_t  *connp = (conn_t *)proto_handle;
 405         int     error;
 406 
 407         cmn_err(CE_NOTE, "dccp_socket.c: dccp_setsockopt");
 408 
 409         ASSERT(connp->conn_upper_handle != NULL);
 410 
 411         /* All Solaris components should pass a cred for this operation */
 412         ASSERT(cr != NULL);
 413 
 414         error = squeue_synch_enter(connp, NULL);
 415         if (error == ENOMEM) {
 416                 return (ENOMEM);
 417         }
 418 
 419         error = proto_opt_check(level, option_name, optlen, NULL,
 420             dccp_opt_obj.odb_opt_des_arr,
 421             dccp_opt_obj.odb_opt_arr_cnt,
 422             B_TRUE, B_FALSE, cr);
 423         if (error != 0) {
 424                 if (error < 0) {
 425                         error = proto_tlitosyserr(-error);
 426                 }
 427                 squeue_synch_exit(connp);
 428                 return (error);
 429         }
 430 
 431         error = dccp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level, option_name,
 432             optlen, (uchar_t *)optvalp, (uint_t *)&optlen, (uchar_t *)optvalp,
 433             NULL, cr);
 434         squeue_synch_exit(connp);
 435 
 436         ASSERT(error >= 0);
 437 
 438         return (error);
 439 }
 440 
 441 /* ARGSUSED */
 442 static int
 443 dccp_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
 444     cred_t *cr)
 445 {
 446         conn_t  *connp = (conn_t *)proto_handle;
 447         dccp_t  *dccp;
 448         uint32_t msize;
 449         int32_t dccpstate;
 450 
 451         cmn_err(CE_NOTE, "dccp_socket.c: dccp_send");
 452 
 453         /* All Solaris components should pass a cred for this operation */
 454         ASSERT(cr != NULL);
 455 
 456         ASSERT(connp->conn_ref >= 2);
 457         ASSERT(connp->conn_upper_handle != NULL);
 458 
 459         if (msg->msg_controllen != 0) {
 460                 freemsg(mp);
 461                 return (EOPNOTSUPP);
 462         }
 463 
 464         switch (DB_TYPE(mp)) {
 465         case M_DATA:
 466                 dccp = connp->conn_dccp;
 467                 ASSERT(dccp != NULL);
 468 
 469                 dccpstate = dccp->dccp_state;
 470 
 471                 /* XXX */
 472 
 473                 msize = msgdsize(mp);
 474 
 475                 if (msg->msg_flags & MSG_OOB) {
 476                         SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_output_urgent,
 477                             connp, NULL, dccp_squeue_flag, SQTAG_DCCP_OUTPUT); 
 478                 } else {
 479                         SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_output,
 480                             connp, NULL, dccp_squeue_flag, SQTAG_DCCP_OUTPUT);
 481                 }
 482 
 483                 return (0);
 484 
 485         default:
 486                 ASSERT(0);
 487         }
 488 
 489         freemsg(mp);
 490 
 491         return (0);
 492 }
 493 
 494 /* ARGSUSED */
 495 static int
 496 dccp_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr)
 497 {
 498         conn_t  *connp = (conn_t *)proto_handle;
 499         dccp_t  *dccp = connp->conn_dccp;
 500 
 501         cmn_err(CE_NOTE, "dccp_socket.c: dccp_shutdown");
 502 
 503         /* All Solaris components should pass a cred for this operation. */
 504         ASSERT(cr != NULL);
 505 
 506         ASSERT(connp->conn_upper_handle != NULL);
 507 
 508 
 509         return (ENOTSUP);
 510 }
 511 
 512 static void
 513 dccp_clr_flowctrl(sock_lower_handle_t proto_handle)
 514 {
 515         conn_t  *connp = (conn_t *)proto_handle;
 516         dccp_t  *dccp = connp->conn_dccp;
 517         mblk_t  *mp;
 518         int     error;
 519 
 520         ASSERT(connp->conn_upper_handle != NULL);
 521 
 522         cmn_err(CE_NOTE, "dccp_socket.c: dccp_clr_flowctrl");
 523 
 524         error = squeue_synch_enter(connp, mp);
 525 
 526         squeue_synch_exit(connp);
 527 }
 528 
 529 /* ARGSUSED */
 530 static int
 531 dccp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg,
 532     int mode, int32_t *rvalp, cred_t *cr)
 533 {
 534         conn_t  *connp = (conn_t *)proto_handle;
 535         int     error;
 536 
 537         cmn_err(CE_NOTE, "dccp_socket.c: dccp_ioctl");
 538 
 539         ASSERT(connp->conn_upper_handle != NULL);
 540 
 541         /* All Solaris components should pass a cred for this operation. */
 542         ASSERT(cr != NULL);
 543 
 544         return (ENOTSUP);
 545 }
 546 
 547 /* ARGSUSED */
 548 static int
 549 dccp_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr)
 550 {
 551         conn_t  *connp = (conn_t *)proto_handle;
 552 
 553         cmn_err(CE_NOTE, "dccp_socket.c: dccp_close\n");
 554 
 555         ASSERT(connp->conn_upper_handle != NULL);
 556 
 557         /* All Solaris components should pass a cred for this operation */
 558         ASSERT(cr != NULL);
 559 
 560         dccp_close_common(connp, flags);
 561 
 562         ip_free_helper_stream(connp);
 563 
 564         CONN_DEC_REF(connp);
 565 
 566         return (EINPROGRESS);
 567 }
 568 
 569 
 570 /*
 571  * Socket create function.
 572  */
 573 sock_lower_handle_t
 574 dccp_create(int family, int type, int proto, sock_downcalls_t **sockdowncalls,
 575     uint_t *smodep, int *errorp, int flags, cred_t *credp)
 576 {
 577         conn_t          *connp;
 578         boolean_t       isv6;
 579 
 580         /* XXX (type != SOCK_STREAM */
 581         if ((family != AF_INET && family != AF_INET6) ||
 582             (proto != 0 && proto != IPPROTO_DCCP)) {
 583                 *errorp = EPROTONOSUPPORT;
 584                 return (NULL);
 585         }
 586 
 587         cmn_err(CE_NOTE, "dccp_socket: dccp_create\n");
 588 
 589         isv6 = family == AF_INET6 ? B_TRUE: B_FALSE;
 590         connp = dccp_create_common(credp, isv6, B_TRUE, errorp);
 591         if (connp == NULL) {
 592                 return (NULL);
 593         }
 594 
 595         /*
 596          * Increment ref for DCCP connection.
 597          */
 598         mutex_enter(&connp->conn_lock);
 599         CONN_INC_REF_LOCKED(connp);
 600         ASSERT(connp->conn_ref == 2);
 601         connp->conn_state_flags &= ~CONN_INCIPIENT;
 602         connp->conn_flags |= IPCL_NONSTR;
 603         mutex_exit(&connp->conn_lock);
 604 
 605         ASSERT(errorp != NULL);
 606         *errorp = 0;
 607         *sockdowncalls = &sock_dccp_downcalls;
 608         *smodep = SM_CONNREQUIRED | SM_EXDATA | SM_ACCEPTSUPP |
 609             SM_SENDFILESUPP;
 610 
 611         return ((sock_lower_handle_t)connp);
 612 }
 613 
 614 int
 615 dccp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
 616     boolean_t issocket, so_proto_quiesced_cb_t quiesced_cb,
 617     sock_quiesce_arg_t *arg)
 618 {
 619         cmn_err(CE_NOTE, "dccp_socket: dccp_fallback\n");
 620 
 621         return (0);
 622 }