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 2008 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/scsi/adapters/iscsi_if.h> /* ioctl interfaces */ 33 /* protocol structs and defines */ 34 #include <sys/scsi/adapters/iscsi_protocol.h> 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 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 267 if (icp->conn_socket->so_laddr.soa_len <= 268 sizeof (cp->cp_local)) { 269 bcopy(icp->conn_socket->so_laddr.soa_sa, 270 &cp->cp_local, 271 icp->conn_socket->so_laddr.soa_len); 272 } 273 if (icp->conn_socket->so_faddr.soa_len <= 274 sizeof (cp->cp_peer)) { 275 bcopy(icp->conn_socket->so_faddr.soa_sa, 276 &cp->cp_peer, 277 icp->conn_socket->so_faddr.soa_len); 278 } 279 280 if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) { 281 cp->cp_params_valid = B_TRUE; 282 bcopy(&icp->conn_params, &cp->cp_params, 283 sizeof (icp->conn_params)); 284 } 285 286 rtn = B_TRUE; 287 break; 288 } 289 icp = icp->conn_next; 290 } 291 rw_exit(&isp->sess_conn_list_rwlock); 292 rw_exit(&ihp->hba_sess_list_rwlock); 293 return (rtn); 294 } 295 296 297 /* 298 * iscsi_ioctl_sendtgts_get - 0 on success; errno on failure 299 * 300 */ 301 int 302 iscsi_ioctl_sendtgts_get(iscsi_hba_t *ihp, iscsi_sendtgts_list_t *stl) 303 { 304 #define ISCSI_SENDTGTS_REQ_STR "SendTargets=All" 305 306 int rtn = EFAULT; 307 iscsi_status_t status; 308 iscsi_sess_t *isp; 309 iscsi_conn_t *icp; 310 uint32_t oid; 311 char *data; 312 uint32_t data_len; 313 uint32_t rx_data_len; 314 iscsi_sockaddr_t addr_snd; 315 316 ASSERT(ihp != NULL); 317 ASSERT(stl != NULL); 318 319 iscsid_addr_to_sockaddr(stl->stl_entry.e_insize, 320 &stl->stl_entry.e_u, stl->stl_entry.e_port, 321 &addr_snd.sin); 322 323 /* create discovery session */ 324 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); 325 isp = iscsi_sess_create(ihp, iSCSIDiscoveryMethodSendTargets, 326 NULL, SENDTARGETS_DISCOVERY, ISCSI_DEFAULT_TPGT, 327 ISCSI_SUN_ISID_5, ISCSI_SESS_TYPE_DISCOVERY, &oid); 328 if (isp == NULL) { 329 rw_exit(&ihp->hba_sess_list_rwlock); 330 return (1); 331 } 332 333 /* create connection */ 334 rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER); 335 status = iscsi_conn_create(&addr_snd.sin, isp, &icp); 336 rw_exit(&isp->sess_conn_list_rwlock); 337 338 if (!ISCSI_SUCCESS(status)) { 339 (void) iscsi_sess_destroy(isp); 340 rw_exit(&ihp->hba_sess_list_rwlock); 341 return (1); 342 } 343 rw_exit(&ihp->hba_sess_list_rwlock); 344 345 /* start login */ 346 mutex_enter(&icp->conn_state_mutex); 347 (void) iscsi_conn_state_machine(icp, ISCSI_CONN_EVENT_T1); 348 mutex_exit(&icp->conn_state_mutex); 349 350 if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) { 351 data_len = icp->conn_params.max_xmit_data_seg_len; 352 retry_sendtgts: 353 /* alloc/init buffer for SendTargets req/resp */ 354 data = kmem_zalloc(data_len, KM_SLEEP); 355 bcopy(ISCSI_SENDTGTS_REQ_STR, data, 356 sizeof (ISCSI_SENDTGTS_REQ_STR)); 357 358 /* execute SendTargets operation */ 359 status = iscsi_handle_text(icp, data, data_len, 360 sizeof (ISCSI_SENDTGTS_REQ_STR), &rx_data_len); 361 362 /* check if allocated buffer is too small for response */ 363 if (status == ISCSI_STATUS_DATA_OVERFLOW) { 364 kmem_free(data, data_len); 365 data_len = rx_data_len; 366 goto retry_sendtgts; 367 } 368 369 if (ISCSI_SUCCESS(status)) { 370 status = iscsi_create_sendtgts_list(icp, data, 371 rx_data_len, stl); 372 if (ISCSI_SUCCESS(status)) { 373 rtn = 0; 374 } 375 } else { 376 rtn = EFAULT; 377 } 378 379 kmem_free(data, data_len); 380 } else { 381 rtn = EFAULT; 382 } 383 384 /* 385 * check if session is still alive. It may have been destroyed 386 * by a driver unload 387 */ 388 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); 389 if (iscsi_sess_get(oid, ihp, &isp) == 0) { 390 (void) iscsi_sess_destroy(isp); 391 } 392 rw_exit(&ihp->hba_sess_list_rwlock); 393 394 return (rtn); 395 } 396 397 398 /* 399 * iscsi_create_sendtgts_list - Based upon the given data, build a 400 * linked list of SendTarget information. The data passed into this 401 * function is expected to be the data portion(s) of SendTarget text 402 * response. 403 */ 404 static iscsi_status_t 405 iscsi_create_sendtgts_list(iscsi_conn_t *icp, char *data, int data_len, 406 iscsi_sendtgts_list_t *stl) 407 { 408 char *line = NULL; 409 boolean_t targetname_added = B_FALSE; 410 iscsi_sendtgts_entry_t *curr_ste = NULL, 411 *prev_ste = NULL; 412 struct hostent *hptr; 413 int error_num; 414 415 /* initialize number of targets found */ 416 stl->stl_out_cnt = 0; 417 418 if (data_len == 0) 419 return (ISCSI_STATUS_SUCCESS); 420 421 while ((line = iscsi_get_next_text(data, data_len, line)) != NULL) { 422 if (strncmp(TARGETNAME, line, strlen(TARGETNAME)) == 0) { 423 /* check if this is first targetname */ 424 if (prev_ste != NULL) { 425 stl->stl_out_cnt++; 426 } 427 if (stl->stl_out_cnt >= stl->stl_in_cnt) { 428 /* 429 * continue processing the data so that 430 * the total number of targets are known 431 * and the caller can retry with the correct 432 * number of entries in the list 433 */ 434 continue; 435 } 436 curr_ste = &(stl->stl_list[stl->stl_out_cnt]); 437 438 /* 439 * This entry will use the IP address and port 440 * that was passed into this routine. If the next 441 * line that we receive is a TargetAddress we will 442 * know to modify this entry with the new IP address, 443 * port and portal group tag. If this state flag 444 * is not set we'll just create a new entry using 445 * only the previous entries targetname. 446 */ 447 (void) strncpy((char *)curr_ste->ste_name, 448 line + strlen(TARGETNAME), 449 sizeof (curr_ste->ste_name)); 450 451 if (icp->conn_base_addr.sin.sa_family == AF_INET) { 452 453 struct sockaddr_in *addr_in = 454 &icp->conn_base_addr.sin4; 455 curr_ste->ste_ipaddr.a_addr.i_insize = 456 sizeof (struct in_addr); 457 bcopy(&addr_in->sin_addr.s_addr, 458 &curr_ste->ste_ipaddr.a_addr.i_addr, 459 sizeof (struct in_addr)); 460 curr_ste->ste_ipaddr.a_port = 461 htons(addr_in->sin_port); 462 463 } else { 464 465 struct sockaddr_in6 *addr_in6 = 466 &icp->conn_base_addr.sin6; 467 curr_ste->ste_ipaddr.a_addr.i_insize = 468 sizeof (struct in6_addr); 469 bcopy(&addr_in6->sin6_addr.s6_addr, 470 &curr_ste->ste_ipaddr.a_addr.i_addr, 471 sizeof (struct in6_addr)); 472 curr_ste->ste_ipaddr.a_port = 473 htons(addr_in6->sin6_port); 474 } 475 curr_ste->ste_tpgt = -1; 476 477 targetname_added = B_TRUE; 478 479 } else if (strncmp(TARGETADDRESS, line, 480 strlen(TARGETADDRESS)) == 0) { 481 482 char *in_str, 483 *tmp_buf, 484 *addr_str, 485 *port_str, 486 *tpgt_str; 487 int type, 488 tmp_buf_len; 489 long result; 490 491 /* 492 * If TARGETADDRESS is first line a SendTarget response 493 * (i.e. no TARGETNAME lines preceding), treat as 494 * an error. To check this an assumption is made that 495 * at least one sendtarget_entry_t should exist prior 496 * to entering this code. 497 */ 498 if (prev_ste == NULL) { 499 cmn_err(CE_NOTE, "SendTargets protocol error: " 500 "TARGETADDRESS first"); 501 return (ISCSI_STATUS_PROTOCOL_ERROR); 502 } 503 504 /* 505 * If we can't find an '=' then the sendtargets 506 * response if invalid per spec. Return empty list. 507 */ 508 in_str = strchr(line, '='); 509 if (in_str == NULL) { 510 return (ISCSI_STATUS_PROTOCOL_ERROR); 511 } 512 513 /* move past the '=' */ 514 in_str++; 515 516 /* Copy addr, port, and tpgt into temporary buffer */ 517 tmp_buf_len = strlen(in_str) + 1; 518 tmp_buf = kmem_zalloc(tmp_buf_len, KM_SLEEP); 519 (void) strncpy(tmp_buf, in_str, tmp_buf_len); 520 521 /* 522 * Parse the addr, port, and tpgt from 523 * sendtarget response 524 */ 525 if (parse_addr_port_tpgt(tmp_buf, &addr_str, &type, 526 &port_str, &tpgt_str) == B_FALSE) { 527 /* Unable to extract addr */ 528 kmem_free(tmp_buf, tmp_buf_len); 529 return (ISCSI_STATUS_PROTOCOL_ERROR); 530 } 531 532 /* Now convert string addr to binary */ 533 hptr = kgetipnodebyname(addr_str, type, 534 AI_ALL, &error_num); 535 if (!hptr) { 536 /* Unable to get valid address */ 537 kmem_free(tmp_buf, tmp_buf_len); 538 return (ISCSI_STATUS_PROTOCOL_ERROR); 539 } 540 541 /* Check if space for response */ 542 if (targetname_added == B_FALSE) { 543 stl->stl_out_cnt++; 544 if (stl->stl_out_cnt >= stl->stl_in_cnt) { 545 /* 546 * continue processing the data so that 547 * the total number of targets are 548 * known and the caller can retry with 549 * the correct number of entries in 550 * the list 551 */ 552 kfreehostent(hptr); 553 kmem_free(tmp_buf, tmp_buf_len); 554 continue; 555 } 556 curr_ste = &(stl->stl_list[stl->stl_out_cnt]); 557 (void) strcpy((char *)curr_ste->ste_name, 558 (char *)prev_ste->ste_name); 559 } 560 561 curr_ste->ste_ipaddr.a_addr.i_insize = hptr->h_length; 562 bcopy(*hptr->h_addr_list, 563 &(curr_ste->ste_ipaddr.a_addr.i_addr), 564 curr_ste->ste_ipaddr.a_addr.i_insize); 565 kfreehostent(hptr); 566 567 if (port_str != NULL) { 568 (void) ddi_strtol(port_str, NULL, 0, &result); 569 curr_ste->ste_ipaddr.a_port = (short)result; 570 } else { 571 curr_ste->ste_ipaddr.a_port = ISCSI_LISTEN_PORT; 572 } 573 574 if (tpgt_str != NULL) { 575 (void) ddi_strtol(tpgt_str, NULL, 0, &result); 576 curr_ste->ste_tpgt = (short)result; 577 } else { 578 cmn_err(CE_NOTE, "SendTargets protocol error: " 579 "TPGT not specified"); 580 kmem_free(tmp_buf, tmp_buf_len); 581 return (ISCSI_STATUS_PROTOCOL_ERROR); 582 } 583 584 kmem_free(tmp_buf, tmp_buf_len); 585 586 targetname_added = B_FALSE; 587 588 } else if (strlen(line) != 0) { 589 /* 590 * Any other string besides an empty string 591 * is a protocol error 592 */ 593 cmn_err(CE_NOTE, "SendTargets protocol error: " 594 "unexpected response"); 595 return (ISCSI_STATUS_PROTOCOL_ERROR); 596 } 597 598 prev_ste = curr_ste; 599 } 600 601 /* 602 * If target found increment out count one more time because 603 * this is the total number of entries in the list not an index 604 * like it was used above 605 */ 606 if (prev_ste != NULL) { 607 stl->stl_out_cnt++; 608 } 609 610 return (ISCSI_STATUS_SUCCESS); 611 } 612 613 /* 614 * iscsi_set_param - This function is a helper to ISCSI_SET_PARAM 615 * IOCTL 616 */ 617 int 618 iscsi_set_param(iscsi_login_params_t *params, iscsi_param_set_t *ipsp) 619 { 620 int rtn = 0; 621 iscsi_param_get_t *ipgp; 622 623 /* 624 * Use get param to get the min, max and increment values for the 625 * given parameter so validation can be done on the new value. 626 */ 627 ipgp = (iscsi_param_get_t *)kmem_alloc(sizeof (*ipgp), KM_SLEEP); 628 ipgp->g_param = ipsp->s_param; 629 rtn = iscsi_get_param(params, B_TRUE, ipgp); 630 if (rtn != 0) { 631 kmem_free(ipgp, sizeof (*ipgp)); 632 return (rtn); 633 } 634 635 if (ipsp->s_param == ISCSI_LOGIN_PARAM_HEADER_DIGEST || 636 ipsp->s_param == ISCSI_LOGIN_PARAM_DATA_DIGEST || 637 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN || 638 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT || 639 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH || 640 ipsp->s_param == ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH || 641 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH) { 642 643 if (ipsp->s_value.v_integer < ipgp->g_value.v_integer.i_min || 644 ipsp->s_value.v_integer > ipgp->g_value.v_integer.i_max || 645 (ipsp->s_value.v_integer % 646 ipgp->g_value.v_integer.i_incr) != 0) { 647 rtn = EINVAL; 648 kmem_free(ipgp, sizeof (*ipgp)); 649 return (rtn); 650 } 651 652 } 653 kmem_free(ipgp, sizeof (*ipgp)); 654 655 656 switch (ipsp->s_param) { 657 658 /* 659 * Boolean parameters 660 */ 661 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER: 662 params->data_sequence_in_order = ipsp->s_value.v_bool; 663 break; 664 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA: 665 params->immediate_data = ipsp->s_value.v_bool; 666 break; 667 case ISCSI_LOGIN_PARAM_INITIAL_R2T: 668 params->initial_r2t = ipsp->s_value.v_bool; 669 break; 670 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER: 671 params->data_pdu_in_order = ipsp->s_value.v_bool; 672 break; 673 674 /* 675 * Integer parameters 676 */ 677 case ISCSI_LOGIN_PARAM_HEADER_DIGEST: 678 params->header_digest = ipsp->s_value.v_integer; 679 break; 680 case ISCSI_LOGIN_PARAM_DATA_DIGEST: 681 params->data_digest = ipsp->s_value.v_integer; 682 break; 683 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN: 684 params->default_time_to_retain = ipsp->s_value.v_integer; 685 break; 686 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT: 687 params->default_time_to_wait = ipsp->s_value.v_integer; 688 break; 689 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH: 690 params->max_recv_data_seg_len = ipsp->s_value.v_integer; 691 break; 692 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH: 693 if (ipsp->s_value.v_integer <= params->max_burst_length) { 694 params->first_burst_length = ipsp->s_value.v_integer; 695 } else { 696 rtn = EINVAL; 697 } 698 break; 699 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH: 700 if (ipsp->s_value.v_integer >= params->first_burst_length) { 701 params->max_burst_length = ipsp->s_value.v_integer; 702 } else { 703 rtn = EINVAL; 704 } 705 break; 706 707 /* 708 * Integer parameters which currently are unsettable 709 */ 710 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS: 711 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T: 712 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL: 713 rtn = ENOTSUP; 714 break; 715 716 default: 717 rtn = EINVAL; 718 break; 719 } 720 return (rtn); 721 } 722 723 int 724 iscsi_set_params(iscsi_param_set_t *ils, iscsi_hba_t *ihp, boolean_t persist) 725 { 726 iscsi_login_params_t *params = NULL; 727 uchar_t *name = NULL; 728 iscsi_sess_t *isp = NULL; 729 iscsi_param_get_t *ilg; 730 int rtn = 0; 731 732 /* handle special case for Initiator name */ 733 if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_NAME) { 734 (void) strlcpy((char *)ihp->hba_name, 735 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN); 736 if (persist) { 737 char *name; 738 boolean_t rval; 739 740 /* save off old Initiator name */ 741 name = kmem_alloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); 742 rval = persistent_initiator_name_get(name, 743 ISCSI_MAX_NAME_LEN); 744 745 (void) persistent_initiator_name_set( 746 (char *)ihp->hba_name); 747 if (rval == B_TRUE) { 748 /* 749 * check to see if we have login param, 750 * chap param, or authentication params 751 * loaded in persistent that we have to change 752 * the name of 753 */ 754 persistent_param_t *pp; 755 iscsi_chap_props_t *chap; 756 iscsi_auth_props_t *auth; 757 758 /* checking login params */ 759 pp = kmem_zalloc(sizeof (persistent_param_t), 760 KM_SLEEP); 761 if (persistent_param_get(name, pp)) { 762 rval = persistent_param_clear(name); 763 if (rval == B_TRUE) { 764 rval = persistent_param_set( 765 (char *)ihp->hba_name, pp); 766 } 767 if (rval == B_FALSE) { 768 rtn = EFAULT; 769 } 770 } 771 kmem_free(pp, sizeof (persistent_param_t)); 772 773 /* check chap params */ 774 chap = kmem_zalloc(sizeof (iscsi_chap_props_t), 775 KM_SLEEP); 776 if (persistent_chap_get(name, chap)) { 777 rval = persistent_chap_clear(name); 778 if (rval == B_TRUE) { 779 /* 780 * Update CHAP user name only if the 781 * original username was set to the 782 * initiator node name. Otherwise 783 * leave it the way it is. 784 */ 785 int userSize; 786 userSize = 787 sizeof (chap->c_user); 788 if (strncmp((char *) 789 chap->c_user, name, 790 sizeof (chap->c_user)) 791 == 0) { 792 bzero(chap->c_user, 793 userSize); 794 bcopy((char *) 795 ihp->hba_name, 796 chap->c_user, 797 strlen((char *) 798 ihp->hba_name)); 799 chap->c_user_len = 800 strlen((char *) 801 ihp->hba_name); 802 803 } 804 rval = persistent_chap_set( 805 (char *)ihp->hba_name, chap); 806 } 807 if (rval == B_FALSE) { 808 rtn = EFAULT; 809 } 810 } 811 kmem_free(chap, sizeof (iscsi_chap_props_t)); 812 813 /* check authentication params */ 814 auth = kmem_zalloc(sizeof (iscsi_auth_props_t), 815 KM_SLEEP); 816 if (persistent_auth_get(name, auth)) { 817 rval = persistent_auth_clear(name); 818 if (rval == B_TRUE) { 819 rval = persistent_auth_set( 820 (char *)ihp->hba_name, 821 auth); 822 } 823 if (rval == B_FALSE) { 824 rtn = EFAULT; 825 } 826 } 827 kmem_free(auth, sizeof (iscsi_auth_props_t)); 828 } 829 kmem_free(name, ISCSI_MAX_NAME_LEN); 830 } 831 } else if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_ALIAS) { 832 (void) strlcpy((char *)ihp->hba_alias, 833 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN); 834 ihp->hba_alias_length = 835 strlen((char *)ils->s_value.v_name); 836 if (persist) { 837 (void) persistent_alias_name_set( 838 (char *)ihp->hba_alias); 839 } 840 } else { 841 /* switch login based if looking for initiator params */ 842 if (ils->s_oid == ihp->hba_oid) { 843 /* initiator */ 844 params = &ihp->hba_params; 845 name = ihp->hba_name; 846 rtn = iscsi_set_param(params, ils); 847 } else { 848 /* session */ 849 name = iscsi_targetparam_get_name(ils->s_oid); 850 851 if (persist) { 852 boolean_t rval; 853 persistent_param_t *pp; 854 855 pp = (persistent_param_t *) 856 kmem_zalloc(sizeof (*pp), KM_SLEEP); 857 if (!persistent_param_get((char *)name, pp)) { 858 iscsi_set_default_login_params( 859 &pp->p_params); 860 } 861 862 pp->p_bitmap |= (1 << ils->s_param); 863 rtn = iscsi_set_param(&pp->p_params, ils); 864 if (rtn == 0) { 865 rval = persistent_param_set( 866 (char *)name, pp); 867 if (rval == B_FALSE) { 868 rtn = EFAULT; 869 } 870 } 871 kmem_free(pp, sizeof (*pp)); 872 } 873 874 /* 875 * Here may have multiple sessions with different 876 * tpgt values. So it is needed to loop through 877 * the sessions and update all sessions. 878 */ 879 if (rtn == 0) { 880 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 881 for (isp = ihp->hba_sess_list; isp; 882 isp = isp->sess_next) { 883 if (iscsiboot_prop && 884 isp->sess_boot && 885 iscsi_chk_bootlun_mpxio(ihp)) { 886 /* 887 * MPxIO is enabled so capable 888 * of changing. All changes 889 * will be applied later, 890 * after this function 891 */ 892 continue; 893 } 894 895 if (strncmp((char *)isp->sess_name, 896 (char *)name, 897 ISCSI_MAX_NAME_LEN) == 0) { 898 mutex_enter(&isp->sess_state_mutex); 899 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7); 900 mutex_exit(&isp->sess_state_mutex); 901 } 902 } 903 rw_exit(&ihp->hba_sess_list_rwlock); 904 } 905 906 } /* end of 'else' */ 907 908 if (params && persist && (rtn == 0)) { 909 boolean_t rval; 910 persistent_param_t *pp; 911 912 pp = (persistent_param_t *) 913 kmem_zalloc(sizeof (*pp), KM_SLEEP); 914 (void) persistent_param_get((char *)name, pp); 915 pp->p_bitmap |= (1 << ils->s_param); 916 bcopy(params, &pp->p_params, sizeof (*params)); 917 rval = persistent_param_set((char *)name, pp); 918 if (rval == B_FALSE) { 919 rtn = EFAULT; 920 } 921 kmem_free(pp, sizeof (*pp)); 922 } 923 /* 924 * if initiator parameter set, modify all associated 925 * sessions that don't already have the parameter 926 * overriden 927 */ 928 if (ils->s_oid == ihp->hba_oid) { 929 ilg = (iscsi_param_get_t *) 930 kmem_alloc(sizeof (*ilg), KM_SLEEP); 931 932 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 933 for (isp = ihp->hba_sess_list; isp; 934 isp = isp->sess_next) { 935 ilg->g_param = ils->s_param; 936 params = &isp->sess_params; 937 if (iscsi_get_persisted_param( 938 isp->sess_name, ilg, params) != 0) { 939 rtn = iscsi_set_param(params, ils); 940 if (rtn != 0) { 941 break; 942 } 943 if (iscsiboot_prop && 944 isp->sess_boot && 945 iscsi_chk_bootlun_mpxio(ihp)) { 946 /* 947 * MPxIO is enabled so capable 948 * of changing. Changes will 949 * be applied later, right 950 * after this function 951 */ 952 continue; 953 } 954 955 /* 956 * Notify the session that 957 * the login parameters have 958 * changed. 959 */ 960 mutex_enter(&isp-> 961 sess_state_mutex); 962 iscsi_sess_state_machine(isp, 963 ISCSI_SESS_EVENT_N7); 964 mutex_exit(&isp-> 965 sess_state_mutex); 966 } 967 } 968 kmem_free(ilg, sizeof (*ilg)); 969 rw_exit(&ihp->hba_sess_list_rwlock); 970 } 971 } 972 return (rtn); 973 } 974 975 int 976 iscsi_target_prop_mod(iscsi_hba_t *ihp, iscsi_property_t *ipp, int cmd) 977 { 978 iscsi_sess_t *isp = NULL; 979 iscsi_conn_t *icp; 980 int rtn; 981 char *name; 982 983 /* 984 * If we're just attempting to get the target properties don't 985 * create the session if it doesn't already exist. If we setting 986 * the property then create the session if needed because we'll 987 * most likely see an ISCSI_LOGIN in a few. 988 */ 989 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 990 991 /* 992 * If the oid does represent a session check to see 993 * if it is a target oid. If so, return the target's 994 * associated session. 995 */ 996 rtn = iscsi_sess_get(ipp->p_oid, ihp, &isp); 997 if (rtn != 0) { 998 rtn = iscsi_sess_get_by_target(ipp->p_oid, ihp, &isp); 999 } 1000 1001 /* 1002 * If rtn is zero then we have found an existing session. 1003 * Use the session name for database lookup. If rtn is 1004 * non-zero then create a targetparam object and use 1005 * its name for database lookup. 1006 */ 1007 if (rtn == 0) { 1008 name = (char *)isp->sess_name; 1009 } else { 1010 name = (char *)iscsi_targetparam_get_name(ipp->p_oid); 1011 isp = NULL; 1012 } 1013 1014 if (name == NULL) { 1015 rw_exit(&ihp->hba_sess_list_rwlock); 1016 rtn = EFAULT; 1017 return (rtn); 1018 } 1019 1020 rtn = 0; 1021 if (cmd == ISCSI_TARGET_PROPS_GET) { 1022 /* 1023 * If isp is not null get the session's parameters, otherwise 1024 * the get is for a target-param object so defaults need to 1025 * be returned. 1026 */ 1027 if (isp != NULL) { 1028 int conn_count = 0; 1029 1030 bcopy(isp->sess_alias, ipp->p_alias, 1031 isp->sess_alias_length); 1032 bcopy(isp->sess_name, ipp->p_name, 1033 isp->sess_name_length); 1034 ipp->p_alias_len = isp->sess_alias_length; 1035 ipp->p_name_len = isp->sess_name_length; 1036 ipp->p_discovery = isp->sess_discovered_by; 1037 ipp->p_last_err = isp->sess_last_err; 1038 ipp->p_tpgt_conf = isp->sess_tpgt_conf; 1039 ipp->p_tpgt_nego = isp->sess_tpgt_nego; 1040 bcopy(isp->sess_isid, ipp->p_isid, ISCSI_ISID_LEN); 1041 1042 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 1043 for (icp = isp->sess_conn_list; icp; 1044 icp = icp->conn_next) { 1045 if (icp->conn_state == 1046 ISCSI_CONN_STATE_LOGGED_IN) { 1047 conn_count++; 1048 } 1049 } 1050 rw_exit(&isp->sess_conn_list_rwlock); 1051 ipp->p_num_of_connections = conn_count; 1052 ipp->p_connected = (conn_count > 0) ? B_TRUE : B_FALSE; 1053 } else { 1054 bcopy(name, ipp->p_name, strlen(name)); 1055 ipp->p_name_len = strlen(name); 1056 bcopy("", ipp->p_alias, strlen("")); 1057 ipp->p_alias_len = strlen(""); 1058 ipp->p_discovery = iSCSIDiscoveryMethodUnknown; 1059 ipp->p_last_err = NoError; 1060 ipp->p_tpgt_conf = ISCSI_DEFAULT_TPGT; 1061 ipp->p_tpgt_nego = ISCSI_DEFAULT_TPGT; 1062 ipp->p_num_of_connections = 0; 1063 ipp->p_connected = B_FALSE; 1064 } 1065 } else { 1066 if (isp == NULL) { 1067 rw_exit(&ihp->hba_sess_list_rwlock); 1068 rtn = EFAULT; 1069 return (rtn); 1070 } 1071 1072 /* ISCSI_TARGET_PROPS_SET */ 1073 /* 1074 * only update if new, otherwise could clear out alias 1075 * if just updating the discovery. 1076 */ 1077 if (ipp->p_alias_len != 0) { 1078 bcopy(ipp->p_alias, isp->sess_alias, 1079 ipp->p_alias_len); 1080 isp->sess_alias_length = ipp->p_alias_len; 1081 } 1082 isp->sess_discovered_by = ipp->p_discovery; 1083 } 1084 rw_exit(&ihp->hba_sess_list_rwlock); 1085 return (rtn); 1086 } 1087 1088 /* 1089 * iscsi_ioctl_get_config_sess - gets configured session information 1090 * 1091 * This function is an ioctl helper function to get the 1092 * configured session information from the persistent store. 1093 */ 1094 int 1095 iscsi_ioctl_get_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1096 { 1097 uchar_t *name; 1098 1099 /* Get the matching iscsi node name for the oid */ 1100 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1101 /* initiator name */ 1102 name = ihp->hba_name; 1103 } else { 1104 /* target name */ 1105 name = iscsi_targetparam_get_name(ics->ics_oid); 1106 if (name == NULL) { 1107 /* invalid node name */ 1108 return (EINVAL); 1109 } 1110 } 1111 1112 /* get configured session information */ 1113 if (persistent_get_config_session((char *)name, ics) == B_FALSE) { 1114 /* 1115 * There might not be anything in the database yet. If 1116 * this is a request for the target check the initiator 1117 * value. If neither is set return the default value. 1118 */ 1119 if (ics->ics_oid != ISCSI_INITIATOR_OID) { 1120 if (persistent_get_config_session( 1121 (char *)ihp->hba_name, ics) == B_FALSE) { 1122 /* 1123 * No initiator value is set. 1124 * Return the defaults. 1125 */ 1126 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1127 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1128 } 1129 } else { 1130 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1131 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1132 } 1133 } 1134 1135 return (0); 1136 } 1137 1138 /* 1139 * iscsi_ioctl_set_config_sess - sets configured session information 1140 * 1141 * This function is an ioctl helper function to set the 1142 * configured session information in the persistent store. 1143 * In addition it will notify any active sessions of the 1144 * changed so this can update binding information. It 1145 * will also destroy sessions that were removed and add 1146 * new sessions. 1147 */ 1148 int 1149 iscsi_ioctl_set_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1150 { 1151 uchar_t *name; 1152 iscsi_sess_t *isp; 1153 1154 /* check range infomration */ 1155 if ((ics->ics_in < ISCSI_MIN_CONFIG_SESSIONS) || 1156 (ics->ics_in > ISCSI_MAX_CONFIG_SESSIONS)) { 1157 /* invalid range information */ 1158 return (EINVAL); 1159 } 1160 1161 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1162 name = ihp->hba_name; 1163 } else { 1164 /* get target name */ 1165 name = iscsi_targetparam_get_name(ics->ics_oid); 1166 if (name == NULL) { 1167 /* invalid node name */ 1168 return (EINVAL); 1169 } 1170 } 1171 1172 /* store the new information */ 1173 if (persistent_set_config_session((char *)name, ics) == B_FALSE) { 1174 /* failed to store new information */ 1175 return (EINVAL); 1176 } 1177 1178 /* notify existing sessions of change */ 1179 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 1180 isp = ihp->hba_sess_list; 1181 while (isp != NULL) { 1182 1183 if ((ics->ics_oid == ISCSI_INITIATOR_OID) || 1184 (strncmp((char *)isp->sess_name, (char *)name, 1185 ISCSI_MAX_NAME_LEN) == 0)) { 1186 1187 /* 1188 * If this sessions least signficant byte 1189 * of the isid is less than or equal to 1190 * the the number of configured sessions 1191 * then we need to tear down this session. 1192 */ 1193 if (ics->ics_in <= isp->sess_isid[5]) { 1194 /* First attempt to destory the session */ 1195 if (ISCSI_SUCCESS(iscsi_sess_destroy(isp))) { 1196 isp = ihp->hba_sess_list; 1197 } else { 1198 /* 1199 * If we can't destroy it then 1200 * atleast poke it to disconnect 1201 * it. 1202 */ 1203 mutex_enter(&isp->sess_state_mutex); 1204 iscsi_sess_state_machine(isp, 1205 ISCSI_SESS_EVENT_N7); 1206 mutex_exit(&isp->sess_state_mutex); 1207 isp = isp->sess_next; 1208 } 1209 } else { 1210 isp = isp->sess_next; 1211 } 1212 } else { 1213 isp = isp->sess_next; 1214 } 1215 } 1216 rw_exit(&ihp->hba_sess_list_rwlock); 1217 1218 /* 1219 * The number of targets has changed. Since we don't expect 1220 * this to be a common operation lets keep the code simple and 1221 * just use a slightly larger hammer and poke discovery. This 1222 * force the reevaulation of this target and all other targets. 1223 */ 1224 iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); 1225 /* lock so only one config operation occrs */ 1226 sema_p(&iscsid_config_semaphore); 1227 iscsid_config_all(ihp, B_FALSE); 1228 sema_v(&iscsid_config_semaphore); 1229 1230 return (0); 1231 } 1232