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