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