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