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
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
24 * -The name of Intel Corporation may not be used to endorse or
25 * promote products derived from this software without specific prior
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
43 #include <sys/types.h>
44 #include <sys/param.h>
48 #ifdef HAVE_NETINET_TCP_H
49 #include <netinet/tcp.h>
56 #ifdef HAVE_SYS_SOCKET_H
57 #include <sys/socket.h>
60 #ifdef HAVE_NETINET_IN_H
61 #include <netinet/in.h>
84 #ifdef HAVE_ARPA_INET_H
85 #include <arpa/inet.h>
88 #ifdef HAVE_INTTYPES_H
93 #include "iscsiprotocol.h"
98 #include "iscsi-md5.h"
99 #include "parameters.h"
103 TARGET_SHUT_DOWN
= 0,
104 TARGET_INITIALIZING
= 1,
105 TARGET_INITIALIZED
= 2,
106 TARGET_SHUTTING_DOWN
= 3
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 *********************/
122 get_iqn(target_session_t
*sess
, uint32_t t
, char *buf
, size_t size
)
126 targv
= sess
->target
->lunv
;
127 if (targv
->v
[t
].iqn
!= NULL
) {
128 (void) strlcpy(buf
, targv
->v
[t
].iqn
, size
);
131 (void) snprintf(buf
, size
, "%s:%s",
132 iscsi_target_getvar(sess
->target
, "iqn"),
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");
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");
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
;
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");
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
);
198 scsi_cmd
.attr
= 0; /* Temp fix FIXME */
200 * RETURN_NOT_EQUAL("ATTR (FIX ME)", scsi_cmd.attr, 0, NO_CLEANUP,
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;
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 "
228 scsi_cmd
.length
, sess
->sess_params
.max_dataseg_len
);
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);
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
) {
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");
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");
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
));
277 iscsi_err(__FILE__
, __LINE__
,
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;
289 case ISCSI_AHS_BIDI_READ
:
290 scsi_cmd
.bidi_trans_len
=
291 ISCSI_NTOHL(*((uint32_t *)(void *)
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
);
301 iscsi_err(__FILE__
, __LINE__
,
302 "unknown AHS type %x\n", ahs_type
);
307 iscsi_trace(TRACE_ISCSI_DEBUG
,
308 "done parsing %u bytes AHS\n", scsi_cmd
.ahs_len
);
310 iscsi_trace(TRACE_ISCSI_DEBUG
, "no AHS to read\n");
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
;
325 cmd
.scsi_cmd
= &scsi_cmd
;
327 if (device_command(sess
, &cmd
) != 0) {
328 iscsi_err(__FILE__
, __LINE__
,
329 "device_command() failed\n");
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;
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
;
354 trans_len
= scsi_cmd
.trans_len
;
356 iscsi_trace(TRACE_ISCSI_DEBUG
,
357 "sending %u bytes input data as separate PDUs\n",
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
;
365 sg_singleton
.iov_base
= scsi_cmd
.send_data
;
366 sg_singleton
.iov_len
= trans_len
;
367 sg_orig
= &sg_singleton
;
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
) :
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");
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");
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
) {
405 if (sess
->UsePhaseCollapsedRead
) {
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
);
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");
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");
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");
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
);
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
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
) {
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");
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");
479 /* Make sure all data was transferred */
481 if (scsi_cmd
.output
) {
483 RETURN_NOT_EQUAL("scsi_cmd.bytes_recv", scsi_cmd
.bytes_recv
, scsi_cmd
.trans_len
, AHS_CLEANUP
, -1);
485 if (scsi_cmd
.bytes_recv
!= scsi_cmd
.trans_len
) {
486 iscsi_err(__FILE__
, __LINE__
,
487 "scsi_cmd.bytes_recv");
492 if (scsi_cmd
.input
) {
494 RETURN_NOT_EQUAL("scsi_cmd.bytes_sent", scsi_cmd
.bytes_sent
, scsi_cmd
.bidi_trans_len
, AHS_CLEANUP
, -1);
496 if (scsi_cmd
.bytes_sent
!=
497 scsi_cmd
.bidi_trans_len
) {
498 iscsi_err(__FILE__
, __LINE__
,
499 "scsi_cmd.bytes_sent");
506 if (scsi_cmd
.input
) {
508 RETURN_NOT_EQUAL("scsi_cmd.bytes_sent", scsi_cmd
.bytes_sent
, scsi_cmd
.trans_len
, AHS_CLEANUP
, -1);
510 if (scsi_cmd
.bytes_sent
!= scsi_cmd
.trans_len
) {
511 iscsi_err(__FILE__
, __LINE__
,
512 "scsi_cmd.bytes_sent");
521 /* Device callback after command has completed */
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");
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");
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
;
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");
565 case ISCSI_TASK_CMD_ABORT_TASK_SET
:
566 printf("ISCSI_TASK_CMD_ABORT_TASK_SET\n");
568 case ISCSI_TASK_CMD_CLEAR_ACA
:
569 printf("ISCSI_TASK_CMD_CLEAR_ACA\n");
571 case ISCSI_TASK_CMD_CLEAR_TASK_SET
:
572 printf("ISCSI_TASK_CMD_CLEAR_TASK_SET\n");
574 case ISCSI_TASK_CMD_LOGICAL_UNIT_RESET
:
575 printf("ISCSI_TASK_CMD_LOGICAL_UNIT_RESET\n");
577 case ISCSI_TASK_CMD_TARGET_WARM_RESET
:
578 printf("ISCSI_TASK_CMD_TARGET_WARM_RESET\n");
580 case ISCSI_TASK_CMD_TARGET_COLD_RESET
:
581 printf("ISCSI_TASK_CMD_TARGET_COLD_RESET\n");
583 case ISCSI_TASK_CMD_TARGET_REASSIGN
:
584 printf("ISCSI_TASK_CMD_TARGET_REASSIGN\n");
587 iscsi_err(__FILE__
, __LINE__
, "Unknown task function %d\n", cmd
.function
);
588 rsp
.response
= ISCSI_TASK_RSP_REJECTED
;
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");
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");
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");
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");
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");
636 iscsi_free(ping_data
);
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");
660 iscsi_free(ping_data
);
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");
668 iscsi_free(ping_data
);
672 iscsi_trace(TRACE_ISCSI_DEBUG
, "successfully sent %u bytes ping response\n", nop_out
.length
);
675 iscsi_free(ping_data
);
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
;
691 uint8_t rsp_header
[ISCSI_HEADER_LEN
];
693 char *text_in
= NULL
;
694 char *text_out
= NULL
;
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)
712 if (iscsi_text_cmd_decap(header
, &text_cmd
) != 0) {
713 iscsi_err(__FILE__
, __LINE__
, "iscsi_text_cmd_decap() failed\n");
716 /* Check args & update numbering */
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);
721 if (text_cmd
.cont
!= 0) {
722 iscsi_err(__FILE__
, __LINE__
, "Continue");
726 if (text_cmd
.CmdSN
!= sess
->ExpCmdSN
) {
727 iscsi_err(__FILE__
, __LINE__
, "CmdSN");
736 if ((text_out
= iscsi_malloc_atomic(2048)) == NULL
) {
737 iscsi_err(__FILE__
, __LINE__
,
738 "iscsi_malloc_atomic() failed\n");
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");
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
,
756 iscsi_err(__FILE__
, __LINE__
,
757 "iscsi_sock_msg() failed\n");
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");
778 strcmp(ptr
->offer_rx
, "All") == 0 &&
779 !param_equiv(sess
->params
, "SessionType",
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,
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
,
794 (void) get_iqn(sess
, i
, buf
,
796 PARAM_TEXT_ADD(sess
->params
,
800 PARAM_TEXT_ADD(sess
->params
,
802 iscsi_target_getvar(sess
->target
, "target address"),
808 "WARNING: attempt to "
809 "discover targets from "
810 "%s (not allowed by %s)"
811 " has been rejected",
820 /* Parse outgoing offer */
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
);
833 text_rsp
.final
= text_cmd
.final
;
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");
848 if (iscsi_sock_msg(sess
->sock
, 1, ISCSI_HEADER_LEN
, rsp_header
, 0) !=
850 iscsi_err(__FILE__
, __LINE__
,
851 "iscsi_sock_msg() failed\n");
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");
866 /* given a target's iqn, find the relevant target that we're exporting */
868 find_target_iqn(target_session_t
*sess
)
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
)))) {
884 /* given a tsih, find the relevant target that we're exporting */
886 find_target_tsih(iscsi_target_t
*target
, int tsih
)
891 targv
= target
->lunv
;
892 for (i
= 0 ; i
< targv
->c
; i
++) {
893 if (targv
->v
[i
].tsih
== tsih
) {
901 * login_command_t() handles login requests and replies.
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
];
911 char *text_in
= NULL
;
912 char *text_out
= NULL
;
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)
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");
944 if (sess
->IsLoggedIn
) {
945 iscsi_err(__FILE__
, __LINE__
,
946 "duplicate login attempt on sess %d\n", sess
->id
);
949 if ((cmd
.cont
!= 0) && (cmd
.transit
!= 0)) {
950 iscsi_err(__FILE__
, __LINE__
,
951 "Bad cmd.continue. Expected 0.\n");
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
;
964 } else if (cmd
.tsih
!= 0) {
965 iscsi_err(__FILE__
, __LINE__
,
966 "Bad cmd.tsih (%u). Expected 0.\n", cmd
.tsih
);
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");
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");
986 if (iscsi_sock_msg(sess
->sock
, 0, (unsigned) len_in
, text_in
,
988 iscsi_err(__FILE__
, __LINE__
,
989 "iscsi_sock_msg() failed\n");
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
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);
1010 case ISCSI_PARAM_STATUS_FAILED
:
1011 rsp
.status_detail
= ISCSI_LOGIN_DETAIL_SUCCESS
;
1013 case ISCSI_PARAM_STATUS_AUTH_FAILED
:
1015 ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE
;
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).
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
);
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. */
1052 rsp
.transit
= cmd
.transit
;
1054 if (cmd
.csg
== ISCSI_LOGIN_STAGE_SECURITY
) {
1055 if (param_equiv(sess
->params
, "AuthResult", "No")) {
1057 } else if (param_equiv(sess
->params
, "AuthResult", "Fail")) {
1058 rsp
.status_class
= rsp
.status_detail
=
1059 ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE
;
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");
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");
1079 if ((i
= find_target_iqn(sess
)) < 0) {
1080 iscsi_err(__FILE__
, __LINE__
,
1081 "Bad TargetName \"%s\"\n",
1082 param_val(sess
->params
, "TargetName"));
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
,
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",
1100 if (param_equiv(sess
->params
, "SessionType", "")) {
1101 iscsi_err(__FILE__
, __LINE__
,
1102 "SessionType not specified\n");
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
);
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
);
1127 rsp
.status_class
= rsp
.status_detail
= ISCSI_LOGIN_DETAIL_SUCCESS
;
1128 rsp
.length
= len_out
;
1130 /* Send login response */
1132 sess
->ExpCmdSN
= sess
->MaxCmdSN
= cmd
.CmdSN
;
1133 rsp
.isid
= cmd
.isid
;
1134 rsp
.StatSN
= cmd
.ExpStatSN
; /* debug */
1136 rsp
.cont
= cmd
.cont
;
1137 rsp
.ExpCmdSN
= sess
->ExpCmdSN
;
1138 rsp
.MaxCmdSN
= sess
->MaxCmdSN
;
1139 if (!rsp
.status_class
) {
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");
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");
1163 iscsi_trace(TRACE_ISCSI_DEBUG
,
1164 "sent login response ok\n");
1165 if (rsp
.status_class
!= 0) {
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"),
1181 printf("%s\n", logbuf
);
1182 #ifdef HAVE_SYSLOG_H
1183 /* log information to syslog */
1184 syslog(LOG_INFO
, "%s", logbuf
);
1187 /* Buffer for data xfers to/from the scsi device */
1188 if (!param_equiv(sess
->params
, "MaxRecvDataSegmentLength",
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");
1200 iscsi_err(__FILE__
, __LINE__
,
1201 "0 MaxRecvDataSegmentLength not supported\n");
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
;
1216 uint8_t rsp_header
[ISCSI_HEADER_LEN
];
1217 char logbuf
[BUFSIZ
];
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");
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
;
1232 RETURN_NOT_EQUAL("CmdSN", cmd
.CmdSN
, sess
->ExpCmdSN
, NO_CLEANUP
, -1);
1233 RETURN_NOT_EQUAL("ExpStatSN", cmd
.ExpStatSN
, sess
->StatSN
, NO_CLEANUP
, -1);
1235 if (cmd
.CmdSN
!= sess
->ExpCmdSN
) {
1236 iscsi_err(__FILE__
, __LINE__
, "CmdSN");
1240 if (cmd
.ExpStatSN
!= sess
->StatSN
) {
1241 iscsi_err(__FILE__
, __LINE__
, "ExpStatSN");
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");
1256 if (iscsi_sock_msg(sess
->sock
, 1, ISCSI_HEADER_LEN
, rsp_header
, 0) !=
1258 iscsi_err(__FILE__
, __LINE__
,
1259 "iscsi_sock_msg() failed\n");
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"),
1274 printf("%s\n", logbuf
);
1276 /* log information to syslog */
1277 syslog(LOG_INFO
, "%s", logbuf
);
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
);
1291 targv
= sess
->target
->lunv
;
1292 targv
->v
[i
].tsih
= 0;
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",
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",
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");
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");
1339 iscsi_trace(TRACE_ISCSI_DEBUG
, "sent login response ok\n");
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.
1350 execute_t(target_session_t
*sess
, uint8_t *header
)
1352 int op
= ISCSI_OPCODE(header
);
1354 if (verify_cmd_t(sess
, header
) != 0) {
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");
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");
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");
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");
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");
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");
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");
1431 * Currently one thread per session, used for both Rx and Tx.
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
;
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",
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");
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",
1532 iscsi_free(sess
->buff
);
1533 if (param_list_destroy(sess
->params
) != 0) {
1534 iscsi_err(__FILE__
, __LINE__
,
1535 "param_list_destroy() failed\n");
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");
1553 ISCSI_UNLOCK(&g_session_q_mutex
, return -1);
1554 iscsi_trace(TRACE_ISCSI_DEBUG
, "session %d: ended\n", sess
->id
);
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
];
1567 if (iscsi_sock_msg(sess
->sock
, 0, ISCSI_HEADER_LEN
, header
, 0) !=
1569 iscsi_err(__FILE__
, __LINE__
,
1570 "iscsi_sock_msg() failed\n");
1573 if ((ret_val
= iscsi_write_data_decap(header
, data
)) != 0) {
1574 iscsi_err(__FILE__
, __LINE__
,
1575 "iscsi_write_data_decap() failed\n");
1579 if (sess
->sess_params
.max_dataseg_len
) {
1580 if (data
->length
> sess
->sess_params
.max_dataseg_len
) {
1581 args
->status
= 0x02;
1585 if ((args
->bytes_recv
+ data
->length
) > args
->trans_len
) {
1586 args
->status
= 0x02;
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
);
1594 args
->status
= 0x02;
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");
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
;
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;
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");
1640 (void) memcpy(iov
, sg
, sizeof(struct iovec
) * 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",
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");
1664 iscsi_trace(TRACE_SCSI_DATA
,
1665 "reading %u bytes immediate write data\n",
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");
1674 iscsi_trace(TRACE_SCSI_DATA
,
1675 "successfully read %u bytes immediate write data\n",
1677 args
->bytes_recv
+= args
->length
;
1681 * Read iSCSI data PDUs
1683 if (args
->bytes_recv
< args
->trans_len
) {
1685 int read_status
= 0;
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
));
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 */
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
-
1708 sess
->sess_params
.max_burst_length
);
1709 iscsi_trace(TRACE_ISCSI_DEBUG
,
1710 "sending R2T for %u bytes data\n",
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");
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
,
1732 if (iscsi_sock_msg(sess
->sock
, 1,
1733 ISCSI_HEADER_LEN
, header
, 0) !=
1735 iscsi_err(__FILE__
, __LINE__
,
1736 "iscsi_sock_msg() failed\n");
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 "
1755 iscsi_err(__FILE__
, __LINE__
,
1756 "read_data_pdu() failed\n");
1757 args
->status
= 0x02;
1762 if (data
.ExpStatSN
!= sess
->StatSN
) {
1763 iscsi_warn(__FILE__
, __LINE__
,
1764 "Bad \"ExpStatSN\": Got %u "
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. */
1774 (void) memcpy(iov
, sg
, sizeof(struct iovec
) * 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");
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");
1792 iscsi_trace(TRACE_ISCSI_DEBUG
,
1793 "successfully scattered %u bytes\n",
1795 args
->bytes_recv
+= data
.length
;
1796 desired_xfer_len
-= data
.length
;
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",
1804 sess
->sess_params
.first_burst_length
);
1805 args
->status
= 0x02;
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",
1814 args
->status
= 0x02;
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;
1826 if ((desired_xfer_len
== 0) &&
1827 (args
->bytes_recv
< args
->trans_len
)) {
1830 } while (args
->bytes_recv
< args
->trans_len
);
1832 RETURN_NOT_EQUAL("Final bit", data
.final
, 1, TTD_CLEANUP
, -1);
1834 if (data
.final
!= 1) {
1835 iscsi_err(__FILE__
, __LINE__
, "Final bit");
1842 RETURN_NOT_EQUAL("Final bit", args
->final
, 1, TTD_CLEANUP
, -1);
1844 if (data
.final
!= 1) {
1845 iscsi_err(__FILE__
, __LINE__
, "Final bit");
1851 iscsi_trace(TRACE_ISCSI_DEBUG
,
1852 "successfully transferred %u bytes write data\n",
1858 /* check there's enough space in the arrays */
1860 size_arrays(iscsi_target_t
*tgt
, unsigned needed
)
1862 if (tgt
->size
== 0) {
1863 /* only get here first time around */
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 */
1877 findvar(iscsi_target_t
*tgt
, const char *name
)
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
)
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");
1908 /* re-read the configuration file */
1910 iscsi_target_reconfigure(iscsi_target_t
*tgt
)
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
);
1928 /* it worked - let's reassign things */
1929 /* XXX - agc - lock */
1930 oldluns
= tgt
->lunv
;
1931 olddevices
= tgt
->devv
;
1932 oldextents
= tgt
->extentv
;
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
);
1945 iscsi_target_start(iscsi_target_t
*tgt
)
1954 if ((dbg
= iscsi_target_getvar(tgt
, "debug")) != NULL
) {
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
);
1969 (void) fprintf(stderr
, "No targets to initialise\n");
1972 maxsessions
= atoi(iscsi_target_getvar(tgt
, "max sessions"));
1973 NEWARRAY(target_session_t
, g_session
, maxsessions
, "iscsi_target_start",
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");
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");
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");
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");
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"));
2019 iscsi_target_shutdown(iscsi_target_t
*tgt
)
2021 target_session_t
*sess
;
2025 if ((tgt
->state
== TARGET_SHUTTING_DOWN
) ||
2026 (tgt
->state
== TARGET_SHUT_DOWN
)) {
2027 iscsi_err(__FILE__
, __LINE__
,
2028 "duplicate target shutdown attempted\n");
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");
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
) {
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");
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");
2072 if (tgt
->listener_pid
!= getpid()) {
2073 iscsi_trace(TRACE_ISCSI_DEBUG
, "waiting for listener thread\n");
2074 while (tgt
->listener_listening
) {
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");
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
;
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];
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");
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");
2133 ISCSI_UNLOCK(&g_session_q_mutex
, return -1);
2135 (void) memset(sess
, 0x0, sizeof(*sess
));
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
,
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");
2158 switch (tgt
->famv
[i
]) {
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");
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");
2180 #ifdef HAVE_GETNAMEINFO
2181 if (getnameinfo((struct sockaddr
*)(void *)
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 *)
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
));
2198 (void) strlcpy(local
,
2199 inet_ntoa(localAddrStorage
.sin_addr
),
2201 (void) strlcpy(sess
->initiator
,
2202 inet_ntoa(remoteAddrStorage
.sin_addr
),
2203 sizeof(sess
->initiator
));
2206 (void) snprintf(targetaddress
, sizeof(targetaddress
),
2208 iscsi_target_getvar(tgt
, "target port"));
2209 iscsi_target_setvar(tgt
, "target address",
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
);
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");
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");
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
),
2258 iscsi_target_getvar(tgt
, "target port"));
2259 iscsi_target_setvar(tgt
, "target address",
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
);
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");
2278 tgt
->listener_listening
--;
2282 /* write the pid to the pid file */
2284 iscsi_target_write_pidfile(const char *f
)
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
));
2295 (void) fprintf(fp
, "%ld\n", (long) getpid());
2300 /* set a variable */
2302 iscsi_target_setvar(iscsi_target_t
*tgt
, const char *name
, const char *value
)
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
);
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
);
2322 /* get a variable's value (NULL if not set) */
2324 iscsi_target_getvar(iscsi_target_t
*tgt
, const char *name
)
2328 return ((i
= findvar(tgt
, name
)) < 0) ? NULL
: tgt
->value
[i
];