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 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 852 if (persist) { 853 boolean_t rval; 854 persistent_param_t *pp; 855 856 pp = (persistent_param_t *) 857 kmem_zalloc(sizeof (*pp), KM_SLEEP); 858 if (!persistent_param_get((char *)name, pp)) { 859 iscsi_set_default_login_params( 860 &pp->p_params); 861 } 862 863 pp->p_bitmap |= (1 << ils->s_param); 864 rtn = iscsi_set_param(&pp->p_params, ils); 865 if (rtn == 0) { 866 rval = persistent_param_set( 867 (char *)name, pp); 868 if (rval == B_FALSE) { 869 rtn = EFAULT; 870 } 871 } 872 kmem_free(pp, sizeof (*pp)); 873 } 874 875 /* 876 * Here may have multiple sessions with different 877 * tpgt values. So it is needed to loop through 878 * the sessions and update all sessions. 879 */ 880 if (rtn == 0) { 881 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 882 for (isp = ihp->hba_sess_list; isp; 883 isp = isp->sess_next) { 884 if (iscsiboot_prop && 885 isp->sess_boot && 886 iscsi_chk_bootlun_mpxio(ihp)) { 887 /* 888 * MPxIO is enabled so capable 889 * of changing. All changes 890 * will be applied later, 891 * after this function 892 */ 893 continue; 894 } 895 896 if (strncmp((char *)isp->sess_name, 897 (char *)name, 898 ISCSI_MAX_NAME_LEN) == 0) { 899 mutex_enter(&isp->sess_state_mutex); 900 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7); 901 mutex_exit(&isp->sess_state_mutex); 902 } 903 } 904 rw_exit(&ihp->hba_sess_list_rwlock); 905 } 906 907 } /* end of 'else' */ 908 909 if (params && persist && (rtn == 0)) { 910 boolean_t rval; 911 persistent_param_t *pp; 912 913 pp = (persistent_param_t *) 914 kmem_zalloc(sizeof (*pp), KM_SLEEP); 915 (void) persistent_param_get((char *)name, pp); 916 pp->p_bitmap |= (1 << ils->s_param); 917 bcopy(params, &pp->p_params, sizeof (*params)); 918 rval = persistent_param_set((char *)name, pp); 919 if (rval == B_FALSE) { 920 rtn = EFAULT; 921 } 922 kmem_free(pp, sizeof (*pp)); 923 } 924 /* 925 * if initiator parameter set, modify all associated 926 * sessions that don't already have the parameter 927 * overriden 928 */ 929 if (ils->s_oid == ihp->hba_oid) { 930 ilg = (iscsi_param_get_t *) 931 kmem_alloc(sizeof (*ilg), KM_SLEEP); 932 933 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 934 for (isp = ihp->hba_sess_list; isp; 935 isp = isp->sess_next) { 936 ilg->g_param = ils->s_param; 937 params = &isp->sess_params; 938 if (iscsi_get_persisted_param( 939 isp->sess_name, ilg, params) != 0) { 940 rtn = iscsi_set_param(params, ils); 941 if (rtn != 0) { 942 break; 943 } 944 if (iscsiboot_prop && 945 isp->sess_boot && 946 iscsi_chk_bootlun_mpxio(ihp)) { 947 /* 948 * MPxIO is enabled so capable 949 * of changing. Changes will 950 * be applied later, right 951 * after this function 952 */ 953 continue; 954 } 955 956 /* 957 * Notify the session that 958 * the login parameters have 959 * changed. 960 */ 961 mutex_enter(&isp-> 962 sess_state_mutex); 963 iscsi_sess_state_machine(isp, 964 ISCSI_SESS_EVENT_N7); 965 mutex_exit(&isp-> 966 sess_state_mutex); 967 } 968 } 969 kmem_free(ilg, sizeof (*ilg)); 970 rw_exit(&ihp->hba_sess_list_rwlock); 971 } 972 } 973 return (rtn); 974 } 975 976 int 977 iscsi_target_prop_mod(iscsi_hba_t *ihp, iscsi_property_t *ipp, int cmd) 978 { 979 iscsi_sess_t *isp = NULL; 980 iscsi_conn_t *icp; 981 int rtn; 982 char *name; 983 984 /* 985 * If we're just attempting to get the target properties don't 986 * create the session if it doesn't already exist. If we setting 987 * the property then create the session if needed because we'll 988 * most likely see an ISCSI_LOGIN in a few. 989 */ 990 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 991 992 /* 993 * If the oid does represent a session check to see 994 * if it is a target oid. If so, return the target's 995 * associated session. 996 */ 997 rtn = iscsi_sess_get(ipp->p_oid, ihp, &isp); 998 if (rtn != 0) { 999 rtn = iscsi_sess_get_by_target(ipp->p_oid, ihp, &isp); 1000 } 1001 1002 /* 1003 * If rtn is zero then we have found an existing session. 1004 * Use the session name for database lookup. If rtn is 1005 * non-zero then create a targetparam object and use 1006 * its name for database lookup. 1007 */ 1008 if (rtn == 0) { 1009 name = (char *)isp->sess_name; 1010 } else { 1011 name = (char *)iscsi_targetparam_get_name(ipp->p_oid); 1012 isp = NULL; 1013 } 1014 1015 if (name == NULL) { 1016 rw_exit(&ihp->hba_sess_list_rwlock); 1017 rtn = EFAULT; 1018 return (rtn); 1019 } 1020 1021 rtn = 0; 1022 if (cmd == ISCSI_TARGET_PROPS_GET) { 1023 /* 1024 * If isp is not null get the session's parameters, otherwise 1025 * the get is for a target-param object so defaults need to 1026 * be returned. 1027 */ 1028 if (isp != NULL) { 1029 int conn_count = 0; 1030 1031 bcopy(isp->sess_alias, ipp->p_alias, 1032 isp->sess_alias_length); 1033 bcopy(isp->sess_name, ipp->p_name, 1034 isp->sess_name_length); 1035 ipp->p_alias_len = isp->sess_alias_length; 1036 ipp->p_name_len = isp->sess_name_length; 1037 ipp->p_discovery = isp->sess_discovered_by; 1038 ipp->p_last_err = isp->sess_last_err; 1039 ipp->p_tpgt_conf = isp->sess_tpgt_conf; 1040 ipp->p_tpgt_nego = isp->sess_tpgt_nego; 1041 bcopy(isp->sess_isid, ipp->p_isid, ISCSI_ISID_LEN); 1042 1043 rw_enter(&isp->sess_conn_list_rwlock, RW_READER); 1044 for (icp = isp->sess_conn_list; icp; 1045 icp = icp->conn_next) { 1046 if (icp->conn_state == 1047 ISCSI_CONN_STATE_LOGGED_IN) { 1048 conn_count++; 1049 } 1050 } 1051 rw_exit(&isp->sess_conn_list_rwlock); 1052 ipp->p_num_of_connections = conn_count; 1053 ipp->p_connected = (conn_count > 0) ? B_TRUE : B_FALSE; 1054 } else { 1055 bcopy(name, ipp->p_name, strlen(name)); 1056 ipp->p_name_len = strlen(name); 1057 bcopy("", ipp->p_alias, strlen("")); 1058 ipp->p_alias_len = strlen(""); 1059 ipp->p_discovery = iSCSIDiscoveryMethodUnknown; 1060 ipp->p_last_err = NoError; 1061 ipp->p_tpgt_conf = ISCSI_DEFAULT_TPGT; 1062 ipp->p_tpgt_nego = ISCSI_DEFAULT_TPGT; 1063 ipp->p_num_of_connections = 0; 1064 ipp->p_connected = B_FALSE; 1065 } 1066 } else { 1067 if (isp == NULL) { 1068 rw_exit(&ihp->hba_sess_list_rwlock); 1069 rtn = EFAULT; 1070 return (rtn); 1071 } 1072 1073 /* ISCSI_TARGET_PROPS_SET */ 1074 /* 1075 * only update if new, otherwise could clear out alias 1076 * if just updating the discovery. 1077 */ 1078 if (ipp->p_alias_len != 0) { 1079 bcopy(ipp->p_alias, isp->sess_alias, 1080 ipp->p_alias_len); 1081 isp->sess_alias_length = ipp->p_alias_len; 1082 } 1083 isp->sess_discovered_by = ipp->p_discovery; 1084 } 1085 rw_exit(&ihp->hba_sess_list_rwlock); 1086 return (rtn); 1087 } 1088 1089 /* 1090 * iscsi_ioctl_get_config_sess - gets configured session information 1091 * 1092 * This function is an ioctl helper function to get the 1093 * configured session information from the persistent store. 1094 */ 1095 int 1096 iscsi_ioctl_get_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1097 { 1098 uchar_t *name; 1099 1100 /* Get the matching iscsi node name for the oid */ 1101 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1102 /* initiator name */ 1103 name = ihp->hba_name; 1104 } else { 1105 /* target name */ 1106 name = iscsi_targetparam_get_name(ics->ics_oid); 1107 if (name == NULL) { 1108 /* invalid node name */ 1109 return (EINVAL); 1110 } 1111 } 1112 1113 /* get configured session information */ 1114 if (persistent_get_config_session((char *)name, ics) == B_FALSE) { 1115 /* 1116 * There might not be anything in the database yet. If 1117 * this is a request for the target check the initiator 1118 * value. If neither is set return the default value. 1119 */ 1120 if (ics->ics_oid != ISCSI_INITIATOR_OID) { 1121 if (persistent_get_config_session( 1122 (char *)ihp->hba_name, ics) == B_FALSE) { 1123 /* 1124 * No initiator value is set. 1125 * Return the defaults. 1126 */ 1127 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1128 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1129 } 1130 } else { 1131 ics->ics_out = ISCSI_DEFAULT_SESS_NUM; 1132 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND; 1133 } 1134 } 1135 1136 return (0); 1137 } 1138 1139 /* 1140 * iscsi_ioctl_set_config_sess - sets configured session information 1141 * 1142 * This function is an ioctl helper function to set the 1143 * configured session information in the persistent store. 1144 * In addition it will notify any active sessions of the 1145 * changed so this can update binding information. It 1146 * will also destroy sessions that were removed and add 1147 * new sessions. 1148 */ 1149 int 1150 iscsi_ioctl_set_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics) 1151 { 1152 uchar_t *name; 1153 iscsi_sess_t *isp; 1154 1155 /* check range infomration */ 1156 if ((ics->ics_in < ISCSI_MIN_CONFIG_SESSIONS) || 1157 (ics->ics_in > ISCSI_MAX_CONFIG_SESSIONS)) { 1158 /* invalid range information */ 1159 return (EINVAL); 1160 } 1161 1162 if (ics->ics_oid == ISCSI_INITIATOR_OID) { 1163 name = ihp->hba_name; 1164 } else { 1165 /* get target name */ 1166 name = iscsi_targetparam_get_name(ics->ics_oid); 1167 if (name == NULL) { 1168 /* invalid node name */ 1169 return (EINVAL); 1170 } 1171 } 1172 1173 /* store the new information */ 1174 if (persistent_set_config_session((char *)name, ics) == B_FALSE) { 1175 /* failed to store new information */ 1176 return (EINVAL); 1177 } 1178 1179 /* notify existing sessions of change */ 1180 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); 1181 isp = ihp->hba_sess_list; 1182 while (isp != NULL) { 1183 1184 if ((ics->ics_oid == ISCSI_INITIATOR_OID) || 1185 (strncmp((char *)isp->sess_name, (char *)name, 1186 ISCSI_MAX_NAME_LEN) == 0)) { 1187 1188 /* 1189 * If this sessions least signficant byte 1190 * of the isid is less than or equal to 1191 * the the number of configured sessions 1192 * then we need to tear down this session. 1193 */ 1194 if (ics->ics_in <= isp->sess_isid[5]) { 1195 /* First attempt to destory the session */ 1196 if (ISCSI_SUCCESS(iscsi_sess_destroy(isp))) { 1197 isp = ihp->hba_sess_list; 1198 } else { 1199 /* 1200 * If we can't destroy it then 1201 * atleast poke it to disconnect 1202 * it. 1203 */ 1204 mutex_enter(&isp->sess_state_mutex); 1205 iscsi_sess_state_machine(isp, 1206 ISCSI_SESS_EVENT_N7); 1207 mutex_exit(&isp->sess_state_mutex); 1208 isp = isp->sess_next; 1209 } 1210 } else { 1211 isp = isp->sess_next; 1212 } 1213 } else { 1214 isp = isp->sess_next; 1215 } 1216 } 1217 rw_exit(&ihp->hba_sess_list_rwlock); 1218 1219 /* 1220 * The number of targets has changed. Since we don't expect 1221 * this to be a common operation lets keep the code simple and 1222 * just use a slightly larger hammer and poke discovery. This 1223 * force the reevaulation of this target and all other targets. 1224 */ 1225 iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); 1226 /* lock so only one config operation occrs */ 1227 sema_p(&iscsid_config_semaphore); 1228 iscsid_config_all(ihp, B_FALSE); 1229 sema_v(&iscsid_config_semaphore); 1230 1231 return (0); 1232 } 1233