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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <inet/ip.h> 29 #include <inet/ip_impl.h> 30 #include <inet/ipclassifier.h> 31 #include <inet/proto_set.h> 32 #include <sys/stream.h> 33 #include <sys/strsubr.h> 34 #include <sys/strsun.h> 35 #include <sys/cmn_err.h> 36 #include <sys/t_kuser.h> 37 #include <sys/tihdr.h> 38 #include <sys/pathname.h> 39 #include <sys/sockio.h> 40 #include <sys/vmem.h> 41 #include <sys/disp.h> 42 43 void ip_helper_wput(queue_t *q, mblk_t *mp); 44 45 static int ip_helper_stream_close(queue_t *, int); 46 47 static struct module_info ip_helper_stream_info = { 48 0, "iphelper", IP_MOD_MINPSZ, IP_MOD_MAXPSZ, IP_MOD_HIWAT, IP_MOD_LOWAT 49 }; 50 51 static struct qinit ip_helper_stream_rinit = { 52 NULL, NULL, NULL, ip_helper_stream_close, NULL, 53 &ip_helper_stream_info, NULL 54 }; 55 56 static struct qinit ip_helper_stream_winit = { 57 (pfi_t)ip_helper_wput, (pfi_t)ip_wsrv, NULL, NULL, NULL, 58 &ip_helper_stream_info, NULL, NULL, NULL, STRUIOT_NONE 59 }; 60 61 #define IP_USE_HELPER_CACHE (ip_helper_stream_cache != NULL) 62 63 /* 64 * set the q_ptr of the 'q' to the conn_t pointer passed in 65 */ 66 static void 67 ip_helper_share_conn(queue_t *q, mblk_t *mp, cred_t *crp) 68 { 69 /* 70 * This operation is allowed only on helper streams with kcred 71 */ 72 73 if (kcred != crp || msgdsize(mp->b_cont) != sizeof (void *)) { 74 miocnak(q, mp, 0, EINVAL); 75 return; 76 } 77 78 if (IP_USE_HELPER_CACHE) { 79 ip_helper_stream_info_t *ip_helper_info; 80 81 ip_helper_info = *((ip_helper_stream_info_t **) 82 mp->b_cont->b_rptr); 83 ip_helper_info->iphs_minfo = q->q_ptr; 84 ip_helper_info->iphs_rq = RD(q); 85 ip_helper_info->iphs_wq = WR(q); 86 } else { 87 conn_t *connp = *((conn_t **)mp->b_cont->b_rptr); 88 89 connp->conn_helper_info->iphs_minfo = q->q_ptr; 90 connp->conn_helper_info->iphs_rq = RD(q); 91 connp->conn_helper_info->iphs_wq = WR(q); 92 WR(q)->q_ptr = RD(q)->q_ptr = (void *)connp; 93 connp->conn_rq = RD(q); 94 connp->conn_wq = WR(q); 95 } 96 miocack(q, mp, 0, 0); 97 } 98 99 void 100 ip_helper_wput(queue_t *q, mblk_t *mp) 101 { 102 struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 103 if (DB_TYPE(mp) == M_IOCTL && 104 iocp->ioc_cmd == SIOCSQPTR) { 105 ip_helper_share_conn(q, mp, iocp->ioc_cr); 106 } else { 107 conn_t *connp = (conn_t *)q->q_ptr; 108 109 if (connp->conn_af_isv6) { 110 ip_wput_v6(q, mp); 111 } else { 112 ip_wput(q, mp); 113 } 114 } 115 } 116 117 /* ARGSUSED */ 118 int 119 ip_helper_stream_setup(queue_t *q, dev_t *devp, int flag, int sflag, 120 cred_t *credp, boolean_t isv6) 121 { 122 major_t maj; 123 ip_helper_minfo_t *ip_minfop; 124 125 ASSERT((flag & ~(FKLYR)) == IP_HELPER_STR); 126 127 ASSERT(RD(q) == q); 128 129 ip_minfop = kmem_alloc(sizeof (ip_helper_minfo_t), KM_NOSLEEP); 130 if (ip_minfop == NULL) { 131 return (ENOMEM); 132 } 133 134 ip_minfop->ip_minfo_dev = 0; 135 ip_minfop->ip_minfo_arena = NULL; 136 137 /* 138 * Clone the device, allocate minor device number 139 */ 140 if (ip_minor_arena_la != NULL) 141 ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_la); 142 143 if (ip_minfop->ip_minfo_dev == 0) { 144 /* 145 * numbers in the large arena are exhausted 146 * Try small arena. 147 * Or this is a 32 bit system, 32 bit systems do not have 148 * ip_minor_arena_la 149 */ 150 ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_sa); 151 if (ip_minfop->ip_minfo_dev == 0) { 152 return (EBUSY); 153 } 154 ip_minfop->ip_minfo_arena = ip_minor_arena_sa; 155 } else { 156 ip_minfop->ip_minfo_arena = ip_minor_arena_la; 157 } 158 159 160 ASSERT(ip_minfop->ip_minfo_dev != 0); 161 ASSERT(ip_minfop->ip_minfo_arena != NULL); 162 163 RD(q)->q_ptr = WR(q)->q_ptr = ip_minfop; 164 165 maj = getemajor(*devp); 166 *devp = makedevice(maj, (ulong_t)(ip_minfop->ip_minfo_dev)); 167 168 q->q_qinfo = &ip_helper_stream_rinit; 169 WR(q)->q_qinfo = &ip_helper_stream_winit; 170 qprocson(q); 171 return (0); 172 } 173 174 /* ARGSUSED */ 175 static int 176 ip_helper_stream_close(queue_t *q, int flag) 177 { 178 ip_helper_minfo_t *ip_minfop; 179 180 qprocsoff(q); 181 ip_minfop = (q)->q_ptr; 182 inet_minor_free(ip_minfop->ip_minfo_arena, 183 ip_minfop->ip_minfo_dev); 184 kmem_free(ip_minfop, sizeof (ip_helper_minfo_t)); 185 RD(q)->q_ptr = NULL; 186 WR(q)->q_ptr = NULL; 187 return (0); 188 } 189 190 /* 191 * Public interface for creating an IP stream with shared conn_t 192 */ 193 /* ARGSUSED */ 194 int 195 ip_create_helper_stream(conn_t *connp, ldi_ident_t li) 196 { 197 int error; 198 int ret; 199 200 ASSERT(!servicing_interrupt()); 201 202 error = 0; 203 if (IP_USE_HELPER_CACHE) { 204 connp->conn_helper_info = kmem_cache_alloc( 205 ip_helper_stream_cache, KM_NOSLEEP); 206 if (connp->conn_helper_info == NULL) 207 return (EAGAIN); 208 connp->conn_rq = connp->conn_helper_info->iphs_rq; 209 connp->conn_wq = connp->conn_helper_info->iphs_wq; 210 /* 211 * Doesn't need to hold the QLOCK for there is no one else 212 * should have a pointer to this queue. 213 */ 214 connp->conn_rq->q_flag |= QWANTR; 215 connp->conn_wq->q_flag |= QWANTR; 216 217 connp->conn_rq->q_ptr = connp; 218 connp->conn_wq->q_ptr = connp; 219 } else { 220 ASSERT(connp->conn_helper_info == NULL); 221 connp->conn_helper_info = kmem_alloc( 222 sizeof (ip_helper_stream_info_t), KM_SLEEP); 223 /* 224 * open ip device via the layered interface. 225 * pass in kcred as some threads do not have the 226 * priviledge to open /dev/ip and the check in 227 * secpolicy_spec_open() will fail the open 228 */ 229 error = ldi_open_by_name(connp->conn_af_isv6 ? 230 DEV_IP6 : DEV_IP, IP_HELPER_STR, 231 kcred, &connp->conn_helper_info->iphs_handle, li); 232 233 if (error != 0) { 234 kmem_free(connp->conn_helper_info, 235 (sizeof (ip_helper_stream_info_t))); 236 connp->conn_helper_info = NULL; 237 return (error); 238 } 239 /* 240 * Share connp with the helper stream 241 */ 242 error = ldi_ioctl(connp->conn_helper_info->iphs_handle, 243 SIOCSQPTR, (intptr_t)connp, FKIOCTL, kcred, &ret); 244 245 if (error != 0) { 246 /* 247 * Passing in a zero flag indicates that an error 248 * occured and stream was not shared 249 */ 250 (void) ldi_close(connp->conn_helper_info->iphs_handle, 251 0, kcred); 252 kmem_free(connp->conn_helper_info, 253 (sizeof (ip_helper_stream_info_t))); 254 connp->conn_helper_info = NULL; 255 } 256 } 257 return (error); 258 } 259 260 /* 261 * Public interface for freeing IP helper stream 262 */ 263 /* ARGSUSED */ 264 void 265 ip_free_helper_stream(conn_t *connp) 266 { 267 ASSERT(!servicing_interrupt()); 268 if (IP_USE_HELPER_CACHE) { 269 270 if (connp->conn_helper_info == NULL) 271 return; 272 ASSERT(connp->conn_helper_info->iphs_rq != NULL); 273 ASSERT(connp->conn_helper_info->iphs_wq != NULL); 274 275 /* Prevent service procedures from being called */ 276 disable_svc(connp->conn_helper_info->iphs_rq); 277 278 /* Wait until service procedure of each queue is run */ 279 wait_svc(connp->conn_helper_info->iphs_rq); 280 281 /* Cleanup any pending ioctls */ 282 conn_ioctl_cleanup(connp); 283 284 /* Allow service procedures to be called again */ 285 enable_svc(connp->conn_helper_info->iphs_rq); 286 287 /* Flush the queues */ 288 flushq(connp->conn_helper_info->iphs_rq, FLUSHALL); 289 flushq(connp->conn_helper_info->iphs_wq, FLUSHALL); 290 291 connp->conn_helper_info->iphs_rq->q_ptr = NULL; 292 connp->conn_helper_info->iphs_wq->q_ptr = NULL; 293 294 kmem_cache_free(ip_helper_stream_cache, 295 connp->conn_helper_info); 296 } else { 297 ASSERT( 298 connp->conn_helper_info->iphs_handle != NULL); 299 300 connp->conn_helper_info->iphs_rq->q_ptr = 301 connp->conn_helper_info->iphs_wq->q_ptr = 302 connp->conn_helper_info->iphs_minfo; 303 (void) ldi_close(connp->conn_helper_info->iphs_handle, 304 IP_HELPER_STR, kcred); 305 kmem_free(connp->conn_helper_info, 306 sizeof (ip_helper_stream_info_t)); 307 } 308 connp->conn_helper_info = NULL; 309 } 310 311 /* 312 * create a T_SVR4_OPTMGMT_REQ TPI message and send down the IP stream 313 */ 314 static int 315 ip_send_option_request(conn_t *connp, uint_t optset_context, int level, 316 int option_name, const void *optval, t_uscalar_t optlen, cred_t *cr) 317 { 318 struct T_optmgmt_req *optmgmt_reqp; 319 struct opthdr *ohp; 320 ssize_t size; 321 mblk_t *mp; 322 323 size = sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) + optlen; 324 /* Not used to generate UCRED, thus don't need correct pid */ 325 mp = allocb_cred(size, cr, NOPID); 326 if (mp == NULL) 327 return (ENOMEM); 328 329 mp->b_datap->db_type = M_PROTO; 330 optmgmt_reqp = (struct T_optmgmt_req *)mp->b_wptr; 331 332 optmgmt_reqp->PRIM_type = T_SVR4_OPTMGMT_REQ; 333 optmgmt_reqp->MGMT_flags = optset_context; 334 optmgmt_reqp->OPT_length = (t_scalar_t)sizeof (struct opthdr) + optlen; 335 optmgmt_reqp->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_req); 336 337 mp->b_wptr += sizeof (struct T_optmgmt_req); 338 339 ohp = (struct opthdr *)mp->b_wptr; 340 341 ohp->level = level; 342 ohp->name = option_name; 343 ohp->len = optlen; 344 345 mp->b_wptr += sizeof (struct opthdr); 346 347 if (optval != NULL) { 348 bcopy(optval, mp->b_wptr, optlen); 349 } else { 350 bzero(mp->b_wptr, optlen); 351 } 352 mp->b_wptr += optlen; 353 354 /* 355 * Send down the primitive 356 */ 357 return (ldi_putmsg(connp->conn_helper_info->iphs_handle, mp)); 358 } 359 360 /* 361 * wait/process the response to T_SVR4_OPTMGMT_REQ TPI message 362 */ 363 static int 364 ip_get_option_response(conn_t *connp, uint_t optset_context, void *optval, 365 t_uscalar_t *optlenp) 366 { 367 union T_primitives *tpr; 368 int error; 369 mblk_t *mp; 370 371 mp = NULL; 372 373 ASSERT(optset_context == T_CHECK || optset_context == T_NEGOTIATE); 374 error = ldi_getmsg(connp->conn_helper_info->iphs_handle, &mp, NULL); 375 if (error != 0) { 376 return (error); 377 } 378 379 if (DB_TYPE(mp) != M_PCPROTO || MBLKL(mp) < sizeof (tpr->type)) { 380 error = EPROTO; 381 goto done; 382 } 383 384 tpr = (union T_primitives *)mp->b_rptr; 385 386 switch (tpr->type) { 387 case T_OPTMGMT_ACK: 388 if (MBLKL(mp) < TOPTMGMTACKSZ) 389 error = EPROTO; 390 break; 391 case T_ERROR_ACK: 392 if (MBLKL(mp) < TERRORACKSZ) { 393 error = EPROTO; 394 break; 395 } 396 397 if (tpr->error_ack.TLI_error == TSYSERR) 398 error = tpr->error_ack.UNIX_error; 399 else 400 error = proto_tlitosyserr(tpr->error_ack.TLI_error); 401 break; 402 default: 403 error = EPROTO; 404 break; 405 } 406 407 if ((optset_context == T_CHECK) && (error == 0)) { 408 struct opthdr *opt_res; 409 t_uscalar_t len; 410 t_uscalar_t size; 411 t_uscalar_t maxlen = *optlenp; 412 void *option; 413 struct T_optmgmt_ack *optmgmt_ack; 414 415 optmgmt_ack = (struct T_optmgmt_ack *)mp->b_rptr; 416 opt_res = (struct opthdr *) 417 ((uintptr_t)mp->b_rptr + optmgmt_ack->OPT_offset); 418 /* 419 * Check mblk boundary 420 */ 421 if (!MBLKIN(mp, optmgmt_ack->OPT_offset, 422 optmgmt_ack->OPT_length)) { 423 error = EPROTO; 424 goto done; 425 } 426 427 /* 428 * Check alignment 429 */ 430 if ((((uintptr_t)opt_res) & (__TPI_ALIGN_SIZE - 1)) != 0) { 431 error = EPROTO; 432 goto done; 433 } 434 435 option = &opt_res[1]; 436 437 /* check to ensure that the option is within bounds */ 438 if ((((uintptr_t)option + opt_res->len) < (uintptr_t)option) || 439 !MBLKIN(mp, sizeof (struct opthdr), opt_res->len)) { 440 error = EPROTO; 441 goto done; 442 } 443 444 len = opt_res->len; 445 size = MIN(len, maxlen); 446 447 /* 448 * Copy data 449 */ 450 bcopy(option, optval, size); 451 bcopy(&size, optlenp, sizeof (size)); 452 } 453 454 done: 455 freemsg(mp); 456 return (error); 457 } 458 459 /* 460 * Public interface to get socketoptions via the ip helper stream. 461 */ 462 int 463 ip_get_options(conn_t *connp, int level, int option_name, void *optval, 464 t_uscalar_t *optlenp, cred_t *cr) 465 { 466 int error; 467 468 error = ip_send_option_request(connp, T_CHECK, level, option_name, NULL, 469 *optlenp, cr); 470 if (error) 471 return (error); 472 473 return (ip_get_option_response(connp, T_CHECK, optval, optlenp)); 474 } 475 476 /* 477 * Public interface to set socket options via the ip helper stream. 478 */ 479 int 480 ip_set_options(conn_t *connp, int level, int option_name, const void *optval, 481 t_uscalar_t optlen, cred_t *cr) 482 { 483 484 int error; 485 486 error = ip_send_option_request(connp, T_NEGOTIATE, level, option_name, 487 optval, optlen, cr); 488 if (error) 489 return (error); 490 491 return (ip_get_option_response(connp, T_NEGOTIATE, (void *)optval, 492 &optlen)); 493 } 494