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