2 * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $
32 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <arpa/inet.h>
42 #if __FreeBSD_version < 500000
45 #include <sys/ioctl.h>
59 #include "iscontrol.h"
63 T2
, /*T3,*/ T4
, T5
, /*T6,*/ T7
, T8
, T9
,
64 T10
, T11
, T12
, T13
, T14
, T15
, T16
, T18
70 | Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan
71 | ume@mahoroba.org ume@{,jp.}FreeBSD.org
72 | http://www.imasy.org/~ume/
75 tcpConnect(isess_t
*sess
)
77 isc_opt_t
*op
= sess
->op
;
78 int val
, sv_errno
, soc
;
79 struct addrinfo
*res
, *res0
, hints
;
83 if(sess
->flags
& (SESS_RECONNECT
|SESS_REDIRECT
)) {
84 syslog(LOG_INFO
, "%s", (sess
->flags
& SESS_RECONNECT
)
85 ? "Reconnect": "Redirected");
87 debug(1, "%s", (sess
->flags
& SESS_RECONNECT
) ? "Reconnect": "Redirected");
88 shutdown(sess
->soc
, SHUT_RDWR
);
92 sess
->flags
&= ~SESS_CONNECTED
;
93 if(sess
->flags
& SESS_REDIRECT
) {
95 sess
->flags
|= SESS_RECONNECT
;
97 sleep(2); // XXX: actually should be ?
101 // make sure we are not in a loop
102 // XXX: this code has to be tested
103 sec
= time(0) - sess
->reconnect_time
;
105 // if we've been connected for more that 5 minutes
106 // then just reconnect
107 sess
->reconnect_time
= sec
;
108 sess
->reconnect_cnt1
= 0;
112 sess
->reconnect_cnt1
++;
113 if((sec
/ sess
->reconnect_cnt1
) < 2) {
114 // if less that 2 seconds from the last reconnect
115 // we are most probably looping
116 syslog(LOG_CRIT
, "too many reconnects %d", sess
->reconnect_cnt1
);
122 sess
->reconnect_cnt
++;
125 snprintf(pbuf
, sizeof(pbuf
), "%d", op
->port
);
126 memset(&hints
, 0, sizeof(hints
));
127 hints
.ai_family
= PF_UNSPEC
;
128 hints
.ai_socktype
= SOCK_STREAM
;
129 debug(1, "targetAddress=%s port=%d", op
->targetAddress
, op
->port
);
130 if((val
= getaddrinfo(op
->targetAddress
, pbuf
, &hints
, &res0
)) != 0) {
131 fprintf(stderr
, "getaddrinfo(%s): %s\n", op
->targetAddress
, gai_strerror(val
));
134 sess
->flags
&= ~SESS_CONNECTED
;
137 for(res
= res0
; res
; res
= res
->ai_next
) {
138 soc
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
142 // from Patrick.Guelat@imp.ch:
143 // iscontrol can be called without waiting for the socket entry to time out
145 if(setsockopt(soc
, SOL_SOCKET
, SO_REUSEADDR
, &val
, (socklen_t
)sizeof(val
)) < 0) {
146 fprintf(stderr
, "Cannot set socket SO_REUSEADDR %d: %s\n\n",
147 errno
, strerror(errno
));
149 if(connect(soc
, res
->ai_addr
, res
->ai_addrlen
) == 0)
161 /* Default to TCP_NODELAY to improve transfers */
162 if(setsockopt(sess
->soc
, IPPROTO_TCP
, TCP_NODELAY
, &val
, sizeof(val
)) < 0)
163 fprintf(stderr
, "Cannot set socket NO delay option err=%d %s\n",
164 errno
, strerror(errno
));
167 struct timeval timeout
;
170 if(setsockopt(sess
->soc
, IPPROTO_TCP
, TCP_KEEPALIVE
, &val
, sizeof(val
)) < 0)
171 fprintf(stderr
, "Cannot set socket KEEPALIVE option err=%d %s\n",
172 errno
, strerror(errno
));
174 if(setsockopt(sess
->soc
, IPPROTO_TCP
, TCP_NODELAY
, &val
, sizeof(val
)) < 0)
175 fprintf(stderr
, "Cannot set socket NO delay option err=%d %s\n",
176 errno
, strerror(errno
));
180 if((setsockopt(sess
->soc
, SOL_SOCKET
, SO_SNDTIMEO
, &timeout
, sizeof(timeout
)) < 0)
181 || (setsockopt(sess
->soc
, SOL_SOCKET
, SO_RCVTIMEO
, &timeout
, sizeof(timeout
)) < 0)) {
182 fprintf(stderr
, "Cannot set socket timeout to %ld err=%d %s\n",
183 timeout
.tv_sec
, errno
, strerror(errno
));
188 int len
= sizeof(val
);
189 if(getsockopt(sess
->soc
, SOL_SOCKET
, SO_SNDBUF
, &val
, &len
) == 0)
190 fprintf(stderr
, "was: SO_SNDBUF=%dK\n", val
/1024);
193 if(sess
->op
->sockbufsize
) {
194 val
= sess
->op
->sockbufsize
* 1024;
195 if((setsockopt(sess
->soc
, SOL_SOCKET
, SO_SNDBUF
, &val
, sizeof(val
)) < 0)
196 || (setsockopt(sess
->soc
, SOL_SOCKET
, SO_RCVBUF
, &val
, sizeof(val
)) < 0)) {
197 fprintf(stderr
, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n",
198 val
, errno
, strerror(errno
));
202 sess
->flags
|= SESS_CONNECTED
;
206 fprintf(stderr
, "errno=%d\n", sv_errno
);
212 if((sess
->flags
& SESS_REDIRECT
) == 0) {
213 if(strcmp(op
->targetAddress
, sess
->target
.address
) != 0) {
214 syslog(LOG_INFO
, "reconnecting to original target address");
215 free(op
->targetAddress
);
216 op
->targetAddress
= sess
->target
.address
;
217 op
->port
= sess
->target
.port
;
218 op
->targetPortalGroupTag
= sess
->target
.pgt
;
222 sleep(5); // for now ...
225 return 0; // terminal error
230 setOptions(isess_t
*sess
, int flag
)
237 bzero(&oop
, sizeof(isc_opt_t
));
239 if((flag
& SESS_FULLFEATURE
) == 0) {
240 oop
.initiatorName
= sess
->op
->initiatorName
;
241 oop
.targetAddress
= sess
->op
->targetAddress
;
242 if(sess
->op
->targetName
!= 0)
243 oop
.targetName
= sess
->op
->targetName
;
245 oop
.maxRecvDataSegmentLength
= sess
->op
->maxRecvDataSegmentLength
;
246 oop
.maxXmitDataSegmentLength
= sess
->op
->maxXmitDataSegmentLength
; // XXX:
247 oop
.maxBurstLength
= sess
->op
->maxBurstLength
;
248 oop
.maxluns
= sess
->op
->maxluns
;
252 | turn on digestion only after login
254 if(sess
->op
->headerDigest
!= NULL
) {
255 sep
= strchr(sess
->op
->headerDigest
, ',');
257 oop
.headerDigest
= sess
->op
->headerDigest
;
258 debug(1, "oop.headerDigest=%s", oop
.headerDigest
);
260 if(sess
->op
->dataDigest
!= NULL
) {
261 sep
= strchr(sess
->op
->dataDigest
, ',');
263 oop
.dataDigest
= sess
->op
->dataDigest
;
264 debug(1, "oop.dataDigest=%s", oop
.dataDigest
);
268 if(ioctl(sess
->fd
, ISCSISETOPT
, &oop
)) {
269 perror("ISCSISETOPT");
276 startSession(isess_t
*sess
)
284 if((sess
->flags
& SESS_CONNECTED
) == 0) {
288 fd
= open(iscsidev
, O_RDWR
);
294 // XXX: this has to go
296 n
= sizeof(sess
->isid
);
297 if(sysctlbyname("net.iscsi.isid", (void *)sess
->isid
, (size_t *)&n
, 0, 0) != 0)
298 perror("sysctlbyname");
300 if(ioctl(fd
, ISCSISETSES
, &n
)) {
301 perror("ISCSISETSES");
304 sleep(1); /* XXX temporary */
305 asprintf(&dev
, "%s%d", iscsidev
, n
);
306 nfd
= open(dev
, O_RDWR
);
316 if(setOptions(sess
, 0) != 0)
320 if(ioctl(sess
->fd
, ISCSISETSOC
, &sess
->soc
)) {
321 perror("ISCSISETSOC");
333 syslog(LOG_NOTICE
, "trapped signal %d", sig
);
334 fprintf(stderr
, "trapped signal %d\n", sig
);
338 currsess
->flags
|= SESS_DISCONNECT
;
342 currsess
->flags
|= SESS_RECONNECT
;
359 if(ioctl(sess
->fd
, ISCSIGETCAM
, &sess
->cam
) != 0) {
360 syslog(LOG_WARNING
, "ISCSIGETCAM failed: %d", errno
);
363 debug(2, "nluns=%d", sess
->cam
.target_nluns
);
365 | for now will do this for each lun ...
367 for(i
= 0; i
< sess
->cam
.target_nluns
; i
++) {
368 debug(2, "CAM path_id=%d target_id=%d target_lun=%d",
369 sess
->cam
.path_id
, sess
->cam
.target_id
, sess
->cam
.target_lun
[i
]);
371 sess
->camdev
= cam_open_btl(sess
->cam
.path_id
, sess
->cam
.target_id
,
372 sess
->cam
.target_lun
[i
], O_RDWR
, NULL
);
373 if(sess
->camdev
== NULL
) {
374 syslog(LOG_WARNING
, "%s", cam_errbuf
);
375 debug(3, "%s", cam_errbuf
);
379 cam_path_string(sess
->camdev
, pathstr
, sizeof(pathstr
));
380 debug(2, "pathstr=%s", pathstr
);
382 ccb
= cam_getccb(sess
->camdev
);
383 bzero(&(&ccb
->ccb_h
)[1], sizeof(struct ccb_relsim
) - sizeof(struct ccb_hdr
));
384 ccb
->ccb_h
.func_code
= XPT_REL_SIMQ
;
385 ccb
->crs
.release_flags
= RELSIM_ADJUST_OPENINGS
;
386 ccb
->crs
.openings
= sess
->op
->tags
;
388 if(cam_send_ccb(sess
->camdev
, ccb
) < 0)
389 syslog(LOG_WARNING
, "%s", cam_errbuf
);
391 if((ccb
->ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
392 syslog(LOG_WARNING
, "XPT_REL_SIMQ CCB failed");
393 // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
396 syslog(LOG_INFO
, "%s tagged openings now %d\n", pathstr
, ccb
->crs
.openings
);
399 cam_close_device(sess
->camdev
);
404 supervise(isess_t
*sess
)
410 if(strcmp(sess
->op
->sessionType
, "Discovery") == 0) {
411 sess
->flags
|= SESS_DISCONNECT
;
416 printf("ready to go scsi\n");
418 if(setOptions(sess
, SESS_FULLFEATURE
) != 0)
421 if((sess
->flags
& SESS_FULLFEATURE
) == 0) {
422 if(daemon(0, 1) != 0) {
427 openlog("iscontrol", LOG_CONS
|LOG_PERROR
|LOG_PID
|LOG_NDELAY
, LOG_KERN
);
428 syslog(LOG_INFO
, "running");
431 if(ioctl(sess
->fd
, ISCSISTART
)) {
432 perror("ISCSISTART");
439 if(ioctl(sess
->fd
, ISCSIRESTART
)) {
440 perror("ISCSIRESTART");
445 signal(SIGINT
, trap
);
446 signal(SIGHUP
, trap
);
447 signal(SIGTERM
, trap
);
451 if(ioctl(sess
->fd
, ISCSISIGNAL
, &sig
)) {
452 perror("ISCSISIGNAL");
455 sess
->flags
|= SESS_FULLFEATURE
;
457 sess
->flags
&= ~(SESS_REDIRECT
| SESS_RECONNECT
);
458 printf("iscontrol: supervise starting main loop\n");
460 | the main loop - actually do nothing
461 | all the work is done inside the kernel
463 while((sess
->flags
& (SESS_REDIRECT
|SESS_RECONNECT
|SESS_DISCONNECT
)) == 0) {
465 // like sending a nop_out?
468 printf("iscontrol: supervise going down\n");
469 syslog(LOG_INFO
, "sess flags=%x", sess
->flags
);
472 if(ioctl(sess
->fd
, ISCSISIGNAL
, &sig
)) {
473 perror("ISCSISIGNAL");
476 if(sess
->flags
& SESS_DISCONNECT
) {
478 if(ioctl(sess
->fd
, ISCSISTOP
, &val
)) {
481 sess
->flags
&= ~SESS_FULLFEATURE
;
485 sess
->flags
|= SESS_INITIALLOGIN1
;
491 handledDiscoveryResp(isess_t
*sess
, pdu_t
*pp
)
503 n
= strlen((char *)ptr
) + 1;
511 doDiscovery(isess_t
*sess
)
514 text_req_t
*tp
= (text_req_t
*)&spp
.ipdu
.bhs
;
518 bzero(&spp
, sizeof(pdu_t
));
519 tp
->cmd
= ISCSI_TEXT_CMD
/*| 0x40 */; // because of a bug in openiscsi-target
521 tp
->ttt
= 0xffffffff;
522 addText(&spp
, "SendTargets=All");
523 return sendPDU(sess
, &spp
, handledDiscoveryResp
);
527 doLogin(isess_t
*sess
)
529 isc_opt_t
*op
= sess
->op
;
534 if(op
->chapSecret
== NULL
&& op
->tgtChapSecret
== NULL
)
536 | don't need any security negotiation
537 | or in other words: we don't have any secrets to exchange
539 sess
->csg
= LON_PHASE
;
541 sess
->csg
= SN_PHASE
;
544 sess
->tsih
= 0; // XXX: no 'reconnect' yet
545 sess
->flags
&= ~SESS_NEGODONE
; // XXX: KLUDGE
547 count
= 10; // should be more than enough
549 debug(3, "count=%d csg=%d", count
, sess
->csg
);
550 status
= loginPhase(sess
);
552 // just in case we get into a loop
554 } while(status
== 0 && (sess
->csg
!= FF_PHASE
));
556 sess
->flags
&= ~SESS_INITIALLOGIN
;
557 debug(3, "status=%d", status
);
560 case 0: // all is ok ...
561 sess
->flags
|= SESS_LOGGEDIN
;
562 if(strcmp(sess
->op
->sessionType
, "Discovery") == 0)
566 case 1: // redirect - temporary/permanent
568 | start from scratch?
570 sess
->flags
&= ~SESS_NEGODONE
;
571 sess
->flags
|= (SESS_REDIRECT
| SESS_INITIALLOGIN1
);
572 syslog(LOG_DEBUG
, "target sent REDIRECT");
575 case 2: // initiator terminal error
577 case 3: // target terminal error -- could retry ...
579 return T7
; // lets try
586 handleLogoutResp(isess_t
*sess
, pdu_t
*pp
)
588 if(sess
->flags
& SESS_DISCONNECT
)
594 startLogout(isess_t
*sess
)
597 logout_req_t
*p
= (logout_req_t
*)&spp
.ipdu
.bhs
;
599 bzero(&spp
, sizeof(pdu_t
));
600 p
->cmd
= ISCSI_LOGOUT_CMD
| 0x40;
601 p
->reason
= BIT(7) | 0;
604 return sendPDU(sess
, &spp
, handleLogoutResp
);
608 inLogout(isess_t
*sess
)
610 if(sess
->flags
& SESS_RECONNECT
)
616 S1
=1, S2
, S3
, S4
, S5
, S6
, S7
, S8
628 -------<-------------+
629 +--------->/ S1 \
<----+ |
648 | | |T5
+--------+---------+
649 | | | /T16
+-----+------+ |
650 | | | / -+-----+--+ | |
651 | | | / / S7 \
|T12
| |
652 | | | / +->\
/<-+ V V
653 | | | / / -+----- -------
654 | | | / /T11
|T10
/ S8 \
655 | | V
/ / V
+----+ \
/
656 | | ---+-+- ----+-- | -------
657 | | / S5 \T9
/ S6 \
<+ ^
658 | +-----\
/--->\
/ T14
|
659 | ------- --+----+------+T17
660 +---------------------------+
669 if((sess
= calloc(1, sizeof(isess_t
))) == NULL
) {
670 // boy, is this a bad start ...
671 fprintf(stderr
, "no memory!\n");
679 sess
->target
.address
= strdup(op
->targetAddress
);
680 sess
->target
.port
= op
->port
;
681 sess
->target
.pgt
= op
->targetPortalGroupTag
;
683 sess
->flags
= SESS_INITIALLOGIN
| SESS_INITIALLOGIN1
;
688 switch(tcpConnect(sess
)) {
689 case T1
: state
= S2
; break;
690 default: state
= S8
; break;
695 switch(startSession(sess
)) {
696 case T2
: state
= S1
; break;
697 case T4
: state
= S4
; break;
698 default: state
= S8
; break;
703 switch(doLogin(sess
)) {
704 case T7
: state
= S1
; break;
705 case T5
: state
= S5
; break;
706 default: state
= S8
; break;
711 switch(supervise(sess
)) {
712 case T8
: state
= S1
; break;
713 case T9
: state
= S6
; break;
714 case T11
: state
= S7
; break;
715 case T15
: state
= S8
; break;
716 default: state
= S8
; break;
721 switch(startLogout(sess
)) {
722 case T13
: state
= S1
; break;
723 case T14
: state
= S6
; break;
724 case T16
: state
= S8
; break;
725 default: state
= S8
; break;
730 switch(inLogout(sess
)) {
731 case T18
: state
= S1
; break;
732 case T10
: state
= S6
; break;
733 case T12
: state
= S7
; break;
734 case T16
: state
= S8
; break;
735 default: state
= S8
; break;
740 // maybe do some clean up?
741 syslog(LOG_INFO
, "terminated");
744 syslog(LOG_INFO
, "unknown state %d", state
);