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