1/*
2 * IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
3 * By downloading, copying, installing or using the software you agree
4 * to this license.  If you do not agree to this license, do not
5 * download, install, copy or use the software.
6 *
7 * Intel License Agreement
8 *
9 * Copyright (c) 2000, Intel Corporation
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * -Redistributions of source code must retain the above copyright
17 *  notice, this list of conditions and the following disclaimer.
18 *
19 * -Redistributions in binary form must reproduce the above copyright
20 *  notice, this list of conditions and the following disclaimer in the
21 *  documentation and/or other materials provided with the
22 *  distribution.
23 *
24 * -The name of Intel Corporation may not be used to endorse or
25 *  promote products derived from this software without specific prior
26 *  written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL INTEL
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41#include "config.h"
42
43#include <sys/types.h>
44#include <sys/param.h>
45
46#include <assert.h>
47#include <stdlib.h>
48
49#ifdef HAVE_NETINET_TCP_H
50#include <netinet/tcp.h>
51#endif
52
53#ifdef HAVE_SYS_UIO_H
54#include <sys/uio.h>
55#endif
56
57#ifdef HAVE_SYS_SOCKET_H
58#include <sys/socket.h>
59#endif
60
61#ifdef HAVE_NETINET_IN_H
62#include <netinet/in.h>
63#endif
64
65#ifdef HAVE_STRING_H
66#include <string.h>
67#endif
68
69#ifdef HAVE_SIGNAL_H
70#include <signal.h>
71#endif
72
73#ifdef HAVE_SYSLOG_H
74#include <syslog.h>
75#endif
76
77#ifdef HAVE_ERRNO_H
78#include <errno.h>
79#endif
80
81#ifdef HAVE_NETDB_H
82#include <netdb.h>
83#endif
84
85#ifdef HAVE_ARPA_INET_H
86#include <arpa/inet.h>
87#endif
88
89#ifdef HAVE_INTTYPES_H
90#include <inttypes.h>
91#endif
92
93
94#include "iscsiprotocol.h"
95#include "conffile.h"
96#include "storage.h"
97#include "target.h"
98#include "device.h"
99#include "iscsi-md5.h"
100#include "parameters.h"
101#include "iscsi.h"
102
103enum {
104	TARGET_SHUT_DOWN     = 0,
105	TARGET_INITIALIZING  = 1,
106	TARGET_INITIALIZED   = 2,
107	TARGET_SHUTTING_DOWN = 3
108};
109
110/***********
111 * Private *
112 ***********/
113
114static target_session_t	*g_session;
115static iscsi_queue_t	g_session_q;
116static iscsi_mutex_t	g_session_q_mutex;
117
118/*********************
119 * Private Functions *
120 *********************/
121
122static char *
123get_iqn(target_session_t *sess, uint32_t t, char *buf, size_t size)
124{
125	targv_t	*targv;
126
127	targv = sess->target->lunv;
128	if (targv->v[t].iqn != NULL) {
129		(void) strlcpy(buf, targv->v[t].iqn, size);
130		return buf;
131	}
132	(void) snprintf(buf, size, "%s:%s",
133			iscsi_target_getvar(sess->target, "iqn"),
134			targv->v[t].target);
135	return buf;
136}
137
138static int
139reject_t(target_session_t * sess, uint8_t *header, uint8_t reason)
140{
141	iscsi_reject_t  reject;
142	uint8_t   rsp_header[ISCSI_HEADER_LEN];
143
144	iscsi_err(__FILE__, __LINE__, "reject %x\n", reason);
145	reject.reason = reason;
146	reject.length = ISCSI_HEADER_LEN;
147	reject.StatSN = ++(sess->StatSN);
148	reject.ExpCmdSN = sess->ExpCmdSN;
149	reject.MaxCmdSN = sess->MaxCmdSN;
150	reject.DataSN = 0;	/* SNACK not yet implemented */
151
152	if (iscsi_reject_encap(rsp_header, &reject) != 0) {
153		iscsi_err(__FILE__, __LINE__,
154				"iscsi_reject_encap() failed\n");
155		return -1;
156	}
157	if (iscsi_sock_send_header_and_data(sess->sock, rsp_header,
158			ISCSI_HEADER_LEN, header, ISCSI_HEADER_LEN, 0) !=
159			2 * ISCSI_HEADER_LEN) {
160		iscsi_err(__FILE__, __LINE__,
161			"iscsi_sock_send_header_and_data() failed\n");
162		return -1;
163	}
164	return 0;
165}
166
167static int
168scsi_command_t(target_session_t *sess, uint8_t *header)
169{
170	iscsi_scsi_cmd_args_t	scsi_cmd;
171	iscsi_read_data_t	data;
172	iscsi_scsi_rsp_t	scsi_rsp;
173	target_cmd_t		cmd;
174	uint32_t		DataSN = 0;
175	uint8_t			rsp_header[ISCSI_HEADER_LEN];
176	struct iovec		*sg_new = NULL;
177	int			result;
178
179	(void) memset(&scsi_cmd, 0x0, sizeof(scsi_cmd));
180	scsi_cmd.ahs = NULL;
181	scsi_cmd.send_buffer = NULL;
182	if (iscsi_scsi_cmd_decap(header, &scsi_cmd) != 0) {
183		iscsi_err(__FILE__, __LINE__,
184				"iscsi_scsi_cmd_decap() failed\n");
185		result = -1;
186		goto out;
187	}
188	iscsi_trace(TRACE_ISCSI_DEBUG,
189		"session %d: SCSI Command (CmdSN %u, op %#x)\n",
190		sess->id, scsi_cmd.CmdSN, scsi_cmd.cdb[0]);
191
192	/* For Non-immediate commands, the CmdSN should be between ExpCmdSN  */
193	/* and MaxCmdSN, inclusive of both.  Otherwise, ignore the command */
194	if (!scsi_cmd.immediate &&
195	    (scsi_cmd.CmdSN < sess->ExpCmdSN ||
196	     scsi_cmd.CmdSN > sess->MaxCmdSN)) {
197		iscsi_err(__FILE__, __LINE__,
198			"CmdSN(%d) of SCSI Command not valid, "
199			"ExpCmdSN(%d) MaxCmdSN(%d). Ignoring the command\n",
200			scsi_cmd.CmdSN, sess->ExpCmdSN, sess->MaxCmdSN);
201		result = 0;
202		goto out;
203	}
204	/* Arg check.   */
205	scsi_cmd.attr = 0;	/* Temp fix FIXME */
206	/*
207	 * RETURN_NOT_EQUAL("ATTR (FIX ME)", scsi_cmd.attr, 0, NO_CLEANUP,
208	 * -1);
209	 */
210
211	/* Check Numbering */
212
213	if (scsi_cmd.CmdSN != sess->ExpCmdSN) {
214		iscsi_warn(__FILE__, __LINE__,
215			"Expected CmdSN %d, got %d. "
216			"(ignoring and resetting expectations)\n",
217			sess->ExpCmdSN, scsi_cmd.CmdSN);
218		sess->ExpCmdSN = scsi_cmd.CmdSN;
219	}
220	/* Check Transfer Lengths */
221	if (sess->sess_params.first_burst_length
222	    && (scsi_cmd.length > sess->sess_params.first_burst_length)) {
223		iscsi_err(__FILE__, __LINE__,
224			"scsi_cmd.length (%u) > FirstBurstLength (%u)\n",
225			scsi_cmd.length, sess->sess_params.first_burst_length);
226		scsi_cmd.status = 0x02;
227		scsi_cmd.length = 0;
228		goto response;
229	}
230	if (sess->sess_params.max_dataseg_len &&
231	    scsi_cmd.length > sess->sess_params.max_dataseg_len) {
232		iscsi_err(__FILE__, __LINE__,
233			"scsi_cmd.length (%u) > MaxRecvDataSegmentLength "
234			"(%u)\n",
235			scsi_cmd.length, sess->sess_params.max_dataseg_len);
236		result = -1;
237		goto out;
238	}
239
240#if 0
241	/* commented out in original Intel reference code */
242	if (scsi_cmd.final && scsi_cmd.output) {
243		RETURN_NOT_EQUAL("Length", scsi_cmd.length,
244			scsi_cmd.trans_len, NO_CLEANUP, -1);
245	}
246#endif
247
248	/* Read AHS.  Need to optimize/clean this.   */
249	/* We should not be calling malloc(). */
250	/* We need to check for properly formated AHS segments. */
251
252	if (scsi_cmd.ahs_len) {
253		uint32_t        ahs_len;
254		uint8_t  *ahs_ptr;
255		uint8_t   ahs_type;
256
257		iscsi_trace(TRACE_ISCSI_DEBUG,
258				"reading %u bytes AHS\n", scsi_cmd.ahs_len);
259		scsi_cmd.ahs = iscsi_malloc_atomic((unsigned)scsi_cmd.ahs_len);
260		if (scsi_cmd.ahs == NULL) {
261			iscsi_err(__FILE__, __LINE__,
262				"iscsi_malloc_atomic() failed\n");
263			result = -1;
264			goto out;
265		}
266		if (iscsi_sock_msg(sess->sock, 0, (unsigned)scsi_cmd.ahs_len,
267				scsi_cmd.ahs, 0) != scsi_cmd.ahs_len) {
268			iscsi_err(__FILE__, __LINE__,
269				"iscsi_sock_msg() failed\n");
270			result = -1;
271			goto out;
272		}
273		iscsi_trace(TRACE_ISCSI_DEBUG,
274				"read %u bytes AHS\n", scsi_cmd.ahs_len);
275		for (ahs_ptr = scsi_cmd.ahs;
276		     ahs_ptr < (scsi_cmd.ahs + scsi_cmd.ahs_len - 1) ;
277		     ahs_ptr += ahs_len) {
278			ahs_len = ISCSI_NTOHS(*((uint16_t *) (void *)ahs_ptr));
279			if (ahs_len == 0) {
280				iscsi_err(__FILE__, __LINE__,
281				 		"Zero ahs_len\n");
282				result = -1;
283				goto out;
284			}
285			switch (ahs_type = *(ahs_ptr + 2)) {
286			case ISCSI_AHS_EXTENDED_CDB:
287				iscsi_trace(TRACE_ISCSI_DEBUG,
288					"Got ExtendedCDB AHS - %u bytes extra "
289					"CDB)\n", ahs_len - 1);
290				scsi_cmd.ext_cdb = ahs_ptr + 4;
291				break;
292			case ISCSI_AHS_BIDI_READ:
293				scsi_cmd.bidi_trans_len =
294					ISCSI_NTOHL(*((uint32_t *)(void *)
295							(ahs_ptr + 4)));
296				*((uint32_t *)(void *)(ahs_ptr + 4)) =
297						scsi_cmd.bidi_trans_len;
298				iscsi_trace(TRACE_ISCSI_DEBUG,
299					"Got Bidirectional Read AHS "
300					"(expected read length %u)\n",
301					scsi_cmd.bidi_trans_len);
302				break;
303			default:
304				iscsi_err(__FILE__, __LINE__,
305					"unknown AHS type %x\n", ahs_type);
306				result = -1;
307				goto out;
308			}
309		}
310		iscsi_trace(TRACE_ISCSI_DEBUG,
311			"done parsing %u bytes AHS\n", scsi_cmd.ahs_len);
312	} else {
313		iscsi_trace(TRACE_ISCSI_DEBUG, "no AHS to read\n");
314		scsi_cmd.ahs = NULL;
315	}
316
317	sess->ExpCmdSN++;
318	sess->MaxCmdSN++;
319
320	/* Execute cdb.  device_command() will set scsi_cmd.input if
321	* there is input data and set the length of the input to
322	* either scsi_cmd.trans_len or scsi_cmd.bidi_trans_len,
323	* depending on whether scsi_cmd.output was set.  */
324	scsi_cmd.send_data = sess->buff;
325	scsi_cmd.input = 0;
326	cmd.scsi_cmd = &scsi_cmd;
327	cmd.callback = NULL;
328	if (device_command(sess, &cmd) != 0) {
329		iscsi_err(__FILE__, __LINE__,
330				"device_command() failed\n");
331		result = -1;
332		goto out;
333	}
334	/* Send any input data */
335
336	scsi_cmd.bytes_sent = 0;
337	if (!scsi_cmd.status && scsi_cmd.input) {
338		struct iovec    sg_singleton;
339		struct iovec   *sg, *sg_orig;
340		int             sg_len_orig, sg_len;
341		uint32_t        offset, trans_len;
342		int             fragment_flag = 0;
343		int             offset_inc;
344
345		if (scsi_cmd.output) {
346			iscsi_trace(TRACE_ISCSI_DEBUG,
347				"sending %u bytes bi-directional input data\n",
348				scsi_cmd.bidi_trans_len);
349			trans_len = scsi_cmd.bidi_trans_len;
350		} else {
351			trans_len = scsi_cmd.trans_len;
352		}
353		iscsi_trace(TRACE_ISCSI_DEBUG,
354			"sending %u bytes input data as separate PDUs\n",
355			trans_len);
356
357		if (scsi_cmd.send_sg_len) {
358			sg_orig = (struct iovec *)(void *)scsi_cmd.send_data;
359			sg_len_orig = scsi_cmd.send_sg_len;
360		} else {
361			sg_len_orig = 1;
362			sg_singleton.iov_base = scsi_cmd.send_data;
363			sg_singleton.iov_len = trans_len;
364			sg_orig = &sg_singleton;
365		}
366		sg = sg_orig;
367		sg_len = sg_len_orig;
368
369		offset_inc = (sess->sess_params.max_dataseg_len) ?
370			sess->sess_params.max_dataseg_len : trans_len;
371
372		for (offset = 0; offset < trans_len; offset += offset_inc) {
373			(void) memset(&data, 0x0, sizeof(data));
374			data.length = (sess->sess_params.max_dataseg_len) ?
375				MIN(trans_len - offset,
376					sess->sess_params.max_dataseg_len) :
377				trans_len - offset;
378			if (data.length != trans_len) {
379				if (!fragment_flag) {
380					sg_new = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len_orig);
381					if (sg_new == NULL) {
382						iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
383						result = -1;
384						goto out;
385					}
386					fragment_flag++;
387				}
388				sg = sg_new;
389				sg_len = sg_len_orig;
390				(void) memcpy(sg, sg_orig, sizeof(struct iovec) * sg_len_orig);
391				if (modify_iov(&sg, &sg_len, offset, data.length) != 0) {
392					iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
393					result = -1;
394					goto out;
395				}
396			}
397			iscsi_trace(TRACE_ISCSI_DEBUG, "sending read data PDU (offset %u, len %u)\n", offset, data.length);
398			if (offset + data.length == trans_len) {
399				data.final = 1;
400
401				if (sess->UsePhaseCollapsedRead) {
402					data.status = 1;
403					data.status = scsi_cmd.status;
404					data.StatSN = ++(sess->StatSN);
405					iscsi_trace(TRACE_ISCSI_DEBUG, "status %#x collapsed into last data PDU\n", data.status);
406				} else {
407					iscsi_trace(TRACE_ISCSI_DEBUG, "NOT collapsing status with last data PDU\n");
408				}
409			} else if (offset + data.length > trans_len) {
410				iscsi_err(__FILE__, __LINE__, "offset+data.length > trans_len??\n");
411				result = -1;
412				goto out;
413			}
414			data.task_tag = scsi_cmd.tag;
415			data.ExpCmdSN = sess->ExpCmdSN;
416			data.MaxCmdSN = sess->MaxCmdSN;
417			data.DataSN = DataSN++;
418			data.offset = offset;
419			if (iscsi_read_data_encap(rsp_header, &data) != 0) {
420				iscsi_err(__FILE__, __LINE__, "iscsi_read_data_encap() failed\n");
421				result = -1;
422				goto out;
423			}
424			if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, sg, data.length, sg_len)
425			    != ISCSI_HEADER_LEN + data.length) {
426				iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
427				result = -1;
428				goto out;
429			}
430			scsi_cmd.bytes_sent += data.length;
431			iscsi_trace(TRACE_ISCSI_DEBUG, "sent read data PDU ok (offset %u, len %u)\n", data.offset, data.length);
432		}
433		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes read data\n", trans_len);
434	}
435	/*
436         * Send a response PDU if
437         *
438         * 1) we're not using phase collapsed input (and status was good)
439         * 2) we are using phase collapsed input, but there was no input data (e.g., TEST UNIT READY)
440         * 3) command had non-zero status and possible sense data
441         */
442response:
443	if (!sess->UsePhaseCollapsedRead || !scsi_cmd.length || scsi_cmd.status) {
444		iscsi_trace(TRACE_ISCSI_DEBUG, "sending SCSI response PDU\n");
445		(void) memset(&scsi_rsp, 0x0, sizeof(scsi_rsp));
446		scsi_rsp.length = scsi_cmd.status ? scsi_cmd.length : 0;
447		scsi_rsp.tag = scsi_cmd.tag;
448		/* If r2t send, then the StatSN is already incremented */
449		if (sess->StatSN < scsi_cmd.ExpStatSN) {
450			++sess->StatSN;
451		}
452		scsi_rsp.StatSN = sess->StatSN;
453		scsi_rsp.ExpCmdSN = sess->ExpCmdSN;
454		scsi_rsp.MaxCmdSN = sess->MaxCmdSN;
455		scsi_rsp.ExpDataSN = (!scsi_cmd.status && scsi_cmd.input) ? DataSN : 0;
456		scsi_rsp.response = 0x00;	/* iSCSI response */
457		scsi_rsp.status = scsi_cmd.status;	/* SCSI status */
458		if (iscsi_scsi_rsp_encap(rsp_header, &scsi_rsp) != 0) {
459			iscsi_err(__FILE__, __LINE__, "iscsi_scsi_rsp_encap() failed\n");
460			result = -1;
461			goto out;
462		}
463		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
464		  scsi_cmd.send_data, scsi_rsp.length, scsi_cmd.send_sg_len)
465		    != ISCSI_HEADER_LEN + scsi_rsp.length) {
466			iscsi_err(__FILE__, __LINE__,
467				"iscsi_sock_send_header_and_data() failed\n");
468			result = -1;
469			goto out;
470		}
471		/* Make sure all data was transferred */
472
473		if (scsi_cmd.output) {
474			if (scsi_cmd.bytes_recv != scsi_cmd.trans_len) {
475				iscsi_err(__FILE__, __LINE__,
476					"scsi_cmd.bytes_recv");
477				result = -1;
478				goto out;
479			}
480			if (scsi_cmd.input) {
481				if (scsi_cmd.bytes_sent !=
482						scsi_cmd.bidi_trans_len) {
483					iscsi_err(__FILE__, __LINE__,
484						"scsi_cmd.bytes_sent");
485					result = -1;
486					goto out;
487				}
488			}
489		} else {
490			if (scsi_cmd.input) {
491				if (scsi_cmd.bytes_sent != scsi_cmd.trans_len) {
492					iscsi_err(__FILE__, __LINE__,
493						"scsi_cmd.bytes_sent");
494					result = -1;
495					goto out;
496				}
497			}
498		}
499	}
500
501	/* Device callback after command has completed */
502	if (cmd.callback) {
503		iscsi_trace(TRACE_ISCSI_DEBUG, "issuing device callback\n");
504		if ((*cmd.callback)(cmd.callback_arg) != 0) {
505			iscsi_err(__FILE__, __LINE__,
506				"device callback failed\n");
507			result = -1;
508			goto out;
509		}
510	}
511	result = 0;
512out:
513	if (scsi_cmd.ahs != NULL) {					\
514		iscsi_free_atomic(scsi_cmd.ahs);			\
515	}								\
516	if (sg_new != NULL) {
517		iscsi_free_atomic(sg_new);
518	}
519	free(scsi_cmd.send_buffer);
520	return result;
521}
522
523static int
524task_command_t(target_session_t * sess, uint8_t *header)
525{
526	iscsi_task_cmd_t cmd;
527	iscsi_task_rsp_t rsp;
528	uint8_t   rsp_header[ISCSI_HEADER_LEN];
529
530	/* Get & check args */
531
532	if (iscsi_task_cmd_decap(header, &cmd) != 0) {
533		iscsi_err(__FILE__, __LINE__,
534				"iscsi_task_cmd_decap() failed\n");
535		return -1;
536	}
537	if (cmd.CmdSN != sess->ExpCmdSN) {
538		iscsi_warn(__FILE__, __LINE__,
539			"Expected CmdSN %d, got %d. "
540			"(ignoring and resetting expectations)\n",
541			 cmd.CmdSN, sess->ExpCmdSN);
542		sess->ExpCmdSN = cmd.CmdSN;
543	}
544	sess->MaxCmdSN++;
545
546	(void) memset(&rsp, 0x0, sizeof(rsp));
547	rsp.response = ISCSI_TASK_RSP_FUNCTION_COMPLETE;
548
549	switch (cmd.function) {
550	case ISCSI_TASK_CMD_ABORT_TASK:
551		printf("ISCSI_TASK_CMD_ABORT_TASK\n");
552		break;
553	case ISCSI_TASK_CMD_ABORT_TASK_SET:
554		printf("ISCSI_TASK_CMD_ABORT_TASK_SET\n");
555		break;
556	case ISCSI_TASK_CMD_CLEAR_ACA:
557		printf("ISCSI_TASK_CMD_CLEAR_ACA\n");
558		break;
559	case ISCSI_TASK_CMD_CLEAR_TASK_SET:
560		printf("ISCSI_TASK_CMD_CLEAR_TASK_SET\n");
561		break;
562	case ISCSI_TASK_CMD_LOGICAL_UNIT_RESET:
563		printf("ISCSI_TASK_CMD_LOGICAL_UNIT_RESET\n");
564		break;
565	case ISCSI_TASK_CMD_TARGET_WARM_RESET:
566		printf("ISCSI_TASK_CMD_TARGET_WARM_RESET\n");
567		break;
568	case ISCSI_TASK_CMD_TARGET_COLD_RESET:
569		printf("ISCSI_TASK_CMD_TARGET_COLD_RESET\n");
570		break;
571	case ISCSI_TASK_CMD_TARGET_REASSIGN:
572		printf("ISCSI_TASK_CMD_TARGET_REASSIGN\n");
573		break;
574	default:
575		iscsi_err(__FILE__, __LINE__, "Unknown task function %d\n", cmd.function);
576		rsp.response = ISCSI_TASK_RSP_REJECTED;
577	}
578
579	rsp.tag = cmd.tag;
580	rsp.StatSN = ++(sess->StatSN);
581	rsp.ExpCmdSN = sess->ExpCmdSN;
582	rsp.MaxCmdSN = sess->MaxCmdSN;
583
584	if (iscsi_task_rsp_encap(rsp_header, &rsp) != 0) {
585		iscsi_err(__FILE__, __LINE__, "iscsi_task_cmd_decap() failed\n");
586		return -1;
587	}
588	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != ISCSI_HEADER_LEN) {
589		iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
590		return -1;
591
592	}
593	return 0;
594}
595
596static int
597nop_out_t(target_session_t * sess, uint8_t *header)
598{
599	iscsi_nop_out_args_t nop_out;
600	char           *ping_data = NULL;
601
602	if (iscsi_nop_out_decap(header, &nop_out) != 0) {
603		iscsi_err(__FILE__, __LINE__, "iscsi_nop_out_decap() failed\n");
604		return -1;
605	}
606	if (nop_out.CmdSN != sess->ExpCmdSN) {
607		iscsi_warn(__FILE__, __LINE__, "Expected CmdSN %d, got %d. (ignoring and resetting expectations)\n",
608			      nop_out.CmdSN, sess->ExpCmdSN);
609		sess->ExpCmdSN = nop_out.CmdSN;
610	}
611	/* TODO Clarify whether we need to update the CmdSN */
612	/* sess->ExpCmdSN++;  */
613	/* sess->MaxCmdSN++;  */
614
615	if (nop_out.length) {
616		iscsi_trace(TRACE_ISCSI_DEBUG, "reading %u bytes ping data\n", nop_out.length);
617		if ((ping_data = iscsi_malloc(nop_out.length)) == NULL) {
618			iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
619			return -1;
620		}
621		if ((uint32_t)iscsi_sock_msg(sess->sock, 0, nop_out.length, ping_data, 0) != nop_out.length) {
622			iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
623			if (ping_data) {
624				iscsi_free(ping_data);
625			}
626			return -1;
627		}
628		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully read %u bytes ping data:\n", nop_out.length);
629		iscsi_print_buffer(ping_data, nop_out.length);
630	}
631	if (nop_out.tag != 0xffffffff) {
632		iscsi_nop_in_args_t  nop_in;
633		uint8_t   rsp_header[ISCSI_HEADER_LEN];
634
635		iscsi_trace(TRACE_ISCSI_DEBUG, "sending %u bytes ping response\n", nop_out.length);
636		(void) memset(&nop_in, 0x0, sizeof(nop_in));
637		nop_in.length = nop_out.length;
638		nop_in.lun = nop_out.lun;
639		nop_in.tag = nop_out.tag;
640		nop_in.transfer_tag = 0xffffffff;
641		nop_in.StatSN = ++(sess->StatSN);
642		nop_in.ExpCmdSN = sess->ExpCmdSN;
643		nop_in.MaxCmdSN = sess->MaxCmdSN;
644
645		if (iscsi_nop_in_encap(rsp_header, &nop_in) != 0) {
646			iscsi_err(__FILE__, __LINE__, "iscsi_nop_in_encap() failed\n");
647			if (ping_data) {
648				iscsi_free(ping_data);
649			}
650			return -1;
651		}
652		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
653						    ping_data, nop_in.length, 0) != ISCSI_HEADER_LEN + nop_in.length) {
654			iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
655			if (ping_data) {
656				iscsi_free(ping_data);
657			}
658			return -1;
659		}
660		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes ping response\n", nop_out.length);
661	}
662	if (ping_data) {
663		iscsi_free(ping_data);
664	}
665	return 0;
666}
667
668/*
669 * text_command_t
670 */
671
672static int
673text_command_t(target_session_t * sess, uint8_t *header)
674{
675	iscsi_text_cmd_args_t	 text_cmd;
676	iscsi_text_rsp_args_t	 text_rsp;
677	unsigned		 len_in;
678	uint32_t		 i;
679	uint8_t   		 rsp_header[ISCSI_HEADER_LEN];
680	targv_t			*targv;
681	char           		*text_in = NULL;
682	char           		*text_out = NULL;
683	char			 buf[BUFSIZ];
684	int			 len_out = 0;
685
686#define TC_CLEANUP do {							\
687	if (text_in != NULL) {						\
688		iscsi_free_atomic(text_in);				\
689	}								\
690	if (text_out != NULL) {						\
691		iscsi_free_atomic(text_out);				\
692	}								\
693} while (/* CONSTCOND */ 0)
694#define TC_ERROR {							\
695	TC_CLEANUP;							\
696	return -1;							\
697}
698	/* Get text args */
699
700	if (iscsi_text_cmd_decap(header, &text_cmd) != 0) {
701		iscsi_err(__FILE__, __LINE__, "iscsi_text_cmd_decap() failed\n");
702		return -1;
703	}
704	/* Check args & update numbering */
705#if 0
706	RETURN_NOT_EQUAL("Continue", text_cmd.cont, 0, NO_CLEANUP, -1);
707	RETURN_NOT_EQUAL("CmdSN", text_cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
708#else
709	if (text_cmd.cont != 0) {
710		iscsi_err(__FILE__, __LINE__, "Continue");
711		NO_CLEANUP;
712		return -1;
713	}
714	if (text_cmd.CmdSN != sess->ExpCmdSN) {
715		iscsi_err(__FILE__, __LINE__, "CmdSN");
716		NO_CLEANUP;
717		return -1;
718	}
719#endif
720
721	sess->ExpCmdSN++;
722	sess->MaxCmdSN++;
723
724	if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
725		iscsi_err(__FILE__, __LINE__,
726				"iscsi_malloc_atomic() failed\n");
727		return -1;
728	}
729
730	/* Read text parameters */
731	if ((len_in = text_cmd.length) != 0) {
732		iscsi_parameter_t *ptr;
733
734		if ((text_in = iscsi_malloc_atomic(len_in + 1)) == NULL) {
735			iscsi_err(__FILE__, __LINE__,
736				"iscsi_malloc_atomic() failed\n");
737			TC_CLEANUP;
738			return -1;
739		}
740		iscsi_trace(TRACE_ISCSI_DEBUG,
741			"reading %u bytes text parameters\n", len_in);
742		if ((unsigned)iscsi_sock_msg(sess->sock, 0, len_in, text_in,
743				0) != len_in) {
744			iscsi_err(__FILE__, __LINE__,
745				"iscsi_sock_msg() failed\n");
746			TC_CLEANUP;
747			return -1;
748		}
749		text_in[len_in] = 0x0;
750		PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred,
751			text_in, (int) len_in, text_out,
752			(int *)(void *)&len_out, 2048, 0, TC_ERROR);
753
754		/*
755		 * Handle exceptional cases not covered by parameters.c
756		 * (e.g., SendTargets)
757		 */
758		if ((ptr = param_get(sess->params, "SendTargets")) == NULL) {
759			iscsi_err(__FILE__, __LINE__,
760					"param_get() failed\n");
761			TC_CLEANUP;
762			return -1;
763		}
764		if (ptr->rx_offer) {
765			if (strcmp(ptr->offer_rx, "All") == 0 &&
766			    !param_equiv(sess->params, "SessionType",
767			    	"Discovery")) {
768				iscsi_trace(TRACE_ISCSI_DEBUG,
769					"Rejecting SendTargets=All in a "
770					"non Discovery session\n");
771				PARAM_TEXT_ADD(sess->params, "SendTargets",
772					"Reject", text_out, &len_out, 2048,
773					0, TC_ERROR);
774			} else {
775				targv = sess->target->lunv;
776				for (i = 0 ; i < targv->c ; i++) {
777					if (sess->address_family == 6 ||
778					    (sess->address_family == 4 &&
779					    	allow_netmask(targv->v[i].mask,
780						sess->initiator))) {
781						(void) get_iqn(sess, i, buf,
782							sizeof(buf));
783						PARAM_TEXT_ADD(sess->params,
784							"TargetName", buf,
785							text_out, &len_out,
786							2048, 0, TC_ERROR);
787						PARAM_TEXT_ADD(sess->params,
788							"TargetAddress",
789			iscsi_target_getvar(sess->target, "target address"),
790							text_out, &len_out,
791							2048, 0, TC_ERROR);
792					} else {
793#ifdef HAVE_SYSLOG_H
794						syslog(LOG_INFO,
795							"WARNING: attempt to "
796							"discover targets from "
797							"%s (not allowed by %s)"
798							" has been rejected",
799							sess->initiator,
800							targv->v[0].mask);
801#endif
802					}
803				}
804			}
805			ptr->rx_offer = 0;
806		}
807		/* Parse outgoing offer */
808
809		if (len_out) {
810			PARAM_TEXT_PARSE(sess->params,
811				&sess->sess_params.cred, text_out, len_out,
812				NULL, NULL, 2048, 1, TC_ERROR);
813		}
814	}
815	if (sess->IsFullFeature) {
816		set_session_parameters(sess->params, &sess->sess_params);
817	}
818	/* Send response */
819
820	text_rsp.final = text_cmd.final;
821	text_rsp.cont = 0;
822	text_rsp.length = len_out;
823	text_rsp.lun = text_cmd.lun;
824	text_rsp.tag = text_cmd.tag;
825	text_rsp.transfer_tag = (text_rsp.final) ? 0xffffffff : 0x1234;
826	text_rsp.StatSN = ++(sess->StatSN);
827	text_rsp.ExpCmdSN = sess->ExpCmdSN;
828	text_rsp.MaxCmdSN = sess->MaxCmdSN;
829	if (iscsi_text_rsp_encap(rsp_header, &text_rsp) != 0) {
830		iscsi_err(__FILE__, __LINE__,
831			"iscsi_text_rsp_encap() failed\n");
832		TC_CLEANUP;
833		return -1;
834	}
835	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
836			ISCSI_HEADER_LEN) {
837		iscsi_err(__FILE__, __LINE__,
838			"iscsi_sock_msg() failed\n");
839		TC_CLEANUP;
840		return -1;
841	}
842	if (len_out && iscsi_sock_msg(sess->sock, 1, (unsigned) len_out,
843			text_out, 0) != len_out) {
844		iscsi_err(__FILE__, __LINE__,
845			"iscsi_sock_msg() failed\n");
846		TC_CLEANUP;
847		return -1;
848	}
849	TC_CLEANUP;
850	return 0;
851}
852
853/* given a target's iqn, find the relevant target that we're exporting */
854int
855find_target_iqn(target_session_t *sess)
856{
857	uint32_t	 i;
858	targv_t		*targv;
859	char		 buf[BUFSIZ];
860
861	targv = sess->target->lunv;
862	for (i = 0 ; i < targv->c ; i++) {
863		if (param_equiv(sess->params, "TargetName",
864				get_iqn(sess, i, buf, sizeof(buf)))) {
865			return sess->d = i;
866		}
867	}
868	return -1;
869}
870
871/* given a tsih, find the relevant target that we're exporting */
872int
873find_target_tsih(iscsi_target_t *target, int tsih)
874{
875	uint32_t	 i;
876	targv_t		*targv;
877
878	targv = target->lunv;
879	for (i = 0 ; i < targv->c ; i++) {
880		if (targv->v[i].tsih == tsih) {
881			return i;
882		}
883	}
884	return -1;
885}
886
887/*
888 * login_command_t() handles login requests and replies.
889 */
890
891static int
892login_command_t(target_session_t * sess, uint8_t *header)
893{
894	iscsi_login_cmd_args_t cmd;
895	iscsi_login_rsp_args_t rsp;
896	uint8_t		rsp_header[ISCSI_HEADER_LEN];
897	targv_t		*targv;
898	char           *text_in = NULL;
899	char           *text_out = NULL;
900	char		logbuf[BUFSIZ];
901	int             len_in = 0;
902	int             len_out = 0;
903	int             status = 0;
904	int		i;
905
906	/* Initialize response */
907
908#define LC_CLEANUP do {							\
909	if (text_in != NULL) {						\
910		iscsi_free_atomic(text_in);				\
911	}								\
912	if (text_out != NULL) {						\
913		iscsi_free_atomic(text_out);				\
914	}								\
915} while (/* CONSTCOND */ 0)
916#define LC_ERROR {							\
917	TC_CLEANUP;							\
918	return -1;							\
919}
920
921	(void) memset(&rsp, 0x0, sizeof(rsp));
922	rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
923
924	/* Get login args & check preconditions */
925
926	if (iscsi_login_cmd_decap(header, &cmd) != 0) {
927		iscsi_err(__FILE__, __LINE__,
928			"iscsi_login_cmd_decap() failed\n");
929		goto response;
930	}
931	if (sess->IsLoggedIn) {
932		iscsi_err(__FILE__, __LINE__,
933			"duplicate login attempt on sess %d\n", sess->id);
934		goto response;
935	}
936	if ((cmd.cont != 0) && (cmd.transit != 0)) {
937		iscsi_err(__FILE__, __LINE__,
938			"Bad cmd.continue.  Expected 0.\n");
939		goto response;
940	} else if ((cmd.version_max < ISCSI_VERSION) ||
941		   (cmd.version_min > ISCSI_VERSION)) {
942		iscsi_err(__FILE__, __LINE__,
943			"Target iscsi version (%u) not supported by initiator "
944			"[Max Ver (%u) and Min Ver (%u)]\n",
945			ISCSI_VERSION, cmd.version_max, cmd.version_min);
946		rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
947		rsp.status_detail = ISCSI_LOGIN_DETAIL_VERSION_NOT_SUPPORTED;
948		rsp.version_max = ISCSI_VERSION;
949		rsp.version_active = ISCSI_VERSION;
950		goto response;
951	} else if (cmd.tsih != 0) {
952		iscsi_err(__FILE__, __LINE__,
953			"Bad cmd.tsih (%u). Expected 0.\n", cmd.tsih);
954		goto response;
955	}
956
957	/* Parse text parameters and build response */
958	if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
959		iscsi_err(__FILE__, __LINE__,
960			"iscsi_malloc_atomic() failed\n");
961		return -1;
962	}
963	if ((len_in = cmd.length) != 0) {
964		iscsi_trace(TRACE_ISCSI_DEBUG,
965			"reading %d bytes text data\n", len_in);
966		text_in = iscsi_malloc_atomic((unsigned)(len_in + 1));
967		if (text_in == NULL) {
968			iscsi_err(__FILE__, __LINE__,
969				"iscsi_malloc() failed\n");
970			LC_CLEANUP;
971			return -1;
972		}
973		if (iscsi_sock_msg(sess->sock, 0, (unsigned) len_in, text_in,
974				0) != len_in) {
975			iscsi_err(__FILE__, __LINE__,
976				"iscsi_sock_msg() failed\n");
977			LC_CLEANUP;
978			return -1;
979		}
980		text_in[len_in] = 0x0;
981		iscsi_trace(TRACE_ISCSI_DEBUG,
982			"successfully read %d bytes text data\n", len_in);
983
984		/*
985		 * Parse incoming parameters (text_out will contain the
986		 * response we need
987		 */
988
989		/* to send back to the initiator */
990
991
992		status = param_text_parse(sess->params,
993				&sess->sess_params.cred, text_in, len_in,
994				text_out, &len_out, 2048, 0);
995		if (status != 0) {
996			switch (status) {
997			case ISCSI_PARAM_STATUS_FAILED:
998				rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
999				break;
1000			case ISCSI_PARAM_STATUS_AUTH_FAILED:
1001				rsp.status_detail =
1002					ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
1003				break;
1004			default:
1005				/*
1006				 * We will need to set the detail
1007				 * field based on more detailed error
1008				 * cases.  Will need to fix this if
1009				 * compliciance test break
1010		                 * (status_detail field).
1011		                 */
1012				break;
1013			}
1014			goto response;
1015		}
1016		/* Parse the outgoing offer */
1017		if (!sess->LoginStarted) {
1018			PARAM_TEXT_ADD(sess->params, "TargetPortalGroupTag",
1019				"1", text_out, &len_out, 2048, 0, LC_ERROR);
1020		}
1021		if (len_out) {
1022			PARAM_TEXT_PARSE(sess->params,
1023				&sess->sess_params.cred, text_out, len_out,
1024				NULL, NULL, 2048, 1, LC_ERROR;
1025			);
1026		}
1027	}
1028	if (!sess->LoginStarted) {
1029		sess->LoginStarted = 1;
1030	}
1031	/*
1032	 * For now, we accept what ever the initiators' current and next
1033	 * states are. And le are always
1034	 */
1035	/* ready to transitition to that state. */
1036
1037	rsp.csg = cmd.csg;
1038	rsp.nsg = cmd.nsg;
1039	rsp.transit = cmd.transit;
1040
1041	if (cmd.csg == ISCSI_LOGIN_STAGE_SECURITY) {
1042		if (param_equiv(sess->params, "AuthResult", "No")) {
1043			rsp.transit = 0;
1044		} else if (param_equiv(sess->params, "AuthResult", "Fail")) {
1045			rsp.status_class = rsp.status_detail =
1046				ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
1047			goto response;
1048		}
1049	}
1050	if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
1051		iscsi_trace(TRACE_ISCSI_DEBUG,
1052			"transitioning to ISCSI_LOGIN_STAGE_FULL_FEATURE\n");
1053
1054		/* Check post conditions */
1055		if (param_equiv(sess->params, "InitiatorName", "")) {
1056			iscsi_err(__FILE__, __LINE__,
1057				"InitiatorName not specified\n");
1058			goto response;
1059		}
1060		if (param_equiv(sess->params, "SessionType", "Normal")) {
1061			if (param_equiv(sess->params, "TargetName", "")) {
1062				iscsi_err(__FILE__, __LINE__,
1063					"TargetName not specified\n");
1064				goto response;
1065			}
1066			if ((i = find_target_iqn(sess)) < 0) {
1067				iscsi_err(__FILE__, __LINE__,
1068					"Bad TargetName \"%s\"\n",
1069					param_val(sess->params, "TargetName"));
1070				goto response;
1071			}
1072			if (cmd.tsih != 0 &&
1073			    find_target_tsih(sess->target, cmd.tsih) != i) {
1074				targv = sess->target->lunv;
1075				iscsi_err(__FILE__, __LINE__,
1076					"target tsih expected %d, cmd.tsih %d, "
1077					"i %d\n", targv->v[i].tsih, cmd.tsih,
1078					i);
1079			}
1080			sess->d = i;
1081		} else if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
1082			iscsi_err(__FILE__, __LINE__,
1083				"Abnormal SessionType cmd.tsih %d not found\n",
1084				cmd.tsih);
1085			i = sess->d;
1086		}
1087		if (param_equiv(sess->params, "SessionType", "")) {
1088			iscsi_err(__FILE__, __LINE__,
1089				"SessionType not specified\n");
1090			goto response;
1091		}
1092		sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
1093		sess->cid = cmd.cid;
1094		sess->isid = cmd.isid;
1095
1096		targv = sess->target->lunv;
1097		targv->v[i].tsih = sess->tsih = ++sess->target->last_tsih;
1098		sess->IsFullFeature = 1;
1099
1100		sess->IsLoggedIn = 1;
1101		if (!param_equiv(sess->params, "SessionType", "Discovery")) {
1102			(void) strlcpy(param_val(sess->params,
1103					"MaxConnections"), "1", 2);
1104		}
1105		set_session_parameters(sess->params, &sess->sess_params);
1106	} else {
1107		if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
1108			iscsi_err(__FILE__, __LINE__,
1109				"cmd.tsih %d not found\n", cmd.tsih);
1110		}
1111	}
1112
1113	/* No errors */
1114	rsp.status_class = rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
1115	rsp.length = len_out;
1116
1117	/* Send login response */
1118response:
1119	sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
1120	rsp.isid = cmd.isid;
1121	rsp.StatSN = cmd.ExpStatSN;	/* debug  */
1122	rsp.tag = cmd.tag;
1123	rsp.cont = cmd.cont;
1124	rsp.ExpCmdSN = sess->ExpCmdSN;
1125	rsp.MaxCmdSN = sess->MaxCmdSN;
1126	if (!rsp.status_class) {
1127		if (rsp.transit &&
1128		    (rsp.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE)) {
1129			rsp.version_max = ISCSI_VERSION;
1130			rsp.version_active = ISCSI_VERSION;
1131			rsp.StatSN = ++(sess->StatSN);
1132			rsp.tsih = sess->tsih;
1133		}
1134	}
1135	if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
1136		iscsi_err(__FILE__, __LINE__,
1137			"iscsi_login_rsp_encap() failed\n");
1138		LC_CLEANUP;
1139		return -1;
1140	}
1141	iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
1142	if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header,
1143			ISCSI_HEADER_LEN, text_out, rsp.length, 0) !=
1144			ISCSI_HEADER_LEN + rsp.length) {
1145		iscsi_err(__FILE__, __LINE__,
1146			"iscsi_sock_send_header_and_data() failed\n");
1147		LC_CLEANUP;
1148		return -1;
1149	}
1150	iscsi_trace(TRACE_ISCSI_DEBUG,
1151		"sent login response ok\n");
1152	if (rsp.status_class != 0) {
1153		LC_CLEANUP;
1154		return -1;
1155	}
1156	if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
1157
1158		/* log information to stdout */
1159		(void) snprintf(logbuf, sizeof(logbuf),
1160			"> iSCSI %s login  successful from %s on %s disk %d, "
1161			"ISID %" PRIu64 ", TSIH %u",
1162			param_val(sess->params, "SessionType"),
1163			param_val(sess->params, "InitiatorName"),
1164			sess->initiator,
1165			sess->d,
1166			sess->isid,
1167			sess->tsih);
1168		printf("%s\n", logbuf);
1169#ifdef HAVE_SYSLOG_H
1170		/* log information to syslog */
1171		syslog(LOG_INFO, "%s", logbuf);
1172#endif
1173
1174		/* Buffer for data xfers to/from the scsi device */
1175		if (!param_equiv(sess->params, "MaxRecvDataSegmentLength",
1176					"0")) {
1177			sess->buff = iscsi_malloc((unsigned)(
1178					param_atoi(sess->params,
1179					"MaxRecvDataSegmentLength")));
1180			if (sess->buff == NULL) {
1181				iscsi_err(__FILE__, __LINE__,
1182					"iscsi_malloc() failed\n");
1183				LC_CLEANUP;
1184				return -1;
1185			}
1186		} else {
1187			iscsi_err(__FILE__, __LINE__,
1188				"0 MaxRecvDataSegmentLength not supported\n");
1189			LC_CLEANUP;
1190			return -1;
1191		}
1192	}
1193	LC_CLEANUP;
1194	return 0;
1195}
1196
1197static int
1198logout_command_t(target_session_t * sess, uint8_t *header)
1199{
1200	iscsi_logout_cmd_args_t	cmd;
1201	iscsi_logout_rsp_args_t	rsp;
1202	targv_t			*targv;
1203	uint8_t			rsp_header[ISCSI_HEADER_LEN];
1204	char			logbuf[BUFSIZ];
1205	int			i;
1206
1207	(void) memset(&rsp, 0x0, sizeof(rsp));
1208	if (iscsi_logout_cmd_decap(header, &cmd) != 0) {
1209		iscsi_err(__FILE__, __LINE__,
1210			"iscsi_logout_cmd_decap() failed\n");
1211		return -1;
1212	}
1213	sess->StatSN = cmd.ExpStatSN;
1214	if ((cmd.reason == ISCSI_LOGOUT_CLOSE_RECOVERY) &&
1215	    (param_equiv(sess->params, "ErrorRecoveryLevel", "0"))) {
1216		rsp.response = ISCSI_LOGOUT_STATUS_NO_RECOVERY;
1217	}
1218#if 0
1219	RETURN_NOT_EQUAL("CmdSN", cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
1220	RETURN_NOT_EQUAL("ExpStatSN", cmd.ExpStatSN, sess->StatSN, NO_CLEANUP, -1);
1221#else
1222	if (cmd.CmdSN != sess->ExpCmdSN) {
1223		iscsi_err(__FILE__, __LINE__, "CmdSN");
1224		NO_CLEANUP;
1225		return -1;
1226	}
1227	if (cmd.ExpStatSN != sess->StatSN) {
1228		iscsi_err(__FILE__, __LINE__, "ExpStatSN");
1229		NO_CLEANUP;
1230		return -1;
1231	}
1232#endif
1233
1234	rsp.tag = cmd.tag;
1235	rsp.StatSN = sess->StatSN;
1236	rsp.ExpCmdSN = ++sess->ExpCmdSN;
1237	rsp.MaxCmdSN = sess->MaxCmdSN;
1238	if (iscsi_logout_rsp_encap(rsp_header, &rsp) != 0) {
1239		iscsi_err(__FILE__, __LINE__,
1240				"iscsi_logout_rsp_encap() failed\n");
1241		return -1;
1242	}
1243	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
1244			ISCSI_HEADER_LEN) {
1245		iscsi_err(__FILE__, __LINE__,
1246			"iscsi_sock_msg() failed\n");
1247		return -1;
1248	}
1249	iscsi_trace(TRACE_ISCSI_DEBUG, "sent logout response OK\n");
1250
1251	/* log information to stdout */
1252	(void) snprintf(logbuf, sizeof(logbuf),
1253		"< iSCSI %s logout successful from %s on %s "
1254		"disk %d, ISID %" PRIu64 ", TSIH %u",
1255		param_val(sess->params, "SessionType"),
1256		param_val(sess->params, "InitiatorName"),
1257		sess->initiator,
1258		sess->d,
1259		sess->isid,
1260		sess->tsih);
1261	printf("%s\n", logbuf);
1262#ifdef HAVE_SYSLOG
1263	/* log information to syslog */
1264	syslog(LOG_INFO, "%s", logbuf);
1265#endif
1266
1267	sess->IsLoggedIn = 0;
1268
1269	if (sess->sess_params.cred.user) {
1270		free(sess->sess_params.cred.user);
1271		sess->sess_params.cred.user = NULL;
1272	}
1273
1274	if ((i = find_target_tsih(sess->target, sess->tsih)) < 0) {
1275		iscsi_err(__FILE__, __LINE__,
1276			"logout sess->tsih %d not found\n", sess->tsih);
1277	} else {
1278		targv = sess->target->lunv;
1279		targv->v[i].tsih = 0;
1280	}
1281	sess->tsih = 0;
1282
1283	return 0;
1284}
1285
1286static int
1287verify_cmd_t(target_session_t * sess, uint8_t *header)
1288{
1289	int             op = ISCSI_OPCODE(header);
1290
1291	if ((!sess->LoginStarted) && (op != ISCSI_LOGIN_CMD)) {
1292		/* Terminate the connection */
1293		iscsi_err(__FILE__, __LINE__,
1294			"session %d: iSCSI op %#x attempted "
1295			"before LOGIN PHASE\n",
1296			    sess->id, op);
1297		return -1;
1298	}
1299	if (!sess->IsFullFeature &&
1300	    ((op != ISCSI_LOGIN_CMD) && (op != ISCSI_LOGOUT_CMD))) {
1301		iscsi_login_rsp_args_t rsp;
1302		uint8_t   rsp_header[ISCSI_HEADER_LEN];
1303		iscsi_err(__FILE__, __LINE__,
1304			"session %d: iSCSI op %#x before FULL FEATURE\n",
1305			sess->id, op);
1306		/* Create Login Reject response */
1307		(void) memset(&rsp, 0x0, sizeof(rsp));
1308		rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
1309		rsp.status_detail = ISCSI_LOGIN_DETAIL_NOT_LOGGED_IN;
1310		rsp.version_max = ISCSI_VERSION;
1311		rsp.version_active = ISCSI_VERSION;
1312
1313		if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
1314			iscsi_err(__FILE__, __LINE__,
1315				"iscsi_login_rsp_encap() failed\n");
1316			return -1;
1317		}
1318		iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
1319		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock,
1320				rsp_header, ISCSI_HEADER_LEN, NULL, 0, 0) !=
1321				ISCSI_HEADER_LEN + rsp.length) {
1322			iscsi_err(__FILE__, __LINE__,
1323				"iscsi_sock_send_header_and_data() failed\n");
1324			return -1;
1325		}
1326		iscsi_trace(TRACE_ISCSI_DEBUG, "sent login response ok\n");
1327		return -1;
1328	}
1329	return 0;
1330}
1331
1332/*
1333 * this function looks at the opcode in the received header for the session,
1334 * and does a switch on the opcode to call the required function.
1335 */
1336static int
1337execute_t(target_session_t *sess, uint8_t *header)
1338{
1339	int             op = ISCSI_OPCODE(header);
1340
1341	if (verify_cmd_t(sess, header) != 0) {
1342		return -1;
1343	}
1344	switch (op) {
1345	case ISCSI_TASK_CMD:
1346		iscsi_trace(TRACE_ISCSI_CMD,
1347				"session %d: Task Command\n", sess->id);
1348		if (task_command_t(sess, header) != 0) {
1349			iscsi_err(__FILE__, __LINE__,
1350				"task_command_t() failed\n");
1351			return -1;
1352		}
1353		break;
1354
1355	case ISCSI_NOP_OUT:
1356		iscsi_trace(TRACE_ISCSI_CMD, "session %d: NOP-Out\n", sess->id);
1357		if (nop_out_t(sess, header) != 0) {
1358			iscsi_err(__FILE__, __LINE__,
1359				"nop_out_t() failed\n");
1360			return -1;
1361		}
1362		break;
1363
1364	case ISCSI_LOGIN_CMD:
1365		iscsi_trace(TRACE_ISCSI_CMD,
1366				"session %d: Login Command\n", sess->id);
1367		if (login_command_t(sess, header) != 0) {
1368			iscsi_err(__FILE__, __LINE__,
1369				"login_command_t() failed\n");
1370			return -1;
1371		}
1372		break;
1373
1374	case ISCSI_TEXT_CMD:
1375		iscsi_trace(TRACE_ISCSI_CMD,
1376				"session %d: Text Command\n", sess->id);
1377		if (text_command_t(sess, header) != 0) {
1378			iscsi_err(__FILE__, __LINE__,
1379				"text_command_t() failed\n");
1380			return -1;
1381		}
1382		break;
1383
1384	case ISCSI_LOGOUT_CMD:
1385		iscsi_trace(TRACE_ISCSI_CMD,
1386			"session %d: Logout Command\n", sess->id);
1387		if (logout_command_t(sess, header) != 0) {
1388			iscsi_err(__FILE__, __LINE__,
1389				"logout_command_t() failed\n");
1390			return -1;
1391		}
1392		break;
1393
1394	case ISCSI_SCSI_CMD:
1395		iscsi_trace(TRACE_ISCSI_CMD,
1396			"session %d: SCSI Command\n", sess->id);
1397		if (scsi_command_t(sess, header) != 0) {
1398			iscsi_err(__FILE__, __LINE__,
1399				"scsi_command_t() failed\n");
1400			return -1;
1401		}
1402		break;
1403
1404	default:
1405		iscsi_err(__FILE__, __LINE__, "Unknown Opcode %#x\n",
1406			ISCSI_OPCODE(header));
1407		if (reject_t(sess, header, 0x04) != 0) {
1408			iscsi_err(__FILE__, __LINE__,
1409				"reject_t() failed\n");
1410			return -1;
1411		}
1412		break;
1413	}
1414	return 0;
1415}
1416
1417/*
1418 * Currently one thread per session, used for both Rx and Tx.
1419 */
1420static int
1421worker_proc_t(void *arg)
1422{
1423	target_session_t *sess = (target_session_t *) arg;
1424	uint8_t   header[ISCSI_HEADER_LEN];
1425	iscsi_parameter_t **l = &sess->params;
1426
1427	ISCSI_THREAD_START("worker_thread");
1428	sess->worker.pid = getpid();
1429	sess->worker.state |= ISCSI_WORKER_STATE_STARTED;
1430	iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: started\n", sess->id);
1431
1432	/*
1433         * ISCSI_PARAM_TYPE_LIST format:        <type> <key> <dflt> <valid list values>
1434         * ISCSI_PARAM_TYPE_BINARY format:      <type> <key> <dflt> <valid binary values>
1435         * ISCSI_PARAM_TYPE_NUMERICAL format:   <type> <key> <dflt> <max>
1436         * ISCSI_PARAM_TYPE_DECLARATIVE format: <type> <key> <dflt> ""
1437         */
1438
1439	sess->params = NULL;
1440	l = &sess->params;
1441
1442	/* CHAP Parameters */
1443	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthMethod", "CHAP", "CHAP,None", return -1);
1444	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "CHAP_A", "None", "5", return -1);
1445	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_N", "", "", return -1);
1446	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_R", "", "", return -1);
1447	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_I", "", "", return -1);
1448	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_C", "", "", return -1);
1449	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetPortalGroupTag", "1", "1", return -1);
1450	/* CHAP Parameters */
1451	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "HeaderDigest", "None", "None", return -1);
1452	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "DataDigest", "None", "None", return -1);
1453	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxConnections", "1", "1", return -1);
1454	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SendTargets", "", "", return -1);
1455	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetName", "", "", return -1);
1456	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorName", "", "", return -1);
1457	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAlias", "", "", return -1);
1458	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorAlias", "", "", return -1);
1459	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAddress", "", "", return -1);
1460	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "InitialR2T", "Yes", "Yes,No", return -1);
1461	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "OFMarker", "No", "Yes,No", return -1);
1462	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "IFMarker", "No", "Yes,No", return -1);
1463	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "OFMarkInt", "1", "65536", return -1);
1464	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "IFMarkInt", "1", "65536", return -1);
1465	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "ImmediateData", "Yes", "Yes,No", return -1);
1466	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxRecvDataSegmentLength", "8192", "16777215", return -1);
1467	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxBurstLength", "262144", "16777215", return -1);
1468	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "FirstBurstLength", "65536", "16777215", return -1);
1469	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Wait", "2", "2", return -1);
1470	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Retain", "20", "20", return -1);
1471	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxOutstandingR2T", "1", "1", return -1);
1472	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataPDUInOrder", "Yes", "Yes,No", return -1);
1473	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataSequenceInOrder", "Yes", "Yes,No", return -1);
1474	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "ErrorRecoveryLevel", "0", "0", return -1);
1475	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SessionType", "Normal", "Normal,Discovery", return -1);
1476	/*
1477	 * Auth Result is not in specs, we use this key to pass
1478	 * authentication result
1479	 */
1480	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthResult", "No", "Yes,No,Fail", return -1);
1481
1482	/* Set remaining session parameters  */
1483
1484	sess->UsePhaseCollapsedRead = ISCSI_USE_PHASE_COLLAPSED_READ_DFLT;
1485
1486	/* Loop for commands */
1487
1488	while (sess->target->state != TARGET_SHUT_DOWN) {
1489		iscsi_trace(TRACE_ISCSI_DEBUG,
1490			"session %d: reading header\n", sess->id);
1491		if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0)
1492				!= ISCSI_HEADER_LEN) {
1493			iscsi_trace(TRACE_ISCSI_DEBUG,
1494				"session %d: iscsi_sock_msg() failed\n",
1495				sess->id);
1496			break;
1497		}
1498		iscsi_trace(TRACE_ISCSI_DEBUG,
1499			"session %d: iscsi op %#x\n", sess->id,
1500			ISCSI_OPCODE(header));
1501		if (execute_t(sess, header) != 0) {
1502			iscsi_err(__FILE__, __LINE__,
1503				"execute_t() failed\n");
1504			break;
1505		}
1506		iscsi_trace(TRACE_ISCSI_DEBUG,
1507			"session %d: iscsi op %#x complete\n", sess->id,
1508			ISCSI_OPCODE(header));
1509		if (ISCSI_OPCODE(header) == ISCSI_LOGOUT_CMD) {
1510			iscsi_trace(TRACE_ISCSI_DEBUG,
1511				"session %d: logout received, ending session\n",
1512				sess->id);
1513			break;
1514		}
1515	}
1516
1517	/* Clean up */
1518
1519	iscsi_free(sess->buff);
1520	if (param_list_destroy(sess->params) != 0) {
1521		iscsi_err(__FILE__, __LINE__,
1522			"param_list_destroy() failed\n");
1523		return -1;
1524	}
1525	/* Terminate connection */
1526
1527	if (iscsi_sock_close(sess->sock) != 0) {
1528		iscsi_err(__FILE__, __LINE__,
1529			"iscsi_sock_close() failed\n");
1530	}
1531	/* Make session available */
1532
1533	ISCSI_LOCK(&g_session_q_mutex, return -1);
1534	(void) memset(sess, 0x0, sizeof(*sess));
1535	sess->d = -1;
1536	if (iscsi_queue_insert(&g_session_q, sess) != 0) {
1537		iscsi_err(__FILE__, __LINE__,
1538				"iscsi_queue_insert() failed\n");
1539		return -1;
1540	}
1541	ISCSI_UNLOCK(&g_session_q_mutex, return -1);
1542	iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: ended\n", sess->id);
1543
1544	return 0;
1545}
1546
1547static int
1548read_data_pdu(target_session_t * sess,
1549	      iscsi_write_data_t * data,
1550	      iscsi_scsi_cmd_args_t * args)
1551{
1552	uint8_t   header[ISCSI_HEADER_LEN];
1553	int             ret_val = -1;
1554
1555	if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) !=
1556			ISCSI_HEADER_LEN) {
1557		iscsi_err(__FILE__, __LINE__,
1558			"iscsi_sock_msg() failed\n");
1559		return -1;
1560	}
1561	if ((ret_val = iscsi_write_data_decap(header, data)) != 0) {
1562		iscsi_err(__FILE__, __LINE__,
1563			"iscsi_write_data_decap() failed\n");
1564		return ret_val;
1565	}
1566	/* Check args */
1567	if (sess->sess_params.max_dataseg_len) {
1568		if (data->length > sess->sess_params.max_dataseg_len) {
1569			args->status = 0x02;
1570			return -1;
1571		}
1572	}
1573	if ((args->bytes_recv + data->length) > args->trans_len) {
1574		args->status = 0x02;
1575		return -1;
1576	}
1577	if (data->tag != args->tag) {
1578		iscsi_trace(TRACE_ISCSI_DEBUG,
1579			"Data ITT (%d) does not match with command ITT (%d)\n",
1580			data->tag, args->tag);
1581		if (data->final) {
1582			args->status = 0x02;
1583			return -1;
1584		} else {
1585			/* Send a reject PDU */
1586			iscsi_trace(TRACE_ISCSI_DEBUG, "Sending Reject PDU\n");
1587			if (reject_t(sess, header, 0x09) != 0) {
1588				/* Invalid PDU Field */
1589				iscsi_trace(TRACE_ISCSI_DEBUG,
1590					"Sending Reject PDU failed\n");
1591				return 1;
1592			}
1593		}
1594	}
1595	return 0;
1596}
1597
1598int
1599target_transfer_data(target_session_t * sess, iscsi_scsi_cmd_args_t * args,
1600		struct iovec * sg, int sg_len)
1601{
1602	iscsi_write_data_t data;
1603	struct iovec   *iov, *iov_ptr = NULL;
1604	int             iov_len;
1605
1606#define TTD_CLEANUP do {						\
1607	if (iov_ptr != NULL) {						\
1608		iscsi_free_atomic(iov_ptr);				\
1609	}								\
1610} while (/* CONSTCOND */ 0)
1611
1612	args->bytes_recv = 0;
1613	if ((!sess->sess_params.immediate_data) && args->length) {
1614		iscsi_trace(TRACE_ISCSI_DEBUG,
1615			"Cannot accept any Immediate data\n");
1616		args->status = 0x02;
1617		return -1;
1618	}
1619
1620	/* Make a copy of the iovec */
1621	iov_ptr = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len);
1622	if (iov_ptr == NULL) {
1623		iscsi_err(__FILE__, __LINE__,
1624				"iscsi_malloc_atomic() failed\n");
1625		return -1;
1626	}
1627	iov = iov_ptr;
1628	(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
1629	iov_len = sg_len;
1630
1631	/*
1632         * Read any immediate data.
1633         */
1634
1635	if (sess->sess_params.immediate_data && args->length) {
1636		if (sess->sess_params.max_dataseg_len &&
1637		    args->length > sess->sess_params.max_dataseg_len) {
1638			iscsi_err(__FILE__, __LINE__,
1639				"args->length (%u) too long\n",
1640				args->length);
1641				TTD_CLEANUP;
1642				return -1;
1643		}
1644
1645		/* Modify iov to include just immediate data */
1646		if (modify_iov(&iov, &iov_len, 0, args->length) != 0) {
1647			iscsi_err(__FILE__, __LINE__,
1648				"modify_iov() failed\n");
1649			TTD_CLEANUP;
1650			return -1;
1651		}
1652		iscsi_trace(TRACE_SCSI_DATA,
1653			"reading %u bytes immediate write data\n",
1654			args->length);
1655		if ((uint32_t)iscsi_sock_msg(sess->sock, 0, args->length, iov,
1656				iov_len) != args->length) {
1657			iscsi_err(__FILE__, __LINE__,
1658				"iscsi_sock_msg() failed\n");
1659			TTD_CLEANUP;
1660			return -1;
1661		}
1662		iscsi_trace(TRACE_SCSI_DATA,
1663			"successfully read %u bytes immediate write data\n",
1664			args->length);
1665		args->bytes_recv += args->length;
1666	}
1667
1668	/*
1669         * Read iSCSI data PDUs
1670         */
1671	if (args->bytes_recv < args->trans_len) {
1672		int             r2t_flag = 0;
1673		int             read_status = 0;
1674		iscsi_r2t_t     r2t;
1675		int             desired_xfer_len;
1676
1677		desired_xfer_len = MIN(sess->sess_params.first_burst_length,
1678				      args->trans_len) - args->bytes_recv;
1679		(void) memset(&r2t, 0x0, sizeof(r2t));
1680		do {
1681
1682			/*
1683			 * Send R2T if we're either operating in solicted
1684			 * mode or we're operating in unsolicted
1685			 */
1686			/* mode and have reached the first burst */
1687			if (!r2t_flag &&
1688			     (sess->sess_params.initial_r2t ||
1689			        (sess->sess_params.first_burst_length &&
1690				(args->bytes_recv >=
1691				sess->sess_params.first_burst_length)))) {
1692				uint8_t   header[ISCSI_HEADER_LEN];
1693
1694				desired_xfer_len = MIN(args->trans_len -
1695							args->bytes_recv,
1696					sess->sess_params.max_burst_length);
1697				iscsi_trace(TRACE_ISCSI_DEBUG,
1698					"sending R2T for %u bytes data\n",
1699					desired_xfer_len);
1700				r2t.tag = args->tag;
1701
1702				r2t.transfer_tag = 0x1234;
1703
1704				r2t.ExpCmdSN = sess->ExpCmdSN;
1705				r2t.MaxCmdSN = sess->MaxCmdSN;
1706				r2t.StatSN = ++(sess->StatSN);
1707				r2t.length = desired_xfer_len;
1708				r2t.offset = args->bytes_recv;
1709				if (iscsi_r2t_encap(header, &r2t) != 0) {
1710					iscsi_err(__FILE__, __LINE__,
1711						"r2t_encap() failed\n");
1712					TTD_CLEANUP;
1713					return -1;
1714				}
1715				iscsi_trace(TRACE_ISCSI_DEBUG,
1716					"sending R2T tag %u transfer tag "
1717					"%u len %u offset %u\n",
1718					r2t.tag, r2t.transfer_tag, r2t.length,
1719					r2t.offset);
1720				if (iscsi_sock_msg(sess->sock, 1,
1721					ISCSI_HEADER_LEN, header, 0) !=
1722					ISCSI_HEADER_LEN) {
1723					iscsi_err(__FILE__, __LINE__,
1724						"iscsi_sock_msg() failed\n");
1725					TTD_CLEANUP;
1726					return -1;
1727				}
1728				r2t_flag = 1;
1729				r2t.R2TSN += 1;
1730			}
1731
1732			/* Read iSCSI data PDU */
1733			iscsi_trace(TRACE_ISCSI_DEBUG, "reading data pdu\n");
1734			read_status = read_data_pdu(sess, &data, args);
1735			if (read_status != 0) {
1736				if (read_status == 1) {
1737					iscsi_trace(TRACE_ISCSI_DEBUG,
1738						"Unknown PDU received and "
1739						"ignored. Expecting "
1740						"Data PDU\n");
1741					continue;
1742				} else {
1743					iscsi_err(__FILE__, __LINE__,
1744						"read_data_pdu() failed\n");
1745					args->status = 0x02;
1746					TTD_CLEANUP;
1747					return -1;
1748				}
1749			}
1750			if (data.ExpStatSN != sess->StatSN) {
1751				iscsi_warn(__FILE__, __LINE__,
1752					"Bad \"ExpStatSN\": Got %u "
1753					"expected %u.\n",
1754					data.ExpStatSN, sess->StatSN);
1755			}
1756			iscsi_trace(TRACE_ISCSI_DEBUG,
1757				"read data pdu OK (offset %u, length %u)\n",
1758				data.offset, data.length);
1759
1760			/* Modify iov with offset and length. */
1761			iov = iov_ptr;
1762			(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
1763			iov_len = sg_len;
1764			if (modify_iov(&iov, &iov_len, data.offset,
1765					data.length) != 0) {
1766				iscsi_err(__FILE__, __LINE__,
1767					"modify_iov() failed\n");
1768				TTD_CLEANUP;
1769				return -1;
1770			}
1771
1772			/* Scatter into destination buffers */
1773			if ((uint32_t)iscsi_sock_msg(sess->sock, 0,
1774				data.length, iov, iov_len) != data.length) {
1775				iscsi_err(__FILE__, __LINE__,
1776					"iscsi_sock_msg() failed\n");
1777				TTD_CLEANUP;
1778				return -1;
1779			}
1780			iscsi_trace(TRACE_ISCSI_DEBUG,
1781				"successfully scattered %u bytes\n",
1782				data.length);
1783			args->bytes_recv += data.length;
1784			desired_xfer_len -= data.length;
1785			if ((!r2t_flag) &&
1786			    (args->bytes_recv >
1787			    	sess->sess_params.first_burst_length)) {
1788				iscsi_err(__FILE__, __LINE__,
1789					"Received unsolicited data (%u) "
1790					"more than first_burst_length (%u)\n",
1791					args->bytes_recv,
1792					sess->sess_params.first_burst_length);
1793				args->status = 0x02;
1794				TTD_CLEANUP;
1795				return -1;
1796			}
1797			if ((desired_xfer_len != 0) && data.final) {
1798				iscsi_err(__FILE__, __LINE__,
1799					"Expecting more data (%d) "
1800					"from initiator for this sequence\n",
1801					desired_xfer_len);
1802				args->status = 0x02;
1803				TTD_CLEANUP;
1804				return -1;
1805			}
1806			if ((desired_xfer_len == 0) && !data.final) {
1807				iscsi_err(__FILE__, __LINE__,
1808					"Final bit not set on the last "
1809					"data PDU of this sequence\n");
1810				args->status = 0x02;
1811				TTD_CLEANUP;
1812				return -1;
1813			}
1814			if ((desired_xfer_len == 0) &&
1815			    (args->bytes_recv < args->trans_len)) {
1816				r2t_flag = 0;
1817			}
1818		} while (args->bytes_recv < args->trans_len);
1819#if 0
1820		RETURN_NOT_EQUAL("Final bit", data.final, 1, TTD_CLEANUP, -1);
1821#else
1822		if (data.final != 1) {
1823			iscsi_err(__FILE__, __LINE__, "Final bit\n");
1824			TTD_CLEANUP;
1825			return -1;
1826		}
1827#endif
1828	} else {
1829#if 0
1830		RETURN_NOT_EQUAL("Final bit", args->final, 1, TTD_CLEANUP, -1);
1831#else
1832		if (args->final != 1) {
1833			iscsi_err(__FILE__, __LINE__, "Final bit\n");
1834			TTD_CLEANUP;
1835			return -1;
1836		}
1837#endif
1838	}
1839	iscsi_trace(TRACE_ISCSI_DEBUG,
1840		"successfully transferred %u bytes write data\n",
1841		args->trans_len);
1842	TTD_CLEANUP;
1843	return 0;
1844}
1845
1846/* check there's enough space in the arrays */
1847static void
1848size_arrays(iscsi_target_t *tgt, unsigned needed)
1849{
1850	if (tgt->size == 0) {
1851		/* only get here first time around */
1852		tgt->size = needed;
1853		tgt->name = calloc(sizeof(char *), needed);
1854		tgt->value = calloc(sizeof(char *), needed);
1855	} else if (tgt->c == tgt->size) {
1856		/* only uses 'needed' when filled array */
1857		tgt->size += needed;
1858		tgt->name = realloc(tgt->name, sizeof(char *) * needed);
1859		tgt->value = realloc(tgt->value, sizeof(char *) * needed);
1860	}
1861}
1862
1863/* find the name in the array */
1864static int
1865findvar(iscsi_target_t *tgt, const char *name)
1866{
1867	unsigned	i;
1868
1869	for (i = 0 ; i < tgt->c && strcmp(tgt->name[i], name) != 0; i++) {
1870	}
1871	return (i == tgt->c) ? -1 : (int)i;
1872}
1873
1874/********************
1875 * Public Functions *
1876 ********************/
1877
1878int
1879iscsi_target_set_defaults(iscsi_target_t *tgt)
1880{
1881	char	buf[32];
1882
1883	/* set defaults */
1884	(void) memset(tgt, 0x0, sizeof(*tgt));
1885	iscsi_target_setvar(tgt, "iqn", DEFAULT_TARGET_NAME);
1886	(void) snprintf(buf, sizeof(buf), "%d", ISCSI_PORT);
1887	iscsi_target_setvar(tgt, "target port", buf);
1888	iscsi_target_setvar(tgt, "address family", "unspec");
1889	(void) snprintf(buf, sizeof(buf), "%d", DEFAULT_TARGET_MAX_SESSIONS);
1890	iscsi_target_setvar(tgt, "max sessions", buf);
1891	iscsi_target_setvar(tgt, "configfile", _PATH_ISCSI_TARGETS);
1892	iscsi_target_setvar(tgt, "blocklen", "512");
1893	return 1;
1894}
1895
1896/* re-read the configuration file */
1897int
1898iscsi_target_reconfigure(iscsi_target_t *tgt)
1899{
1900	targv_t	*oldluns;
1901	devv_t	*olddevices;
1902	extv_t	*oldextents;
1903	targv_t	*luns;
1904	devv_t	*devices;
1905	extv_t	*extents;
1906	char	*config;
1907
1908	NEW(targv_t, luns, "iscsi_target_reconf 1", return -1);
1909	NEW(devv_t, devices, "iscsi_target_reconf 2", return -1);
1910	NEW(extv_t, extents, "iscsi_target_reconf 3", return -1);
1911	config = iscsi_target_getvar(tgt, "configfile");
1912	if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
1913		(void) fprintf(stderr, "Error: can't open `%s'\n", config);
1914		return 0;
1915	}
1916	/* it worked - let's reassign things */
1917	/* XXX - agc - lock */
1918	oldluns = tgt->lunv;
1919	olddevices = tgt->devv;
1920	oldextents = tgt->extentv;
1921	tgt->lunv = luns;
1922	tgt->devv = devices;
1923	tgt->extentv = extents;
1924	/* XXX - agc - unlock */
1925	/* free up storage */
1926	(void) free(oldluns);
1927	(void) free(olddevices);
1928	(void) free(oldextents);
1929	return 1;
1930}
1931
1932int
1933iscsi_target_start(iscsi_target_t *tgt)
1934{
1935	uint32_t	 j;
1936	targv_t		*lunv;
1937	char		*config;
1938	char		*dbg;
1939	int		 maxsessions;
1940	int              i;
1941
1942	if ((dbg = iscsi_target_getvar(tgt, "debug")) != NULL) {
1943		set_debug(dbg);
1944	}
1945	/* allocate space for disks, extents and targets */
1946	NEW(targv_t, tgt->lunv, "iscsi_target_start 1", return -1);
1947	NEW(devv_t, tgt->devv, "iscsi_target_start 2", return -1);
1948	NEW(extv_t, tgt->extentv, "iscsi_target_start 3", return -1);
1949	/* read the configuration file */
1950	config = iscsi_target_getvar(tgt, "configfile");
1951	if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
1952		(void) fprintf(stderr, "Error: can't open `%s'\n", config);
1953		return 0;
1954	}
1955	lunv = tgt->lunv;
1956	if (lunv->c == 0) {
1957		(void) fprintf(stderr, "No targets to initialise\n");
1958		return -1;
1959	}
1960	maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
1961	NEWARRAY(target_session_t, g_session, maxsessions, "iscsi_target_start",
1962			return -1);
1963	device_set_var("blocklen", iscsi_target_getvar(tgt, "blocklen"));
1964	if (tgt->state == TARGET_INITIALIZING ||
1965	    tgt->state == TARGET_INITIALIZED) {
1966		iscsi_err(__FILE__, __LINE__,
1967			"duplicate target initialization attempted\n");
1968		return -1;
1969	}
1970	tgt->state = TARGET_INITIALIZING;
1971	if (iscsi_queue_init(&g_session_q, maxsessions) != 0) {
1972		iscsi_err(__FILE__, __LINE__,
1973			"iscsi_queue_init() failed\n");
1974		return -1;
1975	}
1976	tgt->main_pid = getpid();
1977	for (i = 0; i < maxsessions; i++) {
1978		g_session[i].id = i;
1979		g_session[i].d = -1;
1980		if (iscsi_queue_insert(&g_session_q, &g_session[i]) != 0) {
1981			iscsi_err(__FILE__, __LINE__,
1982				"iscsi_queue_insert() failed\n");
1983			return -1;
1984		}
1985	}
1986	for (j = 0 ; j < lunv->c ; j++) {
1987		int d = device_init(tgt, lunv, &lunv->v[j]);
1988
1989		if (d < 0) {
1990			iscsi_err(__FILE__, __LINE__,
1991				"device_init() failed\n");
1992			return -1;
1993		}
1994	}
1995	ISCSI_MUTEX_INIT(&g_session_q_mutex, return -1);
1996	tgt->listener_listening = 0;
1997	tgt->listener_pid = -1;
1998	tgt->state = TARGET_INITIALIZED;
1999	printf("TARGET: iSCSI Qualified Name (IQN) is %s\n",
2000			iscsi_target_getvar(tgt, "iqn"));
2001	for (i = 0 ; i < tgt->sockc ; i++) {
2002		printf("\tsocket %d listening on port %s\n", tgt->sockv[i],
2003			iscsi_target_getvar(tgt, "target port"));
2004	}
2005	return 0;
2006}
2007
2008int
2009iscsi_target_shutdown(iscsi_target_t *tgt)
2010{
2011	target_session_t	*sess;
2012	int			 maxsessions;
2013	int			 i;
2014
2015	if ((tgt->state == TARGET_SHUTTING_DOWN) ||
2016	    (tgt->state == TARGET_SHUT_DOWN)) {
2017		iscsi_err(__FILE__, __LINE__,
2018			"duplicate target shutdown attempted\n");
2019		return -1;
2020	}
2021	tgt->state = TARGET_SHUTTING_DOWN;
2022	iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down target\n");
2023	maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
2024	for (i = 0; i < maxsessions; i++) {
2025		sess = &g_session[i];
2026
2027		/* Need to replace with a call to session_destroy() */
2028
2029		if (sess->IsLoggedIn) {
2030			printf("shutting down socket on sess %d\n", i);
2031			iscsi_trace(TRACE_ISCSI_DEBUG,
2032				"shutting down socket on sess %d\n", i);
2033			if (iscsi_sock_shutdown(sess->sock, 2) != 0) {
2034				iscsi_err(__FILE__, __LINE__,
2035					"iscsi_sock_shutdown() failed\n");
2036				return -1;
2037			}
2038			printf("waiting for worker %d (pid %d, state %d)\n",
2039				i, sess->worker.pid, sess->worker.state);
2040			iscsi_trace(TRACE_ISCSI_DEBUG,
2041				"waiting for worker %d (pid %d, state %d)\n",
2042				i, sess->worker.pid, sess->worker.state);
2043			while (sess->worker.state &
2044					ISCSI_WORKER_STATE_STARTED) {
2045				ISCSI_SPIN;
2046			}
2047			iscsi_trace(TRACE_ISCSI_DEBUG,
2048					"worker %d has exited\n", i);
2049		}
2050		if (device_shutdown(sess) != 0) {
2051			iscsi_err(__FILE__, __LINE__,
2052					"device_shutdown() failed\n");
2053			return -1;
2054		}
2055	}
2056	iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down accept socket\n");
2057	if (iscsi_sock_shutdown(tgt->sockv[0], 2) != 0) {
2058		iscsi_err(__FILE__, __LINE__,
2059			"iscsi_sock_shutdown() failed\n");
2060		return -1;
2061	}
2062	if (tgt->listener_pid != getpid()) {
2063		iscsi_trace(TRACE_ISCSI_DEBUG, "waiting for listener thread\n");
2064		while (tgt->listener_listening) {
2065			ISCSI_SPIN;
2066		}
2067		iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread has exited\n");
2068	}
2069	iscsi_trace(TRACE_ISCSI_DEBUG, "closing accept socket\n");
2070	if (iscsi_sock_close(tgt->sockv[0]) != 0) {
2071		iscsi_err(__FILE__, __LINE__,
2072			"iscsi_sock_close() failed\n");
2073		return -1;
2074	}
2075	ISCSI_MUTEX_DESTROY(&g_session_q_mutex, return -1);
2076	iscsi_trace(TRACE_ISCSI_DEBUG, "target shutdown complete\n");
2077	tgt->state = TARGET_SHUT_DOWN;
2078
2079	return 0;
2080}
2081
2082int
2083iscsi_target_listen(iscsi_target_t *tgt)
2084{
2085	struct sockaddr_in6	remoteAddrStorage6;
2086	struct sockaddr_in6	localAddrStorage6;
2087	struct sockaddr_in	remoteAddrStorage;
2088	struct sockaddr_in	localAddrStorage;
2089	target_session_t	*sess;
2090	socklen_t		remoteAddrLen;
2091	socklen_t		localAddrLen;
2092	char			targetaddress[2 * 1024];
2093	char			remote[1024];
2094	char			local[1024];
2095	char			*config;
2096	int			newconn;
2097	int			i;
2098
2099	ISCSI_THREAD_START("listen_thread");
2100	tgt->listener_pid = getpid();
2101	tgt->listener_listening++;
2102	iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread started\n");
2103
2104	if (!iscsi_socks_establish(tgt->sockv, tgt->famv, &tgt->sockc,
2105			iscsi_target_getvar(tgt, "address family"),
2106			atoi(iscsi_target_getvar(tgt, "target port")))) {
2107		iscsi_err(__FILE__, __LINE__,
2108				"iscsi_sock_establish() failed\n");
2109		goto done;
2110	}
2111
2112	iscsi_trace(TRACE_NET_DEBUG, "create, bind, listen OK\n");
2113
2114	/* Loop for connections: FIX ME with queue */
2115
2116	while (tgt->state != TARGET_SHUT_DOWN) {
2117		ISCSI_LOCK(&g_session_q_mutex, return -1);
2118		if ((sess = iscsi_queue_remove(&g_session_q)) == NULL) {
2119			iscsi_err(__FILE__, __LINE__,
2120			"no free sessions: iscsi_queue_remove() failed\n");
2121			goto done;
2122		}
2123		ISCSI_UNLOCK(&g_session_q_mutex, return -1);
2124		assert(sess->d == -1);
2125#if 0
2126		(void) memset(sess, 0x0, sizeof(*sess));
2127#endif
2128
2129		sess->target = tgt;
2130
2131		/* Accept connection, spawn session thread, and */
2132		/* clean up old threads */
2133
2134		config = iscsi_target_getvar(tgt, "configfile");
2135		i = iscsi_waitfor_connection(tgt->sockv, tgt->sockc, config,
2136				&newconn);
2137
2138		iscsi_trace(TRACE_NET_DEBUG,
2139				"waiting for %s connection on port %s\n",
2140				iscsi_address_family(tgt->famv[i]),
2141				iscsi_target_getvar(tgt, "target port"));
2142
2143		if (!iscsi_sock_accept(newconn, &sess->sock)) {
2144			iscsi_trace(TRACE_ISCSI_DEBUG,
2145					"iscsi_sock_accept() failed\n");
2146			goto done;
2147		}
2148
2149		switch (tgt->famv[i]) {
2150		case AF_INET:
2151			sess->address_family = 4;
2152			(void) memset(&localAddrStorage, 0x0,
2153				localAddrLen = sizeof(localAddrStorage));
2154			if (getsockname(sess->sock,
2155				(struct sockaddr *)(void *)&localAddrStorage,
2156				&localAddrLen) < 0) {
2157				iscsi_err(__FILE__, __LINE__,
2158					"iscsi_sock_getsockname() failed\n");
2159				goto done;
2160			}
2161			(void) memset(&remoteAddrStorage, 0x0,
2162				remoteAddrLen = sizeof(remoteAddrStorage));
2163			if (getpeername(sess->sock,
2164				(struct sockaddr *)(void *) &remoteAddrStorage,
2165				&remoteAddrLen) < 0) {
2166				iscsi_err(__FILE__, __LINE__,
2167					"iscsi_sock_getpeername() failed\n");
2168				goto done;
2169			}
2170
2171#ifdef HAVE_GETNAMEINFO
2172			if (getnameinfo((struct sockaddr *)(void *)
2173				&localAddrStorage,
2174				sizeof(localAddrStorage), local,
2175				sizeof(local), NULL, 0, NI_NUMERICHOST) < 0) {
2176				iscsi_err(__FILE__, __LINE__,
2177					"getnameinfo local failed\n");
2178			}
2179			if (getnameinfo((struct sockaddr *)(void *)
2180				&remoteAddrStorage,
2181				sizeof(remoteAddrStorage), remote,
2182				sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
2183				iscsi_err(__FILE__, __LINE__,
2184					"getnameinfo remote failed\n");
2185			}
2186			(void) strlcpy(sess->initiator, remote,
2187					sizeof(sess->initiator));
2188#else
2189			(void) strlcpy(local,
2190				inet_ntoa(localAddrStorage.sin_addr),
2191				sizeof(local));
2192			(void) strlcpy(sess->initiator,
2193				inet_ntoa(remoteAddrStorage.sin_addr),
2194				sizeof(sess->initiator));
2195#endif
2196
2197			(void) snprintf(targetaddress, sizeof(targetaddress),
2198				"%s:%s,1", local,
2199				iscsi_target_getvar(tgt, "target port"));
2200			iscsi_target_setvar(tgt, "target address",
2201				targetaddress);
2202			iscsi_trace(TRACE_ISCSI_DEBUG,
2203				"IPv4 connection accepted on port %s "
2204				"(local IP %s, remote IP %s)\n",
2205				iscsi_target_getvar(tgt, "target port"),
2206				local, sess->initiator);
2207			iscsi_trace(TRACE_ISCSI_DEBUG,
2208				"TargetAddress = \"%s\"\n", targetaddress);
2209			break;
2210
2211		case AF_INET6:
2212			sess->address_family = 6;
2213			(void) memset(&localAddrStorage6, 0x0,
2214				localAddrLen = sizeof(localAddrStorage6));
2215			if (getsockname(sess->sock, (struct sockaddr *)(void *)
2216				&localAddrStorage6, &localAddrLen) < 0) {
2217				iscsi_err(__FILE__, __LINE__,
2218					"getsockname() failed\n");
2219				goto done;
2220			}
2221
2222			(void) memset(&remoteAddrStorage6, 0x0,
2223				remoteAddrLen = sizeof(remoteAddrStorage6));
2224			if (getpeername(sess->sock, (struct sockaddr *)(void *)
2225				&remoteAddrStorage6, &remoteAddrLen) < 0) {
2226				iscsi_err(__FILE__, __LINE__,
2227					"iscsi_sock_getpeername() failed\n");
2228				goto done;
2229			}
2230
2231			if (getnameinfo((struct sockaddr *)(void *)
2232				&localAddrStorage6, sizeof(localAddrStorage6),
2233				local, sizeof(local), NULL, 0,
2234				NI_NUMERICHOST) < 0) {
2235				iscsi_err(__FILE__, __LINE__,
2236					"getnameinfo local failed\n");
2237			}
2238			if (getnameinfo((struct sockaddr *)(void *)
2239				&remoteAddrStorage6,
2240				sizeof(remoteAddrStorage6), remote,
2241				sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
2242				iscsi_err(__FILE__, __LINE__,
2243					"getnameinfo remote failed\n");
2244			}
2245			(void) strlcpy(sess->initiator, remote,
2246				sizeof(sess->initiator));
2247			(void) snprintf(targetaddress, sizeof(targetaddress),
2248				"%s:%s,1", local,
2249				iscsi_target_getvar(tgt, "target port"));
2250			iscsi_target_setvar(tgt, "target address",
2251				targetaddress);
2252			iscsi_trace(TRACE_ISCSI_DEBUG,
2253				"IPv6 connection accepted on port %s "
2254				"(local IP %s, remote IP %s)\n",
2255				iscsi_target_getvar(tgt, "target port"),
2256				local, sess->initiator);
2257			iscsi_trace(TRACE_ISCSI_DEBUG,
2258				"TargetAddress = \"%s\"\n", targetaddress);
2259			break;
2260		}
2261		if (iscsi_thread_create(&sess->worker.thread,
2262			(void *) worker_proc_t, sess) != 0) {
2263			iscsi_err(__FILE__, __LINE__,
2264				"iscsi_thread_create() failed\n");
2265			goto done;
2266		}
2267	}
2268done:
2269	tgt->listener_listening--;
2270	return 0;
2271}
2272
2273/* write the pid to the pid file */
2274void
2275iscsi_target_write_pidfile(const char *f)
2276{
2277	FILE	*fp;
2278
2279	if (f == NULL) {
2280		f = _PATH_ISCSI_PID_FILE;
2281	}
2282	if ((fp = fopen(f, "w")) == NULL) {
2283		(void) fprintf(stderr, "Couldn't create pid file \"%s\": %s",
2284				f, strerror(errno));
2285	} else {
2286		(void) fprintf(fp, "%ld\n", (long) getpid());
2287		(void) fclose(fp);
2288	}
2289}
2290
2291/* set a variable */
2292int
2293iscsi_target_setvar(iscsi_target_t *tgt, const char *name, const char *value)
2294{
2295	int	i;
2296
2297	if ((i = findvar(tgt, name)) < 0) {
2298		/* add the element to the array */
2299		size_arrays(tgt, tgt->size + 15);
2300		tgt->name[i = tgt->c++] = strdup(name);
2301	} else {
2302		/* replace the element in the array */
2303		if (tgt->value[i]) {
2304			(void) free(tgt->value[i]);
2305			tgt->value[i] = NULL;
2306		}
2307	}
2308	/* sanity checks for range of values would go here */
2309	tgt->value[i] = strdup(value);
2310	return 1;
2311}
2312
2313/* get a variable's value (NULL if not set) */
2314char *
2315iscsi_target_getvar(iscsi_target_t *tgt, const char *name)
2316{
2317	int	i;
2318
2319	return ((i = findvar(tgt, name)) < 0) ? NULL : tgt->value[i];
2320}
2321