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