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