iscsi_ioctl.c revision 8348:4137e18bfaf0
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
42extern ib_boot_prop_t	*iscsiboot_prop;
43
44static 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 */
50void *
51iscsi_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 */
70int
71iscsi_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 */
86iscsi_conn_list_t *
87iscsi_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 */
118int
119iscsi_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 */
144boolean_t
145iscsi_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 */
234boolean_t
235iscsi_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 */
302int
303iscsi_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;
353retry_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 */
405static iscsi_status_t
406iscsi_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 */
618int
619iscsi_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
724int
725iscsi_set_params(iscsi_param_set_t *ils, iscsi_hba_t *ihp, boolean_t persist)
726{
727	iscsi_login_params_t	*params	= NULL;
728	uchar_t			*name	= NULL;
729	iscsi_sess_t		*isp	= NULL;
730	iscsi_param_get_t	*ilg;
731	int			rtn	= 0;
732
733	/* handle special case for Initiator name */
734	if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_NAME) {
735		(void) strlcpy((char *)ihp->hba_name,
736		    (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN);
737		if (persist) {
738			char			*name;
739			boolean_t		rval;
740
741			/* save off old Initiator name */
742			name = kmem_alloc(ISCSI_MAX_NAME_LEN, KM_SLEEP);
743			rval = persistent_initiator_name_get(name,
744			    ISCSI_MAX_NAME_LEN);
745
746			(void) persistent_initiator_name_set(
747			    (char *)ihp->hba_name);
748			if (rval == B_TRUE) {
749				/*
750				 * check to see if we have login param,
751				 * chap param, or authentication params
752				 * loaded in persistent that we have to change
753				 * the name of
754				 */
755				persistent_param_t	*pp;
756				iscsi_chap_props_t	*chap;
757				iscsi_auth_props_t	*auth;
758
759				/* checking login params */
760				pp = kmem_zalloc(sizeof (persistent_param_t),
761				    KM_SLEEP);
762				if (persistent_param_get(name, pp)) {
763					rval = persistent_param_clear(name);
764					if (rval == B_TRUE) {
765						rval = persistent_param_set(
766						    (char *)ihp->hba_name, pp);
767					}
768					if (rval == B_FALSE) {
769						rtn = EFAULT;
770					}
771				}
772				kmem_free(pp, sizeof (persistent_param_t));
773
774				/* check chap params */
775				chap = kmem_zalloc(sizeof (iscsi_chap_props_t),
776				    KM_SLEEP);
777				if (persistent_chap_get(name, chap)) {
778					rval = persistent_chap_clear(name);
779					if (rval == B_TRUE) {
780					/*
781					 * Update CHAP user name only if the
782					 * original username was set to the
783					 * initiator node name.  Otherwise
784					 * leave it the way it is.
785					 */
786						int userSize;
787						userSize =
788						    sizeof (chap->c_user);
789						if (strncmp((char *)
790						    chap->c_user, name,
791						    sizeof (chap->c_user))
792						    == 0) {
793							bzero(chap->c_user,
794							    userSize);
795							bcopy((char *)
796							    ihp->hba_name,
797							    chap->c_user,
798							    strlen((char *)
799							    ihp->hba_name));
800							chap->c_user_len =
801							    strlen((char *)
802							    ihp->hba_name);
803
804					}
805					rval = persistent_chap_set(
806					    (char *)ihp->hba_name, chap);
807					}
808					if (rval == B_FALSE) {
809						rtn = EFAULT;
810					}
811				}
812				kmem_free(chap, sizeof (iscsi_chap_props_t));
813
814				/* check authentication params */
815				auth = kmem_zalloc(sizeof (iscsi_auth_props_t),
816				    KM_SLEEP);
817				if (persistent_auth_get(name, auth)) {
818					rval = persistent_auth_clear(name);
819					if (rval == B_TRUE) {
820						rval = persistent_auth_set(
821						    (char *)ihp->hba_name,
822						    auth);
823					}
824					if (rval == B_FALSE) {
825						rtn = EFAULT;
826					}
827				}
828				kmem_free(auth, sizeof (iscsi_auth_props_t));
829			}
830			kmem_free(name, ISCSI_MAX_NAME_LEN);
831		}
832	} else if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_ALIAS) {
833		(void) strlcpy((char *)ihp->hba_alias,
834		    (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN);
835		ihp->hba_alias_length =
836		    strlen((char *)ils->s_value.v_name);
837		if (persist) {
838			(void) persistent_alias_name_set(
839			    (char *)ihp->hba_alias);
840		}
841	} else {
842		/* switch login based if looking for initiator params */
843		if (ils->s_oid == ihp->hba_oid) {
844			/* initiator */
845			params = &ihp->hba_params;
846			name = ihp->hba_name;
847			rtn = iscsi_set_param(params, ils);
848		} else {
849			/* session */
850			name = iscsi_targetparam_get_name(ils->s_oid);
851
852			if (persist) {
853				boolean_t		rval;
854				persistent_param_t	*pp;
855
856				pp = (persistent_param_t *)
857				    kmem_zalloc(sizeof (*pp), KM_SLEEP);
858				if (!persistent_param_get((char *)name, pp)) {
859					iscsi_set_default_login_params(
860					    &pp->p_params);
861				}
862
863				pp->p_bitmap |= (1 << ils->s_param);
864				rtn = iscsi_set_param(&pp->p_params, ils);
865				if (rtn == 0) {
866					rval = persistent_param_set(
867					    (char *)name, pp);
868					if (rval == B_FALSE) {
869						rtn = EFAULT;
870					}
871				}
872				kmem_free(pp, sizeof (*pp));
873			}
874
875			/*
876			 * Here may have multiple sessions with different
877			 * tpgt values.  So it is needed to loop through
878			 * the sessions and update all sessions.
879			 */
880			if (rtn == 0) {
881				rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
882				for (isp = ihp->hba_sess_list; isp;
883				    isp = isp->sess_next) {
884					if (iscsiboot_prop &&
885					    isp->sess_boot &&
886					    iscsi_chk_bootlun_mpxio(ihp)) {
887						/*
888						 * MPxIO is enabled so capable
889						 * of changing. All changes
890						 * will be applied later,
891						 * after this function
892						 */
893						continue;
894					}
895
896					if (strncmp((char *)isp->sess_name,
897					    (char *)name,
898					    ISCSI_MAX_NAME_LEN) == 0) {
899mutex_enter(&isp->sess_state_mutex);
900iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7);
901mutex_exit(&isp->sess_state_mutex);
902					}
903				}
904				rw_exit(&ihp->hba_sess_list_rwlock);
905			}
906
907		} /* end of 'else' */
908
909		if (params && persist && (rtn == 0)) {
910			boolean_t		rval;
911			persistent_param_t	*pp;
912
913			pp = (persistent_param_t *)
914			    kmem_zalloc(sizeof (*pp), KM_SLEEP);
915			(void) persistent_param_get((char *)name, pp);
916			pp->p_bitmap |= (1 << ils->s_param);
917			bcopy(params, &pp->p_params, sizeof (*params));
918			rval = persistent_param_set((char *)name, pp);
919			if (rval == B_FALSE) {
920				rtn = EFAULT;
921			}
922			kmem_free(pp, sizeof (*pp));
923		}
924		/*
925		 * if initiator parameter set, modify all associated
926		 * sessions that don't already have the parameter
927		 * overriden
928		 */
929		if (ils->s_oid == ihp->hba_oid) {
930			ilg = (iscsi_param_get_t *)
931			    kmem_alloc(sizeof (*ilg), KM_SLEEP);
932
933			rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
934			for (isp = ihp->hba_sess_list; isp;
935			    isp = isp->sess_next) {
936				ilg->g_param = ils->s_param;
937				params = &isp->sess_params;
938				if (iscsi_get_persisted_param(
939				    isp->sess_name, ilg, params) != 0) {
940					rtn = iscsi_set_param(params, ils);
941					if (rtn != 0) {
942						break;
943					}
944					if (iscsiboot_prop &&
945					    isp->sess_boot &&
946					    iscsi_chk_bootlun_mpxio(ihp)) {
947						/*
948						 * MPxIO is enabled so capable
949						 * of changing. Changes will
950						 * be applied later, right
951						 * after this function
952						 */
953						continue;
954					}
955
956					/*
957					 * Notify the session that
958					 * the login parameters have
959					 * changed.
960					 */
961					mutex_enter(&isp->
962					    sess_state_mutex);
963					iscsi_sess_state_machine(isp,
964					    ISCSI_SESS_EVENT_N7);
965					mutex_exit(&isp->
966					    sess_state_mutex);
967				}
968			}
969			kmem_free(ilg, sizeof (*ilg));
970			rw_exit(&ihp->hba_sess_list_rwlock);
971		}
972	}
973	return (rtn);
974}
975
976int
977iscsi_target_prop_mod(iscsi_hba_t *ihp, iscsi_property_t *ipp, int cmd)
978{
979	iscsi_sess_t *isp = NULL;
980	iscsi_conn_t *icp;
981	int rtn;
982	char *name;
983
984	/*
985	 * If we're just attempting to get the target properties don't
986	 * create the session if it doesn't already exist. If we setting
987	 * the property then create the session if needed because we'll
988	 * most likely see an ISCSI_LOGIN in a few.
989	 */
990	rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
991
992	/*
993	 * If the oid does represent a session check to see
994	 * if it is a target oid.  If so, return the target's
995	 * associated session.
996	 */
997	rtn = iscsi_sess_get(ipp->p_oid, ihp, &isp);
998	if (rtn != 0) {
999		rtn = iscsi_sess_get_by_target(ipp->p_oid, ihp, &isp);
1000	}
1001
1002	/*
1003	 * If rtn is zero then we have found an existing session.
1004	 * Use the session name for database lookup.  If rtn is
1005	 * non-zero then create a targetparam object and use
1006	 * its name for database lookup.
1007	 */
1008	if (rtn == 0) {
1009		name = (char *)isp->sess_name;
1010	} else {
1011		name = (char *)iscsi_targetparam_get_name(ipp->p_oid);
1012		isp = NULL;
1013	}
1014
1015	if (name == NULL) {
1016		rw_exit(&ihp->hba_sess_list_rwlock);
1017		rtn = EFAULT;
1018		return (rtn);
1019	}
1020
1021	rtn = 0;
1022	if (cmd == ISCSI_TARGET_PROPS_GET) {
1023		/*
1024		 * If isp is not null get the session's parameters, otherwise
1025		 * the get is for a target-param object so defaults need to
1026		 * be returned.
1027		 */
1028		if (isp != NULL) {
1029			int conn_count = 0;
1030
1031			bcopy(isp->sess_alias, ipp->p_alias,
1032			    isp->sess_alias_length);
1033			bcopy(isp->sess_name, ipp->p_name,
1034			    isp->sess_name_length);
1035			ipp->p_alias_len = isp->sess_alias_length;
1036			ipp->p_name_len  = isp->sess_name_length;
1037			ipp->p_discovery = isp->sess_discovered_by;
1038			ipp->p_last_err  = isp->sess_last_err;
1039			ipp->p_tpgt_conf = isp->sess_tpgt_conf;
1040			ipp->p_tpgt_nego = isp->sess_tpgt_nego;
1041			bcopy(isp->sess_isid, ipp->p_isid, ISCSI_ISID_LEN);
1042
1043			rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
1044			for (icp = isp->sess_conn_list; icp;
1045			    icp = icp->conn_next) {
1046				if (icp->conn_state ==
1047				    ISCSI_CONN_STATE_LOGGED_IN) {
1048					conn_count++;
1049				}
1050			}
1051			rw_exit(&isp->sess_conn_list_rwlock);
1052			ipp->p_num_of_connections = conn_count;
1053			ipp->p_connected = (conn_count > 0) ? B_TRUE : B_FALSE;
1054		} else {
1055			bcopy(name, ipp->p_name, strlen(name));
1056			ipp->p_name_len  = strlen(name);
1057			bcopy("", ipp->p_alias, strlen(""));
1058			ipp->p_alias_len = strlen("");
1059			ipp->p_discovery = iSCSIDiscoveryMethodUnknown;
1060			ipp->p_last_err  =  NoError;
1061			ipp->p_tpgt_conf = ISCSI_DEFAULT_TPGT;
1062			ipp->p_tpgt_nego = ISCSI_DEFAULT_TPGT;
1063			ipp->p_num_of_connections = 0;
1064			ipp->p_connected = B_FALSE;
1065		}
1066	} else {
1067		if (isp == NULL) {
1068			rw_exit(&ihp->hba_sess_list_rwlock);
1069			rtn = EFAULT;
1070			return (rtn);
1071		}
1072
1073		/* ISCSI_TARGET_PROPS_SET */
1074		/*
1075		 * only update if new, otherwise could clear out alias
1076		 * if just updating the discovery.
1077		 */
1078		if (ipp->p_alias_len != 0) {
1079			bcopy(ipp->p_alias, isp->sess_alias,
1080			    ipp->p_alias_len);
1081			isp->sess_alias_length  = ipp->p_alias_len;
1082		}
1083		isp->sess_discovered_by = ipp->p_discovery;
1084	}
1085	rw_exit(&ihp->hba_sess_list_rwlock);
1086	return (rtn);
1087}
1088
1089/*
1090 * iscsi_ioctl_get_config_sess - gets configured session information
1091 *
1092 * This function is an ioctl helper function to get the
1093 * configured session information from the persistent store.
1094 */
1095int
1096iscsi_ioctl_get_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics)
1097{
1098	uchar_t *name;
1099
1100	/* Get the matching iscsi node name for the oid */
1101	if (ics->ics_oid == ISCSI_INITIATOR_OID) {
1102		/* initiator name */
1103		name = ihp->hba_name;
1104	} else {
1105		/* target name */
1106		name = iscsi_targetparam_get_name(ics->ics_oid);
1107		if (name == NULL) {
1108			/* invalid node name */
1109			return (EINVAL);
1110		}
1111	}
1112
1113	/* get configured session information */
1114	if (persistent_get_config_session((char *)name, ics) == B_FALSE) {
1115		/*
1116		 * There might not be anything in the database yet.  If
1117		 * this is a request for the target check the initiator
1118		 * value.  If neither is set return the default value.
1119		 */
1120		if (ics->ics_oid != ISCSI_INITIATOR_OID) {
1121			if (persistent_get_config_session(
1122			    (char *)ihp->hba_name, ics) == B_FALSE) {
1123				/*
1124				 * No initiator value is set.
1125				 * Return the defaults.
1126				 */
1127				ics->ics_out = ISCSI_DEFAULT_SESS_NUM;
1128				ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND;
1129			}
1130		} else {
1131			ics->ics_out = ISCSI_DEFAULT_SESS_NUM;
1132			ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND;
1133		}
1134	}
1135
1136	return (0);
1137}
1138
1139/*
1140 * iscsi_ioctl_set_config_sess - sets configured session information
1141 *
1142 * This function is an ioctl helper function to set the
1143 * configured session information in the persistent store.
1144 * In addition it will notify any active sessions of the
1145 * changed so this can update binding information.  It
1146 * will also destroy sessions that were removed and add
1147 * new sessions.
1148 */
1149int
1150iscsi_ioctl_set_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics)
1151{
1152	uchar_t *name;
1153	iscsi_sess_t *isp;
1154
1155	/* check range infomration */
1156	if ((ics->ics_in < ISCSI_MIN_CONFIG_SESSIONS) ||
1157	    (ics->ics_in > ISCSI_MAX_CONFIG_SESSIONS)) {
1158		/* invalid range information */
1159		return (EINVAL);
1160	}
1161
1162	if (ics->ics_oid == ISCSI_INITIATOR_OID) {
1163		name = ihp->hba_name;
1164	} else {
1165		/* get target name */
1166		name = iscsi_targetparam_get_name(ics->ics_oid);
1167		if (name == NULL) {
1168			/* invalid node name */
1169			return (EINVAL);
1170		}
1171	}
1172
1173	/* store the new information */
1174	if (persistent_set_config_session((char *)name, ics) == B_FALSE) {
1175		/* failed to store new information */
1176		return (EINVAL);
1177	}
1178
1179	/* notify existing sessions of change */
1180	rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
1181	isp = ihp->hba_sess_list;
1182	while (isp != NULL) {
1183
1184		if ((ics->ics_oid == ISCSI_INITIATOR_OID) ||
1185		    (strncmp((char *)isp->sess_name, (char *)name,
1186		    ISCSI_MAX_NAME_LEN) == 0)) {
1187
1188			/*
1189			 * If this sessions least signficant byte
1190			 * of the isid is less than or equal to
1191			 * the the number of configured sessions
1192			 * then we need to tear down this session.
1193			 */
1194			if (ics->ics_in <= isp->sess_isid[5]) {
1195				/* First attempt to destory the session */
1196				if (ISCSI_SUCCESS(iscsi_sess_destroy(isp))) {
1197					isp = ihp->hba_sess_list;
1198				} else {
1199					/*
1200					 * If we can't destroy it then
1201					 * atleast poke it to disconnect
1202					 * it.
1203					 */
1204					mutex_enter(&isp->sess_state_mutex);
1205					iscsi_sess_state_machine(isp,
1206					    ISCSI_SESS_EVENT_N7);
1207					mutex_exit(&isp->sess_state_mutex);
1208					isp = isp->sess_next;
1209				}
1210			} else {
1211				isp = isp->sess_next;
1212			}
1213		} else {
1214			isp = isp->sess_next;
1215		}
1216	}
1217	rw_exit(&ihp->hba_sess_list_rwlock);
1218
1219	/*
1220	 * The number of targets has changed.  Since we don't expect
1221	 * this to be a common operation lets keep the code simple and
1222	 * just use a slightly larger hammer and poke discovery.  This
1223	 * force the reevaulation of this target and all other targets.
1224	 */
1225	iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown);
1226	/* lock so only one config operation occrs */
1227	sema_p(&iscsid_config_semaphore);
1228	iscsid_config_all(ihp, B_FALSE);
1229	sema_v(&iscsid_config_semaphore);
1230
1231	return (0);
1232}
1233