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