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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * iSCSI Software Initiator 25 */ 26 27 /* 28 * Framework interface routines for iSCSI 29 */ 30 #include "iscsi.h" /* main header */ 31 #include <sys/idm/idm_text.h> /* main header */ 32 #include <sys/iscsi_protocol.h> /* protocol structs */ 33 #include <sys/scsi/adapters/iscsi_if.h> /* ioctl interfaces */ 34 #include "persistent.h" 35 #include <sys/scsi/adapters/iscsi_door.h> 36 #include "iscsi_targetparam.h" 37 #include <sys/strsubr.h> 38 #include <sys/socketvar.h> 39 #include <sys/bootprops.h> 40 41 extern ib_boot_prop_t *iscsiboot_prop; 42 43 static iscsi_status_t iscsi_create_sendtgts_list(iscsi_conn_t *icp, 44 char *data, int data_len, iscsi_sendtgts_list_t *stl); 45 46 /* 47 * iscsi_ioctl_copyin - 48 */ 49 void * 50 iscsi_ioctl_copyin(caddr_t arg, int mode, size_t size) 51 { 52 void *data = NULL; 53 54 ASSERT(arg != NULL); 55 ASSERT(size != 0); 56 57 data = kmem_alloc(size, KM_SLEEP); 58 59 if (ddi_copyin(arg, data, size, mode) != 0) { 60 kmem_free(data, size); 61 data = NULL; 62 } 63 return (data); 64 } 65 66 /* 67 * iscsi_ioctl_copyout - 68 */ 69 int 70 iscsi_ioctl_copyout(void *data, size_t size, caddr_t arg, int mode) 71 { 72 int rtn; 73 74 rtn = EFAULT; 75 if (ddi_copyout(data, arg, size, mode) == 0) { 76 rtn = 0; 77 } 78 kmem_free(data, size); 79 return (rtn); 80 } 81 82 /* 83 * iscsi_conn_list_get_copyin - 84 */ 85 iscsi_conn_list_t * 86 iscsi_ioctl_conn_oid_list_get_copyin(caddr_t arg, int mode) 87 { 88 iscsi_conn_list_t *cl_tmp; 89 iscsi_conn_list_t *cl = NULL; 90 size_t alloc_len; 91 92 ASSERT(arg != NULL); 93 94 cl_tmp = (iscsi_conn_list_t *)kmem_zalloc(sizeof (*cl_tmp), KM_SLEEP); 95 96 if (ddi_copyin(arg, cl_tmp, sizeof (*cl_tmp), mode) == 0) { 97 98 if (cl_tmp->cl_vers == ISCSI_INTERFACE_VERSION) { 99 alloc_len = sizeof (*cl); 100 if (cl_tmp->cl_in_cnt != 0) { 101 alloc_len += ((cl_tmp->cl_in_cnt - 1) * 102 sizeof (iscsi_if_conn_t)); 103 } 104 105 cl = (iscsi_conn_list_t *)kmem_zalloc(alloc_len, 106 KM_SLEEP); 107 bcopy(cl_tmp, cl, sizeof (*cl_tmp)); 108 } 109 } 110 kmem_free(cl_tmp, sizeof (*cl_tmp)); 111 return (cl); 112 } 113 114 /* 115 * iscsi_conn_list_get_copyout - 116 */ 117 int 118 iscsi_ioctl_conn_oid_list_get_copyout(iscsi_conn_list_t *cl, caddr_t arg, 119 int mode) 120 { 121 size_t alloc_len; 122 int rtn; 123 124 ASSERT(cl != NULL); 125 ASSERT(arg != NULL); 126 127 rtn = EFAULT; 128 alloc_len = sizeof (*cl); 129 if (cl->cl_in_cnt != 0) { 130 alloc_len += ((cl->cl_in_cnt - 1) * sizeof (iscsi_if_conn_t)); 131 } 132 133 if (ddi_copyout(cl, arg, alloc_len, mode) == 0) { 134 rtn = 0; 135 } 136 kmem_free(cl, alloc_len); 137 return (rtn); 138 } 139 140 /* 141 * iscsi_conn_oid_list_get - 142 */ 143 boolean_t 144 iscsi_ioctl_conn_oid_list_get(iscsi_hba_t *ihp, iscsi_conn_list_t *cl) 145 { 146 iscsi_sess_t *isp; 147 iscsi_conn_t *icp; 148 iscsi_if_conn_t *cnx; 149 uint32_t target_oid; 150 151 /* Let's check the version. */ 152 if (cl->cl_vers != ISCSI_INTERFACE_VERSION) { 153 return (B_FALSE); 154 } 155 156 /* We preinitialize the output connection counter. */ 157 cl->cl_out_cnt = 0; 158 159 /* The list of sessions is walked holding the HBA mutex. */ 160 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 161 isp = ihp->hba_sess_list; 162 163 /* 164 * Check to see if oid references a target-param oid. If so, 165 * find the associated session oid before getting lu list. 166 */ 167 if (iscsi_targetparam_get_name(cl->cl_sess_oid) != NULL) { 168 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) { 169 if (isp->sess_target_oid == cl->cl_sess_oid) { 170 target_oid = isp->sess_oid; 171 break; 172 } 173 } 174 } else { 175 target_oid = cl->cl_sess_oid; 176 } 177 178 while (isp != NULL) { 179 ASSERT(isp->sess_sig == ISCSI_SIG_SESS); 180 181 /* return connections for NORMAL sessions only */ 182 if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) && 183 ((cl->cl_all_sess == B_TRUE) || 184 (target_oid == isp->sess_oid))) { 185 /* 186 * The list of connections is walked holding 187 * the session mutex. 188 */ 189 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 190 icp = isp->sess_conn_list; 191 192 while (icp != NULL) { 193 ASSERT(icp->conn_sig == ISCSI_SIG_CONN); 194 195 if (icp->conn_state == 196 ISCSI_CONN_STATE_LOGGED_IN) { 197 198 if (cl->cl_out_cnt < cl->cl_in_cnt) { 199 /* There's still room. */ 200 cnx = 201 &cl->cl_list[ 202 cl->cl_out_cnt]; 203 204 bzero(cnx, sizeof (*cnx)); 205 206 cnx->c_cid = icp->conn_cid; 207 cnx->c_oid = icp->conn_oid; 208 cnx->c_sess_oid = isp->sess_oid; 209 } 210 ++cl->cl_out_cnt; 211 } 212 icp = icp->conn_next; 213 } 214 rw_exit(&isp->sess_conn_list_rwlock); 215 216 if (cl->cl_all_sess == B_FALSE) { 217 /* 218 * We got here because it was the only session 219 * we were looking for. We can exit now. 220 */ 221 break; 222 } 223 } 224 isp = isp->sess_next; 225 } 226 rw_exit(&ihp->hba_sess_list_rwlock); 227 return (B_TRUE); 228 } 229 230 /* 231 * iscsi_ioctl_conn_props_get - 232 */ 233 boolean_t 234 iscsi_ioctl_conn_props_get(iscsi_hba_t *ihp, iscsi_conn_props_t *cp) 235 { 236 iscsi_sess_t *isp; 237 iscsi_conn_t *icp; 238 boolean_t rtn; 239 idm_conn_t *idm_conn; 240 241 /* Let's check the version. */ 242 if (cp->cp_vers != ISCSI_INTERFACE_VERSION) { 243 return (B_FALSE); 244 } 245 246 /* Let's find the session. */ 247 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 248 if (iscsi_sess_get(cp->cp_sess_oid, ihp, &isp) != 0) { 249 rw_exit(&ihp->hba_sess_list_rwlock); 250 return (B_FALSE); 251 } 252 253 ASSERT(isp->sess_sig == ISCSI_SIG_SESS); 254 255 rtn = B_FALSE; 256 257 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 258 icp = isp->sess_conn_list; 259 cp->cp_params_valid = B_FALSE; 260 261 while (icp != NULL) { 262 263 ASSERT(icp->conn_sig == ISCSI_SIG_CONN); 264 265 if (icp->conn_oid == cp->cp_oid) { 266 struct sockaddr_storage *sal; 267 struct sockaddr_storage *sar; 268 269 idm_conn = 270 (idm_conn_t *)icp->conn_ic; 271 272 sal = &idm_conn->ic_laddr; 273 sar = &idm_conn->ic_raddr; 274 275 /* Local Address */ 276 if (sal->ss_family == AF_INET) { 277 bcopy(&idm_conn->ic_laddr, 278 &cp->cp_local, 279 sizeof (struct sockaddr_in)); 280 } else { 281 bcopy(&idm_conn->ic_laddr, 282 &cp->cp_local, 283 sizeof (struct sockaddr_in6)); 284 } 285 286 /* Peer Address */ 287 if (sar->ss_family == AF_INET) { 288 bcopy(&idm_conn->ic_raddr, 289 &cp->cp_peer, 290 sizeof (struct sockaddr_in)); 291 } else { 292 bcopy(&idm_conn->ic_raddr, 293 &cp->cp_peer, 294 sizeof (struct sockaddr_in6)); 295 } 296 297 if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) { 298 cp->cp_params_valid = B_TRUE; 299 bcopy(&icp->conn_params, &cp->cp_params, 300 sizeof (icp->conn_params)); 301 } 302 303 rtn = B_TRUE; 304 break; 305 } 306 icp = icp->conn_next; 307 } 308 rw_exit(&isp->sess_conn_list_rwlock); 309 rw_exit(&ihp->hba_sess_list_rwlock); 310 return (rtn); 311 } 312 313 314 /* 315 * iscsi_ioctl_sendtgts_get - 0 on success; errno on failure 316 * 317 */ 318 int 319 iscsi_ioctl_sendtgts_get(iscsi_hba_t *ihp, iscsi_sendtgts_list_t *stl) 320 { 321 #define ISCSI_SENDTGTS_REQ_STR "SendTargets=All" 322 323 int rtn = EFAULT; 324 iscsi_status_t status; 325 iscsi_sess_t *isp; 326 iscsi_conn_t *icp; 327 uint32_t oid; 328 char *data; 329 uint32_t data_len; 330 uint32_t rx_data_len; 331 iscsi_sockaddr_t addr_snd; 332 333 ASSERT(ihp != NULL); 334 ASSERT(stl != NULL); 335 336 iscsid_addr_to_sockaddr(stl->stl_entry.e_insize, 337 &stl->stl_entry.e_u, stl->stl_entry.e_port, 338 &addr_snd.sin); 339 340 /* create discovery session */ 341 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); 342 isp = iscsi_sess_create(ihp, iSCSIDiscoveryMethodSendTargets, 343 NULL, SENDTARGETS_DISCOVERY, ISCSI_DEFAULT_TPGT, 344 ISCSI_SUN_ISID_5, ISCSI_SESS_TYPE_DISCOVERY, &oid); 345 if (isp == NULL) { 346 rw_exit(&ihp->hba_sess_list_rwlock); 347 return (1); 348 } 349 350 /* create connection */ 351 rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER); 352 status = iscsi_conn_create(&addr_snd.sin, isp, &icp); 353 rw_exit(&isp->sess_conn_list_rwlock); 354 355 if (!ISCSI_SUCCESS(status)) { 356 (void) iscsi_sess_destroy(isp); 357 rw_exit(&ihp->hba_sess_list_rwlock); 358 return (1); 359 } 360 rw_exit(&ihp->hba_sess_list_rwlock); 361 362 /* start login */ 363 mutex_enter(&icp->conn_state_mutex); 364 status = iscsi_conn_online(icp); 365 mutex_exit(&icp->conn_state_mutex); 366 367 if (status == ISCSI_STATUS_SUCCESS) { 368 data_len = icp->conn_params.max_xmit_data_seg_len; 369 retry_sendtgts: 370 /* alloc/init buffer for SendTargets req/resp */ 371 data = kmem_zalloc(data_len, KM_SLEEP); 372 bcopy(ISCSI_SENDTGTS_REQ_STR, data, 373 sizeof (ISCSI_SENDTGTS_REQ_STR)); 374 375 /* execute SendTargets operation */ 376 status = iscsi_handle_text(icp, data, data_len, 377 sizeof (ISCSI_SENDTGTS_REQ_STR), &rx_data_len); 378 379 /* check if allocated buffer is too small for response */ 380 if (status == ISCSI_STATUS_DATA_OVERFLOW) { 381 kmem_free(data, data_len); 382 data_len = rx_data_len; 383 goto retry_sendtgts; 384 } 385 386 if (ISCSI_SUCCESS(status)) { 387 status = iscsi_create_sendtgts_list(icp, data, 388 rx_data_len, stl); 389 if (ISCSI_SUCCESS(status)) { 390 rtn = 0; 391 } 392 } else { 393 rtn = EFAULT; 394 } 395 396 kmem_free(data, data_len); 397 } else { 398 rtn = EFAULT; 399 } 400 401 /* 402 * check if session is still alive. It may have been destroyed 403 * by a driver unload 404 */ 405 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); 406 if (iscsi_sess_get(oid, ihp, &isp) == 0) { 407 (void) iscsi_sess_destroy(isp); 408 } 409 rw_exit(&ihp->hba_sess_list_rwlock); 410 411 return (rtn); 412 } 413 414 415 /* 416 * iscsi_create_sendtgts_list - Based upon the given data, build a 417 * linked list of SendTarget information. The data passed into this 418 * function is expected to be the data portion(s) of SendTarget text 419 * response. 420 */ 421 static iscsi_status_t 422 iscsi_create_sendtgts_list(iscsi_conn_t *icp, char *data, int data_len, 423 iscsi_sendtgts_list_t *stl) 424 { 425 char *line = NULL; 426 boolean_t targetname_added = B_FALSE; 427 iscsi_sendtgts_entry_t *curr_ste = NULL, 428 *prev_ste = NULL; 429 struct hostent *hptr; 430 int error_num; 431 432 /* initialize number of targets found */ 433 stl->stl_out_cnt = 0; 434 435 if (data_len == 0) 436 return (ISCSI_STATUS_SUCCESS); 437 438 while ((line = iscsi_get_next_text(data, data_len, line)) != NULL) { 439 if (strncmp(TARGETNAME, line, strlen(TARGETNAME)) == 0) { 440 /* check if this is first targetname */ 441 if (prev_ste != NULL) { 442 stl->stl_out_cnt++; 443 } 444 if (stl->stl_out_cnt >= stl->stl_in_cnt) { 445 /* 446 * continue processing the data so that 447 * the total number of targets are known 448 * and the caller can retry with the correct 449 * number of entries in the list 450 */ 451 continue; 452 } 453 curr_ste = &(stl->stl_list[stl->stl_out_cnt]); 454 455 /* 456 * This entry will use the IP address and port 457 * that was passed into this routine. If the next 458 * line that we receive is a TargetAddress we will 459 * know to modify this entry with the new IP address, 460 * port and portal group tag. If this state flag 461 * is not set we'll just create a new entry using 462 * only the previous entries targetname. 463 */ 464 (void) strncpy((char *)curr_ste->ste_name, 465 line + strlen(TARGETNAME), 466 sizeof (curr_ste->ste_name)); 467 468 if (icp->conn_base_addr.sin.sa_family == AF_INET) { 469 470 struct sockaddr_in *addr_in = 471 &icp->conn_base_addr.sin4; 472 curr_ste->ste_ipaddr.a_addr.i_insize = 473 sizeof (struct in_addr); 474 bcopy(&addr_in->sin_addr.s_addr, 475 &curr_ste->ste_ipaddr.a_addr.i_addr, 476 sizeof (struct in_addr)); 477 curr_ste->ste_ipaddr.a_port = 478 htons(addr_in->sin_port); 479 480 } else { 481 482 struct sockaddr_in6 *addr_in6 = 483 &icp->conn_base_addr.sin6; 484 curr_ste->ste_ipaddr.a_addr.i_insize = 485 sizeof (struct in6_addr); 486 bcopy(&addr_in6->sin6_addr.s6_addr, 487 &curr_ste->ste_ipaddr.a_addr.i_addr, 488 sizeof (struct in6_addr)); 489 curr_ste->ste_ipaddr.a_port = 490 htons(addr_in6->sin6_port); 491 } 492 curr_ste->ste_tpgt = -1; 493 494 targetname_added = B_TRUE; 495 496 } else if (strncmp(TARGETADDRESS, line, 497 strlen(TARGETADDRESS)) == 0) { 498 499 char *in_str, 500 *tmp_buf, 501 *addr_str, 502 *port_str, 503 *tpgt_str; 504 int type, 505 tmp_buf_len; 506 long result; 507 508 /* 509 * If TARGETADDRESS is first line a SendTarget response 510 * (i.e. no TARGETNAME lines preceding), treat as 511 * an error. To check this an assumption is made that 512 * at least one sendtarget_entry_t should exist prior 513 * to entering this code. 514 */ 515 if (prev_ste == NULL) { 516 cmn_err(CE_NOTE, "SendTargets protocol error: " 517 "TARGETADDRESS first"); 518 return (ISCSI_STATUS_PROTOCOL_ERROR); 519 } 520 521 /* 522 * If we can't find an '=' then the sendtargets 523 * response if invalid per spec. Return empty list. 524 */ 525 in_str = strchr(line, '='); 526 if (in_str == NULL) { 527 return (ISCSI_STATUS_PROTOCOL_ERROR); 528 } 529 530 /* move past the '=' */ 531 in_str++; 532 533 /* Copy addr, port, and tpgt into temporary buffer */ 534 tmp_buf_len = strlen(in_str) + 1; 535 tmp_buf = kmem_zalloc(tmp_buf_len, KM_SLEEP); 536 (void) strncpy(tmp_buf, in_str, tmp_buf_len); 537 538 /* 539 * Parse the addr, port, and tpgt from 540 * sendtarget response 541 */ 542 if (parse_addr_port_tpgt(tmp_buf, &addr_str, &type, 543 &port_str, &tpgt_str) == B_FALSE) { 544 /* Unable to extract addr */ 545 kmem_free(tmp_buf, tmp_buf_len); 546 return (ISCSI_STATUS_PROTOCOL_ERROR); 547 } 548 549 /* Now convert string addr to binary */ 550 hptr = kgetipnodebyname(addr_str, type, 551 AI_ALL, &error_num); 552 if (!hptr) { 553 /* Unable to get valid address */ 554 kmem_free(tmp_buf, tmp_buf_len); 555 return (ISCSI_STATUS_PROTOCOL_ERROR); 556 } 557 558 /* Check if space for response */ 559 if (targetname_added == B_FALSE) { 560 stl->stl_out_cnt++; 561 if (stl->stl_out_cnt >= stl->stl_in_cnt) { 562 /* 563 * continue processing the data so that 564 * the total number of targets are 565 * known and the caller can retry with 566 * the correct number of entries in 567 * the list 568 */ 569 kfreehostent(hptr); 570 kmem_free(tmp_buf, tmp_buf_len); 571 continue; 572 } 573 curr_ste = &(stl->stl_list[stl->stl_out_cnt]); 574 (void) strcpy((char *)curr_ste->ste_name, 575 (char *)prev_ste->ste_name); 576 } 577 578 curr_ste->ste_ipaddr.a_addr.i_insize = hptr->h_length; 579 bcopy(*hptr->h_addr_list, 580 &(curr_ste->ste_ipaddr.a_addr.i_addr), 581 curr_ste->ste_ipaddr.a_addr.i_insize); 582 kfreehostent(hptr); 583 584 if (port_str != NULL) { 585 (void) ddi_strtol(port_str, NULL, 0, &result); 586 curr_ste->ste_ipaddr.a_port = (short)result; 587 } else { 588 curr_ste->ste_ipaddr.a_port = ISCSI_LISTEN_PORT; 589 } 590 591 if (tpgt_str != NULL) { 592 (void) ddi_strtol(tpgt_str, NULL, 0, &result); 593 curr_ste->ste_tpgt = (short)result; 594 } else { 595 cmn_err(CE_NOTE, "SendTargets protocol error: " 596 "TPGT not specified"); 597 kmem_free(tmp_buf, tmp_buf_len); 598 return (ISCSI_STATUS_PROTOCOL_ERROR); 599 } 600 601 kmem_free(tmp_buf, tmp_buf_len); 602 603 targetname_added = B_FALSE; 604 605 } else if (strlen(line) != 0) { 606 /* 607 * Any other string besides an empty string 608 * is a protocol error 609 */ 610 cmn_err(CE_NOTE, "SendTargets protocol error: " 611 "unexpected response"); 612 return (ISCSI_STATUS_PROTOCOL_ERROR); 613 } 614 615 prev_ste = curr_ste; 616 } 617 618 /* 619 * If target found increment out count one more time because 620 * this is the total number of entries in the list not an index 621 * like it was used above 622 */ 623 if (prev_ste != NULL) { 624 stl->stl_out_cnt++; 625 } 626 627 return (ISCSI_STATUS_SUCCESS); 628 } 629 630 /* 631 * iscsi_set_param - This function is a helper to ISCSI_SET_PARAM 632 * IOCTL 633 */ 634 int 635 iscsi_set_param(iscsi_login_params_t *params, iscsi_param_set_t *ipsp) 636 { 637 int rtn = 0; 638 iscsi_param_get_t *ipgp; 639 640 /* 641 * Use get param to get the min, max and increment values for the 642 * given parameter so validation can be done on the new value. 643 */ 644 ipgp = (iscsi_param_get_t *)kmem_alloc(sizeof (*ipgp), KM_SLEEP); 645 ipgp->g_param = ipsp->s_param; 646 rtn = iscsi_get_param(params, B_TRUE, ipgp); 647 if (rtn != 0) { 648 kmem_free(ipgp, sizeof (*ipgp)); 649 return (rtn); 650 } 651 652 if (ipsp->s_param == ISCSI_LOGIN_PARAM_HEADER_DIGEST || 653 ipsp->s_param == ISCSI_LOGIN_PARAM_DATA_DIGEST || 654 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN || 655 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT || 656 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH || 657 ipsp->s_param == ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH || 658 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH) { 659 660 if (ipsp->s_value.v_integer < ipgp->g_value.v_integer.i_min || 661 ipsp->s_value.v_integer > ipgp->g_value.v_integer.i_max || 662 (ipsp->s_value.v_integer % 663 ipgp->g_value.v_integer.i_incr) != 0) { 664 rtn = EINVAL; 665 kmem_free(ipgp, sizeof (*ipgp)); 666 return (rtn); 667 } 668 669 } 670 kmem_free(ipgp, sizeof (*ipgp)); 671 672 673 switch (ipsp->s_param) { 674 675 /* 676 * Boolean parameters 677 */ 678 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER: 679 params->data_sequence_in_order = ipsp->s_value.v_bool; 680 break; 681 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA: 682 params->immediate_data = ipsp->s_value.v_bool; 683 break; 684 case ISCSI_LOGIN_PARAM_INITIAL_R2T: 685 params->initial_r2t = ipsp->s_value.v_bool; 686 break; 687 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER: 688 params->data_pdu_in_order = ipsp->s_value.v_bool; 689 break; 690 691 /* 692 * Integer parameters 693 */ 694 case ISCSI_LOGIN_PARAM_HEADER_DIGEST: 695 params->header_digest = ipsp->s_value.v_integer; 696 break; 697 case ISCSI_LOGIN_PARAM_DATA_DIGEST: 698 params->data_digest = ipsp->s_value.v_integer; 699 break; 700 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN: 701 params->default_time_to_retain = ipsp->s_value.v_integer; 702 break; 703 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT: 704 params->default_time_to_wait = ipsp->s_value.v_integer; 705 break; 706 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH: 707 params->max_recv_data_seg_len = ipsp->s_value.v_integer; 708 break; 709 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH: 710 if (ipsp->s_value.v_integer <= params->max_burst_length) { 711 params->first_burst_length = ipsp->s_value.v_integer; 712 } else { 713 rtn = EINVAL; 714 } 715 break; 716 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH: 717 if (ipsp->s_value.v_integer >= params->first_burst_length) { 718 params->max_burst_length = ipsp->s_value.v_integer; 719 } else { 720 rtn = EINVAL; 721 } 722 break; 723 724 /* 725 * Integer parameters which currently are unsettable 726 */ 727 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS: 728 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T: 729 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL: 730 rtn = ENOTSUP; 731 break; 732 733 default: 734 rtn = EINVAL; 735 break; 736 } 737 return (rtn); 738 } 739 740 int 741 iscsi_set_params(iscsi_param_set_t *ils, iscsi_hba_t *ihp, boolean_t persist) 742 { 743 iscsi_login_params_t *params = NULL; 744 uchar_t *name = NULL; 745 iscsi_sess_t *isp = NULL; 746 iscsi_param_get_t *ilg; 747 int rtn = 0; 748 uint32_t event_count; 749 750 /* handle special case for Initiator name */ 751 if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_NAME) { 752 (void) strlcpy((char *)ihp->hba_name, 753 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN); 754 if (persist) { 755 char *name; 756 boolean_t rval; 757 758 /* save off old Initiator name */ 759 name = kmem_alloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); 760 rval = persistent_initiator_name_get(name, 761 ISCSI_MAX_NAME_LEN); 762 763 (void) persistent_initiator_name_set( 764 (char *)ihp->hba_name); 765 if (rval == B_TRUE) { 766 /* 767 * check to see if we have login param, 768 * chap param, or authentication params 769 * loaded in persistent that we have to change 770 * the name of 771 */ 772 persistent_param_t *pp; 773 iscsi_chap_props_t *chap; 774 iscsi_auth_props_t *auth; 775 776 /* checking login params */ 777 pp = kmem_zalloc(sizeof (persistent_param_t), 778 KM_SLEEP); 779 if (persistent_param_get(name, pp)) { 780 rval = persistent_param_clear(name); 781 if (rval == B_TRUE) { 782 rval = persistent_param_set( 783 (char *)ihp->hba_name, pp); 784 } 785 if (rval == B_FALSE) { 786 rtn = EFAULT; 787 } 788 } 789 kmem_free(pp, sizeof (persistent_param_t)); 790 791 /* check chap params */ 792 chap = kmem_zalloc(sizeof (iscsi_chap_props_t), 793 KM_SLEEP); 794 if (persistent_chap_get(name, chap)) { 795 rval = persistent_chap_clear(name); 796 if (rval == B_TRUE) { 797 /* 798 * Update CHAP user name only if the 799 * original username was set to the 800 * initiator node name. Otherwise 801 * leave it the way it is. 802 */ 803 int userSize; 804 userSize = 805 sizeof (chap->c_user); 806 if (strncmp((char *) 807 chap->c_user, name, 808 sizeof (chap->c_user)) 809 == 0) { 810 bzero(chap->c_user, 811 userSize); 812 bcopy((char *) 813 ihp->hba_name, 814 chap->c_user, 815 strlen((char *) 816 ihp->hba_name)); 817 chap->c_user_len = 818 strlen((char *) 819 ihp->hba_name); 820 821 } 822 rval = persistent_chap_set( 823 (char *)ihp->hba_name, chap); 824 } 825 if (rval == B_FALSE) { 826 rtn = EFAULT; 827 } 828 } 829 kmem_free(chap, sizeof (iscsi_chap_props_t)); 830 831 /* check authentication params */ 832 auth = kmem_zalloc(sizeof (iscsi_auth_props_t), 833 KM_SLEEP); 834 if (persistent_auth_get(name, auth)) { 835 rval = persistent_auth_clear(name); 836 if (rval == B_TRUE) { 837 rval = persistent_auth_set( 838 (char *)ihp->hba_name, 839 auth); 840 } 841 if (rval == B_FALSE) { 842 rtn = EFAULT; 843 } 844 } 845 kmem_free(auth, sizeof (iscsi_auth_props_t)); 846 } 847 kmem_free(name, ISCSI_MAX_NAME_LEN); 848 } 849 } else if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_ALIAS) { 850 (void) strlcpy((char *)ihp->hba_alias, 851 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN); 852 ihp->hba_alias_length = 853 strlen((char *)ils->s_value.v_name); 854 if (persist) { 855 (void) persistent_alias_name_set( 856 (char *)ihp->hba_alias); 857 } 858 } else { 859 /* switch login based if looking for initiator params */ 860 if (ils->s_oid == ihp->hba_oid) { 861 /* initiator */ 862 params = &ihp->hba_params; 863 name = ihp->hba_name; 864 rtn = iscsi_set_param(params, ils); 865 } else { 866 /* session */ 867 name = iscsi_targetparam_get_name(ils->s_oid); 868 if (name == NULL) 869 rtn = EFAULT; 870 871 if (persist && (rtn == 0)) { 872 boolean_t rval; 873 persistent_param_t *pp; 874 875 pp = (persistent_param_t *) 876 kmem_zalloc(sizeof (*pp), KM_SLEEP); 877 if (!persistent_param_get((char *)name, pp)) { 878 iscsi_set_default_login_params( 879 &pp->p_params); 880 } 881 882 pp->p_bitmap |= (1 << ils->s_param); 883 rtn = iscsi_set_param(&pp->p_params, ils); 884 if (rtn == 0) { 885 rval = persistent_param_set( 886 (char *)name, pp); 887 if (rval == B_FALSE) { 888 rtn = EFAULT; 889 } 890 } 891 kmem_free(pp, sizeof (*pp)); 892 } 893 894 /* 895 * Here may have multiple sessions with different 896 * tpgt values. So it is needed to loop through 897 * the sessions and update all sessions. 898 */ 899 if (rtn == 0) { 900 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 901 for (isp = ihp->hba_sess_list; isp; 902 isp = isp->sess_next) { 903 if (iscsiboot_prop && 904 isp->sess_boot && 905 iscsi_chk_bootlun_mpxio(ihp)) { 906 /* 907 * MPxIO is enabled so capable 908 * of changing. All changes 909 * will be applied later, 910 * after this function 911 */ 912 continue; 913 } 914 915 if (strncmp((char *)isp->sess_name, 916 (char *)name, 917 ISCSI_MAX_NAME_LEN) == 0) { 918 event_count = atomic_inc_32_nv(&isp->sess_state_event_count); 919 iscsi_sess_enter_state_zone(isp); 920 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7, event_count); 921 iscsi_sess_exit_state_zone(isp); 922 } 923 } 924 rw_exit(&ihp->hba_sess_list_rwlock); 925 } 926 927 } /* end of 'else' */ 928 929 if (params && persist && (rtn == 0)) { 930 boolean_t rval; 931 persistent_param_t *pp; 932 933 pp = (persistent_param_t *) 934 kmem_zalloc(sizeof (*pp), KM_SLEEP); 935 (void) persistent_param_get((char *)name, pp); 936 pp->p_bitmap |= (1 << ils->s_param); 937 bcopy(params, &pp->p_params, sizeof (*params)); 938 rval = persistent_param_set((char *)name, pp); 939 if (rval == B_FALSE) { 940 rtn = EFAULT; 941 } 942 kmem_free(pp, sizeof (*pp)); 943 } 944 /* 945 * if initiator parameter set, modify all associated 946 * sessions that don't already have the parameter 947 * overriden 948 */ 949 if ((ils->s_oid == ihp->hba_oid) && (rtn == 0)) { 950 ilg = (iscsi_param_get_t *) 951 kmem_alloc(sizeof (*ilg), KM_SLEEP); 952 953 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 954 for (isp = ihp->hba_sess_list; isp; 955 isp = isp->sess_next) { 956 ilg->g_param = ils->s_param; 957 params = &isp->sess_params; 958 if (iscsi_get_persisted_param( 959 isp->sess_name, ilg, params) != 0) { 960 rtn = iscsi_set_param(params, ils); 961 if (rtn != 0) { 962 break; 963 } 964 if (iscsiboot_prop && 965 isp->sess_boot && 966 iscsi_chk_bootlun_mpxio(ihp)) { 967 /* 968 * MPxIO is enabled so capable 969 * of changing. Changes will 970 * be applied later, right 971 * after this function 972 */ 973 continue; 974 } 975 976 /* 977 * Notify the session that 978 * the login parameters have 979 * changed. 980 */ 981 event_count = atomic_inc_32_nv( 982 &isp->sess_state_event_count); 983 iscsi_sess_enter_state_zone(isp); 984 iscsi_sess_state_machine(isp, 985 ISCSI_SESS_EVENT_N7, event_count); 986 iscsi_sess_exit_state_zone(isp); 987 } 988 } 989 kmem_free(ilg, sizeof (*ilg)); 990 rw_exit(&ihp->hba_sess_list_rwlock); 991 } 992 } 993 return (rtn); 994 } 995 996 int 997 iscsi_target_prop_mod(iscsi_hba_t *ihp, iscsi_property_t *ipp, int cmd) 998 { 999 iscsi_sess_t *isp = NULL; 1000 iscsi_conn_t *icp; 1001 int rtn; 1002 char *name; 1003 1004 /* 1005 * If we're just attempting to get the target properties don't 1006 * create the session if it doesn't already exist. If we setting 1007 * the property then create the session if needed because we'll 1008 * most likely see an ISCSI_LOGIN in a few. 1009 */ 1010 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 1011 1012 /* 1013 * If the oid does represent a session check to see 1014 * if it is a target oid. If so, return the target's 1015 * associated session. 1016 */ 1017 rtn = iscsi_sess_get(ipp->p_oid, ihp, &isp); 1018 if (rtn != 0) { 1019 rtn = iscsi_sess_get_by_target(ipp->p_oid, ihp, &isp); 1020 } 1021 1022 /* 1023 * If rtn is zero then we have found an existing session. 1024 * Use the session name for database lookup. If rtn is 1025 * non-zero then create a targetparam object and use 1026 * its name for database lookup. 1027 */ 1028 if (rtn == 0) { 1029 name = (char *)isp->sess_name; 1030 } else { 1031 name = (char *)iscsi_targetparam_get_name(ipp->p_oid); 1032 isp = NULL; 1033 } 1034 1035 if (name == NULL) { 1036 rw_exit(&ihp->hba_sess_list_rwlock); 1037 rtn = EFAULT; 1038 return (rtn); 1039 } 1040 1041 rtn = 0; 1042 if (cmd == ISCSI_TARGET_PROPS_GET) { 1043 /* 1044 * If isp is not null get the session's parameters, otherwise 1045 * the get is for a target-param object so defaults need to 1046 * be returned. 1047 */ 1048 if (isp != NULL) { 1049 int conn_count = 0; 1050 1051 bcopy(isp->sess_alias, ipp->p_alias, 1052 isp->sess_alias_length); 1053 bcopy(isp->sess_name, ipp->p_name, 1054 isp->sess_name_length); 1055 ipp->p_alias_len = isp->sess_alias_length; 1056 ipp->p_name_len = isp->sess_name_length; 1057 ipp->p_discovery = isp->sess_discovered_by; 1058 ipp->p_last_err = isp->sess_last_err; 1059 ipp->p_tpgt_conf = isp->sess_tpgt_conf; 1060 ipp->p_tpgt_nego = isp->sess_tpgt_nego; 1061 bcopy(isp->sess_isid, ipp->p_isid, ISCSI_ISID_LEN); 1062 1063 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 1064 for (icp = isp->sess_conn_list; icp; 1065 icp = icp->conn_next) { 1066 if (icp->conn_state == 1067 ISCSI_CONN_STATE_LOGGED_IN) { 1068 conn_count++; 1069 } 1070 } 1071 rw_exit(&isp->sess_conn_list_rwlock); 1072 ipp->p_num_of_connections = conn_count; 1073 ipp->p_connected = (conn_count > 0) ? B_TRUE : B_FALSE; 1074 } else { 1075 bcopy(name, ipp->p_name, strlen(name)); 1076 ipp->p_name_len = strlen(name); 1077 bcopy("", ipp->p_alias, strlen("")); 1078 ipp->p_alias_len = strlen(""); 1079 ipp->p_discovery = iSCSIDiscoveryMethodUnknown; 1080 ipp->p_last_err = NoError; 1081 ipp->p_tpgt_conf = ISCSI_DEFAULT_TPGT; 1082 ipp->p_tpgt_nego = ISCSI_DEFAULT_TPGT; 1083 ipp->p_num_of_connections = 0; 1084 ipp->p_connected = B_FALSE; 1085 } 1086 } else { 1087 if (isp == NULL) { 1088 rw_exit(&ihp->hba_sess_list_rwlock); 1089 rtn = EFAULT; 1090 return (rtn); 1091 } 1092 1093 /* ISCSI_TARGET_PROPS_SET */ 1094 /* 1095 * only update if new, otherwise could clear out alias 1096 * if just updating the discovery. 1097 */ 1098 if (ipp->p_alias_len != 0) { 1099 bcopy(ipp->p_alias, isp->sess_alias, 1100 ipp->p_alias_len); 1101 isp->sess_alias_length = ipp->p_alias_len; 1102 } 1103 isp->sess_discovered_by = ipp->p_discovery; 1104 } 1105 rw_exit(&ihp->hba_sess_list_rwlock); 1106 return (rtn); 1107 } 1108 1109 /* 1110 * iscsi_ioctl_get_config_sess - gets configured session information 1111 * 1112 * This function is an ioctl helper function to get the 1113 * configured session information from the persistent store. 1114 */ 1115 int 1116 iscsi_ioctl_get_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1117 { 1118 uchar_t *name; 1119 1120 /* Get the matching iscsi node name for the oid */ 1121 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1122 /* initiator name */ 1123 name = ihp->hba_name; 1124 } else { 1125 /* target name */ 1126 name = iscsi_targetparam_get_name(ics->ics_oid); 1127 if (name == NULL) { 1128 /* invalid node name */ 1129 return (EINVAL); 1130 } 1131 } 1132 1133 /* get configured session information */ 1134 if (persistent_get_config_session((char *)name, ics) == B_FALSE) { 1135 /* 1136 * There might not be anything in the database yet. If 1137 * this is a request for the target check the initiator 1138 * value. If neither is set return the default value. 1139 */ 1140 if (ics->ics_oid != ISCSI_INITIATOR_OID) { 1141 if (persistent_get_config_session( 1142 (char *)ihp->hba_name, ics) == B_FALSE) { 1143 /* 1144 * No initiator value is set. 1145 * Return the defaults. 1146 */ 1147 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1148 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1149 } 1150 } else { 1151 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1152 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1153 } 1154 } 1155 1156 return (0); 1157 } 1158 1159 /* 1160 * iscsi_ioctl_set_config_sess - sets configured session information 1161 * 1162 * This function is an ioctl helper function to set the 1163 * configured session information in the persistent store. 1164 * In addition it will notify any active sessions of the 1165 * changed so this can update binding information. It 1166 * will also destroy sessions that were removed and add 1167 * new sessions. 1168 */ 1169 int 1170 iscsi_ioctl_set_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1171 { 1172 uchar_t *name; 1173 iscsi_sess_t *isp; 1174 uint32_t event_count; 1175 1176 /* check range infomration */ 1177 if ((ics->ics_in < ISCSI_MIN_CONFIG_SESSIONS) || 1178 (ics->ics_in > ISCSI_MAX_CONFIG_SESSIONS)) { 1179 /* invalid range information */ 1180 return (EINVAL); 1181 } 1182 1183 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1184 name = ihp->hba_name; 1185 } else { 1186 /* get target name */ 1187 name = iscsi_targetparam_get_name(ics->ics_oid); 1188 if (name == NULL) { 1189 /* invalid node name */ 1190 return (EINVAL); 1191 } 1192 } 1193 1194 /* store the new information */ 1195 if (persistent_set_config_session((char *)name, ics) == B_FALSE) { 1196 /* failed to store new information */ 1197 return (EINVAL); 1198 } 1199 1200 /* notify existing sessions of change */ 1201 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 1202 isp = ihp->hba_sess_list; 1203 while (isp != NULL) { 1204 1205 if ((ics->ics_oid == ISCSI_INITIATOR_OID) || 1206 (strncmp((char *)isp->sess_name, (char *)name, 1207 ISCSI_MAX_NAME_LEN) == 0)) { 1208 1209 /* 1210 * If this sessions least signficant byte 1211 * of the isid is less than or equal to 1212 * the the number of configured sessions 1213 * then we need to tear down this session. 1214 */ 1215 if (ics->ics_in <= isp->sess_isid[5]) { 1216 /* First attempt to destory the session */ 1217 if (ISCSI_SUCCESS(iscsi_sess_destroy(isp))) { 1218 isp = ihp->hba_sess_list; 1219 } else { 1220 /* 1221 * If we can't destroy it then 1222 * atleast poke it to disconnect 1223 * it. 1224 */ 1225 event_count = atomic_inc_32_nv( 1226 &isp->sess_state_event_count); 1227 iscsi_sess_enter_state_zone(isp); 1228 iscsi_sess_state_machine(isp, 1229 ISCSI_SESS_EVENT_N7, event_count); 1230 iscsi_sess_exit_state_zone(isp); 1231 1232 isp = isp->sess_next; 1233 } 1234 } else { 1235 isp = isp->sess_next; 1236 } 1237 } else { 1238 isp = isp->sess_next; 1239 } 1240 } 1241 rw_exit(&ihp->hba_sess_list_rwlock); 1242 1243 /* 1244 * The number of targets has changed. Since we don't expect 1245 * this to be a common operation lets keep the code simple and 1246 * just use a slightly larger hammer and poke discovery. This 1247 * force the reevaulation of this target and all other targets. 1248 */ 1249 iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); 1250 /* lock so only one config operation occrs */ 1251 sema_p(&iscsid_config_semaphore); 1252 iscsid_config_all(ihp, B_FALSE); 1253 sema_v(&iscsid_config_semaphore); 1254 1255 return (0); 1256 } 1257 1258 int 1259 iscsi_ioctl_set_tunable_param(iscsi_hba_t *ihp, iscsi_tunable_object_t *tpss) 1260 { 1261 uchar_t *name; 1262 iscsi_sess_t *isp; 1263 iscsi_conn_t *icp; 1264 int param_id = 0; 1265 persistent_tunable_param_t *pparam; 1266 1267 if (tpss->t_oid == ihp->hba_oid) { 1268 name = ihp->hba_name; 1269 } else { 1270 /* get target name */ 1271 name = iscsi_targetparam_get_name(tpss->t_oid); 1272 if (name == NULL) { 1273 /* invalid node name */ 1274 return (EINVAL); 1275 } 1276 } 1277 1278 pparam = (persistent_tunable_param_t *)kmem_zalloc(sizeof (*pparam), 1279 KM_SLEEP); 1280 if (persistent_get_tunable_param((char *)name, pparam) == B_FALSE) { 1281 /* use default value */ 1282 pparam->p_params.recv_login_rsp_timeout = 1283 ISCSI_DEFAULT_RX_TIMEOUT_VALUE; 1284 pparam->p_params.polling_login_delay = 1285 ISCSI_DEFAULT_LOGIN_POLLING_DELAY; 1286 pparam->p_params.conn_login_max = 1287 ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX; 1288 } 1289 1290 pparam->p_bitmap |= (1 << (tpss->t_param -1)); 1291 param_id = 1 << (tpss->t_param -1); 1292 switch (param_id) { 1293 case ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE: 1294 pparam->p_params.recv_login_rsp_timeout = 1295 tpss->t_value.v_integer; 1296 break; 1297 case ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY: 1298 pparam->p_params.polling_login_delay = 1299 tpss->t_value.v_integer; 1300 break; 1301 case ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX: 1302 pparam->p_params.conn_login_max = 1303 tpss->t_value.v_integer; 1304 break; 1305 default: 1306 break; 1307 } 1308 if (persistent_set_tunable_param((char *)name, 1309 pparam) == B_FALSE) { 1310 kmem_free(pparam, sizeof (*pparam)); 1311 return (EINVAL); 1312 } 1313 1314 if (tpss->t_oid == ihp->hba_oid) { 1315 bcopy(&pparam->p_params, &ihp->hba_tunable_params, 1316 sizeof (iscsi_tunable_params_t)); 1317 } 1318 1319 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 1320 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) { 1321 if (isp->sess_type != ISCSI_SESS_TYPE_NORMAL) { 1322 continue; 1323 } 1324 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 1325 icp = isp->sess_conn_list; 1326 while (icp != NULL) { 1327 if (strcmp((const char *)name, 1328 (const char *)isp->sess_name) == 0) { 1329 bcopy(&pparam->p_params, 1330 &icp->conn_tunable_params, 1331 sizeof (iscsi_tunable_params_t)); 1332 } else { 1333 /* 1334 * this session connected target 1335 * tunable parameters not set, 1336 * use initiator's default 1337 */ 1338 bcopy(&ihp->hba_tunable_params, 1339 &icp->conn_tunable_params, 1340 sizeof (iscsi_tunable_params_t)); 1341 } 1342 icp = icp->conn_next; 1343 } 1344 rw_exit(&isp->sess_conn_list_rwlock); 1345 } 1346 rw_exit(&ihp->hba_sess_list_rwlock); 1347 kmem_free(pparam, sizeof (*pparam)); 1348 return (0); 1349 } 1350