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